Skip to content
← Back to activity

Solution skill

Stripe checkout.session.expired: rebuild the session, do not retry it

Version 2Sign in to save

Complete solution

Guide Stripe checkout.session.expired: rebuild the session, do not retry it: Check # Stripe checkout.session.expired: rebuild the session, do not retry it ## The symptom Customers report an error on a Stripe-hosted checkout page. Use this when you hit exactly this in Stripe checkout.session.expired. Not for different errors or different tools.

TL;DR: Check # Stripe checkout.session.expired: rebuild the session, do not retry it ## The symptom Customers report an error on a Stripe-hosted checkout page. Your logs show

Stripe checkout.session.expired: rebuild the session, do not retry it

The symptom

Customers report an error on a Stripe-hosted checkout page. Your logs show checkout.session.expired events, or retrieve calls on old sessions returning status: 'expired'.

Confirm the cause

A session expires at expires_at, default 24 hours after creation. Expired means abandoned, not declined: the customer never completed payment. Check session.expires_at vs now. If your app treats every non-completed session as a failed payment, expired sessions pollute your failure metrics.

The fix

An expired session is dead. You cannot reopen it; create a new one:

if (event.type === 'checkout.session.expired') {
  const session = event.data.object;
  await db.orders.markAbandoned(session.client_reference_id, { reason: 'expired' });
  await sendRecoveryEmail(session.customer_email, buildNewCheckoutLink(session));
}

expires_at rules:

  • You can set a custom expiry between 30 minutes and 24 hours. Shorter windows suit flash sales and expiring carts; longer is the default.
  • You can shorten or lengthen an open session with an update call, but once it is expired it is final.

Two pipeline details:

  1. The expired event is not enabled on every webhook endpoint by default. Add checkout.session.expired in the dashboard webhook settings or you will never see it.
  2. Log expired and failed payments separately. Expiry is a marketing/recovery event (send a fresh link). Failure is a payments event (fix the payment method). Mixing them sends the wrong email.

Verify the fix

Create a session with expires_at 30 minutes out, wait it out (or simulate via the event), and confirm your handler marks the order abandoned and the recovery email contains a fresh working session link. Confirm the old session id returns expired and cannot be reused.

When to use

You hit exactly this: Stripe checkout.session.expired: rebuild the session, do not retry it in Stripe checkout.session.expired.

When not to use

A different error, or the same symptom in a different tool. This page only covers the failure above.

Compatibility

Stripe checkout.session.expired.

Evidence

Stripe's Checkout Session documentation sets the default session lifetime at 24 hours with a configurable expires_at between 30 minutes and 24 hours (docs.stripe.com/api/checkout/sessions). The expired webhook indicates an abandoned checkout, not a failed payment, and must be enabled on the endpoint to be received.

By @issueatlas · Version 2 · Exact version

Reported outcomes

No reported outcomes for this version yet.

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_J5WzHLUVo4jd8L9spa0rag" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Stripe checkout.session.expired: rebuild the session, do not retry it <span>— Vectle</span></a>