Go-live checklist

Test mode and live mode run the same API against the same routing engine — the only difference is real providers and real money. Walk this list once, honestly, before you swap keys. Every item here is a production incident we'd rather you never have.

The path to live

build in test this checklist swap keys first payment first settlement sk_test_ sk_live_ (server only) stl_ report ↔ payout

Your account must also be active — onboarding and KYB checks complete. See Onboarding & KYB if the dashboard shows anything else.

1. Prove the paths you'll actually hit

Success is the easy case. In test mode, force every outcome your integration must survive and check what your system does:

ScenarioForce it withYour system should
Approved payment4242 4242 4242 4242Fulfil on the payment.succeeded webhook — not on the redirect return.
Final decline4000 0000 0000 0002card_declinedShow a retryable checkout error; never auto-retry a final decline.
Insufficient funds4000 0000 0000 9995insufficient_fundsSame as a decline — offer another method.
Provider failover"amount": 4999Nothing special — one payment, two entries in provider_attempts.
Risk review hold"amount": 4991risk.decision: "review"Treat processing as pending; act on payment.risk_review, don't time out the order.
Risk block"amount": 4992failure_code: payment_blockedFail the order cleanly; don't leak the block reason to the customer.
Abandoned redirectCreate, then never follow next_action.urlExpire the order on your side; the payment stays requires_action until it expires to canceled.

If you use manual capture, also rehearse: partial capture, a void via POST /v1/payments/{id}/cancel, and the 7-day authorization expiry.

2. Webhooks are your source of truth

  • Live endpoint registered. Create it with POST /v1/webhook_endpoints using your live key — test-mode endpoints don't carry over. Store the new whsec_… secret; it's shown once.
  • Signature verified. Reject any delivery whose Finscale-Signature doesn't verify. No exceptions in production.
  • 2xx fast, work async. Queue the event and return within seconds. Slow handlers get retried and you'll process duplicates.
  • Duplicates and ordering handled. Delivery is at-least-once and order is not guaranteed — key your handlers on event.id and the object's status, not on arrival order. Recipes in Webhooks.
  • Fulfilment is webhook-driven. The return_url hit means the customer came back, nothing more. Ship goods on payment.succeeded.

3. Keys and config

  • Live secret key server-side only. sk_live_•••••••••••• lives in your secret manager — never in client code, logs, or the repo. Rotation steps are in Authentication.
  • Test keys can't leak into production — and vice versa. Assert on boot that the key mode matches the environment; livemode on every object tells you which side created it.
  • Idempotency keys on every POST. Derive them from your order id (idem_ord_9f21_0716), so a network retry can never double-charge. See Idempotency.
  • Version pinned. Send Finscale-Version: v1 explicitly rather than relying on the account default. See Versioning.
  • Rate-limit handling. Back off on 429 and honor Retry-After — idempotency makes the retry safe. See Rate limits.

4. Day-two operations

  • Refund path tested — full and partial, from your tooling, by payment id. See Refunds.
  • Dispute inbox owned. Someone acts on dispute.created within days, not weeks — evidence has deadlines. See Disputes.
  • Reconciliation job scheduled. Pull the CSV on settlement.report.ready and match rows by reference. See Reconciliation.
  • Routing rules reviewed. Delete experiments before launch — every prefer_provider rule overrides live health ranking. See Routing rules.
  • Alerting wired. Watch your payment failure rate and webhook delivery failures; subscribe to provider.health.changed for provider incidents.

5. The first live week

  1. Make a real payment

    A small live transaction with your own card, end to end: checkout → webhook → dashboard. Refund it afterwards and confirm the refund webhook lands too.

  2. Verify the first settlement report

    When settlement.report.ready fires, check the report's totalsgross + refunds + fees = net — and that the payout amount matches what arrives at your bank.

  3. Watch provider_attempts

    A rising share of multi-attempt payments means failover is earning its keep — or that a preferred provider is struggling. Either way, you want to know.

Something looks wrong after switching? Every API response carries a request_id (req_…). Capture it in your logs from day one — it's the fastest path to an answer from your Finscale representative.