# Going live

You build entirely in test mode, then swap to live keys. Nothing else in your code
changes. This checklist covers the switch.

---

## Before you flip

Read the full list once: [Integration hard rules](./27-integration-hard-rules.md)
(also on the [reference checkout](/demos/checkout-reference.html)).

- [ ] **Charges:** a successful charge returns `status: "succeeded"`; you store the `id`.
- [ ] **Declines:** a test decline (an amount below 1.00, `"amount": 99`; the sandbox has no
      decline card) is handled gracefully (you branch on `code`, not crash). Buyer UI shows a
      **generic** message, never raw fraud reasons. See [Testing](./15-testing.md) and
      [Errors](./10-errors.md).
- [ ] **3-D Secure:** `status: "requires_action"` is handled; you complete the step-up and
      resubmit the **same** charge with `three_ds.session_id` added (never client cavv/eci).
      See [3-D Secure](./07-3ds.md).
- [ ] **Client IP:** browser + `pk_` charge (buyer peer), server `sk_` charge with the
      buyer's `device_session`, **or**, if you proxy `/check`, body `ip` = shopper. A server
      charge without a session is never scored on (or blocked by) your egress IP; it just
      has no buyer IP. See [Charges → Client IP](./03-charges.md#client-ip-fraud-signal-not-a-body-field).
- [ ] **PAN:** only `ev:` or `wp:v1:` tokens on charges; raw digits never hit the API.
- [ ] **Idempotency:** every charge and refund sends a **stable** [`Idempotency-Key`](./12-idempotency.md)
      per order (not a new UUID on every Pay click).
- [ ] **`/check` / decision token:** not pre-approval; token optional but strict if sent;
      send `card_bin` + `last4` when you have them. See [Decision token](./26-decision-token.md).
- [ ] **Device signals:** fingerprint / Kount present when CSP allows; empty fails open
      (sale still runs). See [Device data](./24-device-data-collection.md).
- [ ] **Keys:** `sk_` server-only; `pk_` + Allowed Domains for browser. Mode on the key, not body.
- [ ] **Refunds:** full and partial refunds work; you verify with `GET /v1/refunds/:id`.
- [ ] **Rate limits:** you back off on `429` honoring `Retry-After`. See [Rate limits](./14-rate-limits.md).
- [ ] **Webhooks:** your endpoint is registered, [verifies signatures](./16-webhooks.md),
      returns `2xx` fast, and is idempotent (dedupes on the event/charge id).
- [ ] **Versioning:** your key is [pinned to a version](./20-versioning.md).

---

## The switch

1. **Create live keys** in the dashboard (`pk_live_…` / `sk_live_…` / `rk_live_…`).
   Use a **restricted key** (`rk_`) scoped to only what each service needs, not a full
   secret key, wherever possible. See [Authentication](./02-authentication.md).
2. **Move secrets to your secret manager.** Never commit a live `sk_`/`rk_` key.
3. **Configure your live webhook endpoint** and store its signing secret.
4. **Set an IP allowlist** on server keys if your infrastructure has stable egress IPs.
5. **Swap the key prefix** in your config from `_test_` to `_live_`. Done.

---

## After launch

- Monitor the `Request-Id` on failures and keep them for support.
- Reconcile periodically with `GET /v1/charges` (and `/v1/events`) so a missed webhook
  never leaves your records out of sync.
- Roll keys with the dashboard's roll action. The old key keeps working during a short
  grace window so deploys can drain without an outage.
- Need higher rate limits for your live volume? Contact support.
