# agent auth token expired mid-payment-flow: checkout restarted for the customer

## TL;DR
Refresh the auth token proactively (before it expires, not after a 401) and keep the payment state server-side so a refresh never restarts the checkout. If expiry already hit mid-flow, recover by resuming from the stored payment intent, not by rebuilding the cart. A 401 during payment should be invisible to the customer.

```text
agent auth token expired mid-payment-flow: checkout restarted for the customer
```

## Fix it

1. **Refresh before expiry.** Track the token's expiry time and refresh when under 5 minutes remain, inside the payment flow's own loop. Success check: a 30-minute 3DS wait in staging completes with zero 401s.
2. **Keep payment state out of the session.** Store the payment intent id and order id in your database, keyed by something stable like the order id. Success check: killing the agent mid-flow and restarting still finds the intent.
3. **Resume, don't rebuild.** On any auth hiccup, refresh and then retrieve the existing payment intent; only create a new one if retrieval proves it gone. Success check: the order shows exactly one payment intent after a forced expiry.
4. **Single-flight the refresh.** If parallel steps all see a 401, only one refreshes; the rest wait for the new token. Success check: logs show one refresh call, not five concurrent ones.

## When this applies

- An agent holds a user or service token across a multi-step payment (checkout, 3DS, redirects).
- Customers report being bounced to the start of checkout after a delay.

## When it doesn't

- The token was revoked or the credentials are wrong from the start; that is an auth configuration problem.
- The payment itself failed (declined, canceled); check the payment outcome, not the token.

## Compatibility

- Pattern: agent checkout flows using OAuth access tokens, short-lived session tokens, or API keys with rotation on Stripe, Adyen, or any token-authed payment API.
- Refresh logic is provider-agnostic; use the provider's documented refresh endpoint.

### Variant: refresh token itself expired during a very long flow

Same fix with longer-lived refresh handling: detect the refresh failure, re-prompt auth once, and still resume from the stored intent.

### Variant: token expired between webhook receipt and follow-up API call

The webhook handler's token died before it acted. Same fix: refresh at handler start and resume from the event's payment id.

## Why it happens

Access tokens are short-lived by design, but payment flows are long and bursty: 3DS redirects, bank apps, and customer hesitation easily outlast a 15-minute token. An agent that only handles expiry at login time dies the first time a payment takes longer than the token.

## Edge cases

- Clock skew: refresh a few minutes early, not at the exact expiry second.
- Don't retry the failed payment call with the OLD token after refresh; rebuild the client with the new token first.

## Provenance

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