Commit 09707f2952a for woocommerce
commit 09707f2952a9d85278d63044e76fd17eb2b3b7a9
Author: Mayisha <33387139+Mayisha@users.noreply.github.com>
Date: Thu Mar 5 22:11:40 2026 +0600
PayPal Standard: Prevent duplicate order details requests on 404 (#63464)
* prevent repeated auth capture method call when get order returns 404
* add tests
* Add changefile(s) from automation for the following project(s): woocommerce
* address feedback about test
diff --git a/plugins/woocommerce/changelog/63464-fix-paypal-standard-404-prevent-duplicate-order-details-requests b/plugins/woocommerce/changelog/63464-fix-paypal-standard-404-prevent-duplicate-order-details-requests
new file mode 100644
index 00000000000..88f011fe8b7
--- /dev/null
+++ b/plugins/woocommerce/changelog/63464-fix-paypal-standard-404-prevent-duplicate-order-details-requests
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent repeated PayPal Standard order details API requests when the first request returns 404.
\ No newline at end of file
diff --git a/plugins/woocommerce/src/Gateways/PayPal/Request.php b/plugins/woocommerce/src/Gateways/PayPal/Request.php
index 992e5ba889b..e5d361088d5 100644
--- a/plugins/woocommerce/src/Gateways/PayPal/Request.php
+++ b/plugins/woocommerce/src/Gateways/PayPal/Request.php
@@ -541,7 +541,18 @@ class Request {
\WC_Gateway_Paypal::log( 'Authorization ID not found, trying to retrieve from PayPal order details as a fallback for backwards compatibility. Order ID: ' . $order->get_id() );
try {
- $order_data = $this->get_paypal_order_details( $paypal_order_id );
+ $order_data = $this->get_paypal_order_details( $paypal_order_id );
+ } catch ( Exception $e ) {
+ \WC_Gateway_Paypal::log( 'Error retrieving PayPal order details. Order ID: ' . $order->get_id() . '. Error: ' . $e->getMessage() );
+ // On 404 (order not found), set flag to prevent repeated API calls.
+ if ( false !== strpos( $e->getMessage(), 'HTTP 404' ) ) {
+ $order->update_meta_data( PayPalConstants::PAYPAL_ORDER_META_AUTHORIZATION_CHECKED, 'yes' );
+ $order->save();
+ }
+ return null;
+ }
+
+ try {
$authorization_data = $this->get_latest_transaction_data(
$order_data['purchase_units'][0]['payments']['authorizations'] ?? array()
);
diff --git a/plugins/woocommerce/tests/php/src/Gateways/PayPal/RequestTest.php b/plugins/woocommerce/tests/php/src/Gateways/PayPal/RequestTest.php
index 1d471bd085e..6cd66dc448f 100644
--- a/plugins/woocommerce/tests/php/src/Gateways/PayPal/RequestTest.php
+++ b/plugins/woocommerce/tests/php/src/Gateways/PayPal/RequestTest.php
@@ -522,6 +522,53 @@ class RequestTest extends \WC_Unit_Test_Case {
$this->assertEquals( 0, $capture_api_call_count, 'Expected no capture_auth API call when PayPal Order ID is missing' );
}
+ /**
+ * Test that 404 from get order details sets authorization_checked flag and prevents repeated requests.
+ *
+ * @return void
+ */
+ public function test_capture_authorized_payment_not_attempted_when_order_details_404(): void {
+ $order = \WC_Helper_Order::create_order();
+ $order->update_meta_data( PayPalConstants::PAYPAL_ORDER_META_ORDER_ID, 'PAYPAL_ORDER_123' );
+ $order->save();
+
+ $order_details_call_count = 0;
+ add_filter(
+ 'pre_http_request',
+ function ( $value, $parsed_args, $url ) use ( &$order_details_call_count ) {
+ if (
+ isset( $parsed_args['method'] ) &&
+ 'GET' === $parsed_args['method'] &&
+ strpos( $url, 'order/PAYPAL_ORDER_123' ) !== false
+ ) {
+ ++$order_details_call_count;
+ return array(
+ 'response' => array( 'code' => 404 ),
+ 'body' => wp_json_encode( array( 'message' => 'Order not found' ) ),
+ );
+ }
+
+ return $value;
+ },
+ 10,
+ 3
+ );
+
+ $request = new PayPalRequest( new \WC_Gateway_Paypal() );
+ $request->capture_authorized_payment( $order );
+
+ // First call: one order details request, flag should be set.
+ $this->assertEquals( 1, $order_details_call_count, 'Expected one order details request on first capture attempt' );
+ $order = wc_get_order( $order->get_id() );
+ $this->assertSame( 'yes', $order->get_meta( PayPalConstants::PAYPAL_ORDER_META_AUTHORIZATION_CHECKED, true ), 'Expected authorization_checked flag to be set after 404' );
+
+ // Second call: should not hit order details again because flag is set.
+ $request->capture_authorized_payment( $order );
+ $this->assertEquals( 1, $order_details_call_count, 'Expected no additional order details request after 404 (flag prevents retry)' );
+
+ remove_all_filters( 'pre_http_request' );
+ }
+
/**
* Test capture is skipped when payment is already captured (via capture_id).
*