Oyun Qurdu Corporate API
Oyun yükləmələrini və rəqəmsal hədiyyə kartlarını topdan qiymətə alıb öz məhsulunuzda çatdırmaq üçün REST API. Sifarişlər hesabınızdakı əvvəlcədən ödənilmiş balansdan ödənilir.
https://corporative.oyunqurdu.az/api/v1
- Bütün sorğu və cavablar JSON, UTF-8-dir. API cavablarındakı mesajlar ingiliscədir — kodunuz onlara arxalana bilər.
- Yalnız HTTPS. Adi HTTP yönləndirilir və API çağırışları üçün işlədilməməlidir.
- Versiya yolun bir hissəsidir (
/v1). Versiyanı dəyişmədən sahə əlavə edirik; v1 daxilində sahələri heç vaxt silmirik və adını dəyişmirik. - İki növ məhsul var: yükləmə (birbaşa oyun hesabına yazılır — oyunçu ID-sini göndərirsiniz) və kod (hədiyyə kartları — kodu cavabda alırsınız).
Sürətli başlanğıc
1. Daxil olun və API açarları səhifəsində açar yaradın. 2. Balansınızın sıfırdan çox olduğuna əmin olun. 3. Məhsulu tapın, canlı qiymətini yoxlayın və sifariş verin:
# 1. find the product code curl "https://corporative.oyunqurdu.az/api/v1/products?category=pubg-mobile" \ -H "Authorization: Bearer $OQ_API_KEY" # 2. live price right now curl "https://corporative.oyunqurdu.az/api/v1/products/PUBGM-660-UC/quote" \ -H "Authorization: Bearer $OQ_API_KEY" # 3. order it (max_unit_price locks the quoted price) curl -X POST https://corporative.oyunqurdu.az/api/v1/orders \ -H "Authorization: Bearer $OQ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"product": "PUBGM-660-UC", "fields": {"player_id": "5123456789"}, "reference": "INV-10045"}' # 4. read the result (or wait for the webhook) curl https://corporative.oyunqurdu.az/api/v1/orders/reference/INV-10045 \ -H "Authorization: Bearer $OQ_API_KEY"
import os, time, requests
API = "https://corporative.oyunqurdu.az/api/v1"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['OQ_API_KEY']}"
order = s.post(f"{API}/orders", json={
"product": "PUBGM-660-UC",
"fields": {"player_id": "5123456789"},
"reference": "INV-10045", # your own unique ID
}, timeout=30).json()
while order["status"] == "processing":
time.sleep(3)
order = s.get(f"{API}/orders/{order['id']}", timeout=30).json()
print(order["status"], order["delivery"])
<?php
$api = "https://corporative.oyunqurdu.az/api/v1";
$ch = curl_init("$api/orders");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("OQ_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"product" => "PUBGM-660-UC",
"fields" => ["player_id" => "5123456789"],
"reference" => "INV-10045",
]),
]);
$order = json_decode(curl_exec($ch), true);
echo $order["id"], " ", $order["status"];
const API = "https://corporative.oyunqurdu.az/api/v1";
const headers = {
"Authorization": `Bearer ${process.env.OQ_API_KEY}`,
"Content-Type": "application/json",
};
const res = await fetch(`${API}/orders`, {
method: "POST",
headers,
body: JSON.stringify({
product: "PUBGM-660-UC",
fields: { player_id: "5123456789" },
reference: "INV-10045",
}),
});
const order = await res.json();
console.log(order.id, order.status);
Kimlik yoxlaması
Hər sorğuya (GET /ping və GET /openapi.json istisna) API açarı lazımdır. Onu bearer token kimi göndərin:
Authorization: Bearer oqc_Ab3dE5fG7h_••••••••••••••••••••••••••••••••••••••••
X-API-Key: oqc_… başlığı da qəbul olunur.
- Açarlar
oqc_ilə başlayır. Bir neçə açarınız ola bilər (məs. hər server üçün biri) və hər birini ayrıca ləğv edə bilərsiniz. - Açar yalnız bir dəfə, yaradılanda göstərilir. Biz yalnız onun izini saxlayırıq; itmiş açarı bərpa etmək olmur — yenisini yaradın.
- Hər açarı məhdudlaşdırmaq olar: icazəli IP-lər, icazəli saytlar, gündəlik xərc həddi və ya yalnız oxuma (sifarişsiz).
- Açarları serverinizdə saxlayın. Onları heç vaxt mobil tətbiqə, brauzer koduna və ya açıq repozitoriyaya qoymayın.
Saytdan istifadə
Veb səhifənin içindəki açarı səhifəni açan hər kəs oxuya bilər. Onu öz domeninizə bağlamaq başqa saytların brauzerdə onu işlətməsinin qarşısını alır, amma açarı köçürən adam yenə də skriptdən sorğu göndərib özünü sizin sayt kimi göstərə bilər. Ona görə:
- Ən yaxşısı: API-ni serverinizdən (və ya kiçik proksidən) çağırın və açarı orada saxlayın. Səhifəniz serverinizlə, serveriniz bizimlə danışır.
- Google Sites-in öz serveri yoxdur və yerləşdirilmiş kod ortaq Google domenində (
*.googleusercontent.com) işləyir — domen kilidi sizin saytı başqasından ayıra bilmir. Proksi kimi Google Apps Script veb tətbiqi işlədin (aşağıdakı nümunə): açar Google serverlərində skriptin xüsusiyyətlərində qalır və brauzerə heç vaxt çatmır. - Brauzerdən çağırmaq məcburidirsə, saytın domenini açara əlavə edin (İcazəli saytlar), ona gündəlik xərc həddi qoyun və qiymətlər üçün ayrıca yalnız oxuma açarı düşünün. Saytları olan açar yalnız o saytlardan gələn sorğulara cavab verir; saytsız açar brauzer sorğularını ümumiyyətlə rədd edir.
// Extensions → Apps Script. Project Settings → Script properties → OQ_API_KEY = oqc_… // Deploy → New deployment → Web app. Embed its URL in Google Sites. const API = "https://corporative.oyunqurdu.az/api/v1"; const KEY = PropertiesService.getScriptProperties().getProperty("OQ_API_KEY"); function call_(method, path, body) { const res = UrlFetchApp.fetch(API + path, { method: method, contentType: "application/json", headers: { Authorization: "Bearer " + KEY }, payload: body ? JSON.stringify(body) : undefined, muteHttpExceptions: true, }); return JSON.parse(res.getContentText()); } function getPrice(code) { // google.script.run.getPrice("PUBGM-660-UC") return call_("get", "/products/" + encodeURIComponent(code) + "/quote"); } function placeOrder(code, playerId, reference) { // add your own checks here return call_("post", "/orders", { product: code, fields: { player_id: playerId }, reference: reference, }); } function doGet() { return HtmlService.createHtmlOutputFromFile("Index"); }
Sorğular və cavablar
- Pul iki onluq rəqəmli sətirdir (
"9.17") — heç vaxt float deyil. Valyuta hesabın valyutasıdır (USD). - Vaxt UTC-də ISO 8601-dir (
2026-10-24T09:12:44Z). - ID-lərin prefiksi var:
ord_sifarişlər,txn_balans hərəkətləri,evt_webhook hadisələri,acc_hesablar. - Siyahılar
pagevəper_page(ən çox 100) ilə səhifələnir vədatailəpaginationqaytarır. - Hər cavabda
X-Request-Idvar. Sorğu barədə bizə yazanda onu göndərin. - Sondakı «/» istəyə bağlıdır:
/ordersvə/orders/eynidir.
Səhvlər
Səhvlər standart HTTP kodları ilə gəlir və həmişə eyni formadadır. Məntiqdə error.code işlədin — mesaj dəyişə bilər.
{
"error": {
"code": "insufficient_balance",
"message": "Your balance is too low for this order.",
"retryable": false,
"details": {
"balance": "4.20",
"required": "9.17",
"shortfall": "4.97"
},
"doc_url": "https://corporative.oyunqurdu.az/docs/#error-insufficient_balance"
},
"request_id": "req_3f9a1c0b7d2e4a6b8c1d"
}Hər səhv həm də təkrarın kömək edib-etməyəcəyini (retryable) bildirir və aşağıdakı sətrinə keçid verir (doc_url).
| code | HTTP | Təkrar? | Nə deməkdir | Nə etməli |
|---|---|---|---|---|
| unauthorized | 401 | xeyr | API açarı göndərilməyib. | `Authorization: Bearer <key>` (və ya `X-API-Key`) göndərin. |
| malformed_api_key | 401 | xeyr | Bu dəyər API açarı deyil (açarlar `oqc_…_…` kimidir). | Açarı tam köçürün; boşluq və dırnaq olmasın. |
| invalid_api_key | 401 | xeyr | Belə API açarı yoxdur. | Açarı yoxlayın və ya paneldə yenisini yaradın. |
| api_key_revoked | 401 | xeyr | Açar ləğv edilib. | Paneldə yeni açar yaradın. |
| account_disabled | 403 | xeyr | Hesab bağlıdır. | Bizimlə əlaqə saxlayın. |
| ip_not_allowed | 403 | xeyr | Sorğu açarın icazəli IP siyahısında olmayan ünvandan gəlib. | IP-ni paneldə açara əlavə edin. `details.ip` gördüyümüz ünvandır. |
| origin_not_allowed | 403 | xeyr | Sorğu açarın sayt siyahısında olmayan saytdan gəlib. | Domeni paneldə açara əlavə edin. `details.origin` gördüyümüzdür. |
| key_read_only | 403 | xeyr | Bu açar oxuya bilir, amma sifariş verə bilmir. | Sifariş icazəsi olan açar işlədin və ya paneldə icazəni açın. |
| invalid_json | 400 | xeyr | Gövdə düzgün JSON obyekti deyil. | `Content-Type: application/json` ilə JSON obyekti göndərin. |
| unsupported_media_type | 415 | xeyr | Gövdə JSON deyil. | `Content-Type: application/json` yazın. |
| payload_too_large | 413 | xeyr | Sorğunun gövdəsi 32 KB-dan böyükdür. | Daha kiçik gövdə göndərin. |
| validation_error | 422 | xeyr | Bir və ya bir neçə parametr yoxdur və ya səhvdir. | `details` hər parametri `code` və `message` ilə sadalayır. |
| invalid_parameter | 422 | xeyr | Sorğu parametri səhvdir. | `details` parametri və icazəli dəyərləri göstərir. |
| invalid_fields | 422 | xeyr | Məhsul sahələri (oyunçu ID, server …) yoxdur və ya səhvdir. | `details.fields.<key>`-də `code` var: required, unknown_field, too_long, invalid_option, invalid_type, invalid_characters. |
| not_found | 404 | xeyr | Belə uc yoxdur. | Yolu və `/v1` prefiksini yoxlayın. |
| method_not_allowed | 405 | xeyr | Bu HTTP metodu burada dəstəklənmir. | `Allow` başlığına baxın. |
| product_not_found | 404 | xeyr | Bu kodla məhsul yoxdur. | Məhsulları `GET /products` ilə siyahılayın. |
| product_discontinued | 410 | xeyr | Məhsul artıq satılmır. | Onu kataloqunuzdan çıxarın; başqa məhsul seçin. |
| product_unavailable | 409 | bəli | Məhsul müvəqqəti əlçatmazdır. | Sonra yenidən cəhd edin və ya başqa məhsul təklif edin. |
| out_of_stock | 409 | bəli | İstənilən say üçün stok çatmır. | `details.available` qalan saydır. Sayı azaldın və ya sonra cəhd edin. |
| quantity_out_of_range | 422 | xeyr | Say məhsulun həddlərindən kənardadır. | `details`-də `min` və `max` var. |
| not_supported | 422 | xeyr | Bu əməliyyat bu məhsul üçün yoxdur. | |
| insufficient_balance | 402 | xeyr | Balansınız sifariş məbləğindən azdır. | Balansı artırın. `details`-də `balance`, `required` və `shortfall` var. |
| order_limit_exceeded | 422 | xeyr | Sifarişin məbləği hesabınızın bir sifariş həddindən yuxarıdır. | Sifarişi bölün və ya həddi qaldırmağımızı istəyin. `details`-də `total` və `limit` var. |
| key_daily_limit_exceeded | 422 | xeyr | Bu açar gündəlik xərc həddinə çatıb. | 00:00 UTC-ni gözləyin və ya paneldə açarın həddini qaldırın. |
| price_changed | 409 | bəli | Hazırkı qiymət `max_unit_price`-dan yüksəkdir. | Qiyməti yenidən oxuyun (`details.unit_price`) və təkrar edin. |
| duplicate_reference | 409 | xeyr | Bu `reference` başqa parametrlərlə artıq işlənib. | Yeni reference işlədin. `details.order_id` mövcud sifarişdir. |
| order_not_found | 404 | xeyr | Bu ID və ya reference ilə sifariş yoxdur. | ID-ni yoxlayın. |
| rate_limited | 429 | bəli | Bu dəqiqədə çox sorğu. | `Retry-After` saniyə gözləyin. |
| maintenance | 503 | bəli | Sifariş qəbulu qısa texniki iş üçün dayandırılıb. | `Retry-After` saniyədən sonra təkrar edin. Balansınızdan heç nə çıxılmayıb. Baxış işləyir. |
| service_unavailable | 503 | bəli | Xidmətin bir hissəsi müvəqqəti əlçatmazdır. | Sonra təkrar edin. Heç nə çıxılmayıb. |
| internal_error | 500 | bəli | Bizim tərəfdə gözlənilməz xəta. | Təkrar edin. Təkrarlansa, bizə `request_id`-ni göndərin. |
Sahə səhvləri
validation_error (gövdə parametrləri) və invalid_fields (məhsul sahələri) hər problemi ayrıca izah edir:
{
"error": {
"code": "invalid_fields",
"message": "Some product fields are missing or invalid.",
"retryable": false,
"details": {"fields": {
"server_id": {"code": "required", "message": "Server ID is required."},
"zone": {"code": "unknown_field", "message": "This product does not take `zone`. Expected: player_id, server_id."}
}},
"doc_url": "https://corporative.oyunqurdu.az/docs/#error-invalid_fields"
},
"request_id": "req_3f9a1c0b7d2e4a6b8c1d"
}POST /orders hər hansı səhv qaytarırsa, balansınıza toxunulmayıb. Pul yalnız sifariş yaradılanda hərəkət edir (201)."retryable": true olan səhvlər sonra uğurlu ola bilər — fasilə ilə təkrar edin (eyni reference ilə). Qalanları üçün əvvəlcə sizin tərəfdə dəyişiklik lazımdır.Texniki iş
Bəzən sifariş qəbulu qısa texniki iş üçün dayandırılır. Bu vaxt:
POST /ordersRetry-Afterbaşlığı (saniyə) ilə503 maintenanceqaytarır. Heç nə çıxılmır.- Qalan hər şey — məhsullar, qiymətlər, balans, sifarişlər, hərəkətlər — işləməyə davam edir.
GET /pingvəGET /accountbunuservice-də göstərir:
{"status": "ok", "version": "1.0.0",
"service": {"status": "maintenance", "message": "Ordering is paused for a short maintenance…", "retry_after": 300}}Adətən service.status operational-dır. Müştərilərinizə qısa «bir neçə dəqiqəyə yenidən cəhd edin» göstərin və Retry-After-dan sonra təkrar edin.
Sorğu limitləri
Hər hesabın dəqiqəlik sorğu limiti var (standart 120, bütün açarlar üçün ortaq). Hər cavab vəziyyəti göstərir:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 41 # seconds until the window resetsLimiti aşanda Retry-After başlığı ilə 429 rate_limited alırsınız. Daha çox lazımdır? Bizə yazın.
İdempotentlik
Şəbəkə qopur. POST /orders vaxtı keçsə, sifarişin yaradılıb-yaradılmadığını bilə bilməzsiniz — ona görə həmişə öz reference-inizi göndərin (faktura və ya əməliyyat nömrəniz, hər sifariş üçün unikal):
- Eyni
reference, eyni məhsul və say → mövcud sifarişi200vəIdempotent-Replayed: trueilə geri alırsınız. Heç nə iki dəfə çıxılmır. - Eyni
reference, fərqli parametrlər →409 duplicate_reference. - Sifarişi istənilən vaxt reference ilə tapın:
GET /orders/reference/{reference}.
Hesab və balans
Şirkətiniz, girişiniz, balansınız, sorğu limitiniz, webhook ayarlarınız və xidmətin vəziyyəti.
{"balance": "1250.40", "currency": "USD"}Balansı pulu köçürdükdən sonra komandamız artırır. Hər hərəkət balans hərəkətlərində görünür.
Kateqoriyalar
Oyunlar və xidmətlər, hər biri üçün mövcud regionlarla.
{"data": [
{"code": "free-fire", "name": "Free Fire", "regions": ["CIS", "EU", "ID"], "product_count": 37},
{"code": "steam", "name": "Steam Wallet", "regions": ["CIS", "GLOBAL"], "product_count": 29}
]}Məhsullar
Bütün məhsullar sizin qiymətlərinizlə. Kodlara baxmaq bazara sorğu tələb etmir — dərhal cavab verir. Süzgəclər:
| category | Kateqoriya kodu, məs. free-fire |
| region | GLOBAL, EU, TR, US, CIS, RU, ID |
| type | topup və ya code |
| q | Ad və kodda axtar |
| in_stock | true — yalnız indi sifariş verilə bilən məhsullar |
| live | true — kateqoriyanın qiymətini indi bazardan yenilə (category lazımdır) |
| page, per_page | Səhifələmə (per_page ≤ 100) |
{
"code": "PUBGM-660-UC",
"name": "PUBG Mobile — 660 UC",
"category": "pubg-mobile",
"category_name": "PUBG Mobile",
"region": "GLOBAL",
"type": "topup",
"price": "9.17",
"currency": "USD",
"in_stock": true,
"stock": null,
"min_quantity": 1,
"max_quantity": 10,
"fields": [
{
"key": "player_id",
"label": "Player ID",
"type": "text",
"required": true
}
],
"description": "Enter your Player ID before placing the order."
}| code | Sabit məhsul kodu. Heç vaxt dəyişmir — onu öz sisteminizdə saxlayın. |
| type | topup: fields-dəki oyun hesabına yazılır. code: hədiyyə kartı kodlarını alırsınız. |
| price | Vahid qiymətiniz, ən azı 15 dəqiqədə bir bazardan yenilənir (price_updated_at). İndiki dəqiq qiymət üçün quote ucunu işlədin. |
| stock | Kodlar üçün mövcud say. null — stokla məhdud deyil (yükləmələr). |
| min_quantity / max_quantity | Bir sifarişdə icazəli say. |
| fields | Sifarişdə fields-ə nə göndərməli — məs. player_id, server_id. Kodlar üçün boşdur. |
Canlı qiymətlər
Bazardan indiki qiyməti soruşur, marjanızı əlavə edir və vahid qiymətinizi və cəmi qaytarır. Qiyməti müştərinizə göstərməzdən əvvəl işlədin.
{
"product": "PUBGM-660-UC",
"name": "PUBG Mobile — 660 UC",
"quantity": 2,
"unit_price": "9.17",
"total": "18.34",
"currency": "USD",
"available": true,
"in_stock": true,
"stock": null,
"live": true,
"quoted_at": "2026-10-24T09:12:40Z",
"valid_until": "2026-10-24T09:13:40Z",
"hint": "Send max_unit_price with the order to lock this price."
}live: true— indi qiymətləndirilib.false— bazar cavab vermədi, bu son bilinən qiymətdir.- Qiymət dəyişə bilər. Sifarişlə
max_unit_price(quote-dakıunit_price) göndərin: arada qiymət qalxıbsa, sifariş409 price_changedilə rədd edilir və heç nə çıxılmır. - Bütöv kateqoriya üçün birdən:
GET /products?category=pubg-mobile&live=true.
Oyunçu ID-sini yoxlamaq
Yükləmə məhsulları üçün: müştəridən pul almazdan əvvəl oyun hesabını yoxlayın. Hər oyun bunu dəstəkləmir — onda 422 not_supported alırsınız.
POST /products/MLBB-GLOBAL-86-DIAMONDS/validate
{"fields": {"player_id": "123456789", "server_id": "2001"}}
→ {"valid": true, "player_name": "ShadowFox"}Sifariş yarat
| Sahə | Məcburi | Təsvir |
|---|---|---|
| product | bəli | Məhsul kodu. |
| quantity | xeyr | Standart 1. Məhsulun min/max həddləri daxilində olmalıdır. |
| fields | yükləmələr üçün | Məhsulun fields-indəki hər açarla obyekt. Naməlum açarlar rədd edilir. |
| reference | tövsiyə olunur | Sizin unikal nömrəniz (1–64 simvol: hərf, rəqəm, _ . : -). Sorğunu təkrar etməyi təhlükəsiz edir. |
| max_unit_price | xeyr | Vahid qiymət indi daha yüksəkdirsə, sifarişi 409 price_changed ilə rədd et. |
| callback_url | xeyr | Bu sifarişin webhook-larını alan https ünvanı — hesabın webhook-una əlavə olaraq. |
Məbləğ dərhal balansınızdan çıxılır və sifariş processing ilə başlayır:
{
"id": "ord_7kq2m9xw4hz8n3pd5t",
"reference": "INV-10045",
"status": "processing",
"status_message": "The order is being fulfilled.",
"product": {
"code": "STEAM-GLOBAL-20-USD",
"name": "Steam Wallet (Global) — 20 USD"
},
"quantity": 2,
"unit_price": "21.62",
"total": "43.24",
"currency": "USD",
"fields": {},
"delivery": null,
"failure": null,
"created_at": "2026-10-24T09:12:44Z",
"updated_at": "2026-10-24T09:12:47Z",
"completed_at": null
}Pulu çıxmazdan əvvəl canlı qiyməti, stoku və mövcudluğu yoxlayırıq. Biri tutmasa səhv alırsınız və heç nə çıxılmır.
Sifarişin həyat dövrü
| status | Son | Nə deməkdir |
|---|---|---|
| processing | xeyr | İcra olunur. Adətən saniyələr; bəzi yükləmələr bir neçə dəqiqə çəkir. |
| completed | bəli | Hazırdır. Kodlar delivery.codes-dadır; yükləmələrdə hesaba yazılıb. |
| failed | bəli | İcra oluna bilmədi. Məbləğ balansınıza qaytarıldı. failure.code-a baxın. |
| on_hold | xeyr | Komandamız əl ilə yoxlayır (nadir hal). completed və ya refunded olacaq — heç nə etmək lazım deyil. |
| refunded | bəli | Komandamız balansınıza qaytarıb. |
Tamamlanmış sifariş:
{
"id": "ord_7kq2m9xw4hz8n3pd5t",
"reference": "INV-10045",
"status": "completed",
"status_message": "The order is complete.",
"product": {
"code": "STEAM-GLOBAL-20-USD",
"name": "Steam Wallet (Global) — 20 USD"
},
"quantity": 2,
"unit_price": "21.62",
"total": "43.24",
"currency": "USD",
"fields": {},
"delivery": {
"type": "code",
"codes": [
"XXXXX-XXXXX-XXXXX",
"YYYYY-YYYYY-YYYYY"
],
"message": null
},
"failure": null,
"created_at": "2026-10-24T09:12:44Z",
"updated_at": "2026-10-24T09:12:47Z",
"completed_at": "2026-10-24T09:12:47Z"
}Uğursuzluq kodları
Sifariş failed ilə bitəndə failure.code səbəbi deyir. Məbləğ həmişə balansınıza qayıdır.
| failure.code | Nə deməkdir | Nə etməli |
|---|---|---|
| invalid_account_id | Oyun hesabının ID-si (və ya server) qəbul edilmədi. | Sahələri düzəldin və yeni sifariş verin. |
| out_of_stock | Sifariş icra olunana qədər stok bitdi. | Sonra təkrar edin. |
| product_unavailable | İcra zamanı məhsul əlçatmaz oldu. | Başqa məhsul təklif edin. |
| maintenance | Sifariş qəbulu qısa texniki iş üçün dayandırılmışdı. | Bir neçə dəqiqəyə təkrar edin. |
| fulfillment_failed | Sifariş icra oluna bilmədi. | Bir dəfə təkrar edin; yenə olsa bizə yazın. |
| refunded | Sifariş komandamız tərəfindən geri qaytarıldı. |
Sifarişləri almaq
Siyahı süzgəcləri: status, product, reference, from, to (ISO tarixlər), page, per_page. Ən yenisi birinci.
Sorğulama: processing olduqca hər 2–5 saniyədən bir yoxlayın; son vəziyyətdən sonra dayanın. Webhook-lar sizi sorğulamadan qurtarır.
Balans hərəkətləri
Hər balans hərəkəti, ən yenisi birinci, sonrakı balansla. Növlər: deposit, purchase, refund, adjustment, withdrawal. Süzgəclər: type, from, to.
{"data": [
{"id": "txn_k2m8q4w7x3z9n5p6r1", "type": "purchase", "amount": "-9.17",
"balance_after": "1241.23", "currency": "USD", "order_id": "ord_7kq2m9xw4hz8n3pd5t",
"description": "PUBGM-660-UC × 1", "created_at": "2026-10-24T09:12:44Z"}
], "pagination": {"page": 1, "per_page": 50, "total": 1, "total_pages": 1}}Webhook-lar
Webhook ünvanını paneldəki Webhook-lar səhifəsində (və ya hər sifariş üçün callback_url ilə) yazın. Sifarişin vəziyyəti dəyişəndə order.updated hadisəsini POST edirik:
POST https://your-server.example/oq/webhook
Content-Type: application/json
X-OQ-Event: order.updated
X-OQ-Delivery: evt_9w3k7m2q8x4z6n5p1r
X-OQ-Signature: t=1792833167,v1=5f1c…e9a2
{
"id": "evt_9w3k7m2q8x4z6n5p1r",
"type": "order.updated",
"created_at": "2026-10-24T09:12:47Z",
"data": {
"order": {
"id": "ord_7kq2m9xw4hz8n3pd5t",
"reference": "INV-10045",
"status": "completed",
"status_message": "The order is complete.",
"product": {
"code": "STEAM-GLOBAL-20-USD",
"name": "Steam Wallet (Global) — 20 USD"
},
"quantity": 2,
"unit_price": "21.62",
"total": "43.24",
"currency": "USD",
"fields": {},
"delivery": {
"type": "code",
"codes": [
"XXXXX-XXXXX-XXXXX",
"YYYYY-YYYYY-YYYYY"
],
"message": null
},
"failure": null,
"created_at": "2026-10-24T09:12:44Z",
"updated_at": "2026-10-24T09:12:47Z",
"completed_at": "2026-10-24T09:12:47Z"
}
}
}- 10 saniyə ərzində istənilən
2xxilə cavab verin. Əks halda 1 dəq, 5 dəq, 30 dəq, 2 saat və 6 saatdan sonra təkrar göndəririk. - Hadisələr bir neçə dəfə və qarışıq sırada gələ bilər.
data.order.statusvəupdated_atişlədin, sizdə artıq bitmiş sifarişlərin hadisələrini nəzərə almayın. X-OQ-DeliveryID-si hər hadisə üçün unikaldır — təkrarları atmaq üçün işlədin.
İmzanı yoxlamaq
Hər webhook imza açarınızla imzalanır (whsec_…, Webhook-lar səhifəsində). X-OQ-Signature belədir: t=<unix time>,v1=<hex>, burada hex = HMAC-SHA256(secret, "<t>.<raw request body>"). İmza uyğun gəlmirsə və ya t 5 dəqiqədən köhnədirsə sorğunu rədd edin.
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, sig = parts.get("t", ""), parts.get("v1", "")
if not t.isdigit() or abs(time.time() - int(t)) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
function oq_verify(string $rawBody, string $header, string $secret): bool {
parse_str(str_replace(",", "&", $header), $p);
if (!isset($p["t"], $p["v1"]) || abs(time() - (int)$p["t"]) > 300) return false;
$expected = hash_hmac("sha256", $p["t"] . "." . $rawBody, $secret);
return hash_equals($expected, $p["v1"]);
}
// $ok = oq_verify(file_get_contents("php://input"), $_SERVER["HTTP_X_OQ_SIGNATURE"], $secret);
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const p = Object.fromEntries(header.split(",").map(x => x.split("=")));
if (!p.t || Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${p.t}.`).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(p.v1 || ""));
}
// Express: app.post("/oq/webhook", express.raw({type: "application/json"}), …)
Tövsiyələr
- Həmişə
referencegöndərin. Vaxt keçəndə eyni reference ilə təkrar edin və ya sifarişi onunla tapın. - Ödənişdən dərhal əvvəl qiyməti
/quoteilə alın və onumax_unit_pricekimi göndərin. GET /products-u hər sifarişdən əvvəl çağırmaq əvəzinə bir neçə dəqiqəlik yadda saxlayın.- Mümkün olan yerdə oyunçu ID-lərini
/validateilə yoxlayın vəfieldsaçarlarını dəqiq köçürün. 503-ü müvəqqəti sayın — sonra təkrar edin; heç nə çıxılmayıb.- İstehsal açarları üçün hər server üçün ayrıca açar, IP siyahısı və gündəlik hədd işlədin.
POST /ordersüçün ən azı 30 saniyəlik gözləmə vaxtı saxlayın.
OpenAPI
Postman, Insomnia və ya kod generatorları üçün tam maşın oxunaqlı spesifikasiya (OpenAPI 3.1). İnteraktiv versiya API arayışı səhifəsindədir.
https://corporative.oyunqurdu.az/api/v1/openapi.json
Dəyişikliklər
| v1.0.0 | İlk buraxılış: məhsullar, canlı qiymətlər, sifarişlər, balans, hərəkətlər, webhook-lar, oyunçu ID yoxlaması, strukturlu səhvlər, texniki iş vəziyyəti, açar məhdudiyyətləri (IP-lər, saytlar, gündəlik hədd, yalnız oxuma). |