Webhooks
Stel callbackUrl in op een betaling, en Stablezact stuurt er een POST naartoe
naarmate de betaling vordert. Dit is de betrouwbare manier om te weten dat een
betaling is afgewikkeld, omdat het niet afhangt van een klant die een
browsertabblad open houdt.
config={{ amount: 49.99, currency: 'USDC', callbackUrl: 'https://yourstore.com/webhooks/stablezact', orderId: 'ORD-1234', metadata: { basket: 'b-99' },}}Zowel orderId als metadata komen bij elk event terug. Zo koppelt u een event
aan een bestelling zonder zelf een koppeling bij te houden.
Events
Section titled “Events”Lees type. Het is dezelfde kleine set waarden, hoe de klant ook heeft betaald.
type |
Betekenis | Uitvoeren |
|---|---|---|
payment.pending |
er is geld gezien, de betaling is nog niet definitief | nee |
payment.succeeded |
de klant heeft betaald | ja |
payment.underpaid |
er is minder aangekomen dan gefactureerd | nee, wijs de klant erop |
payment.failed |
verlopen, geannuleerd of mislukt | nee |
payment.refunded |
het geld is teruggegaan naar de klant | draai de bestelling terug |
settlement.completed |
uw uitbetaling heeft uw wallet bereikt | al uitgevoerd |
settlement.delayed |
uw uitbetaling duurt langer dan normaal, wordt automatisch opnieuw geprobeerd | niets te doen |
settlement.failed |
uw uitbetaling kon niet worden voltooid, wij nemen contact met u op | niets te doen |
webhook.test |
een testlevering | nee |
payment.succeeded is de enige die betekent: uitvoeren. Al het andere is er
voor als u het wilt gebruiken.
De payment-events beantwoorden de vraag “moet ik deze bestelling verzenden”.
De settlement-events beantwoorden de vraag “waar is mijn geld”, en ze
veranderen nooit of u verzendt. Veel integraties negeren ze en reconciliëren in
plaats daarvan vanuit hun wallet.
type wordt ook verzonden als de header X-Webhook-Type, als u liever op een
header routeert dan de body te parsen.
Het oudere veld event
Section titled “Het oudere veld event”Elke payload bevat ook een veld event, dat ouder is dan type. Het gebruikt
twee naamgevingsconventies, geeft dezelfde uitkomst twee verschillende namen
afhankelijk van hoe de klant heeft betaald, en benoemt onze interne
afwikkelingsstappen. Het werkt nog steeds en blijft werken, dus bestaande
integraties hoeven niet te worden aangepast.
Bouw nieuwe integraties op type. Als u een oudere integratie leest, ziet u
hier hoe de twee zich tot elkaar verhouden:
event |
type |
|---|---|
payment_success, deposit.confirmed |
payment.succeeded |
payment_failure, deposit.failed, bridge.failed |
payment.failed |
deposit.detected, bridge.filled |
payment.pending |
deposit.swept, settlement.completed |
settlement.completed |
settlement.delayed |
settlement.delayed |
payment.underpaid |
payment.underpaid |
payment.refunded |
payment.refunded |
webhook_test |
webhook.test |
De betaling is paymentId in elke payload. Sommige oudere events noemen die ook
transactionId; beide zijn aanwezig en identiek. De uitbetalingstransactie is
settlementTxHash, bij oudere events ook aanwezig als sweepTxHash.
Als u niet zeker weet naar welk event u kijkt, haal dan de betaling op en lees
outcome. Dat geeft hetzelfde antwoord, en het hangt niet af van welke events
u hebben bereikt.
Verifiëren dat het request van ons kwam
Section titled “Verifiëren dat het request van ons kwam”Elke levering bevat twee headers:
X-Webhook-Signature: sha256=<hex digest>X-Webhook-Timestamp: <milliseconds since epoch>Verifieer voordat u iets met de body doet. Een endpoint dat bestellingen uitvoert op basis van een niet-geverifieerde POST, voert bestellingen uit voor iedereen die de URL vindt.
Gebruik de SDK-helper als dat kan
Section titled “Gebruik de SDK-helper als dat kan”De SDK bevat de verificatie al, zodat u die niet opnieuw hoeft te implementeren:
import { verifyWebhookSignature } from 'stablezact-pay';
app.post('/webhooks/stablezact', express.json(), (req, res) => { const result = verifyWebhookSignature( req.body, req.headers['x-webhook-signature'], req.headers['x-webhook-timestamp'], process.env.STABLEZACT_WEBHOOK_SECRET );
if (!result.valid) return res.status(400).json({ error: result.error });
res.sendStatus(200); handleEvent(req.body);});Er is ook coinleyWebhookMiddleware, als u het liever als middleware koppelt.
Zelf verifiëren
Section titled “Zelf verifiëren”import crypto from 'node:crypto';
function verify(payload, signatureHeader, timestampHeader, secret) { const ts = parseInt(timestampHeader, 10);
// Reject anything older than five minutes, so a captured delivery cannot be // replayed later. if (Date.now() - ts > 5 * 60 * 1000) return false;
const withTimestamp = { ...payload, timestamp: ts }; const serialized = JSON.stringify(withTimestamp, Object.keys(withTimestamp).sort());
const expected = crypto.createHmac('sha256', secret).update(serialized).digest('hex'); const received = signatureHeader.replace(/^sha256=/, '');
const a = Buffer.from(received, 'hex'); const b = Buffer.from(expected, 'hex'); return a.length === b.length && crypto.timingSafeEqual(a, b);}Drie details die ertoe doen:
De timestamp maakt deel uit van het ondertekende materiaal, dus die moet vóór
het serialiseren aan de payload worden toegevoegd, als getal.
Keys worden gesorteerd. JSON.stringify(value, Object.keys(value).sort()) is
exact de aanroep die de server doet.
Uw webhooksecret moet minstens 32 tekens lang zijn. Kortere secrets worden door de verifier geweigerd, in plaats van dat de handtekening stilletjes zwakker wordt.
Leveringen afhandelen
Section titled “Leveringen afhandelen”Antwoord snel met 200 en doe het werk daarna. Een traag endpoint lijkt op een
mislukt endpoint.
Houd er rekening mee dat hetzelfde event twee keer kan binnenkomen. Nieuwe
pogingen en netwerktimeouts betekenen at-least-once-levering, dus paymentId
plus event als sleutel gebruiken en herhalingen negeren is de paar regels code
die het kost zeker waard.
