Duplicate orders and duplicate charges are related but not identical. Two WooCommerce orders may point to one provider payment, one order may contain two charges, or both systems may contain duplicates. Each pattern has a different cause and refund risk.
Stop uncontrolled retesting. Reconcile every transaction before deleting, refunding or changing an order status.
Classify the duplicate pattern
Create a privacy-safe table containing WooCommerce order IDs, amounts, timestamps, customer reference, gateway transaction IDs and status. Determine whether product lines and addresses match.
Pattern A: two orders, one transaction
Pattern B: one order, two transactions
Pattern C: two orders, two transactions
Pattern D: one real charge plus one temporary authorisation
Confirm captured payment status in the provider dashboard. A pending bank hold is not necessarily a second completed charge.
Inspect customer submission behaviour
Slow checkout responses can encourage repeated taps, refreshes or opening another tab. Browser Network data may show multiple checkout requests seconds apart.
The button should become visibly busy after a valid submission, but a disabled button is only a user-interface safeguard. Server-side payment processing must remain safe when a request is retried.
Check mobile layouts and error visibility. Customers may press again because the first request completed invisibly.
Trace gateway idempotency
Many payment APIs accept an idempotency key so a retried request returns the original result instead of charging again. Review the gateway plugin’s logs and API request references.
One logical payment attempt should retain:
- one merchant/order reference
- one idempotency key where supported
- one stored provider transaction ID
Never print API secrets or full payment payloads. If custom code creates payment requests, verify the key remains stable across network retries but changes for a genuinely new order.
Check webhook replay
Providers retry webhooks when the store times out or returns a non-success status. The same event can therefore arrive several times. A handler must recognise the provider event or transaction already processed.
Inspect delivery history and WooCommerce order notes. If identical event IDs create repeated fulfilment, email or status effects, repair idempotency before replaying anything.
Respond promptly to the provider, then queue slow non-critical processing. A webhook that performs a long ERP export before responding invites retries.
Review custom hooks
Custom code may create an order on more than one lifecycle hook, call payment APIs from both browser and server, or run twice when checkout updates.
Search for wc_create_order(), gateway API calls and hooks attached to checkout or thank-you pages. The thank-you page can be refreshed and must not be treated as proof that payment should run again.
Do not edit WooCommerce core. Put corrected logic in maintained site code with logging and tests.
Check scheduled tasks and external systems
Failed scheduled actions may be retried, while an ERP or automation platform may resend order creation after a timeout. Compare correlation IDs across systems.
A timeout means “unknown outcome,” not “nothing happened.” Before a retry, the caller should query by stable reference or use an idempotent operation.
Reconcile customer impact
Identify which charge and order should remain. Refund confirmed duplicate captures through the gateway’s supported process and add private order notes linking all references.
Restore stock once where appropriate and prevent duplicate fulfilment. Do not simply trash an order: accounting, gateway, inventory and customer email records would diverge.
Communicate facts and refund timing without blaming the customer for pressing twice.
Verify the repair
Test a normal payment, a deliberate double click, a browser retry and a replayed sandbox webhook. Each logical attempt should result in one order, one captured charge and one fulfilment path.
Recurring monitoring should flag same-customer same-amount orders in a short interval, reused references and multiple captured payments per order. Detection complements idempotent design; it does not replace it.