sk_v1_test_… key.
1. Stand up a receiver
A webhook endpoint is an HTTPS URL that accepts aPOST. Start with a handler
that captures the raw body (the signature covers the exact bytes, so a body
that’s been parsed and re-serialized by framework middleware will not verify)
and applies the delivery acknowledgment contract.
On Node, webhooks.unwrap() from the server SDK checks the signature and
replay window, then returns a typed event:
WebhookVerificationError.
An authenticated payload that is not a valid event envelope throws
WebhookParseError. Your receiver must reject unverified deliveries. A 2xx
acknowledges acceptance; Idempotency & retries
defines the persistence and redelivery contract.
Not on Node? The signature scheme is standard HMAC-SHA256 over
{id}.{timestamp}.{body} — Verify signatures has the
full manual implementation in Node, Python, and Go.
2. Register the endpoint
Tell Ionic where to deliver and which events you want. For this walkthrough, subscribe topayment_intent.created.
secret that starts with whsec_. This is the only time it’s shown. Save it where your server can read it:
3. Verify every delivery
Your handler already does:unwrap recomputes the signature over the raw body
with constant-time comparison, rejects deliveries older than the replay
window, and throws on anything that doesn’t match. Don’t skip verification:
an unverified endpoint will accept forged events from anyone who learns your
URL. If you’re implementing it yourself, Verify
signatures walks through the same checks step by step.
4. Trigger an event
Create a PaymentIntent. This needs only an amount and currency, with no card, and emitspayment_intent.created.
type: "payment_intent.created" and an evt_… ID.
Its data.object is the created PaymentIntent.
5. You’re receiving webhooks
unwrap returns a typed event. The type field identifies the payload shape
in data.object; the event catalog documents each shape.
Verification and receipt do not implement payment or fulfillment decisions.
Those belong to your application.
From here:
Event catalog
Subscribe to the events you actually handle —
payment_intent.succeeded, refund.succeeded, and the rest.Idempotency & retries
Make your handler safe against duplicate and out-of-order deliveries before you ship.
Developing locally
Ionic can’t deliver webhooks tolocalhost. Two ways to work while local:
- Read the current status. Fetch the resource directly when you need an
answer — for example
GET /v1/checkout/sessions/{id}returnspayment_statuson demand. This works for most local testing. - Tunnel your receiver. Expose your local port (for example
ngrok http 3000), register the tunnel URL as a webhook endpoint, and update the endpoint’surlwhenever the tunnel address changes.
What’s next
Verify signatures
Check each delivery by hand, or understand what
unwrap does.Manage endpoints
Update, disable, and rotate the signing secret on an endpoint.
Handle retries and duplicates
Deduplicate on the event
id and handle out-of-order delivery.
