# agent lost the checkout session after a long 3DS wait: payment abandoned

## TL;DR
Set the checkout session expiry well beyond the worst-case 3DS wait, store the session id against the order server-side, and recover by retrieving the session on return instead of creating a new one. If the session already expired, rebuild it from the stored order with the same idempotency key. Customers should come back to a payment, not a dead link.

```text
agent lost the checkout session after a long 3DS wait: payment abandoned
```

## Fix it

1. **Extend the expiry.** Set session `expires_at` (or the provider equivalent) to at least 24 hours; some 3DS flows with bank apps take 10+ minutes. Success check: a session created in staging is still retrievable after 12 hours.
2. **Persist the session id with the order.** Store it in your own database keyed by order id before redirecting. Success check: your order row always has a session id after redirect.
3. **Recover, don't recreate.** On the return URL, retrieve the session by the stored id and read its payment status. Success check: a customer returning after 20 minutes sees their payment state, not a fresh cart.
4. **Handle the truly-expired case.** If retrieval says expired, create a replacement session reusing the same order and idempotency key, then redirect once. Success check: only one paid payment_intent exists for the order.

## When this applies

- Customers are dropped to the cart or an error page after completing 3D Secure.
- An agent orchestrates checkout redirects and sessions can outlive a single run.

## When it doesn't

- The customer failed or canceled 3DS; that is an authentication outcome, not a session bug.
- The session was created without any redirect flow (embedded elements); check element lifecycle instead.

## Compatibility

- Pattern: agent-driven checkout on Stripe Checkout Sessions, Adyen drop-in/redirect, or any hosted session with an expiry.
- Stripe API 2024-06 and later (Checkout Session `expires_at` configurable).

### Variant: customer opened the bank app and the browser tab timed out

The tab died but the session was still valid server-side. Same fix: the return/refresh path retrieves by stored id and resumes.

### Variant: agent rebuilt the session and double-created payment intents

Same fix with emphasis on step 4: reuse the idempotency key when recreating so the provider dedupes.

## Why it happens

Hosted sessions default to short expiries. An agent that treats the redirect as fire-and-forget loses the handle; when the customer returns after a long authentication, there is nothing valid to return to.

## Edge cases

- Some providers cap expiry at a max (e.g. 24h for Stripe Checkout); design the recovery path for that ceiling.
- If the customer completed payment in the dead tab, the retrieve in step 3 may show `paid` already; never create a new session without checking first.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_KsQ1RHXsPXgljFuyOyWZ1Q
