FoxpayDocs
Custom REST APIv1.0Beta
Guide

Reliability and retries

Custom REST integrations are split across three retry surfaces: payment initialization, status polling, and webhook handling. Each has different idempotency guarantees and retry policies on Foxpay's side.

Initialization

Idempotent on (`shop_id`, `orderId`, `amountCents`, `currency`, optional `remittanceInfo`). Safe to retry.

Polling

Read-only and naturally idempotent. Safe to retry on 5xx; redirect buyers based on the latest read.

Webhooks

At-least-once delivery, up to 3 attempts. Process by `X-Foxpay-Delivery` for deduplication.

Initialization idempotency

POST /payments/initialize reuses an existing pending transaction when the new request shares the merchant store, order id, amount in cents, currency, and (when supplied) the same merchant-supplied remittance reference. The reuse response includes message: "Using existing pending transaction to prevent duplicate" so you can distinguish reuse from a fresh creation.

Match fieldSourceBehavior on mismatch
shop_idResolved from `merchantId` (or the `shopId` alias)404 invalid merchant ID
order_idRequest body orderIdNew transaction created (different order)
amountRequest body amountCentsNew transaction created (amount-correction case)
currencyRequest body currencyNew transaction created
remittance_infoRequest body remittanceInfo (only when supplied)New transaction created when the supplied value differs; if you omit remittanceInfo, any pending transaction matching the other four fields is eligible for reuse
  • Use a stable, merchant-side orderId for the lifetime of an order. Never recycle order ids across distinct buyer attempts.
  • Persist the returned paymentId immediately. If your request retries before persistence, the next call will return the same paymentId via reuse.
  • Treat HTTP 4xx responses as terminal — fix the underlying problem (auth, KYC eligibility, payment-method gating) instead of retrying.
  • Treat HTTP 5xx and network errors as retryable. Use exponential backoff with at most 3 retries; the reuse rule will dedupe successful but unacknowledged creations.

Status polling

  • GET /payments/{paymentId}/status is read-only and idempotent.
  • Poll on a backoff cadence (e.g. 1s, 2s, 5s, 10s) for buyer-facing UX. Avoid sub-second polling.
  • Always trust your latest poll over earlier reads when surfacing UI state.
  • Polling does not settle the order. Use webhooks for durable backend reconciliation.
  • Stop polling once the status reaches a terminal state (`completed`, `failed`, `cancelled`, `expired`) or a sensible timeout (typically 15 minutes for hosted checkout).

Bank-detail reads

  • GET /payments/{paymentId}/bank-details?orderId=… is also read-only.
  • Cache the response for the lifetime of a single buyer session; the IBAN, BIC, and reference are stable for a transaction.
  • Re-fetch only when the buyer needs to display transfer instructions again, not on every render.

Webhook delivery and processing

Foxpay treats outbound webhook delivery as at-least-once. Your handler is responsible for deduplication and idempotent processing.

SurfaceFoxpay sideMerchant side
Attempts (queue path)Up to 12 per delivery across ~7 days with ±20% jitterUse X-Foxpay-Delivery as the dedupe key
Attempts (legacy path)Up to 3 per delivery; being rolled forward per merchantSame dedupe key applies
Schedule (queue path)Inline, then 30s · 2m · 10m · 30m · 2h · 6h · 24h (hot), then +24h · +24h · +36h · +48h (long-tail)Persist + ack before downstream business logic
Per-attempt timeout30s on the queue path; 30s/45s/60s on the legacy pathRespond well within 30s to avoid retries
No-retry HTTP statuses400, 401, 403, 404, 410 (both paths)Surface fixable failures with these codes; surface transient failures with 5xx
Manual redeliveryPer-transaction Resend button on the Technical Info tab of /transactionsBrings the next attempt due within ~1 minute and resets the curve when reopening a terminal job
Failure escalationOne "webhook delivery failed" email when the job moves to the terminal state (exhausted / dead)Trigger redelivery from the panel after fixing

Idempotent webhook processing

  • Persist the raw signed payload first; mutate order state only after the database transaction commits.
  • Use a unique constraint on (merchant_id, foxpay_delivery_id) to absorb duplicate deliveries.
  • Allow the same `transaction_id` to receive multiple status transitions over time — only ignore exact replays of the same delivery id.
  • Ignore late deliveries that move backwards in your local order state machine; do not undo a completed order on a stale `pending` retry.

Initialization retry checklist

  • Persist the request id you are retrying so support can correlate.
  • Do not change orderId, amountCents, or currency across retries — that breaks reuse and creates duplicate pending transactions.
  • Cap merchant-side retries at 3 with exponential backoff; surface the original error to ops if all fail. Foxpay’s own retry queue absorbs longer-tail recoveries on the webhook return path — you do not need to mirror the 12-attempt curve client-side.
  • Use the response message field to know whether you triggered a fresh create or hit reuse.

Where polling and webhooks meet

  • Polling drives the buyer experience: "your bank is processing", "transfer received", "payment failed".
  • Webhook delivery drives backend truth: order fulfillment, accounting, fraud signals.
  • Both can disagree transiently — treat the most recent webhook with a verified signature as authoritative when they do.
  • Never settle an order on polling alone. The buyer-facing checkout flow can show "completed" briefly during confirmation; only a verified webhook should trigger fulfillment.