# Fixing "agent charged an expired card for 3 renewals: used the cached card"

## TL;DR

Stop caching card details in agent memory or local state. Before every renewal charge, fetch the customer's current default payment method from the provider and check its expiry month and year. If it is expired or expiring this cycle, ask the customer for a new method instead of charging. The stale cache is the bug, not the card.

```text
agent charged an expired card for 3 renewals: used the cached card
```

## Steps

1. Find every place the agent stores card details outside the provider (memory, files, databases, conversation context) and delete them.
   Expected: A grep for card number or expiry fields in your own storage returns nothing.
2. Change the renewal flow to fetch the payment method from the provider by its id right before charging.
   Expected: The charge request uses the provider's current expiry, not a copied one.
3. Add an expiry guard: if exp year and month are at or before the current month, skip the charge and request a new payment method.
   Expected: Renewals against expired cards never reach the charge step.
4. Set up dunning: on a decline, retry on a schedule and email the customer a payment-update link instead of retrying the same dead card forever.
   Expected: Failed renewals recover through the customer, not through retries of an expired card.

## Prevention

Never persist card details in agent state. Treat the provider as the single source of truth and fetch fresh on every billing run. Prefer provider-managed payment method ids and setup intents over raw card data, which also keeps you out of PCI scope.

## Detection

Watch for renewal charges where the card expiry at charge time was already past. Alert when the same payment method id fails 2 or more consecutive renewals. Compare the expiry the agent used against the expiry on the provider's record; a mismatch means a cached copy slipped through.

## Recovery

Refund or void any charges that should not have happened. Contact each affected customer, explain the billing error plainly, and send a secure link to update their payment method. Re-run the renewal only after the new method is confirmed.

## When this applies

- Renewal charges fail with expired-card declines on a card the customer already replaced
- Charges succeed against a card the customer thought was removed
- The agent's stored card details differ from the provider's current record

## When it doesn't

- The card is current and the decline is for insufficient funds or a bank block
- This is a first-time checkout, not a renewal
- The customer only has one card on file and it is genuinely expired (that is a dunning problem, not a caching problem)

## Compatibility

Stripe API (PaymentMethods, exp_month and exp_year fields), Adyen API v68 and later (stored payment details with expiryDate), PayPal REST API v2 (vaulted payment tokens). Any provider that returns card expiry on the payment method object.

### agent billed the old card after the customer updated it

### renewal used a card number saved in the agent's notes

### expired card charged because the agent skipped the expiry check

## Why it happens

Agents optimize for fewer API calls, so they copy card details into working memory during setup and reuse them for months. Meanwhile the customer updates their card in the provider's portal. The agent's copy goes stale silently because nothing re-validates it before each charge.

## Edge cases

- Cards expiring in the current month: treat as expired for renewals that post after month end
- Multiple payment methods on file: always use the marked default, and re-resolve which one is default at charge time
- Test cards in sandbox have far-future expiries that mask this bug; simulate an expired card explicitly before shipping

## Provenance

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