Exception handling

Build for the unhappy path

Payments routinely involve three or four systems in series — your app, Paytia, the payment gateway, and the card issuer. Any one of them can hiccup. Here's how to make sure a transient failure doesn't lose a payment or charge twice.

API reference menu

Idempotency

Use a unique, deterministic reference_number per logical transaction — an order ID, an invoice number, or a hash of both. If a request fails and you retry with the same reference, Paytia and the downstream gateway deduplicate instead of creating a second charge. This is the single biggest safeguard against double-billing.

Retry the right things

Retry only on transient failures. Specifically:

  • Network errors — DNS failure, connection reset, TCP timeout.
  • HTTP 502, 503, 504 — upstream or availability issues.
  • HTTP 500 where the response body indicates a transient condition.

Do not retry:

  • HTTP 400 / 401 / 405 — the request is wrong; retrying gets the same answer.
  • HTTP 200 — the payment completed. Retrying a successful request is the fastest way to create duplicates.

Backoff

Exponential with jitter. 1s, 2s, 4s, 8s, capped at 30s. Give up after 5 retries and surface the error to a human operator rather than hammering the API indefinitely.

Circuit breaking

If a high volume of requests is failing in a short window, stop sending. A circuit breaker (open for 30–60 seconds, then half-open to test) prevents you from piling onto an outage and getting rate-limited as a side effect.

Reconciling silent failures

The worst case is a request that succeeded at the gateway but never returned a response to you — connection reset on the way back. You don't know if the payment went through. Two strategies:

  • Webhooks first. Treat the webhook as authoritative, not the HTTP response. If a webhook arrives for a transaction you never got a response for, act on the webhook.
  • Periodic reconciliation. Pull the status of unresolved transactions daily and reconcile against your own records. Surface any drift to an operator.

Webhook handlers must be idempotent

Paytia retries webhook deliveries on non-2xx responses. Your handler will see the same transaction_idmore than once. Check "have I already processed this ID?" before acting, and return 200 quickly even if the underlying work is queued asynchronously. See webhook reference for the full retry schedule.

Observability

Log every request with its X-Paytia-Request-Id (present on every response header) and your own reference_number. When something goes wrong, those two IDs let us trace the request end to end through our logs and pinpoint where it failed.

Ready to build with Paytia?

The docs are open. Keys are gated — drop us a line and we'll issue sandbox credentials so you can try every flow against a real endpoint.