Skip to content

Webhooks

Webhooks notify your systems when events occur.

POST /api/stripe-webhook

Headers:

Stripe-Signature: <signature>
Content-Type: application/json

Events:

  • checkout.session.completed - Payment successful
  • payment_intent.payment_failed - Payment failed

Example Payload:

{
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_abc123",
"metadata": {
"orderId": "order_xyz789"
}
}
}
}
POST /api/webhook/billplz

Format: URL-encoded

Parameters:

  • billplz[id] - Bill ID
  • billplz[paid] - true/false
  • billplz[paid_at] - Timestamp
POST /api/webhook/bayarcash

Payload:

{
"payment_intent_id": "pi_123",
"status": "completed",
"transaction_id": "txn_456"
}
POST /api/webhook/chip

Payload:

{
"id": "purchase_123",
"status": "paid",
"payment": {
"status": "captured"
}
}

All webhooks verify signatures:

  1. Gateway sends signature header
  2. Pintas computes expected signature
  3. Compares signatures
  4. Rejects if mismatch
const crypto = require('crypto');
function verifyStripeSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
  1. Verify signatures - Always validate
  2. Respond quickly - Return 200 fast
  3. Handle duplicates - Webhooks may retry
  4. Log everything - Debug issues
  5. Use HTTPS - Required for production
Terminal window
stripe listen --forward-to localhost:3000/api/stripe-webhook
Terminal window
ngrok http 3000
# Use: https://abc123.ngrok.io/api/stripe-webhook
IssueSolution
Signature errorCheck secret matches
TimeoutProcess async, respond fast
Not receivingVerify URL and network
DuplicatesTrack processed IDs