From a5c2a97127e13c2dba4daee45bbc316911b72fca Mon Sep 17 00:00:00 2001 From: James Allan Date: Thu, 4 Jan 2024 15:37:21 +1000 Subject: [PATCH 01/29] Add suport for Stripe APMs to be used on block checkout pages with split UPE --- .../class-wc-stripe-upe-payment-method.php | 11 ++++++----- woocommerce-gateway-stripe.php | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index fd5d32addd..4773edd27a 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -13,7 +13,7 @@ /** * Extendable abstract class for payment methods. */ -abstract class WC_Stripe_UPE_Payment_Method { +abstract class WC_Stripe_UPE_Payment_Method extends WC_Payment_Gateway { use WC_Stripe_Subscriptions_Utilities_Trait; use WC_Stripe_Pre_Orders_Trait; @@ -30,7 +30,7 @@ abstract class WC_Stripe_UPE_Payment_Method { * * @var string */ - protected $title; + public $title; /** * Method label @@ -44,7 +44,7 @@ abstract class WC_Stripe_UPE_Payment_Method { * * @var string */ - protected $description; + public $description; /** * Can payment method be saved or reused? @@ -72,7 +72,7 @@ abstract class WC_Stripe_UPE_Payment_Method { * * @var bool */ - protected $enabled; + public $enabled; /** * List of supported countries @@ -94,6 +94,7 @@ public function __construct() { } $this->enabled = in_array( static::STRIPE_ID, $enabled_upe_methods, true ); + $this->id = 'stripe_' . static::STRIPE_ID; } /** @@ -120,7 +121,7 @@ public function is_enabled() { * @return bool */ public function is_available() { - return true; + return $this->is_enabled_at_checkout(); } /** diff --git a/woocommerce-gateway-stripe.php b/woocommerce-gateway-stripe.php index e799200ea3..4c2cd39804 100644 --- a/woocommerce-gateway-stripe.php +++ b/woocommerce-gateway-stripe.php @@ -400,6 +400,10 @@ public function add_gateways( $methods ) { if ( isset( $sofort_settings['enabled'] ) && 'yes' === $sofort_settings['enabled'] ) { $methods[] = WC_Gateway_Stripe_Sofort::class; } + } else { + foreach ( WC_Stripe_UPE_Payment_Gateway::UPE_AVAILABLE_METHODS as $method_class ) { + $methods[] = $method_class; + } } // These payment gateways will always be visible, regardless if UPE is enabled or disabled: From 968f91131bd4a840e523079e0f177131d463fa41 Mon Sep 17 00:00:00 2001 From: James Allan Date: Fri, 5 Jan 2024 12:18:42 +1000 Subject: [PATCH 02/29] Add support for processing payments using split UPE on the block checkout --- .../class-wc-stripe-intent-controller.php | 31 +++++++++++++++++++ .../class-wc-stripe-upe-payment-gateway.php | 23 ++++++++------ .../class-wc-stripe-upe-payment-method.php | 12 +++++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index faba86c6ef..aaee721dcc 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -725,6 +725,23 @@ public function create_and_confirm_payment_intent( $payment_information ) { ]; } + // For UPE methods like Bancontact, GiroPay etc we need to + if ( $this->upe_needs_redirection( $selected_payment_type ) ) { + $request['return_url'] = wp_sanitize_redirect( + esc_url_raw( + add_query_arg( + [ + 'order_id' => $order->get_id(), + 'wc_payment_method' => $this->get_gateway()->id, + '_wpnonce' => wp_create_nonce( 'wc_stripe_process_redirect_order_nonce' ), + 'save_payment_method' => $payment_information['save_payment_method_to_store'] ? 'yes' : 'no', + ], + $payment_information['return_url'] + ) + ) + ); + } + if ( $payment_information['save_payment_method_to_store'] ) { $request['setup_future_usage'] = 'off_session'; } @@ -938,4 +955,18 @@ public function create_and_confirm_setup_intent_ajax() { ); } } + + /** + * Determines if the given UPE payment method type requires redirection. + * + * Payment methods like GiroPay, Bancontact, etc redirect the user to their bank to complete payment. Payment intents using these kind + * of payment methods require a return URL to be provided. This function is used to determine if the given payment method type + * requires redirection. + * + * @param string $payment_method_type The UPE payment method type being used to create the payment intent. + * @return bool Whether the payment method type requires redirection. + */ + private function upe_needs_redirection( $payment_method_type ) { + return 'card' !== $payment_method_type; + } } diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 2f61a39481..fb048f2a28 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -701,15 +701,19 @@ private function process_payment_with_deferred_intent( int $order_id ) { $redirect = $this->get_return_url( $order ); - // If the payment intent requires action, respond with the pi and client secret so it can confirmed on checkout. - if ( 'requires_action' === $payment_intent->status ) { - $redirect = sprintf( - '#wc-stripe-confirm-%s:%s:%s:%s', - $payment_needed ? 'pi' : 'si', - $order_id, - $payment_intent->client_secret, - wp_create_nonce( 'wc_stripe_update_order_status_nonce' ) - ); + // If the payment intent requires action, respond with redirect URL or the pi and client secret so it can confirmed on checkout. + if ( 'requires_action' === $payment_intent->status || 'requires_confirmation' === $payment_intent->status ) { + if ( isset( $payment_intent->next_action->type ) && 'redirect_to_url' === $payment_intent->next_action->type && ! empty( $payment_intent->next_action->redirect_to_url->url ) ) { + $redirect = $payment_intent->next_action->redirect_to_url->url; + } else { + $redirect = sprintf( + '#wc-stripe-confirm-%s:%s:%s:%s', + $payment_needed ? 'pi' : 'si', + $order_id, + $payment_intent->client_secret, + wp_create_nonce( 'wc_stripe_update_order_status_nonce' ) + ); + } } return [ @@ -1630,6 +1634,7 @@ private function prepare_payment_information_from_request( WC_Order $order ) { 'selected_payment_type' => $selected_payment_type, 'shipping' => $shipping_details, 'statement_descriptor' => $this->get_statement_descriptor( $selected_payment_type ), + 'return_url' => $this->get_return_url( $order ), ]; return $payment_information; diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 4773edd27a..b35618129d 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -353,4 +353,16 @@ public function can_refund_via_stripe() { public function get_testing_instructions() { return ''; } + + /** + * Processes an order payment. + * + * UPE Payment methods use the WC_Stripe_UPE_Payment_Gateway::process_payment() function. + * + * @param int $order_id The order ID to process. + * @return array The payment result. + */ + public function process_payment( $order_id ) { + return WC_Stripe::get_instance()->get_main_stripe_gateway()->process_payment( $order_id ); + } } From 7f1231f59afc7aa972c266dc836fa6479a9e806e Mon Sep 17 00:00:00 2001 From: James Allan Date: Fri, 5 Jan 2024 13:19:22 +1000 Subject: [PATCH 03/29] Use Stipe gateway ID constant rather than hardcoding 'stripe_' --- includes/payment-methods/class-wc-stripe-upe-payment-method.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index b35618129d..4a45ac69ea 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -94,7 +94,7 @@ public function __construct() { } $this->enabled = in_array( static::STRIPE_ID, $enabled_upe_methods, true ); - $this->id = 'stripe_' . static::STRIPE_ID; + $this->id = WC_Gateway_Stripe::ID . '_' . static::STRIPE_ID; } /** From 4d10d92ffbbcb41189d4450968b22f09e5983860 Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 8 Jan 2024 14:53:00 +1000 Subject: [PATCH 04/29] Use already created instances of the upe payment methods --- woocommerce-gateway-stripe.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/woocommerce-gateway-stripe.php b/woocommerce-gateway-stripe.php index 4c2cd39804..eb6e8c9eb3 100644 --- a/woocommerce-gateway-stripe.php +++ b/woocommerce-gateway-stripe.php @@ -380,7 +380,8 @@ public function plugin_row_meta( $links, $file ) { * @version 5.6.0 */ public function add_gateways( $methods ) { - $methods[] = $this->get_main_stripe_gateway(); + $main_gateway = $this->get_main_stripe_gateway(); + $methods[] = $main_gateway; if ( ! WC_Stripe_Feature_Flags::is_upe_preview_enabled() || ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) { // These payment gateways will be hidden when UPE is enabled: @@ -400,10 +401,8 @@ public function add_gateways( $methods ) { if ( isset( $sofort_settings['enabled'] ) && 'yes' === $sofort_settings['enabled'] ) { $methods[] = WC_Gateway_Stripe_Sofort::class; } - } else { - foreach ( WC_Stripe_UPE_Payment_Gateway::UPE_AVAILABLE_METHODS as $method_class ) { - $methods[] = $method_class; - } + } elseif ( is_a( $main_gateway, 'WC_Stripe_UPE_Payment_Gateway' ) ) { + $methods = array_merge( $main_gateway->payment_methods, $methods ); } // These payment gateways will always be visible, regardless if UPE is enabled or disabled: From 5545ec7a49fc6d1ababa5f2f47b22b55bc01a459 Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 8 Jan 2024 16:27:17 +1000 Subject: [PATCH 05/29] Hide Stripe APMs on the WooCommerce > Settings > Payments screen --- .../class-wc-stripe-settings-controller.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/includes/admin/class-wc-stripe-settings-controller.php b/includes/admin/class-wc-stripe-settings-controller.php index 93a136b62e..aa08fae93a 100644 --- a/includes/admin/class-wc-stripe-settings-controller.php +++ b/includes/admin/class-wc-stripe-settings-controller.php @@ -26,6 +26,9 @@ public function __construct( WC_Stripe_Account $account ) { $this->account = $account; add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] ); add_action( 'wc_stripe_gateway_admin_options_wrapper', [ $this, 'admin_options' ] ); + + // Priority 5 so we can manipulate the registered gateways before they are shown. + add_action( 'woocommerce_admin_field_payment_gateways', [ $this, 'hide_gateways_on_settings_page' ], 5 ); } /** @@ -128,4 +131,18 @@ public function admin_scripts( $hook_suffix ) { wp_enqueue_script( 'woocommerce_stripe_admin' ); wp_enqueue_style( 'woocommerce_stripe_admin' ); } + + /** + * Removes all Stripe alternative payment methods (eg Bancontact, giropay) on the WooCommerce Settings page. + * + * Note: This function is hooked onto `woocommerce_admin_field_payment_gateways` which is the hook used + * to display the payment gateways on the WooCommerce Settings page. + */ + public static function hide_gateways_on_settings_page() { + foreach ( WC()->payment_gateways->payment_gateways as $index => $payment_gateway ) { + if ( $payment_gateway instanceof WC_Stripe_UPE_Payment_Method ) { + unset( WC()->payment_gateways->payment_gateways[ $index ] ); + } + } + } } From 66deb6a804d44fbdb7d349f2b22430ebe679cd0c Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 8 Jan 2024 17:18:20 +1000 Subject: [PATCH 06/29] Test troubleshooting --- .../admin/test-class-wc-rest-stripe-settings-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php b/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php index 8d96b21043..1c7d0150b5 100644 --- a/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php +++ b/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php @@ -121,6 +121,7 @@ public function test_enum_fields( $rest_key, $option_name, $original_valid_value $request->set_param( $rest_key, $new_valid_value ); $response = rest_do_request( $request ); + var_export( $response ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( $new_valid_value, $this->get_gateway()->get_option( $option_name ) ); From 797cc2b98d9ea1e9c7761645d994d0d46f5ac864 Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 8 Jan 2024 17:38:09 +1000 Subject: [PATCH 07/29] Attempt to fix test --- .../payment-methods/class-wc-stripe-upe-payment-gateway.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 0a1d9173da..dca404c755 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -443,9 +443,6 @@ public function get_upe_available_payment_methods() { $available_payment_methods = []; foreach ( $this->payment_methods as $payment_method ) { - if ( ! $payment_method->is_available() ) { - continue; - } $available_payment_methods[] = $payment_method->get_id(); } return $available_payment_methods; From 8452320c7d8b259f871d9d8280fe7da435c1dbe3 Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 8 Jan 2024 17:51:01 +1000 Subject: [PATCH 08/29] revert test troubleshooting code --- .../admin/test-class-wc-rest-stripe-settings-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php b/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php index 1c7d0150b5..8d96b21043 100644 --- a/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php +++ b/tests/phpunit/admin/test-class-wc-rest-stripe-settings-controller.php @@ -121,7 +121,6 @@ public function test_enum_fields( $rest_key, $option_name, $original_valid_value $request->set_param( $rest_key, $new_valid_value ); $response = rest_do_request( $request ); - var_export( $response ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( $new_valid_value, $this->get_gateway()->get_option( $option_name ) ); From 227862a88500ad70630b1d6113df4ce798a333c1 Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 13:06:51 +1000 Subject: [PATCH 09/29] Ensure the Link payment method is available before returning from get_upe_available_payment_methods() --- .../payment-methods/class-wc-stripe-upe-payment-gateway.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index dca404c755..58a0a40e55 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -443,8 +443,12 @@ public function get_upe_available_payment_methods() { $available_payment_methods = []; foreach ( $this->payment_methods as $payment_method ) { + if ( $payment_method->get_id() === WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID && ! $payment_method->is_available() ) { + continue; + } $available_payment_methods[] = $payment_method->get_id(); } + return $available_payment_methods; } From 26c392c3f5e005f117c8405dbee98bad02ec0bff Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 13:28:08 +1000 Subject: [PATCH 10/29] Add unit testing troubleshooting code --- includes/notes/class-wc-stripe-upe-stripelink-note.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/notes/class-wc-stripe-upe-stripelink-note.php b/includes/notes/class-wc-stripe-upe-stripelink-note.php index 81ec43912b..20e35a9be6 100644 --- a/includes/notes/class-wc-stripe-upe-stripelink-note.php +++ b/includes/notes/class-wc-stripe-upe-stripelink-note.php @@ -74,6 +74,7 @@ private static function get_note_class() { */ public static function init( WC_Stripe_Payment_Gateway $gateway ) { if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) { + var_export( 'return 1' ); return; } @@ -81,15 +82,18 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { $available_upe_payment_methods = $gateway->get_upe_available_payment_methods(); if ( ! in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $available_upe_payment_methods, true ) ) { + var_export( 'return 2' ); return; } if ( ! is_a( $gateway, 'WC_Stripe_UPE_Payment_Gateway' ) ) { + var_export( 'return 3' ); return; } // If store currency is not USD, skip if ( 'USD' !== get_woocommerce_currency() ) { + var_export( 'return 4' ); return; } @@ -100,6 +104,7 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { ! in_array( WC_Stripe_UPE_Payment_Method_CC::STRIPE_ID, $enabled_payment_methods, true ) || in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $enabled_payment_methods, true ) ) { + var_export( 'return 5' ); return; } From 6be2b12d27ff0492423edec4e302d6a42f480afc Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 13:45:46 +1000 Subject: [PATCH 11/29] add track which function is failing unit test --- .../class-wc-stripe-upe-stripelink-note.php | 21 ++++++++++++++----- tests/phpunit/test-class-wc-stripe-notes.php | 2 ++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/includes/notes/class-wc-stripe-upe-stripelink-note.php b/includes/notes/class-wc-stripe-upe-stripelink-note.php index 20e35a9be6..321a73f314 100644 --- a/includes/notes/class-wc-stripe-upe-stripelink-note.php +++ b/includes/notes/class-wc-stripe-upe-stripelink-note.php @@ -74,7 +74,9 @@ private static function get_note_class() { */ public static function init( WC_Stripe_Payment_Gateway $gateway ) { if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) { - var_export( 'return 1' ); + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'return 1: ' . $GLOBALS['james-unit-test-func'] ); + } return; } @@ -82,18 +84,25 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { $available_upe_payment_methods = $gateway->get_upe_available_payment_methods(); if ( ! in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $available_upe_payment_methods, true ) ) { - var_export( 'return 2' ); + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'return 2: ' . $GLOBALS['james-unit-test-func'] ); + } + return; } if ( ! is_a( $gateway, 'WC_Stripe_UPE_Payment_Gateway' ) ) { - var_export( 'return 3' ); + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'return 3: ' . $GLOBALS['james-unit-test-func'] ); + } return; } // If store currency is not USD, skip if ( 'USD' !== get_woocommerce_currency() ) { - var_export( 'return 4' ); + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'return 4: ' . $GLOBALS['james-unit-test-func'] ); + } return; } @@ -104,7 +113,9 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { ! in_array( WC_Stripe_UPE_Payment_Method_CC::STRIPE_ID, $enabled_payment_methods, true ) || in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $enabled_payment_methods, true ) ) { - var_export( 'return 5' ); + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'return 5: ' . $GLOBALS['james-unit-test-func'] ); + } return; } diff --git a/tests/phpunit/test-class-wc-stripe-notes.php b/tests/phpunit/test-class-wc-stripe-notes.php index a4b3cbce6e..8986a81b19 100644 --- a/tests/phpunit/test-class-wc-stripe-notes.php +++ b/tests/phpunit/test-class-wc-stripe-notes.php @@ -71,7 +71,9 @@ public function test_create_upe_stripelink_note() { ) ->getMock(); WC_Stripe::get_instance()->account->method( 'get_cached_account_data' )->willReturn( [ 'country' => 'US' ] ); + $GLOBALS['james-unit-test-func'] = __METHOD__; WC_Stripe_Inbox_Notes::create_upe_notes(); + unset( $GLOBALS['james-unit-test-func'] ); $admin_note_store = WC_Data_Store::load( 'admin-note' ); $this->assertSame( 1, count( $admin_note_store->get_notes_with_name( WC_Stripe_UPE_StripeLink_Note::NOTE_NAME ) ) ); } From 9cd39f9ed8116a2d1e18b3f961f083af536a3aac Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 13:52:56 +1000 Subject: [PATCH 12/29] Troubleshoot why Stripe Link isnt available --- .../class-wc-stripe-upe-payment-method-link.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php index 5c2d300a17..25a48d3b94 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php @@ -81,6 +81,10 @@ public function is_available() { $cached_account_data = WC_Stripe::get_instance()->account->get_cached_account_data(); $account_country = $cached_account_data['country'] ?? null; + if ( isset( $GLOBALS['james-unit-test-func'] ) ) { + var_export( 'account country: ' . $account_country ); + } + return 'US' === $account_country && parent::is_available(); } } From 612f1e637f9da4dae6e7a8f54f63d730772b5eaa Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 14:22:26 +1000 Subject: [PATCH 13/29] Introduce new function to deteremine if a UPE methods is available depending on Stripe Account country --- .../class-wc-stripe-upe-payment-gateway.php | 2 +- ...lass-wc-stripe-upe-payment-method-link.php | 19 ++++++++++++------- .../class-wc-stripe-upe-payment-method.php | 9 +++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 58a0a40e55..478a0a3380 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -443,7 +443,7 @@ public function get_upe_available_payment_methods() { $available_payment_methods = []; foreach ( $this->payment_methods as $payment_method ) { - if ( $payment_method->get_id() === WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID && ! $payment_method->is_available() ) { + if ( ! $payment_method->is_available_for_account_country() ) { continue; } $available_payment_methods[] = $payment_method->get_id(); diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php index 25a48d3b94..0e52f21617 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php @@ -72,19 +72,24 @@ public function create_payment_token_for_user( $user_id, $payment_method ) { } /** - * Returns true if the UPE method is available. + * Determines if the Stripe Account country this UPE method supports. * * @return bool */ - public function is_available() { - //if merchant is outside US, Link payment method should not be available + public function is_available_for_account_country() { + // If merchant is outside US, Link payment method should not be available. $cached_account_data = WC_Stripe::get_instance()->account->get_cached_account_data(); $account_country = $cached_account_data['country'] ?? null; - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'account country: ' . $account_country ); - } + return 'US' === $account_country; + } - return 'US' === $account_country && parent::is_available(); + /** + * Returns true if the UPE method is available. + * + * @return bool + */ + public function is_available() { + return $this->is_available_for_account_country() && parent::is_available(); } } diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 4a45ac69ea..d99e6be0d8 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -365,4 +365,13 @@ public function get_testing_instructions() { public function process_payment( $order_id ) { return WC_Stripe::get_instance()->get_main_stripe_gateway()->process_payment( $order_id ); } + + /** + * Determines if the Stripe Account country supports this UPE method. + * + * @return bool + */ + public function is_available_for_account_country() { + return true; + } } From 72150cc93786ecff9d14636432fef5de618c4844 Mon Sep 17 00:00:00 2001 From: James Allan Date: Tue, 9 Jan 2024 14:32:25 +1000 Subject: [PATCH 14/29] Revert unit test troubleshooting code --- .../class-wc-stripe-upe-stripelink-note.php | 16 ---------------- tests/phpunit/test-class-wc-stripe-notes.php | 2 -- 2 files changed, 18 deletions(-) diff --git a/includes/notes/class-wc-stripe-upe-stripelink-note.php b/includes/notes/class-wc-stripe-upe-stripelink-note.php index 321a73f314..81ec43912b 100644 --- a/includes/notes/class-wc-stripe-upe-stripelink-note.php +++ b/includes/notes/class-wc-stripe-upe-stripelink-note.php @@ -74,9 +74,6 @@ private static function get_note_class() { */ public static function init( WC_Stripe_Payment_Gateway $gateway ) { if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) { - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'return 1: ' . $GLOBALS['james-unit-test-func'] ); - } return; } @@ -84,25 +81,15 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { $available_upe_payment_methods = $gateway->get_upe_available_payment_methods(); if ( ! in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $available_upe_payment_methods, true ) ) { - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'return 2: ' . $GLOBALS['james-unit-test-func'] ); - } - return; } if ( ! is_a( $gateway, 'WC_Stripe_UPE_Payment_Gateway' ) ) { - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'return 3: ' . $GLOBALS['james-unit-test-func'] ); - } return; } // If store currency is not USD, skip if ( 'USD' !== get_woocommerce_currency() ) { - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'return 4: ' . $GLOBALS['james-unit-test-func'] ); - } return; } @@ -113,9 +100,6 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) { ! in_array( WC_Stripe_UPE_Payment_Method_CC::STRIPE_ID, $enabled_payment_methods, true ) || in_array( WC_Stripe_UPE_Payment_Method_Link::STRIPE_ID, $enabled_payment_methods, true ) ) { - if ( isset( $GLOBALS['james-unit-test-func'] ) ) { - var_export( 'return 5: ' . $GLOBALS['james-unit-test-func'] ); - } return; } diff --git a/tests/phpunit/test-class-wc-stripe-notes.php b/tests/phpunit/test-class-wc-stripe-notes.php index 8986a81b19..a4b3cbce6e 100644 --- a/tests/phpunit/test-class-wc-stripe-notes.php +++ b/tests/phpunit/test-class-wc-stripe-notes.php @@ -71,9 +71,7 @@ public function test_create_upe_stripelink_note() { ) ->getMock(); WC_Stripe::get_instance()->account->method( 'get_cached_account_data' )->willReturn( [ 'country' => 'US' ] ); - $GLOBALS['james-unit-test-func'] = __METHOD__; WC_Stripe_Inbox_Notes::create_upe_notes(); - unset( $GLOBALS['james-unit-test-func'] ); $admin_note_store = WC_Data_Store::load( 'admin-note' ); $this->assertSame( 1, count( $admin_note_store->get_notes_with_name( WC_Stripe_UPE_StripeLink_Note::NOTE_NAME ) ) ); } From 6161bbe41ee8a2fb9ecc088e2c8b3957b8bcb30a Mon Sep 17 00:00:00 2001 From: mattallan Date: Fri, 12 Jan 2024 14:58:41 +1000 Subject: [PATCH 15/29] Remove non-split temporary code added while developing deferred intents --- client/classic/upe/payment-processing.js | 51 ++++++++---------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/client/classic/upe/payment-processing.js b/client/classic/upe/payment-processing.js index c25d27a5a9..d2566e9687 100644 --- a/client/classic/upe/payment-processing.js +++ b/client/classic/upe/payment-processing.js @@ -85,15 +85,6 @@ function createStripePaymentElement( api, paymentMethodType = null ) { }, } ); - // To be removed with Split PE. - if ( paymentMethodType === null ) { - paymentMethodType = 'stripe'; - gatewayUPEComponents.stripe = { - elements: null, - upeElement: null, - }; - } - gatewayUPEComponents[ paymentMethodType ].elements = elements; gatewayUPEComponents[ paymentMethodType @@ -184,31 +175,28 @@ export async function mountStripePaymentElement( api, domElement ) { const event = new Event( 'wc-credit-card-form-init' ); document.body.dispatchEvent( event ); - const paymentMethodType = domElement.dataset.paymentMethodType; - let upeElement; + let paymentMethodType = domElement.dataset.paymentMethodType; - // Non-split PE. To be removed. if ( typeof paymentMethodType === 'undefined' ) { - upeElement = await createStripePaymentElement( api ); - - upeElement.on( 'change', ( e ) => { - const selectedUPEPaymentType = e.value.type; - const isPaymentMethodReusable = - paymentMethodsConfig[ selectedUPEPaymentType ].isReusable; - showNewPaymentMethodCheckbox( isPaymentMethodReusable ); - setSelectedUPEPaymentType( selectedUPEPaymentType ); - } ); - } else { - // Split PE. - if ( ! gatewayUPEComponents[ paymentMethodType ] ) { - return; - } + paymentMethodType = 'card'; + } - upeElement = - gatewayUPEComponents[ paymentMethodType ].upeElement || - ( await createStripePaymentElement( api, paymentMethodType ) ); + if ( ! gatewayUPEComponents[ paymentMethodType ] ) { + return; } + const upeElement = + gatewayUPEComponents[ paymentMethodType ].upeElement || + ( await createStripePaymentElement( api, paymentMethodType ) ); + + upeElement.on( 'change', ( e ) => { + const selectedUPEPaymentType = e.value.type; + const isPaymentMethodReusable = + paymentMethodsConfig[ selectedUPEPaymentType ].isReusable; + showNewPaymentMethodCheckbox( isPaymentMethodReusable ); + setSelectedUPEPaymentType( selectedUPEPaymentType ); + } ); + upeElement.mount( domElement ); } @@ -264,11 +252,6 @@ export const processPayment = ( blockUI( jQueryForm ); - // Non split. To be removed. - if ( paymentMethodType === null ) { - paymentMethodType = 'stripe'; - } - const elements = gatewayUPEComponents[ paymentMethodType ].elements; ( async () => { From 887120855efd0f928c5d7947e760be82ca1d0291 Mon Sep 17 00:00:00 2001 From: mattallan Date: Fri, 12 Jan 2024 15:00:41 +1000 Subject: [PATCH 16/29] Update generateCheckoutEventNames() so that it attaches checkout hooks for all of our payment methods --- client/stripe-utils/constants.js | 26 ++++++++++++++++++++++++++ client/stripe-utils/utils.js | 10 ++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/client/stripe-utils/constants.js b/client/stripe-utils/constants.js index 7257f91cca..76a589ce83 100644 --- a/client/stripe-utils/constants.js +++ b/client/stripe-utils/constants.js @@ -1,3 +1,29 @@ +export const PAYMENT_METHOD_NAME_CARD = 'stripe'; +export const PAYMENT_METHOD_NAME_GIROPAY = 'stripe_giropay'; +export const PAYMENT_METHOD_NAME_EPS = 'stripe_eps'; +export const PAYMENT_METHOD_NAME_IDEAL = 'stripe_ideal'; +export const PAYMENT_METHOD_NAME_P24 = 'stripe_p24'; +export const PAYMENT_METHOD_NAME_SEPA = 'stripe_sepa_debit'; +export const PAYMENT_METHOD_NAME_SOFORT = 'stripe_sofort'; +export const PAYMENT_METHOD_NAME_BOLETO = 'stripe_boleto'; +export const PAYMENT_METHOD_NAME_OXXO = 'stripe_oxxo'; +export const PAYMENT_METHOD_NAME_BANCONTACT = 'stripe_bancontact'; + +export function getPaymentMethodsConstants() { + return [ + PAYMENT_METHOD_NAME_CARD, + PAYMENT_METHOD_NAME_GIROPAY, + PAYMENT_METHOD_NAME_EPS, + PAYMENT_METHOD_NAME_IDEAL, + PAYMENT_METHOD_NAME_P24, + PAYMENT_METHOD_NAME_SEPA, + PAYMENT_METHOD_NAME_SOFORT, + PAYMENT_METHOD_NAME_BOLETO, + PAYMENT_METHOD_NAME_OXXO, + PAYMENT_METHOD_NAME_BANCONTACT, + ]; +} + export const errorTypes = { INVALID_EMAIL: 'email_invalid', INVALID_REQUEST: 'invalid_request_error', diff --git a/client/stripe-utils/utils.js b/client/stripe-utils/utils.js index b7fc4f09a2..4758becc1d 100644 --- a/client/stripe-utils/utils.js +++ b/client/stripe-utils/utils.js @@ -2,7 +2,11 @@ import { __ } from '@wordpress/i18n'; import { getAppearance } from '../styles/upe'; -import { errorTypes, errorCodes } from './constants'; +import { + errorTypes, + errorCodes, + getPaymentMethodsConstants, +} from './constants'; /** * @typedef {import('./type-defs').StripeServerData} StripeServerData @@ -263,9 +267,7 @@ function shouldIncludeTerms() { } export const generateCheckoutEventNames = () => { - const paymentMethods = [ 'stripe' ]; - - return paymentMethods + return getPaymentMethodsConstants() .map( ( method ) => `checkout_place_order_${ method }` ) .join( ' ' ); }; From 660645057323c061d5a9cf3a86723c557aff26b2 Mon Sep 17 00:00:00 2001 From: mattallan Date: Fri, 12 Jan 2024 15:01:16 +1000 Subject: [PATCH 17/29] Sends gatewayId in JS params to fix getSelectedUPEGatewayPaymentMethod() --- includes/payment-methods/class-wc-stripe-upe-payment-gateway.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 478a0a3380..32bcceec8a 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -299,6 +299,7 @@ public function javascript_params() { $is_change_payment_method = $this->is_changing_payment_method_for_subscription(); $stripe_params = [ + 'gatewayId' => self::ID, 'title' => $this->title, 'isUPEEnabled' => true, 'key' => $this->publishable_key, From 1beba9dd9046375a69db520c9a5f94737aeeae69 Mon Sep 17 00:00:00 2001 From: mattallan Date: Fri, 12 Jan 2024 15:02:02 +1000 Subject: [PATCH 18/29] Don't add the UPE CC class to the list of WC gateways as it's already added --- woocommerce-gateway-stripe.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/woocommerce-gateway-stripe.php b/woocommerce-gateway-stripe.php index eb6e8c9eb3..f0b094778d 100644 --- a/woocommerce-gateway-stripe.php +++ b/woocommerce-gateway-stripe.php @@ -402,7 +402,11 @@ public function add_gateways( $methods ) { $methods[] = WC_Gateway_Stripe_Sofort::class; } } elseif ( is_a( $main_gateway, 'WC_Stripe_UPE_Payment_Gateway' ) ) { - $methods = array_merge( $main_gateway->payment_methods, $methods ); + // the $main_gateway represents the card gateway so we don't want to include it in the list of UPE gateways. + $upe_payment_methods = $main_gateway->payment_methods; + unset( $upe_payment_methods['card'] ); + + $methods = array_merge( $methods, $upe_payment_methods ); } // These payment gateways will always be visible, regardless if UPE is enabled or disabled: From 6492c7aabb6b7e737ca8be2ab29f98a235955803 Mon Sep 17 00:00:00 2001 From: mattallan Date: Fri, 12 Jan 2024 15:03:10 +1000 Subject: [PATCH 19/29] Adds necessary payment_fields() function to UPE payment method base class - This function was copied over from the main UPE gateway class. - This function calls other methods from the main UPE gateway class and so they've been copied over as well. --- .../class-wc-stripe-upe-payment-method.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index d99e6be0d8..4ef64d6850 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -374,4 +374,76 @@ public function process_payment( $order_id ) { public function is_available_for_account_country() { return true; } + + public function get_option_key() { + return 'woocommerce_stripe_settings'; + } + + /** + * Renders the UPE input fields needed to get the user's payment information on the checkout page + */ + public function payment_fields() { + try { + + // Output the form HTML. + ?> + get_description() ) ) : ?> +

get_description() ); ?>

+ +
+
+ +
+ is_saved_cards_enabled() && $this->is_reusable() ) { + $force_save_payment = ( $this->is_reusable() && ! apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $this->is_reusable() ) ) || is_add_payment_method_page(); + if ( is_user_logged_in() ) { + $this->save_payment_method_checkbox( $force_save_payment ); + } + } + } catch ( Exception $e ) { + // Output the error message. + WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() ); + ?> +
+ +
+ get_option( 'upe_checkout_experience_accepted_payments', [ 'card' ] ); + } + + public function is_saved_cards_enabled() { + return 'yes' === $this->get_option( 'saved_cards' ); + } + + /** + * Displays the save to account checkbox. + * + * @since 4.1.0 + * @version 5.6.0 + */ + public function save_payment_method_checkbox( $force_checked = false ) { + $id = 'wc-' . $this->get_id() . '-new-payment-method'; + ?> +
> +

+ /> + +

+
+ Date: Fri, 12 Jan 2024 15:04:47 +1000 Subject: [PATCH 20/29] Clean up main constuctor after adding helper functions --- .../class-wc-stripe-upe-payment-method.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 4ef64d6850..2e6c483d9f 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -87,14 +87,9 @@ abstract class WC_Stripe_UPE_Payment_Method extends WC_Payment_Gateway { public function __construct() { $main_settings = get_option( 'woocommerce_stripe_settings' ); - if ( isset( $main_settings['upe_checkout_experience_accepted_payments'] ) ) { - $enabled_upe_methods = $main_settings['upe_checkout_experience_accepted_payments']; - } else { - $enabled_upe_methods = [ WC_Stripe_UPE_Payment_Method_CC::STRIPE_ID ]; - } - - $this->enabled = in_array( static::STRIPE_ID, $enabled_upe_methods, true ); - $this->id = WC_Gateway_Stripe::ID . '_' . static::STRIPE_ID; + $this->enabled = in_array( static::STRIPE_ID, $this->get_upe_enabled_payment_method_ids(), true ); + $this->id = WC_Gateway_Stripe::ID . '_' . static::STRIPE_ID; + $this->testmode = ! empty( $main_settings['testmode'] ) && 'yes' === $main_settings['testmode']; } /** From 259ab863677dbc56488dabb01c075fff129dbe64 Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 15 Jan 2024 13:45:36 +1000 Subject: [PATCH 21/29] Consolidate logic to handle the redirect url for 3ds, vouchers and bank offsite URLs --- .../class-wc-stripe-upe-payment-gateway.php | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 7182deb5b4..4b33fd0538 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -705,9 +705,24 @@ private function process_payment_with_deferred_intent( int $order_id ) { $redirect = $this->get_return_url( $order ); - // If the payment intent requires action, respond with redirect URL or the pi and client secret so it can confirmed on checkout. - if ( 'requires_action' === $payment_intent->status || 'requires_confirmation' === $payment_intent->status ) { - if ( isset( $payment_intent->next_action->type ) && 'redirect_to_url' === $payment_intent->next_action->type && ! empty( $payment_intent->next_action->redirect_to_url->url ) ) { + /** + * Depending on the payment method used to process the payment, we may need to redirect the user to a URL for further processing. + * + * - Voucher payments (Boleto or Oxxo) respond with a hash URL so the client JS code can recognize the response, pull out the necessary args and handle the displaying of the voucher. + * - Other payment methods like Giropay, iDEAL etc require a redirect to a URL provided by Stripe. + * - 3DS Card payments return a hash URL so the client JS code can recognize the response, pull out the necessary PI args and display the 3DS confirmation modal. + */ + if ( in_array( $payment_intent->status, [ 'requires_confirmation', 'requires_action' ], true ) ) { + if ( isset( $payment_intent->payment_method_types ) && count( array_intersect( [ 'boleto', 'oxxo' ], $payment_intent->payment_method_types ) ) !== 0 ) { + // For Voucher payment method types (Boleto/Oxxo), redirect the customer to a URL hash formatted #wc-stripe-voucher-{order_id}:{payment_method_type}:{client_secret}:{redirect_url} to confirm the intent which also displays the voucher. + $redirect = sprintf( + '#wc-stripe-voucher-%s:%s:%s:%s', + $order_id, + $payment_information['selected_payment_type'], + $payment_intent->client_secret, + rawurlencode( $redirect ) + ); + } elseif ( isset( $payment_intent->next_action->type ) && 'redirect_to_url' === $payment_intent->next_action->type && ! empty( $payment_intent->next_action->redirect_to_url->url ) ) { $redirect = $payment_intent->next_action->redirect_to_url->url; } else { $redirect = sprintf( @@ -720,17 +735,6 @@ private function process_payment_with_deferred_intent( int $order_id ) { } } - // For Voucher payment method types (Boleto/Oxxo), redirect the customer to a URL hash formatted #wc-stripe-voucher-{order_id}:{payment_method_type}:{client_secret}:{redirect_url} to confirm the intent which also displays the voucher. - if ( in_array( $payment_intent->status, [ 'requires_confirmation', 'requires_action' ], true ) && isset( $payment_intent->payment_method_types ) && count( array_intersect( [ 'boleto', 'oxxo' ], $payment_intent->payment_method_types ) ) !== 0 ) { - $redirect = sprintf( - '#wc-stripe-voucher-%s:%s:%s:%s', - $order_id, - $payment_information['selected_payment_type'], - $payment_intent->client_secret, - rawurlencode( $redirect ) - ); - } - return [ 'result' => 'success', 'redirect' => $redirect, From b60906f1a7cb6f02bf812306e5118fd889dc066e Mon Sep 17 00:00:00 2001 From: James Allan Date: Mon, 15 Jan 2024 14:03:48 +1000 Subject: [PATCH 22/29] Defend against possible uncallable function --- .../payment-methods/class-wc-stripe-upe-payment-gateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 4b33fd0538..bd3e992d3b 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -443,7 +443,7 @@ public function get_upe_available_payment_methods() { $available_payment_methods = []; foreach ( $this->payment_methods as $payment_method ) { - if ( ! $payment_method->is_available_for_account_country() ) { + if ( is_callable( [ $payment_method, 'is_available_for_account_country' ] ) && ! $payment_method->is_available_for_account_country() ) { continue; } $available_payment_methods[] = $payment_method->get_id(); From 527959c44c56cdc3b1cd7ac3d81b514c1ecc8ff1 Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 14:58:51 +1000 Subject: [PATCH 23/29] Remove non-split PE support code and manually set selected UPE Payment Method --- client/classic/upe/payment-processing.js | 32 +----------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/client/classic/upe/payment-processing.js b/client/classic/upe/payment-processing.js index d2566e9687..1ab2c2bcfd 100644 --- a/client/classic/upe/payment-processing.js +++ b/client/classic/upe/payment-processing.js @@ -157,8 +157,6 @@ function createStripePaymentMethod( * Mounts the existing Stripe Payment Element to the DOM element. * Creates the Stripe Payment Element instance if it doesn't exist and mounts it to the DOM element. * - * @todo Make it only Split when implemented. - * * @param {Object} api The API object. * @param {string} domElement The selector of the DOM element of particular payment method to mount the UPE element to. **/ @@ -188,15 +186,6 @@ export async function mountStripePaymentElement( api, domElement ) { const upeElement = gatewayUPEComponents[ paymentMethodType ].upeElement || ( await createStripePaymentElement( api, paymentMethodType ) ); - - upeElement.on( 'change', ( e ) => { - const selectedUPEPaymentType = e.value.type; - const isPaymentMethodReusable = - paymentMethodsConfig[ selectedUPEPaymentType ].isReusable; - showNewPaymentMethodCheckbox( isPaymentMethodReusable ); - setSelectedUPEPaymentType( selectedUPEPaymentType ); - } ); - upeElement.mount( domElement ); } @@ -207,26 +196,6 @@ function setSelectedUPEPaymentType( paymentType ) { ).value = paymentType; } -// Show or hide save payment information checkbox -function showNewPaymentMethodCheckbox( show = true ) { - const saveCardElement = document.querySelector( - '.woocommerce-SavedPaymentMethods-saveNew' - ); - - if ( saveCardElement ) { - saveCardElement.style.visibility = show ? 'visible' : 'hidden'; - } - - const stripeSaveCardCheckbox = document.querySelector( - 'input#wc-stripe-new-payment-method' - ); - - if ( ! show && stripeSaveCardCheckbox ) { - stripeSaveCardCheckbox.setAttribute( 'checked', false ); - stripeSaveCardCheckbox.dispatchEvent( new Event( 'change' ) ); - } -} - /** * Handles the checkout process for the provided jQuery form and Stripe payment method type. The function blocks the * form UI to prevent duplicate submission and validates the Stripe elements. It then creates a Stripe payment method @@ -263,6 +232,7 @@ export const processPayment = ( jQueryForm, paymentMethodType ); + setSelectedUPEPaymentType( paymentMethodType ); appendIsUsingDeferredIntentToForm( jQueryForm ); appendPaymentMethodIdToForm( jQueryForm, From 74a5809c36e27eb851d8ae02e515aa2ff7409268 Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 15:10:26 +1000 Subject: [PATCH 24/29] Fix isUsingSavedPaymentMethod util function to work in a split PE environment --- client/stripe-utils/constants.js | 24 ++++++++++++------------ client/stripe-utils/utils.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/client/stripe-utils/constants.js b/client/stripe-utils/constants.js index 76a589ce83..390ecbb7a0 100644 --- a/client/stripe-utils/constants.js +++ b/client/stripe-utils/constants.js @@ -10,18 +10,18 @@ export const PAYMENT_METHOD_NAME_OXXO = 'stripe_oxxo'; export const PAYMENT_METHOD_NAME_BANCONTACT = 'stripe_bancontact'; export function getPaymentMethodsConstants() { - return [ - PAYMENT_METHOD_NAME_CARD, - PAYMENT_METHOD_NAME_GIROPAY, - PAYMENT_METHOD_NAME_EPS, - PAYMENT_METHOD_NAME_IDEAL, - PAYMENT_METHOD_NAME_P24, - PAYMENT_METHOD_NAME_SEPA, - PAYMENT_METHOD_NAME_SOFORT, - PAYMENT_METHOD_NAME_BOLETO, - PAYMENT_METHOD_NAME_OXXO, - PAYMENT_METHOD_NAME_BANCONTACT, - ]; + return { + card: PAYMENT_METHOD_NAME_CARD, + giropay: PAYMENT_METHOD_NAME_GIROPAY, + eps: PAYMENT_METHOD_NAME_EPS, + ideal: PAYMENT_METHOD_NAME_IDEAL, + p24: PAYMENT_METHOD_NAME_P24, + sepa: PAYMENT_METHOD_NAME_SEPA, + sofort: PAYMENT_METHOD_NAME_SOFORT, + boleto: PAYMENT_METHOD_NAME_BOLETO, + oxxo: PAYMENT_METHOD_NAME_OXXO, + bancontact: PAYMENT_METHOD_NAME_BANCONTACT, + }; } export const errorTypes = { diff --git a/client/stripe-utils/utils.js b/client/stripe-utils/utils.js index 4758becc1d..e7d3ffaf7d 100644 --- a/client/stripe-utils/utils.js +++ b/client/stripe-utils/utils.js @@ -266,8 +266,14 @@ function shouldIncludeTerms() { return false; } +/** + * Returns a string of event names to be used for registering checkout submission handlers. + * For example: "checkout_place_order_stripe checkout_place_order_stripe_ideal ...checkout_place_order_{paymentMethod}" + * + * @return {string} String of event names. + */ export const generateCheckoutEventNames = () => { - return getPaymentMethodsConstants() + return Object.values( getPaymentMethodsConstants() ) .map( ( method ) => `checkout_place_order_${ method }` ) .join( ' ' ); }; @@ -294,13 +300,17 @@ export const appendSetupIntentToForm = ( form, setupIntent ) => { /** * Checks if the customer is using a saved payment method. * + * @param {string} paymentMethodType The payment method type ('card', 'ideal', etc.). + * * @return {boolean} Boolean indicating whether or not a saved payment method is being used. */ -export const isUsingSavedPaymentMethod = () => { +export const isUsingSavedPaymentMethod = ( paymentMethodType ) => { + const paymentMethod = getPaymentMethodName( paymentMethodType ); return ( - document.querySelector( '#wc-stripe-new-payment-method' )?.length && + document.querySelector( `#wc-${ paymentMethod }-new-payment-method` ) + ?.length && ! document - .querySelector( '#wc-stripe-new-payment-method' ) + .querySelector( `#wc-${ paymentMethod }-new-payment-method` ) .is( ':checked' ) ); }; @@ -468,3 +478,17 @@ export const initializeUPEAppearance = () => { return appearance; }; + +/** + * Gets the payment method name from the given payment method type. + * For example, when passed 'card' returns 'stripe' and for 'ideal' returns 'stripe_ideal'. + * + * Defaults to 'stripe' if the given payment method type is not found in the list of payment methods constants. + * + * @param {string} paymentMethodType The payment method type ('card', 'ideal', etc.). + * + * @return {string} The payment method name. + */ +export const getPaymentMethodName = ( paymentMethodType ) => { + return getPaymentMethodsConstants()[ paymentMethodType ] || 'stripe'; +}; From f4114613680c30d7b9fff13051f339d980dc90be Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 15:13:06 +1000 Subject: [PATCH 25/29] Remove get_upe_enabled_payment_method_ids() as it doesn't make sense in base UPE class --- .../class-wc-stripe-upe-payment-method.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 2e6c483d9f..9820959ea4 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -87,7 +87,7 @@ abstract class WC_Stripe_UPE_Payment_Method extends WC_Payment_Gateway { public function __construct() { $main_settings = get_option( 'woocommerce_stripe_settings' ); - $this->enabled = in_array( static::STRIPE_ID, $this->get_upe_enabled_payment_method_ids(), true ); + $this->enabled = in_array( static::STRIPE_ID, $this->get_option( 'upe_checkout_experience_accepted_payments', [ 'card' ] ), true ) ? 'yes' : 'no'; $this->id = WC_Gateway_Stripe::ID . '_' . static::STRIPE_ID; $this->testmode = ! empty( $main_settings['testmode'] ) && 'yes' === $main_settings['testmode']; } @@ -410,14 +410,10 @@ public function payment_fields() { } /** - * Returns the list of enabled payment method types for UPE. + * Returns true if the saved cards feature is enabled. * - * @return string[] + * @return bool */ - public function get_upe_enabled_payment_method_ids() { - return $this->get_option( 'upe_checkout_experience_accepted_payments', [ 'card' ] ); - } - public function is_saved_cards_enabled() { return 'yes' === $this->get_option( 'saved_cards' ); } From 5bb5a2ea19293dcb2c57d0d24a1f7bfc04a28cca Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 15:14:08 +1000 Subject: [PATCH 26/29] Fix is_enabled() so that it returns a boolean again --- includes/payment-methods/class-wc-stripe-upe-payment-method.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 9820959ea4..5a46920f98 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -107,7 +107,7 @@ public function get_id() { * @return bool */ public function is_enabled() { - return $this->enabled; + return 'yes' === $this->enabled; } /** From 2351511ee193784a78c81658e7993711ebfebb76 Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 15:15:00 +1000 Subject: [PATCH 27/29] Don't show Stripe payment methods on checkout that have been disabled in the settings --- .../payment-methods/class-wc-stripe-upe-payment-method.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 5a46920f98..870dd94d43 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -116,7 +116,11 @@ public function is_enabled() { * @return bool */ public function is_available() { - return $this->is_enabled_at_checkout(); + if ( is_add_payment_method_page() && ! $this->is_reusable() ) { + return false; + } + + return $this->is_enabled_at_checkout() && parent::is_available(); } /** From be433addce22dddbb26d43520f7e3583782a4ffe Mon Sep 17 00:00:00 2001 From: mattallan Date: Tue, 16 Jan 2024 15:17:09 +1000 Subject: [PATCH 28/29] Make sure stripe payment elements are unique by using IDs with 'stripe_' before each payment method name --- .../class-wc-stripe-upe-payment-method.php | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 870dd94d43..096ff8775e 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -374,28 +374,34 @@ public function is_available_for_account_country() { return true; } + /** + * Returns the UPE Payment Method settings option. + * + * Overrides @see WC:Settings_API::get_option_key() to use the same option key as the main Stripe gateway. + * + * @return string + */ public function get_option_key() { return 'woocommerce_stripe_settings'; } /** - * Renders the UPE input fields needed to get the user's payment information on the checkout page + * Renders the UPE payment fields. */ public function payment_fields() { try { + $display_tokenization = $this->is_reusable() && is_checkout(); - // Output the form HTML. - ?> - get_description() ) ) : ?> + if ( ! empty( $this->get_description() ) ) : ?>

get_description() ); ?>

-
+
- +
is_saved_cards_enabled() && $this->is_reusable() ) { - $force_save_payment = ( $this->is_reusable() && ! apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $this->is_reusable() ) ) || is_add_payment_method_page(); + $force_save_payment = ( $display_tokenization && ! apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) ) || is_add_payment_method_page(); if ( is_user_logged_in() ) { $this->save_payment_method_checkbox( $force_save_payment ); } @@ -405,9 +411,7 @@ public function payment_fields() { WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() ); ?>
- +
get_id() . '-new-payment-method'; + $id = 'wc-' . $this->id . '-new-payment-method'; ?>
>

From 13a640e5fa5fe8e79018ad0c453051d57ddff72c Mon Sep 17 00:00:00 2001 From: mattallan Date: Thu, 18 Jan 2024 13:29:31 +1000 Subject: [PATCH 29/29] Remove gateway description from payment fields and display testing instruction if they exist --- .../payment-methods/class-wc-stripe-upe-payment-method.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method.php b/includes/payment-methods/class-wc-stripe-upe-payment-method.php index 096ff8775e..97adb3775a 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-method.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-method.php @@ -392,8 +392,8 @@ public function payment_fields() { try { $display_tokenization = $this->is_reusable() && is_checkout(); - if ( ! empty( $this->get_description() ) ) : ?> -

get_description() ); ?>

+ if ( $this->testmode && ! empty( $this->get_testing_instructions() ) ) : ?> +

get_testing_instructions() ); ?>