Going live
What to verify before you switch from pk_test_ to pk_live_, and what to watch in the first week.
Going live is a credential swap - the code is identical. That is exactly why it is worth a deliberate checklist: nothing will break at the boundary to warn you that something was untested.
Before you switch
Credentials
- [ ] A
PRODUCTIONkey issued, with explicit scopes - not an unscoped key. See API keys. - [ ]
PAYOUTS_WRITEgranted only if you genuinely disburse to third parties. If you only cash out to yourself,WITHDRAWALS_WRITEalone is strictly safer. - [ ] Raw key and secret key in a secret manager, not in a repository, an image, or a
.envcommitted anywhere. - [ ] A rotation procedure written down, and rehearsed once with test keys.
Correctness
- [ ]
Idempotency-Keysent on every write, derived from something stable, and persisted before the first attempt. - [ ] Your client reads
data.status, not justresponse.ok- a200with"status": "error"is handled as a failure. See Errors. - [ ]
429handled without parsing the body as JSON (it is plain text) and retried with jittered exponential backoff. - [ ]
409retried with the same idempotency key, never a new one. - [ ] Amounts held in a decimal type, never a binary float.
- [ ] You display and reconcile
totalDebit, not your own arithmetic.
Webhooks
- [ ]
webhookUrlset to a production HTTPS endpoint. - [ ] A signing secret held in your secret manager - and unsigned deliveries rejected.
- [ ] Signature verified against the raw body, with a timestamp tolerance and a constant-time compare.
- [ ] Handler branches on
environment- a TEST event must not be processed as real. - [ ] Deduped on
eventId, with the claim persisted for at least 30 days. - [ ] Returns
2xxin under a couple of seconds; real work happens out of band. - [ ] Unknown event types acknowledged and ignored, not 500'd.
Operations
- [ ] A daily reconciliation sweep, running and alerting - not a script someone runs by hand.
- [ ] Alerting on:
PENDINGtransactions older than expected, your own webhook handler error rate, andRateLimit-Remainingtrending toward zero. - [ ] Settlement account registered for each currency you will withdraw in, and verified with one small withdrawal made with a sandbox key.
- [ ] Someone on your side owns payment incidents and knows where the transaction id, the journey snapshot and the delivery log live.
Tested before launch, not just the happy path
Test keys resolve every payment immediately, so the PENDING cases need a sandbox key and a real
GHS 1 payment.
- [ ] A
PENDINGcollection resolved by a webhook. - [ ] A
PENDINGcollection resolved by a reconcile, with the webhook deliberately blocked. - [ ] A
FAILEDpayout, and your books left correct afterwards. - [ ] A duplicate
Idempotency-Keyreplay (200identical) and a mismatched-body reuse (422). - [ ] A forged webhook signature - rejected.
- [ ] A missing webhook signature header - rejected.
- [ ] Your endpoint returning
500, and the delivery retried and eventually succeeding.
The switch
- Confirm the live settlement account.
- With a sandbox key, run one small real transaction end to end, in each direction you support: a GHS 1 collection and a GHS 1 payout to a phone someone in the room is holding.
- Verify it in
GET /transactions/find/{id}, in the webhook your endpoint received, and in the ledger entry. - Deploy your production credentials - the code does not change.
- Only then open the flow to customers.
Caution
Do not skip step 2. Test keys mock every payment, so a sandbox transaction is the first time a real bank or mobile money network handles your integration's requests. Make it a GHS 1 one you are watching, not a customer's GHS 5,000 one.
The first week
- Read the changelog before each deploy.
- Reconcile daily and actually look at the output, even when it is clean - that is how you learn what normal looks like.
- Watch your
PENDINGageing distribution. A rail getting slower shows up there first. - Keep your test-key integration alive and deployable. It is where you will reproduce the first production oddity.
Tip
Ramp rather than switch. Route a small share of real volume through the live path for the first few days, with a kill switch back to your previous flow. Payment bugs are cheapest to find at 1% of traffic.
Next
Changelog - contract changes you may need to act on.