Powpowders Autobuyer
API Documentation v2.0
Встроенный асинхронный HTTP-сервер с очередью задач, неблокирующей моделью ввода и webhook-нотификациями. Никаких внешних процессов — поднимается прямо внутри .exe.
┌─────────────────────────────────────────────────┐ │ Powpowders Autobuyer.exe │ │ │ │ ┌──────────────┐ ┌────────────────────┐ │ │ │ HTTP Server │────▶│ TaskStore │ │ │ │ :7788 │ │ (thread-safe TTL) │ │ │ └──────┬───────┘ └────────┬───────────┘ │ │ │ │ │ │ │ ┌────────▼───────────┐ │ │ │ │ TaskExecutor │ │ │ │ │ worker-thread │ │ │ │ │ asyncio event loop│ │ │ │ └────────┬───────────┘ │ │ │ │ │ │ │ ┌────────▼───────────┐ │ │ │ │ InputManager │ │ │ │ │ (blocking Queue) │ │ │ │ └────────────────────┘ │ └─────────────────────────────────────────────────┘ │ webhooks (non-blocking, отдельный поток) ▼ внешний сервер клиента
Сервер реализует асинхронную очередь задач с изолированным event loop на каждую задачу:
• Каждый POST /tasks/sale помещает задачу в queue.Queue
• Воркер-тред извлекает задачу и запускает её в asyncio.new_event_loop()
— полностью изолированно от других задач
• Параллельные заказы исполняются в параллельных тредах,
каждый со своим event loop
• Задачи в статусе waiting_input не блокируют воркер —
они ждут в queue.Queue.get(timeout=...) внутри своего треда,
пока клиент не пришлёт ответ через POST /tasks/{id}/input
OrderProcessor InputManager API Client
│ │ │
│── register(task_id) ─────▶│ │
│── set_waiting_input() ────▶│ (status → waiting_input)
│ │ │
│ [заблокирован │◀── provide_input() ────│
│ Queue.get(timeout)] │ (POST /input) │
│ │ │
│◀── value ─────────────────│ │
│── set_running() ──────────▶│ │
│ [продолжает работу] │ │Все исходящие вебхуки отправляются неблокирующе — каждый вызов _fire_webhook() запускает отдельный daemon-тред с urllib.request, не задерживая выполнение задачи.
Настройка и аутентификация
| Параметр | По умолчанию | Описание |
|---|---|---|
| Bind address | 127.0.0.1 | Только loopback — не экспонируется наружу без reverse-proxy |
| Порт | 7788 | Настраивается в GUI вкладки API |
| TTL задач | 3600 сек | Завершённые задачи очищаются через 1 час |
| Таймаут ввода | 300 сек | Максимальное время ожидания waiting_input |
Все запросы требуют заголовок:
X-API-Key: YOUR_SECRET_KEYcurl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:7788/api/v1/ping # → {"pong": true, "version": "2.0"}
Справочник эндпоинтов
{
"pong": true,
"version": "2.0"
}Состояние системы: мониторинг FunPay, пул Razer-аккаунтов, слоты задач.
{
"api_version": "2.0",
"monitoring": true,
"razer_accounts": 3,
"cards": 2,
"tasks": {
"pending": 0,
"running": 2,
"waiting": 1
}
}Список товаров, сконфигурированных в приложении.
{
"razer": [
{"name": "Fortnite Crew", "link": "https://store.epicgames.com/..."},
{"name": "1000 V-Bucks", "link": "https://store.epicgames.com/..."}
],
"card": [
{"name": "Fortnite Crew Card", "link": "https://store.epicgames.com/..."}
]
}Агрегированная статистика продаж.
| Параметр | Допустимые значения |
|---|---|
period | today, 7d, 30d, all |
{
"period": "7d",
"total": 42,
"success": 40,
"fail": 2,
"gross": 15000.0,
"expenses": 12000.0,
"commission_3pct": 450.0,
"net": 2550.0,
"rate_pct": 95.2
}Список задач с опциональной фильтрацией по статусу, отсортированный по created_at desc.
| Параметр | По умолчанию | Описание |
|---|---|---|
limit | 50 | Максимум записей в ответе |
status | (все) | Фильтр по статусу конечного автомата |
GET /api/v1/tasks?status=waiting_input&limit=10Полное состояние задачи включая накопленный лог сообщений.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "sale",
"status": "waiting_auth",
"auth_url": "https://www.epicgames.com/activate?userCode=ABCDE",
"input_needed": null,
"messages": [
{"text": "🔄 Начинаем обработку заказа...", "ts": 1710000001.0},
{"text": "🔐 Ссылка для входа отправлена", "ts": 1710000005.0}
],
"result": null,
"error": null,
"created_at": 1710000000.0,
"updated_at": 1710000010.0,
"payload": {
"product_name": "Fortnite Crew",
"payment_type": "razer",
"buyer": "username"
}
}| Статус | Описание |
|---|---|
| pending | Задача в очереди, воркер ещё не взял |
| running | Активно выполняется в event loop |
| waiting_auth | Заблокирована: ждёт авторизации покупателя в Epic (device code flow) |
| waiting_input | Заблокирована: ждёт внешнего ввода через POST /input (email-код, PIN) |
| done | Терминальное состояние — успех |
| error | Терминальное состояние — ошибка |
| cancelled | Терминальное состояние — отменена клиентом |
Создать задачу продажи. Возвращает 202 Accepted немедленно — задача ставится в очередь и выполняется асинхронно.
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
product_name | string | ✅ | Точное имя товара из /api/v1/products |
payment_type | string | ✅ | "razer" или "card" |
product_type | string | "one_time" (по умолчанию) или "subscription" | |
chat_id | int | ID чата для fallback-сообщений через FunPay | |
buyer | string | Идентификатор покупателя (для логов) | |
order_id | string | Идемпотентный внешний ID заказа | |
exchange_code | string | Epic OAuth exchange code — позволяет пропустить device code flow | |
proxy | string | Прокси для этого заказа: http://user:pass@host:port | |
message_webhook_url | string | Endpoint для получения сообщений покупателю (event: message) | |
status_webhook_url | string | Endpoint для статусных событий (waiting_auth, waiting_input, status_change) |
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}Создать задачу смены региона Epic без выполнения покупки. Проводит полный device code flow + смена региона на TR.
Request body: те же поля что у /tasks/sale, кроме product_name, payment_type, product_type, exchange_code.
Разблокировать задачу в состоянии waiting_input — передать введённый пользователем код или PIN. Принимает поле value, input или code (любое из трёх).
{"value": "847291"}{"accepted": true, "task_id": "550e8400-..."}{"error": "Task not waiting for input (status: running)", "status": "running"}
Отменить задачу. Допустимо только для pending, waiting_auth, waiting_input — задачу в состоянии running прервать нельзя.
{"cancelled": true}Получить список всех доступных Razer PIN-кодов с их статусами.
{
"pins": [
{"code": "RAZER-XXXX-XXXX", "balance": 500, "used": false, "account": "razer_acc_1"},
{"code": "RAZER-YYYY-YYYY", "balance": 250, "used": true, "account": "razer_acc_2"}
]
}Добавить новый Razer PIN-код в систему.
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
code | string | ✅ | PIN-код (например RAZER-XXXX-XXXX-XXXX) |
account | string | Название аккаунта Razer для привязки |
{"code": "RAZER-XXXX-XXXX-XXXX", "account": "razer_acc_1"}{"success": true, "message": "PIN добавлен"}Удалить Razer PIN-код из системы.
{"success": true, "message": "PIN удалён"}Конечный автомат задачи
POST /tasks/sale
│
▼
pending ◀──── задача в очереди TaskExecutor
│
воркер-тред взял
│
▼
running
│
┌───────────┴────────────┐
│ │
▼ ▼
waiting_auth waiting_input
[device code flow] [email-код / PIN]
блокирует тред, блокирует тред,
ждёт Epic API ждёт POST /input
│ │
Epic авторизован input получен
│ │
└───────────┬────────────┘
▼
running
│
┌────────┴────────┐
▼ ▼
done error
[терминальный] [терминальный]
+ cancelled — из pending/waiting_* через DELETEPolling — простое опрашивание каждые N секунд:
import time, requests def poll_task(task_id: str, interval: float = 2.0): HEADERS = {"X-API-Key": "YOUR_KEY"} BASE_URL = "http://127.0.0.1:7788/api/v1" seen_msgs = 0 while True: r = requests.get(f"{BASE_URL}/tasks/{task_id}", headers=HEADERS, timeout=5) task = r.json() # Дренируем накопленные сообщения для покупателя for msg in task["messages"][seen_msgs:]: deliver_to_user(msg["text"]) seen_msgs = len(task["messages"]) match task["status"]: case "waiting_auth": notify_user_auth_url(task["auth_url"]) case "waiting_input": code = ask_user_for_input(task["input_needed"]) requests.post( f"{BASE_URL}/tasks/{task_id}/input", headers=HEADERS, json={"value": code} ) case "done": return task["result"] case "error" | "cancelled": raise RuntimeError(task.get("error") or task["status"]) time.sleep(interval)
Webhook-нотификации
Все события доставляются неблокирующим POST в отдельном daemon-треде. Гарантия доставки — best-effort (таймаут 5 секунд, без retry).
Событие эмитируется сразу после генерации device code. Покупатель должен перейти по auth_url и подтвердить вход.
{
"event": "waiting_auth",
"task_id": "550e8400-...",
"auth_url": "https://www.epicgames.com/activate?userCode=ABCDE",
"ts": 1710000005.123
}Эмитируется когда OrderProcessor запрашивает внешний ввод (6-значный email-код Epic, PIN родительского контроля и т.д.). Клиент обязан вызвать POST /tasks/{id}/input до истечения таймаута (300 сек).
{
"event": "waiting_input",
"task_id": "550e8400-...",
"input_needed": "Введите 6-значный код подтверждения из письма Epic Games",
"ts": 1710000060.0
}Эмитируется каждый раз, когда OrderProcessor хочет что-то сообщить покупателю. Если message_webhook_url не указан — сообщение пишется в FunPay чат через chat_id (fallback).
{
"event": "message",
"task_id": "550e8400-...",
"text": "✅ Вход подтверждён! Начали обработку заказа.",
"ts": 1710000047.0
}Эмитируется при переходе в done, error или cancelled.
{
"event": "status_change",
"task_id": "550e8400-...",
"status": "done",
"result": {
"success": true,
"epic_display_name": "CoolGamer2024",
"epic_account_id": "abc123def456",
"country": "TR",
"order_id": "ORDER-9876"
},
"error": null,
"ts": 1710000180.0
}Примеры интеграций
Полная реализация: приём заказа → device code flow → интерактивный ввод кода → финальный статус.
pip install aiogram aiohttp
Telegram ──▶ aiogram polling ──▶ /buy → POST /tasks/sale
│
Autobuyer ──▶ webhook :8080 ◀──────── status events
│
└── message event ──▶ bot.send_message(chat_id, text)
└── waiting_auth ──▶ bot.send_message(chat_id, auth_url)
└── waiting_input ──▶ bot.send_message(chat_id, prompt)
покупатель пишет код
──▶ POST /tasks/{id}/inputimport asyncio import aiohttp from aiohttp import web from aiogram import Bot, Dispatcher, Router, F from aiogram.types import Message from aiogram.filters import Command from aiogram.fsm.storage.memory import MemoryStorage API_BASE = "http://127.0.0.1:7788/api/v1" API_KEY = "YOUR_KEY" BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN" WEBHOOK_HOST = "https://your-server.example.com" WEBHOOK_PORT = 8080 bot = Bot(token=BOT_TOKEN) dp = Dispatcher(storage=MemoryStorage()) router = Router() dp.include_router(router) HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} # task_id → chat_id (для доставки сообщений) # "input:{chat}" → task_id (chat ожидает ввода) sessions: dict[str, int | str] = {} async def api(method: str, path: str, **kwargs) -> dict: async with aiohttp.ClientSession() as s: async with getattr(s, method)( f"{API_BASE}{path}", headers=HEADERS, **kwargs ) as r: return await r.json() @router.message(Command("buy")) async def cmd_buy(msg: Message): resp = await api("post", "/tasks/sale", json={ "product_name": "Fortnite Crew", "payment_type": "razer", "chat_id": msg.chat.id, "buyer": msg.from_user.username or str(msg.from_user.id), "order_id": f"TG-{msg.chat.id}-{msg.message_id}", "message_webhook_url": f"{WEBHOOK_HOST}/wh/message", "status_webhook_url": f"{WEBHOOK_HOST}/wh/status", }) task_id = resp.get("task_id") if not task_id: await msg.answer("❌ Не удалось создать задачу. Попробуйте позже.") return sessions[task_id] = msg.chat.id await msg.answer("✅ Заказ принят в обработку.\nЧерез несколько секунд придёт ссылка для входа в Epic Games.") @router.message(F.text) async def handle_text(msg: Message): chat_id = msg.chat.id task_id = sessions.get(f"input:{chat_id}") if not task_id: return resp = await api("post", f"/tasks/{task_id}/input", json={"value": msg.text.strip()}) if resp.get("accepted"): sessions.pop(f"input:{chat_id}", None) await msg.answer("✅ Принято, продолжаем обработку...") async def wh_message(request: web.Request) -> web.Response: data = await request.json() chat_id = sessions.get(data.get("task_id")) if chat_id: await bot.send_message(chat_id, data.get("text", "")) return web.Response(status=200) async def wh_status(request: web.Request) -> web.Response: data = await request.json() task_id = data.get("task_id") event = data.get("event") chat_id = sessions.get(task_id) if not chat_id: return web.Response(status=200) if event == "waiting_auth": await bot.send_message(chat_id, f"🔐 *Необходима авторизация Epic Games*\n\nПерейдите по ссылке:\n{data['auth_url']}", parse_mode="Markdown") elif event == "waiting_input": sessions[f"input:{chat_id}"] = task_id await bot.send_message(chat_id, f"✏️ *Требуется ввод*\n{data.get('input_needed','Введите код:')}", parse_mode="Markdown") elif event == "status_change": status = data.get("status") if status == "done": r = data.get("result", {}) await bot.send_message(chat_id, f"✅ *Заказ выполнен!*\nАккаунт: `{r.get('epic_display_name')}`\nРегион: `{r.get('country')}`", parse_mode="Markdown") elif status == "error": await bot.send_message(chat_id, f"❌ *Ошибка*\n`{data.get('error')}`", parse_mode="Markdown") sessions.pop(task_id, None) return web.Response(status=200) async def main(): app = web.Application() app.router.add_post("/wh/message", wh_message) app.router.add_post("/wh/status", wh_status) runner = web.AppRunner(app) await runner.setup() await web.TCPSite(runner, "0.0.0.0", WEBHOOK_PORT).start() await dp.start_polling(bot, skip_updates=True) if __name__ == "__main__": asyncio.run(main())
import asyncio import discord, aiohttp from discord.ext import commands from aiohttp import web API_BASE = "http://127.0.0.1:7788/api/v1" API_KEY = "YOUR_KEY" BOT_TOKEN = "YOUR_DISCORD_BOT_TOKEN" WEBHOOK_HOST = "https://your-server.example.com" WEBHOOK_PORT = 8080 intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix="!", intents=intents) HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} channel_tasks: dict[int | str, int | str] = {} awaiting_input: dict[int, str] = {} @bot.command(name="buy") async def buy(ctx: commands.Context, *, product: str = "Fortnite Crew"): async with aiohttp.ClientSession() as s: async with s.post(f"{API_BASE}/tasks/sale", json={ "product_name": product, "payment_type": "razer", "buyer": ctx.author.name, "order_id": f"DC-{ctx.message.id}", "message_webhook_url": f"{WEBHOOK_HOST}/wh/message", "status_webhook_url": f"{WEBHOOK_HOST}/wh/status", }, headers=HEADERS) as r: resp = await r.json() task_id = resp.get("task_id") if task_id: channel_tasks[task_id] = ctx.channel.id channel_tasks[ctx.channel.id] = task_id await ctx.send(f"✅ Задача `{task_id[:8]}...` создана.") @bot.listen("on_message") async def intercept_input(message: discord.Message): if message.author.bot or message.content.startswith("!"): return task_id = awaiting_input.pop(message.channel.id, None) if not task_id: return async with aiohttp.ClientSession() as s: async with s.post(f"{API_BASE}/tasks/{task_id}/input", json={"value": message.content.strip()}, headers=HEADERS) as r: resp = await r.json() if resp.get("accepted"): await message.add_reaction("✅")
Digiseller нотифицирует продавца о продаже через HTTP-уведомление на notify URL.
Настройка: Товары → Настройки товара → Уведомление продавцу → URL: https://your-server/ds/notify
Покупатель оплатил → Digiseller POST /ds/notify
│
POST /tasks/sale
│
Autobuyer выполняет заказ
│
event=message → Digiseller Debates API
event=done → логируем / закрываем тикетPRODUCT_MAP = {
"1234567": "Fortnite Crew",
"1234568": "1000 V-Bucks",
}
async def ds_notify(request: web.Request):
# GET или POST в зависимости от настройки Digiseller
unique_code = params.get("uniquecode", "")
product_id = params.get("id_goods", "")
# → POST /api/v1/tasks/sale с order_id=f"DS-{unique_code}"
async def ds_message(request: web.Request):
# Digiseller Debates API v2 — покупатель видит в личном кабинете
await s.post("https://api.digiseller.ru/api/debates/v2",
json={"id_goods": unique_code, "message": text, "secret_key": DIGISELLER_KEY})Playerok нотифицирует о подтверждённых заказах через webhook при статусе CONFIRMED.
auth_url и коды необходимо доставлять через сторонний канал (Telegram, email). Рекомендуется связывать заказ с tg_chat_id покупателя.
PRODUCT_MAP = {
"Fortnite Crew подписка": "Fortnite Crew",
"1000 В-баксов": "1000 V-Bucks",
}
async def playerok_notify(request: web.Request):
if data.get("status") != "CONFIRMED": return
# tg_chat_id передаётся в description лота
# → POST /api/v1/tasks/sale с order_id=f"PO-{order_id}"ЮKassa нотифицирует через IPN о событиях платёжного жизненного цикла.
payment = yookassa.Payment.create({
"amount": {"value": "299.00", "currency": "RUB"},
"confirmation": {"type": "redirect", "return_url": "https://your-site.ru/thanks"},
"capture": True,
"description": "Fortnite Crew",
"metadata": {
"product_name": "Fortnite Crew",
"payment_type": "razer",
"buyer": "username",
"tg_chat_id": "123456789",
}
})
# → редиректим на payment.confirmation.confirmation_urlpayment.succeeded — POST /api/v1/tasks/sale с данными из metadata.
{
"sum": 299,
"orderId": "unique-order-id",
"hookUrl": "https://your-server.example.com/lava/notify",
"successUrl": "https://your-site.ru/thanks",
"comment": "Fortnite Crew",
"customFields": {
"product_name": "Fortnite Crew",
"tg_chat_id": "123456789",
"buyer": "username"
}
}HMAC-SHA256 верификация подписи через заголовок Signature. При статусе "success" — POST /api/v1/tasks/sale с данными из custom_fields.
[Webhook trigger]
│ {product, buyer, tg_chat_id}
▼
[HTTP Request]
POST http://127.0.0.1:7788/api/v1/tasks/sale
Headers: X-API-Key: ...
Body: {product_name, payment_type, buyer, status_webhook_url, message_webhook_url}
│
│ {task_id, status: "pending"}
▼
[Set variable: task_id]
│
▼
[Respond to Webhook: 200 OK]
[Webhook trigger: /wh/autobuyer]
│ {event, task_id, ...}
▼
[Switch по event]
├── "waiting_auth" → [Telegram: send auth_url покупателю]
├── "waiting_input" → [Telegram: запросить код]
│ [Webhook: /wh/input/{task_id}]
│ → [HTTP: POST /tasks/{id}/input]
└── "status_change" → [Switch по status]
├── "done" → [Telegram: ✅]
└── "error" → [Telegram: ❌]| № | Модуль | Настройка |
|---|---|---|
| 1 | Webhooks → Custom webhook | Принимает заказ от магазина |
| 2 | HTTP → Make a request | POST /tasks/sale, передаём webhook URL |
| 3 | Tools → Set variable | Сохраняем task_id из ответа |
| 4 | Webhooks → Custom webhook | Принимает события от Autobuyer |
| 5 | Router | Ветвление по полю event |
| 6 | Telegram → Send a message | Доставка auth_url / сообщений покупателю |
| 7 | HTTP → Make a request | POST /tasks/{id}/input после получения кода |
Reverse-proxy (nginx + TLS)
API слушает только на 127.0.0.1 — для внешней доступности вебхук-сервера нужен TLS-терминирующий reverse-proxy.
sudo apt install nginx certbot python3-certbot-nginx -y sudo certbot --nginx -d your-domain.ru
server { listen 443 ssl http2; server_name your-domain.ru; ssl_certificate /etc/letsencrypt/live/your-domain.ru/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.ru/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; # Публичные вебхуки от платёжных систем location /wh/ { proxy_pass http://127.0.0.1:8080; proxy_read_timeout 30s; proxy_send_timeout 10s; } # API Autobuyer — только с IP-whitelist location /api/ { allow 1.2.3.4; # твой офис / VPS deny all; proxy_pass http://127.0.0.1:7788; proxy_read_timeout 120s; } } server { listen 80; server_name your-domain.ru; return 301 https://$host$request_uri; }
sudo nginx -t && sudo systemctl reload nginx
Коды ошибок и диагностика
| Код | Описание |
|---|---|
| 200 | Успех |
| 202 | Задача принята в очередь (асинхронная обработка) |
| 400 | Невалидный запрос — отсутствуют обязательные поля |
| 401 | Неверный или отсутствующий X-API-Key |
| 404 | Эндпоинт или задача не найдены |
| 409 | Конфликт состояния — нельзя отменить running-задачу или подать ввод в не-waiting_input задачу |
| Значение | Причина |
|---|---|
auth_timeout | Покупатель не авторизовался в Epic за auth_timeout_minutes минут |
no_razer_account | Пул Razer-аккаунтов пуст — все слоты заняты или не настроены |
no_card_available | Нет активных банковских карт в конфиге |
invalid_exchange_code | Переданный exchange_code истёк или невалиден |
unknown_task_type | Неизвестный type задачи (внутренняя ошибка) |
# Все активные задачи curl -H "X-API-Key: KEY" "http://127.0.0.1:7788/api/v1/tasks?status=running" # Задачи ожидающие ввода curl -H "X-API-Key: KEY" "http://127.0.0.1:7788/api/v1/tasks?status=waiting_input" # Статистика за сегодня curl -H "X-API-Key: KEY" "http://127.0.0.1:7788/api/v1/stats?period=today" # Состояние пула аккаунтов curl -H "X-API-Key: KEY" "http://127.0.0.1:7788/api/v1/status"
Полные примеры payload
Если клиент уже имеет Epic exchange_code — device code flow и waiting_auth пропускаются, задача сразу переходит в running:
{
"product_name": "1000 V-Bucks",
"payment_type": "card",
"product_type": "one_time",
"buyer": "username123",
"order_id": "MYSHOP-9876",
"exchange_code": "abc123def456ghi789...",
"message_webhook_url": "https://your-server.ru/wh/message",
"status_webhook_url": "https://your-server.ru/wh/status"
}{
"product_name": "Fortnite Crew",
"payment_type": "razer",
"buyer": "username",
"order_id": "MYSHOP-1234",
"proxy": "http://proxyuser:proxypass@gate.proxy.ru:8080"
}{
"buyer": "username",
"order_id": "REGION-001",
"message_webhook_url": "https://your-server.ru/wh/message",
"status_webhook_url": "https://your-server.ru/wh/status"
}{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "sale",
"status": "done",
"messages": [
{"text": "🔄 Начинаем обработку заказа. Ожидайте ссылку для входа...", "ts": 1710000001.0},
{"text": "✅ Вход в аккаунт подтверждён! Начали делать ваш заказ!", "ts": 1710000045.0},
{"text": "✅ Заказ выполнен!", "ts": 1710000180.0}
],
"result": {
"success": true,
"epic_display_name": "CoolGamer2024",
"epic_account_id": "abc123def456",
"country": "TR",
"order_id": "MYSHOP-9876"
},
"error": null,
"created_at": 1710000000.0,
"updated_at": 1710000180.0
}