Inbound Stripe event (server-to-server)

Stripe posts billing events to https://rrmacademy.org/api/stripe-webhook . The handler verifies the Stripe-Signature header (HMAC-SHA256 over the raw request body, prefixed with a timestamp, per https://docs.stripe.com/webhooks/signatures) before processing. Requests with a missing or invalid signature are rejected with 400. The handler is idempotent: replayed events with the same id short-circuit on a dedup KV record. Accepted event types: checkout.session.completed, checkout.session.expired, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, charge.refunded. This is the only inbound webhook surface; rrmacademy.org does not currently emit outbound webhooks to consumers.

postWebhookstripeEvent

Headers

Stripe-Signaturestring required
Example:t=1701234567,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Stripe HMAC-SHA256 signature over the raw POST body. Format: t=<unix_ts>,v1=<hex_hmac>. Verified by stripe.webhooks.constructEventAsync using the rotating webhook signing secret. See https://docs.stripe.com/webhooks/signatures.

Payload

idstring required
typestring required

Stripe event type. Accepted types: checkout.session.completed, checkout.session.expired, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, charge.refunded.

dataobject required

Event payload. Shape varies by event type.

livemodeboolean
createdinteger

Example payload

{
  "id": "evt_1NkFh82eZvKYlo2C0w6cVHaJ",
  "type": "checkout.session.completed"
}

Response

Event accepted (verified, dispatched to the matching handler, or idempotently skipped).

Changes

Webhooks have no change history: a webhook is keyed by its event name rather than a path and method, so no recorded change can name it.