ROKIConnect

Reconciliation and operations runbook

Void, refund, receipts and listing all exist now, so recovery is possible - but only if you decided in advance what to do. Define these paths before launch.

Persist enough to recover. Store roki_payment_id, external_reference, status, total, checkout_url, expires_at and last_event_id on your order. Mirror your order id into metadata as well, so a payment inspected in the portal can be traced back without your database.

Sweep pending orders on a schedule. Re-check stored ids for orders still pending past a threshold, and for any order past its expires_at. With the list endpoint you can also sweep the other way - GET /payments?status=paid&from=... - and catch payments that are settled at ROKI but still open in your system because a webhook was lost.

Handle terminal states arriving late. expired, voided, refunded and partially_refunded can appear at any time - including from a human acting in the portal, with no API call from you. A handler that assumes "only my own calls change state" will be wrong.

Recover from a create that timed out. If a creation request returns no response, do not create a second payment. Retry once with the same Idempotency-Key: if the first attempt reached ROKI, you get that payment back; if not, it is created now.

Alert on. Repeated 401s (a rotated or broken key), 422 spikes (a deploy sending a bad payload), webhook signature failures (wrong secret, or raw-body handling broken by a middleware change), and orders paid in ROKI but still pending locally (webhooks not arriving).

Automate reversals, but keep the ordering. Void applies before settlement and refunds the full amount immediately; refund applies after, and can be partial. Implement reversal as: try void, and if it is rejected as already settled or not voidable, refund instead. Both are also available to a human in the portal, and both emit webhooks either way.