واجهة D2Bulk البرمجية للريسيلرزD2Bulk Reseller API
اربط متجرك مباشرة بكتالوج D2Bulk ورصيدك ونظام الطلبات. تصفّح المنتجات، تحقّق من رصيدك، نفّذ الطلبات، واستلم أكواد التفعيل فوراً — كل ذلك عبر واجهة HTTPS بسيطة وموقّعة. Connect your store directly to D2Bulk's catalog, balance and ordering system. Browse products, check your balance, place orders and receive redemption codes instantly — all over a simple, signed HTTPS API.
https://api.d2bulk.com/api/v1
نظرة عامةOverview
تعكس واجهة الريسيلر البرمجية بالضبط ما يمكنك فعله بالفعل داخل بوت D2Bulk على تيليجرام — تصفّح المنتجات، والتحقق من رصيدك، والشراء — لكن في صورة واجهة HTTP صغيرة وموقّعة يمكنك استدعاؤها من متجرك أو نظامك الخاص. The Reseller API mirrors exactly what you can already do inside the D2Bulk Telegram bot — browsing products, checking your balance and buying — but as a small, signed HTTP API you can call from your own store or backend.
- جميع الطلبات والردود بصيغة JSON.All requests and responses are JSON.
- يجب أن تُرسَل جميع الطلبات عبر HTTPS؛ يُرفض أي طلب عبر HTTP العادي.All requests must use HTTPS. Plain HTTP requests are rejected.
- تُوثَّق كل عملية طلب بمفتاح API وتوقيع HMAC-SHA256 — بلا جلسات وبلا ملفات تعريف ارتباط (cookies).Every request is authenticated with an API key + HMAC-SHA256 signature — no session, no cookies.
- تُنفَّذ الطلبات خصماً من رصيد حسابك في D2Bulk، بسعر الريسيلر الخاص بك.Orders are placed against your own D2Bulk account balance, at your reseller pricing.
الحصول على مفتاح الـ APIGetting your API key
بلا نموذج تسجيل، وبلا انتظار موافقة — يُولَّد مفتاحك فوراً من داخل بوت D2Bulk. No sign-up form, no waiting for approval — your key is generated instantly from inside the D2Bulk bot.
-
افتح بوت D2Bulk على تيليجرام وادخل على
💳 My Account←✏️ API. Open the D2Bulk bot on Telegram and go to💳 My Account→✏️ API. -
اضغط "🔑 Generate API Key". يتم إنشاء
api_keyوapi_secretفوراً ويظهران لك مباشرة. Tap "🔑 Generate API Key". Yourapi_keyandapi_secretare created and shown to you right there. - احتفظ بالسر في مكان آمن. يمكنك رؤيته مرة أخرى في أي وقت تفتح فيه هذه الشاشة (إلى أن تُجدِّده)، لكن تعامل معه كأنه كلمة مرور — فأي شخص يملكه يستطيع تنفيذ طلبات باستخدام رصيدك. Store the secret safely. It's shown to you again any time you reopen that screen (until you regenerate), but treat it like a password — anyone who has it can place orders using your balance.
-
اختياري: استخدم
🛡️ IP Filterمن نفس الشاشة لتقييد المفتاح بعنوان (أو عناوين) IP الخاصة بسيرفرك. راجع تقييد عناوين IP. Optional: use🛡️ IP Filteron the same screen to restrict the key to your server's IP address(es). See IP filtering.
المصادقةAuthentication
يُوقَّع كل طلب بخوارزمية HMAC-SHA256 على نص ثابت، باستخدام api_secret الخاص بك. لا توجد رموز دخول (bearer tokens) ولا جلسات تحتاج لإدارتها.
Every request is signed with HMAC-SHA256 over a fixed string, using your api_secret. There are no bearer tokens or sessions to manage.
الترويسات (Headers) المطلوبةRequired headers
| الترويسةHeader | الوصفDescription |
|---|---|
X-API-KEY | مفتاحك العام (api_key).Your public api_key. |
X-TIMESTAMP | الوقت الحالي بصيغة Unix timestamp بالثواني. يجب ألا يتجاوز الفارق 60 ثانية عن وقت السيرفر.Current Unix time in seconds. Must be within 60 seconds of server time. |
X-SIGNATURE | توقيع HMAC-SHA256 بصيغة hex — التفاصيل أدناه.Hex-encoded HMAC-SHA256 signature — see below. |
معادلة التوقيعSigning formula
// The exact string you sign, in order, separated by "\n":
string_to_sign = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + RAW_BODY
signature = hex(HMAC_SHA256(api_secret, string_to_sign))
METHOD— طريقة HTTP بحروف كبيرة، مثلGETأوPOST.METHOD— uppercase HTTP method, e.g.GETorPOST.PATH— مسار الطلب بعد/api/v1، مع الشرطة المائلة في البداية وبدون query string. مثال: من أجلhttps://api.d2bulk.com/api/v1/orders/D2B-1A2B3Cيكون التوقيع على/orders/D2B-1A2B3C.PATH— the request path after/api/v1, including the leading slash and no query string. E.g. forhttps://api.d2bulk.com/api/v1/orders/D2B-1A2B3C, sign/orders/D2B-1A2B3C.TIMESTAMP— نفس القيمة المُرسَلة فيX-TIMESTAMP.TIMESTAMP— the same value sent inX-TIMESTAMP.RAW_BODY— نص جسم الطلب (body) كما أُرسل بالضبط. نص فارغ في حالة طلبات GET.RAW_BODY— the exact raw request body as sent. Empty string for GET requests.
function signRequest(string $method, string $path, string $body, string $apiKey, string $apiSecret): array {
$timestamp = (string) time();
$stringToSign = $method . "\n" . $path . "\n" . $timestamp . "\n" . $body;
$signature = hash_hmac('sha256', $stringToSign, $apiSecret);
return [
'X-API-KEY' => $apiKey,
'X-TIMESTAMP' => $timestamp,
'X-SIGNATURE' => $signature,
];
}import time, hmac, hashlib
def sign_request(method, path, body, api_key, api_secret):
timestamp = str(int(time.time()))
string_to_sign = f"{method}\n{path}\n{timestamp}\n{body}"
signature = hmac.new(api_secret.encode(), string_to_sign.encode(), hashlib.sha256).hexdigest()
return {
"X-API-KEY": api_key,
"X-TIMESTAMP": timestamp,
"X-SIGNATURE": signature,
}# Example for GET /balance (empty body) — replace TS/SIG with real values
TS=$(date +%s)
BODY=""
SIG=$(printf "GET\n/balance\n%s\n%s" "$TS" "$BODY" | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.* //')
curl https://api.d2bulk.com/api/v1/balance \
-H "X-API-KEY: $API_KEY" \
-H "X-TIMESTAMP: $TS" \
-H "X-SIGNATURE: $SIG"نقاط الوصولEndpoints
جميع نقاط الوصول أدناه نسبية إلى الرابط الأساسي https://api.d2bulk.com/api/v1، وتتطلب ترويسات المصادقة الموضحة أعلاه.
All endpoints below are relative to the base URL https://api.d2bulk.com/api/v1 and require the authentication headers described above.
/catalogيُرجع كل المنتجات المتاحة لك، مُسعَّرة بسعر الريسيلر الخاص بك.Returns every product available to you, priced at your reseller rate.
{
"ok": true,
"data": {
"products": [
{
"id": 1024,
"name": "Google Play Gift Card 10 USD",
"price": 9.20,
"stock": 137,
"currency": "USD"
}
]
}
}
تعكس الأسعار سعر الريسيلر الخاص بك إن كان مُفعَّلاً لحسابك، وإلا فالسعر العادي.Prices reflect your reseller rate where one is configured for you, otherwise the standard price.
/balanceيُرجع رصيدك الحالي في محفظة D2Bulk.Returns your current D2Bulk wallet balance.
{ "ok": true, "data": { "balance": 482.50, "currency": "USD" } }
/ordersيُنفِّذ طلباً، وعند نجاحه يُعيد كود (أو أكواد) التفعيل فوراً — بنفس آلية التسليم الفوري المتاحة في البوت.Places an order and, on success, returns the redemption code(s) immediately — the same instant delivery you get in the bot.
محتوى الطلبRequest body
| الحقلField | النوعType | الوصفDescription | |
|---|---|---|---|
product_id | integer | إلزاميrequired | من GET /catalog.From GET /catalog. |
quantity | integer | إلزاميrequired | يجب ألا تتجاوز الكمية المتوفرة.Must not exceed available stock. |
idempotency_key | string | إلزاميrequired | مفتاح فريد لكل محاولة طلب (مثل UUID) — التفاصيل أدناه.A unique key per order attempt (e.g. a UUID) — see below. |
reseller_ref | string | اختياريoptional | معرّف داخلي خاص بك للطلب، يُعاد إليك كما هو لسجلاتك.Your own internal order/reference id, echoed back for your records. |
idempotency_key فريداً لكل محاولة طلب.Always send a unique idempotency_key per order attempt.
إذا أُعيد إرسال الطلب بنفس المفتاح (مثلاً بعد انقطاع الاتصال)، يُعيد D2Bulk نفس النتيجة الأصلية بدلاً من خصم المبلغ أو تسليم المنتج مرتين.
If a request is retried with the same key (e.g. after a network timeout), D2Bulk returns the original result instead of charging you or delivering the product twice.
{
"idempotency_key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"product_id": 1024,
"quantity": 2,
"reseller_ref": "order-88213"
}
الرد (200)Response (200)
{
"ok": true,
"data": {
"order_ref": "D2B-9F3A21",
"status": "delivered",
"total": 18.40,
"codes": ["XXXX-XXXX-XXXX", "YYYY-YYYY-YYYY"]
}
}
إذا كان المنتج من مورّد خارجي، قد يعود الطلب بحالة status: "pending" بدون حقل codes بعد — تابع الحالة عبر GET /orders/{id} أو من خلال إشعار order.delivered.
If the product is sourced from an upstream supplier, an order can come back as status: "pending" with no codes field yet — poll GET /orders/{id} or listen for the order.delivered webhook.
/orders/{id}يبحث عن طلب واحد باستخدام order_ref الخاص به.Looks up a single order by its order_ref.
{ "ok": true, "data": { "order_ref": "D2B-9F3A21", "status": "delivered", "codes": ["XXXX-XXXX-XXXX"] } }
القيم الممكنة لحالة status: pending (قيد الانتظار) · processing (قيد المعالجة) · delivered (تم التسليم) · failed (فشل) · refunded (مُسترجَع).
Possible status values: pending · processing · delivered · failed · refunded.
/ordersيعرض طلباتك السابقة، الأحدث أولاً.Lists your past orders, newest first.
| معامل الاستعلامQuery param | الافتراضيDefault | الوصفDescription |
|---|---|---|
page | 1 | رقم الصفحة.Page number. |
limit | 50 | عدد النتائج في كل صفحة.Results per page. |
from | — | تصفية بحسب التاريخ، بصيغة YYYY-MM-DD.Filter, YYYY-MM-DD. |
to | — | تصفية بحسب التاريخ، بصيغة YYYY-MM-DD.Filter, YYYY-MM-DD. |
GET /orders?page=1&limit=20&from=2026-09-01
شحن الألعاب (Game Top-Ups)Game Top-Ups
نقاط وصول منفصلة تماماً لشحن حسابات الألعاب (UC، شدة، جواهر، إلخ) بدلاً من بطاقات الكود. لا يوجد product_id/quantity هنا — تُحدَّد الباقة عبر service_id، وكل طلب يعني شحنة واحدة لـ Player ID واحد (شحن باقتين يعني طلبين منفصلين، كل واحد بـ idempotency_key مستقل). الأسعار واحدة للجميع — نفس سعر عملاء البوت، بدون سعر ريسيلر مخفّض لهذا النوع من المنتجات.
A completely separate set of endpoints for topping up game accounts (UC, Diamonds, etc.) rather than redeeming code cards. There's no product_id/quantity here — a package is addressed by its service_id, and every order means exactly one top-up for one Player ID (topping up two packages means two separate orders, each with its own idempotency_key). Pricing is the same for everyone — resellers pay the exact same price a bot customer pays; there's no discounted reseller rate for this product type.
/topup/catalogيُرجع كل باقات الشحن المتاحة والمُفعَّلة حالياً.Returns every currently active, priced top-up package.
| معامل الاستعلامQuery param | الوصفDescription |
|---|---|
game | اختياري — تصفية بحسب لعبة واحدة (مثل pubg). بدون هذا المعامل تُعاد كل الألعاب.Optional — filter to a single game (e.g. pubg). Omit to get every game. |
{
"ok": true,
"data": {
"packages": [
{
"service_id": "10",
"game": "pubg",
"game_label": "PUBGM UC & Items",
"category": "UC Packages",
"name": "60 UC",
"price": 1.03,
"currency": "USD"
}
]
}
}
/topup/ordersيُنفِّذ طلب شحن لباقة ولاعب مُحدَّدين. على عكس POST /orders، لا يعود الرد بحالة نهائية فوراً — الموردّ لا يؤكد النجاح أو الفشل لحظة الطلب، فتبدأ كل الطلبات بحالة processing ثم تُحدَّث تلقائياً (عبر Webhook أو بالاستعلام) عند استقرار النتيجة.Places a top-up order for a specific package and player. Unlike POST /orders, the response is never final right away — the upstream supplier doesn't confirm success or failure at request time, so every order starts as processing and is updated automatically (via webhook or by polling) once it resolves.
محتوى الطلبRequest body
| الحقلField | النوعType | الوصفDescription | |
|---|---|---|---|
service_id | string | إلزاميrequired | من GET /topup/catalog.From GET /topup/catalog. |
player_id | string | إلزاميrequired | معرّف اللاعب الذي يُشحَن إليه — قد يكون Player ID فقط، أو Player ID|Server ID|Character Name حسب ما تتطلبه هذه الباقة بالتحديد.The player being topped up — may be just a Player ID, or Player ID|Server ID|Character Name, whatever this specific package needs. |
idempotency_key | string | إلزاميrequired | مفتاح فريد لكل محاولة طلب — نفس المبدأ المُستخدم في POST /orders.A unique key per order attempt — same principle as POST /orders. |
reseller_ref | string | اختياريoptional | معرّف داخلي خاص بك، يُعاد إليك كما هو.Your own internal reference id, echoed back as-is. |
{
"service_id": "10",
"player_id": "123456789",
"idempotency_key": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}
الرد (200)Response (200)
{
"ok": true,
"data": {
"order_id": "TOPUP-28174029C9-1791065940",
"status": "processing",
"total_price": 1.03,
"player_id": "123456789",
"created_at": "2026-10-03T22:19:00Z"
}
}
processing — راجع GET /topup/orders/{id} أو اشترك في Webhooks (الإشعارات الفورية) لمعرفة النتيجة النهائية.
Every top-up order starts as processing — poll GET /topup/orders/{id} or subscribe to webhooks to learn the final outcome.
/topup/orders/{id}يبحث عن طلب شحن واحد باستخدام order_id الخاص به.Looks up a single top-up order by its order_id.
{ "ok": true, "data": { "order_id": "TOPUP-28174029C9-1791065940", "status": "delivered", "total_price": 1.03, "player_id": "123456789", "created_at": "2026-10-03 22:19:00" } }
القيم الممكنة لحالة status: processing (قيد المعالجة — لم تُحسَم النتيجة بعد) · delivered (تم الشحن بنجاح) · failed (فشل الطلب — ويُرَد المبلغ إلى رصيدك تلقائياً، ويظهر السبب في error_message).
Possible status values: processing (not resolved yet) · delivered (topped up successfully) · failed (order failed — your balance is refunded automatically, and the reason is in error_message).
/topup/ordersيعرض طلبات الشحن السابقة الخاصة بك، الأحدث أولاً. نفس معاملات GET /orders بالضبط (page, limit, from, to).Lists your past top-up orders, newest first. Exactly the same query params as GET /orders (page, limit, from, to).
GET /topup/orders?page=1&limit=20
رموز الأخطاءError codes
يتّبع كل رد خطأ نفس الشكل:Every error response follows the same shape:
{ "ok": false, "error": { "code": "insufficient_balance", "message": "..." } }
| HTTP | Code | المعنىMeaning |
|---|---|---|
| 400 | invalid_request | حقل مفقود أو بصيغة خاطئة.Missing or malformed field. |
| 401 | invalid_signature | التوقيع غير مطابق، أو مفتاح API غير معروف.Signature doesn't match, or unknown API key. |
| 401 | timestamp_expired | فارق X-TIMESTAMP عن وقت السيرفر أكثر من 60 ثانية.X-TIMESTAMP more than 60s off server time. |
| 403 | account_suspended | حساب الريسيلر الخاص بك موقوف.Your reseller account is suspended. |
| 403 | ip_not_allowed | الطلب وصل من عنوان IP غير مسموح به لهذا المفتاح.Request came from an IP not on your key's allow-list. |
| 404 | product_not_found | product_id غير معروف.Unknown product_id. |
| 404 | order_not_found | order_ref غير معروف.Unknown order_ref. |
| 409 | duplicate_idempotency_key | نفس المفتاح استُخدم لبيانات طلب مختلفة.Same key reused for a different order payload. |
| 422 | insufficient_balance | الرصيد غير كافٍ لتغطية الطلب.Not enough balance to cover the order. |
| 422 | out_of_stock | الكمية المطلوبة تتجاوز المتوفر في المخزون.Requested quantity exceeds available stock. |
| 429 | rate_limit_exceeded | عدد طلبات كبير جداً — راجع حدود معدل الطلبات أدناه.Too many requests — see rate limits below. |
| 500 | internal_error | حدث خطأ من جهتنا — يمكنك إعادة المحاولة بأمان بنفس idempotency_key.Something went wrong on our side — safe to retry with the same idempotency_key. |
حدود معدل الطلباتRate limits
تُطبَّق الحدود لكل مفتاح API، لكل دقيقة:Limits are applied per API key, per minute:
POST /orders— 10 طلبات في الدقيقةPOST /orders— 10 requests / minute- باقي نقاط الوصول — 60 طلباً في الدقيقةAll other endpoints — 60 requests / minute
يحتوي كل رد على ترويستَي X-RateLimit-Limit وX-RateLimit-Remaining حتى تستطيع تخفيف معدل طلباتك قبل الوصول إلى الخطأ 429.Every response includes X-RateLimit-Limit and X-RateLimit-Remaining headers so you can back off before hitting 429.
تقييد عناوين IP (اختياري)IP filtering (optional)
إذا كان تكاملك يعمل من سيرفر ثابت، يمكنك تقييد مفتاحك بعناوين IP محددة من شاشة ✏️ API ← 🛡️ IP Filter في البوت.If your integration runs from a fixed server, you can lock your key to specific IP addresses from the bot's ✏️ API → 🛡️ IP Filter screen.
- أرسل عنوان IP واحداً أو أكثر (IPv4 أو IPv6)، مفصولة بفواصل، لتقييد المفتاح بها.Send one or more IPv4/IPv6 addresses, comma-separated, to restrict the key to them.
- أرسل
offفي أي وقت لإزالة القيد.Sendoffat any time to remove the restriction. - لا يوجد أي قيد افتراضياً — يعمل المفتاح من أي عنوان IP.By default, no restriction is applied — the key works from any IP.
- يُرفض أي طلب من عنوان IP غير مسموح به برمز الخطأ
ip_not_allowed(403).A request from a non-allowed IP is rejected withip_not_allowed(403).
الإشعارات الفورية (Webhooks)Webhooks
يدعم D2Bulk إرسال أحداث الطلبات والمخزون إلى رابط تتحكم فيه أنت، موقَّعة بترويسة X-D2Bulk-Signature (توقيع HMAC-SHA256 لمحتوى الرسالة، باستخدام سر الـ webhook الخاص بك) — من الأحداث المخطط دعمها: order.delivered وorder.failed وproduct.price_changed وproduct.stock_changed.
D2Bulk supports pushing order and stock events to a URL you control, signed with X-D2Bulk-Signature (HMAC-SHA256 of the raw payload, using your webhook secret) — planned events include order.delivered, order.failed, product.price_changed and product.stock_changed.
نفس الحدَثين order.delivered وorder.failed يُطلَقان أيضاً لطلبات شحن الألعاب (POST /topup/orders) لحظة استقرار نتيجتها — وهو البديل عن انتظار استعلامك المتكرر على GET /topup/orders/{id}.
The same two events, order.delivered and order.failed, also fire for Game Top-Up orders (POST /topup/orders) the moment they resolve — this is the alternative to repeatedly polling GET /topup/orders/{id}.
GET /orders/{id} — ولا تعتمد على الـ webhook وحده أبداً.
Until then, contact the D2Bulk team to have a webhook URL registered for your account manually. In the meantime, poll GET /orders/{id} to confirm order status — never rely on a webhook alone.
أفضل الممارساتBest practices
- أرسل دائماً
idempotency_keyفريداً لكل محاولة طلب حتى لا تتسبب إعادة المحاولة في خصم مضاعف.Always send a uniqueidempotency_keyper order attempt so a retried request never double-charges you. - جدِّد السر الخاص بك دورياً (كل 90 يوماً على الأقل، وفوراً إذا اشتبهت في تسرّبه) باستخدام "🔄 Regenerate API Key" في البوت.Rotate your secret periodically (at least every 90 days, and immediately if you suspect it leaked) using "🔄 Regenerate API Key" in the bot.
- قيِّد الوصول بعنوان IP إذا كان تكاملك يعمل من سيرفر ثابت — راجع تقييد عناوين IP.Restrict by IP if you integrate from a fixed server — see IP filtering.
- تعامل مع الطلبات ذات الحالة
pending— بعض المنتجات تُورَّد من مزوّد خارجي ولا تُسلَّم فوراً. تابعGET /orders/{id}حتى تستقر الحالة.Handlependingorders — some products are sourced from an upstream supplier and aren't delivered instantly. PollGET /orders/{id}untilstatussettles. - راقب ترويسات حدود المعدل وخفِّف من طلباتك قبل الوصول إلى الخطأ
429.Watch the rate-limit headers and back off before you hit429. - استخدم HTTPS فقط — تُرفض طلبات HTTP العادية مباشرة.Use HTTPS only — plain HTTP requests are rejected outright.