Skip to content

Errors

The gateway returns standard HTTP status codes. Error bodies are plain text strings (not JSON envelopes) — designed to be human-readable in curl output.

BodyMeaningFix
missing required fields: …A required body field is empty.Send the listed fields. See per-endpoint reference for which are required.
invoice_prefix must match …Prefix doesn’t match ^[A-Z][A-Z0-9-]{0,11}$.Uppercase letters / digits / dashes only, 1–12 chars, must start with a letter.
body read (on inbound webhooks)Body couldn’t be read.Network glitch; retry.
BodyMeaningFix
missing auth headersOne of X-PAY-Key / X-PAY-Timestamp / X-PAY-Signature absent.Check spelling — exact case X-PAY-Key.
bad timestampTimestamp not parseable as integer.Send Unix epoch seconds, integer.
timestamp out of range|now − ts| > 300s.NTP your machine.
bad signatureHMAC mismatch.See Authentication common-mistakes table.
bad signature (on /webhooks/<psp> endpoints)Inbound PSP signature invalid.Operator verifies env secret matches PSP dashboard. Not your problem unless you’re operating the gateway.
BodyMeaning
payment not foundThe id doesn’t exist OR doesn’t belong to your app. (Tenant scoping is enforced — you can’t read another product’s payments.)
unknown payment (on inbound webhooks)The PSP referenced a payment_id we don’t have. Usually means the PSP is replaying an event from before your app existed. Operator investigates.
BodyMeaningFix
idempotency conflict — different body for same keyYou reused Idempotency-Key with different request body within 24h.Use a fresh key OR send the exact same body.
BodyMeaning
no provider supports method=X for currency=YRouting found no PSP for the (currency, method) combo. Check Routing.
provider X not allowed for this appApp’s allowed_providers blocks the picked PSP. Operator extends the allowlist.
method X not enabled for provider Y on this appApp’s allowed_methods[Y] doesn’t include this method.
payment must be in 'succeeded' state to refund (got X)You tried to refund a non-succeeded payment.
payment already has a refundOnly one refund per payment today.
BodyMeaning
payment create failedDB or other gateway-internal failure. Retryable — try again with the same Idempotency-Key.
customer upsert failedSame — DB hiccup. Retryable.
store provider_refThe PSP returned a checkout but persisting it failed. Rare; operator investigates.
BodyMeaning
provider checkout failed: …PSP rejected the registration. The PSP’s verbatim error is included — common cases per-PSP guides.
refund failed: …PSP rejected the refund (window expired, already refunded, etc.).

The gateway is down or rolling out. Retryable.

These show up in the PSP’s webhook delivery dashboard, not yours. Listed for ops:

StatusWhen
200 acknowledged:true,action:noneEvent received, signature valid, but it’s not a status-changing type (PSP heartbeat, intermediate state). No-op.
200 acknowledged:true,action:already_finalizedEvent valid, but the payment was already moved to a terminal state by an earlier event. Idempotent no-op.
200 acknowledged:trueNormal success path; payment was finalized; outbound delivery enqueued.
400 bad reference …PSP-side reference (their order id) couldn’t be resolved to a gateway payment.
401 bad signaturePSP signature didn’t verify. Operator checks env secret against PSP dashboard.

Diagnostics endpoints (operator-only, behind admin auth)

Section titled “Diagnostics endpoints (operator-only, behind admin auth)”

If a payment is misbehaving, the operator can:

  • GET /admin/payments/{id} — full row including last_error.
  • GET /admin/inbound_events?payment_id=… — every PSP webhook archive entry for this payment, including signature_valid + raw body.

These are not exposed to product apps. Ask ops if you need a deep look.