Commit 6145d34b19 for woocommerce
commit 6145d34b19df6ca6f2cc8191a7de06efce415b9e
Author: Mayisha <33387139+Mayisha@users.noreply.github.com>
Date: Wed Dec 17 12:15:20 2025 +0600
PayPal Standard: Refactor order address update functionality (#62418)
* set flag to detect address update attempted
* store different meta for success and failure
* add helper method to update address in order
* update address when a paypal order is approved
* use helper method
* fix phpstan issues
* update baseline
* use boolean meta
* add detailed comment
* use explode with limit
* store boolean in wc way
diff --git a/plugins/woocommerce/changelog/62418-fix-paypal-standard-prevent-repeated-order-address-update b/plugins/woocommerce/changelog/62418-fix-paypal-standard-prevent-repeated-order-address-update
new file mode 100644
index 0000000000..c02883c0cf
--- /dev/null
+++ b/plugins/woocommerce/changelog/62418-fix-paypal-standard-prevent-repeated-order-address-update
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Refactored the address update functionality of PayPal Standard with improved status tracking to prevent duplicate requests.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php b/plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
index 231d9d9ef3..dd07241b8c 100644
--- a/plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
+++ b/plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
@@ -246,7 +246,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$order = wc_get_order( $order_id );
// Bail early if the order is not a PayPal order.
- if ( ! $order || $order->get_payment_method() !== $this->id ) {
+ if ( ! $order instanceof WC_Order || $order->get_payment_method() !== $this->id ) {
return;
}
@@ -260,52 +260,32 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
return;
}
+ /**
+ * Bail early if the addresses update already have been attempted (whether successful or not).
+ * Prevent duplicate address update attempts from the thankyou page.
+ *
+ * Address updates are primarily handled by the PayPal webhook when the order is approved.
+ * This method serves as a fallback if the webhook hasn't fired yet,
+ * but we want to show the correct addresses to the customer on the thankyou page.
+ * Once an attempt is made (meta exists), we skip to prevent repeated API calls on page reloads.
+ * The webhook handler will always update the addresses.
+ */
+ $addresses_update_attempted = $order->meta_exists( '_paypal_addresses_updated' );
+ if ( $addresses_update_attempted ) {
+ return;
+ }
+
try {
include_once WC_ABSPATH . 'includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php';
$paypal_request = new WC_Gateway_Paypal_Request( $this );
$paypal_order_details = $paypal_request->get_paypal_order_details( $paypal_order_id );
- // Update the shipping information.
- $full_name = $paypal_order_details['purchase_units'][0]['shipping']['name']['full_name'] ?? '';
- if ( ! empty( $full_name ) ) {
- $approximate_first_name = explode( ' ', $full_name )[0] ?? '';
- $approximate_last_name = explode( ' ', $full_name )[1] ?? '';
- $order->set_shipping_first_name( $approximate_first_name );
- $order->set_shipping_last_name( $approximate_last_name );
- }
-
- $shipping_address = $paypal_order_details['purchase_units'][0]['shipping']['address'] ?? array();
- if ( ! empty( $shipping_address ) ) {
- $order->set_shipping_country( $shipping_address['country_code'] ?? '' );
- $order->set_shipping_postcode( $shipping_address['postal_code'] ?? '' );
- $order->set_shipping_state( $shipping_address['admin_area_1'] ?? '' );
- $order->set_shipping_city( $shipping_address['admin_area_2'] ?? '' );
- $order->set_shipping_address_1( $shipping_address['address_line_1'] ?? '' );
- $order->set_shipping_address_2( $shipping_address['address_line_2'] ?? '' );
- }
-
- // Update the billing information.
- $full_name = $paypal_order_details['payer']['name'] ?? array();
- $email = $paypal_order_details['payer']['email_address'] ?? '';
- if ( ! empty( $full_name ) ) {
- $order->set_billing_first_name( $full_name['given_name'] ?? '' );
- $order->set_billing_last_name( $full_name['surname'] ?? '' );
- $order->set_billing_email( $email );
- }
-
- $billing_address = $paypal_order_details['payer']['address'] ?? array();
- if ( ! empty( $billing_address ) ) {
- $order->set_billing_country( $billing_address['country_code'] ?? '' );
- $order->set_billing_postcode( $billing_address['postal_code'] ?? '' );
- $order->set_billing_state( $billing_address['admin_area_1'] ?? '' );
- $order->set_billing_city( $billing_address['admin_area_2'] ?? '' );
- $order->set_billing_address_1( $billing_address['address_line_1'] ?? '' );
- $order->set_billing_address_2( $billing_address['address_line_2'] ?? '' );
- }
-
- $order->save();
+ // Update the addresses in the order with the addresses from the PayPal order details.
+ WC_Gateway_Paypal_Helper::update_addresses_in_order( $order, $paypal_order_details );
} catch ( Exception $e ) {
self::log( 'Error updating addresses for order #' . $order_id . ': ' . $e->getMessage(), 'error' );
+ $order->update_meta_data( '_paypal_addresses_updated', 'no' );
+ $order->save();
}
}
diff --git a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
index a961821372..58212c0fc3 100644
--- a/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
+++ b/plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
@@ -152,4 +152,64 @@ class WC_Gateway_Paypal_Helper {
return $masked_local . '@' . $domain;
}
+
+ /**
+ * Update the addresses in the order.
+ *
+ * @param WC_Order|null $order The order object.
+ * @param array $paypal_order_details The PayPal order details.
+ * @return void
+ */
+ public static function update_addresses_in_order( ?WC_Order $order, array $paypal_order_details ): void {
+ if ( empty( $order ) || empty( $paypal_order_details ) ) {
+ return;
+ }
+
+ // Bail early if '_paypal_addresses_updated' is 'yes', meaning the addresses update already have been successful.
+ if ( 'yes' === $order->get_meta( '_paypal_addresses_updated', true ) ) {
+ return;
+ }
+
+ // Update the shipping information.
+ $full_name = $paypal_order_details['purchase_units'][0]['shipping']['name']['full_name'] ?? '';
+ if ( ! empty( $full_name ) ) {
+ $name_parts = explode( ' ', $full_name, 2 );
+ $approximate_first_name = $name_parts[0] ?? '';
+ $approximate_last_name = isset( $name_parts[1] ) ? $name_parts[1] : '';
+ $order->set_shipping_first_name( $approximate_first_name );
+ $order->set_shipping_last_name( $approximate_last_name );
+ }
+
+ $shipping_address = $paypal_order_details['purchase_units'][0]['shipping']['address'] ?? array();
+ if ( ! empty( $shipping_address ) ) {
+ $order->set_shipping_country( $shipping_address['country_code'] ?? '' );
+ $order->set_shipping_postcode( $shipping_address['postal_code'] ?? '' );
+ $order->set_shipping_state( $shipping_address['admin_area_1'] ?? '' );
+ $order->set_shipping_city( $shipping_address['admin_area_2'] ?? '' );
+ $order->set_shipping_address_1( $shipping_address['address_line_1'] ?? '' );
+ $order->set_shipping_address_2( $shipping_address['address_line_2'] ?? '' );
+ }
+
+ // Update the billing information.
+ $full_name = $paypal_order_details['payer']['name'] ?? array();
+ $email = $paypal_order_details['payer']['email_address'] ?? '';
+ if ( ! empty( $full_name ) ) {
+ $order->set_billing_first_name( $full_name['given_name'] ?? '' );
+ $order->set_billing_last_name( $full_name['surname'] ?? '' );
+ $order->set_billing_email( $email );
+ }
+
+ $billing_address = $paypal_order_details['payer']['address'] ?? array();
+ if ( ! empty( $billing_address ) ) {
+ $order->set_billing_country( $billing_address['country_code'] ?? '' );
+ $order->set_billing_postcode( $billing_address['postal_code'] ?? '' );
+ $order->set_billing_state( $billing_address['admin_area_1'] ?? '' );
+ $order->set_billing_city( $billing_address['admin_area_2'] ?? '' );
+ $order->set_billing_address_1( $billing_address['address_line_1'] ?? '' );
+ $order->set_billing_address_2( $billing_address['address_line_2'] ?? '' );
+ }
+
+ $order->update_meta_data( '_paypal_addresses_updated', 'yes' );
+ $order->save();
+ }
}
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 52996cd977..f1f6912abb 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
@@ -92,6 +92,9 @@ class WC_Gateway_Paypal_Webhook_Handler {
);
$order->save();
+ // Update the addresses in the order with the addresses from the PayPal order details.
+ WC_Gateway_Paypal_Helper::update_addresses_in_order( $order, $event['resource'] );
+
// Authorize or capture the payment after approval.
$paypal_intent = $event['resource']['intent'] ?? null;
$links = $event['resource']['links'] ?? null;
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 74491a8d9f..dd7d1186b4 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -24759,13 +24759,13 @@ parameters:
-
message: '#^Cannot call method get_meta\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
identifier: method.nonObject
- count: 3
+ count: 2
path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
message: '#^Cannot call method get_payment_method\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
identifier: method.nonObject
- count: 2
+ count: 1
path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
@@ -24777,108 +24777,6 @@ parameters:
-
message: '#^Cannot call method save\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
identifier: method.nonObject
- count: 2
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_address_1\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_address_2\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_city\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_country\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_email\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_first_name\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_last_name\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_postcode\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_billing_state\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_address_1\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_address_2\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_city\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_country\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_first_name\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_last_name\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_postcode\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
- count: 1
- path: includes/gateways/paypal/class-wc-gateway-paypal.php
-
- -
- message: '#^Cannot call method set_shipping_state\(\) on WC_Order\|WC_Order_Refund\|true\.$#'
- identifier: method.nonObject
count: 1
path: includes/gateways/paypal/class-wc-gateway-paypal.php