From 96a78891a13da4daa7f692d86ee8b356178cb328 Mon Sep 17 00:00:00 2001 From: frosso Date: Tue, 3 Jun 2025 19:31:40 +0200 Subject: [PATCH 01/11] POC: Google Pay as a payment method --- changelog/poc-google-pay-as-payment-method | 4 ++ client/checkout/blocks/index.js | 3 ++ client/express-checkout/blocks/index.js | 33 +++++++++++++++++ includes/class-wc-gateway-google-pay.php | 43 ++++++++++++++++++++++ includes/class-wc-payments.php | 3 ++ 5 files changed, 86 insertions(+) create mode 100644 changelog/poc-google-pay-as-payment-method create mode 100644 includes/class-wc-gateway-google-pay.php diff --git a/changelog/poc-google-pay-as-payment-method b/changelog/poc-google-pay-as-payment-method new file mode 100644 index 00000000000..5cc941b4c74 --- /dev/null +++ b/changelog/poc-google-pay-as-payment-method @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +POC: Google Pay as a payment method diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 418c5c1852c..ba4cdb0c412 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -21,6 +21,7 @@ import enqueueFraudScripts from 'fraud-scripts'; import { expressCheckoutElementApplePay, expressCheckoutElementGooglePay, + paymentMethodGooglePay, } from 'wcpay/express-checkout/blocks'; import { getDeferredIntentCreationUPEFields } from './payment-elements'; @@ -111,6 +112,8 @@ const addCheckoutTracking = () => { } }; +registerPaymentMethod( paymentMethodGooglePay( api ) ); + // Call handleWooPayEmailInput if woopay is enabled and this is the checkout page. if ( getUPEConfig( 'isWooPayEnabled' ) ) { if ( diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index a87428658cd..a6cee70d963 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -77,3 +77,36 @@ export const expressCheckoutElementGooglePay = ( api ) => ( { return checkPaymentMethodIsAvailable( 'googlePay', cart ); }, } ); + +const CustomWrapper = ( { children } ) =>
{ children }
; +export const paymentMethodGooglePay = ( api ) => { + return { + name: 'woocommerce_payments_googlePay', + edit: , + content: , + paymentMethodId: 'woocommerce_payments_googlePay', + placeOrderButton: ( props ) => { + return ( + + ); + }, + label: 'Google Pay - WooPayments', + ariaLabel: 'Google Pay - WooPayments', + supports: { + showSavedCards: false, + showSaveOption: false, + features: getConfig( 'features' ), + }, + canMakePayment: ( { cart } ) => { + if ( typeof wcpayExpressCheckoutParams === 'undefined' ) { + return false; + } + + return checkPaymentMethodIsAvailable( 'googlePay', cart ); + }, + }; +}; diff --git a/includes/class-wc-gateway-google-pay.php b/includes/class-wc-gateway-google-pay.php new file mode 100644 index 00000000000..9ccff6fb631 --- /dev/null +++ b/includes/class-wc-gateway-google-pay.php @@ -0,0 +1,43 @@ +id = 'woocommerce_payments_googlePay'; + $this->method_title = __( 'Google Pay', 'woocommerce-payments' ); + $this->title = __( 'Google Pay', 'woocommerce-payments' ); + $this->has_fields = false; + $this->enabled = true; + $this->description = ''; + $this->supports = [ + 'products', + 'refunds', + ]; + } + + /** + * Make this gateway always available. + * + * @return bool + */ + public function is_available() { + return true; + } +} diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 52ad2e57a40..67a808af809 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -861,6 +861,9 @@ public static function register_gateway( $gateways ) { $gateways[] = self::get_payment_gateway_by_id( $payment_method_id ); } + require_once WCPAY_ABSPATH . '/includes/class-wc-gateway-google-pay.php'; + $gateways[] = 'WC_Gateway_Google_Pay'; + return $gateways; } From 4ecbfbc6446bdae8a4a3cb6faedb1a6302fb3ec8 Mon Sep 17 00:00:00 2001 From: frosso Date: Tue, 3 Jun 2025 19:39:05 +0200 Subject: [PATCH 02/11] WIP --- .../blocks/components/express-checkout-component.js | 2 ++ .../blocks/hooks/use-express-checkout.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/client/express-checkout/blocks/components/express-checkout-component.js b/client/express-checkout/blocks/components/express-checkout-component.js index 88fcc160166..1f415cea0d3 100644 --- a/client/express-checkout/blocks/components/express-checkout-component.js +++ b/client/express-checkout/blocks/components/express-checkout-component.js @@ -81,6 +81,7 @@ const ExpressCheckoutComponent = ( { expressPaymentMethod = '', buttonAttributes, isPreview = false, + eventEmitters, } ) => { const { buttonOptions, @@ -96,6 +97,7 @@ const ExpressCheckoutComponent = ( { onClick, onClose, setExpressPaymentError, + eventEmitters, } ); const onClickHandler = ! isPreview ? onButtonClick : () => {}; const onShippingAddressChange = ( event ) => diff --git a/client/express-checkout/blocks/hooks/use-express-checkout.js b/client/express-checkout/blocks/hooks/use-express-checkout.js index a3f28340342..7a8ba43d35a 100644 --- a/client/express-checkout/blocks/hooks/use-express-checkout.js +++ b/client/express-checkout/blocks/hooks/use-express-checkout.js @@ -31,6 +31,7 @@ export const useExpressCheckout = ( { onClick, onClose, setExpressPaymentError, + eventEmitters, } ) => { const stripe = useStripe(); const elements = useElements(); @@ -54,6 +55,13 @@ export const useExpressCheckout = ( { const onButtonClick = useCallback( ( event ) => { + if ( eventEmitters && eventEmitters.onPlaceOrderButtonValidation ) { + const validationResult = eventEmitters.onPlaceOrderButtonValidation(); + if ( ! validationResult ) { + return; + } + } + // If login is required for checkout, display redirect confirmation dialog. if ( getExpressCheckoutData( 'login_confirmation' ) ) { displayLoginConfirmation( event.expressPaymentType ); @@ -133,6 +141,7 @@ export const useExpressCheckout = ( { billing.cartTotal.value, shippingData.needsShipping, shippingData.shippingRates, + eventEmitters, ] ); From f62c9e706c28c708713f92c8899e4ff4a23f19a0 Mon Sep 17 00:00:00 2001 From: frosso Date: Tue, 3 Jun 2025 19:40:32 +0200 Subject: [PATCH 03/11] WIP --- client/express-checkout/blocks/hooks/use-express-checkout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/express-checkout/blocks/hooks/use-express-checkout.js b/client/express-checkout/blocks/hooks/use-express-checkout.js index 7a8ba43d35a..1086757f9e0 100644 --- a/client/express-checkout/blocks/hooks/use-express-checkout.js +++ b/client/express-checkout/blocks/hooks/use-express-checkout.js @@ -40,7 +40,7 @@ export const useExpressCheckout = ( { const onCancel = () => { onCancelHandler(); - onClose(); + onClose?.(); }; const completePayment = ( redirectUrl ) => { @@ -130,7 +130,7 @@ export const useExpressCheckout = ( { }; // Click event from WC Blocks. - onClick(); + onClick?.(); // Global click event handler from WooPayments to ECE. onClickHandler( event ); event.resolve( options ); From 0d3ad156b9f10e4e49194dc1a1a14e2c45e897a0 Mon Sep 17 00:00:00 2001 From: frosso Date: Tue, 3 Jun 2025 19:44:45 +0200 Subject: [PATCH 04/11] WIP --- client/express-checkout/blocks/hooks/use-express-checkout.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/express-checkout/blocks/hooks/use-express-checkout.js b/client/express-checkout/blocks/hooks/use-express-checkout.js index 1086757f9e0..da2c69b37f7 100644 --- a/client/express-checkout/blocks/hooks/use-express-checkout.js +++ b/client/express-checkout/blocks/hooks/use-express-checkout.js @@ -68,7 +68,10 @@ export const useExpressCheckout = ( { return; } - const shippingAddressRequired = shippingData?.needsShipping; + const shippingAddressRequired = + eventEmitters && eventEmitters.onPlaceOrderButtonValidation + ? false + : shippingData?.needsShipping; let shippingRates; if ( shippingAddressRequired ) { From 6d4f10db0657e1805e48ec4027c07a7fe62703ed Mon Sep 17 00:00:00 2001 From: frosso Date: Tue, 3 Jun 2025 22:09:50 +0200 Subject: [PATCH 05/11] WIP --- .../components/express-checkout-component.js | 4 ++-- .../blocks/hooks/use-express-checkout.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/express-checkout/blocks/components/express-checkout-component.js b/client/express-checkout/blocks/components/express-checkout-component.js index 1f415cea0d3..e8dbe196ddf 100644 --- a/client/express-checkout/blocks/components/express-checkout-component.js +++ b/client/express-checkout/blocks/components/express-checkout-component.js @@ -81,7 +81,7 @@ const ExpressCheckoutComponent = ( { expressPaymentMethod = '', buttonAttributes, isPreview = false, - eventEmitters, + eventRegistration, } ) => { const { buttonOptions, @@ -97,7 +97,7 @@ const ExpressCheckoutComponent = ( { onClick, onClose, setExpressPaymentError, - eventEmitters, + eventRegistration, } ); const onClickHandler = ! isPreview ? onButtonClick : () => {}; const onShippingAddressChange = ( event ) => diff --git a/client/express-checkout/blocks/hooks/use-express-checkout.js b/client/express-checkout/blocks/hooks/use-express-checkout.js index da2c69b37f7..bd4fb5801bf 100644 --- a/client/express-checkout/blocks/hooks/use-express-checkout.js +++ b/client/express-checkout/blocks/hooks/use-express-checkout.js @@ -31,7 +31,7 @@ export const useExpressCheckout = ( { onClick, onClose, setExpressPaymentError, - eventEmitters, + eventRegistration, } ) => { const stripe = useStripe(); const elements = useElements(); @@ -55,8 +55,9 @@ export const useExpressCheckout = ( { const onButtonClick = useCallback( ( event ) => { - if ( eventEmitters && eventEmitters.onPlaceOrderButtonValidation ) { - const validationResult = eventEmitters.onPlaceOrderButtonValidation(); + if ( eventRegistration?.onPlaceOrderButtonValidation ) { + const validationResult = eventRegistration.onPlaceOrderButtonValidation(); + console.log( '### vr', validationResult ); if ( ! validationResult ) { return; } @@ -68,10 +69,9 @@ export const useExpressCheckout = ( { return; } - const shippingAddressRequired = - eventEmitters && eventEmitters.onPlaceOrderButtonValidation - ? false - : shippingData?.needsShipping; + const shippingAddressRequired = eventRegistration?.onPlaceOrderButtonValidation + ? false + : shippingData?.needsShipping; let shippingRates; if ( shippingAddressRequired ) { @@ -144,7 +144,7 @@ export const useExpressCheckout = ( { billing.cartTotal.value, shippingData.needsShipping, shippingData.shippingRates, - eventEmitters, + eventRegistration, ] ); From dbab9fdc37aed25fc489b6200719e779627a2d82 Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Wed, 10 Sep 2025 21:42:49 -0300 Subject: [PATCH 06/11] wip --- client/checkout/blocks/index.js | 16 ++++-- .../blocks/hooks/use-express-checkout.js | 8 ++- client/express-checkout/blocks/index.js | 50 +++++++++++++++++++ includes/class-wc-gateway-apple-pay.php | 43 ++++++++++++++++ includes/class-wc-payments-checkout.php | 1 + includes/class-wc-payments.php | 3 ++ 6 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 includes/class-wc-gateway-apple-pay.php diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index ba4cdb0c412..1daf6bbf385 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -112,6 +112,18 @@ const addCheckoutTracking = () => { } }; +// Register Apple Pay and Google Pay as payment methods when the setting is enabled +if ( getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) && getUPEConfig( 'isDynamicCheckoutPlaceOrderButtonEnabled' ) ) { + registerPaymentMethod( paymentMethodApplePay( api ) ); + registerPaymentMethod( paymentMethodGooglePay( api ) ); +} + +// Register express payment methods only when in-payment-methods is disabled +if ( getUPEConfig( 'isPaymentRequestEnabled' ) && ! getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) ) { + registerExpressPaymentMethod( expressCheckoutElementApplePay( api ) ); + registerExpressPaymentMethod( expressCheckoutElementGooglePay( api ) ); +} + registerPaymentMethod( paymentMethodGooglePay( api ) ); // Call handleWooPayEmailInput if woopay is enabled and this is the checkout page. @@ -129,10 +141,6 @@ if ( getUPEConfig( 'isWooPayEnabled' ) ) { } } -if ( getUPEConfig( 'isPaymentRequestEnabled' ) ) { - registerExpressPaymentMethod( expressCheckoutElementApplePay( api ) ); - registerExpressPaymentMethod( expressCheckoutElementGooglePay( api ) ); -} window.addEventListener( 'load', () => { enqueueFraudScripts( getUPEConfig( 'fraudServices' ) ); addCheckoutTracking(); diff --git a/client/express-checkout/blocks/hooks/use-express-checkout.js b/client/express-checkout/blocks/hooks/use-express-checkout.js index bd4fb5801bf..81cea5d565f 100644 --- a/client/express-checkout/blocks/hooks/use-express-checkout.js +++ b/client/express-checkout/blocks/hooks/use-express-checkout.js @@ -55,10 +55,15 @@ export const useExpressCheckout = ( { const onButtonClick = useCallback( ( event ) => { + // Enhanced validation with better error handling if ( eventRegistration?.onPlaceOrderButtonValidation ) { const validationResult = eventRegistration.onPlaceOrderButtonValidation(); - console.log( '### vr', validationResult ); + if ( ! validationResult ) { + // Remove console.warn and use proper error handling + setExpressPaymentError( + __( 'Please complete all required fields before proceeding.', 'woocommerce-payments' ) + ); return; } } @@ -145,6 +150,7 @@ export const useExpressCheckout = ( { shippingData.needsShipping, shippingData.shippingRates, eventRegistration, + setExpressPaymentError, ] ); diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index 54d3df51008..1b08ab87ec9 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -85,7 +85,18 @@ export const paymentMethodGooglePay = ( api ) => { edit: , content: , paymentMethodId: 'woocommerce_payments_googlePay', + // Enhanced placeOrderButton with proper props typing placeOrderButton: ( props ) => { + // Validate that we have the required props + if ( ! props || typeof props.onSubmit !== 'function' ) { + // Remove console.error and return proper error component + return ( +
+ { __( 'Error: Missing payment method interface', 'woocommerce-payments' ) } +
+ ); + } + return ( { }, }; }; + +export const paymentMethodApplePay = ( api ) => { + return { + name: 'woocommerce_payments_applePay', + edit: , + content: , + paymentMethodId: 'woocommerce_payments_applePay', + // Enhanced placeOrderButton with proper props typing + placeOrderButton: ( props ) => { + // Validate that we have the required props + if ( ! props || typeof props.onSubmit !== 'function' ) { + console.error( 'Apple Pay placeOrderButton: Missing required props' ); + return
Error: Missing payment method interface
; + } + + return ( + + ); + }, + label: 'Apple Pay - WooPayments', + ariaLabel: 'Apple Pay - WooPayments', + supports: { + showSavedCards: false, + showSaveOption: false, + features: getConfig( 'features' ), + }, + canMakePayment: ( { cart } ) => { + if ( typeof wcpayExpressCheckoutParams === 'undefined' ) { + return false; + } + + return checkPaymentMethodIsAvailable( 'applePay', cart ); + }, + }; +}; diff --git a/includes/class-wc-gateway-apple-pay.php b/includes/class-wc-gateway-apple-pay.php new file mode 100644 index 00000000000..3a71793eb53 --- /dev/null +++ b/includes/class-wc-gateway-apple-pay.php @@ -0,0 +1,43 @@ +id = 'woocommerce_payments_applePay'; + $this->method_title = __( 'Apple Pay', 'woocommerce-payments' ); + $this->title = __( 'Apple Pay', 'woocommerce-payments' ); + $this->has_fields = false; + $this->enabled = true; + $this->description = ''; + $this->supports = [ + 'products', + 'refunds', + ]; + } + + /** + * Make this gateway always available. + * + * @return bool + */ + public function is_available() { + return true; + } +} diff --git a/includes/class-wc-payments-checkout.php b/includes/class-wc-payments-checkout.php index c54c2c35956..015a1b80669 100644 --- a/includes/class-wc-payments-checkout.php +++ b/includes/class-wc-payments-checkout.php @@ -199,6 +199,7 @@ public function get_payment_fields_js_config() { 'isPreview' => is_preview(), 'isSavedCardsEnabled' => $this->gateway->is_saved_cards_enabled(), 'isPaymentRequestEnabled' => $this->gateway->is_payment_request_enabled(), + 'isAppleGooglePayInPaymentMethodsOptionsEnabled' => 'yes' === $this->gateway->get_option( 'apple_google_pay_in_payment_methods_options' ), 'isWooPayEnabled' => $this->woopay_util->should_enable_woopay( $this->gateway ) && $this->woopay_util->should_enable_woopay_on_guest_checkout(), 'isWoopayExpressCheckoutEnabled' => $this->woopay_util->is_woopay_express_checkout_enabled(), 'isWoopayFirstPartyAuthEnabled' => $this->woopay_util->is_woopay_first_party_auth_enabled(), diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 8893ef40fc1..9ce94f7170a 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -884,6 +884,9 @@ public static function register_gateway( $gateways ) { require_once WCPAY_ABSPATH . '/includes/class-wc-gateway-google-pay.php'; $gateways[] = 'WC_Gateway_Google_Pay'; + require_once WCPAY_ABSPATH . '/includes/class-wc-gateway-apple-pay.php'; + $gateways[] = 'WC_Gateway_Apple_Pay'; + return $gateways; } From b948df1aba11a810b2ca672a8ba1f0bbed6e350a Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Thu, 11 Sep 2025 15:06:36 -0300 Subject: [PATCH 07/11] removed console log --- client/checkout/blocks/index.js | 10 ++++++---- client/express-checkout/blocks/index.js | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 1daf6bbf385..074a749ca99 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -22,6 +22,7 @@ import { expressCheckoutElementApplePay, expressCheckoutElementGooglePay, paymentMethodGooglePay, + paymentMethodApplePay, } from 'wcpay/express-checkout/blocks'; import { getDeferredIntentCreationUPEFields } from './payment-elements'; @@ -113,19 +114,20 @@ const addCheckoutTracking = () => { }; // Register Apple Pay and Google Pay as payment methods when the setting is enabled -if ( getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) && getUPEConfig( 'isDynamicCheckoutPlaceOrderButtonEnabled' ) ) { +if ( getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) ) { registerPaymentMethod( paymentMethodApplePay( api ) ); registerPaymentMethod( paymentMethodGooglePay( api ) ); } // Register express payment methods only when in-payment-methods is disabled -if ( getUPEConfig( 'isPaymentRequestEnabled' ) && ! getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) ) { +if ( + getUPEConfig( 'isPaymentRequestEnabled' ) && + ! getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) +) { registerExpressPaymentMethod( expressCheckoutElementApplePay( api ) ); registerExpressPaymentMethod( expressCheckoutElementGooglePay( api ) ); } -registerPaymentMethod( paymentMethodGooglePay( api ) ); - // Call handleWooPayEmailInput if woopay is enabled and this is the checkout page. if ( getUPEConfig( 'isWooPayEnabled' ) ) { if ( diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index 1b08ab87ec9..b8a4a2f95e4 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -92,7 +92,10 @@ export const paymentMethodGooglePay = ( api ) => { // Remove console.error and return proper error component return (
- { __( 'Error: Missing payment method interface', 'woocommerce-payments' ) } + { __( + 'Error: Missing payment method interface', + 'woocommerce-payments' + ) }
); } @@ -105,8 +108,8 @@ export const paymentMethodGooglePay = ( api ) => { /> ); }, - label: 'Google Pay - WooPayments', - ariaLabel: 'Google Pay - WooPayments', + label: 'Google Pay', + ariaLabel: 'Google Pay', supports: { showSavedCards: false, showSaveOption: false, @@ -117,7 +120,7 @@ export const paymentMethodGooglePay = ( api ) => { return false; } - return checkPaymentMethodIsAvailable( 'googlePay', cart ); + return checkPaymentMethodIsAvailable( 'googlePay', cart, api ); }, }; }; @@ -132,7 +135,9 @@ export const paymentMethodApplePay = ( api ) => { placeOrderButton: ( props ) => { // Validate that we have the required props if ( ! props || typeof props.onSubmit !== 'function' ) { - console.error( 'Apple Pay placeOrderButton: Missing required props' ); + console.error( + 'Apple Pay placeOrderButton: Missing required props' + ); return
Error: Missing payment method interface
; } @@ -144,8 +149,8 @@ export const paymentMethodApplePay = ( api ) => { /> ); }, - label: 'Apple Pay - WooPayments', - ariaLabel: 'Apple Pay - WooPayments', + label: 'Apple Pay', + ariaLabel: 'Apple Pay', supports: { showSavedCards: false, showSaveOption: false, @@ -156,7 +161,7 @@ export const paymentMethodApplePay = ( api ) => { return false; } - return checkPaymentMethodIsAvailable( 'applePay', cart ); + return checkPaymentMethodIsAvailable( 'applePay', cart, api ); }, }; }; From d674b40c08608a0015b927ca665dd847d005c133 Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Thu, 11 Sep 2025 17:38:01 -0300 Subject: [PATCH 08/11] wip --- client/checkout/blocks/index.js | 8 ++++---- client/express-checkout/blocks/index.js | 8 -------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 074a749ca99..3c198cd9314 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -115,15 +115,15 @@ const addCheckoutTracking = () => { // Register Apple Pay and Google Pay as payment methods when the setting is enabled if ( getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) ) { + console.log( + 'Register Apple Pay and Google Pay as payment methods when the setting is enabled' + ); registerPaymentMethod( paymentMethodApplePay( api ) ); registerPaymentMethod( paymentMethodGooglePay( api ) ); } // Register express payment methods only when in-payment-methods is disabled -if ( - getUPEConfig( 'isPaymentRequestEnabled' ) && - ! getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) -) { +if ( getUPEConfig( 'isPaymentRequestEnabled' ) ) { registerExpressPaymentMethod( expressCheckoutElementApplePay( api ) ); registerExpressPaymentMethod( expressCheckoutElementGooglePay( api ) ); } diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index b8a4a2f95e4..52fbc33e776 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -116,10 +116,6 @@ export const paymentMethodGooglePay = ( api ) => { features: getConfig( 'features' ), }, canMakePayment: ( { cart } ) => { - if ( typeof wcpayExpressCheckoutParams === 'undefined' ) { - return false; - } - return checkPaymentMethodIsAvailable( 'googlePay', cart, api ); }, }; @@ -157,10 +153,6 @@ export const paymentMethodApplePay = ( api ) => { features: getConfig( 'features' ), }, canMakePayment: ( { cart } ) => { - if ( typeof wcpayExpressCheckoutParams === 'undefined' ) { - return false; - } - return checkPaymentMethodIsAvailable( 'applePay', cart, api ); }, }; From 6d9a6fab89f5bc9ed2860646bb45820a2bca3716 Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Thu, 11 Sep 2025 18:31:14 -0300 Subject: [PATCH 09/11] wip --- client/checkout/blocks/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 3c198cd9314..1ca34ed0ee1 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -115,9 +115,6 @@ const addCheckoutTracking = () => { // Register Apple Pay and Google Pay as payment methods when the setting is enabled if ( getUPEConfig( 'isAppleGooglePayInPaymentMethodsOptionsEnabled' ) ) { - console.log( - 'Register Apple Pay and Google Pay as payment methods when the setting is enabled' - ); registerPaymentMethod( paymentMethodApplePay( api ) ); registerPaymentMethod( paymentMethodGooglePay( api ) ); } From d9c9383af8182d3604b95803b4b578a94c2e8ce3 Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Thu, 11 Sep 2025 18:43:55 -0300 Subject: [PATCH 10/11] wip --- .../checkout/blocks/payment-method-label.js | 67 ++++++++++++++----- client/express-checkout/blocks/index.js | 23 ++++++- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js index 8a527e51450..b3a137140f6 100644 --- a/client/checkout/blocks/payment-method-label.js +++ b/client/checkout/blocks/payment-method-label.js @@ -6,6 +6,8 @@ import Visa from 'assets/images/payment-method-icons/visa.svg?asset'; import Mastercard from 'assets/images/payment-method-icons/mastercard.svg?asset'; import Amex from 'assets/images/payment-method-icons/amex.svg?asset'; import Discover from 'assets/images/payment-method-icons/discover.svg?asset'; +import ApplePay from 'assets/images/payment-method-icons/applepay.svg?asset'; +import GooglePay from 'assets/images/payment-method-icons/gpay.svg?asset'; import { useStripeForUPE } from 'wcpay/hooks/use-stripe-async'; import { getUPEConfig } from 'wcpay/utils/checkout'; import { __ } from '@wordpress/i18n'; @@ -33,6 +35,10 @@ const paymentMethods = [ // TODO: Missing Diners Club // TODO: What other card payment methods should be here? ]; + +const applePayLogos = [ { name: 'applepay', component: ApplePay } ]; + +const googlePayLogos = [ { name: 'googlepay', component: GooglePay } ]; const breakpointConfigs = [ { breakpoint: 550, maxElements: 2 }, { breakpoint: 330, maxElements: 1 }, @@ -75,7 +81,11 @@ export default ( { api, title, iconLight, iconDark, upeName } ) => { const stripe = useStripeForUPE( api, upeName ); - if ( ! stripe ) { + if ( + ! stripe && + upeName !== 'woocommerce_payments_applePay' && + upeName !== 'woocommerce_payments_googlePay' + ) { return null; } @@ -87,21 +97,46 @@ export default ( { api, title, iconLight, iconDark, upeName } ) => { { __( 'Test Mode', 'woocommerce-payments' ) } ) } - { upeName === 'card' ? ( - - ) : ( - { - ) } + { ( () => { + if ( upeName === 'card' ) { + return ( + + ); + } + if ( upeName === 'woocommerce_payments_applePay' ) { + return ( + + ); + } + if ( upeName === 'woocommerce_payments_googlePay' ) { + return ( + + ); + } + return ( + { + ); + } )() } ); }; diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index 52fbc33e776..e3139b89c54 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -10,6 +10,9 @@ import { PAYMENT_METHOD_NAME_EXPRESS_CHECKOUT_ELEMENT } from 'wcpay/checkout/con import { getConfig } from 'wcpay/utils/checkout'; import ExpressCheckoutContainer from './components/express-checkout-container'; import { checkPaymentMethodIsAvailable } from '../utils/checkPaymentMethodIsAvailable'; +import PaymentMethodLabel from '../../checkout/blocks/payment-method-label'; +import ApplePay from 'assets/images/payment-method-icons/applepay.svg?asset'; +import GooglePay from 'assets/images/payment-method-icons/gpay.svg?asset'; export const expressCheckoutElementApplePay = ( api ) => ( { paymentMethodId: PAYMENT_METHOD_NAME_EXPRESS_CHECKOUT_ELEMENT, @@ -108,7 +111,15 @@ export const paymentMethodGooglePay = ( api ) => { /> ); }, - label: 'Google Pay', + label: ( + + ), ariaLabel: 'Google Pay', supports: { showSavedCards: false, @@ -145,7 +156,15 @@ export const paymentMethodApplePay = ( api ) => { /> ); }, - label: 'Apple Pay', + label: ( + + ), ariaLabel: 'Apple Pay', supports: { showSavedCards: false, From f59934a2f6a9f7ef57ce88b54901bcb7c1ae4ddd Mon Sep 17 00:00:00 2001 From: Rafael Meneses Date: Thu, 11 Sep 2025 18:59:52 -0300 Subject: [PATCH 11/11] wip --- client/checkout/classic/event-handlers.js | 100 ++++++++++++++++++++-- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js index f17a10b7d75..ec5671e5275 100644 --- a/client/checkout/classic/event-handlers.js +++ b/client/checkout/classic/event-handlers.js @@ -36,6 +36,8 @@ import Visa from 'assets/images/payment-method-icons/visa.svg?asset'; import Mastercard from 'assets/images/payment-method-icons/mastercard.svg?asset'; import Amex from 'assets/images/payment-method-icons/amex.svg?asset'; import Discover from 'assets/images/payment-method-icons/discover.svg?asset'; +import ApplePay from 'assets/images/payment-method-icons/applepay.svg?asset'; +import GooglePay from 'assets/images/payment-method-icons/gpay.svg?asset'; jQuery( function ( $ ) { enqueueFraudScripts( getUPEConfig( 'fraudServices' ) ); @@ -188,6 +190,90 @@ jQuery( function ( $ ) { { name: 'discover', component: Discover }, ]; + injectLogosForPaymentMethod( + cardLabel, + target, + logosContainer, + innerContainer, + paymentMethods + ); + + // Handle Apple Pay payment method + const applePayLabel = document.querySelector( + 'label[for="payment_method_woocommerce_payments_applePay"]' + ); + if ( ! applePayLabel ) return; + + if ( applePayLabel.querySelector( '.payment-methods--logos' ) ) return; + + const applePayTarget = applePayLabel.querySelector( 'img' ); + if ( ! applePayTarget ) return; + + // Create container div + const applePayLogosContainer = document.createElement( 'div' ); + applePayLogosContainer.className = 'payment-methods--logos'; + + // Create inner div for flex layout + const applePayInnerContainer = document.createElement( 'div' ); + applePayInnerContainer.setAttribute( 'role', 'button' ); + applePayInnerContainer.setAttribute( 'tabindex', '0' ); + applePayInnerContainer.setAttribute( + 'data-testid', + 'payment-methods-logos' + ); + + const applePayLogos = [ { name: 'applepay', component: ApplePay } ]; + + injectLogosForPaymentMethod( + applePayLabel, + applePayTarget, + applePayLogosContainer, + applePayInnerContainer, + applePayLogos + ); + + // Handle Google Pay payment method + const googlePayLabel = document.querySelector( + 'label[for="payment_method_woocommerce_payments_googlePay"]' + ); + if ( ! googlePayLabel ) return; + + if ( googlePayLabel.querySelector( '.payment-methods--logos' ) ) return; + + const googlePayTarget = googlePayLabel.querySelector( 'img' ); + if ( ! googlePayTarget ) return; + + // Create container div + const googlePayLogosContainer = document.createElement( 'div' ); + googlePayLogosContainer.className = 'payment-methods--logos'; + + // Create inner div for flex layout + const googlePayInnerContainer = document.createElement( 'div' ); + googlePayInnerContainer.setAttribute( 'role', 'button' ); + googlePayInnerContainer.setAttribute( 'tabindex', '0' ); + googlePayInnerContainer.setAttribute( + 'data-testid', + 'payment-methods-logos' + ); + + const googlePayLogos = [ { name: 'googlepay', component: GooglePay } ]; + + injectLogosForPaymentMethod( + googlePayLabel, + googlePayTarget, + googlePayLogosContainer, + googlePayInnerContainer, + googlePayLogos + ); + } + + function injectLogosForPaymentMethod( + label, + target, + logosContainer, + innerContainer, + paymentMethods + ) { function getMaxElements() { const paymentMethodElement = document.querySelector( '.payment_method_woocommerce_payments' @@ -240,11 +326,11 @@ jQuery( function ( $ ) { } function positionPopover( popover, anchor ) { - const label = anchor.closest( 'label' ); - if ( ! label ) return; + const labelElement = anchor.closest( 'label' ); + if ( ! labelElement ) return; - const labelRect = label.getBoundingClientRect(); - const labelStyle = window.getComputedStyle( label ); + const labelRect = labelElement.getBoundingClientRect(); + const labelStyle = window.getComputedStyle( labelElement ); const labelPaddingRight = parseInt( labelStyle.paddingRight, 10 ); popover.style.position = 'fixed'; @@ -289,7 +375,7 @@ jQuery( function ( $ ) { } // Remove existing popover if we no longer need it - const existingPopover = cardLabel.querySelector( '.logo-popover' ); + const existingPopover = label.querySelector( '.logo-popover' ); if ( existingPopover && ! shouldHavePopover() ) { existingPopover.remove(); } @@ -299,7 +385,7 @@ jQuery( function ( $ ) { const popover = createPopover( paymentMethods.slice( getMaxElements() ) ); - cardLabel.appendChild( popover ); + label.appendChild( popover ); positionPopover( popover, innerContainer ); const handleResize = () => @@ -348,7 +434,7 @@ jQuery( function ( $ ) { function togglePopover() { if ( ! shouldHavePopover() ) return; - const existingPopover = cardLabel.querySelector( '.logo-popover' ); + const existingPopover = label.querySelector( '.logo-popover' ); if ( existingPopover ) { existingPopover.remove(); return;