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.