Oyun Qurdu Corporate API
Oyun yüklemelerini ve dijital hediye kartlarını toptan fiyatla alıp kendi ürününüzde teslim etmek için bir REST API. Siparişler hesabınızdaki ön ödemeli bakiyeden ödenir.
https://corporative.oyunqurdu.az/api/v1
- Tüm istek ve yanıtlar JSON, UTF-8'dir. API yanıtlarındaki mesajlar İngilizcedir, kodunuz bunlara güvenebilir.
- Yalnızca HTTPS. Düz HTTP yönlendirilir ve API çağrıları için kullanılmamalıdır.
- Sürüm yolun bir parçasıdır (
/v1). Sürümü değiştirmeden alan ekleriz; v1 içinde alanları asla kaldırmaz veya yeniden adlandırmayız. - İki tür ürün vardır: yükleme (doğrudan oyun hesabına yüklenir — oyuncu ID'sini gönderirsiniz) ve kod (hediye kartları — kodu yanıtta alırsınız).
Hızlı başlangıç
1. Giriş yapın ve API anahtarları sayfasında bir anahtar oluşturun. 2. Bakiyenizin sıfırın üzerinde olduğundan emin olun. 3. Bir ürün bulun, canlı fiyatını kontrol edin ve sipariş 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 doğrulama
Her isteğin (GET /ping ve GET /openapi.json hariç) bir API anahtarına ihtiyacı vardır. Bearer token olarak gönderin:
Authorization: Bearer oqc_Ab3dE5fG7h_••••••••••••••••••••••••••••••••••••••••
X-API-Key: oqc_… başlığı da kabul edilir.
- Anahtarlar
oqc_ile başlar. Birden fazla anahtarınız olabilir (örn. her sunucu için bir tane) ve her birini ayrı ayrı iptal edebilirsiniz. - Anahtar yalnızca bir kez, oluşturulduğunda gösterilir. Yalnızca parmak izini saklarız; kaybolan anahtar geri getirilemez — yenisini oluşturun.
- Her anahtar sınırlandırılabilir: izinli IP adresleri, izinli siteler, günlük harcama sınırı veya salt okunur (sipariş yok).
- Anahtarları sunucunuzda tutun. Asla bir mobil uygulamaya, tarayıcı koduna veya herkese açık bir depoya koymayın.
Bir web sitesinden kullanım
Bir web sayfasının içindeki anahtarı sayfayı açan herkes okuyabilir. Onu alan adınıza kısıtlamak diğer sitelerin tarayıcıda kullanmasını engeller, ancak anahtarı kopyalayan biri yine de bir betikten istek gönderip sizin siteniz gibi davranabilir. Bu yüzden:
- En iyisi: API'yi sunucunuzdan (veya küçük bir proxy'den) çağırın ve anahtarı orada tutun. Sayfanız sunucunuzla, sunucunuz bizimle konuşur.
- Google Sites'ın kendi sunucusu yoktur ve gömülü kod ortak bir Google alan adında (
*.googleusercontent.com) çalışır — alan adı kilidi sizin sitenizi diğerlerinden ayıramaz. Proxy olarak bir Google Apps Script web uygulaması kullanın (aşağıdaki örnek): anahtar, Google sunucularında betiğin özelliklerinde kalır ve tarayıcıya asla ulaşmaz. - Tarayıcıdan çağırmanız şartsa, sitenin alan adını anahtara ekleyin (İzinli siteler), ona bir günlük harcama sınırı verin ve fiyatlar için ayrı bir salt okunur anahtar düşünün. Siteleri olan bir anahtar yalnızca o sitelerden gelen isteklere yanıt verir; sitesi olmayan bir anahtar tarayıcı isteklerini tamamen reddeder.
// 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"); }
İstekler ve yanıtlar
- Para, iki ondalıklı bir dizedir (
"9.17") — asla float değildir. Para birimi hesabın para birimidir (USD). - Zamanlar UTC'de ISO 8601'dir (
2026-10-24T09:12:44Z). - ID'lerin önekleri vardır:
ord_siparişler,txn_işlemler,evt_webhook olayları,acc_hesaplar. - Listeler
pageveper_page(en fazla 100) ile sayfalanır vedatailepaginationdöndürür. - Her yanıtta
X-Request-Idbulunur. Bir istek hakkında bize yazarken bunu ekleyin. - Sondaki eğik çizgi isteğe bağlıdır:
/ordersve/orders/aynıdır.
Hatalar
Hatalar standart HTTP durum kodları kullanır ve her zaman aynı biçimdedir. Mantığınızda error.code kullanın — mesaj değişebilir.
{
"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"
}Her hata ayrıca tekrar denemenin işe yarayıp yaramayacağını (retryable) söyler ve aşağıdaki satırına bağlantı verir (doc_url).
| code | HTTP | Tekrar? | Anlamı | Ne yapmalı |
|---|---|---|---|---|
| unauthorized | 401 | hayır | API anahtarı gönderilmedi. | `Authorization: Bearer <key>` (veya `X-API-Key`) gönderin. |
| malformed_api_key | 401 | hayır | Bu değer bir API anahtarı değil (anahtarlar `oqc_…_…` biçimindedir). | Anahtarın tamamını kopyalayın; boşluk veya tırnak olmasın. |
| invalid_api_key | 401 | hayır | Böyle bir API anahtarı yok. | Anahtarı kontrol edin veya panelden yenisini oluşturun. |
| api_key_revoked | 401 | hayır | Anahtar iptal edildi. | Panelden yeni bir anahtar oluşturun. |
| account_disabled | 403 | hayır | Hesap devre dışı. | Bizimle iletişime geçin. |
| ip_not_allowed | 403 | hayır | İstek, anahtarın izin listesinde olmayan bir IP'den geldi. | IP'yi panelden anahtara ekleyin. `details.ip` gördüğümüz adrestir. |
| origin_not_allowed | 403 | hayır | İstek, anahtarın site listesinde olmayan bir siteden geldi. | Alan adını panelden anahtara ekleyin. `details.origin` gördüğümüzdür. |
| key_read_only | 403 | hayır | Bu anahtar okuyabilir ama sipariş veremez. | Sipariş izni olan bir anahtar kullanın veya panelden izni açın. |
| invalid_json | 400 | hayır | Gövde geçerli bir JSON nesnesi değil. | `Content-Type: application/json` ile bir JSON nesnesi gönderin. |
| unsupported_media_type | 415 | hayır | Gövde JSON değil. | `Content-Type: application/json` ayarlayın. |
| payload_too_large | 413 | hayır | İstek gövdesi 32 KB'tan büyük. | Daha küçük bir gövde gönderin. |
| validation_error | 422 | hayır | Bir veya birkaç parametre eksik ya da hatalı. | `details` her parametreyi `code` ve `message` ile listeler. |
| invalid_parameter | 422 | hayır | Bir sorgu parametresi hatalı. | `details` parametreyi ve izin verilen değerleri gösterir. |
| invalid_fields | 422 | hayır | Ürün alanları (oyuncu ID, sunucu …) eksik veya hatalı. | `details.fields.<key>` içinde `code` vardır: required, unknown_field, too_long, invalid_option, invalid_type, invalid_characters. |
| not_found | 404 | hayır | Böyle bir uç nokta yok. | Yolu ve `/v1` önekini kontrol edin. |
| method_not_allowed | 405 | hayır | Bu HTTP metodu burada desteklenmiyor. | `Allow` başlığına bakın. |
| product_not_found | 404 | hayır | Bu kodla bir ürün yok. | Ürünleri `GET /products` ile listeleyin. |
| product_discontinued | 410 | hayır | Ürün artık satılmıyor. | Kataloğunuzdan çıkarın; başka bir ürün seçin. |
| product_unavailable | 409 | evet | Ürün geçici olarak kullanılamıyor. | Daha sonra tekrar deneyin veya başka bir ürün sunun. |
| out_of_stock | 409 | evet | İstenen miktar için yeterli stok yok. | `details.available` kalan miktardır. Miktarı azaltın veya sonra deneyin. |
| quantity_out_of_range | 422 | hayır | Miktar ürünün sınırlarının dışında. | `details` içinde `min` ve `max` vardır. |
| not_supported | 422 | hayır | Bu işlem bu ürün için kullanılamaz. | |
| insufficient_balance | 402 | hayır | Bakiyeniz sipariş tutarından az. | Bakiye yükleyin. `details` içinde `balance`, `required` ve `shortfall` vardır. |
| order_limit_exceeded | 422 | hayır | Sipariş tutarı hesabınızın sipariş başına sınırının üzerinde. | Siparişi bölün veya sınırı yükseltmemizi isteyin. `details` içinde `total` ve `limit` vardır. |
| key_daily_limit_exceeded | 422 | hayır | Bu anahtar günlük harcama sınırına ulaştı. | 00:00 UTC'yi bekleyin veya panelden anahtarın sınırını yükseltin. |
| price_changed | 409 | evet | Güncel fiyat `max_unit_price` değerinden yüksek. | Fiyatı yeniden okuyun (`details.unit_price`) ve tekrar deneyin. |
| duplicate_reference | 409 | hayır | Bu `reference` farklı parametrelerle zaten kullanıldı. | Yeni bir reference kullanın. `details.order_id` mevcut siparişdir. |
| order_not_found | 404 | hayır | Bu ID veya reference ile bir sipariş yok. | ID'yi kontrol edin. |
| rate_limited | 429 | evet | Bu dakikada çok fazla istek. | `Retry-After` saniye bekleyin. |
| maintenance | 503 | evet | Sipariş alımı kısa bir bakım için durduruldu. | `Retry-After` saniye sonra tekrar deneyin. Bakiyenizden bir şey düşülmedi. Görüntüleme çalışır. |
| service_unavailable | 503 | evet | Hizmetin bir bölümü geçici olarak kullanılamıyor. | Daha sonra tekrar deneyin. Hiçbir şey düşülmedi. |
| internal_error | 500 | evet | Bizim tarafımızda beklenmeyen bir hata. | Tekrar deneyin. Tekrarlanırsa bize `request_id` gönderin. |
Alan hataları
validation_error (gövde parametreleri) ve invalid_fields (ürün alanları) her sorunu ayrı ayrı açıklar:
{
"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 herhangi bir hata döndürürse bakiyenize dokunulmamıştır. Para yalnızca sipariş oluşturulduğunda hareket eder (201)."retryable": true olan hatalar daha sonra başarılı olabilir — beklemeli olarak tekrar deneyin (aynı reference ile). Diğerleri önce sizin tarafınızda bir değişiklik gerektirir.Bakım
Zaman zaman sipariş alımı kısa bir bakım için durdurulur. Bu süre zarfında:
POST /orders,Retry-Afterbaşlığıyla (saniye)503 maintenancedöndürür. Hiçbir ücret alınmaz.- Diğer her şey — ürünler, fiyatlar, bakiye, siparişler, işlemler — çalışmaya devam eder.
GET /pingveGET /accountbunuserviceiçinde gösterir:
{"status": "ok", "version": "1.0.0",
"service": {"status": "maintenance", "message": "Ordering is paused for a short maintenance…", "retry_after": 300}}Normalde service.status operational olur. Müşterilerinize kısa bir «birkaç dakika sonra tekrar deneyin» gösterin ve Retry-After sonrasında tekrar deneyin.
İstek limitleri
Her hesabın dakikalık bir istek limiti vardır (varsayılan 120, tüm anahtarlar arasında ortak). Her yanıt nerede olduğunuzu gösterir:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 41 # seconds until the window resetsLimiti aşarsanız Retry-After başlığıyla 429 rate_limited alırsınız. Daha fazlası mı lazım? Bize yazın.
İdempotentlik
Ağlar çöker. Bir POST /orders zaman aşımına uğrarsa siparişin oluşturulup oluşturulmadığını bilemezsiniz — bu yüzden her zaman kendi reference değerinizi gönderin (fatura veya işlem numaranız, sipariş başına benzersiz):
- Aynı
reference, aynı ürün ve miktar → mevcut siparişi200veIdempotent-Replayed: trueile geri alırsınız. Hiçbir şey iki kez ücretlendirilmez. - Aynı
reference, farklı parametreler →409 duplicate_reference. - Siparişi istediğiniz zaman reference ile bulun:
GET /orders/reference/{reference}.
Hesap ve bakiye
Şirketiniz, girişiniz, bakiyeniz, istek limitiniz, webhook ayarlarınız ve hizmet durumu.
{"balance": "1250.40", "currency": "USD"}Bakiye, para transferinizden sonra ekibimiz tarafından yüklenir. Her hareket işlemler altında listelenir.
Kategoriler
Oyunlar ve hizmetler, her biri için mevcut bölgelerle.
{"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}
]}Ürünler
Tüm ürünler sizin fiyatlarınızla. Kodlara bakmak piyasaya istek gerektirmez — anında yanıt verir. Filtreler:
| category | Kategori kodu, örn. free-fire |
| region | GLOBAL, EU, TR, US, CIS, RU, ID |
| type | topup veya code |
| q | Ad ve kodda ara |
| in_stock | true — yalnızca şu an sipariş verilebilen ürünler |
| live | true — kategorinin fiyatını şimdi piyasadan yenile (category gerekir) |
| page, per_page | Sayfalama (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 ürün kodu. Asla değişmez — sisteminizde saklayın. |
| type | topup: fields içindeki oyun hesabına yüklenir. code: hediye kartı kodlarını alırsınız. |
| price | Birim fiyatınız, en az 15 dakikada bir piyasadan yenilenir (price_updated_at). Tam şu anki fiyat için quote uç noktasını kullanın. |
| stock | Kodlar için mevcut adet. null stokla sınırlı olmadığı anlamına gelir (yüklemeler). |
| min_quantity / max_quantity | Sipariş başına izin verilen miktar. |
| fields | Siparişte fields içine ne göndermeniz gerektiği — örn. player_id, server_id. Kodlar için boştur. |
Canlı fiyatlar
Piyasaya fiyatı tam şu an sorar, marjınızı ekler ve birim fiyatınızı ve toplamı döndürür. Fiyatı müşterinize göstermeden hemen önce kullanın.
{
"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— şu an fiyatlandı.false— piyasa yanıt vermedi, bu bilinen son fiyattır.- Fiyatlar değişebilir. Siparişle birlikte
max_unit_price(teklif edilenunit_price) gönderin: arada fiyat yükseldiyse sipariş409 price_changedile reddedilir ve hiçbir ücret alınmaz. - Bütün bir kategori için tek seferde:
GET /products?category=pubg-mobile&live=true.
Oyuncu ID'sini doğrulama
Yükleme ürünleri için: müşterinizden ücret almadan önce oyun hesabını kontrol edin. Her oyun desteklemez — o zaman 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"}Sipariş oluştur
| Alan | Zorunlu | Açıklama |
|---|---|---|
| product | evet | Ürün kodu. |
| quantity | hayır | Varsayılan 1. Ürünün min/max değerleri içinde olmalıdır. |
| fields | yüklemeler için | Ürünün fields içindeki her anahtarı içeren nesne. Bilinmeyen anahtarlar reddedilir. |
| reference | önerilir | Benzersiz kimliğiniz (1–64 karakter: harf, rakam, _ . : -). İsteği tekrar denemeyi güvenli kılar. |
| max_unit_price | hayır | Birim fiyat artık daha yüksekse siparişi 409 price_changed ile reddet. |
| callback_url | hayır | Hesap webhook'unuza ek olarak bu siparişin webhook'larını alan https adresi. |
Toplam hemen bakiyenizden düşülür ve sipariş processing olarak başlar:
{
"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
}Ücret almadan önce canlı fiyatı, stoku ve kullanılabilirliği kontrol ederiz. Herhangi biri başarısız olursa bir hata alırsınız ve hiçbir ücret alınmaz.
Sipariş yaşam döngüsü
| status | Son | Anlamı |
|---|---|---|
| processing | hayır | İşleniyor. Genellikle saniyeler; bazı yüklemeler birkaç dakika sürer. |
| completed | evet | Tamam. Kodlar delivery.codes içindedir; yüklemelerde hesaba yüklendi. |
| failed | evet | Tamamlanamadı. Tutar bakiyenize iade edildi. failure.code değerine bakın. |
| on_hold | hayır | Ekibimiz tarafından manuel incelemede (nadir). completed veya refunded durumuna geçecek — işlem yapmanıza gerek yok. |
| refunded | evet | Ekibimiz tarafından bakiyenize iade edildi. |
Tamamlanmış bir sipariş:
{
"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"
}Başarısızlık kodları
Bir sipariş failed ile bittiğinde failure.code nedenini söyler. Tutar her zaman bakiyenize geri döner.
| failure.code | Anlamı | Ne yapmalı |
|---|---|---|
| invalid_account_id | Oyun hesabı ID'si (veya sunucu) kabul edilmedi. | Alanları düzeltin ve yeni bir sipariş verin. |
| out_of_stock | Sipariş tamamlanmadan stok tükendi. | Daha sonra tekrar deneyin. |
| product_unavailable | İşlem sırasında ürün kullanılamaz hale geldi. | Başka bir ürün sunun. |
| maintenance | Sipariş alımı kısa bir bakım için durdurulmuştu. | Birkaç dakika sonra tekrar deneyin. |
| fulfillment_failed | Sipariş tamamlanamadı. | Bir kez tekrar deneyin; yine olursa bize yazın. |
| refunded | Sipariş ekibimiz tarafından iade edildi. |
Siparişleri getirme
Liste filtreleri: status, product, reference, from, to (ISO tarihler), page, per_page. En yenisi önce.
Yoklama: processing iken her 2–5 saniyede bir kontrol edin; son durumdan sonra durun. Webhook'lar sizi yoklamadan kurtarır.
İşlemler
Her bakiye hareketi, en yenisi önce, sonraki bakiyeyle birlikte. Türler: deposit, purchase, refund, adjustment, withdrawal. Filtreler: 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 URL'sini paneldeki Webhook'lar sayfasında (veya sipariş başına callback_url ile) ayarlayın. Bir siparişin durumu değiştiğinde bir order.updated olayını POST ederiz:
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 saniye içinde herhangi bir
2xxile yanıt verin. Aksi halde 1 dk, 5 dk, 30 dk, 2 sa ve 6 sa sonra tekrar deneriz. - Olaylar birden fazla kez ve sırasız gelebilir.
data.order.statusveupdated_atkullanın, sisteminizde zaten sonlanmış siparişlerin olaylarını yok sayın. X-OQ-DeliveryID'si her olay için benzersizdir — tekrarları atmak için kullanın.
İmzaları doğrulama
Her webhook imza anahtarınızla imzalanır (whsec_…, Webhook'lar sayfasında). X-OQ-Signature şöyledir: t=<unix time>,v1=<hex>, burada hex = HMAC-SHA256(secret, "<t>.<raw request body>"). İmza eşleşmiyorsa veya t 5 dakikadan eskiyse isteği reddedin.
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"}), …)
En iyi uygulamalar
- Her zaman bir
referencegönderin. Zaman aşımında aynı reference ile tekrar deneyin veya siparişi onunla bulun. - Ödemeden hemen önce fiyatı
/quoteile alın vemax_unit_priceolarak gönderin. GET /productsçağrısını her siparişten önce yapmak yerine birkaç dakika önbellekte tutun.- Desteklenen yerlerde oyuncu ID'lerini
/validateile doğrulayın vefieldsanahtarlarını aynen kopyalayın. 503değerini geçici sayın — daha sonra tekrar deneyin; hiçbir ücret alınmadı.- Üretim anahtarları için her sunucuya ayrı bir anahtar, IP izin listesi ve günlük sınır kullanın.
POST /ordersiçin en az 30 saniyelik bir zaman aşımı tutun.
OpenAPI
Postman, Insomnia veya kod üreteçleri için makine tarafından okunabilir tam şartname (OpenAPI 3.1). Etkileşimli sürümü API referansı sayfasındadır.
https://corporative.oyunqurdu.az/api/v1/openapi.json
Değişiklik günlüğü
| v1.0.0 | İlk sürüm: ürünler, canlı fiyatlar, siparişler, bakiye, işlemler, webhook'lar, oyuncu ID doğrulaması, yapılandırılmış hatalar, bakım durumu, anahtar kısıtlamaları (IP'ler, siteler, günlük sınır, salt okunur). |