What matters first: Customers can be charged twice when checkout is resubmitted, a timeout encourages another attempt, or custom retry logic lacks idempotency.
Confirm whether two charges actually exist
WooCommerce order status alone cannot prove the number of captures. Gateway transaction records, idempotency keys and timestamps are decisive.
Build a charge-to-order timeline
- Collect every gateway charge, authorisation and refund reference.
- Map charges to WooCommerce orders, checkout sessions and customer attempts.
- Review timeout, retry and webhook logs around the incident.
- Disable the failing payment path if duplicate charging is ongoing.
Separate customer retries from gateway retries
- Customer retry: The first payment completed without a usable confirmation page.
- Gateway/client retry: A request was repeated without an idempotency key.
- Webhook and return race: Two code paths both capture or finalise payment.
- Custom payment code: The integration calls capture more than once.
Contain duplicates before refunding
- Contain the issue and communicate with affected customers.
- Refund confirmed duplicate captures according to gateway and business process.
- Implement idempotency and one authoritative payment-completion path.
- Test timeout and repeated-webhook scenarios, not only the happy path.
Which layer created the second capture?
Start with the gateway ledger, not the WooCommerce status. If there are two captured transactions, compare their request IDs, idempotency keys and timestamps. A customer retry usually follows a missing or broken confirmation; a gateway or integration retry repeats the payment request itself. The repair depends on which boundary allowed the second capture.
When duplicate charges are accompanied by duplicate order creation, compare the incident with WooCommerce duplicate orders. If the payment succeeded but the order state is wrong, payment-completed orders that remain failed provide a better reconciliation path than refunding blindly.
Why refund records must stay aligned
Do not refund from WooCommerce until you know whether the gateway supports and recorded that refund; otherwise records can diverge further.
Prove retries are idempotent
- Each checkout session can produce at most one captured payment.
- Gateway and order refund records match.
- Timeouts and duplicate callbacks remain safe and idempotent.
Close the incident only after one checkout attempt can create at most one captured payment, timeout and callback replays remain idempotent, and gateway, WooCommerce and refund records reconcile transaction by transaction.
WP REPAIR INCIDENT STANDARD
Safe intervention sequence
WooCommerce order status alone cannot prove the number of captures. Gateway transaction records, idempotency keys and timestamps are decisive.
- Collect every gateway charge, authorisation and refund reference.
- Map charges to WooCommerce orders, checkout sessions and customer attempts.
- Review timeout, retry and webhook logs around the incident.
- Disable the failing payment path if duplicate charging is ongoing.
What must be verified
- Each checkout session can produce at most one captured payment.
- Gateway and order refund records match.
- Timeouts and duplicate callbacks remain safe and idempotent.
Official technical sources
Continue the diagnosis
This guide explains the diagnosis. If the site is affected now, the intervention should preserve a rollback path and verify the real business journey.
See the emergency repair service →ABOUT THIS SYMPTOM
Frequently asked questions about this guide.
Can WooCommerce's order status tell me how many times a customer was actually charged?+
No. Order status alone cannot prove the number of captures; gateway transaction records, idempotency keys and timestamps are the decisive evidence for determining whether a customer was charged once or multiple times.
Should I issue a refund directly from WooCommerce as soon as I spot a duplicate charge?+
Not until you confirm the gateway actually supports and has recorded that refund. Refunding from WooCommerce without that check can cause the gateway and order records to diverge further instead of reconciling them.
If duplicate charges are actively happening right now, what's the first move?+
Disable the failing payment path immediately if duplicate charging is ongoing, before working through root-cause analysis. Containing active harm to customers takes priority over diagnosing whether it's a customer retry, a gateway retry, or a race between webhook and return-page handling.
Does testing only the successful checkout flow catch this kind of bug?+
No. The guide specifically calls for testing timeout and repeated-webhook scenarios, not only the happy path, since duplicate payments typically emerge from retry and race conditions that never appear when everything completes on the first try.