Failed PaymentsRetriesCustomer Experience

Handling Failed Payments Without Charging Customers Twice

By PayEurasia Team · 11 October 2026 · 3 min read

Last updated 11 October 2026

Handling Failed Payments Without Charging Customers Twice

How to handle failed, timed-out and uncertain payments safely: distinguishing true failures from unknown results, safe retries and clear customer messages.

Header image: conceptual illustration.

When a payment fails, the instinct is to let the customer try again immediately. That is right for some failures and dangerous for others. The key is to separate a definite failure from an unknown result. This guide explains how.

Three kinds of "failure"

  1. Definite decline. The provider returned a clear failure: insufficient funds, cancelled by customer, invalid details. No money moved. A new attempt is safe.
  2. Unknown result. Your request timed out, the connection dropped, or the customer closed the app mid-flow. The payment may or may not have succeeded.
  3. Late success. A payment marked failed or expired is later confirmed by the provider.

Most double charges come from treating case 2 as case 1.

Rules for unknown results

  • Keep the payment in `pending`, not `failed`.
  • Query the provider's status API using your merchant reference.
  • If the provider supports idempotency keys, retry the original request with the same key (idempotency explained).
  • Don't let the customer start a fresh payment for the same order until the status is known — or allow it, but with automatic refund of any duplicate.

Rules for retries by customers

  • Each new attempt should be a new payment record linked to the same order.
  • Only one payment per order may be marked paid; if a second succeeds, refund it automatically and log it.
  • Show the customer what happened to the previous attempt.

Customer messages

  • Declined: "Your payment wasn't completed and you haven't been charged by this attempt. You can try again or choose another method."
  • Unknown: "We're confirming your payment with the provider. Please don't pay again yet — we'll update this page automatically."
  • Duplicate detected: "We received two payments for this order. The extra payment is being refunded."

Never ask the customer for a PIN, OTP or password in your own interface or support chat.

Hypothetical example

*Invented.* A customer pays by wallet; the confirmation request times out. The page shows "confirming" rather than "failed". A background job queries status after 60 seconds, finds success, and marks the order paid. Without this, the customer would likely have paid a second time.

Monitoring

  • Count unknown results per method per hour; a spike suggests a provider or network issue.
  • Track automatic duplicate refunds; they should be rare.
  • Alert on payments stuck in pending beyond your threshold.

Frequently asked questions

Should failed payments be retried automatically?

Definite declines generally shouldn't be retried without the customer. Unknown results should be resolved by status checks, not blind retries.

What if the provider has no status API?

Use reports and webhooks, and hold the order pending until resolved. Factor this into provider selection.

Related: pre-launch API testing checklist and payment uptime and failover.

Sources

  • Stripe Docs, "Idempotent requests": https://docs.stripe.com/api/idempotent_requests

Talk to PayEurasia

Working in a high-risk vertical across South Asia? We can probably help.

Request integration →

Related articles

View all articles →