## TL;DR
Your app is sending live payment requests with a test-mode key. Go back to wherever you deployed the rotated key and replace it with the live secret key from the Stripe dashboard, then redeploy. The fix is a config value, not code.

```text
testmode_charges_only
```

## Use this when
- Live charges fail with testmode_charges_only right after a key rotation.
- Staging and production configs may have crossed during the rotation.
- You need a checklist to verify key mode before the next rotation.

## Not for this skill when
- You are deliberately working in test mode. testmode_charges_only is expected there when live methods are attempted.
- The error is authentication_failed or invalid_api_key. That is a wrong key, not a wrong-mode key.

## Steps
1. ('Confirm the failing requests use a key starting with sk_test while targeting live resources. Verify: the key prefix in your environment config.', 'In the Stripe dashboard, switch to live mode and copy the correct live secret key. Verify: the prefix is sk_live.', 'Replace the key in every place it lives: env vars, secrets manager, CI config, per-worker configs. Verify: grep your configs for sk_test in production paths.', 'Redeploy or restart the services that read the key at boot. Verify: a live-mode test request succeeds without the error.', 'Write the mode into the rotation checklist: every future rotation verifies sk_live vs sk_test in each environment before rollout.')

## Variant phrasings
### stripe testmode_charges_only error
The verbatim search.

### stripe live payments failing after api key rotation
Incident phrasing.

### sk_test in production stripe
The config-smell search.


Compatibility: Stripe API, all client versions. Secret key mode is determined by the sk_live vs sk_test prefix.

## Why it happens
Stripe keys are mode-scoped: a test key can only touch test data, and any attempt to create a real charge with one is rejected with testmode_charges_only. Key rotations fail this way when someone copies the test key into production config, often because the dashboard defaults to test mode and the copy button grabs whatever mode is active. The API rejects the request by design rather than silently creating a test charge.

## Edge cases / pitfalls
- Restricted keys also carry mode. A live-mode restricted key is still live; do not confuse this error with permission errors.
- Publishable keys have the same split (pk_test vs pk_live). A live page loading pk_test breaks Elements.
- Search every service for the key, not just the billing service. Cron workers and webhooks often hold their own copy.
- Store keys in a secrets manager with environment scoping so test and live can never cross again.

## Provenance

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