From 8467c25ddcc0855d8b00567cf6677b76ed099e25 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Mon, 4 Aug 2025 17:41:45 -0300 Subject: [PATCH 01/18] Allow purchase of free trials with ECE --- .../class-wc-stripe-express-checkout-helper.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php index eec5c79f4b..a7ffa2a52e 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php @@ -684,10 +684,9 @@ public function should_show_express_checkout_button() { return false; } - // Don't show in the product page if the product price is 0. - // ToDo: support free trials. Free trials should be supported if the product does not require shipping. - if ( $is_product && $product && 0.0 === (float) $product->get_price() ) { - WC_Stripe_Logger::log( 'Stripe Express Checkout does not support free products.' ); + // Don't show in the product page if the product price is 0 and the product requires shipping. + if ( $is_product && $product && 0.0 === (float) $product->get_price() && $this->product_or_cart_needs_shipping() ) { + WC_Stripe_Logger::log( 'Stripe Express Checkout does not support free products that requires shipping.' ); return false; } From c17d9de7c0c55b4687bc34b7c7e9a4647d1c08da Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Mon, 4 Aug 2025 17:44:55 -0300 Subject: [PATCH 02/18] Changelog and readme entries --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 71de87dbf4..f7ffd72493 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,7 @@ *** Changelog *** = 9.8.0 - xxxx-xx-xx = +* Add - Allow the purchase of free trials using the Express Payment methods when the product does not require shipping * Add - A new pill to the payment methods page to indicate the credit card requirement when the Optimized Checkout feature is enabled * Add - Tracks the toggle of the Optimized Checkout feature in the promotional banner * Fix - Force the card payment method to be enabled when the Optimized Checkout is enabled in the merchant's Payment Method Configuration diff --git a/readme.txt b/readme.txt index 55d04cf08a..a447edfb2d 100644 --- a/readme.txt +++ b/readme.txt @@ -111,6 +111,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o == Changelog == = 9.8.0 - xxxx-xx-xx = +* Add - Allow the purchase of free trials using the Express Payment methods when the product does not require shipping * Add - A new pill to the payment methods page to indicate the credit card requirement when the Optimized Checkout feature is enabled * Add - Tracks the toggle of the Optimized Checkout feature in the promotional banner * Fix - Force the card payment method to be enabled when the Optimized Checkout is enabled in the merchant's Payment Method Configuration From 33e176ce6ce59edae2109d77cd03c3234940b3f1 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Tue, 5 Aug 2025 10:05:32 -0300 Subject: [PATCH 03/18] Unit tests --- ...WC_Stripe_Express_Checkout_Helper_Test.php | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php b/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php index d00e703aa2..bf81db887a 100644 --- a/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php +++ b/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php @@ -314,6 +314,86 @@ public function test_hides_ece_if_stripe_gateway_unavailable() { WC()->payment_gateways()->payment_gateways = $original_gateways; } + /** + * Test should_show_express_checkout_button, free trial logic. + * + * @return void + */ + public function test_hides_ece_if_free_trial_requires_shipping() { + $this->set_up_shipping_methods(); + + $wc_stripe_ece_helper_mock = $this->createPartialMock( + WC_Stripe_Express_Checkout_Helper::class, + [ + 'is_product', + 'get_product', + 'allowed_items_in_cart', + 'should_show_ece_on_cart_page', + 'should_show_ece_on_checkout_page', + ], + ); + + $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'is_product' )->willReturn( true ); + $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'allowed_items_in_cart' )->willReturn( true ); + $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_cart_page' )->willReturn( true ); + $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_checkout_page' )->willReturn( true ); + $wc_stripe_ece_helper_mock->testmode = true; + + if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { + define( 'WOOCOMMERCE_CHECKOUT', true ); + } + + // Ensure that the 'stripe' gateway is available. + $original_gateways = WC()->payment_gateways()->payment_gateways; + WC()->payment_gateways()->payment_gateways = [ + 'stripe' => new WC_Gateway_Stripe(), + ]; + + update_option( 'woocommerce_calc_taxes', 'no' ); + + // Should show, as free virtual products does not require shipping. + $virtual_product = WC_Helper_Product::create_simple_product(); + $virtual_product->set_virtual( true ); + $virtual_product->set_tax_status( 'none' ); + $virtual_product->set_price( 0 ); + $virtual_product->save(); + + WC()->session->init(); + WC()->cart->empty_cart(); + + WC()->cart->add_to_cart( $virtual_product->get_id(), 1 ); + $wc_stripe_ece_helper_mock + ->expects( $this->any() ) + ->method( 'get_product' ) + ->willReturn( $virtual_product ); + + $this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() ); + + // Should hide if the free product requires shipping. + $shippable_product = WC_Helper_Product::create_simple_product(); + $shippable_product->set_virtual( false ); + $shippable_product->set_tax_status( 'none' ); + $shippable_product->save(); + + WC()->session->init(); + WC()->cart->empty_cart(); + + WC()->cart->add_to_cart( $shippable_product->get_id(), 1 ); + $wc_stripe_ece_helper_mock + ->expects( $this->any() ) + ->method( 'get_product' ) + ->willReturn( $shippable_product ); + + $this->assertFalse( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() ); + + // Restore original settings. + WC()->cart->empty_cart(); + WC()->session->cleanup_sessions(); + WC()->payment_gateways()->payment_gateways = $original_gateways; + + update_option( 'woocommerce_calc_taxes', 'yes' ); + } + /** * Test for get_checkout_data(). */ From 63d45fdf37dafc8fa4e9f98196b800efdbd25c0a Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Tue, 5 Aug 2025 17:56:51 -0300 Subject: [PATCH 04/18] Remove condition inside canMakePayment --- client/blocks/express-checkout/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/blocks/express-checkout/index.js b/client/blocks/express-checkout/index.js index a297842cfb..6442bc92c4 100644 --- a/client/blocks/express-checkout/index.js +++ b/client/blocks/express-checkout/index.js @@ -83,10 +83,6 @@ const expressCheckoutElement = ( expressPaymentMethod, api ) => { ); const edit = getEditorElement( expressPaymentMethod ); const canMakePayment = ( { cart } ) => { - if ( parseFloat( cart.cartTotals.total_price ) === 0.0 ) { - return false; - } - if ( ! getBlocksConfiguration()?.shouldShowExpressCheckoutButton ) { return false; } From cdca3511efeece6b7f7d141368dfc733d1db0380 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Tue, 19 Aug 2025 15:48:55 -0300 Subject: [PATCH 05/18] Temporary changes to debug --- client/blocks/express-checkout/index.js | 2 ++ client/express-checkout/utils/index.js | 1 - .../payment-methods/class-wc-stripe-express-checkout-helper.php | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/blocks/express-checkout/index.js b/client/blocks/express-checkout/index.js index 6442bc92c4..c40a4feba1 100644 --- a/client/blocks/express-checkout/index.js +++ b/client/blocks/express-checkout/index.js @@ -83,6 +83,8 @@ const expressCheckoutElement = ( expressPaymentMethod, api ) => { ); const edit = getEditorElement( expressPaymentMethod ); const canMakePayment = ( { cart } ) => { + return true; + if ( ! getBlocksConfiguration()?.shouldShowExpressCheckoutButton ) { return false; } diff --git a/client/express-checkout/utils/index.js b/client/express-checkout/utils/index.js index 7dab154c39..f701b35a50 100644 --- a/client/express-checkout/utils/index.js +++ b/client/express-checkout/utils/index.js @@ -105,7 +105,6 @@ export const getExpressCheckoutButtonAppearance = () => { return { variables: { borderRadius: `${ - getExpressCheckoutData( 'button' )?.radius || getDefaultBorderRadius() }px`, spacingUnit: '6px', diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php index 1851a10c12..e03a4a8912 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php @@ -622,6 +622,8 @@ public function is_page_supported() { * @return boolean True if express checkout elements are supported on current page, false otherwise */ public function should_show_express_checkout_button() { + return true; + // Bail if account is not connected. if ( ! WC_Stripe::get_instance()->connect->is_connected() ) { WC_Stripe_Logger::log( 'Account is not connected.' ); From 48ebe0d2623d22080d7f312bfb76e178d4156fd6 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 11:06:27 -0300 Subject: [PATCH 06/18] Method to check for existing free trial in cart --- ...lass-wc-stripe-express-checkout-helper.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php index e03a4a8912..1b60d1d700 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php @@ -571,6 +571,36 @@ public function has_subscription_product() { return false; } + /** + * Checks whether the subscription product has a free trial. + * + * @return bool + */ + public function has_free_trial() { + if ( ! class_exists( 'WC_Subscriptions_Product' ) ) { + return false; + } + + if ( $this->is_product() ) { + $product = $this->get_product(); + if ( ! $product ) { + return false; + } + if ( WC_Subscriptions_Product::is_subscription( $product ) && WC_Subscriptions_Product::get_trial_length( $product ) > 0 ) { + return true; + } + } elseif ( WC_Stripe_Helper::has_cart_or_checkout_on_current_page() ) { + foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { + $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key ); + if ( WC_Subscriptions_Product::is_subscription( $_product ) && WC_Subscriptions_Product::get_trial_length( $_product ) > 0 ) { + return true; + } + } + } + + return false; + } + /** * Checks if this is a product page or content contains a product_page shortcode. * From fa57d69501d28a8962f8a2f9df4892e4fa8e73d0 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 11:06:51 -0300 Subject: [PATCH 07/18] Adding the JS property to flag the free trial --- .../payment-methods/class-wc-stripe-express-checkout-element.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-element.php b/includes/payment-methods/class-wc-stripe-express-checkout-element.php index fcb23bf5a6..0a9f8ccd4b 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-element.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-element.php @@ -236,6 +236,7 @@ public function javascript_params() { 'taxes_based_on_billing' => wc_tax_enabled() && get_option( 'woocommerce_tax_based_on' ) === 'billing', 'allowed_shipping_countries' => $this->express_checkout_helper->get_allowed_shipping_countries(), 'custom_checkout_fields' => ( new WC_Stripe_Express_Checkout_Custom_Fields() )->get_custom_checkout_fields(), + 'has_free_trial' => $this->express_checkout_helper->has_free_trial(), ]; } From 1b99ba74b95c476f510101f031914d6e53e7cbf2 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 11:07:48 -0300 Subject: [PATCH 08/18] Checking for free trials to change the configuration mode parameter --- client/blocks/express-checkout/express-checkout-container.js | 4 +++- .../utils/check-payment-method-availability.js | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/blocks/express-checkout/express-checkout-container.js b/client/blocks/express-checkout/express-checkout-container.js index 1fe76a1772..b99ccce594 100644 --- a/client/blocks/express-checkout/express-checkout-container.js +++ b/client/blocks/express-checkout/express-checkout-container.js @@ -11,7 +11,9 @@ import { export const ExpressCheckoutContainer = ( props ) => { const { stripe, billing, expressPaymentMethod } = props; const options = { - mode: 'payment', + mode: getExpressCheckoutData( 'has_free_trial' ) + ? 'subscription' + : 'payment', ...( isManualPaymentMethodCreation( expressPaymentMethod ) && { paymentMethodCreation: 'manual', } ), diff --git a/client/express-checkout/utils/check-payment-method-availability.js b/client/express-checkout/utils/check-payment-method-availability.js index 0818b086cb..f9e772d828 100644 --- a/client/express-checkout/utils/check-payment-method-availability.js +++ b/client/express-checkout/utils/check-payment-method-availability.js @@ -2,6 +2,7 @@ import ReactDOM from 'react-dom'; import { ExpressCheckoutElement, Elements } from '@stripe/react-stripe-js'; import { memoize } from 'lodash'; import { + getExpressCheckoutData, getPaymentMethodTypesForExpressMethod, isManualPaymentMethodCreation, } from 'wcstripe/express-checkout/utils'; @@ -29,7 +30,9 @@ export const checkPaymentMethodIsAvailable = memoize( Date: Wed, 27 Aug 2025 12:27:09 -0300 Subject: [PATCH 09/18] Remove debug code --- client/blocks/express-checkout/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/blocks/express-checkout/index.js b/client/blocks/express-checkout/index.js index cdc0e11d56..f3c492cee5 100644 --- a/client/blocks/express-checkout/index.js +++ b/client/blocks/express-checkout/index.js @@ -83,8 +83,6 @@ const expressCheckoutElement = ( expressPaymentMethod, api ) => { ); const edit = getEditorElement( expressPaymentMethod ); const canMakePayment = ( { cart } ) => { - return true; - if ( ! getBlocksConfiguration()?.shouldShowExpressCheckoutButton ) { return false; } From 9a71639f96cfd4ecf6bdaa98c7fe5661ebfe062b Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 12:33:42 -0300 Subject: [PATCH 10/18] Revert unnecessary change --- client/express-checkout/utils/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/express-checkout/utils/index.js b/client/express-checkout/utils/index.js index 00da50ef0c..a4c0e238da 100644 --- a/client/express-checkout/utils/index.js +++ b/client/express-checkout/utils/index.js @@ -105,6 +105,7 @@ export const getExpressCheckoutButtonAppearance = () => { return { variables: { borderRadius: `${ + getExpressCheckoutData( 'button' )?.radius || getDefaultBorderRadius() }px`, spacingUnit: '6px', From c91368f746b76605d4e0988c243739850c423b05 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 12:36:41 -0300 Subject: [PATCH 11/18] Revert unnecessary change --- .../payment-methods/class-wc-stripe-express-checkout-helper.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php index 1b60d1d700..05e3aa8dab 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php @@ -652,8 +652,6 @@ public function is_page_supported() { * @return boolean True if express checkout elements are supported on current page, false otherwise */ public function should_show_express_checkout_button() { - return true; - // Bail if account is not connected. if ( ! WC_Stripe::get_instance()->connect->is_connected() ) { WC_Stripe_Logger::log( 'Account is not connected.' ); From 3dc3e41605e75fd6c8fb49de3c58a74e81148164 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 27 Aug 2025 16:53:03 -0300 Subject: [PATCH 12/18] Helper class for WC_Subscriptions_Product --- .../Helpers/WC_Subscriptions_Product.php | 61 +++++++++++++++++++ tests/phpunit/bootstrap.php | 1 + 2 files changed, 62 insertions(+) create mode 100644 tests/phpunit/Helpers/WC_Subscriptions_Product.php diff --git a/tests/phpunit/Helpers/WC_Subscriptions_Product.php b/tests/phpunit/Helpers/WC_Subscriptions_Product.php new file mode 100644 index 0000000000..34567e0c4c --- /dev/null +++ b/tests/phpunit/Helpers/WC_Subscriptions_Product.php @@ -0,0 +1,61 @@ + Date: Wed, 27 Aug 2025 16:53:20 -0300 Subject: [PATCH 13/18] Simplifying logic + unit test --- ...lass-wc-stripe-express-checkout-helper.php | 15 +-- ...WC_Stripe_Express_Checkout_Helper_Test.php | 101 ++++++++++++++++++ 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php index 05e3aa8dab..b6f826bd9e 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php @@ -577,24 +577,19 @@ public function has_subscription_product() { * @return bool */ public function has_free_trial() { - if ( ! class_exists( 'WC_Subscriptions_Product' ) ) { - return false; - } - if ( $this->is_product() ) { $product = $this->get_product(); if ( ! $product ) { return false; } - if ( WC_Subscriptions_Product::is_subscription( $product ) && WC_Subscriptions_Product::get_trial_length( $product ) > 0 ) { + if ( class_exists( 'WC_Subscriptions_Product' ) + && WC_Subscriptions_Product::is_subscription( $product ) + && WC_Subscriptions_Product::get_trial_length( $product ) > 0 ) { return true; } } elseif ( WC_Stripe_Helper::has_cart_or_checkout_on_current_page() ) { - foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { - $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key ); - if ( WC_Subscriptions_Product::is_subscription( $_product ) && WC_Subscriptions_Product::get_trial_length( $_product ) > 0 ) { - return true; - } + if ( class_exists( 'WC_Subscriptions_Cart' ) && WC_Subscriptions_Cart::cart_contains_free_trial() ) { + return true; } } diff --git a/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php b/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php index 1af2098861..4403606ad8 100644 --- a/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php +++ b/tests/phpunit/PaymentMethods/WC_Stripe_Express_Checkout_Helper_Test.php @@ -10,6 +10,10 @@ use WC_Shipping_Zones; use WC_Stripe_Express_Checkout_Helper; use WC_Stripe_Helper; +use WC_Subscription; +use WC_Subscriptions_Cart; +use WC_Subscriptions_Product; +use WooCommerce\Stripe\Tests\Helpers\WC_Helper_Order; use WooCommerce\Stripe\Tests\Helpers\WC_Helper_Product; use WP_UnitTestCase; @@ -885,4 +889,101 @@ public function provide_test_get_booking_ids_from_cart() { ], ]; } + + /** + * Test for has_free_trial(). + * + * @param bool $is_product Whether is product page. + * @param \WC_Order|null $product Product on product page. + * @param int $trial_length Trial length of the product. + * @param bool $is_checkout Whether is checkout page. + * @param bool $cart_contains_free_trial Whether cart contains a product with free trial. + * @param bool $expected Expected result. + * @return void + * @dataProvider provide_test_has_free_trial + */ + public function test_has_free_trial( $is_product, $product, $trial_length, $is_checkout, $cart_contains_free_trial, $expected ) { + add_filter( + 'woocommerce_is_checkout', + function () use ( $is_checkout ) { + return $is_checkout; + } + ); + + WC_Subscriptions_Cart::set_cart_contains_free_trial( $cart_contains_free_trial ); + + WC_Subscriptions_Product::set_is_subscription( true ); + + WC_Subscriptions_Product::set_trial_length( $trial_length ); + + $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class ) + ->onlyMethods( [ 'is_product', 'get_product' ] ) + ->getMock(); + + $helper->method( 'is_product' ) + ->willReturn( $is_product ); + + $helper->method( 'get_product' ) + ->willReturn( $product ); + + $actual = $helper->has_free_trial(); + + $this->assertSame( $expected, $actual ); + } + + /** + * Provider for `test_has_free_trial`. + * + * @return array + */ + public function provide_test_has_free_trial() { + $subscription = new WC_Subscription(); + + $subscription_with_trial = new WC_Subscription(); + $subscription_with_trial->update_meta_data( 'subscription_trial_length', 14 ); + $subscription_with_trial->save_meta_data(); + + return [ + 'product page, missing product' => [ + 'is_product' => true, + 'product' => null, + 'trial length' => 0, + 'is checkout' => false, + 'cart contains free trial' => false, + 'expected' => false, + ], + 'product page, no free trial' => [ + 'is_product' => true, + 'product' => $subscription, + 'trial length' => 0, + 'is checkout' => false, + 'cart contains free trial' => false, + 'expected' => false, + ], + 'product page, with free trial' => [ + 'is_product' => true, + 'product' => $subscription_with_trial, + 'trial length' => 14, + 'is checkout' => false, + 'cart contains free trial' => false, + 'expected' => true, + ], + 'cart/checkout page, no free trial' => [ + 'is_product' => false, + 'product' => $subscription, + 'trial length' => 0, + 'is checkout' => true, + 'cart contains free trial' => false, + 'expected' => false, + ], + 'cart/checkout page, with free trial' => [ + 'is_product' => false, + 'product' => $subscription_with_trial, + 'trial length' => 14, + 'is checkout' => true, + 'cart contains free trial' => true, + 'expected' => true, + ], + ]; + } } From ad40478a62d27339d250c437825046769508fbc5 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Fri, 5 Sep 2025 12:13:52 -0300 Subject: [PATCH 14/18] Fix payment method creation --- .../express-checkout-container.js | 10 ++++++---- client/express-checkout/utils/index.js | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/client/blocks/express-checkout/express-checkout-container.js b/client/blocks/express-checkout/express-checkout-container.js index 7edda352d9..ed6d2b6b59 100644 --- a/client/blocks/express-checkout/express-checkout-container.js +++ b/client/blocks/express-checkout/express-checkout-container.js @@ -10,11 +10,13 @@ import { export const ExpressCheckoutContainer = ( props ) => { const { stripe, billing, expressPaymentMethod } = props; + const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); const options = { - mode: getExpressCheckoutData( 'has_free_trial' ) - ? 'subscription' - : 'payment', - ...( isManualPaymentMethodCreation( expressPaymentMethod ) && { + mode: hasFreeTrial ? 'subscription' : 'payment', + ...( isManualPaymentMethodCreation( + expressPaymentMethod, + hasFreeTrial + ) && { paymentMethodCreation: 'manual', } ), amount: billing.cartTotal.value, diff --git a/client/express-checkout/utils/index.js b/client/express-checkout/utils/index.js index 7eb63367bf..79baddbd72 100644 --- a/client/express-checkout/utils/index.js +++ b/client/express-checkout/utils/index.js @@ -389,12 +389,18 @@ export const expressCheckoutNoticeDelay = async () => { /** * Determine if the express payment type should use manual payment method creation. * - * @param {string} expressPaymentType The express payment type, e.g 'googlePay' or 'google_pay' + * @param {string} expressPaymentType The express payment type, e.g 'googlePay' or 'google_pay' + * @param {boolean} hasFreeTrial Whether the product being purchased has a free trial. * @return {boolean} True if manual payment method creation should be used, false otherwise. */ -export const isManualPaymentMethodCreation = ( expressPaymentType ) => { - return ! [ - EXPRESS_PAYMENT_METHOD_SETTING_AMAZON_PAY, - PAYMENT_METHOD_AMAZON_PAY, - ].includes( expressPaymentType ); +export const isManualPaymentMethodCreation = ( + expressPaymentType, + hasFreeTrial +) => { + return ( + ! [ + EXPRESS_PAYMENT_METHOD_SETTING_AMAZON_PAY, + PAYMENT_METHOD_AMAZON_PAY, + ].includes( expressPaymentType ) || hasFreeTrial + ); }; From 0bd5184294b6b6fe400d264c803dfffe18282c4f Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Thu, 11 Sep 2025 18:11:47 -0300 Subject: [PATCH 15/18] Revert removal of free product check + adding the free trial check along it --- client/blocks/express-checkout/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/blocks/express-checkout/index.js b/client/blocks/express-checkout/index.js index f3c492cee5..9cac0d31ac 100644 --- a/client/blocks/express-checkout/index.js +++ b/client/blocks/express-checkout/index.js @@ -17,6 +17,7 @@ import { EXPRESS_PAYMENT_METHOD_SETTING_GOOGLE_PAY, EXPRESS_PAYMENT_METHOD_SETTING_LINK, } from 'wcstripe/stripe-utils/constants'; +import { getExpressCheckoutData } from 'wcstripe/express-checkout/utils'; /** @typedef {import('react')} React */ @@ -83,6 +84,13 @@ const expressCheckoutElement = ( expressPaymentMethod, api ) => { ); const edit = getEditorElement( expressPaymentMethod ); const canMakePayment = ( { cart } ) => { + if ( + parseFloat( cart.cartTotals.total_price ) === 0.0 && + ! getExpressCheckoutData( 'has_free_trial' ) + ) { + return false; + } + if ( ! getBlocksConfiguration()?.shouldShowExpressCheckoutButton ) { return false; } From 80d57e3b235f40c2970e0998c16b72559d844e02 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Thu, 11 Sep 2025 18:25:08 -0300 Subject: [PATCH 16/18] Adding the free trial parameter to missing methods --- client/entrypoints/express-checkout/index.js | 33 +++++++++++++++---- client/express-checkout/event-handler.js | 9 +++-- .../check-payment-method-availability.js | 11 ++++--- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/client/entrypoints/express-checkout/index.js b/client/entrypoints/express-checkout/index.js index 51b5bc9ef3..49d7d4a616 100644 --- a/client/entrypoints/express-checkout/index.js +++ b/client/entrypoints/express-checkout/index.js @@ -233,15 +233,25 @@ jQuery( function ( $ ) { isLinkEnabled && EXPRESS_PAYMENT_METHOD_SETTING_LINK, ].filter( Boolean ); + const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); + expressPaymentTypes.forEach( ( expressPaymentType ) => { - wcStripeECE.createExpressCheckoutElement( expressPaymentType, { - ...options, - shippingRates, - } ); + wcStripeECE.createExpressCheckoutElement( + expressPaymentType, + hasFreeTrial, + { + ...options, + shippingRates, + } + ); } ); }, - createExpressCheckoutElement: ( expressPaymentType, options ) => { + createExpressCheckoutElement: ( + expressPaymentType, + hasFreeTrial, + options + ) => { const handleProductPageECEButtonClick = async ( event, clickOptions @@ -331,11 +341,19 @@ jQuery( function ( $ ) { return; } + let mode = options.mode; + if ( ! mode ) { + mode = hasFreeTrial ? 'subscription' : 'payment'; + } + const elements = api.getStripe().elements( { - mode: options.mode ? options.mode : 'payment', + mode, amount: options.total, currency: options.currency, - ...( isManualPaymentMethodCreation( expressPaymentType ) && { + ...( isManualPaymentMethodCreation( + expressPaymentType, + hasFreeTrial + ) && { paymentMethodCreation: 'manual', } ), appearance: getExpressCheckoutButtonAppearance(), @@ -462,6 +480,7 @@ jQuery( function ( $ ) { event, order, orderDetails, + hasFreeTrial, } ); } ); diff --git a/client/express-checkout/event-handler.js b/client/express-checkout/event-handler.js index 76f9f602d6..64f2e35809 100644 --- a/client/express-checkout/event-handler.js +++ b/client/express-checkout/event-handler.js @@ -50,14 +50,19 @@ export const shippingRateChangeHandler = async ( api, event, elements ) => { }; export const onConfirmHandler = async ( params ) => { - const { abortPayment, elements, event } = params; + const { abortPayment, elements, event, hasFreeTrial } = params; const submitResponse = await elements.submit(); if ( submitResponse?.error ) { return abortPayment( event, submitResponse?.error?.message ); } - if ( ! isManualPaymentMethodCreation( event.expressPaymentType ) ) { + if ( + ! isManualPaymentMethodCreation( + event.expressPaymentType, + hasFreeTrial + ) + ) { return handleConfirmationTokenFlow( params ); } diff --git a/client/express-checkout/utils/check-payment-method-availability.js b/client/express-checkout/utils/check-payment-method-availability.js index f0d0785340..947d35978a 100644 --- a/client/express-checkout/utils/check-payment-method-availability.js +++ b/client/express-checkout/utils/check-payment-method-availability.js @@ -16,6 +16,8 @@ import { export const checkPaymentMethodIsAvailable = memoize( ( paymentMethod, api, cart ) => { return new Promise( ( resolve ) => { + const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); + // Create the DIV container on the fly const containerEl = document.createElement( 'div' ); @@ -30,10 +32,11 @@ export const checkPaymentMethodIsAvailable = memoize( Date: Fri, 12 Sep 2025 10:45:04 -0300 Subject: [PATCH 17/18] Fix shortcode checkout --- client/entrypoints/express-checkout/index.js | 42 +++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/client/entrypoints/express-checkout/index.js b/client/entrypoints/express-checkout/index.js index 49d7d4a616..6f0ec1c5df 100644 --- a/client/entrypoints/express-checkout/index.js +++ b/client/entrypoints/express-checkout/index.js @@ -233,25 +233,15 @@ jQuery( function ( $ ) { isLinkEnabled && EXPRESS_PAYMENT_METHOD_SETTING_LINK, ].filter( Boolean ); - const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); - expressPaymentTypes.forEach( ( expressPaymentType ) => { - wcStripeECE.createExpressCheckoutElement( - expressPaymentType, - hasFreeTrial, - { - ...options, - shippingRates, - } - ); + wcStripeECE.createExpressCheckoutElement( expressPaymentType, { + ...options, + shippingRates, + } ); } ); }, - createExpressCheckoutElement: ( - expressPaymentType, - hasFreeTrial, - options - ) => { + createExpressCheckoutElement: ( expressPaymentType, options ) => { const handleProductPageECEButtonClick = async ( event, clickOptions @@ -341,13 +331,10 @@ jQuery( function ( $ ) { return; } - let mode = options.mode; - if ( ! mode ) { - mode = hasFreeTrial ? 'subscription' : 'payment'; - } + const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); const elements = api.getStripe().elements( { - mode, + mode: options.mode ? options.mode : 'payment', amount: options.total, currency: options.currency, ...( isManualPaymentMethodCreation( @@ -536,8 +523,10 @@ jQuery( function ( $ ) { return; } + const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); + wcStripeECE.startExpressCheckout( { - mode: 'payment', + mode: hasFreeTrial ? 'subscription' : 'payment', total, currency: getExpressCheckoutData( 'checkout' ).currency_code, @@ -554,10 +543,12 @@ jQuery( function ( $ ) { getExpressCheckoutData( 'product' ) ?.validVariationSelected ?? true; if ( isProductSupported ) { + const hasFreeTrial = + getExpressCheckoutData( 'has_free_trial' ); const displayItems = getExpressCheckoutData( 'product' ).displayItems ?? []; wcStripeECE.startExpressCheckout( { - mode: 'payment', + mode: hasFreeTrial ? 'subscription' : 'payment', total: getExpressCheckoutData( 'product' )?.total .amount, currency: getExpressCheckoutData( 'product' )?.currency, @@ -581,13 +572,16 @@ jQuery( function ( $ ) { cart.totals ); - if ( total === 0 ) { + const hasFreeTrial = + getExpressCheckoutData( 'has_free_trial' ); + + if ( total === 0 && ! hasFreeTrial ) { wcStripeECE.hide(); return; } wcStripeECE.startExpressCheckout( { - mode: 'payment', + mode: hasFreeTrial ? 'subscription' : 'payment', total, currency: getExpressCheckoutData( 'checkout' )?.currency_code, From c9dd64e86752d554251fd39443fbb90164f86227 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Fri, 12 Sep 2025 11:54:20 -0300 Subject: [PATCH 18/18] Simplifying setting of mode parameter --- client/entrypoints/express-checkout/index.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/client/entrypoints/express-checkout/index.js b/client/entrypoints/express-checkout/index.js index 6f0ec1c5df..7666fc0b07 100644 --- a/client/entrypoints/express-checkout/index.js +++ b/client/entrypoints/express-checkout/index.js @@ -334,7 +334,7 @@ jQuery( function ( $ ) { const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); const elements = api.getStripe().elements( { - mode: options.mode ? options.mode : 'payment', + mode: hasFreeTrial ? 'subscription' : 'payment', amount: options.total, currency: options.currency, ...( isManualPaymentMethodCreation( @@ -523,10 +523,7 @@ jQuery( function ( $ ) { return; } - const hasFreeTrial = getExpressCheckoutData( 'has_free_trial' ); - wcStripeECE.startExpressCheckout( { - mode: hasFreeTrial ? 'subscription' : 'payment', total, currency: getExpressCheckoutData( 'checkout' ).currency_code, @@ -543,12 +540,9 @@ jQuery( function ( $ ) { getExpressCheckoutData( 'product' ) ?.validVariationSelected ?? true; if ( isProductSupported ) { - const hasFreeTrial = - getExpressCheckoutData( 'has_free_trial' ); const displayItems = getExpressCheckoutData( 'product' ).displayItems ?? []; wcStripeECE.startExpressCheckout( { - mode: hasFreeTrial ? 'subscription' : 'payment', total: getExpressCheckoutData( 'product' )?.total .amount, currency: getExpressCheckoutData( 'product' )?.currency, @@ -572,16 +566,15 @@ jQuery( function ( $ ) { cart.totals ); - const hasFreeTrial = - getExpressCheckoutData( 'has_free_trial' ); - - if ( total === 0 && ! hasFreeTrial ) { + if ( + total === 0 && + ! getExpressCheckoutData( 'has_free_trial' ) + ) { wcStripeECE.hide(); return; } wcStripeECE.startExpressCheckout( { - mode: hasFreeTrial ? 'subscription' : 'payment', total, currency: getExpressCheckoutData( 'checkout' )?.currency_code,