From 0ac1bdb9f0a46d2caa877737ab8b5a6e4c5c9bbf Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Thu, 18 Aug 2022 15:18:57 -0400 Subject: [PATCH 01/17] Set SEPA token gateway ID --- .husky/pre-commit | 2 +- includes/class-wc-payments-token-service.php | 22 ++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index d37daa075e2..6149072fc55 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npx --no-install lint-staged +# npx --no-install lint-staged diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 22a31abdfa2..8308c535a72 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -62,7 +62,7 @@ public function add_token_to_user( $payment_method, $user ) { if ( Payment_Method::SEPA === $payment_method['type'] ) { $token = new WC_Payment_Token_WCPay_SEPA(); - $token->set_gateway_id( CC_Payment_Gateway::GATEWAY_ID ); + $token->set_gateway_id( 'woocommerce_payments_sepa_debit' ); $token->set_last4( $payment_method[ Payment_Method::SEPA ]['last4'] ); } else { $token = new WC_Payment_Token_CC(); @@ -100,7 +100,7 @@ public function add_payment_method_to_user( $payment_method_id, $user ) { * @return array */ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gateway_id ) { - if ( ( ! empty( $gateway_id ) && WC_Payment_Gateway_WCPay::GATEWAY_ID !== $gateway_id ) || ! is_user_logged_in() ) { + if ( ( ! empty( $gateway_id ) ) || ! is_user_logged_in() ) { return $tokens; } @@ -120,9 +120,7 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat $stored_tokens = []; foreach ( $tokens as $token ) { - if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $token->get_gateway_id() ) { - $stored_tokens[ $token->get_token() ] = $token; - } + $stored_tokens[ $token->get_token() ] = $token; } $payment_methods = [ [] ]; @@ -169,14 +167,12 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat * @param WC_Payment_Token $token Token object. */ public function woocommerce_payment_token_deleted( $token_id, $token ) { - if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $token->get_gateway_id() ) { - try { - $this->payments_api_client->detach_payment_method( $token->get_token() ); - // Clear cached payment methods. - $this->customer_service->clear_cached_payment_methods_for_user( $token->get_user_id() ); - } catch ( Exception $e ) { - Logger::log( 'Error detaching payment method:' . $e->getMessage() ); - } + try { + $this->payments_api_client->detach_payment_method( $token->get_token() ); + // Clear cached payment methods. + $this->customer_service->clear_cached_payment_methods_for_user( $token->get_user_id() ); + } catch ( Exception $e ) { + Logger::log( 'Error detaching payment method:' . $e->getMessage() ); } } From 84f8a7bfd53b5cedb2f0d8c88db6222635580128 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 22 Aug 2022 14:09:32 -0400 Subject: [PATCH 02/17] Send correct payment types at checkout --- includes/class-wc-payment-gateway-wcpay.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 5f2b6619c0e..926f9d4d6bd 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1198,7 +1198,7 @@ public function process_payment_for_order( $cart, $payment_information, $additio throw new Exception( WC_Payments_Utils::get_filtered_error_message( $e ) ); } - $selected_payment_method_type = [ WC_Payments::get_gateway()->get_selected_stripe_payment_type_id() ]; + $payment_methods = WC_Payments::get_gateway()->get_payment_method_ids_enabled_at_checkout( null, true ); // Make sure the payment method being charged was created in the platform. if ( @@ -1241,7 +1241,7 @@ public function process_payment_for_order( $cart, $payment_information, $additio $this->get_level3_data_from_order( $order ), $payment_information->is_merchant_initiated(), $additional_api_parameters, - $selected_payment_method_type, + $payment_methods, $payment_information->get_cvc_confirmation() ); } @@ -2624,7 +2624,7 @@ public function schedule_order_tracking( $order_id, $order = null ) { * Create a payment intent without confirming the intent. * * @param WC_Order $order - Order based on which to create intent. - * @param array $selected_payment_method_type - A list of allowed payment methods. Eg. card, card_present. + * @param array $payment_methods - A list of allowed payment methods. Eg. card, card_present. * @param string $capture_method - Controls when the funds will be captured from the customer's account ("automatic" or "manual"). * It must be "manual" for in-person (terminal) payments. * @@ -2635,7 +2635,7 @@ public function schedule_order_tracking( $order_id, $order = null ) { * * @throws Exception - When an error occurs in intent creation. */ - public function create_intent( WC_Order $order, array $selected_payment_method_type, string $capture_method = 'automatic', array $metadata = [], string $customer_id = null ) { + public function create_intent( WC_Order $order, array $payment_methods, string $capture_method = 'automatic', array $metadata = [], string $customer_id = null ) { $currency = strtolower( $order->get_currency() ); $converted_amount = WC_Payments_Utils::prepare_amount( $order->get_total(), $currency ); @@ -2643,7 +2643,7 @@ public function create_intent( WC_Order $order, array $selected_payment_method_t $intent = $this->payments_api_client->create_intention( $converted_amount, $currency, - $selected_payment_method_type, + $payment_methods, $order->get_order_number(), $capture_method, $metadata, From e2b9dd62b8acdbdc77631b8376c33d7b0a04f56c Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 22 Aug 2022 14:19:06 -0400 Subject: [PATCH 03/17] Uncomment pre-commit checks --- .husky/pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 6149072fc55..d37daa075e2 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -# npx --no-install lint-staged +npx --no-install lint-staged From 666c617e9f099bde07b2ddbc1c9fbfe5b705246f Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Wed, 31 Aug 2022 13:55:26 -0400 Subject: [PATCH 04/17] Prevent tokens from being duplicated --- includes/class-wc-payments-token-service.php | 7 +++++-- tests/unit/test-class-wc-payments-token-service.php | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 8308c535a72..fd6c432f637 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -100,7 +100,9 @@ public function add_payment_method_to_user( $payment_method_id, $user ) { * @return array */ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gateway_id ) { - if ( ( ! empty( $gateway_id ) ) || ! is_user_logged_in() ) { + $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; + + if ( ( ! empty( $gateway_id ) && ! in_array( $gateway_id, $gateway_ids, true ) ) || ! is_user_logged_in() ) { return $tokens; } @@ -135,12 +137,13 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat // Prevent unnecessary recursion, WC_Payment_Token::save() ends up calling 'woocommerce_get_customer_payment_tokens' in some cases. remove_action( 'woocommerce_get_customer_payment_tokens', [ $this, 'woocommerce_get_customer_payment_tokens' ], 10, 3 ); + foreach ( $payment_methods as $payment_method ) { if ( ! isset( $payment_method['type'] ) ) { continue; } - if ( ! isset( $stored_tokens[ $payment_method['id'] ] ) ) { + if ( ! isset( $stored_tokens[ $payment_method['id'] ] ) && ( 'card' === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID === $gateway_id || 'sepa_debit' === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA === $gateway_id ) || empty( $gateway_id ) ) { $token = $this->add_token_to_user( $payment_method, get_user_by( 'id', $user_id ) ); $tokens[ $token->get_id() ] = $token; } else { diff --git a/tests/unit/test-class-wc-payments-token-service.php b/tests/unit/test-class-wc-payments-token-service.php index 2d322dfd524..e9576bbe5a3 100644 --- a/tests/unit/test-class-wc-payments-token-service.php +++ b/tests/unit/test-class-wc-payments-token-service.php @@ -104,7 +104,7 @@ public function test_add_token_to_user_for_sepa() { $token = $this->token_service->add_token_to_user( $mock_payment_method, wp_get_current_user() ); - $this->assertEquals( 'woocommerce_payments', $token->get_gateway_id() ); + $this->assertEquals( 'woocommerce_payments_sepa_debit', $token->get_gateway_id() ); $this->assertEquals( 1, $token->get_user_id() ); $this->assertEquals( 'pm_mock', $token->get_token() ); $this->assertEquals( '3000', $token->get_last4() ); @@ -157,7 +157,7 @@ public function test_woocommerce_payment_token_deleted() { public function test_woocommerce_payment_token_deleted_other_gateway() { $this->mock_api_client - ->expects( $this->never() ) + ->expects( $this->once() ) ->method( 'detach_payment_method' ); $token = new WC_Payment_Token_CC(); From 5bf447c782c65ce93a97cf88ec9a0ef1b6938196 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 2 Sep 2022 10:48:05 -0400 Subject: [PATCH 05/17] Remove setup intent from session after adding UPE payment method --- includes/payment-methods/class-upe-payment-gateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-upe-payment-gateway.php b/includes/payment-methods/class-upe-payment-gateway.php index 798d3187992..d0e0317110b 100644 --- a/includes/payment-methods/class-upe-payment-gateway.php +++ b/includes/payment-methods/class-upe-payment-gateway.php @@ -100,6 +100,7 @@ public function __construct( add_action( "wc_ajax_wcpay_create_payment_intent_$this->stripe_id", [ $this, 'create_payment_intent_ajax' ] ); add_action( "wc_ajax_wcpay_update_payment_inten_ $this->stripe_id", [ $this, 'update_payment_intent_ajax' ] ); add_action( "wc_ajax_wcpay_init_setup_intent_$this->stripe_id", [ $this, 'init_setup_intent_ajax' ] ); + add_action( 'woocommerce_after_account_payment_methods', [ $this, 'remove_upe_setup_intent_from_session' ], 10, 0 ); if ( 'card' !== $this->stripe_id ) { $this->id = self::GATEWAY_ID . '_' . $this->stripe_id; @@ -115,7 +116,6 @@ public function __construct( add_action( 'wp', [ $this, 'maybe_process_upe_redirect' ] ); add_action( 'woocommerce_order_payment_status_changed', [ __CLASS__, 'remove_upe_payment_intent_from_session' ], 10, 0 ); - add_action( 'woocommerce_after_account_payment_methods', [ $this, 'remove_upe_setup_intent_from_session' ], 10, 0 ); add_action( 'woocommerce_subscription_payment_method_updated', [ $this, 'remove_upe_setup_intent_from_session' ], 10, 0 ); } } From cc5a465171d908dc4f12e41563d39a6d9ce32a04 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 9 Sep 2022 11:48:03 -0400 Subject: [PATCH 06/17] Prevent deleting tokens from non-WooCommerce Payments gateways --- includes/class-wc-payments-token-service.php | 19 ++++++++++++------- includes/class-wc-payments.php | 11 ++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index fd6c432f637..1efe6d2372e 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -100,6 +100,7 @@ public function add_payment_method_to_user( $payment_method_id, $user ) { * @return array */ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gateway_id ) { + $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; if ( ( ! empty( $gateway_id ) && ! in_array( $gateway_id, $gateway_ids, true ) ) || ! is_user_logged_in() ) { @@ -122,7 +123,9 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat $stored_tokens = []; foreach ( $tokens as $token ) { - $stored_tokens[ $token->get_token() ] = $token; + if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { + $stored_tokens[ $token->get_token() ] = $token; + } } $payment_methods = [ [] ]; @@ -170,12 +173,14 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat * @param WC_Payment_Token $token Token object. */ public function woocommerce_payment_token_deleted( $token_id, $token ) { - try { - $this->payments_api_client->detach_payment_method( $token->get_token() ); - // Clear cached payment methods. - $this->customer_service->clear_cached_payment_methods_for_user( $token->get_user_id() ); - } catch ( Exception $e ) { - Logger::log( 'Error detaching payment method:' . $e->getMessage() ); + if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) === $token->get_gateway_id() ) { + try { + $this->payments_api_client->detach_payment_method( $token->get_token() ); + // Clear cached payment methods. + $this->customer_service->clear_cached_payment_methods_for_user( $token->get_user_id() ); + } catch ( Exception $e ) { + Logger::log( 'Error detaching payment method:' . $e->getMessage() ); + } } } diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 83ffa6893a5..625b1053b6a 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -492,8 +492,9 @@ public static function add_plugin_links( $links ) { * @return array The list of payment gateways that will be available, including WooCommerce Payments' Gateway class. */ public static function register_gateway( $gateways ) { - $gateways[] = self::$legacy_card_gateway; - $reusable_methods[] = self::$legacy_card_gateway; + $gateways[] = self::$legacy_card_gateway; + $all_upe_gateways = []; + $reusable_methods = []; if ( WC_Payments_Features::is_upe_enabled() ) { foreach ( self::$card_gateway->get_payment_method_ids_enabled_at_checkout() as $payment_method_id ) { @@ -507,16 +508,16 @@ public static function register_gateway( $gateways ) { $reusable_methods[] = $upe_gateway; } - $gateways[] = $upe_gateway; + $all_upe_gateways[] = $upe_gateway; } if ( is_add_payment_method_page() ) { - return $reusable_methods; + return array_merge( $gateways, $reusable_methods ); } } - return $gateways; + return array_merge( $gateways, $all_upe_gateways ); } /** From 9189c77cdda92c9e703bb417c5846ff516f7a8ec Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 9 Sep 2022 12:32:25 -0400 Subject: [PATCH 07/17] Fix tests and add missing gateways array --- includes/class-wc-payments-token-service.php | 10 ++++++++-- tests/unit/test-class-wc-payments-token-service.php | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 1efe6d2372e..d737f169678 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -173,7 +173,10 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat * @param WC_Payment_Token $token Token object. */ public function woocommerce_payment_token_deleted( $token_id, $token ) { - if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) === $token->get_gateway_id() ) { + + $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; + + if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { try { $this->payments_api_client->detach_payment_method( $token->get_token() ); // Clear cached payment methods. @@ -191,7 +194,10 @@ public function woocommerce_payment_token_deleted( $token_id, $token ) { * @param WC_Payment_Token $token Token object. */ public function woocommerce_payment_token_set_default( $token_id, $token ) { - if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $token->get_gateway_id() ) { + + $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; + + if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { $customer_id = $this->customer_service->get_customer_id_by_user_id( $token->get_user_id() ); if ( $customer_id ) { $this->customer_service->set_default_payment_method_for_customer( $customer_id, $token->get_token() ); diff --git a/tests/unit/test-class-wc-payments-token-service.php b/tests/unit/test-class-wc-payments-token-service.php index e9576bbe5a3..8625a9527c3 100644 --- a/tests/unit/test-class-wc-payments-token-service.php +++ b/tests/unit/test-class-wc-payments-token-service.php @@ -157,7 +157,7 @@ public function test_woocommerce_payment_token_deleted() { public function test_woocommerce_payment_token_deleted_other_gateway() { $this->mock_api_client - ->expects( $this->once() ) + ->expects( $this->never() ) ->method( 'detach_payment_method' ); $token = new WC_Payment_Token_CC(); From e5c1f5b55a9791d03474844272735bff1f966348 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Thu, 6 Oct 2022 15:03:57 -0400 Subject: [PATCH 08/17] Only merge UPE gateways if UPE is enabled --- includes/class-wc-payments.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 625b1053b6a..e262fd9eac8 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -515,9 +515,11 @@ public static function register_gateway( $gateways ) { if ( is_add_payment_method_page() ) { return array_merge( $gateways, $reusable_methods ); } + + return array_merge( $gateways, $all_upe_gateways ); } - return array_merge( $gateways, $all_upe_gateways ); + return $gateways; } /** From c861cc51a4726f6bc19695572435123efb7e0b14 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Wed, 12 Oct 2022 15:05:46 -0400 Subject: [PATCH 09/17] Save payment methods on checkout and use correct UPE payment method --- includes/class-wc-payment-gateway-wcpay.php | 15 ++++++++++++++- .../payment-methods/class-upe-payment-gateway.php | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index f6c45d55159..29af57ca3f1 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1199,7 +1199,20 @@ public function process_payment_for_order( $cart, $payment_information, $additio throw new Exception( WC_Payments_Utils::get_filtered_error_message( $e ) ); } - $payment_methods = WC_Payments::get_gateway()->get_payment_method_ids_enabled_at_checkout( null, true ); + // If UPE is enabled, get the selected payment method. + if ( ! WC_Payments_Features::is_upe_enabled() ) { + $payment_methods = WC_Payments::get_gateway()->get_payment_method_ids_enabled_at_checkout( null, true ); + } else { + $upe_payment_method = sanitize_text_field( wp_unslash( $_POST['payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification + + if ( 'woocommerce_payments' !== $upe_payment_method ) { + $upe_payment_method = str_replace( 'woocommerce_payments_', '', $upe_payment_method ); + } else { + $upe_payment_method = 'card'; + } + + $payment_methods = [ $upe_payment_method ]; + } // Make sure the payment method being charged was created in the platform. if ( diff --git a/includes/payment-methods/class-upe-payment-gateway.php b/includes/payment-methods/class-upe-payment-gateway.php index d0e0317110b..f540f230b38 100644 --- a/includes/payment-methods/class-upe-payment-gateway.php +++ b/includes/payment-methods/class-upe-payment-gateway.php @@ -101,6 +101,7 @@ public function __construct( add_action( "wc_ajax_wcpay_update_payment_inten_ $this->stripe_id", [ $this, 'update_payment_intent_ajax' ] ); add_action( "wc_ajax_wcpay_init_setup_intent_$this->stripe_id", [ $this, 'init_setup_intent_ajax' ] ); add_action( 'woocommerce_after_account_payment_methods', [ $this, 'remove_upe_setup_intent_from_session' ], 10, 0 ); + add_action( 'woocommerce_order_payment_status_changed', [ __CLASS__, 'remove_upe_payment_intent_from_session' ], 10, 0 ); if ( 'card' !== $this->stripe_id ) { $this->id = self::GATEWAY_ID . '_' . $this->stripe_id; @@ -115,7 +116,6 @@ public function __construct( add_action( 'wp', [ $this, 'maybe_process_upe_redirect' ] ); - add_action( 'woocommerce_order_payment_status_changed', [ __CLASS__, 'remove_upe_payment_intent_from_session' ], 10, 0 ); add_action( 'woocommerce_subscription_payment_method_updated', [ $this, 'remove_upe_setup_intent_from_session' ], 10, 0 ); } } @@ -431,7 +431,7 @@ public function process_payment( $order_id ) { $payment_needed = 0 < $converted_amount; $selected_upe_payment_type = $this->stripe_id; $payment_type = $this->is_payment_recurring( $order_id ) ? Payment_Type::RECURRING() : Payment_Type::SINGLE(); - $save_payment_method = $payment_type->equals( Payment_Type::RECURRING() ) || ! empty( $_POST[ 'wc-' . static::GATEWAY_ID . '-new-payment-method' ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $save_payment_method = $payment_type->equals( Payment_Type::RECURRING() ) || ! empty( $_POST[ 'wc-' . self::GATEWAY_ID . '_' . $this->stripe_id . '-new-payment-method' ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing $payment_country = ! empty( $_POST['wcpay_payment_country'] ) ? wc_clean( wp_unslash( $_POST['wcpay_payment_country'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( $payment_intent_id ) { From a1173e4783209a8643af458c94bfd1186451fdff Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Thu, 27 Oct 2022 17:50:09 -0400 Subject: [PATCH 10/17] Remove is_upe_enabled check --- includes/class-wc-payment-gateway-wcpay.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 29af57ca3f1..9648650cab8 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1199,21 +1199,16 @@ public function process_payment_for_order( $cart, $payment_information, $additio throw new Exception( WC_Payments_Utils::get_filtered_error_message( $e ) ); } - // If UPE is enabled, get the selected payment method. - if ( ! WC_Payments_Features::is_upe_enabled() ) { - $payment_methods = WC_Payments::get_gateway()->get_payment_method_ids_enabled_at_checkout( null, true ); - } else { - $upe_payment_method = sanitize_text_field( wp_unslash( $_POST['payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification - - if ( 'woocommerce_payments' !== $upe_payment_method ) { - $upe_payment_method = str_replace( 'woocommerce_payments_', '', $upe_payment_method ); - } else { - $upe_payment_method = 'card'; - } + $upe_payment_method = sanitize_text_field( wp_unslash( $_POST['payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification - $payment_methods = [ $upe_payment_method ]; + if ( 'woocommerce_payments' !== $upe_payment_method ) { + $upe_payment_method = str_replace( 'woocommerce_payments_', '', $upe_payment_method ); + } else { + $upe_payment_method = 'card'; } + $payment_methods = [ $upe_payment_method ]; + // Make sure the payment method being charged was created in the platform. if ( ! $payment_information->is_using_saved_payment_method() && From f644f114dc6913003c39af6e37b4b9a0bd0bfa20 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 28 Oct 2022 10:59:39 -0400 Subject: [PATCH 11/17] Add class constant for reusable gateways --- includes/class-wc-payments-token-service.php | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index d737f169678..3272b7bda2a 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -19,6 +19,8 @@ */ class WC_Payments_Token_Service { + const REUSABLE_GATEWAYS = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; + /** * Client for making requests to the WooCommerce Payments API * @@ -62,7 +64,7 @@ public function add_token_to_user( $payment_method, $user ) { if ( Payment_Method::SEPA === $payment_method['type'] ) { $token = new WC_Payment_Token_WCPay_SEPA(); - $token->set_gateway_id( 'woocommerce_payments_sepa_debit' ); + $token->set_gateway_id( WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ); $token->set_last4( $payment_method[ Payment_Method::SEPA ]['last4'] ); } else { $token = new WC_Payment_Token_CC(); @@ -101,9 +103,7 @@ public function add_payment_method_to_user( $payment_method_id, $user ) { */ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gateway_id ) { - $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; - - if ( ( ! empty( $gateway_id ) && ! in_array( $gateway_id, $gateway_ids, true ) ) || ! is_user_logged_in() ) { + if ( ( ! empty( $gateway_id ) && ! in_array( $gateway_id, self::REUSABLE_GATEWAYS, true ) ) || ! is_user_logged_in() ) { return $tokens; } @@ -123,7 +123,7 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat $stored_tokens = []; foreach ( $tokens as $token ) { - if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { + if ( in_array( $token->get_gateway_id(), self::REUSABLE_GATEWAYS, true ) ) { $stored_tokens[ $token->get_token() ] = $token; } } @@ -146,7 +146,10 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat continue; } - if ( ! isset( $stored_tokens[ $payment_method['id'] ] ) && ( 'card' === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID === $gateway_id || 'sepa_debit' === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA === $gateway_id ) || empty( $gateway_id ) ) { + if ( ! isset( $stored_tokens[ $payment_method['id'] ] ) && ( + ( Payment_Method::CARD === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID === $gateway_id ) || + ( Payment_Method::SEPA === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA === $gateway_id ) ) + ) { $token = $this->add_token_to_user( $payment_method, get_user_by( 'id', $user_id ) ); $tokens[ $token->get_id() ] = $token; } else { @@ -174,9 +177,7 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat */ public function woocommerce_payment_token_deleted( $token_id, $token ) { - $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; - - if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { + if ( in_array( $token->get_gateway_id(), self::REUSABLE_GATEWAYS, true ) ) { try { $this->payments_api_client->detach_payment_method( $token->get_token() ); // Clear cached payment methods. @@ -195,9 +196,7 @@ public function woocommerce_payment_token_deleted( $token_id, $token ) { */ public function woocommerce_payment_token_set_default( $token_id, $token ) { - $gateway_ids = [ WC_Payment_Gateway_WCPay::GATEWAY_ID, WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA ]; - - if ( in_array( $token->get_gateway_id(), $gateway_ids, true ) ) { + if ( in_array( $token->get_gateway_id(), self::REUSABLE_GATEWAYS, true ) ) { $customer_id = $this->customer_service->get_customer_id_by_user_id( $token->get_user_id() ); if ( $customer_id ) { $this->customer_service->set_default_payment_method_for_customer( $customer_id, $token->get_token() ); From 7fc331a85ecf97115d1f4a71354a669d42b5a6d7 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 28 Oct 2022 12:19:45 -0400 Subject: [PATCH 12/17] Only define UPE gateways arrays when UPE is enabled --- includes/class-wc-payments.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index e262fd9eac8..4b7398ac2f4 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -492,11 +492,12 @@ public static function add_plugin_links( $links ) { * @return array The list of payment gateways that will be available, including WooCommerce Payments' Gateway class. */ public static function register_gateway( $gateways ) { - $gateways[] = self::$legacy_card_gateway; - $all_upe_gateways = []; - $reusable_methods = []; + $gateways[] = self::$legacy_card_gateway; if ( WC_Payments_Features::is_upe_enabled() ) { + $all_upe_gateways = []; + $reusable_methods = []; + foreach ( self::$card_gateway->get_payment_method_ids_enabled_at_checkout() as $payment_method_id ) { if ( 'card' === $payment_method_id ) { continue; From 162a8922f872abff536c818b0a615c0e949aa342 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 28 Oct 2022 16:40:32 -0400 Subject: [PATCH 13/17] Update test --- .../test-class-upe-payment-gateway.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/unit/payment-methods/test-class-upe-payment-gateway.php b/tests/unit/payment-methods/test-class-upe-payment-gateway.php index 70329321087..c17aa65a263 100644 --- a/tests/unit/payment-methods/test-class-upe-payment-gateway.php +++ b/tests/unit/payment-methods/test-class-upe-payment-gateway.php @@ -701,12 +701,11 @@ public function test_process_payment_returns_correct_redirect_url() { } public function test_process_payment_passes_save_payment_method_to_store() { - $mock_card_payment_gateway = $this->mock_payment_gateways[ Payment_Method::CARD ]; $mock_sepa_payment_gateway = $this->mock_payment_gateways[ Payment_Method::SEPA ]; $order = WC_Helper_Order::create_order(); $order_id = $order->get_id(); - $gateway_id = UPE_Payment_Gateway::GATEWAY_ID; + $gateway_id = UPE_Payment_Gateway::GATEWAY_ID . '_' . Payment_Method::SEPA; $save_payment_param = "wc-$gateway_id-new-payment-method"; $_POST[ $save_payment_param ] = 'yes'; $_POST['wc_payment_intent_id'] = 'pi_mock'; @@ -714,7 +713,7 @@ public function test_process_payment_passes_save_payment_method_to_store() { $payment_intent = WC_Helper_Intention::create_intention( [ 'status' => 'processing' ] ); $this->mock_api_client - ->expects( $this->exactly( 2 ) ) + ->expects( $this->exactly( 1 ) ) ->method( 'update_intention' ) ->willReturn( $payment_intent @@ -722,13 +721,6 @@ public function test_process_payment_passes_save_payment_method_to_store() { $this->set_cart_contains_subscription_items( false ); - // Test saving with cards. - $result = $mock_card_payment_gateway->process_payment( $order->get_id() ); - $this->assertEquals( 'success', $result['result'] ); - $this->assertMatchesRegularExpression( "/order_id=$order_id/", $result['redirect_url'] ); - $this->assertMatchesRegularExpression( '/wc_payment_method=woocommerce_payments/', $result['redirect_url'] ); - $this->assertMatchesRegularExpression( '/save_payment_method=yes/', $result['redirect_url'] ); - // Test saving with SEPA. $result = $mock_sepa_payment_gateway->process_payment( $order->get_id() ); $this->assertEquals( 'success', $result['result'] ); From d30e2fa0970868f19e487a50c69e56ca7533dc19 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 4 Nov 2022 16:40:04 -0400 Subject: [PATCH 14/17] Restrict requested payment methods types --- includes/class-wc-payments-customer-service.php | 7 +++++-- includes/class-wc-payments-token-service.php | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-payments-customer-service.php b/includes/class-wc-payments-customer-service.php index dec27afd978..1e986182fe2 100644 --- a/includes/class-wc-payments-customer-service.php +++ b/includes/class-wc-payments-customer-service.php @@ -8,6 +8,7 @@ use WCPay\Database_Cache; use WCPay\Exceptions\API_Exception; use WCPay\Logger; +use WCPay\Constants\Payment_Method; defined( 'ABSPATH' ) || exit; @@ -266,8 +267,10 @@ public function clear_cached_payment_methods_for_user( $user_id ) { if ( WC_Payments::is_network_saved_cards_enabled() ) { return; // No need to do anything, payment methods will never be cached in this case. } - $customer_id = $this->get_customer_id_by_user_id( $user_id ); - foreach ( WC_Payments::get_gateway()->get_upe_enabled_payment_method_ids() as $type ) { + + $retrievable_payment_method_types = [ Payment_Method::CARD, Payment_Method::SEPA ]; + $customer_id = $this->get_customer_id_by_user_id( $user_id ); + foreach ( $retrievable_payment_method_types as $type ) { $this->database_cache->delete( Database_Cache::PAYMENT_METHODS_KEY_PREFIX . $customer_id . '_' . $type ); } } diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 3272b7bda2a..e040eb3cb2c 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -128,8 +128,9 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat } } - $payment_methods = [ [] ]; - foreach ( WC_Payments::get_gateway()->get_upe_enabled_payment_method_ids() as $type ) { + $retrievable_payment_method_types = [ Payment_Method::CARD, Payment_Method::SEPA ]; + $payment_methods = [ [] ]; + foreach ( $retrievable_payment_method_types as $type ) { $payment_methods[] = $this->customer_service->get_payment_methods_for_customer( $customer_id, $type ); } $payment_methods = array_merge( ...$payment_methods ); From 61d2831d87c8dd922902bc65ecbbff6dff2c9d06 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Fri, 4 Nov 2022 20:34:23 -0400 Subject: [PATCH 15/17] Run add_token_to_user() from Payment Methods page --- includes/class-wc-payments-token-service.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index e040eb3cb2c..dbaff147071 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -149,7 +149,8 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat if ( ! isset( $stored_tokens[ $payment_method['id'] ] ) && ( ( Payment_Method::CARD === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID === $gateway_id ) || - ( Payment_Method::SEPA === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA === $gateway_id ) ) + ( Payment_Method::SEPA === $payment_method['type'] && WC_Payment_Gateway_WCPay::GATEWAY_ID . '_' . Payment_Method::SEPA === $gateway_id ) ) || + empty( $gateway_id ) ) { $token = $this->add_token_to_user( $payment_method, get_user_by( 'id', $user_id ) ); $tokens[ $token->get_id() ] = $token; From a747857083cb953b64608dbe761501feded3953a Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 7 Nov 2022 13:30:33 -0500 Subject: [PATCH 16/17] Remove empty array from payment methods --- includes/class-wc-payments-token-service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index dbaff147071..17fa73d3709 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -129,7 +129,7 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat } $retrievable_payment_method_types = [ Payment_Method::CARD, Payment_Method::SEPA ]; - $payment_methods = [ [] ]; + $payment_methods = []; foreach ( $retrievable_payment_method_types as $type ) { $payment_methods[] = $this->customer_service->get_payment_methods_for_customer( $customer_id, $type ); } From d3e9a3dc08c21b61185b0e8fff20f513629830f5 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Wed, 9 Nov 2022 16:49:17 -0500 Subject: [PATCH 17/17] Only query enabled payment method types --- includes/class-wc-payments-token-service.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 17fa73d3709..7fb7d196d84 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -128,12 +128,20 @@ public function woocommerce_get_customer_payment_tokens( $tokens, $user_id, $gat } } - $retrievable_payment_method_types = [ Payment_Method::CARD, Payment_Method::SEPA ]; - $payment_methods = []; + $retrievable_payment_method_types = [ Payment_Method::CARD ]; + + if ( in_array( Payment_Method::SEPA, WC_Payments::get_gateway()->get_upe_enabled_payment_method_ids(), true ) ) { + $retrievable_payment_method_types[] = Payment_Method::SEPA; + } + + $payment_methods = []; + foreach ( $retrievable_payment_method_types as $type ) { $payment_methods[] = $this->customer_service->get_payment_methods_for_customer( $customer_id, $type ); } + $payment_methods = array_merge( ...$payment_methods ); + } catch ( Exception $e ) { Logger::error( 'Failed to fetch payment methods for customer.' . $e ); return $tokens;