VietBilling
VietBilling Docs

Webhooks

Verify and process subscription webhooks safely and idempotently.

Verify before parsing the business event

Read the raw request body, verify its signature with the webhook secret, and only then parse JSON. If your framework transforms the body first, the signature might no longer match.

export async function POST(request: Request) {
  const rawBody = await request.text()
  const signature = request.headers.get("x-vietbilling-signature")

  verifyVietBillingSignature(rawBody, signature, process.env.VIETBILLING_WEBHOOK_SECRET!)
  const event = JSON.parse(rawBody)

  if (await wasProcessed(event.id)) return new Response("ok")
  await processBillingEvent(event)
  await markProcessed(event.id)
  return new Response("ok")
}

Use the header name and signing algorithm shown by the current Organization webhook configuration; do not infer them from this illustrative handler.

Idempotent processing

Put a unique constraint on event id. Events can be redelivered or arrive out of order. Handle Checkout, Order, Payment, Subscription, and BenefitGrant events idempotently, comparing resource timestamps and ids before updating records.

Retry behavior

Return 2xx after the event is durably stored. Return a temporary error when processing cannot continue so VietBilling can retry; move heavy work to a queue.