بدل أن يسأل نظامك فوتّر كل دقيقة عن جديد، يرسل فوتّر طلب POST إلى رابطك لحظة وقوع الحدث. أضف الرابط من التكاملات واحفظ مفتاح التوقيع الذي يظهر مرة واحدة.
الأحداث
invoice.created—إصدار فاتورةinvoice.paid—سداد فاتورة بالكاملpayment.received—استلام دفعةcustomer.created—إضافة عميلquote.accepted—قبول عرض سعرexpense.created—تسجيل مصروفpayroll.posted—ترحيل مسيّر رواتبpos.order_settled—تحصيل طلب مطعمjournal.posted—ترحيل قيد
شكل الرسالة
POST /your-endpoint
Content-Type: application/json
Fawtar-Event: invoice.paid
Fawtar-Delivery: 3c9e6f7a-…
Fawtar-Signature: t=1800000000,v1=5f2b…
{
"event": "invoice.paid",
"created_at": "2026-08-28T09:14:22.031Z",
"data": { "invoice_id": "…", "invoice_number": "INV-0042", "total": 1150 }
}
التحقق من التوقيع
التوقيع HMAC-SHA256 على النص `${t}.${rawBody}` بمفتاحك. الطابع الزمني موقَّع مع الجسم تحديدًا كي لا تُعاد رسالة قديمة صحيحة التوقيع لاحقًا — تحقق من عمرها، وارفض ما تجاوز خمس دقائق. قارن التوقيعين بمقارنة ثابتة الزمن، ووقّع على الجسم الخام قبل أي تحليل JSON.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.trim().split("=", 2))
);
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t));
if (!Number.isFinite(age) || age > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`, "utf8")
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(parts.v1 ?? "", "utf8");
return a.length === b.length && timingSafeEqual(a, b);
}
إعادة المحاولة
أي رد خارج نطاق 2xx يُعد فشلًا. نعيد المحاولة حتى 6 مرات بفواصل متزايدة (1، 5، 30، 120، 360 دقيقة). الرابط الذي يفشل مرارًا يُوقَف تلقائيًا، ويظهر ذلك في صفحة التكاملات مع زر لإعادة تفعيله.
ردّ بـ 200 فور الاستلام وعالج الرسالة بعد ذلك: المهلة عشر ثوانٍ. وتعامل مع التكرار — قد تصل الرسالة نفسها مرتين، لذا استخدم Fawtar-Delivery مفتاحًا للتفرد.