Commit 953c595eab for woocommerce
commit 953c595eab549f339822c39ffadc089a084c81a2
Author: Wesley Rosa <wesleyjrosa@gmail.com>
Date: Mon Dec 15 16:09:19 2025 -0300
Avoid capturing orders requiring payer action in PayPal Standard (#62433)
* Avoid capturing orders requiring payer action in PayPal Standard
* Fix lint issues
* Add changefile(s) from automation for the following project(s): woocommerce
* Fix tests
* Using constant
---------
Co-authored-by: github-actions <github-actions@github.com>
diff --git a/plugins/woocommerce/changelog/62433-fix-avoid-capturing-orders-requiring-payer-action b/plugins/woocommerce/changelog/62433-fix-avoid-capturing-orders-requiring-payer-action
new file mode 100644
index 0000000000..426e12b180
--- /dev/null
+++ b/plugins/woocommerce/changelog/62433-fix-avoid-capturing-orders-requiring-payer-action
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Stores the PayPal Standard order status upon creation and skips capture when payer action is required.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-constants.php b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-constants.php
index 931af7b86a..a607ec3ce8 100644
--- a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-constants.php
+++ b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-constants.php
@@ -26,10 +26,12 @@ class WC_Gateway_Paypal_Constants {
/**
* PayPal payment statuses.
*/
- const STATUS_COMPLETED = 'COMPLETED';
- const STATUS_APPROVED = 'APPROVED';
- const STATUS_CAPTURED = 'CAPTURED';
- const STATUS_AUTHORIZED = 'AUTHORIZED';
+ const STATUS_COMPLETED = 'COMPLETED';
+ const STATUS_APPROVED = 'APPROVED';
+ const STATUS_CAPTURED = 'CAPTURED';
+ const STATUS_AUTHORIZED = 'AUTHORIZED';
+ const STATUS_PAYER_ACTION_REQUIRED = 'PAYER_ACTION_REQUIRED';
+ const VOIDED = 'VOIDED';
/**
* PayPal payment intents.
diff --git a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
index a6e2e94dbd..450a2c9ba0 100644
--- a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
+++ b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
@@ -196,6 +196,9 @@ class WC_Gateway_Paypal_Request {
// Save the PayPal order ID to the order.
$order->update_meta_data( '_paypal_order_id', $response_data['id'] );
+ // Save the PayPal order status to the order.
+ $order->update_meta_data( '_paypal_status', $response_data['status'] );
+
// Remember the payment source: payment_source is not patchable.
// If the payment source is changed, we need to create a new PayPal order.
$order->update_meta_data( '_paypal_payment_source', $payment_source );
@@ -363,12 +366,25 @@ class WC_Gateway_Paypal_Request {
}
$paypal_status = $order->get_meta( '_paypal_status', true );
+
// Skip if the payment is already captured.
if ( WC_Gateway_Paypal_Constants::STATUS_CAPTURED === $paypal_status || WC_Gateway_Paypal_Constants::STATUS_COMPLETED === $paypal_status ) {
WC_Gateway_Paypal::log( 'PayPal payment is already captured. Skipping capture. Order ID: ' . $order->get_id() );
return;
}
+ // Skip if the payment requires payer action.
+ if ( WC_Gateway_Paypal_Constants::STATUS_PAYER_ACTION_REQUIRED === $paypal_status ) {
+ WC_Gateway_Paypal::log( 'PayPal payment requires payer action. Skipping capture. Order ID: ' . $order->get_id() );
+ return;
+ }
+
+ // Skip if the payment is voided.
+ if ( WC_Gateway_Paypal_Constants::VOIDED === $paypal_status ) {
+ WC_Gateway_Paypal::log( 'PayPal payment voided. Skipping capture. Order ID: ' . $order->get_id() );
+ return;
+ }
+
$authorization_id = $this->get_authorization_id_for_capture( $order );
if ( ! $authorization_id ) {
WC_Gateway_Paypal::log( 'Authorization ID not found to capture authorized payment. Order ID: ' . $order->get_id() );
@@ -557,7 +573,7 @@ class WC_Gateway_Paypal_Request {
*/
private function get_approve_link( $http_code, $response_data ) {
// See https://developer.paypal.com/docs/api/orders/v2/#orders_create.
- if ( isset( $response_data['status'] ) && 'PAYER_ACTION_REQUIRED' === $response_data['status'] ) {
+ if ( isset( $response_data['status'] ) && WC_Gateway_Paypal_Constants::STATUS_PAYER_ACTION_REQUIRED === $response_data['status'] ) {
$rel = 'payer-action';
} else {
$rel = 'approve';
diff --git a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
index 43d2944c90..52996cd977 100644
--- a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
+++ b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
@@ -80,7 +80,7 @@ class WC_Gateway_Paypal_Webhook_Handler {
$status = $event['resource']['status'] ?? null;
$paypal_order_id = $event['resource']['id'] ?? null;
- if ( 'APPROVED' === $status ) {
+ if ( WC_Gateway_Paypal_Constants::STATUS_APPROVED === $status ) {
WC_Gateway_Paypal::log( 'PayPal payment approved. Order ID: ' . $order->get_id() );
$order->update_meta_data( '_paypal_status', $status );
$order->add_order_note(
diff --git a/plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php b/plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
index 6f5266b897..fb94fa55c3 100644
--- a/plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
+++ b/plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
@@ -155,8 +155,9 @@ class WC_Gateway_Paypal_Request_Test extends \WC_Unit_Test_Case {
),
'body' => wp_json_encode(
array(
- 'id' => '123',
- 'links' => array(
+ 'id' => '123',
+ 'status' => 'CREATED',
+ 'links' => array(
array(
'rel' => 'approve',
'href' => 'https://www.paypal.com/checkoutnow?token=123',