Aiki na gaske
Webhooks
Signed, retried event deliveries: endpoints, event types, verification in four languages, testing and replay.
Karatun minti 6
An rubuta jagororin masu haɓakawa da Turanci domin lambar, sunayen filaye da saƙonnin kuskure su dace daidai da API.
A wannan shafi
Endpoints
Create one or more endpoints with POST /partner/webhooks/endpoints (name, url, events as a comma-separated list or *, and mode). Each gets its own signing secret, shown once at creation. Update, delete and list them with the /partner/webhooks/endpoint… calls, or in the console.
Event types
| Event | Sent when |
|---|---|
charge.created | A prompt-to-pay charge was created. |
charge.completed | The customer approved and the money moved. |
charge.declined | The customer declined the prompt. |
charge.expired | The prompt expired unanswered. |
checkout.completed | A hosted checkout session was paid. |
c2b.confirmation | A customer paid your till or pay bill directly. |
What a delivery looks like
| Header | Value |
|---|---|
X-PesaBridge-Event | The event type, e.g. charge.completed. |
X-PesaBridge-Signature | Hex HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret. |
X-PesaBridge-Timestamp | Unix time the delivery was signed. |
X-PesaBridge-Signature-V2 | t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>">: replay-resistant. |
X-PesaBridge-Delivery | Delivery id: log it, and use it to ignore duplicates. |
Verify every delivery
Compute the HMAC over the exact bytes you received, before parsing JSON, and compare in constant time. Prefer V2 and reject timestamps older than five minutes.
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))
}Retries and duplicates
Answer with any 2xx within 10 seconds. Anything else, or a timeout, is retried with exponential back-off (1, 2, 4, 8 and 16 minutes, up to 6 attempts), after which the delivery is marked failed. Because of retries, the same event can arrive twice: make your handler idempotent on X-PesaBridge-Delivery or on charge_ref.
We only deliver to public HTTPS addresses. Private and internal network addresses are refused.
Test and replay
POST /partner/webhooks/testsends a test event to an endpoint.GET /partner/webhooks/deliverieslists recent deliveries with their status code and attempts.POST /partner/webhooks/resendreplays a delivery after you fix your handler.- The Echo receiver (
GET /partner/echo/url) captures and auto-verifies deliveries with no server of your own.
Injiniyoyinmu suna amsa tambayoyin haɗawa. Aiko mana da lambar bayanin buƙatar, za mu nemo ainihin kiran.
Tuntuɓi tallafi