Skip to content

Duplicate PayPal captures and double stock reductions not fixed #4173

@princess-del

Description

@princess-del

Describe the Bug

The race condition causing duplicate WooCommerce orders via PayPal was reported as fixed in v3.4.1 (GitHub issue #3885), but the bug is still occurring in production as of March 12, 2026.

When a customer completes a PayPal checkout, multiple simultaneous PHP processes each independently create a separate WooCommerce order for the same PayPal transaction. PayPal charges the customer only once, but 3–4 WooCommerce orders are created with status "Processing", requiring manual intervention to cancel duplicates and reconcile stock.

The root trigger is consistent across all incidents: PayPal fires the CHECKOUT.ORDER.APPROVED webhook before WooCommerce has created a local order record. The plugin logs a warning — "WC order ID was not found in webhook event" — and multiple PHP processes then race to create the order simultaneously.

To Reproduce

  1. Add a product to the cart on a WooCommerce store running this plugin
  2. Proceed to checkout and select PayPal as the payment method
  3. Complete payment approval in the PayPal popup
  4. Observe WooCommerce orders — multiple duplicate orders are created for the same transaction

The issue appears more likely under normal server load and does not require any unusual conditions to trigger.

Screenshots

N/A — see log excerpts in Additional Details below.

Expected Behavior

One WooCommerce order is created per PayPal transaction. Duplicate orders do not occur.

Actual Behavior

3–4 WooCommerce orders are created per transaction. One order is "Failed", the rest are "Processing". PayPal is only charged once. Stock is reduced multiple times. Manual cleanup is required after every occurrence.

We have now had two separate customers affected:

  • Incident 1: February 25, 2026 — 6 duplicate orders created
  • Incident 2: March 12, 2026 — 4 duplicate orders created

Both incidents occurred before and after the supposed fix was released on March 5, 2026.

Environment

  • WordPress Version: 6.9.4
  • WooCommerce Version: 10.6.0
  • Plugin Version: 3.4.1 (WooCommerce PayPal Payments)
  • Browser: N/A — bug occurs server-side
  • Other relevant plugins: N/A — isolated to PayPal checkout flow

Additional Details

Both incidents produce an identical warning in the plugin log immediately before the race condition occurs:
WARNING: WC order ID was not found in webhook event [WEBHOOK_ID] for PayPal order [PAYPAL_ORDER_ID]

The sequence of events each time:
T+0:00 - Customer completes PayPal approval. Single PayPal order created.
T+1:02 - CHECKOUT.ORDER.APPROVED webhook received from PayPal
T+1:05 - WARNING: WC order ID not found in webhook event (order not yet in DB)
T+1:08 - PHP process 1 → creates WC order A, patches PayPal order, attempts capture
T+1:08 - PHP process 2 → creates WC order B, patches PayPal order, attempts capture
T+1:09 - PHP process 3 → creates WC order C, patches PayPal order, attempts capture
T+1:11 - PHP process 4 → creates WC order D, patches PayPal order, attempts capture
T+1:11 - One process receives 500 INTERNAL_SERVICE_ERROR from PayPal → that order = Failed
T+1:12 - Remaining processes all receive 201 Created
T+1:15 - PAYMENT.CAPTURE.COMPLETED webhook confirms single charge only

Result: One PayPal charge, 3 WooCommerce orders in "Processing" status.

The fix in v3.4.1 has not resolved this. A server-side lock or mutex around the
order creation and capture flow is needed to prevent concurrent processes from
executing this code path simultaneously.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions