Going live
A checkout session will not complete until all four of these are in place. The first three are hard requirements; the fourth is optional but worth setting.
1. KYC approval
Submit your documents in the portal and wait for the account status to become active. Until then POST /api/v1/checkout/initialize answers 403 Vendor account is not active to process payments, and any claim answers 403 VENDOR_INACTIVE.
2. At least one paired phone
Pairing an Android phone is what lets ToruPay see the money arrive. During pairing the phone generates its own EC P-256 key inside AndroidKeyStore, and only the public key leaves it.
- The receiving number on the device is the number buyers are told to send to.
- A device declares which providers it covers. The hosted checkout only offers providers with active device coverage, so buyers are never shown a method that cannot be confirmed.
- Pair a second phone if the first one is ever off, out of credit or being replaced.
3. A callback secret
The secret is what signs x-kronx-signature. Set it in the portal and store the same value in your own environment. It is encrypted at rest with AES-256-GCM and is never shown again after it is saved.
4. An allowed-host list
Optional. Once set, a webhook_url whose hostname is not in the list is refused at initialize time with a 400. It is a cheap way to make sure a compromised integration cannot point your callbacks somewhere else.
Pre-launch checklist
| Check | How to confirm |
|---|---|
| The API key lives only on your server | Search your frontend bundle and your repository for the key prefix. |
webhook_url is https and publicly reachable | A private or loopback address is refused at send time, not silently skipped. |
| Your endpoint verifies the signature in constant time | Send it a request with a wrong signature and confirm you return 401. See webhooks. |
Your endpoint is idempotent on x-kronx-event-id | Replay the same event twice and confirm the order is only fulfilled once. |
| Your endpoint answers within 10 seconds | Return 200 first, then do the slow work. |
| You handle expiry | Decide what your order does when a session passes expires_at with no payment. |
| You have run one real payment end to end | A small amount from a real phone, through the hosted page, to a callback your server logged. |
When something does not match
An SMS that arrives after the session expired still lands in the ledger as parsed_unclaimed. Nothing is lost, and the transaction can be reconciled to the order from the portal once the money is confirmed.