From 23d5c9ad0d3467eefd96352b89295cda494b7c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Mon, 20 May 2024 16:04:52 -0300 Subject: [PATCH 01/18] Add get email logic for the connect page --- client/checkout/woopay/connect/user-connect.js | 16 ++++++++++++++++ .../direct-checkout/woopay-direct-checkout.js | 13 ++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/client/checkout/woopay/connect/user-connect.js b/client/checkout/woopay/connect/user-connect.js index 9e71772f086..d6dd3ce35f5 100644 --- a/client/checkout/woopay/connect/user-connect.js +++ b/client/checkout/woopay/connect/user-connect.js @@ -11,6 +11,7 @@ class WooPayUserConnect extends WoopayConnect { this.listeners = { ...this.listeners, getIsUserLoggedInCallback: () => {}, + getUserEmailCallback: () => {}, }; } @@ -26,6 +27,18 @@ class WooPayUserConnect extends WoopayConnect { ); } + /** + * Retrieves the email of the logged in user. + * + * @return {Promise} Resolves to the user's email if they're logged in, and an empty string otherwise. + */ + async getUserEmail() { + return await this.sendMessageAndListenWith( + { action: 'getUserEmail' }, + 'getUserEmailCallback' + ); + } + /** * Handles the callback from the WooPayConnectIframe. * @@ -38,6 +51,9 @@ class WooPayUserConnect extends WoopayConnect { case 'get_is_user_logged_in_success': this.listeners.getIsUserLoggedInCallback( data.value ); break; + case 'get_user_email_success': + this.listeners.getUserEmailCallback( data.value ); + break; } } } diff --git a/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js b/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js index b44db8d07e6..9b6fa77e3e9 100644 --- a/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js +++ b/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js @@ -79,12 +79,21 @@ class WooPayDirectCheckout { /** * Checks if the user is logged in. * - * @return {Promise<*>} Resolves to true if the user is logged in. + * @return {Promise} Resolves to true if the user is logged in. */ static async isUserLoggedIn() { return this.getUserConnect().isUserLoggedIn(); } + /** + * Retrieves the email of the logged in user. + * + * @return {Promise} Resolves to the user's email if they're logged in, and an empty string otherwise. + */ + static async getUserEmail() { + return this.getUserConnect().getUserEmail(); + } + /** * Checks if third-party cookies are enabled. * @@ -363,10 +372,12 @@ class WooPayDirectCheckout { * @return {Promise|*>} Resolves to the WooPay session response. */ static async getEncryptedSessionData() { + const email = await this.getUserEmail(); return request( buildAjaxURL( getConfig( 'wcAjaxUrl' ), 'get_woopay_session' ), { _ajax_nonce: getConfig( 'woopaySessionNonce' ), + ...( email && { email } ), } ); } From ad06dc9a8a427ccab7ddfd56e2a6aed8dfb353d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Mon, 20 May 2024 16:06:24 -0300 Subject: [PATCH 02/18] Update email retrieval logic in the init session request --- includes/woopay/class-woopay-session.php | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index 81f45bc4b69..f35b61ab26e 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -371,6 +371,29 @@ private static function get_checkout_data( $woopay_request ) { return $checkout_data; } + /** + * Retrieves the user email from the current session. + * + * @param WP_User $user The user object. + * @return string The user email. + */ + private static function get_user_email( $user ) { + if ( ! empty( $_POST['email'] ) ) { + return sanitize_email( wp_unslash( $_POST['email'] ) ); + } + + if ( ! empty( $_GET['email'] ) ) { + return sanitize_email( wp_unslash( $_GET['email'] ) ); + } + + // As a last resort, we try to get the email from the customer logged in the store. + if ( $user->ID > 0 ) { + return $user->user_email; + } + + return ''; + } + /** * Returns the initial session request data. * @@ -416,15 +439,14 @@ public static function get_init_session_request( $order_id = null, $key = null, include_once WCPAY_ABSPATH . 'includes/compat/blocks/class-blocks-data-extractor.php'; $blocks_data_extractor = new Blocks_Data_Extractor(); - $cart_data = self::get_cart_data( $is_pay_for_order, $order_id, $key, $billing_email, $woopay_request ); + $cart_data = self::get_cart_data( $is_pay_for_order, $order_id, $key, $billing_email, $woopay_request ); $checkout_data = self::get_checkout_data( $woopay_request ); + $email = self::get_user_email( $user ); if ( $woopay_request ) { $order_id = $checkout_data['order_id'] ?? null; } - $email = ! empty( $_POST['email'] ) ? wc_clean( wp_unslash( $_POST['email'] ) ) : ''; - $request = [ 'wcpay_version' => WCPAY_VERSION_NUMBER, 'user_id' => $user->ID, From 0862ff3d895f770b059a49c316c3d1b8e61f1ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Tue, 21 May 2024 10:32:15 -0300 Subject: [PATCH 03/18] Remove 1st party auth check from direct checkout feature check --- includes/class-wc-payments-features.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-payments-features.php b/includes/class-wc-payments-features.php index f74475eaef8..8a1c2bd6d29 100644 --- a/includes/class-wc-payments-features.php +++ b/includes/class-wc-payments-features.php @@ -286,7 +286,7 @@ public static function is_woopay_direct_checkout_enabled() { $is_direct_checkout_eligible = is_array( $account_cache ) && ( $account_cache['platform_direct_checkout_eligible'] ?? false ); $is_direct_checkout_flag_enabled = '1' === get_option( self::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' ); - return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled && self::is_woopay_first_party_auth_enabled(); + return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled; } /** From 1edeeaa0e09bf4d884ce7a503274fc73f8e91153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Wed, 22 May 2024 15:12:58 -0300 Subject: [PATCH 04/18] Add args to WooPay session data endpoint --- includes/admin/class-wc-rest-woopay-session-controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/includes/admin/class-wc-rest-woopay-session-controller.php b/includes/admin/class-wc-rest-woopay-session-controller.php index e35f6e32f24..6cbdff4d9a0 100644 --- a/includes/admin/class-wc-rest-woopay-session-controller.php +++ b/includes/admin/class-wc-rest-woopay-session-controller.php @@ -41,6 +41,13 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_session_data' ], 'permission_callback' => [ $this, 'check_permission' ], + 'args' => [ + 'email' => [ + 'type' => 'string', + 'format' => 'email', + 'required' => true, + ], + ], ] ); } From 75f10d6d7a55b23cf5b4106df5cfdfb6183654c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Thu, 23 May 2024 12:43:55 -0300 Subject: [PATCH 05/18] Update comment --- client/checkout/woopay/connect/user-connect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/checkout/woopay/connect/user-connect.js b/client/checkout/woopay/connect/user-connect.js index d6dd3ce35f5..3512809640a 100644 --- a/client/checkout/woopay/connect/user-connect.js +++ b/client/checkout/woopay/connect/user-connect.js @@ -30,7 +30,7 @@ class WooPayUserConnect extends WoopayConnect { /** * Retrieves the email of the logged in user. * - * @return {Promise} Resolves to the user's email if they're logged in, and an empty string otherwise. + * @return {Promise} Resolves to the user's email if they're logged in, or an empty string otherwise. */ async getUserEmail() { return await this.sendMessageAndListenWith( From 454c6b36b2b2f8eaebe3b62744e40087f0b808ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Thu, 23 May 2024 14:06:33 -0300 Subject: [PATCH 06/18] Update WC Payments Features PHP tests --- tests/unit/test-class-wc-payments-features.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/unit/test-class-wc-payments-features.php b/tests/unit/test-class-wc-payments-features.php index a27ff2c432f..a8e61c36ad4 100644 --- a/tests/unit/test-class-wc-payments-features.php +++ b/tests/unit/test-class-wc-payments-features.php @@ -183,7 +183,6 @@ public function test_is_woopay_express_checkout_enabled_returns_false_when_woopa public function test_is_woopay_direct_checkout_enabled_returns_true() { $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' ); - $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' ); $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' ); $this->mock_cache->method( 'get' )->willReturn( [ @@ -196,7 +195,6 @@ public function test_is_woopay_direct_checkout_enabled_returns_true() { public function test_is_woopay_direct_checkout_enabled_returns_false_when_flag_is_false() { $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' ); - $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' ); $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '0' ); $this->mock_cache->method( 'get' )->willReturn( [ @@ -209,7 +207,6 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_flag_i public function test_is_woopay_direct_checkout_enabled_returns_false_when_woopay_eligible_is_false() { $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' ); - $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' ); $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' ); $this->mock_cache->method( 'get' )->willReturn( [ @@ -220,7 +217,7 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_woopay $this->assertFalse( WC_Payments_Features::is_woopay_direct_checkout_enabled() ); } - public function test_is_woopay_direct_checkout_enabled_returns_false_when_first_party_auth_is_disabled() { + public function test_is_woopay_direct_checkout_enabled_returns_true_when_first_party_auth_is_disabled() { $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' ); $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '0' ); $this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' ); @@ -230,7 +227,7 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_first_ 'platform_direct_checkout_eligible' => true, ] ); - $this->assertFalse( WC_Payments_Features::is_woopay_direct_checkout_enabled() ); + $this->assertTrue( WC_Payments_Features::is_woopay_direct_checkout_enabled() ); } public function test_is_wcpay_frt_review_feature_active_returns_true() { From 34b4c7c14d18c92cd715c408ab52f00df9411524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Thu, 23 May 2024 14:07:26 -0300 Subject: [PATCH 07/18] Add WooPay eligibility check to direct checkout feature check --- includes/class-wc-payments-features.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-payments-features.php b/includes/class-wc-payments-features.php index 8a1c2bd6d29..07ca2f2968d 100644 --- a/includes/class-wc-payments-features.php +++ b/includes/class-wc-payments-features.php @@ -286,7 +286,7 @@ public static function is_woopay_direct_checkout_enabled() { $is_direct_checkout_eligible = is_array( $account_cache ) && ( $account_cache['platform_direct_checkout_eligible'] ?? false ); $is_direct_checkout_flag_enabled = '1' === get_option( self::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' ); - return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled; + return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled && self::is_woopay_eligible(); } /** From 24b855101af729872d81952c1a76ca626f8b40ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Thu, 23 May 2024 14:08:39 -0300 Subject: [PATCH 08/18] Add changelog entry --- changelog/add-2648-adapted-extensions-compatibility | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/add-2648-adapted-extensions-compatibility diff --git a/changelog/add-2648-adapted-extensions-compatibility b/changelog/add-2648-adapted-extensions-compatibility new file mode 100644 index 00000000000..6f4dfabe3d4 --- /dev/null +++ b/changelog/add-2648-adapted-extensions-compatibility @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Enable adapted extensions compatibility with Direct Checkout. From 98503ac44da476cb4125c96623894f5b129d3fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Thu, 23 May 2024 14:39:48 -0300 Subject: [PATCH 09/18] Fix psalm error --- includes/woopay/class-woopay-session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index f236a043743..4f3dfb1c09f 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -380,7 +380,7 @@ private static function get_checkout_data( $woopay_request ) { /** * Retrieves the user email from the current session. * - * @param WP_User $user The user object. + * @param \WP_User $user The user object. * @return string The user email. */ private static function get_user_email( $user ) { From a778dd3e4024168ede658c84ec61be3049fd8030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Tue, 28 May 2024 18:24:56 -0300 Subject: [PATCH 10/18] Add blog ID to connect URL params --- client/checkout/woopay/connect/woopay-connect-iframe.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/checkout/woopay/connect/woopay-connect-iframe.js b/client/checkout/woopay/connect/woopay-connect-iframe.js index d87d24d8fdc..bfe6d783c11 100644 --- a/client/checkout/woopay/connect/woopay-connect-iframe.js +++ b/client/checkout/woopay/connect/woopay-connect-iframe.js @@ -22,9 +22,11 @@ export const WooPayConnectIframe = () => { const fetchConfigAndSetIframeUrl = async () => { const testMode = getConfig( 'testMode' ); const woopayHost = getConfig( 'woopayHost' ); + const blogId = getConfig( 'woopayMerchantId' ); const urlParams = new URLSearchParams( { testMode, - source_url: window.location.href, + source_url: window.location.href, // TODO: refactor this to camel case. + blogId, } ); const tracksUserId = await getTracksIdentity(); From 0907bd1ac72ba26148d6254aee0b510d9e148e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Tue, 28 May 2024 18:25:44 -0300 Subject: [PATCH 11/18] Create decrypt signed data helper --- includes/woopay/class-woopay-utilities.php | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/includes/woopay/class-woopay-utilities.php b/includes/woopay/class-woopay-utilities.php index 588285246cd..d939218d53d 100644 --- a/includes/woopay/class-woopay-utilities.php +++ b/includes/woopay/class-woopay-utilities.php @@ -288,6 +288,41 @@ public static function encrypt_and_sign_data( $data ) { ]; } + /** + * Decode encrypted and signed data and return it. + * + * @param array $data The session, iv, and hash data for the encryption. + * @return mixed The decoded data. + */ + public static function decrypt_signed_data( $data ) { + $blog_token = ( self::get_woopay_url() === self::DEFAULT_WOOPAY_URL ) ? Jetpack_Options::get_option( 'blog_token' ) : 'dev_mode'; + + // Decode the data. + $decoded_data_request = array_map( 'base64_decode', $data ); + $passphrase = ( defined( 'WOOPAY_BLOG_ID' ) && WOOPAY_BLOG_ID === \Jetpack_Options::get_option( 'id' ) ) ? $blog_token : 'dev_mode'; + + if ( empty( $passphrase ) ) { + return null; + } + + // Verify the HMAC hash before decryption to ensure data integrity. + $computed_hash = hash_hmac( 'sha256', $decoded_data_request['data'], $passphrase ); + + // If the hashes don't match, the message may have been tampered with. + if ( ! hash_equals( $computed_hash, $decoded_data_request['hash'] ) ) { + return null; + } + + // Decipher the data data using the passphrase and the IV. + $decrypted_data = openssl_decrypt( $decoded_data_request['data'], 'aes-256-cbc', $passphrase, OPENSSL_RAW_DATA, $decoded_data_request['iv'] ); + + if ( false === $decrypted_data ) { + return null; + } + + return $decrypted_data; + } + /** * Get the persisted available countries. * From f45d41d6f0fbf62cb342a87a04aa1c68735cd13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Tue, 28 May 2024 18:32:58 -0300 Subject: [PATCH 12/18] Decrypt email if it's passed as an array --- includes/woopay/class-woopay-session.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index 4f3dfb1c09f..02e58112360 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -385,6 +385,10 @@ private static function get_checkout_data( $woopay_request ) { */ private static function get_user_email( $user ) { if ( ! empty( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( is_array( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + return sanitize_email( wp_unslash( WooPay_Utilities::decrypt_signed_data( $_POST['email'] ) ) ) ?? ''; + } return sanitize_email( wp_unslash( $_POST['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification } From ec911a3c9844135a38cba91f053c64a73b57c9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Fri, 31 May 2024 14:56:06 -0300 Subject: [PATCH 13/18] Ensure WooPay merchant ID is registered where we need --- includes/class-wc-payments.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 63e8b079157..c5445e2894a 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -1593,6 +1593,7 @@ public static function enqueue_woopay_common_config_script() { 'testMode' => $is_test_mode, 'wcAjaxUrl' => WC_AJAX::get_endpoint( '%%endpoint%%' ), 'woopaySessionNonce' => wp_create_nonce( 'woopay_session_nonce' ), + 'woopayMerchantId' => Jetpack_Options::get_option( 'id' ), 'isWooPayDirectCheckoutEnabled' => WC_Payments_Features::is_woopay_direct_checkout_enabled(), 'platformTrackerNonce' => wp_create_nonce( 'platform_tracks_nonce' ), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), From fe9c01ff7a537acb836e3fb621cf0863db9ca2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Fri, 31 May 2024 15:04:12 -0300 Subject: [PATCH 14/18] Fix redundant variable --- includes/woopay/class-woopay-utilities.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/includes/woopay/class-woopay-utilities.php b/includes/woopay/class-woopay-utilities.php index d939218d53d..e24f0f0cf91 100644 --- a/includes/woopay/class-woopay-utilities.php +++ b/includes/woopay/class-woopay-utilities.php @@ -295,26 +295,25 @@ public static function encrypt_and_sign_data( $data ) { * @return mixed The decoded data. */ public static function decrypt_signed_data( $data ) { - $blog_token = ( self::get_woopay_url() === self::DEFAULT_WOOPAY_URL ) ? Jetpack_Options::get_option( 'blog_token' ) : 'dev_mode'; - - // Decode the data. - $decoded_data_request = array_map( 'base64_decode', $data ); - $passphrase = ( defined( 'WOOPAY_BLOG_ID' ) && WOOPAY_BLOG_ID === \Jetpack_Options::get_option( 'id' ) ) ? $blog_token : 'dev_mode'; + $store_blog_token = ( self::get_woopay_url() === self::DEFAULT_WOOPAY_URL ) ? Jetpack_Options::get_option( 'blog_token' ) : 'dev_mode'; - if ( empty( $passphrase ) ) { + if ( empty( $store_blog_token ) ) { return null; } + // Decode the data. + $decoded_data_request = array_map( 'base64_decode', $data ); + // Verify the HMAC hash before decryption to ensure data integrity. - $computed_hash = hash_hmac( 'sha256', $decoded_data_request['data'], $passphrase ); + $computed_hash = hash_hmac( 'sha256', $decoded_data_request['data'], $store_blog_token ); // If the hashes don't match, the message may have been tampered with. if ( ! hash_equals( $computed_hash, $decoded_data_request['hash'] ) ) { return null; } - // Decipher the data data using the passphrase and the IV. - $decrypted_data = openssl_decrypt( $decoded_data_request['data'], 'aes-256-cbc', $passphrase, OPENSSL_RAW_DATA, $decoded_data_request['iv'] ); + // Decipher the data using the blog token and the IV. + $decrypted_data = openssl_decrypt( $decoded_data_request['data'], 'aes-256-cbc', $store_blog_token, OPENSSL_RAW_DATA, $decoded_data_request['iv'] ); if ( false === $decrypted_data ) { return null; From 2abe133d92aba2d6dd563bd1ada61ae7795dc76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Fri, 31 May 2024 15:04:58 -0300 Subject: [PATCH 15/18] Remove unnecessary fallback --- includes/woopay/class-woopay-session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index 02e58112360..015b0a49905 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -387,7 +387,7 @@ private static function get_user_email( $user ) { if ( ! empty( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification if ( is_array( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash - return sanitize_email( wp_unslash( WooPay_Utilities::decrypt_signed_data( $_POST['email'] ) ) ) ?? ''; + return sanitize_email( wp_unslash( WooPay_Utilities::decrypt_signed_data( $_POST['email'] ) ) ); } return sanitize_email( wp_unslash( $_POST['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification } From eb72230141544362f476bb65795181ba9e51e410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Fri, 31 May 2024 15:42:06 -0300 Subject: [PATCH 16/18] Refactor encrypted data approach --- client/checkout/woopay/connect/user-connect.js | 16 ++++++++-------- .../direct-checkout/woopay-direct-checkout.js | 12 ++++++------ includes/woopay/class-woopay-session.php | 13 +++++++++---- includes/woopay/class-woopay-utilities.php | 2 ++ 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/client/checkout/woopay/connect/user-connect.js b/client/checkout/woopay/connect/user-connect.js index 3512809640a..ebae7e7ecf2 100644 --- a/client/checkout/woopay/connect/user-connect.js +++ b/client/checkout/woopay/connect/user-connect.js @@ -11,7 +11,7 @@ class WooPayUserConnect extends WoopayConnect { this.listeners = { ...this.listeners, getIsUserLoggedInCallback: () => {}, - getUserEmailCallback: () => {}, + getEncryptedDataCallback: () => {}, }; } @@ -28,14 +28,14 @@ class WooPayUserConnect extends WoopayConnect { } /** - * Retrieves the email of the logged in user. + * Retrieves encrypted data from WooPay. * - * @return {Promise} Resolves to the user's email if they're logged in, or an empty string otherwise. + * @return {Promise} Resolves to an object with encrypted data. */ - async getUserEmail() { + async getEncryptedData() { return await this.sendMessageAndListenWith( - { action: 'getUserEmail' }, - 'getUserEmailCallback' + { action: 'getEncryptedData' }, + 'getEncryptedDataCallback' ); } @@ -51,8 +51,8 @@ class WooPayUserConnect extends WoopayConnect { case 'get_is_user_logged_in_success': this.listeners.getIsUserLoggedInCallback( data.value ); break; - case 'get_user_email_success': - this.listeners.getUserEmailCallback( data.value ); + case 'get_encrypted_data_success': + this.listeners.getEncryptedDataCallback( data.value ); break; } } diff --git a/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js b/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js index 9b6fa77e3e9..595d7229454 100644 --- a/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js +++ b/client/checkout/woopay/direct-checkout/woopay-direct-checkout.js @@ -86,12 +86,12 @@ class WooPayDirectCheckout { } /** - * Retrieves the email of the logged in user. + * Retrieves encrypted data from WooPay. * - * @return {Promise} Resolves to the user's email if they're logged in, and an empty string otherwise. + * @return {Promise} Resolves to an object with encrypted data. */ - static async getUserEmail() { - return this.getUserConnect().getUserEmail(); + static async getEncryptedData() { + return this.getUserConnect().getEncryptedData(); } /** @@ -372,12 +372,12 @@ class WooPayDirectCheckout { * @return {Promise|*>} Resolves to the WooPay session response. */ static async getEncryptedSessionData() { - const email = await this.getUserEmail(); + const encryptedData = await this.getEncryptedData(); return request( buildAjaxURL( getConfig( 'wcAjaxUrl' ), 'get_woopay_session' ), { _ajax_nonce: getConfig( 'woopaySessionNonce' ), - ...( email && { email } ), + ...( encryptedData && { encrypted_data: encryptedData } ), } ); } diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index 015b0a49905..6b4d32b39a1 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -385,10 +385,6 @@ private static function get_checkout_data( $woopay_request ) { */ private static function get_user_email( $user ) { if ( ! empty( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification - if ( is_array( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification - // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash - return sanitize_email( wp_unslash( WooPay_Utilities::decrypt_signed_data( $_POST['email'] ) ) ); - } return sanitize_email( wp_unslash( $_POST['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification } @@ -396,6 +392,15 @@ private static function get_user_email( $user ) { return sanitize_email( wp_unslash( $_GET['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification } + if ( ! empty( $_POST['encrypted_data'] ) && is_array( $_POST['encrypted_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + $decrypted_data = WooPay_Utilities::decrypt_signed_data( $_POST['encrypted_data'] ); + + if ( ! empty( $decrypted_data['user_email'] ) ) { + return sanitize_email( wp_unslash( $decrypted_data['user_email'] ) ); + } + } + // As a last resort, we try to get the email from the customer logged in the store. if ( $user->ID > 0 ) { return $user->user_email; diff --git a/includes/woopay/class-woopay-utilities.php b/includes/woopay/class-woopay-utilities.php index e24f0f0cf91..8b784556647 100644 --- a/includes/woopay/class-woopay-utilities.php +++ b/includes/woopay/class-woopay-utilities.php @@ -319,6 +319,8 @@ public static function decrypt_signed_data( $data ) { return null; } + $decrypted_data = json_decode( $decrypted_data, true ); + return $decrypted_data; } From e0b8be4feaed9e3daa4cce05045d48df88db5812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Mon, 3 Jun 2024 15:41:02 -0300 Subject: [PATCH 17/18] Update is user logged in condition --- includes/woopay/class-woopay-session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/woopay/class-woopay-session.php b/includes/woopay/class-woopay-session.php index 6b4d32b39a1..06fc2a93a35 100644 --- a/includes/woopay/class-woopay-session.php +++ b/includes/woopay/class-woopay-session.php @@ -402,7 +402,7 @@ private static function get_user_email( $user ) { } // As a last resort, we try to get the email from the customer logged in the store. - if ( $user->ID > 0 ) { + if ( $user->exists() ) { return $user->user_email; } From 9840b260594a4f09f386aeab34c779f4fe7b760b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= Date: Fri, 7 Jun 2024 18:12:39 -0300 Subject: [PATCH 18/18] Update HMAC hash --- includes/woopay/class-woopay-utilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/woopay/class-woopay-utilities.php b/includes/woopay/class-woopay-utilities.php index 8b784556647..3c6116c2367 100644 --- a/includes/woopay/class-woopay-utilities.php +++ b/includes/woopay/class-woopay-utilities.php @@ -305,7 +305,7 @@ public static function decrypt_signed_data( $data ) { $decoded_data_request = array_map( 'base64_decode', $data ); // Verify the HMAC hash before decryption to ensure data integrity. - $computed_hash = hash_hmac( 'sha256', $decoded_data_request['data'], $store_blog_token ); + $computed_hash = hash_hmac( 'sha256', $decoded_data_request['iv'] . $decoded_data_request['data'], $store_blog_token ); // If the hashes don't match, the message may have been tampered with. if ( ! hash_equals( $computed_hash, $decoded_data_request['hash'] ) ) {