للمطوّرين
ابنِ على الشبكة
شغّل المهام على الشبكة العالمية، وتابع تقدّمها، واستلم نتائج تم التحقق منها من شيفرتك الخاصة — أو ابنِ تطبيقًا يفعل ذلك للآخرين. واجهة HTTPS واحدة لبرامجك الخاصة وللتطبيقات التي تعمل نيابةً عن الآخرين: مفتاح API (dk_…) لشيفرتك، ورمز وصول OAuth 2 (do_…) لتطبيق أذن له أحدهم، ونطاقات صلاحية تحدّ من كليهما، وخطافات ويب موقّعة بـ HMAC لتعلم بالأحداث لحظة وقوعها.
بيانات الاعتماد
dk_…مفتاح API — لبرامجك وخوادمكdo_…رمز وصول — ساعة واحدةdr_…رمز تجديد — ثلاثون يومًا، استخدام واحدwhsec_…سرّ توقيع خطافات الويب
تُرسل جميعها في Authorization: Bearer …، باستثناء سرّ خطاف الويب الذي لا يغادر خادمك أبدًا.
البداية
طريقتان للدخول
استخدم مفتاحًا حين تكون الشيفرة لك وتعمل لحسابك أنت. واستخدم OAuth حين يسجّل الناس الدخول إلى تطبيقك ويسمحون له بالعمل نيابةً عنهم.
بمفتاح API
- 1
أنشئ مفتاحًا في إعدادات المطوّر. امنحه اسمًا ونطاقات الصلاحية التي يحتاجها فقط، وتاريخ انتهاء إن كان لغرض مؤقت.
إلى أن يتأكّد بريدك الإلكتروني، لا يحمل المفتاح إلا نطاقات القراءة.
- 2
انسخ المفتاح حين يظهر. فهو لا يظهر إلا مرة واحدة: لا نحتفظ إلا ببصمة منه، فالمفتاح المفقود لا يُستعاد بل يُستبدل.
- 3
أرسله رمزًا من نوع bearer مع كل طلب:
shellcurl https://api.thecubefloor.com/v1/me \ -H "Authorization: Bearer dk_…" - 4
ألغِه من الصفحة نفسها حالما تنتهي الحاجة إليه. تُظهر القائمة آخر استخدام لكل مفتاح.
باستخدام OAuth 2
منح رمز التفويض، مع PKCE (S256) إلزاميًا في كل طلب. كل تطبيق عميل سرّي يملك سرًّا خاصًا به.
- 1
سجّل التطبيق في إعدادات المطوّر: اسمه (يراه الناس حين يُطلب إذنهم)، وعناوين إعادة التوجيه، ونطاقات الصلاحية التي يحق له طلبها. تحصل على
client_idوclient_secret، ويظهر السرّ مرة واحدة. يتطلّب تسجيل تطبيق بريدًا إلكترونيًا مؤكّدًا.يجب أن تطابق عناوين إعادة التوجيه حرفًا بحرف. وهي عناوين
https://كاملة بلا جزء بعد العلامة #؛ ولا يُقبلhttp://العادي إلا نحوlocalhostأو عنوان الحلقة المحلية، للتطوير والتطبيقات الأصلية. - 2
أنشئ
code_verifierعشوائيًا ووجّه متصفح الشخص إلى/oauth/authorizeمع بصمته SHA-256 فيcode_challenge:shell# المُتحقِّق: من 43 إلى 128 حرفًا غير محجوز. احتفظ به حتى الاستبدال. CODE_VERIFIER=$(openssl rand -base64 64 | tr -dc 'A-Za-z0-9' | cut -c1-64) CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary \ | openssl base64 -A | tr '+/' '-_' | tr -d '=') https://api.thecubefloor.com/oauth/authorize?response_type=code &client_id=app_… &redirect_uri=https%3A%2F%2Fyour.app%2Fcallback &scope=account%3Aread%20jobs%3Aread &state=… &code_challenge=$CODE_CHALLENGE &code_challenge_method=S256 - 3
يرى صفحة الموافقة الخاصة بنا على thecubefloor.com، وفيها اسم تطبيقك وما يطلبه والمكان الذي سيعود إليه. إن وافق، يعود المتصفح إلى عنوان إعادة التوجيه ومعه
codeوstateالخاص بك؛ وإن رفض، يعود ومعهerror=access_denied. - 4
استبدل الرمز بالرموز خلال دقيقة واحدة ولمرة واحدة فقط. صادِق عبر HTTP Basic أو بإرسال
client_idوclient_secretفي النموذج:shellcurl https://api.thecubefloor.com/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=authorization_code \ -d code="$CODE" \ -d redirect_uri=https://your.app/callback \ -d code_verifier="$CODE_VERIFIER" - 5
يتضمّن الرد رمز وصول (
do_…، صالح لساعة) ورمز تجديد (dr_…، ثلاثون يومًا). كل تجديد يستهلك رمز التجديد ويعيد زوجًا جديدًا؛ وتقديم رمز مستهلك يُنهي كل الرموز الناشئة عن الموافقة نفسها، لأن أحد حامليه لص.json · shell{ "access_token": "do_…", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "dr_…", "scope": "account:read jobs:read" } curl https://api.thecubefloor.com/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=refresh_token \ -d refresh_token="$REFRESH_TOKEN" - 6
حين يقطع الشخص الاتصال في تطبيقك، أعِد الرمز (RFC 7009). رمز التجديد يُنهي الموافقة كلها:
shellcurl https://api.thecubefloor.com/oauth/revoke \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d token="$REFRESH_TOKEN"
حين يُرفض طلب
الأخطاء على الشكل {"error": "E_…", "message": "…"}: رمز يستطيع برنامجك التصرّف بناءً عليه وجملة يفهمها الإنسان. يعني 401 أن بيانات الاعتماد مفقودة أو مجهولة أو ملغاة أو منتهية؛ و403 E_SCOPE أنها لا تملك نطاق الصلاحية؛ و403 E_SESSION_ONLY أن العملية مخصّصة للموقع نفسه. أما نقاط /oauth/ فتجيب بصيغة RFC 6749.
نطاقات الصلاحية
ما يمكن لمفتاح أو رمز فعله
لا يستطيع مفتاح أو رمز أن يفعل أكثر مما يستطيعه حسابه، والحساب الذي لم يتأكّد بريده الإلكتروني لا يمنح إلا نطاقات القراءة. وكل ما لا يرد هنا — شحن الرصيد والسحب والمطالبة بالأجهزة وربط التخزين وإعدادات الحساب وإدارة المفاتيح والتطبيقات — يبقى حكرًا على الموقع.
خطافات الويب
اعلم بالحدث لحظة وقوعه
سجّل عنوان https:// والأحداث التي تريدها في إعدادات المطوّر. يُرسل كل حدث إليه بطلب POST بصيغة JSON، موقّعًا بالسرّ الذي ظهر عند إنشاء خطاف الويب.
الأحداث
| الحدث | يُرسل حين |
|---|---|
job.finished | انتهت مهمة ونتائجها جاهزة |
job.failed | فشلت مهمة |
job.undelivered | شُغّلت مهمة لكن تعذّر تسليم نتائجها |
topup.confirmed | تأكّد شحن الرصيد |
withdrawal.paid | دُفع سحب |
withdrawal.rejected | رُفض سحب |
machine.claimed | أصبحت أرباح جهاز تعود إلى هذا الحساب |
ping | حدث تجريبي يُرسل عند الضغط على «إرسال اختبار» |
الترويسات
DAIN-Webhook-Id- معرّف الحدث، ولا يتغيّر في أي إعادة محاولة. استخدمه لتجاهل التكرار: يُسلَّم كل حدث مرة واحدة على الأقل.
DAIN-Webhook-Timestamp- ثواني Unix لهذه المحاولة. ارفض كل ما مضى عليه أكثر من خمس دقائق.
DAIN-Webhook-Signature- HMAC-SHA256 للطابع الزمني ثم نقطة ثم الجسم الخام، بمفتاح هو سرّك
whsec_…، بالصيغة الست عشرية.
v1=<hex HMAC-SHA256(secret, timestamp + "." + body)>ما يصلك
POST /your/webhook HTTP/1.1
Content-Type: application/json
DAIN-Webhook-Id: evt_…
DAIN-Webhook-Timestamp: 1793351642
DAIN-Webhook-Signature: v1=5f2b…c09e
{
"id": "evt_…",
"type": "job.finished",
"created_at": "2026-10-30T09:14:02Z",
"data": { "job": { "id": "job_…", "state": "done", … } }
}يحمل data الكائن كما تعرضه الواجهة في نقطة الوصول الخاصة به: المهمة، أو شحن الرصيد، أو السحب (مع إخفاء الوجهة)، أو الجهاز. أما المهمة الملغاة فمن فعلك ولا يُبلَّغ عنها.
إعادة المحاولة
- المحاولة 1فورًا
- المحاولة 2+1 د
- المحاولة 3+5 د
- المحاولة 4+30 د
- المحاولة 5+2 س
- المحاولة 6+6 س
- المحاولة 7+24 س
- معطّل
أي رد 2xx يُنهيها. وإلا تُعاد محاولة التسليم بعد كل مهلة من هذه المهل — سبع محاولات على مدى 33 ساعة تقريبًا — وإذا فشلت الأخيرة يُعطَّل خطاف الويب. وإرسال اختبار يحصل على رد 2xx يعيد تفعيله. لا تُتّبع عمليات إعادة التوجيه.
تُفحص العناوين بعد استعلام DNS، عند التسجيل ومع كل تسليم: لا يُستدعى أبدًا أي عنوان خاص أو حلقة محلية أو محلي الرابط أو محجوز.
التحقق من التوقيع
احسب التوقيع على الجسم كما وصل تمامًا — قبل أي تحليل لـ JSON — وقارن في زمن ثابت.
import hashlib, hmac, time
def verify(secret: str, headers, body: bytes) -> bool:
"""body: the request body exactly as it arrived, before json.loads."""
ts = headers["DAIN-Webhook-Timestamp"]
sig = headers.get("DAIN-Webhook-Signature", "")
if abs(time.time() - int(ts)) > 300: # more than five minutes old
return False
mac = hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256)
return hmac.compare_digest(sig, "v1=" + mac.hexdigest())مرجع الواجهة
كل نقاط الوصول
يُقرأ مباشرةً من /v1/openapi.yaml، عقد OpenAPI 3.1 الذي يُختبر الخادم على أساسه. وجّه مولّد العملاء إلى العنوان نفسه.