Event payloads
What each event means and what it carries.
Every delivery uses the same envelope; only type and data.object differ. created is a Unix timestamp in seconds.
{ "id": "evt_9f1c2ab34d5e6f70", "object": "event", "type": "checkout.session.completed",
"environment": "live", "installation": "3f0c…-uuid", "created": 1755423672,
"data": { "object": { … } } }
Checkout session payload
All checkout.session.* events carry the same base object, plus a few event-specific fields.
{
"id": "b6b1…-uuid",
"object": "checkout.session",
"status": "complete",
"mode": "live",
"amount": 2500,
"currency": "eur",
"reference": "cs_2f81c0",
"metadata": { "order_id": "1042" },
"payment_intent": "pi_3Nk91x…",
"setup_intent": null,
"subscription": null,
"charge": "ch_3Nk91x…"
}
| Field | Meaning |
|---|---|
id |
spuke checkout session ID (UUID). Your order key. |
status |
open, complete, expired, failed, canceled. |
mode |
live or test — same meaning as the envelope environment. |
amount |
Total in minor units (2500 = €25.00). |
currency |
Lowercase ISO-4217. |
reference |
Short human-readable session reference shown to the shopper. |
metadata |
Key/value map set when the session was created — your safest link to your own order. |
payment_intent |
Payment reference at the processor, null for setup-only sessions. |
setup_intent |
Set instead of payment_intent when the session only saved a payment method. |
subscription |
Subscription ID when the session started a recurring plan. |
charge |
Charge reference once money moved; needed to match refunds/disputes. |
Event-specific extras
| Event | Extra fields |
|---|---|
checkout.session.async_payment_pending |
payment_status: "processing", status: "open" |
checkout.session.async_payment_failed |
failure_code, failure_message |
checkout.session.canceled |
cancellation_reason |
checkout.session.refunded |
amount_refunded (minor units), fully_refunded (boolean) |
{ "type": "checkout.session.refunded",
"data": { "object": { "id": "b6b1…", "object": "checkout.session", "status": "complete",
"amount": 2500, "currency": "eur", "amount_refunded": 500, "fully_refunded": false } } }
Payment intent payloads
payment_intent.succeeded
{ "id": "pi_3Nk91x", "amount": 2500, "amount_received": 2500, "currency": "eur",
"status": "succeeded", "customer": "cus_8f21", "latest_charge": "ch_3Nk91x",
"payment_method_types": ["card"], "metadata": { "order_id": "1042" } }
| Field | Meaning |
|---|---|
amount |
Amount requested, minor units. |
amount_received |
Amount actually captured — compare both for partial captures. |
status |
Processor status, e.g. succeeded, requires_action, canceled. |
customer |
Processor customer reference, may be null for guest checkout. |
latest_charge |
The charge that carries the money; use it to match refunds and disputes. |
payment_method_types |
Methods that were allowed on this payment. |
metadata |
Whatever the merchant/app set at creation. |
payment_intent.payment_failed replaces amount_received/latest_charge with:
{ "id": "pi_3Nk91x", "amount": 2500, "currency": "eur", "status": "requires_payment_method",
"customer": null, "failure_code": "card_declined", "decline_code": "insufficient_funds",
"failure_message": "Your card has insufficient funds.", "metadata": {} }
| Field | Meaning |
|---|---|
failure_code |
Coarse reason (card_declined, expired_card, …). Branch on this. |
decline_code |
Issuer's detailed reason. Show it to the merchant, not the shopper. |
failure_message |
Human-readable text, already customer-safe. |
payment_intent.requires_action and payment_intent.canceled carry the same base fields (id, amount, currency, status, customer, metadata).
Billing payloads
invoice.payment_failed and customer.subscription.deleted carry the processor invoice/subscription object. Always read status before changing entitlements, and treat unknown fields as additive.
Delivery guarantees
At-least-once and unordered. Deduplicate on the event id; when order matters, compare created or re-fetch the object from the App API.