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