diff --git a/changelog/fix-tokenized-ece-pay-for-order-without-billing-email b/changelog/fix-tokenized-ece-pay-for-order-without-billing-email new file mode 100644 index 00000000000..6e34989b976 --- /dev/null +++ b/changelog/fix-tokenized-ece-pay-for-order-without-billing-email @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized ECE do not show button when missing billing email + + diff --git a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php index 86d1a82c54d..0613e3a4557 100644 --- a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php +++ b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php @@ -412,7 +412,7 @@ public function should_show_express_checkout_button() { // Order total doesn't matter for Pay for Order page. Thus, this page should always display payment buttons. if ( $this->is_pay_for_order_page() ) { - return true; + return $this->is_pay_for_order_supported(); } // Non-shipping product and tax is calculated based on shopper billing address. Excludes Pay for Order page. @@ -749,6 +749,38 @@ public function get_product_data() { return apply_filters( 'wcpay_payment_request_product_data', $data, $product ); } + /** + * The Store API doesn't allow checkout without the billing email address present on the order data. + * https://github.com/woocommerce/woocommerce/issues/48540 + * + * @return bool + */ + private function is_pay_for_order_supported() { + if ( ! WC_Payments_Features::is_tokenized_cart_ece_enabled() ) { + return true; + } + + $order_id = absint( get_query_var( 'order-pay' ) ); + if ( 0 === $order_id ) { + return false; + } + + $order = wc_get_order( $order_id ); + if ( ! is_a( $order, 'WC_Order' ) ) { + return false; + } + + // we don't need to check its validity or value, we just need to ensure a billing email is present. + $billing_email = $order->get_billing_email(); + if ( ! empty( $billing_email ) ) { + return true; + } + + Logger::log( 'Billing email not present ( Express Checkout Element button disabled )' ); + + return false; + } + /** * Whether product page has a supported product. * diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php index 8006faac78f..9c7ebbef971 100644 --- a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php +++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php @@ -104,6 +104,7 @@ public function tear_down() { remove_filter( 'wc_tax_enabled', '__return_false' ); remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_excl' ] ); remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); + delete_option( '_wcpay_feature_tokenized_cart_ece' ); parent::tear_down(); } @@ -208,6 +209,30 @@ function () { remove_all_filters( 'wcpay_payment_request_total_label_suffix' ); } + public function test_should_show_express_checkout_button_for_tokenized_ece_with_billing_email() { + global $wp; + global $wp_query; + + $this->mock_wcpay_account + ->method( 'is_stripe_connected' ) + ->willReturn( true ); + WC_Payments::mode()->dev(); + $_GET['pay_for_order'] = true; + + // Total is 100 USD, which is above both payment methods (Affirm and AfterPay) minimums. + $order = WC_Helper_Order::create_order( 1, 100 ); + $order_id = $order->get_id(); + $wp->query_vars = [ 'order-pay' => strval( $order_id ) ]; + $wp_query->query_vars = [ 'order-pay' => strval( $order_id ) ]; + + update_option( '_wcpay_feature_tokenized_cart_ece', '1' ); + add_filter( 'woocommerce_is_checkout', '__return_true' ); + + $this->assertTrue( $this->system_under_test->should_show_express_checkout_button() ); + + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + } + public function test_should_show_express_checkout_button_for_non_shipping_but_price_includes_tax() { $this->mock_wcpay_account ->method( 'is_stripe_connected' ) @@ -229,7 +254,6 @@ public function test_should_show_express_checkout_button_for_non_shipping_but_pr remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); } - public function test_should_not_show_express_checkout_button_for_non_shipping_but_price_does_not_include_tax() { $this->mock_wcpay_account ->method( 'is_stripe_connected' )