Guides

Sandbox vs Live

Sandbox and Live share one host and one key pair, and each request chooses with the X-Tenant-ID header. Live moves real money and needs approval.

Choosing the environment

X-Tenant-IDEnvironment
testSandbox. Available as soon as you have keys.
Missing, or anything elseSandbox on the pay-in endpoints, with no error, so a typo such as production silently runs in sandbox. Account enquiry and Query payout status refuse a missing header.
liveLive, if your business is approved. Otherwise a correctly signed request gets 403 {"code":"LIVE_NOT_ENABLED","message":"This merchant is not approved for live. Remove X-Tenant-ID or send test."}

There are no separate sandbox keys and no sandbox host. The same API key, secret, base URL and webhook URL serve both environments, so test events reach your production handler unless you check environment. Regenerating keys replaces them at once for both. The Sandbox/Live switch in the dashboard changes only what the dashboard shows, and has no effect on API calls.

Pay-ins are kept per environment: Query payin status under test never returns a live pay-in. Sandbox pay-ins do not settle real funds.

Sandbox pay-ins

Every sandbox pay-in without a gateway goes to Korapay's test mode, with the channel chosen by paymentMethod. Use paymentMethod: CARD and Korapay's test cards. BANK_TRANSFER returns a test checkout too. Never enter a real card on a test page.

PalmPay and Hydrogen have no sandbox. gateway: PALMPAY or gateway: HYDROGENPAY under test answers HTTP 200 with PALMPAY_SANDBOX_DISABLED or HYDROGEN_SANDBOX_DISABLED, and the pay-in closes as FAILED with no provider call.

What Korapay refuses

A refusal is HTTP 200 with status: "error", and providerHttpStatus and message from Korapay, so always read the body. The pay-in closes as FAILED and a failed webhook follows. Branch on providerHttpStatus and message together: 409 means two different things, and Korapay's own error codes are not passed on. These were observed in Korapay's test mode:

RequestproviderHttpStatusmessage
No customerName (null or absent)400invalid request data
No customerEmail422One or more fields are invalid. Please fix them and try again.
A reference already known to Korapay, including one another Klustar merchant used409duplicate payment reference
A card amount above 0 and below NGN 100409The transaction amount should be between NGN100 and NGN1000000 for [card] payments. Please check and try again
A card amount above NGN 1,000,000, or an amount of 0 or less422One or more fields are invalid. Please fix them and try again.

The 422 message does not name the field, so send both customer fields every time. A number or boolean in customerName reaches Korapay as a string, and you sign it as that string. An object or array never reaches Korapay: Klustar answers HTTP 400 Request body could not be read... itself, with no providerHttpStatus.

Decimal amounts such as "100.50" are accepted. Klustar itself answers 409 DUPLICATE_REFERENCE before calling Korapay when you reuse your own reference in the same environment.

References and timing

  • Generate a new reference for every attempt, using only A-Za-z0-9_-, and 5 to 50 characters. Korapay refuses anything else without naming the field. A failed attempt uses its reference up.
  • Send the customer to data.checkout_url immediately. The link is not stored and cannot be fetched again.
  • A pay-in with no provider verdict 10 minutes after creation closes as TIMED_OUT, with a failed webhook, and is not checked again, even if the customer pays later.

Going live

Upload your business documents in the dashboard. Once Klustar approves them your business is approved for live, and the same keys work with X-Tenant-ID: live. Under live, card pay-ins go to Korapay and bank transfers to Hydrogen or PalmPay, whose responses have a different shape: see Create a payin.

API pay-ins do not credit your dashboard wallet or appear in dashboard Transactions, under live as under test, so they give a dashboard payout no balance to send from. Agree how your API collections are settled with Klustar before you go live.

Tips

  • Send X-Tenant-ID on every call, so a live call can never silently fall back to test
  • Reject or ignore webhooks whose environment does not match the handler's purpose
  • Allow Klustar's IP 98.85.73.36 through your firewall so webhooks reach you