Developers
الخطافات
يخبرك webhook أن طلبًا تغيرت حالته. لا يحل محل قراءة الطلب: إنه إشعار، وليس مصدر حقيقة.
عقد منشور، غير مفتوح بعد
لا يتم إصدار webhooks اليوم. شكله وتوقيعه وسياسة إعادة المحاولة محددة ومنشورة هنا حتى يمكن كتابة جهاز الاستقبال الخاص بك واختباره مسبقًا.
الأحداث
يحمل كل حدث نفس الغلاف: معرف، ونوع، وطابع زمني، وكائن بيانات. اشترك في الأنواع التي تتعامل معها وتجاهل الباقي بصمت — ستتم إضافة أنواع جديدة، والمستقبل الذي يفشل على نوع غير معروف يكسر نفسه.
| النوع | ماذا يعني |
|---|---|
order.created | تم إنشاء الطلب وتثبيت السعر. تم تعيين عنوان الإيداع. |
order.deposit_detected | تظهر معاملة واردة على الشبكة، قبل التأكيد. استخدم هذا الحدث لطمأنة المستخدم، ولا تسلم أي شيء أبدًا. |
order.confirming | عداد التأكيد يتقدم. يُصدر عند كل خطوة، وليس عند كل كتلة. |
order.deposit_confirmed | تم الوصول إلى عدد التأكيدات المطلوب من قبل الشبكة. |
order.underpaid | المبلغ المستلم أقل من المبلغ المتوقع بما يتجاوز التسامح. الطلب ليس مفقودًا: إنه ينتظر الاختيار بين إضافة المبلغ، أو المتابعة بالمبلغ المستلم، أو استرداد الأموال. |
order.overpaid | المبلغ المستلم يتجاوز المبلغ المتوقع. يبقى المبلغ الأولي بالسعر المثبت؛ يتم التعامل مع الزيادة بشكل منفصل. |
order.payout_sent | غادر التحويل عبر قناة الدفع. انتبه: الإرسال ليس استلامًا — يعتمد التأخير المتبقي على بنك المستفيد. |
order.completed | تم تأكيد التسوية من قبل قناة الدفع. حالة نهائية. |
order.payout_failed | رفضت قناة الدفع التحويل أو أعادته، مع ذكر السبب. لا تتم إعادة المحاولة تلقائيًا عند إرجاع بنكي: السبب دائمًا تقريبًا في التفاصيل. |
order.refunded | تم إرجاع الأموال، فقط إلى العنوان الأصلي، صافية من رسوم الشبكة. |
الحمولة
غلاف ثابت، مهما كان النوع.
{
"id": "evt_01J9ZQF3K8N2M4X7",
"type": "order.payout_sent",
"created": 1788356981,
"data": {
"reference": "K7Q4-M2XB",
"state": "PAYOUT_SENT",
"railId": "sepa_instant",
"netMinor": 91228,
"currency": "EUR",
"sentAt": "2026-09-02T12:21:44Z"
}
}التوقيع
يحمل كل تسليم رأس توقيع: طابع زمني وHMAC-SHA256 محسوب على ذلك الطابع الزمني، ونقطة، ونص الطلب الخام.
Fiatside-Signature: t=<timestamp unix>,v1=<hex hmac-sha256>import { createHmac, timingSafeEqual } from 'node:crypto'
// IMPORTANT : le corps doit etre le corps BRUT, avant tout parsing JSON.
// Re-serialiser l'objet change l'ordre des cles et invalide la signature.
export function verify(rawBody: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=') as [string, string]))
const timestamp = Number(parts['t'])
const signature = parts['v1']
if (!timestamp || !signature) return false
// Rejeu : une signature valide capturee hier ne doit pas etre rejouable aujourd'hui.
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(signature, 'hex')
// Comparaison a temps constant : un === laisse fuiter la signature octet par octet.
return a.length === b.length && timingSafeEqual(a, b)
}- وقع النص الخام، قبل أي تحليل. إعادة تسلسل كائن JSON تغير ترتيب المفاتيح والمسافات: لن يتطابق التوقيع بعد ذلك، وستبحث لفترة طويلة.
- قارن في وقت ثابت. مقارنة === تتوقف عند أول بايت مختلف، مما يسمح للمهاجم بقياس التوقيع المتوقع بايتًا بايت.
- ارفض ما بعد نافذة التسامح البالغة 300 ثانية. بدون هذا الفحص، يظل طلب صالح تم التقاطه بالأمس قابلاً لإعادة التشغيل اليوم.
- أجب بـ 200 قبل المعالجة. ضع الحدث في قائمتك وتعامل معه بعد ذلك: المعالجة الطويلة تؤدي إلى مهلة وبالتالي إعادة محاولة، حتى لو كنت قد استلمت الحدث.
إعادة المحاولة والترتيب
أي حالة غير 2xx، أو عدم وجود استجابة على الإطلاق، تؤدي إلى إعادة محاولة وفق الجدول أدناه.
| المحاولة | التأخير |
|---|---|
| 1 | فوري |
| 2 | 30 s |
| 3 | 2 min |
| 4 | 10 min |
| 5 | 1 h |
| 6 | 6 h |
- ترتيب الوصول غير مضمون. قد يصل حدث دفع قبل حدث التأكيد الذي يسبقه منطقيًا: ثق بحالة الطلب، وليس بترتيب الاستلام.
- يمكن أن يصل نفس الحدث مرتين. قم بتخزين معرف الحدث وتجاهل المعرف الذي تمت معالجته بالفعل: التكرارية (idempotency) على جهاز الاستقبال الخاص بك هي مسؤوليتك، ومن السهل تحقيقها.
- لا تثق أبدًا بمبالغ الحمولة (payload) لاعتماد أي شيء على جانبك. أعد قراءة الطلب عبر API: الحمولة تقول أن شيئًا ما حدث، وAPI يحدد بالضبط ما حدث.
- بعد المحاولة الأخيرة، يتم إسقاط الحدث ويبقى مرئيًا في سجل الأحداث الخاص بك. يمكنك إعادة تشغيله يدويًا.