Skip to content

ACDC card checkout: concurrent double-POST race condition marks order as Failed before 3DS authentication completes (regression in 3.4.0) #4099

@alphapete

Description

@alphapete

Describe the Bug
When a customer attempts to pay using Advanced Credit/Debit Card (ACDC) processing at checkout, a race condition causes a concurrent second POST request to fire simultaneously with the PayPal order creation request. This second request marks the WooCommerce order as "Failed" at OrderProcessor.php:205 before 3D Secure authentication has a chance to complete. The payment then fails entirely, with no recovery. Customers see "There was an error processing your order" and cannot complete their purchase.
The bug was introduced in version 3.4.0. Rolling back to 3.3.2 partially resolves the issue — the race condition still occurs but the PAYMENT.CAPTURE.COMPLETED webhook correctly recovers the order after 3DS completes, so the payment ultimately succeeds. In 3.4.0 the webhook recovery does not function correctly and the order remains failed.
To Reproduce

Enable WooCommerce PayPal Payments with Advanced Credit/Debit Card (ACDC) processing
Enable fraud protection features (reCAPTCHA and/or 3D Secure settings)
Add a product to the cart and proceed to checkout
Select "Debit & Credit Cards" as the payment method
Enter valid card details and click Place Order
Observe that two concurrent POST requests are fired — one creates the PayPal order (201 Created), and a simultaneous second POST immediately throws a payment failure error before 3DS authentication can complete

Screenshots
N/A — issue is backend/server-side. Log evidence provided in Additional Details.
Expected Behavior
A single POST request should create the PayPal order, 3DS authentication should complete, and the payment should be captured successfully. The WooCommerce order should be marked as Processing/Complete.
Actual Behavior
Two concurrent POST requests fire simultaneously when Place Order is clicked. The first successfully creates the PayPal order (HTTP 201). The second immediately throws:
Payment failed: There was an error processing your order. Please check for any charges in your payment method and review your order history before placing the order again. OrderProcessor.php:205
The WooCommerce order is marked as Failed before 3DS authentication completes. When 3DS subsequently succeeds (enrollment_status: Y, authentication_status: Y), the plugin logs Order #XXXX has status "failed", skipping payment processing and the payment is abandoned. In 3.4.0 the webhook does not recover the order. The customer cannot complete their purchase.
Environment

WordPress Version: 6.9.1
WooCommerce Version: 10.5.2
Plugin Version: 3.4.0 (bug present) / 3.3.2 (partial recovery via webhook)
PHP Version: 8.3.30
MariaDB: 10.3.39
Browser: Firefox 147 / Chrome (both affected)
Theme: Kadence Child Theme
Other relevant plugins: WP Rocket 3.20.4, Solid Security Pro 8.7.0, Rank Math SEO, Imagify

Additional Details
The issue was triggered when fraud protection was activated on 17 February 2026. Three customers were unable to complete card payments within 24 hours of activation (multiple failed attempts, orders 7971–7981). No charges were taken — PayPal dashboard confirmed 0% conversion rate for the affected period, with transactions not reaching capture stage.
Log evidence of the race condition (from woocommerce-paypal-payments-2026-02-18.log):
2026-02-18T12:07:18 DEBUG #4484 - POST https://api-m.paypal.com/v2/checkout/orders
[Request Body contains SCA_WHEN_REQUIRED]
Response: [code] => 201 / [message] => Created

2026-02-18T12:07:18 DEBUG #1885 - [New Request] POST /
2026-02-18T12:07:18 ERROR #1885 - Payment failed: There was an error processing your order... OrderProcessor.php:205

2026-02-18T12:07:26 INFO #7962 - 3DS Authentication Result:
[liability_shift] => POSSIBLE
[enrollment_status] => Y
[authentication_status] => Y

2026-02-18T12:07:30 INFO #9040 - Order #7988 has status "failed", skipping payment processing.
The concurrent second POST fires at the same timestamp as the order creation (12:07:18), confirming this is a race condition introduced in 3.4.0. The same test on 3.3.2 results in the order being recovered via the PAYMENT.CAPTURE.COMPLETED webhook, confirming the regression is isolated to 3.4.0.
Workaround: Roll back to version 3.3.2 and disable auto-updates.

System status ``` WordPress version: 6.9.1 WooCommerce version: 10.5.2 PHP version: 8.3.30 MariaDB version: 10.3.39 Plugin version exhibiting bug: 3.4.0 Last known good version: 3.3.2 Server: Plesk / Linux ```

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