التشغيل الفعلي
Webhooks
تسليمات أحداث موقعة ومعادة المحاولة: نقاط النهاية، وأنواع الأحداث، والتحقق بأربع لغات، والاختبار وإعادة التشغيل.
6 دقائق قراءة
في هذه الصفحة
نقاط النهاية
أنشئ نقطة نهاية أو أكثر باستخدام POST /partner/webhooks/endpoints (name وurl وevents كقائمة مفصولة بفواصل أو *، وmode). تحصل كل نقطة على سر توقيع خاص بها، يظهر مرة واحدة عند الإنشاء. عدّلها واحذفها واعرضها عبر استدعاءات /partner/webhooks/endpoint… أو في وحدة التحكم.
أنواع الأحداث
| الحدث | يُرسل عندما |
|---|---|
charge.created | أُنشئ خصم عبر طلب الدفع. |
charge.completed | وافق العميل وانتقلت الأموال. |
charge.declined | رفض العميل الطلب. |
charge.expired | انتهت صلاحية الطلب دون رد. |
checkout.completed | دُفعت جلسة في صفحة الدفع المستضافة. |
c2b.confirmation | دفع عميل مباشرة إلى صندوقك أو رقم فاتورتك. |
كيف يبدو التسليم
| الترويسة | القيمة |
|---|---|
X-PesaBridge-Event | نوع الحدث، مثل charge.completed. |
X-PesaBridge-Signature | HMAC-SHA256 بالنظام الست عشري لنص الطلب الخام، بمفتاح سر التوقيع لنقطة النهاية. |
X-PesaBridge-Timestamp | وقت Unix لتوقيع التسليم. |
X-PesaBridge-Signature-V2 | t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>">: مقاوم لإعادة الإرسال. |
X-PesaBridge-Delivery | معرّف التسليم: سجّله واستخدمه لتجاهل النسخ المكررة. |
تحقق من كل تسليم
احسب HMAC على البايتات التي استلمتها كما هي، قبل تحليل JSON، وقارن في زمن ثابت. فضّل V2 وارفض الطوابع الزمنية الأقدم من خمس دقائق.
import crypto from "node:crypto";
// Express: app.post("/webhook", express.raw({ type: "application/json" }), handler)
function verified(req, secret) {
const [t, v1] = (req.get("X-PesaBridge-Signature-V2") || "").split(",").map((p) => p.split("=")[1]);
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const want = crypto.createHmac("sha256", secret).update(`${t}.${req.body}`).digest("hex");
return v1?.length === want.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}import hmac, hashlib, time
def verified(raw_body: bytes, headers, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in headers.get("X-PesaBridge-Signature-V2", "").split(",") if "=" in p)
t, v1 = parts.get("t"), parts.get("v1", "")
if not t or abs(time.time() - int(t)) > 300:
return False
want = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(v1, want)<?php
$raw = file_get_contents("php://input");
parse_str(str_replace(",", "&", $_SERVER["HTTP_X_PESABRIDGE_SIGNATURE_V2"] ?? ""), $sig);
$ok = isset($sig["t"], $sig["v1"]) && abs(time() - (int)$sig["t"]) <= 300
&& hash_equals(hash_hmac("sha256", $sig["t"] . "." . $raw, getenv("PB_WHSEC")), $sig["v1"]);
http_response_code($ok ? 200 : 401);package webhook
import ("crypto/hmac"; "crypto/sha256"; "encoding/hex")
// Verified checks the V1 signature: hex HMAC-SHA256 of the raw body.
func Verified(raw []byte, sig, secret string) bool {
m := hmac.New(sha256.New, []byte(secret))
m.Write(raw)
return hmac.Equal([]byte(hex.EncodeToString(m.Sum(nil))), []byte(sig))
}إعادة المحاولة والتكرار
أجب بأي رمز 2xx خلال 10 ثوانٍ. وأي رد آخر أو انتهاء مهلة يؤدي إلى إعادة المحاولة بتأخير أسي (1 و2 و4 و8 و16 دقيقة، حتى 6 محاولات)، وبعدها يوسم التسليم بالحالة failed. وبسبب إعادة المحاولة قد يصل الحدث نفسه مرتين: اجعل المعالج لديك مانعًا للتكرار بناءً على X-PesaBridge-Delivery أو charge_ref.
نسلّم فقط إلى عناوين HTTPS العامة. وتُرفض عناوين الشبكات الخاصة والداخلية.
الاختبار وإعادة التشغيل
- يرسل
POST /partner/webhooks/testحدثًا اختباريًا إلى نقطة نهاية. - يعرض
GET /partner/webhooks/deliveriesالتسليمات الأخيرة مع رمز الحالة وعدد المحاولات. - يعيد
POST /partner/webhooks/resendتشغيل تسليم بعد إصلاح المعالج لديك. - يلتقط مستقبِل Echo (
GET /partner/echo/url) التسليمات ويتحقق منها تلقائيًا دون خادم خاص بك.