FAQ
Use Ctrl-F to find the symptom you are debugging. Each answer is intentionally terse and links to the chapter that holds the full explanation.
Getting started
Q: Where do I get my api_key?
Your account manager provisions it during onboarding. You receive two independent keys — one for sandbox (typically prefixed pre_), one for production. There is no self-service portal for issuance. See Security for storage and rotation rules.
Q: What's the difference between sandbox and production?
Sandbox (https://pre-api.open-pay.co) runs the same code as production (https://api.open-pay.co) against an isolated database, isolated channels, and simulated funds. Credentials, balances, and orders do not cross between the two. See Sandbox for the full comparison.
Q: Why are my requests returning 401 even though my api_key is correct?
The 401 may be due to a wrong signature or an out-of-window X-Timestamp, not the key itself. Verify the code field in the JSON envelope: INVALID_SIGNATURE and INVALID_TIMESTAMP are both 401. See Errors for the auth code catalog.
Q: Can I test the integration without a public callback URL?
No. The gateway delivers callbacks over HTTPS to a URL it can reach. For local development, use an ngrok tunnel, a request-bin, or a sandbox-only host in your own infrastructure. See Callbacks for the request shape you must accept.
Q: How do I know which channels are available in my account?
Channel routing is operator-managed and not exposed through the merchant API. Ask your account manager which currencies and channels are wired up to your merchant for sandbox and production. See Sandbox for why sandbox channel coverage is often smaller than production.
Authentication & signatures
Q: My signature keeps failing. What do I check?
In order: sort keys by ASCII (not locale), skip null values but include empty strings, append api_key with no & separator, and output lowercase hex. The four reference implementations in Authentication are byte-for-byte verified by CI — diff your output against theirs on the worked example.
Q: My timestamps are within 5 minutes — why am I getting INVALID_TIMESTAMP?
Your local clock is drifting. The 5-minute window is enforced against the gateway's NTP-synced clock, not yours. Run ntpdate (or your platform equivalent) and re-test. Do not "fix" drift by trusting the server's timestamp back. See Security.
Q: Do I need to sign GET requests?
Yes. The signature is computed over the query parameters (or just timestamp=... if there are none) plus your api_key. The worked GET example is in Quickstart, step 4.
Q: Can I use HMAC-SHA256 instead of MD5?
Not today. The signing contract is MD5(sorted_params + api_key). A future migration to HMAC-SHA256 may be announced in the changelog with a parallel-acceptance window. Do not pre-implement. See Security for the rationale.
Q: How do I rotate my api_key?
Contact your account manager. The operator provisions a second active key, you cut over, and the old key is retired after a short overlap window (~5 minutes). There is no merchant-portal self-service rotation. See Security.
Deposits & withdrawals
Q: What's the difference between Direct API and Cashier session?
Direct API (POST /deposit) creates the DepositOrder immediately and returns bank details or a payment_url for you to render. Cashier session (POST /deposit/cashier) creates a CashierSession and returns a cashier_url; without amount, the DepositOrder is created when the payer submits on the hosted page, while requests with amount create the DepositOrder immediately and skip denomination selection. See Deposits.
Q: When does status MATCHED transition to COMPLETED?
MATCHED means the gateway recognised the payment; COMPLETED means the ledger committed the funds to your balance. The transition is usually milliseconds but is not guaranteed instant. The signed callback fires at COMPLETED, not at MATCHED. See Deposits.
Q: Why is my withdrawal stuck in PENDING?
PENDING on a withdrawal means an operator must manually approve before it moves forward. This is driven by the approval engine — order amount, daily counts, and whitelist matches all play in. See Withdrawals for the approval rules.
Q: Can I cancel a withdrawal via the merchant API?
No. There is no merchant cancel endpoint. CANCELLED is reachable only via internal TransferTask cancellation by an operator. If you submitted in error, contact platform operations. See Withdrawals.
Q: What happens if my deposit expires before the payer completes?
The order transitions to EXPIRED and stops accepting payments. Funds that arrive after expiry will not auto-match and may need manual reconciliation. By default no callback fires on expiry — reconcile via GET /query/deposit/{order_no}. See Deposits.
Callbacks
Q: Why am I receiving the same callback twice?
The gateway retries any callback that did not get a 2xx ack, up to 5 attempts at 30-second intervals. Network races during legitimate retries are indistinguishable from adversarial replay. Deduplicate on (order_no, status) and ack with 200. See Callbacks.
Q: My callback URL returned 500 once. Will the gateway retry?
Yes. 5xx, timeouts, and network errors all trigger retry. The schedule is fixed at 30s, 30s, 30s, 30s — five attempts total over ~2 minutes — then FAILED. See Callbacks for the retry semantics.
Q: How long until callbacks stop retrying?
After 5 attempts (~2 minutes from the first dispatch) the NotificationLog is marked FAILED and no more attempts are made. If your endpoint was down for the whole window, all 5 attempts fail and you must reconcile via the /query/* endpoints. See Callbacks.
Q: Can callbacks arrive out of order?
Yes. During recovery from outages, deposit.matched can arrive after deposit.completed. Your (order_no, status) idempotency table prevents double-applying state, but your business logic must accept terminal states regardless of which intermediate states arrived first. See Callbacks.
Q: Do I need to return a body in the ack response?
No. Any 2xx is treated as a successful ack; the body is ignored. A body containing the word success is also accepted. Respond quickly — the per-attempt timeout is 10 seconds. See Callbacks.
Errors & retries
Q: I got a 5xx. Should I retry?
Yes, with exponential backoff and the same merchant_order_no. A 5xx on a create call does not mean the order was not created — the server may have committed before failing. After retrying, query by merchant_order_no to confirm the real state. See Errors.
Q: What does OPTIMISTIC_LOCK_ERROR mean?
A concurrent update raced your request on the same merchant balance or order row. It is transient. Retry up to 3 times with short jitter, then query the order to confirm whether the first attempt landed before resubmitting. See Errors and Withdrawals for the retry pattern.
Q: Why does duplicate merchant_order_no return 409?
Duplicate merchant_order_no now returns HTTP 409 with CONFLICT code in the standard {success, code, message, data} error envelope. Do not retry blindly; query the existing order by merchant_order_no before deciding the next step. See Deposits and Errors.
Q: Can I rely on message text for error handling?
No. The message field is not part of the API contract — wording may change without notice. Always branch on the code field in the envelope. See Errors.
Sandbox & operations
Q: I'm getting a 403 — is my source IP blocked?
Source IP is enforced only at the Cloudflare edge (WAF / Access). A block there is not a JSON envelope — it surfaces as a TLS handshake failure or an HTML challenge / block page before your request reaches the gateway. If you receive a JSON 403 AUTHORIZATION_ERROR envelope, it is not about source IP: it means the merchant is disabled or is missing the required can_deposit / can_withdraw permission. See Sandbox.
Q: How do I reproduce a specific channel failure in sandbox?
Sandbox channels can be tuned by the operations team to return specific statuses (PENDING, COMPLETED, FAILED) for testing. Ask your account manager to wire your sandbox merchant to a mock channel configured for the failure mode you need. See Sandbox.
Q: Is there a rate limit?
Cloudflare edge rate limits apply to the API hostnames. The 429 status is reserved in the error catalog but not currently enforced on all endpoints. Sandbox is typically more permissive than production — do not extrapolate sandbox throughput. See Errors and Sandbox.
Q: Are sandbox credentials different from production?
Yes — completely. A sandbox api_key is rejected by production and vice versa. Keep them in separate secret-manager paths with no fall-through defaults. See Security.
Compliance & security
Q: Can I store api_key in environment variables?
Inject it at runtime from a secret manager (AWS Secrets Manager, GCP Secret Manager, Vault) into your process environment. Do not commit it to source control even encrypted, do not bake it into container images, and never ship it in client-side code. See Security.
Q: What data does Payment Gateway log?
The platform logs request metadata, envelope codes, and order_no. Sensitive fields (api_key, X-Signature, bank_card_number, account_no) are masked. Your side must apply the same redaction discipline — see the do-log / do-not-log lists in Security.
Q: How do I report a security vulnerability?
Contact your account manager directly. Do not file vulnerability details in a public ticketing system or in the merchant portal. They will route the report to the platform security team.
Still need help?
If your symptom is not on this page, contact your account manager. Include the code field from the response envelope, your merchant_order_no, the gateway order_no if you have one, and the X-Timestamp you sent. Those four values let the platform team find your request in the logs in seconds.