From 0038922fe84bf7e881db5c430b170cacc1c2536f Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 9 Jul 2024 15:40:16 +0400 Subject: [PATCH 01/72] Add filter for the total --- modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 625157a05..2a7e4fe82 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -131,6 +131,7 @@ class WooCommerceOrderCreator { } $total = $product->get_price() * $quantity; + $total = apply_filters( 'woocommerce_paypal_payments_shipping_callback_cart_line_item_total', $total, $cart_item ); $item->set_name( $product->get_name() ); $item->set_subtotal( $total ); From fd56d758c57faee57f0cd52a7e3c93081c9cf8ed Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 9 Jul 2024 15:40:50 +0400 Subject: [PATCH 02/72] Add service to check if NYP is active --- modules/ppcp-compat/services.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index 70ae6dac2..d27091162 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -83,6 +83,9 @@ return array( 'compat.wc_shipping_tax.is_supported_plugin_version_active' => function (): bool { return class_exists( 'WC_Connect_Loader' ); }, + 'compat.nyp.is_supported_plugin_version_active' => function (): bool { + return function_exists( 'wc_nyp_init' ); + }, 'compat.module.url' => static function ( ContainerInterface $container ): string { /** From 6599686329e67c7ab8388f12252e1c561fb2d932 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 9 Jul 2024 15:41:14 +0400 Subject: [PATCH 03/72] Add NYP compat layer --- modules/ppcp-compat/src/CompatModule.php | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index a2d0a3bad..aa6829cc0 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -56,6 +56,11 @@ class CompatModule implements ModuleInterface { $this->fix_page_builders(); $this->exclude_cache_plugins_js_minification( $c ); $this->set_elementor_checkout_context(); + + $is_nyp_active = $c->get( 'compat.nyp.is_supported_plugin_version_active' ); + if ( $is_nyp_active ) { + $this->initialize_nyp_compat_layer(); + } } /** @@ -378,4 +383,24 @@ class CompatModule implements ModuleInterface { 3 ); } + + /** + * Sets up the compatibility layer for PayPal Shipping callback & WooCommerce Name Your Price plugin. + * + * @return void + */ + protected function initialize_nyp_compat_layer(): void { + add_filter( + 'woocommerce_paypal_payments_shipping_callback_cart_line_item_total', + static function( $total, $cart_item ) { + if ( ! isset( $cart_item['nyp'] ) ) { + return $total; + } + + return $cart_item['nyp']; + }, + 10, + 2 + ); + } } From b350a20e345d1aa9325eea60f58b11fba974cb1e Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 9 Jul 2024 17:05:05 +0400 Subject: [PATCH 04/72] Fix the PSALM --- modules/ppcp-compat/src/CompatModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index aa6829cc0..32a8ff4cc 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -392,7 +392,7 @@ class CompatModule implements ModuleInterface { protected function initialize_nyp_compat_layer(): void { add_filter( 'woocommerce_paypal_payments_shipping_callback_cart_line_item_total', - static function( $total, $cart_item ) { + static function( string $total, array $cart_item ): string { if ( ! isset( $cart_item['nyp'] ) ) { return $total; } From 49faab654a2eddce4a909ce8740089a69eaea475 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 10 Jul 2024 09:23:04 +0400 Subject: [PATCH 05/72] Prevent enabling Standard Card Button when ACDC is enabled --- .../resources/js/gateway-settings.js | 9 ++--- .../GatewayWithoutPayPalAdminNotice.php | 36 ++++++++++++++++--- .../ppcp-wc-gateway/src/WCGatewayModule.php | 8 +++++ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js index eee948657..9257bd86d 100644 --- a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js +++ b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js @@ -125,10 +125,6 @@ document.addEventListener( } function shouldDisableCardButton() { - if (currentTabId() === 'ppcp-card-button-gateway') { - return false; - } - return PayPalCommerceGatewaySettings.is_acdc_enabled || jQuery('#ppcp-allow_card_button_gateway').is(':checked'); } @@ -157,6 +153,11 @@ document.addEventListener( } if (shouldDisableCardButton()) { + const standardCardButtonInput = document.querySelector('#woocommerce_ppcp-card-button-gateway_enabled'); + if (standardCardButtonInput) { + standardCardButtonInput.disabled = true; + } + disabledSources = disabledSources.concat('card'); } diff --git a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php index 81b057423..051b38178 100644 --- a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php @@ -22,6 +22,7 @@ class GatewayWithoutPayPalAdminNotice { private const NOTICE_OK = ''; private const NOTICE_DISABLED_GATEWAY = 'disabled_gateway'; private const NOTICE_DISABLED_LOCATION = 'disabled_location'; + private const NOTICE_DISABLED_CARD_BUTTON = 'disabled_card'; /** * The gateway ID. @@ -114,6 +115,15 @@ class GatewayWithoutPayPalAdminNotice { 'woocommerce-paypal-payments' ); break; + case self::NOTICE_DISABLED_CARD_BUTTON: + /* translators: %1$s Standard Card Button section URL, %2$s Advanced Card Processing section URL. */ + $text = __( + 'The Standard Card Button cannot be used while Advanced Card Processing is enabled.', + 'woocommerce-paypal-payments' + ); + $url1 = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-card-button-gateway' ); + $url2 = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-credit-card-gateway' ); + break; default: return null; } @@ -125,11 +135,20 @@ class GatewayWithoutPayPalAdminNotice { $name = $gateway->get_method_title(); - $message = sprintf( - $text, - $name, - admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ) - ); + if ( $notice_type === self::NOTICE_DISABLED_CARD_BUTTON ) { + $message = sprintf( + $text, + $url1, + $url2 + ); + } else { + $message = sprintf( + $text, + $name, + admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ) + ); + } + return new Message( $message, 'warning' ); } @@ -160,6 +179,13 @@ class GatewayWithoutPayPalAdminNotice { return self::NOTICE_DISABLED_LOCATION; } + $is_dcc_enabled = $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) ?? false; + $is_card_button_allowed = $this->settings->has( 'allow_card_button_gateway' ) && $this->settings->get( 'allow_card_button_gateway' ); + + if ( $is_dcc_enabled && $is_card_button_allowed ) { + return self::NOTICE_DISABLED_CARD_BUTTON; + } + return self::NOTICE_OK; } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 76de0478c..0b1315376 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -528,6 +528,14 @@ class WCGatewayModule implements ModuleInterface { return $methods; } + $is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ) ?? false; + $standard_card_button = get_option( 'woocommerce_ppcp-card-button-gateway_settings' ); + + if ( $is_dcc_enabled && isset( $standard_card_button['enabled'] ) ) { + $standard_card_button['enabled'] = 'no'; + update_option( 'woocommerce_ppcp-card-button-gateway_settings', $standard_card_button ); + } + $dcc_applies = $container->get( 'api.helpers.dccapplies' ); assert( $dcc_applies instanceof DccApplies ); From 5123a1511c1e53d6bc2647f354082f2b049da044 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 10 Jul 2024 10:22:14 +0400 Subject: [PATCH 06/72] Add notice when upgrading to plugin version >= 3.0.0 --- woocommerce-paypal-payments.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index 32ea572df..dafcaf9a1 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -222,6 +222,22 @@ define( 'PAYPAL_INTEGRATION_DATE', '2024-06-25' ); } ); + add_action( + 'in_plugin_update_message-woocommerce-paypal-payments/woocommerce-paypal-payments.php', + static function( array $plugin_data, \stdClass $new_data ) { + if ( version_compare( $plugin_data['Version'], '3.0.0', '<' ) && + version_compare( $new_data->new_version, '3.0.0', '>=' ) ) { + printf( + '

%s: %s

', + esc_html__( 'Warning', 'woocommerce-paypal-payments' ), + esc_html__( 'WooCommerce PayPal Payments version 3.0.0 contains significant changes that may impact your website. We strongly recommend reviewing the changes and testing the update on a staging site before updating it on your production environment.', 'woocommerce-paypal-payments' ) + ); + } + }, + 10, + 2 + ); + /** * Check if WooCommerce is active. * From 8a37f10883343c1a9bd7ccd865ecefe00b0c7834 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 10 Jul 2024 13:59:00 +0400 Subject: [PATCH 07/72] Update contributor account inpsyde => syde --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index c1d1b0504..938721db9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ === WooCommerce PayPal Payments === -Contributors: woocommerce, automattic, inpsyde +Contributors: woocommerce, automattic, syde Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple pay, subscriptions, debit card, credit card, google pay Requires at least: 5.3 Tested up to: 6.5 From 300804a738d3438f77e9f959c4909ffc8cc503b6 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 10 Jul 2024 14:30:09 +0400 Subject: [PATCH 08/72] Fix psalm error --- .../src/Notice/GatewayWithoutPayPalAdminNotice.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php index 051b38178..77185e6d1 100644 --- a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php @@ -100,6 +100,9 @@ class GatewayWithoutPayPalAdminNotice { public function message(): ?Message { $notice_type = $this->check(); + $url1 = ''; + $url2 = ''; + switch ( $notice_type ) { case self::NOTICE_DISABLED_GATEWAY: /* translators: %1$s the gateway name, %2$s URL. */ From 59953ae58c97ed7215c0f6802f0b0bcae1d4a979 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 10 Jul 2024 18:08:46 +0400 Subject: [PATCH 09/72] Remove redundant else condition --- .../Notice/GatewayWithoutPayPalAdminNotice.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php index 77185e6d1..2dfc88904 100644 --- a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php @@ -19,9 +19,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; * Creates the admin message about the gateway being enabled without the PayPal gateway. */ class GatewayWithoutPayPalAdminNotice { - private const NOTICE_OK = ''; - private const NOTICE_DISABLED_GATEWAY = 'disabled_gateway'; - private const NOTICE_DISABLED_LOCATION = 'disabled_location'; + private const NOTICE_OK = ''; + private const NOTICE_DISABLED_GATEWAY = 'disabled_gateway'; + private const NOTICE_DISABLED_LOCATION = 'disabled_location'; private const NOTICE_DISABLED_CARD_BUTTON = 'disabled_card'; /** @@ -138,18 +138,18 @@ class GatewayWithoutPayPalAdminNotice { $name = $gateway->get_method_title(); + $message = sprintf( + $text, + $name, + admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ) + ); + if ( $notice_type === self::NOTICE_DISABLED_CARD_BUTTON ) { $message = sprintf( $text, $url1, $url2 ); - } else { - $message = sprintf( - $text, - $name, - admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ) - ); } return new Message( $message, 'warning' ); From 484d026cced1675c36b02894b8c727c89504b468 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 10 Jul 2024 19:48:40 +0400 Subject: [PATCH 10/72] Remove the return type to avoid potential errors --- modules/ppcp-compat/src/CompatModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 32a8ff4cc..e06f4c518 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -392,7 +392,7 @@ class CompatModule implements ModuleInterface { protected function initialize_nyp_compat_layer(): void { add_filter( 'woocommerce_paypal_payments_shipping_callback_cart_line_item_total', - static function( string $total, array $cart_item ): string { + static function( string $total, array $cart_item ) { if ( ! isset( $cart_item['nyp'] ) ) { return $total; } From 56eaf73a29f0cbe5ef2ea37395a102c904d7cc9c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 11 Jul 2024 15:52:40 +0200 Subject: [PATCH 11/72] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Optimize=20loading?= =?UTF-8?q?=20of=20plugin=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Assets/OnboardingAssets.php | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php b/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php index 4b0c9c165..c5385216d 100644 --- a/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php +++ b/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php @@ -96,21 +96,21 @@ class OnboardingAssets { */ public function register(): bool { - $url = untrailingslashit( $this->module_url ) . '/assets/css/onboarding.css'; wp_register_style( 'ppcp-onboarding', - $url, + $this->module_url . '/assets/css/onboarding.css', array(), $this->version ); - $url = untrailingslashit( $this->module_url ) . '/assets/js/settings.js'; + wp_register_script( 'ppcp-settings', - $url, + $this->module_url . '/assets/js/settings.js', array(), $this->version, true ); + wp_localize_script( 'ppcp-settings', 'PayPalCommerceSettings', @@ -122,14 +122,14 @@ class OnboardingAssets { ) ); - $url = untrailingslashit( $this->module_url ) . '/assets/js/onboarding.js'; wp_register_script( 'ppcp-onboarding', - $url, + $this->module_url . '/assets/js/onboarding.js', array( 'jquery' ), $this->version, true ); + wp_localize_script( 'ppcp-onboarding', 'PayPalCommerceGatewayOnboarding', @@ -164,17 +164,22 @@ class OnboardingAssets { /** * Enqueues the necessary scripts. * - * @return bool + * @return void */ - public function enqueue(): bool { - wp_enqueue_style( 'ppcp-onboarding' ); - wp_enqueue_script( 'ppcp-settings' ); - if ( ! $this->should_render_onboarding_script() ) { - return false; + public function enqueue(): void { + // Do not enqueue anything when we are not on a PayPal Payments settings tab. + if ( ! $this->page_id ) { + return; } - wp_enqueue_script( 'ppcp-onboarding' ); - return true; + // Enqueue general assets for the plugin's settings page. + wp_enqueue_script( 'ppcp-settings' ); + wp_enqueue_style( 'ppcp-onboarding' ); // File also contains general settings styles. + + // Conditionally enqueue the onboarding script, when needed. + if ( $this->should_render_onboarding_script() ) { + wp_enqueue_script( 'ppcp-onboarding' ); + } } /** From 4b8843d93d64606b57d8492d83548fa83f7c81c9 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 12 Jul 2024 13:49:50 +0200 Subject: [PATCH 12/72] Google Pay: Fix the incorrect popup triggering (2645) --- .../resources/js/GooglepayButton.js | 89 +++++++++---------- .../resources/js/GooglepayManager.js | 57 ++++++++++-- .../ppcp-googlepay/resources/js/boot-admin.js | 6 +- 3 files changed, 96 insertions(+), 56 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index b3ca0a935..e94a49e1a 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -1,4 +1,3 @@ -import ContextHandlerFactory from "./Context/ContextHandlerFactory"; import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder"; @@ -7,7 +6,7 @@ import {apmButtonsInit} from "../../../ppcp-button/resources/js/modules/Helper/A class GooglepayButton { - constructor(context, externalHandler, buttonConfig, ppcpConfig) { + constructor(context, externalHandler, buttonConfig, ppcpConfig, contextHandler) { apmButtonsInit(ppcpConfig); this.isInitialized = false; @@ -16,24 +15,18 @@ class GooglepayButton { this.externalHandler = externalHandler; this.buttonConfig = buttonConfig; this.ppcpConfig = ppcpConfig; + this.contextHandler = contextHandler; this.paymentsClient = null; - this.contextHandler = ContextHandlerFactory.create( - this.context, - this.buttonConfig, - this.ppcpConfig, - this.externalHandler - ); - this.log = function() { - if ( this.buttonConfig.is_debug ) { + if (this.buttonConfig.is_debug) { //console.log('[GooglePayButton]', ...arguments); } } } - init(config) { + init(config, transactionInfo) { if (this.isInitialized) { return; } @@ -48,6 +41,7 @@ class GooglepayButton { } this.googlePayConfig = config; + this.transactionInfo = transactionInfo; this.allowedPaymentMethods = config.allowedPaymentMethods; this.baseCardPaymentMethod = this.allowedPaymentMethods[0]; @@ -73,16 +67,16 @@ class GooglepayButton { } this.isInitialized = false; - this.init(this.googlePayConfig); + this.init(this.googlePayConfig, this.transactionInfo); } validateConfig() { - if ( ['PRODUCTION', 'TEST'].indexOf(this.buttonConfig.environment) === -1) { + if (['PRODUCTION', 'TEST'].indexOf(this.buttonConfig.environment) === -1) { console.error('[GooglePayButton] Invalid environment.', this.buttonConfig.environment); return false; } - if ( !this.contextHandler ) { + if (!this.contextHandler) { console.error('[GooglePayButton] Invalid context handler.', this.contextHandler); return false; } @@ -125,7 +119,7 @@ class GooglepayButton { onPaymentAuthorized: this.onPaymentAuthorized.bind(this) } - if ( this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed() ) { + if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) { callbacks['onPaymentDataChanged'] = this.onPaymentDataChanged.bind(this); } @@ -218,7 +212,7 @@ class GooglepayButton { async onButtonClick() { this.log('onButtonClick', this.context); - const paymentDataRequest = await this.paymentDataRequest(); + const paymentDataRequest = this.paymentDataRequest(); this.log('onButtonClick: paymentDataRequest', paymentDataRequest, this.context); window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler. @@ -226,7 +220,7 @@ class GooglepayButton { this.paymentsClient.loadPaymentData(paymentDataRequest); } - async paymentDataRequest() { + paymentDataRequest() { let baseRequest = { apiVersion: 2, apiVersionMinor: 0 @@ -235,11 +229,11 @@ class GooglepayButton { const googlePayConfig = this.googlePayConfig; const paymentDataRequest = Object.assign({}, baseRequest); paymentDataRequest.allowedPaymentMethods = googlePayConfig.allowedPaymentMethods; - paymentDataRequest.transactionInfo = await this.contextHandler.transactionInfo(); + paymentDataRequest.transactionInfo = this.transactionInfo; paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo; - if ( this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed() ) { - paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS", "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"]; + if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) { + paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS", "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"]; paymentDataRequest.shippingAddressRequired = true; paymentDataRequest.shippingAddressParameters = this.shippingAddressParameters(); paymentDataRequest.shippingOptionRequired = true; @@ -266,37 +260,42 @@ class GooglepayButton { this.log('paymentData', paymentData); return new Promise(async (resolve, reject) => { - let paymentDataRequestUpdate = {}; + try { + let paymentDataRequestUpdate = {}; - const updatedData = await (new UpdatePaymentData(this.buttonConfig.ajax.update_payment_data)).update(paymentData); - const transactionInfo = await this.contextHandler.transactionInfo(); + const updatedData = await (new UpdatePaymentData(this.buttonConfig.ajax.update_payment_data)).update(paymentData); + const transactionInfo = this.transactionInfo; - this.log('onPaymentDataChanged:updatedData', updatedData); - this.log('onPaymentDataChanged:transactionInfo', transactionInfo); + this.log('onPaymentDataChanged:updatedData', updatedData); + this.log('onPaymentDataChanged:transactionInfo', transactionInfo); - updatedData.country_code = transactionInfo.countryCode; - updatedData.currency_code = transactionInfo.currencyCode; - updatedData.total_str = transactionInfo.totalPrice; + updatedData.country_code = transactionInfo.countryCode; + updatedData.currency_code = transactionInfo.currencyCode; + updatedData.total_str = transactionInfo.totalPrice; + + // Handle unserviceable address. + if (!(updatedData.shipping_options?.shippingOptions?.length)) { + paymentDataRequestUpdate.error = this.unserviceableShippingAddressError(); + resolve(paymentDataRequestUpdate); + return; + } + + switch (paymentData.callbackTrigger) { + case 'INITIALIZE': + case 'SHIPPING_ADDRESS': + paymentDataRequestUpdate.newShippingOptionParameters = updatedData.shipping_options; + paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); + break; + case 'SHIPPING_OPTION': + paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); + break; + } - // Handle unserviceable address. - if (!(updatedData.shipping_options?.shippingOptions?.length)) { - paymentDataRequestUpdate.error = this.unserviceableShippingAddressError(); resolve(paymentDataRequestUpdate); - return; + } catch(error) { + console.error('Error during onPaymentDataChanged:', error); + reject(error); } - - switch (paymentData.callbackTrigger) { - case 'INITIALIZE': - case 'SHIPPING_ADDRESS': - paymentDataRequestUpdate.newShippingOptionParameters = updatedData.shipping_options; - paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); - break; - case 'SHIPPING_OPTION': - paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); - break; - } - - resolve(paymentDataRequestUpdate); }); } diff --git a/modules/ppcp-googlepay/resources/js/GooglepayManager.js b/modules/ppcp-googlepay/resources/js/GooglepayManager.js index 72475cfe5..e03a61ed8 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayManager.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayManager.js @@ -1,5 +1,6 @@ import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher"; import GooglepayButton from "./GooglepayButton"; +import ContextHandlerFactory from "./Context/ContextHandlerFactory"; class GooglepayManager { @@ -8,34 +9,72 @@ class GooglepayManager { this.buttonConfig = buttonConfig; this.ppcpConfig = ppcpConfig; this.googlePayConfig = null; + this.transactionInfo = null; + this.contextHandler = null; this.buttons = []; - buttonModuleWatcher.watchContextBootstrap((bootstrap) => { + buttonModuleWatcher.watchContextBootstrap(async (bootstrap) => { + if (!this.contextHandler) { + this.contextHandler = ContextHandlerFactory.create( + bootstrap.context, + buttonConfig, + ppcpConfig, + bootstrap.handler + ); + } + const button = new GooglepayButton( bootstrap.context, bootstrap.handler, buttonConfig, ppcpConfig, + this.contextHandler ); this.buttons.push(button); - if (this.googlePayConfig) { - button.init(this.googlePayConfig); + // Initialize button only if googlePayConfig and transactionInfo are already fetched. + if (this.googlePayConfig && this.transactionInfo) { + button.init(this.googlePayConfig, this.transactionInfo); + } else { + await this.init(); + if (this.googlePayConfig && this.transactionInfo) { + button.init(this.googlePayConfig, this.transactionInfo); + } } }); } - init() { - (async () => { - // Gets GooglePay configuration of the PayPal merchant. - this.googlePayConfig = await paypal.Googlepay().config(); + async init() { + try { + if (!this.googlePayConfig) { + // Gets GooglePay configuration of the PayPal merchant. + this.googlePayConfig = await paypal.Googlepay().config(); + } + + if (!this.transactionInfo) { + this.transactionInfo = await this.fetchTransactionInfo(); + } for (const button of this.buttons) { - button.init(this.googlePayConfig); + button.init(this.googlePayConfig, this.transactionInfo); } - })(); + } catch(error) { + console.error('Error during initialization:', error); + } + } + + async fetchTransactionInfo() { + try { + if (!this.contextHandler) { + throw new Error('ContextHandler is not initialized'); + } + return await this.contextHandler.transactionInfo(); + } catch(error) { + console.error('Error fetching transaction info:', error); + throw error; + } } reinit() { diff --git a/modules/ppcp-googlepay/resources/js/boot-admin.js b/modules/ppcp-googlepay/resources/js/boot-admin.js index 3e561e6e4..62fda993e 100644 --- a/modules/ppcp-googlepay/resources/js/boot-admin.js +++ b/modules/ppcp-googlepay/resources/js/boot-admin.js @@ -1,6 +1,7 @@ import GooglepayButton from './GooglepayButton'; import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton'; import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager'; +import ContextHandlerFactory from './Context/ContextHandlerFactory'; /** * Accessor that creates and returns a single PreviewButtonManager instance. @@ -94,9 +95,10 @@ class GooglePayPreviewButton extends PreviewButton { } createButton(buttonConfig) { - const button = new GooglepayButton('preview', null, buttonConfig, this.ppcpConfig); + const contextHandler = ContextHandlerFactory.create('preview', buttonConfig, this.ppcpConfig, null); + const button = new GooglepayButton('preview', null, buttonConfig, this.ppcpConfig, contextHandler); - button.init(this.apiConfig); + button.init(this.apiConfig, contextHandler.transactionInfo()); } /** From d7e58d6f6f8f175ecdd646d7fb3cb855d831ca89 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 15 Jul 2024 11:21:56 +0400 Subject: [PATCH 13/72] Fixed notice formatting --- woocommerce-paypal-payments.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index dafcaf9a1..8394055a2 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce PayPal Payments * Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/ * Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage. - * Version: 2.8.1 + * Version: 2.8.0 * Author: WooCommerce * Author URI: https://woocommerce.com/ * License: GPL-2.0 @@ -228,7 +228,7 @@ define( 'PAYPAL_INTEGRATION_DATE', '2024-06-25' ); if ( version_compare( $plugin_data['Version'], '3.0.0', '<' ) && version_compare( $new_data->new_version, '3.0.0', '>=' ) ) { printf( - '

%s: %s

', + '
%s: %s', esc_html__( 'Warning', 'woocommerce-paypal-payments' ), esc_html__( 'WooCommerce PayPal Payments version 3.0.0 contains significant changes that may impact your website. We strongly recommend reviewing the changes and testing the update on a staging site before updating it on your production environment.', 'woocommerce-paypal-payments' ) ); From 26daa3a52fa46551236517d3f56e174919c542ed Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 15 Jul 2024 11:23:01 +0400 Subject: [PATCH 14/72] Revert version number --- woocommerce-paypal-payments.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index 8394055a2..d18ab2969 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce PayPal Payments * Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/ * Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage. - * Version: 2.8.0 + * Version: 2.8.1 * Author: WooCommerce * Author URI: https://woocommerce.com/ * License: GPL-2.0 From 525fee7edd17c88507cebfe61c068f7613856503 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 15 Jul 2024 10:58:03 +0200 Subject: [PATCH 15/72] Remove the redundant async --- modules/ppcp-googlepay/resources/js/GooglepayButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index e94a49e1a..64ea52062 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -209,7 +209,7 @@ class GooglepayButton { /** * Show Google Pay payment sheet when Google Pay payment button is clicked */ - async onButtonClick() { + onButtonClick() { this.log('onButtonClick', this.context); const paymentDataRequest = this.paymentDataRequest(); From 8b43a06f9f298e465a80b61832b1c3dc5fc26e57 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 15 Jul 2024 11:21:56 +0200 Subject: [PATCH 16/72] Fix JS linting --- .../resources/js/GooglepayButton.js | 866 ++++++++++-------- .../resources/js/GooglepayManager.js | 143 ++- .../ppcp-googlepay/resources/js/boot-admin.js | 174 ++-- 3 files changed, 638 insertions(+), 545 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 64ea52062..53a2f9931 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -1,397 +1,479 @@ -import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; -import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; -import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder"; -import UpdatePaymentData from "./Helper/UpdatePaymentData"; -import {apmButtonsInit} from "../../../ppcp-button/resources/js/modules/Helper/ApmButtons"; +import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; +import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; +import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder'; +import UpdatePaymentData from './Helper/UpdatePaymentData'; +import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons'; class GooglepayButton { - - constructor(context, externalHandler, buttonConfig, ppcpConfig, contextHandler) { - apmButtonsInit(ppcpConfig); - - this.isInitialized = false; - - this.context = context; - this.externalHandler = externalHandler; - this.buttonConfig = buttonConfig; - this.ppcpConfig = ppcpConfig; - this.contextHandler = contextHandler; - - this.paymentsClient = null; - - this.log = function() { - if (this.buttonConfig.is_debug) { - //console.log('[GooglePayButton]', ...arguments); - } - } - } - - init(config, transactionInfo) { - if (this.isInitialized) { - return; - } - this.isInitialized = true; - - if (!this.validateConfig()) { - return; - } - - if (!this.contextHandler.validateContext()) { - return; - } - - this.googlePayConfig = config; - this.transactionInfo = transactionInfo; - this.allowedPaymentMethods = config.allowedPaymentMethods; - this.baseCardPaymentMethod = this.allowedPaymentMethods[0]; - - this.initClient(); - this.initEventHandlers(); - - this.paymentsClient.isReadyToPay( - this.buildReadyToPayRequest(this.allowedPaymentMethods, config) - ) - .then((response) => { - if (response.result) { - this.addButton(this.baseCardPaymentMethod); - } - }) - .catch(function(err) { - console.error(err); - }); - } - - reinit() { - if (!this.googlePayConfig) { - return; - } - - this.isInitialized = false; - this.init(this.googlePayConfig, this.transactionInfo); - } - - validateConfig() { - if (['PRODUCTION', 'TEST'].indexOf(this.buttonConfig.environment) === -1) { - console.error('[GooglePayButton] Invalid environment.', this.buttonConfig.environment); - return false; - } - - if (!this.contextHandler) { - console.error('[GooglePayButton] Invalid context handler.', this.contextHandler); - return false; - } - - return true; - } - - /** - * Returns configurations relative to this button context. - */ - contextConfig() { - let config = { - wrapper: this.buttonConfig.button.wrapper, - ppcpStyle: this.ppcpConfig.button.style, - buttonStyle: this.buttonConfig.button.style, - ppcpButtonWrapper: this.ppcpConfig.button.wrapper - } - - if (this.context === 'mini-cart') { - config.wrapper = this.buttonConfig.button.mini_cart_wrapper; - config.ppcpStyle = this.ppcpConfig.button.mini_cart_style; - config.buttonStyle = this.buttonConfig.button.mini_cart_style; - config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper; - - // Handle incompatible types. - if (config.buttonStyle.type === 'buy') { - config.buttonStyle.type = 'pay'; - } - } - - if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) { - config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway-paypal'; - } - - return config; - } - - initClient() { - const callbacks = { - onPaymentAuthorized: this.onPaymentAuthorized.bind(this) - } - - if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) { - callbacks['onPaymentDataChanged'] = this.onPaymentDataChanged.bind(this); - } - - this.paymentsClient = new google.payments.api.PaymentsClient({ - environment: this.buttonConfig.environment, - // add merchant info maybe - paymentDataCallbacks: callbacks - }); - } - - initEventHandlers() { - const { wrapper, ppcpButtonWrapper } = this.contextConfig(); - - if (wrapper === ppcpButtonWrapper) { - throw new Error(`[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${wrapper}"`); - } - - const syncButtonVisibility = () => { - const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper); - setVisible(wrapper, $ppcpButtonWrapper.is(':visible')); - setEnabled(wrapper, !$ppcpButtonWrapper.hasClass('ppcp-disabled')); - } - - jQuery(document).on('ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', (ev, data) => { - if (jQuery(data.selector).is(ppcpButtonWrapper)) { - syncButtonVisibility(); - } - }); - - syncButtonVisibility(); - } - - buildReadyToPayRequest(allowedPaymentMethods, baseRequest) { - return Object.assign({}, baseRequest, { - allowedPaymentMethods: allowedPaymentMethods, - }); - } - - /** - * Add a Google Pay purchase button - */ - addButton(baseCardPaymentMethod) { - this.log('addButton', this.context); - - const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig(); - - this.waitForWrapper(wrapper, () => { - jQuery(wrapper).addClass('ppcp-button-' + ppcpStyle.shape); - - if (ppcpStyle.height) { - jQuery(wrapper).css('height', `${ppcpStyle.height}px`) - } - - const button = - this.paymentsClient.createButton({ - onClick: this.onButtonClick.bind(this), - allowedPaymentMethods: [baseCardPaymentMethod], - buttonColor: buttonStyle.color || 'black', - buttonType: buttonStyle.type || 'pay', - buttonLocale: buttonStyle.language || 'en', - buttonSizeMode: 'fill', - }); - - jQuery(wrapper).append(button); - }); - } - - waitForWrapper(selector, callback, delay = 100, timeout = 2000) { - const startTime = Date.now(); - const interval = setInterval(() => { - const el = document.querySelector(selector); - const timeElapsed = Date.now() - startTime; - - if (el) { - clearInterval(interval); - callback(el); - } else if (timeElapsed > timeout) { - clearInterval(interval); - } - }, delay); - } - - //------------------------ - // Button click - //------------------------ - - /** - * Show Google Pay payment sheet when Google Pay payment button is clicked - */ - onButtonClick() { - this.log('onButtonClick', this.context); - - const paymentDataRequest = this.paymentDataRequest(); - this.log('onButtonClick: paymentDataRequest', paymentDataRequest, this.context); - - window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler. - - this.paymentsClient.loadPaymentData(paymentDataRequest); - } - - paymentDataRequest() { - let baseRequest = { - apiVersion: 2, - apiVersionMinor: 0 - } - - const googlePayConfig = this.googlePayConfig; - const paymentDataRequest = Object.assign({}, baseRequest); - paymentDataRequest.allowedPaymentMethods = googlePayConfig.allowedPaymentMethods; - paymentDataRequest.transactionInfo = this.transactionInfo; - paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo; - - if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) { - paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS", "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"]; - paymentDataRequest.shippingAddressRequired = true; - paymentDataRequest.shippingAddressParameters = this.shippingAddressParameters(); - paymentDataRequest.shippingOptionRequired = true; - } else { - paymentDataRequest.callbackIntents = ['PAYMENT_AUTHORIZATION']; - } - - return paymentDataRequest; - } - - //------------------------ - // Shipping processing - //------------------------ - - shippingAddressParameters() { - return { - allowedCountryCodes: this.buttonConfig.shipping.countries, - phoneNumberRequired: true - }; - } - - onPaymentDataChanged(paymentData) { - this.log('onPaymentDataChanged', this.context); - this.log('paymentData', paymentData); - - return new Promise(async (resolve, reject) => { - try { - let paymentDataRequestUpdate = {}; - - const updatedData = await (new UpdatePaymentData(this.buttonConfig.ajax.update_payment_data)).update(paymentData); - const transactionInfo = this.transactionInfo; - - this.log('onPaymentDataChanged:updatedData', updatedData); - this.log('onPaymentDataChanged:transactionInfo', transactionInfo); - - updatedData.country_code = transactionInfo.countryCode; - updatedData.currency_code = transactionInfo.currencyCode; - updatedData.total_str = transactionInfo.totalPrice; - - // Handle unserviceable address. - if (!(updatedData.shipping_options?.shippingOptions?.length)) { - paymentDataRequestUpdate.error = this.unserviceableShippingAddressError(); - resolve(paymentDataRequestUpdate); - return; - } - - switch (paymentData.callbackTrigger) { - case 'INITIALIZE': - case 'SHIPPING_ADDRESS': - paymentDataRequestUpdate.newShippingOptionParameters = updatedData.shipping_options; - paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); - break; - case 'SHIPPING_OPTION': - paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData); - break; - } - - resolve(paymentDataRequestUpdate); - } catch(error) { - console.error('Error during onPaymentDataChanged:', error); - reject(error); - } - }); - } - - unserviceableShippingAddressError() { - return { - reason: "SHIPPING_ADDRESS_UNSERVICEABLE", - message: "Cannot ship to the selected address", - intent: "SHIPPING_ADDRESS" - }; - } - - calculateNewTransactionInfo(updatedData) { - return { - countryCode: updatedData.country_code, - currencyCode: updatedData.currency_code, - totalPriceStatus: 'FINAL', - totalPrice: updatedData.total_str - }; - } - - - //------------------------ - // Payment process - //------------------------ - - onPaymentAuthorized(paymentData) { - this.log('onPaymentAuthorized', this.context); - return this.processPayment(paymentData); - } - - async processPayment(paymentData) { - this.log('processPayment', this.context); - - return new Promise(async (resolve, reject) => { - try { - let id = await this.contextHandler.createOrder(); - - this.log('processPayment: createOrder', id, this.context); - - const confirmOrderResponse = await widgetBuilder.paypal.Googlepay().confirmOrder({ - orderId: id, - paymentMethodData: paymentData.paymentMethodData - }); - - this.log('processPayment: confirmOrder', confirmOrderResponse, this.context); - - /** Capture the Order on the Server */ - if (confirmOrderResponse.status === "APPROVED") { - - let approveFailed = false; - await this.contextHandler.approveOrder({ - orderID: id - }, { // actions mock object. - restart: () => new Promise((resolve, reject) => { - approveFailed = true; - resolve(); - }), - order: { - get: () => new Promise((resolve, reject) => { - resolve(null); - }) - } - }); - - if (!approveFailed) { - resolve(this.processPaymentResponse('SUCCESS')); - } else { - resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'FAILED TO APPROVE')); - } - - } else { - resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'TRANSACTION FAILED')); - } - } catch(err) { - resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', err.message)); - } - }); - } - - processPaymentResponse(state, intent = null, message = null) { - let response = { - transactionState: state, - } - - if (intent || message) { - response.error = { - intent: intent, - message: message, - } - } - - this.log('processPaymentResponse', response, this.context); - - return response; - } - + constructor( + context, + externalHandler, + buttonConfig, + ppcpConfig, + contextHandler + ) { + apmButtonsInit( ppcpConfig ); + + this.isInitialized = false; + + this.context = context; + this.externalHandler = externalHandler; + this.buttonConfig = buttonConfig; + this.ppcpConfig = ppcpConfig; + this.contextHandler = contextHandler; + + this.paymentsClient = null; + + this.log = function () { + if ( this.buttonConfig.is_debug ) { + //console.log('[GooglePayButton]', ...arguments); + } + }; + } + + init( config, transactionInfo ) { + if ( this.isInitialized ) { + return; + } + this.isInitialized = true; + + if ( ! this.validateConfig() ) { + return; + } + + if ( ! this.contextHandler.validateContext() ) { + return; + } + + this.googlePayConfig = config; + this.transactionInfo = transactionInfo; + this.allowedPaymentMethods = config.allowedPaymentMethods; + this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ]; + + this.initClient(); + this.initEventHandlers(); + + this.paymentsClient + .isReadyToPay( + this.buildReadyToPayRequest( + this.allowedPaymentMethods, + config + ) + ) + .then( ( response ) => { + if ( response.result ) { + this.addButton( this.baseCardPaymentMethod ); + } + } ) + .catch( function ( err ) { + console.error( err ); + } ); + } + + reinit() { + if ( ! this.googlePayConfig ) { + return; + } + + this.isInitialized = false; + this.init( this.googlePayConfig, this.transactionInfo ); + } + + validateConfig() { + if ( + [ 'PRODUCTION', 'TEST' ].indexOf( + this.buttonConfig.environment + ) === -1 + ) { + console.error( + '[GooglePayButton] Invalid environment.', + this.buttonConfig.environment + ); + return false; + } + + if ( ! this.contextHandler ) { + console.error( + '[GooglePayButton] Invalid context handler.', + this.contextHandler + ); + return false; + } + + return true; + } + + /** + * Returns configurations relative to this button context. + */ + contextConfig() { + const config = { + wrapper: this.buttonConfig.button.wrapper, + ppcpStyle: this.ppcpConfig.button.style, + buttonStyle: this.buttonConfig.button.style, + ppcpButtonWrapper: this.ppcpConfig.button.wrapper, + }; + + if ( this.context === 'mini-cart' ) { + config.wrapper = this.buttonConfig.button.mini_cart_wrapper; + config.ppcpStyle = this.ppcpConfig.button.mini_cart_style; + config.buttonStyle = this.buttonConfig.button.mini_cart_style; + config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper; + + // Handle incompatible types. + if ( config.buttonStyle.type === 'buy' ) { + config.buttonStyle.type = 'pay'; + } + } + + if ( + [ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1 + ) { + config.ppcpButtonWrapper = + '#express-payment-method-ppcp-gateway-paypal'; + } + + return config; + } + + initClient() { + const callbacks = { + onPaymentAuthorized: this.onPaymentAuthorized.bind( this ), + }; + + if ( + this.buttonConfig.shipping.enabled && + this.contextHandler.shippingAllowed() + ) { + callbacks.onPaymentDataChanged = + this.onPaymentDataChanged.bind( this ); + } + + this.paymentsClient = new google.payments.api.PaymentsClient( { + environment: this.buttonConfig.environment, + // add merchant info maybe + paymentDataCallbacks: callbacks, + } ); + } + + initEventHandlers() { + const { wrapper, ppcpButtonWrapper } = this.contextConfig(); + + if ( wrapper === ppcpButtonWrapper ) { + throw new Error( + `[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper }"` + ); + } + + const syncButtonVisibility = () => { + const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper ); + setVisible( wrapper, $ppcpButtonWrapper.is( ':visible' ) ); + setEnabled( + wrapper, + ! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' ) + ); + }; + + jQuery( document ).on( + 'ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', + ( ev, data ) => { + if ( jQuery( data.selector ).is( ppcpButtonWrapper ) ) { + syncButtonVisibility(); + } + } + ); + + syncButtonVisibility(); + } + + buildReadyToPayRequest( allowedPaymentMethods, baseRequest ) { + return Object.assign( {}, baseRequest, { + allowedPaymentMethods, + } ); + } + + /** + * Add a Google Pay purchase button + * @param baseCardPaymentMethod + */ + addButton( baseCardPaymentMethod ) { + this.log( 'addButton', this.context ); + + const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig(); + + this.waitForWrapper( wrapper, () => { + jQuery( wrapper ).addClass( 'ppcp-button-' + ppcpStyle.shape ); + + if ( ppcpStyle.height ) { + jQuery( wrapper ).css( 'height', `${ ppcpStyle.height }px` ); + } + + const button = this.paymentsClient.createButton( { + onClick: this.onButtonClick.bind( this ), + allowedPaymentMethods: [ baseCardPaymentMethod ], + buttonColor: buttonStyle.color || 'black', + buttonType: buttonStyle.type || 'pay', + buttonLocale: buttonStyle.language || 'en', + buttonSizeMode: 'fill', + } ); + + jQuery( wrapper ).append( button ); + } ); + } + + waitForWrapper( selector, callback, delay = 100, timeout = 2000 ) { + const startTime = Date.now(); + const interval = setInterval( () => { + const el = document.querySelector( selector ); + const timeElapsed = Date.now() - startTime; + + if ( el ) { + clearInterval( interval ); + callback( el ); + } else if ( timeElapsed > timeout ) { + clearInterval( interval ); + } + }, delay ); + } + + //------------------------ + // Button click + //------------------------ + + /** + * Show Google Pay payment sheet when Google Pay payment button is clicked + */ + onButtonClick() { + this.log( 'onButtonClick', this.context ); + + const paymentDataRequest = this.paymentDataRequest(); + this.log( + 'onButtonClick: paymentDataRequest', + paymentDataRequest, + this.context + ); + + window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler. + + this.paymentsClient.loadPaymentData( paymentDataRequest ); + } + + paymentDataRequest() { + const baseRequest = { + apiVersion: 2, + apiVersionMinor: 0, + }; + + const googlePayConfig = this.googlePayConfig; + const paymentDataRequest = Object.assign( {}, baseRequest ); + paymentDataRequest.allowedPaymentMethods = + googlePayConfig.allowedPaymentMethods; + paymentDataRequest.transactionInfo = this.transactionInfo; + paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo; + + if ( + this.buttonConfig.shipping.enabled && + this.contextHandler.shippingAllowed() + ) { + paymentDataRequest.callbackIntents = [ + 'SHIPPING_ADDRESS', + 'SHIPPING_OPTION', + 'PAYMENT_AUTHORIZATION', + ]; + paymentDataRequest.shippingAddressRequired = true; + paymentDataRequest.shippingAddressParameters = + this.shippingAddressParameters(); + paymentDataRequest.shippingOptionRequired = true; + } else { + paymentDataRequest.callbackIntents = [ 'PAYMENT_AUTHORIZATION' ]; + } + + return paymentDataRequest; + } + + //------------------------ + // Shipping processing + //------------------------ + + shippingAddressParameters() { + return { + allowedCountryCodes: this.buttonConfig.shipping.countries, + phoneNumberRequired: true, + }; + } + + onPaymentDataChanged( paymentData ) { + this.log( 'onPaymentDataChanged', this.context ); + this.log( 'paymentData', paymentData ); + + return new Promise( async ( resolve, reject ) => { + try { + const paymentDataRequestUpdate = {}; + + const updatedData = await new UpdatePaymentData( + this.buttonConfig.ajax.update_payment_data + ).update( paymentData ); + const transactionInfo = this.transactionInfo; + + this.log( 'onPaymentDataChanged:updatedData', updatedData ); + this.log( + 'onPaymentDataChanged:transactionInfo', + transactionInfo + ); + + updatedData.country_code = transactionInfo.countryCode; + updatedData.currency_code = transactionInfo.currencyCode; + updatedData.total_str = transactionInfo.totalPrice; + + // Handle unserviceable address. + if ( ! updatedData.shipping_options?.shippingOptions?.length ) { + paymentDataRequestUpdate.error = + this.unserviceableShippingAddressError(); + resolve( paymentDataRequestUpdate ); + return; + } + + switch ( paymentData.callbackTrigger ) { + case 'INITIALIZE': + case 'SHIPPING_ADDRESS': + paymentDataRequestUpdate.newShippingOptionParameters = + updatedData.shipping_options; + paymentDataRequestUpdate.newTransactionInfo = + this.calculateNewTransactionInfo( updatedData ); + break; + case 'SHIPPING_OPTION': + paymentDataRequestUpdate.newTransactionInfo = + this.calculateNewTransactionInfo( updatedData ); + break; + } + + resolve( paymentDataRequestUpdate ); + } catch ( error ) { + console.error( 'Error during onPaymentDataChanged:', error ); + reject( error ); + } + } ); + } + + unserviceableShippingAddressError() { + return { + reason: 'SHIPPING_ADDRESS_UNSERVICEABLE', + message: 'Cannot ship to the selected address', + intent: 'SHIPPING_ADDRESS', + }; + } + + calculateNewTransactionInfo( updatedData ) { + return { + countryCode: updatedData.country_code, + currencyCode: updatedData.currency_code, + totalPriceStatus: 'FINAL', + totalPrice: updatedData.total_str, + }; + } + + //------------------------ + // Payment process + //------------------------ + + onPaymentAuthorized( paymentData ) { + this.log( 'onPaymentAuthorized', this.context ); + return this.processPayment( paymentData ); + } + + async processPayment( paymentData ) { + this.log( 'processPayment', this.context ); + + return new Promise( async ( resolve, reject ) => { + try { + const id = await this.contextHandler.createOrder(); + + this.log( 'processPayment: createOrder', id, this.context ); + + const confirmOrderResponse = await widgetBuilder.paypal + .Googlepay() + .confirmOrder( { + orderId: id, + paymentMethodData: paymentData.paymentMethodData, + } ); + + this.log( + 'processPayment: confirmOrder', + confirmOrderResponse, + this.context + ); + + /** Capture the Order on the Server */ + if ( confirmOrderResponse.status === 'APPROVED' ) { + let approveFailed = false; + await this.contextHandler.approveOrder( + { + orderID: id, + }, + { + // actions mock object. + restart: () => + new Promise( ( resolve, reject ) => { + approveFailed = true; + resolve(); + } ), + order: { + get: () => + new Promise( ( resolve, reject ) => { + resolve( null ); + } ), + }, + } + ); + + if ( ! approveFailed ) { + resolve( this.processPaymentResponse( 'SUCCESS' ) ); + } else { + resolve( + this.processPaymentResponse( + 'ERROR', + 'PAYMENT_AUTHORIZATION', + 'FAILED TO APPROVE' + ) + ); + } + } else { + resolve( + this.processPaymentResponse( + 'ERROR', + 'PAYMENT_AUTHORIZATION', + 'TRANSACTION FAILED' + ) + ); + } + } catch ( err ) { + resolve( + this.processPaymentResponse( + 'ERROR', + 'PAYMENT_AUTHORIZATION', + err.message + ) + ); + } + } ); + } + + processPaymentResponse( state, intent = null, message = null ) { + const response = { + transactionState: state, + }; + + if ( intent || message ) { + response.error = { + intent, + message, + }; + } + + this.log( 'processPaymentResponse', response, this.context ); + + return response; + } } export default GooglepayButton; diff --git a/modules/ppcp-googlepay/resources/js/GooglepayManager.js b/modules/ppcp-googlepay/resources/js/GooglepayManager.js index e03a61ed8..71c07989e 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayManager.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayManager.js @@ -1,88 +1,85 @@ -import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher"; -import GooglepayButton from "./GooglepayButton"; -import ContextHandlerFactory from "./Context/ContextHandlerFactory"; +import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; +import GooglepayButton from './GooglepayButton'; +import ContextHandlerFactory from './Context/ContextHandlerFactory'; class GooglepayManager { + constructor( buttonConfig, ppcpConfig ) { + this.buttonConfig = buttonConfig; + this.ppcpConfig = ppcpConfig; + this.googlePayConfig = null; + this.transactionInfo = null; + this.contextHandler = null; - constructor(buttonConfig, ppcpConfig) { + this.buttons = []; - this.buttonConfig = buttonConfig; - this.ppcpConfig = ppcpConfig; - this.googlePayConfig = null; - this.transactionInfo = null; - this.contextHandler = null; + buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => { + if ( ! this.contextHandler ) { + this.contextHandler = ContextHandlerFactory.create( + bootstrap.context, + buttonConfig, + ppcpConfig, + bootstrap.handler + ); + } - this.buttons = []; + const button = new GooglepayButton( + bootstrap.context, + bootstrap.handler, + buttonConfig, + ppcpConfig, + this.contextHandler + ); - buttonModuleWatcher.watchContextBootstrap(async (bootstrap) => { - if (!this.contextHandler) { - this.contextHandler = ContextHandlerFactory.create( - bootstrap.context, - buttonConfig, - ppcpConfig, - bootstrap.handler - ); - } + this.buttons.push( button ); - const button = new GooglepayButton( - bootstrap.context, - bootstrap.handler, - buttonConfig, - ppcpConfig, - this.contextHandler - ); + // Initialize button only if googlePayConfig and transactionInfo are already fetched. + if ( this.googlePayConfig && this.transactionInfo ) { + button.init( this.googlePayConfig, this.transactionInfo ); + } else { + await this.init(); + if ( this.googlePayConfig && this.transactionInfo ) { + button.init( this.googlePayConfig, this.transactionInfo ); + } + } + } ); + } - this.buttons.push(button); + async init() { + try { + if ( ! this.googlePayConfig ) { + // Gets GooglePay configuration of the PayPal merchant. + this.googlePayConfig = await paypal.Googlepay().config(); + } - // Initialize button only if googlePayConfig and transactionInfo are already fetched. - if (this.googlePayConfig && this.transactionInfo) { - button.init(this.googlePayConfig, this.transactionInfo); - } else { - await this.init(); - if (this.googlePayConfig && this.transactionInfo) { - button.init(this.googlePayConfig, this.transactionInfo); - } - } - }); - } + if ( ! this.transactionInfo ) { + this.transactionInfo = await this.fetchTransactionInfo(); + } - async init() { - try { - if (!this.googlePayConfig) { - // Gets GooglePay configuration of the PayPal merchant. - this.googlePayConfig = await paypal.Googlepay().config(); - } + for ( const button of this.buttons ) { + button.init( this.googlePayConfig, this.transactionInfo ); + } + } catch ( error ) { + console.error( 'Error during initialization:', error ); + } + } - if (!this.transactionInfo) { - this.transactionInfo = await this.fetchTransactionInfo(); - } - - for (const button of this.buttons) { - button.init(this.googlePayConfig, this.transactionInfo); - } - } catch(error) { - console.error('Error during initialization:', error); - } - } - - async fetchTransactionInfo() { - try { - if (!this.contextHandler) { - throw new Error('ContextHandler is not initialized'); - } - return await this.contextHandler.transactionInfo(); - } catch(error) { - console.error('Error fetching transaction info:', error); - throw error; - } - } - - reinit() { - for (const button of this.buttons) { - button.reinit(); - } - } + async fetchTransactionInfo() { + try { + if ( ! this.contextHandler ) { + throw new Error( 'ContextHandler is not initialized' ); + } + return await this.contextHandler.transactionInfo(); + } catch ( error ) { + console.error( 'Error fetching transaction info:', error ); + throw error; + } + } + reinit() { + for ( const button of this.buttons ) { + button.reinit(); + } + } } export default GooglepayManager; diff --git a/modules/ppcp-googlepay/resources/js/boot-admin.js b/modules/ppcp-googlepay/resources/js/boot-admin.js index 62fda993e..2c0c008d2 100644 --- a/modules/ppcp-googlepay/resources/js/boot-admin.js +++ b/modules/ppcp-googlepay/resources/js/boot-admin.js @@ -7,110 +7,124 @@ import ContextHandlerFactory from './Context/ContextHandlerFactory'; * Accessor that creates and returns a single PreviewButtonManager instance. */ const buttonManager = () => { - if (!GooglePayPreviewButtonManager.instance) { - GooglePayPreviewButtonManager.instance = new GooglePayPreviewButtonManager(); - } + if ( ! GooglePayPreviewButtonManager.instance ) { + GooglePayPreviewButtonManager.instance = + new GooglePayPreviewButtonManager(); + } - return GooglePayPreviewButtonManager.instance; + return GooglePayPreviewButtonManager.instance; }; - /** * Manages all GooglePay preview buttons on this page. */ class GooglePayPreviewButtonManager extends PreviewButtonManager { - constructor() { - const args = { - methodName: 'GooglePay', - buttonConfig: window.wc_ppcp_googlepay_admin, - }; + constructor() { + const args = { + methodName: 'GooglePay', + buttonConfig: window.wc_ppcp_googlepay_admin, + }; - super(args); - } + super( args ); + } - /** - * Responsible for fetching and returning the PayPal configuration object for this payment - * method. - * - * @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder. - * @return {Promise<{}>} - */ - async fetchConfig(payPal) { - const apiMethod = payPal?.Googlepay()?.config; + /** + * Responsible for fetching and returning the PayPal configuration object for this payment + * method. + * + * @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder. + * @return {Promise<{}>} + */ + async fetchConfig( payPal ) { + const apiMethod = payPal?.Googlepay()?.config; - if (!apiMethod) { - this.error('configuration object cannot be retrieved from PayPal'); - return {}; - } + if ( ! apiMethod ) { + this.error( + 'configuration object cannot be retrieved from PayPal' + ); + return {}; + } - try { - return await apiMethod(); - } catch (error) { - if (error.message.includes('Not Eligible')) { - this.apiError = 'Not Eligible'; - } - return null; - } - } + try { + return await apiMethod(); + } catch ( error ) { + if ( error.message.includes( 'Not Eligible' ) ) { + this.apiError = 'Not Eligible'; + } + return null; + } + } - /** - * This method is responsible for creating a new PreviewButton instance and returning it. - * - * @param {string} wrapperId - CSS ID of the wrapper element. - * @return {GooglePayPreviewButton} - */ - createButtonInstance(wrapperId) { - return new GooglePayPreviewButton({ - selector: wrapperId, - apiConfig: this.apiConfig, - }); - } + /** + * This method is responsible for creating a new PreviewButton instance and returning it. + * + * @param {string} wrapperId - CSS ID of the wrapper element. + * @return {GooglePayPreviewButton} + */ + createButtonInstance( wrapperId ) { + return new GooglePayPreviewButton( { + selector: wrapperId, + apiConfig: this.apiConfig, + } ); + } } - /** * A single GooglePay preview button instance. */ class GooglePayPreviewButton extends PreviewButton { - constructor(args) { - super(args); + constructor( args ) { + super( args ); - this.selector = `${args.selector}GooglePay`; - this.defaultAttributes = { - button: { - style: { - type: 'pay', - color: 'black', - language: 'en', - }, - }, - }; - } + this.selector = `${ args.selector }GooglePay`; + this.defaultAttributes = { + button: { + style: { + type: 'pay', + color: 'black', + language: 'en', + }, + }, + }; + } - createNewWrapper() { - const element = super.createNewWrapper(); - element.addClass('ppcp-button-googlepay'); + createNewWrapper() { + const element = super.createNewWrapper(); + element.addClass( 'ppcp-button-googlepay' ); - return element; - } + return element; + } - createButton(buttonConfig) { - const contextHandler = ContextHandlerFactory.create('preview', buttonConfig, this.ppcpConfig, null); - const button = new GooglepayButton('preview', null, buttonConfig, this.ppcpConfig, contextHandler); + createButton( buttonConfig ) { + const contextHandler = ContextHandlerFactory.create( + 'preview', + buttonConfig, + this.ppcpConfig, + null + ); + const button = new GooglepayButton( + 'preview', + null, + buttonConfig, + this.ppcpConfig, + contextHandler + ); - button.init(this.apiConfig, contextHandler.transactionInfo()); - } + button.init( this.apiConfig, contextHandler.transactionInfo() ); + } - /** - * Merge form details into the config object for preview. - * Mutates the previewConfig object; no return value. - */ - dynamicPreviewConfig(buttonConfig, ppcpConfig) { - // Merge the current form-values into the preview-button configuration. - if (ppcpConfig.button && buttonConfig.button) { - Object.assign(buttonConfig.button.style, ppcpConfig.button.style); - } - } + /** + * Merge form details into the config object for preview. + * Mutates the previewConfig object; no return value. + * @param buttonConfig + * @param ppcpConfig + */ + dynamicPreviewConfig( buttonConfig, ppcpConfig ) { + // Merge the current form-values into the preview-button configuration. + if ( ppcpConfig.button && buttonConfig.button ) { + Object.assign( buttonConfig.button.style, ppcpConfig.button.style ); + } + } } // Initialize the preview button manager. From 311d513fbf224b13fd9b9d4ad16df1340c0f6526 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 15 Jul 2024 11:40:09 +0200 Subject: [PATCH 17/72] Add WC gateway --- modules/ppcp-googlepay/GooglePayGateway.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 modules/ppcp-googlepay/GooglePayGateway.php diff --git a/modules/ppcp-googlepay/GooglePayGateway.php b/modules/ppcp-googlepay/GooglePayGateway.php new file mode 100644 index 000000000..936c7ce48 --- /dev/null +++ b/modules/ppcp-googlepay/GooglePayGateway.php @@ -0,0 +1,18 @@ + Date: Mon, 15 Jul 2024 13:06:00 +0200 Subject: [PATCH 18/72] Add WC gateway --- modules/ppcp-googlepay/GooglePayGateway.php | 18 ------ modules/ppcp-googlepay/services.php | 4 +- .../ppcp-googlepay/src/GooglePayGateway.php | 59 +++++++++++++++++++ .../ppcp-googlepay/src/GooglepayModule.php | 22 +++++++ 4 files changed, 84 insertions(+), 19 deletions(-) delete mode 100644 modules/ppcp-googlepay/GooglePayGateway.php create mode 100644 modules/ppcp-googlepay/src/GooglePayGateway.php diff --git a/modules/ppcp-googlepay/GooglePayGateway.php b/modules/ppcp-googlepay/GooglePayGateway.php deleted file mode 100644 index 936c7ce48..000000000 --- a/modules/ppcp-googlepay/GooglePayGateway.php +++ /dev/null @@ -1,18 +0,0 @@ - static function ( ContainerInterface $container ): GooglePayGateway { + return new GooglePayGateway(); + }, ); diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php new file mode 100644 index 000000000..c2dc3effc --- /dev/null +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -0,0 +1,59 @@ +id = self::ID; + + $this->method_title = __( 'Google Pay', 'woocommerce-paypal-payments' ); + $this->method_description = __( 'Google Pay', 'woocommerce-paypal-payments' ); + + $this->title = $this->get_option( 'title', $this->method_title ); + $this->description = $this->get_option( 'description', $this->method_description ); + + $this->init_form_fields(); + $this->init_settings(); + } + + /** + * Initialize the form fields. + */ + public function init_form_fields() { + $this->form_fields = array( + 'enabled' => array( + 'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'label' => __( 'Google Pay', 'woocommerce-paypal-payments' ), + 'default' => 'no', + 'desc_tip' => true, + 'description' => __( 'Enable/Disable Google Pay payment gateway.', 'woocommerce-paypal-payments' ), + ), + 'title' => array( + 'title' => __( 'Title', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'default' => $this->title, + 'desc_tip' => true, + 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-paypal-payments' ), + ), + 'description' => array( + 'title' => __( 'Description', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'default' => $this->description, + 'desc_tip' => true, + 'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-paypal-payments' ), + ), + ); + } +} diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index e2b0ea8c5..ee989a019 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Googlepay; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; +use WC_Payment_Gateway; use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint; @@ -159,6 +160,27 @@ class GooglepayModule implements ModuleInterface { }, 1 ); + + add_filter( + 'woocommerce_payment_gateways', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + static function ( $methods ) use ( $c ): array { + if ( ! is_array( $methods ) ) { + return $methods; + } + + $googlepay_gateway = $c->get( 'googlepay.wc-gateway' ); + assert( $googlepay_gateway instanceof WC_Payment_Gateway ); + + $methods[] = $googlepay_gateway; + + return $methods; + } + ); } /** From 397acdd2bf34f0fd945934662b6e53dd38928f0e Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 15 Jul 2024 16:45:10 +0400 Subject: [PATCH 19/72] Prevent displaying smart button multiple times on variable product page --- .../js/modules/ContextBootstrap/SingleProductBootstap.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index c9978306e..74d6cd31b 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -7,6 +7,7 @@ import {getPlanIdFromVariation} from "../Helper/Subscriptions" import SimulateCart from "../Helper/SimulateCart"; import {strRemoveWord, strAddWord, throttle} from "../Helper/Utils"; import merge from "deepmerge"; +import {debounce} from "../../../../../ppcp-blocks/resources/js/Helper/debounce"; class SingleProductBootstap { constructor(gateway, renderer, errorHandler) { @@ -17,7 +18,8 @@ class SingleProductBootstap { this.formSelector = 'form.cart'; // Prevent simulate cart being called too many times in a burst. - this.simulateCartThrottled = throttle(this.simulateCart, this.gateway.simulate_cart.throttling || 5000); + this.simulateCartThrottled = throttle(this.simulateCart.bind(this), this.gateway.simulate_cart.throttling || 5000); + this.debouncedHandleChange = debounce(this.handleChange.bind(this), 100); this.renderer.onButtonsInit(this.gateway.button.wrapper, () => { this.handleChange(); @@ -31,7 +33,7 @@ class SingleProductBootstap { } handleChange() { - this.subscriptionButtonsLoaded = false + this.subscriptionButtonsLoaded = false; if (!this.shouldRender()) { this.renderer.disableSmartButtons(this.gateway.button.wrapper); @@ -65,8 +67,9 @@ class SingleProductBootstap { } jQuery(document).on('change', this.formSelector, () => { - this.handleChange(); + this.debouncedHandleChange(); }); + this.mutationObserver.observe(form, { childList: true, subtree: true }); const addToCartButton = form.querySelector('.single_add_to_cart_button'); From e52939200f0e68ff804b2b4f9d77ce96cd6d3c84 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 15 Jul 2024 14:54:10 +0200 Subject: [PATCH 20/72] Hide wc place order button when applepay payment method is selected --- .../js/modules/ContextBootstrap/CheckoutBootstap.js | 7 ++++++- .../resources/js/modules/Helper/CheckoutMethodState.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index 848285815..ad75a3d62 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -181,9 +181,14 @@ class CheckoutBootstap { const isSeparateButtonGateway = [ PaymentMethods.CARD_BUTTON ].includes( currentPaymentMethod ); + const isGooglePayMethod = + currentPaymentMethod === PaymentMethods.GOOGLEPAY; const isSavedCard = isCard && isSavedCardSelected(); const isNotOurGateway = - ! isPaypal && ! isCard && ! isSeparateButtonGateway; + ! isPaypal && + ! isCard && + ! isSeparateButtonGateway && + ! isGooglePayMethod; const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart; const hasVaultedPaypal = PayPalCommerceGateway.vaulted_paypal_email !== ''; diff --git a/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js b/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js index 0ea05f255..3e284c8ef 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js +++ b/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js @@ -3,6 +3,7 @@ export const PaymentMethods = { CARDS: 'ppcp-credit-card-gateway', OXXO: 'ppcp-oxxo-gateway', CARD_BUTTON: 'ppcp-card-button-gateway', + GOOGLEPAY: 'ppcp-googlepay', }; export const ORDER_BUTTON_SELECTOR = '#place_order'; From f8ed3328a80904b7459a565881fe595a632f1c32 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 15 Jul 2024 15:56:35 +0200 Subject: [PATCH 21/72] Dispaly googlepay payment method only if googlepay button is enabled --- modules/ppcp-googlepay/src/GooglepayModule.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index ee989a019..8167e87f9 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -173,10 +173,15 @@ class GooglepayModule implements ModuleInterface { return $methods; } - $googlepay_gateway = $c->get( 'googlepay.wc-gateway' ); - assert( $googlepay_gateway instanceof WC_Payment_Gateway ); + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); - $methods[] = $googlepay_gateway; + if ( $settings->has( 'googlepay_button_enabled' ) && $settings->get( 'googlepay_button_enabled' ) ) { + $googlepay_gateway = $c->get( 'googlepay.wc-gateway' ); + assert( $googlepay_gateway instanceof WC_Payment_Gateway ); + + $methods[] = $googlepay_gateway; + } return $methods; } From 473b286de0745ae8b3a20c97f3eb20bfcffbb839 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 15 Jul 2024 16:13:12 +0200 Subject: [PATCH 22/72] Do not display googlepay button inside smart buttons in classic checkout if goolepay button is enabled --- modules/ppcp-googlepay/src/Assets/Button.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-googlepay/src/Assets/Button.php b/modules/ppcp-googlepay/src/Assets/Button.php index 6fab601b5..7e1d4962f 100644 --- a/modules/ppcp-googlepay/src/Assets/Button.php +++ b/modules/ppcp-googlepay/src/Assets/Button.php @@ -13,6 +13,7 @@ use Exception; use Psr\Log\LoggerInterface; use WC_Countries; use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface; +use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; @@ -25,6 +26,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; */ class Button implements ButtonInterface { + use ContextTrait; + /** * The URL to the module. * @@ -235,7 +238,7 @@ class Button implements ButtonInterface { $button_enabled_product = $this->settings_status->is_smart_button_enabled_for_location( 'product' ); $button_enabled_cart = $this->settings_status->is_smart_button_enabled_for_location( 'cart' ); - $button_enabled_checkout = true; + $button_enabled_checkout = ! ( $this->context() === 'checkout' && $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' ) ); $button_enabled_payorder = true; $button_enabled_minicart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ); @@ -409,7 +412,7 @@ class Button implements ButtonInterface { */ public function script_data(): array { $shipping = array( - 'enabled' => $this->settings->has( 'googlepay_button_shipping_enabled' ) + 'enabled' => $this->settings->has( 'googlepay_button_shipping_enabled' ) ? boolval( $this->settings->get( 'googlepay_button_shipping_enabled' ) ) : false, 'configured' => wc_shipping_enabled() && wc_get_shipping_method_count( false, true ) > 0, From 82f46baa162c3658ed961e347ebd933e2db4f8f1 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 15 Jul 2024 18:42:44 +0200 Subject: [PATCH 23/72] Add button method for checkout context --- .../resources/js/GooglepayButton.js | 37 +++++++++++++++++++ .../ppcp-googlepay/src/GooglepayModule.php | 4 ++ 2 files changed, 41 insertions(+) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 973648af3..44daed173 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -62,6 +62,30 @@ class GooglepayButton { ) .then( ( response ) => { if ( response.result ) { + if ( this.context === 'checkout' ) { + const wrapper = document.getElementById( + 'ppc-button-ppcp-googlepay' + ); + + if ( wrapper ) { + const { ppcpStyle, buttonStyle } = + this.contextConfig(); + wrapper.className = `ppcp-button-${ ppcpStyle.shape }`; + + if ( ppcpStyle.height ) { + wrapper.style.height = `${ ppcpStyle.height }px`; + } + + this.addButtonCheckout( + this.baseCardPaymentMethod, + wrapper, + buttonStyle + ); + + return; + } + } + this.addButton( this.baseCardPaymentMethod ); } } ) @@ -221,6 +245,19 @@ class GooglepayButton { } ); } + addButtonCheckout( baseCardPaymentMethod, wrapper, buttonStyle ) { + const button = this.paymentsClient.createButton( { + onClick: this.onButtonClick.bind( this ), + allowedPaymentMethods: [ baseCardPaymentMethod ], + buttonColor: buttonStyle.color || 'black', + buttonType: buttonStyle.type || 'pay', + buttonLocale: buttonStyle.language || 'en', + buttonSizeMode: 'fill', + } ); + + wrapper.appendChild( button ); + } + waitForWrapper( selector, callback, delay = 100, timeout = 2000 ) { const startTime = Date.now(); const interval = setInterval( () => { diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index 8167e87f9..03ad140bb 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -186,6 +186,10 @@ class GooglepayModule implements ModuleInterface { return $methods; } ); + + add_action('woocommerce_review_order_after_payment', function () { + echo '
'; + }); } /** From 37ca92c57e9a9a83cd42a78626340b9b7e696fba Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 15 Jul 2024 22:38:35 +0200 Subject: [PATCH 24/72] Fix the GooglePay button preview in the settings widget --- modules/ppcp-googlepay/resources/js/boot-admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-googlepay/resources/js/boot-admin.js b/modules/ppcp-googlepay/resources/js/boot-admin.js index 5f996bcde..b9f7b3c87 100644 --- a/modules/ppcp-googlepay/resources/js/boot-admin.js +++ b/modules/ppcp-googlepay/resources/js/boot-admin.js @@ -111,7 +111,7 @@ class GooglePayPreviewButton extends PreviewButton { contextHandler ); - button.init( this.apiConfig, contextHandler.transactionInfo() ); + button.init( this.apiConfig, null ); } /** From e70891d3591d6c229c0046a284147f98f66b7a77 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Jul 2024 14:17:12 +0200 Subject: [PATCH 25/72] Add googlepay wc gateway --- .../ppcp-button/src/Assets/SmartButton.php | 1 + .../resources/js/GooglepayButton.js | 2 +- modules/ppcp-googlepay/services.php | 8 +- modules/ppcp-googlepay/src/Assets/Button.php | 1 + .../ppcp-googlepay/src/GooglePayGateway.php | 173 +++++++++++++++++- .../ppcp-googlepay/src/GooglepayModule.php | 4 +- 6 files changed, 184 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 6b92a9084..1ce9804e2 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -34,6 +34,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Button\Helper\DisabledFundingSources; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; +use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\PayLaterWCBlocks\PayLaterWCBlocksModule; diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 44daed173..5a6cac6a6 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -27,7 +27,7 @@ class GooglepayButton { this.log = function () { if ( this.buttonConfig.is_debug ) { - //console.log('[GooglePayButton]', ...arguments); + console.log( '[GooglePayButton]', ...arguments ); } }; } diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index 9d6cbd2a8..a566630e6 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -939,6 +939,12 @@ return array( ); }, 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { - return new GooglePayGateway(); + return new GooglePayGateway( + $container->get( 'wcgateway.order-processor' ), + $container->get( 'wc-subscriptions.helper' ), + $container->get( 'api.factory.paypal-checkout-url' ), + $container->get( 'wcgateway.processor.refunds' ), + $container->get( 'wcgateway.transaction-url-provider' ) + ); }, ); diff --git a/modules/ppcp-googlepay/src/Assets/Button.php b/modules/ppcp-googlepay/src/Assets/Button.php index 7e1d4962f..aa009b5b7 100644 --- a/modules/ppcp-googlepay/src/Assets/Button.php +++ b/modules/ppcp-googlepay/src/Assets/Button.php @@ -239,6 +239,7 @@ class Button implements ButtonInterface { $button_enabled_product = $this->settings_status->is_smart_button_enabled_for_location( 'product' ); $button_enabled_cart = $this->settings_status->is_smart_button_enabled_for_location( 'cart' ); $button_enabled_checkout = ! ( $this->context() === 'checkout' && $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' ) ); + $button_enabled_checkout = true; $button_enabled_payorder = true; $button_enabled_minicart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ); diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index c2dc3effc..89c5f88a5 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -9,12 +9,78 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Googlepay; +use Exception; +use WC_Order; use WC_Payment_Gateway; +use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; +use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException; +use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; +use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; +use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; +/** + * Class GooglePayGateway + */ class GooglePayGateway extends WC_Payment_Gateway { + use ProcessPaymentTrait; + const ID = 'ppcp-googlepay'; - public function __construct() { + /** + * The processor for orders. + * + * @var OrderProcessor + */ + protected $order_processor; + + /** + * The subscription helper. + * + * @var SubscriptionHelper + */ + protected $subscription_helper; + + /** + * The function return the PayPal checkout URL for the given order ID. + * + * @var callable(string):string + */ + private $paypal_checkout_url_factory; + + /** + * The Refund Processor. + * + * @var RefundProcessor + */ + private $refund_processor; + + /** + * Service able to provide transaction url for an order. + * + * @var TransactionUrlProvider + */ + protected $transaction_url_provider; + + /** + * GooglePayGateway constructor. + * + * @param OrderProcessor $order_processor The Order Processor. + * @param SubscriptionHelper $subscription_helper The subscription helper. + * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. + * @param RefundProcessor $refund_processor The Refund Processor. + * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. + */ + public function __construct( + OrderProcessor $order_processor, + SubscriptionHelper $subscription_helper, + callable $paypal_checkout_url_factory, + RefundProcessor $refund_processor, + TransactionUrlProvider $transaction_url_provider + ) { $this->id = self::ID; $this->method_title = __( 'Google Pay', 'woocommerce-paypal-payments' ); @@ -25,6 +91,11 @@ class GooglePayGateway extends WC_Payment_Gateway { $this->init_form_fields(); $this->init_settings(); + $this->order_processor = $order_processor; + $this->subscription_helper = $subscription_helper; + $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; + $this->refund_processor = $refund_processor; + $this->transaction_url_provider = $transaction_url_provider; } /** @@ -56,4 +127,104 @@ class GooglePayGateway extends WC_Payment_Gateway { ), ); } + + /** + * Process payment for a WooCommerce order. + * + * @param int $order_id The WooCommerce order id. + * + * @return array + */ + public function process_payment( $order_id ) { + $wc_order = wc_get_order( $order_id ); + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return $this->handle_payment_failure( + null, + new GatewayGenericException( new Exception( 'WC order was not found.' ) ) + ); + } + + /** + * If customer has chosen change Subscription payment. + */ + if ( $this->subscription_helper->has_subscription( $order_id ) && $this->subscription_helper->is_subscription_change_payment() ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $saved_paypal_payment = wc_clean( wp_unslash( $_POST['saved_paypal_payment'] ?? '' ) ); + if ( $saved_paypal_payment ) { + $wc_order->update_meta_data( 'payment_token_id', $saved_paypal_payment ); + $wc_order->save(); + + return $this->handle_payment_success( $wc_order ); + } + } + + /** + * If the WC_Order is paid through the approved webhook. + */ + //phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) { + return $this->handle_payment_success( $wc_order ); + } + //phpcs:enable WordPress.Security.NonceVerification.Recommended + + try { + try { + $this->order_processor->process( $wc_order ); + + do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order ); + + return $this->handle_payment_success( $wc_order ); + } catch ( PayPalOrderMissingException $exc ) { + $order = $this->order_processor->create_order( $wc_order ); + + return array( + 'result' => 'success', + 'redirect' => ( $this->paypal_checkout_url_factory )( $order->id() ), + ); + } + } catch ( PayPalApiException $error ) { + return $this->handle_payment_failure( + $wc_order, + new Exception( + Messages::generic_payment_error_message() . ' ' . $error->getMessage(), + $error->getCode(), + $error + ) + ); + } catch ( Exception $error ) { + return $this->handle_payment_failure( $wc_order, $error ); + } + } + + /** + * Process refund. + * + * If the gateway declares 'refunds' support, this will allow it to refund. + * a passed in amount. + * + * @param int $order_id Order ID. + * @param float $amount Refund amount. + * @param string $reason Refund reason. + * @return boolean True or false based on success, or a WP_Error object. + */ + public function process_refund( $order_id, $amount = null, $reason = '' ) { + $order = wc_get_order( $order_id ); + if ( ! is_a( $order, \WC_Order::class ) ) { + return false; + } + return $this->refund_processor->process( $order, (float) $amount, (string) $reason ); + } + + /** + * Return transaction url for this gateway and given order. + * + * @param \WC_Order $order WC order to get transaction url by. + * + * @return string + */ + public function get_transaction_url( $order ): string { + $this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order ); + + return parent::get_transaction_url( $order ); + } } diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index 03ad140bb..7c558c6a2 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -187,8 +187,8 @@ class GooglepayModule implements ModuleInterface { } ); - add_action('woocommerce_review_order_after_payment', function () { - echo '
'; + add_action('woocommerce_review_order_after_submit', function () { + echo '
Google Pay...
'; }); } From 895cd5c4c14687d8b9ae15870d02b5e7bb7d3162 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Jul 2024 16:00:11 +0200 Subject: [PATCH 26/72] Return payment method if it exist --- modules/ppcp-googlepay/services.php | 3 +- .../ppcp-googlepay/src/GooglePayGateway.php | 29 +++++++++++++------ .../src/Checkout/DisableGateways.php | 6 ++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index a566630e6..ae86d129b 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -944,7 +944,8 @@ return array( $container->get( 'wc-subscriptions.helper' ), $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.processor.refunds' ), - $container->get( 'wcgateway.transaction-url-provider' ) + $container->get( 'wcgateway.transaction-url-provider' ), + $container->get( 'session.handler' ) ); }, ); diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index 89c5f88a5..144d5da9e 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -13,6 +13,7 @@ use Exception; use WC_Order; use WC_Payment_Gateway; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; +use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException; use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages; @@ -65,21 +66,30 @@ class GooglePayGateway extends WC_Payment_Gateway { */ protected $transaction_url_provider; + /** + * The Session Handler. + * + * @var SessionHandler + */ + protected $session_handler; + /** * GooglePayGateway constructor. * - * @param OrderProcessor $order_processor The Order Processor. - * @param SubscriptionHelper $subscription_helper The subscription helper. + * @param OrderProcessor $order_processor The Order Processor. + * @param SubscriptionHelper $subscription_helper The subscription helper. * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. - * @param RefundProcessor $refund_processor The Refund Processor. - * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. + * @param RefundProcessor $refund_processor The Refund Processor. + * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. + * @param SessionHandler $session_handler The Session Handler. */ public function __construct( OrderProcessor $order_processor, SubscriptionHelper $subscription_helper, callable $paypal_checkout_url_factory, RefundProcessor $refund_processor, - TransactionUrlProvider $transaction_url_provider + TransactionUrlProvider $transaction_url_provider, + SessionHandler $session_handler ) { $this->id = self::ID; @@ -91,11 +101,12 @@ class GooglePayGateway extends WC_Payment_Gateway { $this->init_form_fields(); $this->init_settings(); - $this->order_processor = $order_processor; - $this->subscription_helper = $subscription_helper; + $this->order_processor = $order_processor; + $this->subscription_helper = $subscription_helper; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; - $this->refund_processor = $refund_processor; - $this->transaction_url_provider = $transaction_url_provider; + $this->refund_processor = $refund_processor; + $this->transaction_url_provider = $transaction_url_provider; + $this->session_handler = $session_handler; } /** diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index de07ecfef..a55fe621c 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -106,6 +106,12 @@ class DisableGateways { return $methods; } + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $payment_method = wc_clean( wp_unslash( $_POST['payment_method'] ?? '' ) ); + if ( $payment_method && is_string( $payment_method ) ) { + return array( $payment_method => $methods[ $payment_method ] ); + } + return array( PayPalGateway::ID => $methods[ PayPalGateway::ID ] ); } From c941430b7de4c48958963753958ebce76d7de5f5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Jul 2024 16:45:32 +0200 Subject: [PATCH 27/72] Show googlepay button conditionally --- .../resources/js/modules/ContextBootstrap/CheckoutBootstap.js | 2 ++ modules/ppcp-googlepay/resources/css/styles.scss | 4 ++++ modules/ppcp-googlepay/resources/js/GooglepayButton.js | 3 ++- modules/ppcp-googlepay/src/GooglepayModule.php | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index ad75a3d62..33d1ecfd3 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -232,6 +232,8 @@ class CheckoutBootstap { } } + setVisible( '#ppc-button-ppcp-googlepay', isGooglePayMethod ); + jQuery( document.body ).trigger( 'ppcp_checkout_rendered' ); } diff --git a/modules/ppcp-googlepay/resources/css/styles.scss b/modules/ppcp-googlepay/resources/css/styles.scss index c60212a2e..6cf2119a0 100644 --- a/modules/ppcp-googlepay/resources/css/styles.scss +++ b/modules/ppcp-googlepay/resources/css/styles.scss @@ -13,3 +13,7 @@ min-width: 0 !important; } } + +#ppc-button-ppcp-googlepay { + display: none; +} diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 5a6cac6a6..494373a57 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -27,7 +27,7 @@ class GooglepayButton { this.log = function () { if ( this.buttonConfig.is_debug ) { - console.log( '[GooglePayButton]', ...arguments ); + //console.log('[GooglePayButton]', ...arguments); } }; } @@ -70,6 +70,7 @@ class GooglepayButton { if ( wrapper ) { const { ppcpStyle, buttonStyle } = this.contextConfig(); + wrapper.className = `ppcp-button-${ ppcpStyle.shape }`; if ( ppcpStyle.height ) { diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index 7c558c6a2..6d787ce5e 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -188,7 +188,7 @@ class GooglepayModule implements ModuleInterface { ); add_action('woocommerce_review_order_after_submit', function () { - echo '
Google Pay...
'; + echo '
'; }); } From 335f6803441190f04900d79e2118e6b82f54d57a Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Jul 2024 17:51:02 +0200 Subject: [PATCH 28/72] Extract logic from gateway to modules --- modules/ppcp-googlepay/services.php | 1 - .../ppcp-googlepay/src/GooglePayGateway.php | 34 +------------------ .../src/WcSubscriptionsModule.php | 20 ++++++++++- modules/ppcp-webhooks/src/WebhookModule.php | 13 +++++++ 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index ae86d129b..6997976bf 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -941,7 +941,6 @@ return array( 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { return new GooglePayGateway( $container->get( 'wcgateway.order-processor' ), - $container->get( 'wc-subscriptions.helper' ), $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.processor.refunds' ), $container->get( 'wcgateway.transaction-url-provider' ), diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index 144d5da9e..6b02560b2 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -21,7 +21,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; -use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; /** * Class GooglePayGateway @@ -38,13 +37,6 @@ class GooglePayGateway extends WC_Payment_Gateway { */ protected $order_processor; - /** - * The subscription helper. - * - * @var SubscriptionHelper - */ - protected $subscription_helper; - /** * The function return the PayPal checkout URL for the given order ID. * @@ -77,7 +69,6 @@ class GooglePayGateway extends WC_Payment_Gateway { * GooglePayGateway constructor. * * @param OrderProcessor $order_processor The Order Processor. - * @param SubscriptionHelper $subscription_helper The subscription helper. * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. * @param RefundProcessor $refund_processor The Refund Processor. * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. @@ -85,7 +76,6 @@ class GooglePayGateway extends WC_Payment_Gateway { */ public function __construct( OrderProcessor $order_processor, - SubscriptionHelper $subscription_helper, callable $paypal_checkout_url_factory, RefundProcessor $refund_processor, TransactionUrlProvider $transaction_url_provider, @@ -102,7 +92,6 @@ class GooglePayGateway extends WC_Payment_Gateway { $this->init_form_fields(); $this->init_settings(); $this->order_processor = $order_processor; - $this->subscription_helper = $subscription_helper; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; $this->refund_processor = $refund_processor; $this->transaction_url_provider = $transaction_url_provider; @@ -155,28 +144,7 @@ class GooglePayGateway extends WC_Payment_Gateway { ); } - /** - * If customer has chosen change Subscription payment. - */ - if ( $this->subscription_helper->has_subscription( $order_id ) && $this->subscription_helper->is_subscription_change_payment() ) { - // phpcs:ignore WordPress.Security.NonceVerification.Missing - $saved_paypal_payment = wc_clean( wp_unslash( $_POST['saved_paypal_payment'] ?? '' ) ); - if ( $saved_paypal_payment ) { - $wc_order->update_meta_data( 'payment_token_id', $saved_paypal_payment ); - $wc_order->save(); - - return $this->handle_payment_success( $wc_order ); - } - } - - /** - * If the WC_Order is paid through the approved webhook. - */ - //phpcs:disable WordPress.Security.NonceVerification.Recommended - if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) { - return $this->handle_payment_success( $wc_order ); - } - //phpcs:enable WordPress.Security.NonceVerification.Recommended + do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order ); try { try { diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index df31b45bc..f2ccf3f0f 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; @@ -33,7 +34,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; */ class WcSubscriptionsModule implements ModuleInterface { - use TransactionIdHandlingTrait; + use TransactionIdHandlingTrait, ProcessPaymentTrait; /** * {@inheritDoc} @@ -255,6 +256,23 @@ class WcSubscriptionsModule implements ModuleInterface { 10, 3 ); + + add_action( 'woocommerce_paypal_payments_before_process_order', function(WC_Order $wc_order) use ( $c ) { + $subscriptions_helper = $c->get( 'wc-subscriptions.helper' ); + assert( $subscriptions_helper instanceof SubscriptionHelper ); + + // If the customer has chosen to change the subscription payment. + if ( $subscriptions_helper->has_subscription( $wc_order->get_id() ) && $subscriptions_helper->is_subscription_change_payment() ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $saved_paypal_payment = wc_clean( wp_unslash( $_POST['saved_paypal_payment'] ?? '' ) ); + if ( $saved_paypal_payment ) { + $wc_order->update_meta_data( 'payment_token_id', $saved_paypal_payment ); + $wc_order->save(); + + return $this->handle_payment_success( $wc_order ); + } + } + }); } /** diff --git a/modules/ppcp-webhooks/src/WebhookModule.php b/modules/ppcp-webhooks/src/WebhookModule.php index 2458e5b4d..f6343f0c2 100644 --- a/modules/ppcp-webhooks/src/WebhookModule.php +++ b/modules/ppcp-webhooks/src/WebhookModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Webhooks; +use WC_Order; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; @@ -16,6 +17,7 @@ use Exception; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint; @@ -27,6 +29,8 @@ use WooCommerce\PayPalCommerce\Webhooks\Status\Assets\WebhooksStatusPageAssets; */ class WebhookModule implements ModuleInterface { + use ProcessPaymentTrait; + /** * {@inheritDoc} */ @@ -158,6 +162,15 @@ class WebhookModule implements ModuleInterface { ); } ); + + // If the WC_Order is paid through the approved webhook. + add_action( 'woocommerce_paypal_payments_before_process_order', function(WC_Order $wc_order) { + //phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) { + return $this->handle_payment_success( $wc_order ); + } + //phpcs:enable + }); } /** From db4787a36017f58db71114440299e12d2212e679 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Jul 2024 17:57:33 +0200 Subject: [PATCH 29/72] Remove temp leftovers --- modules/ppcp-button/src/Assets/SmartButton.php | 1 - modules/ppcp-googlepay/src/Assets/Button.php | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 45a2ec447..4b309818e 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -34,7 +34,6 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Button\Helper\DisabledFundingSources; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; -use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\PayLaterWCBlocks\PayLaterWCBlocksModule; diff --git a/modules/ppcp-googlepay/src/Assets/Button.php b/modules/ppcp-googlepay/src/Assets/Button.php index aa009b5b7..258b862cc 100644 --- a/modules/ppcp-googlepay/src/Assets/Button.php +++ b/modules/ppcp-googlepay/src/Assets/Button.php @@ -238,7 +238,6 @@ class Button implements ButtonInterface { $button_enabled_product = $this->settings_status->is_smart_button_enabled_for_location( 'product' ); $button_enabled_cart = $this->settings_status->is_smart_button_enabled_for_location( 'cart' ); - $button_enabled_checkout = ! ( $this->context() === 'checkout' && $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' ) ); $button_enabled_checkout = true; $button_enabled_payorder = true; $button_enabled_minicart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ); From 3c23382c03524866c1eb4567c756c779a5222bcc Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 17 Jul 2024 12:53:11 +0400 Subject: [PATCH 30/72] Don't declare Automatic Recurring Payments support when PayPal subscriptions mode is disabled --- .../src/WcSubscriptionsModule.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index df31b45bc..ce13f2a7d 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -416,7 +416,9 @@ class WcSubscriptionsModule implements ModuleInterface { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); - if ( $subscriptions_helper->plugin_is_active() ) { + $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : null; + + if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) { $supports = array( 'subscriptions', 'subscription_cancellation', @@ -467,7 +469,12 @@ class WcSubscriptionsModule implements ModuleInterface { $subscriptions_helper = $c->get( 'wc-subscriptions.helper' ); assert( $subscriptions_helper instanceof SubscriptionHelper ); - if ( $subscriptions_helper->plugin_is_active() ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : null; + + if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) { $supports = array( 'subscriptions', 'subscription_cancellation', From 6d927ead36162848813d55d8159ea2c1ecb473a8 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 17 Jul 2024 12:58:01 +0400 Subject: [PATCH 31/72] Fix condition --- modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index ce13f2a7d..af68dbded 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -416,7 +416,7 @@ class WcSubscriptionsModule implements ModuleInterface { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); - $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : null; + $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : ''; if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) { $supports = array( @@ -472,7 +472,7 @@ class WcSubscriptionsModule implements ModuleInterface { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); - $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : null; + $subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : ''; if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) { $supports = array( From f66e6aff99cf5abf198bf09f55c9704ce44677da Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 17 Jul 2024 12:36:44 +0200 Subject: [PATCH 32/72] Google Pay: Fix the context being stuck on mini-cart --- .../resources/js/GooglepayManager.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayManager.js b/modules/ppcp-googlepay/resources/js/GooglepayManager.js index 71c07989e..e267f1b8a 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayManager.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayManager.js @@ -13,14 +13,12 @@ class GooglepayManager { this.buttons = []; buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => { - if ( ! this.contextHandler ) { - this.contextHandler = ContextHandlerFactory.create( - bootstrap.context, - buttonConfig, - ppcpConfig, - bootstrap.handler - ); - } + this.contextHandler = ContextHandlerFactory.create( + bootstrap.context, + buttonConfig, + ppcpConfig, + bootstrap.handler + ); const button = new GooglepayButton( bootstrap.context, From 4f8dfb22e2e9f31b21c87397718fa532e7f4ddef Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 17 Jul 2024 16:49:24 +0400 Subject: [PATCH 33/72] Don't declare ACDC subscriptions support when vaulting is disabled --- .../ppcp-wc-subscriptions/src/WcSubscriptionsModule.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index af68dbded..835ec4d4a 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -444,7 +444,12 @@ class WcSubscriptionsModule implements ModuleInterface { $subscriptions_helper = $c->get( 'wc-subscriptions.helper' ); assert( $subscriptions_helper instanceof SubscriptionHelper ); - if ( $subscriptions_helper->plugin_is_active() ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + $vaulting_enabled = $settings->has( 'vault_enabled_dcc' ) && $settings->get( 'vault_enabled_dcc' ); + + if ( $vaulting_enabled && $subscriptions_helper->plugin_is_active() ) { $supports = array( 'subscriptions', 'subscription_cancellation', From 40805c1a133f526954f8f5a96b41176413dc43bd Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Jul 2024 15:16:58 +0200 Subject: [PATCH 34/72] Add googlepay button to pay for order page --- .../resources/js/GooglepayButton.js | 5 ++++- modules/ppcp-googlepay/src/GooglepayModule.php | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 494373a57..9de1cb0b2 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -62,7 +62,10 @@ class GooglepayButton { ) .then( ( response ) => { if ( response.result ) { - if ( this.context === 'checkout' ) { + if ( + this.context === 'checkout' || + this.context === 'pay-now' + ) { const wrapper = document.getElementById( 'ppc-button-ppcp-googlepay' ); diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index 6d787ce5e..b7feedc07 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -187,9 +187,19 @@ class GooglepayModule implements ModuleInterface { } ); - add_action('woocommerce_review_order_after_submit', function () { - echo '
'; - }); + add_action( + 'woocommerce_review_order_after_submit', + function () { + echo '
'; + } + ); + + add_action( + 'woocommerce_pay_order_after_submit', + function () { + echo '
'; + } + ); } /** From 39ffbaa6e73e28156f36877ce9147a63320d6f40 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Jul 2024 15:23:10 +0200 Subject: [PATCH 35/72] Add googlepay and apm css classes to button wrapper --- modules/ppcp-googlepay/resources/js/GooglepayButton.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 9de1cb0b2..4c7dc3185 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -74,7 +74,11 @@ class GooglepayButton { const { ppcpStyle, buttonStyle } = this.contextConfig(); - wrapper.className = `ppcp-button-${ ppcpStyle.shape }`; + wrapper.classList.add( + `ppcp-button-${ ppcpStyle.shape }`, + 'ppcp-button-apm', + 'ppcp-button-googlepay' + ); if ( ppcpStyle.height ) { wrapper.style.height = `${ ppcpStyle.height }px`; From e547d7e09703066ac0c5e9d26f07093b220d4f87 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Jul 2024 16:39:53 +0200 Subject: [PATCH 36/72] Show button in PayPal gateway if Google Pay gateway is disabled --- .../resources/js/GooglepayButton.js | 5 +++-- modules/ppcp-googlepay/src/Assets/Button.php | 19 ++++++++++++------- .../ppcp-googlepay/src/GooglePayGateway.php | 8 ++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 4c7dc3185..8b74c780a 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -63,8 +63,9 @@ class GooglepayButton { .then( ( response ) => { if ( response.result ) { if ( - this.context === 'checkout' || - this.context === 'pay-now' + ( this.context === 'checkout' || + this.context === 'pay-now' ) && + this.buttonConfig.is_wc_gateway_enabled === '1' ) { const wrapper = document.getElementById( 'ppc-button-ppcp-googlepay' diff --git a/modules/ppcp-googlepay/src/Assets/Button.php b/modules/ppcp-googlepay/src/Assets/Button.php index 258b862cc..bd6faea79 100644 --- a/modules/ppcp-googlepay/src/Assets/Button.php +++ b/modules/ppcp-googlepay/src/Assets/Button.php @@ -15,6 +15,7 @@ use WC_Countries; use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint; +use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; @@ -424,19 +425,23 @@ class Button implements ButtonInterface { $is_enabled = $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' ); + $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); + $is_wc_gateway_enabled = isset( $available_gateways[ GooglePayGateway::ID ] ); + return array( - 'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION', - 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'is_enabled' => $is_enabled, - 'sdk_url' => $this->sdk_url, - 'button' => array( + 'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION', + 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'is_enabled' => $is_enabled, + 'is_wc_gateway_enabled' => $is_wc_gateway_enabled, + 'sdk_url' => $this->sdk_url, + 'button' => array( 'wrapper' => '#ppc-button-googlepay-container', 'style' => $this->button_styles_for_context( 'cart' ), // For now use cart. Pass the context if necessary. 'mini_cart_wrapper' => '#ppc-button-googlepay-container-minicart', 'mini_cart_style' => $this->button_styles_for_context( 'mini-cart' ), ), - 'shipping' => $shipping, - 'ajax' => array( + 'shipping' => $shipping, + 'ajax' => array( 'update_payment_data' => array( 'endpoint' => \WC_AJAX::get_endpoint( UpdatePaymentDataEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( UpdatePaymentDataEndpoint::nonce() ), diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index 6b02560b2..349699214 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -96,6 +96,14 @@ class GooglePayGateway extends WC_Payment_Gateway { $this->refund_processor = $refund_processor; $this->transaction_url_provider = $transaction_url_provider; $this->session_handler = $session_handler; + + add_action( + 'woocommerce_update_options_payment_gateways_' . $this->id, + array( + $this, + 'process_admin_options', + ) + ); } /** From 8f4fa3562bf95fcb8a90a7374c1721cf2648cf66 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 17 Jul 2024 19:15:19 +0400 Subject: [PATCH 37/72] Add tax configurator --- .../src/Helper/WooCommerceOrderCreator.php | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 625157a05..be2ecc170 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -14,8 +14,10 @@ use WC_Cart; use WC_Order; use WC_Order_Item_Product; use WC_Order_Item_Shipping; +use WC_Product; use WC_Subscription; use WC_Subscriptions_Product; +use WC_Tax; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer; use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping; @@ -106,6 +108,7 @@ class WooCommerceOrderCreator { * @param Payer|null $payer The payer. * @param Shipping|null $shipping The shipping. * @return void + * @psalm-suppress InvalidScalarArgument */ protected function configure_line_items( WC_Order $wc_order, WC_Cart $wc_cart, ?Payer $payer, ?Shipping $shipping ): void { $cart_contents = $wc_cart->get_cart(); @@ -130,18 +133,20 @@ class WooCommerceOrderCreator { return; } - $total = $product->get_price() * $quantity; + $subtotal = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) ); $item->set_name( $product->get_name() ); - $item->set_subtotal( $total ); - $item->set_total( $total ); + $item->set_subtotal( $subtotal ); + $item->set_total( $subtotal ); + + $this->configure_taxes( $product, $item, $subtotal ); $product_id = $product->get_id(); if ( $this->is_subscription( $product_id ) ) { $subscription = $this->create_subscription( $wc_order, $product_id ); $sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee( $product ); - $subscription_total = $total + $sign_up_fee; + $subscription_total = (float) $subtotal + (float) $sign_up_fee; $item->set_subtotal( $subscription_total ); $item->set_total( $subscription_total ); @@ -282,6 +287,30 @@ class WooCommerceOrderCreator { } } + /** + * Configures the taxes. + * + * @param WC_Product $product The Product. + * @param WC_Order_Item_Product $item The line item. + * @param float|string $subtotal The subtotal. + * @return void + * @psalm-suppress InvalidScalarArgument + */ + protected function configure_taxes( WC_Product $product, WC_Order_Item_Product $item, $subtotal ): void { + $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); + $taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, true ); + + $item->set_tax_class( $product->get_tax_class() ); + $item->set_total_tax( (float) array_sum( $taxes ) ); + + foreach ( $taxes as $tax_rate_id => $tax_amount ) { + if ( $tax_amount > 0 ) { + $item->add_meta_data( 'tax_rate_id', $tax_rate_id, true ); + $item->add_meta_data( 'tax_amount', $tax_amount, true ); + } + } + } + /** * Checks if the product with given ID is WC subscription. * From 9b0bb2c8b4b46b3a39d8d85cf5c6c27af34e54f6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 11:52:04 +0200 Subject: [PATCH 38/72] Do not provide support for wc susbcriotions for now --- .../src/WcSubscriptionsModule.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index f2ccf3f0f..10b44d2fd 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -256,23 +256,6 @@ class WcSubscriptionsModule implements ModuleInterface { 10, 3 ); - - add_action( 'woocommerce_paypal_payments_before_process_order', function(WC_Order $wc_order) use ( $c ) { - $subscriptions_helper = $c->get( 'wc-subscriptions.helper' ); - assert( $subscriptions_helper instanceof SubscriptionHelper ); - - // If the customer has chosen to change the subscription payment. - if ( $subscriptions_helper->has_subscription( $wc_order->get_id() ) && $subscriptions_helper->is_subscription_change_payment() ) { - // phpcs:ignore WordPress.Security.NonceVerification.Missing - $saved_paypal_payment = wc_clean( wp_unslash( $_POST['saved_paypal_payment'] ?? '' ) ); - if ( $saved_paypal_payment ) { - $wc_order->update_meta_data( 'payment_token_id', $saved_paypal_payment ); - $wc_order->save(); - - return $this->handle_payment_success( $wc_order ); - } - } - }); } /** From e592bbb47b9daab911ed94f1d7b9a09656bd76a3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 12:10:34 +0200 Subject: [PATCH 39/72] Do not provide support for wc susbcriotions for now --- modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php index 10b44d2fd..df31b45bc 100644 --- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php +++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php @@ -23,7 +23,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; -use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; @@ -34,7 +33,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; */ class WcSubscriptionsModule implements ModuleInterface { - use TransactionIdHandlingTrait, ProcessPaymentTrait; + use TransactionIdHandlingTrait; /** * {@inheritDoc} From 86e0007ae6b7a56c9f0851150b5de12a3d5844e9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 12:53:41 +0200 Subject: [PATCH 40/72] Add gateway icon, title and description --- modules/ppcp-googlepay/services.php | 5 +++-- .../ppcp-googlepay/src/GooglePayGateway.php | 22 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index 6997976bf..11cbd740b 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -938,13 +938,14 @@ return array( esc_html( $button_text ) ); }, - 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { + 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { return new GooglePayGateway( $container->get( 'wcgateway.order-processor' ), $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.processor.refunds' ), $container->get( 'wcgateway.transaction-url-provider' ), - $container->get( 'session.handler' ) + $container->get( 'session.handler' ), + $container->get( 'googlepay.url' ) ); }, ); diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index 349699214..bc5e209b6 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -65,6 +65,13 @@ class GooglePayGateway extends WC_Payment_Gateway { */ protected $session_handler; + /** + * The URL to the module. + * + * @var string + */ + private $module_url; + /** * GooglePayGateway constructor. * @@ -73,21 +80,26 @@ class GooglePayGateway extends WC_Payment_Gateway { * @param RefundProcessor $refund_processor The Refund Processor. * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. * @param SessionHandler $session_handler The Session Handler. + * @param string $module_url The URL to the module. */ public function __construct( OrderProcessor $order_processor, callable $paypal_checkout_url_factory, RefundProcessor $refund_processor, TransactionUrlProvider $transaction_url_provider, - SessionHandler $session_handler + SessionHandler $session_handler, + string $module_url ) { $this->id = self::ID; - $this->method_title = __( 'Google Pay', 'woocommerce-paypal-payments' ); - $this->method_description = __( 'Google Pay', 'woocommerce-paypal-payments' ); + $this->method_title = __( 'Google Pay (via PayPal) ', 'woocommerce-paypal-payments' ); + $this->method_description = __( 'The separate payment gateway with the Google Pay button. If disabled, the button is included in the PayPal gateway.', 'woocommerce-paypal-payments' ); - $this->title = $this->get_option( 'title', $this->method_title ); - $this->description = $this->get_option( 'description', $this->method_description ); + $this->title = $this->get_option( 'title', __( 'Google Pay', 'woocommerce-paypal-payments' ) ); + $this->description = $this->get_option( 'description', '' ); + + $this->module_url = $module_url; + $this->icon = esc_url( $this->module_url ) . 'assets/images/googlepay.png'; $this->init_form_fields(); $this->init_settings(); From 2249ebd8119260f80119c7762ed7641893142625 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 14:35:11 +0200 Subject: [PATCH 41/72] Show settings tabs in googlepay gateway settings page --- modules/ppcp-wc-gateway/services.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index f0d848216..ce41619ca 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -20,6 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers; use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; +use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Onboarding\State; @@ -196,6 +197,7 @@ return array( OXXOGateway::ID, Settings::PAY_LATER_TAB_ID, AxoGateway::ID, + GooglePayGateway::ID, ), true ); @@ -217,6 +219,7 @@ return array( CardButtonGateway::ID, Settings::PAY_LATER_TAB_ID, Settings::CONNECTION_TAB_ID, + GooglePayGateway::ID, ), true ); From 93506fe5b743b8214b8aa286e32d1b38b141e6fe Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 14:42:03 +0200 Subject: [PATCH 42/72] Revert webhook handling for now so psalm does not complain --- modules/ppcp-googlepay/src/GooglePayGateway.php | 9 +++++++++ modules/ppcp-webhooks/src/WebhookModule.php | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-googlepay/src/GooglePayGateway.php b/modules/ppcp-googlepay/src/GooglePayGateway.php index bc5e209b6..26a84f326 100644 --- a/modules/ppcp-googlepay/src/GooglePayGateway.php +++ b/modules/ppcp-googlepay/src/GooglePayGateway.php @@ -164,6 +164,15 @@ class GooglePayGateway extends WC_Payment_Gateway { ); } + /** + * If the WC_Order is paid through the approved webhook. + */ + //phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) { + return $this->handle_payment_success( $wc_order ); + } + //phpcs:enable WordPress.Security.NonceVerification.Recommended + do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order ); try { diff --git a/modules/ppcp-webhooks/src/WebhookModule.php b/modules/ppcp-webhooks/src/WebhookModule.php index f6343f0c2..3a9af0a57 100644 --- a/modules/ppcp-webhooks/src/WebhookModule.php +++ b/modules/ppcp-webhooks/src/WebhookModule.php @@ -162,15 +162,6 @@ class WebhookModule implements ModuleInterface { ); } ); - - // If the WC_Order is paid through the approved webhook. - add_action( 'woocommerce_paypal_payments_before_process_order', function(WC_Order $wc_order) { - //phpcs:disable WordPress.Security.NonceVerification.Recommended - if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) { - return $this->handle_payment_success( $wc_order ); - } - //phpcs:enable - }); } /** From 467960f5a23e0c9bd71fd2e325213728005de815 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 14:49:01 +0200 Subject: [PATCH 43/72] Revert webhook handling for now so psalm does not complain --- modules/ppcp-webhooks/src/WebhookModule.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/ppcp-webhooks/src/WebhookModule.php b/modules/ppcp-webhooks/src/WebhookModule.php index 3a9af0a57..939a8d512 100644 --- a/modules/ppcp-webhooks/src/WebhookModule.php +++ b/modules/ppcp-webhooks/src/WebhookModule.php @@ -17,7 +17,6 @@ use Exception; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint; @@ -29,8 +28,6 @@ use WooCommerce\PayPalCommerce\Webhooks\Status\Assets\WebhooksStatusPageAssets; */ class WebhookModule implements ModuleInterface { - use ProcessPaymentTrait; - /** * {@inheritDoc} */ From 3f02d8326aa710d1766d67c620c397b4c91b7de0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 22 Jul 2024 16:25:35 +0200 Subject: [PATCH 44/72] Do not override `settings` base class property --- .../src/AdvancedCardPaymentMethod.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 2c6fd60f4..df113f8f0 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -52,7 +52,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * * @var Settings */ - protected $settings; + protected $plugin_settings; /** * AdvancedCardPaymentMethod constructor. @@ -70,12 +70,12 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { $smart_button, Settings $settings ) { - $this->name = CreditCardGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; - $this->smart_button = $smart_button; - $this->settings = $settings; + $this->name = CreditCardGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; + $this->plugin_settings = $settings; } /** @@ -118,8 +118,8 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { 'scriptData' => $script_data, 'supports' => $this->gateway->supports, 'save_card_text' => esc_html__( 'Save your card', 'woocommerce-paypal-payments' ), - 'is_vaulting_enabled' => $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ), - 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), + 'is_vaulting_enabled' => $this->plugin_settings->has( 'vault_enabled_dcc' ) && $this->plugin_settings->get( 'vault_enabled_dcc' ), + 'card_icons' => $this->plugin_settings->has( 'card_icons' ) ? (array) $this->plugin_settings->get( 'card_icons' ) : array(), ); } From ffefce57b13679ccb1a6d86091bad85a78a60958 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Mon, 22 Jul 2024 17:09:21 -0300 Subject: [PATCH 45/72] woorelease: Product version bump update --- changelog.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index e980e51a0..aaf167948 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ *** Changelog *** -= 2.8.2 - xxxx-xx-xx = += 2.8.2 - 2024-07-22 = * Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415 * Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400 * Fix - W3 Total Cache: Remove type from file parameter as sometimes null gets passed causing errors #2403 diff --git a/readme.txt b/readme.txt index ffe94e2a5..e701a54d2 100644 --- a/readme.txt +++ b/readme.txt @@ -179,7 +179,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == -= 2.8.2 - xxxx-xx-xx = += 2.8.2 - 2024-07-22 = * Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415 * Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400 * Fix - W3 Total Cache: Remove type from file parameter as sometimes null gets passed causing errors #2403 From 548c0082eb6c0851f5cc0d32f80fbc7d246a5e73 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 29 Jul 2024 13:11:54 +0400 Subject: [PATCH 46/72] Add China to GPay support, simplify country-currency matrix --- modules/ppcp-googlepay/services.php | 834 ++---------------- .../ppcp-googlepay/src/Helper/ApmApplies.php | 42 +- 2 files changed, 105 insertions(+), 771 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index 11cbd740b..c1ef967a1 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -29,12 +29,13 @@ return array( $apm_applies = $container->get( 'googlepay.helpers.apm-applies' ); assert( $apm_applies instanceof ApmApplies ); - return $apm_applies->for_country_currency(); + return $apm_applies->for_country() && $apm_applies->for_currency(); }, 'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { return new ApmApplies( - $container->get( 'googlepay.supported-country-currency-matrix' ), + $container->get( 'googlepay.supported-countries' ), + $container->get( 'googlepay.supported-currencies' ), $container->get( 'api.shop.currency' ), $container->get( 'api.shop.country' ) ); @@ -81,767 +82,84 @@ return array( ), /** - * The matrix which countries and currency combinations can be used for GooglePay. + * The list of which countries can be used for GooglePay. */ - 'googlepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array { + 'googlepay.supported-countries' => static function ( ContainerInterface $container) : array { /** - * Returns which countries and currency combinations can be used for GooglePay. + * Returns which countries can be used for GooglePay. */ return apply_filters( - 'woocommerce_paypal_payments_googlepay_supported_country_currency_matrix', + 'woocommerce_paypal_payments_googlepay_supported_countries', array( - 'AU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'AT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'BE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'BG' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CA' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CY' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CZ' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'DK' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'EE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'FI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'FR' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'DE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'GR' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'HU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'IE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'IT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LV' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'MT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'NO' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'NL' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'PL' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'PT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'RO' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SK' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'ES' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'GB' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'US' => array( - 'AUD', - 'CAD', - 'EUR', - 'GBP', - 'JPY', - 'USD', - ), + 'AU', // Australia + 'AT', // Austria + 'BE', // Belgium + 'BG', // Bulgaria + 'CA', // Canada + 'CN', // China + 'CY', // Cyprus + 'CZ', // Czech Republic + 'DK', // Denmark + 'EE', // Estonia + 'FI', // Finland + 'FR', // France + 'DE', // Germany + 'GR', // Greece + 'HU', // Hungary + 'IE', // Ireland + 'IT', // Italy + 'LV', // Latvia + 'LI', // Liechtenstein + 'LT', // Lithuania + 'LU', // Luxembourg + 'MT', // Malta + 'NL', // Netherlands + 'NO', // Norway + 'PL', // Poland + 'PT', // Portugal + 'RO', // Romania + 'SK', // Slovakia + 'SI', // Slovenia + 'ES', // Spain + 'SE', // Sweden + 'US', // United States + 'GB' // United Kingdom + ) + ); + }, + + /** + * The list of which currencies can be used for GooglePay. + */ + 'googlepay.supported-currencies' => static function ( ContainerInterface $container ) : array { + /** + * Returns which currencies can be used for GooglePay. + */ + return apply_filters( + 'woocommerce_paypal_payments_googlepay_supported_currencies', + array( + 'AUD', // Australian Dollar + 'BRL', // Brazilian Real + 'CAD', // Canadian Dollar + 'CHF', // Swiss Franc + 'CZK', // Czech Koruna + 'DKK', // Danish Krone + 'EUR', // Euro + 'GBP', // British Pound Sterling + 'HKD', // Hong Kong Dollar + 'HUF', // Hungarian Forint + 'ILS', // Israeli New Shekel + 'JPY', // Japanese Yen + 'MXN', // Mexican Peso + 'NOK', // Norwegian Krone + 'NZD', // New Zealand Dollar + 'PHP', // Philippine Peso + 'PLN', // Polish Zloty + 'SEK', // Swedish Krona + 'SGD', // Singapore Dollar + 'THB', // Thai Baht + 'TWD', // New Taiwan Dollar + 'USD' // United States Dollar ) ); }, diff --git a/modules/ppcp-googlepay/src/Helper/ApmApplies.php b/modules/ppcp-googlepay/src/Helper/ApmApplies.php index 84ad2e057..96a16b210 100644 --- a/modules/ppcp-googlepay/src/Helper/ApmApplies.php +++ b/modules/ppcp-googlepay/src/Helper/ApmApplies.php @@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper; class ApmApplies { /** - * The matrix which countries and currency combinations can be used for GooglePay. + * The list of which countries can be used for GooglePay. * * @var array */ - private $allowed_country_currency_matrix; + private $allowed_countries; + + /** + * The list of which currencies can be used for GooglePay. + * + * @var array + */ + private $allowed_currencies; /** * 3-letter currency code of the shop. @@ -39,30 +46,39 @@ class ApmApplies { /** * DccApplies constructor. * - * @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for GooglePay. + * @param array $allowed_countries The list of which countries can be used for GooglePay. + * @param array $allowed_currencies The list of which currencies can be used for GooglePay. * @param string $currency 3-letter currency code of the shop. * @param string $country 2-letter country code of the shop. */ public function __construct( - array $allowed_country_currency_matrix, + array $allowed_countries, + array $allowed_currencies, string $currency, string $country ) { - $this->allowed_country_currency_matrix = $allowed_country_currency_matrix; - $this->currency = $currency; - $this->country = $country; + $this->allowed_countries = $allowed_countries; + $this->allowed_currencies = $allowed_currencies; + $this->currency = $currency; + $this->country = $country; } /** - * Returns whether GooglePay can be used in the current country and the current currency used. + * Returns whether GooglePay can be used in the current country used. * * @return bool */ - public function for_country_currency(): bool { - if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) { - return false; - } - return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); + public function for_country(): bool { + return in_array( $this->country, $this->allowed_countries, true ); + } + + /** + * Returns whether GooglePay can be used in the current currency used. + * + * @return bool + */ + public function for_currency(): bool { + return in_array( $this->currency, $this->allowed_currencies, true ); } } From a06c01ad1f631497e12e92f7c2deffdbe33b3033 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 29 Jul 2024 13:13:56 +0400 Subject: [PATCH 47/72] phpcs fix --- modules/ppcp-googlepay/services.php | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index c1ef967a1..e496ae629 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -25,14 +25,14 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( // If GooglePay can be configured. - 'googlepay.eligible' => static function ( ContainerInterface $container ): bool { + 'googlepay.eligible' => static function ( ContainerInterface $container ): bool { $apm_applies = $container->get( 'googlepay.helpers.apm-applies' ); assert( $apm_applies instanceof ApmApplies ); return $apm_applies->for_country() && $apm_applies->for_currency(); }, - 'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { + 'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { return new ApmApplies( $container->get( 'googlepay.supported-countries' ), $container->get( 'googlepay.supported-currencies' ), @@ -42,7 +42,7 @@ return array( }, // If GooglePay is configured and onboarded. - 'googlepay.available' => static function ( ContainerInterface $container ): bool { + 'googlepay.available' => static function ( ContainerInterface $container ): bool { if ( apply_filters( 'woocommerce_paypal_payments_googlepay_validate_product_status', true ) ) { $status = $container->get( 'googlepay.helpers.apm-product-status' ); assert( $status instanceof ApmProductStatus ); @@ -55,14 +55,14 @@ return array( }, // We assume it's a referral if we can check product status without API request failures. - 'googlepay.is_referral' => static function ( ContainerInterface $container ): bool { + 'googlepay.is_referral' => static function ( ContainerInterface $container ): bool { $status = $container->get( 'googlepay.helpers.apm-product-status' ); assert( $status instanceof ApmProductStatus ); return ! $status->has_request_failure(); }, - 'googlepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice { + 'googlepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice { return new AvailabilityNotice( $container->get( 'googlepay.helpers.apm-product-status' ), $container->get( 'wcgateway.is-wc-gateways-list-page' ), @@ -70,7 +70,7 @@ return array( ); }, - 'googlepay.helpers.apm-product-status' => SingletonDecorator::make( + 'googlepay.helpers.apm-product-status' => SingletonDecorator::make( static function( ContainerInterface $container ): ApmProductStatus { return new ApmProductStatus( $container->get( 'wcgateway.settings' ), @@ -84,7 +84,7 @@ return array( /** * The list of which countries can be used for GooglePay. */ - 'googlepay.supported-countries' => static function ( ContainerInterface $container) : array { + 'googlepay.supported-countries' => static function ( ContainerInterface $container ) : array { /** * Returns which countries can be used for GooglePay. */ @@ -123,7 +123,7 @@ return array( 'ES', // Spain 'SE', // Sweden 'US', // United States - 'GB' // United Kingdom + 'GB', // United Kingdom ) ); }, @@ -131,7 +131,7 @@ return array( /** * The list of which currencies can be used for GooglePay. */ - 'googlepay.supported-currencies' => static function ( ContainerInterface $container ) : array { + 'googlepay.supported-currencies' => static function ( ContainerInterface $container ) : array { /** * Returns which currencies can be used for GooglePay. */ @@ -159,12 +159,12 @@ return array( 'SGD', // Singapore Dollar 'THB', // Thai Baht 'TWD', // New Taiwan Dollar - 'USD' // United States Dollar + 'USD', // United States Dollar ) ); }, - 'googlepay.button' => static function ( ContainerInterface $container ): ButtonInterface { + 'googlepay.button' => static function ( ContainerInterface $container ): ButtonInterface { return new Button( $container->get( 'googlepay.url' ), $container->get( 'googlepay.sdk_url' ), @@ -178,7 +178,7 @@ return array( ); }, - 'googlepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { + 'googlepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { return new BlocksPaymentMethod( 'ppcp-googlepay', $container->get( 'googlepay.url' ), @@ -188,7 +188,7 @@ return array( ); }, - 'googlepay.url' => static function ( ContainerInterface $container ): string { + 'googlepay.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { return ''; @@ -199,26 +199,26 @@ return array( ); }, - 'googlepay.sdk_url' => static function ( ContainerInterface $container ): string { + 'googlepay.sdk_url' => static function ( ContainerInterface $container ): string { return 'https://pay.google.com/gp/p/js/pay.js'; }, - 'googlepay.endpoint.update-payment-data' => static function ( ContainerInterface $container ): UpdatePaymentDataEndpoint { + 'googlepay.endpoint.update-payment-data' => static function ( ContainerInterface $container ): UpdatePaymentDataEndpoint { return new UpdatePaymentDataEndpoint( $container->get( 'button.request-data' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'googlepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string { + 'googlepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string { return 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY'; }, - 'googlepay.enable-url-live' => static function ( ContainerInterface $container ): string { + 'googlepay.enable-url-live' => static function ( ContainerInterface $container ): string { return 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY'; }, - 'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { + 'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { $state = $container->get( 'onboarding.state' ); if ( $state->current_state() < State::STATE_ONBOARDED ) { return ''; @@ -256,7 +256,7 @@ return array( esc_html( $button_text ) ); }, - 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { + 'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway { return new GooglePayGateway( $container->get( 'wcgateway.order-processor' ), $container->get( 'api.factory.paypal-checkout-url' ), From 883a5b15bcea94c4b2ccd8cfd9966f720530b61e Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 29 Jul 2024 13:28:33 +0400 Subject: [PATCH 48/72] Add China to ApplePay support, simplify country-currency matrix --- modules/ppcp-applepay/services.php | 877 ++---------------- .../ppcp-applepay/src/Helper/ApmApplies.php | 44 +- 2 files changed, 130 insertions(+), 791 deletions(-) diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 6079fcb40..e4a3297dc 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -24,32 +24,33 @@ use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( - 'applepay.eligible' => static function ( ContainerInterface $container ): bool { + 'applepay.eligible' => static function ( ContainerInterface $container ): bool { $apm_applies = $container->get( 'applepay.helpers.apm-applies' ); assert( $apm_applies instanceof ApmApplies ); - return $apm_applies->for_country_currency(); + return $apm_applies->for_country() && $apm_applies->for_currency(); }, - 'applepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { + 'applepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { return new ApmApplies( - $container->get( 'applepay.supported-country-currency-matrix' ), + $container->get( 'applepay.supported-countries' ), + $container->get( 'applepay.supported-currencies' ), $container->get( 'api.shop.currency' ), $container->get( 'api.shop.country' ) ); }, - 'applepay.status-cache' => static function( ContainerInterface $container ): Cache { + 'applepay.status-cache' => static function( ContainerInterface $container ): Cache { return new Cache( 'ppcp-paypal-apple-status-cache' ); }, // We assume it's a referral if we can check product status without API request failures. - 'applepay.is_referral' => static function ( ContainerInterface $container ): bool { + 'applepay.is_referral' => static function ( ContainerInterface $container ): bool { $status = $container->get( 'applepay.apple-product-status' ); assert( $status instanceof AppleProductStatus ); return ! $status->has_request_failure(); }, - 'applepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice { + 'applepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice { $settings = $container->get( 'wcgateway.settings' ); return new AvailabilityNotice( @@ -63,17 +64,17 @@ return array( ); }, - 'applepay.has_validated' => static function ( ContainerInterface $container ): bool { + 'applepay.has_validated' => static function ( ContainerInterface $container ): bool { $settings = $container->get( 'wcgateway.settings' ); return $settings->has( 'applepay_validated' ); }, - 'applepay.is_validated' => static function ( ContainerInterface $container ): bool { + 'applepay.is_validated' => static function ( ContainerInterface $container ): bool { $settings = $container->get( 'wcgateway.settings' ); return $settings->has( 'applepay_validated' ) ? $settings->get( 'applepay_validated' ) === true : false; }, - 'applepay.apple-product-status' => SingletonDecorator::make( + 'applepay.apple-product-status' => SingletonDecorator::make( static function( ContainerInterface $container ): AppleProductStatus { return new AppleProductStatus( $container->get( 'wcgateway.settings' ), @@ -83,7 +84,7 @@ return array( ); } ), - 'applepay.available' => static function ( ContainerInterface $container ): bool { + 'applepay.available' => static function ( ContainerInterface $container ): bool { if ( apply_filters( 'woocommerce_paypal_payments_applepay_validate_product_status', true ) ) { $status = $container->get( 'applepay.apple-product-status' ); assert( $status instanceof AppleProductStatus ); @@ -94,10 +95,10 @@ return array( } return true; }, - 'applepay.server_supported' => static function ( ContainerInterface $container ): bool { + 'applepay.server_supported' => static function ( ContainerInterface $container ): bool { return ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off'; }, - 'applepay.is_browser_supported' => static function ( ContainerInterface $container ): bool { + 'applepay.is_browser_supported' => static function ( ContainerInterface $container ): bool { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' ); if ( $user_agent ) { @@ -127,7 +128,7 @@ return array( } return false; }, - 'applepay.url' => static function ( ContainerInterface $container ): string { + 'applepay.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { return ''; @@ -137,13 +138,13 @@ return array( dirname( $path, 3 ) . '/woocommerce-paypal-payments.php' ); }, - 'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string { + 'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string { return 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js'; }, - 'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts { + 'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts { return new DataToAppleButtonScripts( $container->get( 'applepay.sdk_script_url' ), $container->get( 'wcgateway.settings' ) ); }, - 'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton { + 'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton { return new ApplePayButton( $container->get( 'wcgateway.settings' ), $container->get( 'woocommerce.logger.woocommerce' ), @@ -155,7 +156,7 @@ return array( $container->get( 'button.helper.cart-products' ) ); }, - 'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { + 'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { return new BlocksPaymentMethod( 'ppcp-applepay', $container->get( 'applepay.url' ), @@ -164,781 +165,103 @@ return array( $container->get( 'blocks.method' ) ); }, + /** - * The matrix which countries and currency combinations can be used for ApplePay. + * The list of which countries can be used for ApplePay. */ - 'applepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array { + 'applepay.supported-countries' => static function ( ContainerInterface $container ) : array { /** - * Returns which countries and currency combinations can be used for ApplePay. + * Returns which countries can be used for ApplePay. */ return apply_filters( - 'woocommerce_paypal_payments_applepay_supported_country_currency_matrix', + 'woocommerce_paypal_payments_applepay_supported_countries', + // phpcs:disable Squiz.Commenting.InlineComment array( - 'AU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'AT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'BE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'BG' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CA' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CY' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'CZ' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'DK' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'EE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'FI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'FR' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'DE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'GR' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'HU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'IE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'IT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LV' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'LU' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'MT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'NO' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'NL' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'PL' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'PT' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'RO' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SK' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SI' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'ES' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'SE' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'GB' => array( - 'AUD', - 'BRL', - 'CAD', - 'CHF', - 'CZK', - 'DKK', - 'EUR', - 'GBP', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MXN', - 'NOK', - 'NZD', - 'PHP', - 'PLN', - 'SEK', - 'SGD', - 'THB', - 'TWD', - 'USD', - ), - 'US' => array( - 'AUD', - 'CAD', - 'EUR', - 'GBP', - 'JPY', - 'USD', - ), + 'AU', // Australia + 'AT', // Austria + 'BE', // Belgium + 'BG', // Bulgaria + 'CA', // Canada + 'CN', // China + 'CY', // Cyprus + 'CZ', // Czech Republic + 'DK', // Denmark + 'EE', // Estonia + 'FI', // Finland + 'FR', // France + 'DE', // Germany + 'GR', // Greece + 'HU', // Hungary + 'IE', // Ireland + 'IT', // Italy + 'LV', // Latvia + 'LI', // Liechtenstein + 'LT', // Lithuania + 'LU', // Luxembourg + 'MT', // Malta + 'NL', // Netherlands + 'NO', // Norway + 'PL', // Poland + 'PT', // Portugal + 'RO', // Romania + 'SK', // Slovakia + 'SI', // Slovenia + 'ES', // Spain + 'SE', // Sweden + 'US', // United States + 'GB', // United Kingdom ) + // phpcs:enable Squiz.Commenting.InlineComment ); }, - 'applepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string { + /** + * The list of which currencies can be used for ApplePay. + */ + 'applepay.supported-currencies' => static function ( ContainerInterface $container ) : array { + /** + * Returns which currencies can be used for ApplePay. + */ + return apply_filters( + 'woocommerce_paypal_payments_applepay_supported_currencies', + // phpcs:disable Squiz.Commenting.InlineComment + array( + 'AUD', // Australian Dollar + 'BRL', // Brazilian Real + 'CAD', // Canadian Dollar + 'CHF', // Swiss Franc + 'CZK', // Czech Koruna + 'DKK', // Danish Krone + 'EUR', // Euro + 'GBP', // British Pound Sterling + 'HKD', // Hong Kong Dollar + 'HUF', // Hungarian Forint + 'ILS', // Israeli New Shekel + 'JPY', // Japanese Yen + 'MXN', // Mexican Peso + 'NOK', // Norwegian Krone + 'NZD', // New Zealand Dollar + 'PHP', // Philippine Peso + 'PLN', // Polish Zloty + 'SEK', // Swedish Krona + 'SGD', // Singapore Dollar + 'THB', // Thai Baht + 'TWD', // New Taiwan Dollar + 'USD', // United States Dollar + ) + // phpcs:enable Squiz.Commenting.InlineComment + ); + }, + + 'applepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string { return 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY'; }, - 'applepay.enable-url-live' => static function ( ContainerInterface $container ): string { + 'applepay.enable-url-live' => static function ( ContainerInterface $container ): string { return 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY'; }, - 'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { + 'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { $state = $container->get( 'onboarding.state' ); if ( $state->current_state() < State::STATE_ONBOARDED ) { return ''; diff --git a/modules/ppcp-applepay/src/Helper/ApmApplies.php b/modules/ppcp-applepay/src/Helper/ApmApplies.php index 0fa12420b..4deeade14 100644 --- a/modules/ppcp-applepay/src/Helper/ApmApplies.php +++ b/modules/ppcp-applepay/src/Helper/ApmApplies.php @@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Applepay\Helper; class ApmApplies { /** - * The matrix which countries and currency combinations can be used for ApplePay. + * The list of which countries can be used for ApplePay. * * @var array */ - private $allowed_country_currency_matrix; + private $allowed_countries; + + /** + * The list of which currencies can be used for ApplePay. + * + * @var array + */ + private $allowed_currencies; /** * 3-letter currency code of the shop. @@ -37,32 +44,41 @@ class ApmApplies { private $country; /** - * ApmApplies constructor. + * DccApplies constructor. * - * @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for ApplePay. + * @param array $allowed_countries The list of which countries can be used for ApplePay. + * @param array $allowed_currencies The list of which currencies can be used for ApplePay. * @param string $currency 3-letter currency code of the shop. * @param string $country 2-letter country code of the shop. */ public function __construct( - array $allowed_country_currency_matrix, + array $allowed_countries, + array $allowed_currencies, string $currency, string $country ) { - $this->allowed_country_currency_matrix = $allowed_country_currency_matrix; - $this->currency = $currency; - $this->country = $country; + $this->allowed_countries = $allowed_countries; + $this->allowed_currencies = $allowed_currencies; + $this->currency = $currency; + $this->country = $country; } /** - * Returns whether ApplePay can be used in the current country and the current currency used. + * Returns whether ApplePay can be used in the current country used. * * @return bool */ - public function for_country_currency(): bool { - if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) { - return false; - } - return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); + public function for_country(): bool { + return in_array( $this->country, $this->allowed_countries, true ); + } + + /** + * Returns whether ApplePay can be used in the current currency used. + * + * @return bool + */ + public function for_currency(): bool { + return in_array( $this->currency, $this->allowed_currencies, true ); } } From 5252888721d679c997394087996ab71156cdadb1 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 29 Jul 2024 13:29:48 +0400 Subject: [PATCH 49/72] phpcs fix --- modules/ppcp-googlepay/services.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index e496ae629..110b56937 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -90,6 +90,7 @@ return array( */ return apply_filters( 'woocommerce_paypal_payments_googlepay_supported_countries', + // phpcs:disable Squiz.Commenting.InlineComment array( 'AU', // Australia 'AT', // Austria @@ -125,6 +126,7 @@ return array( 'US', // United States 'GB', // United Kingdom ) + // phpcs:enable Squiz.Commenting.InlineComment ); }, @@ -137,6 +139,7 @@ return array( */ return apply_filters( 'woocommerce_paypal_payments_googlepay_supported_currencies', + // phpcs:disable Squiz.Commenting.InlineComment array( 'AUD', // Australian Dollar 'BRL', // Brazilian Real @@ -161,6 +164,7 @@ return array( 'TWD', // New Taiwan Dollar 'USD', // United States Dollar ) + // phpcs:enable Squiz.Commenting.InlineComment ); }, From 06378c34c880deeae455af728409437a01d969c2 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 29 Jul 2024 14:15:35 +0400 Subject: [PATCH 50/72] Add Amex support for ACDC China --- modules/ppcp-api-client/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index dbdf2efe3..241240ce2 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -1443,6 +1443,7 @@ return array( 'CN' => array( 'mastercard' => array(), 'visa' => array(), + 'amex' => array(), ), 'CY' => array( 'mastercard' => array(), From a7c43ab0d125931220cf2ea1ab4220ba64e9fc29 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 29 Jul 2024 15:55:29 +0400 Subject: [PATCH 51/72] Do not add tex info into order meta during order creation --- modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 5ff2b02e6..888a567c5 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -303,13 +303,6 @@ class WooCommerceOrderCreator { $item->set_tax_class( $product->get_tax_class() ); $item->set_total_tax( (float) array_sum( $taxes ) ); - - foreach ( $taxes as $tax_rate_id => $tax_amount ) { - if ( $tax_amount > 0 ) { - $item->add_meta_data( 'tax_rate_id', $tax_rate_id, true ); - $item->add_meta_data( 'tax_amount', $tax_amount, true ); - } - } } /** From 5ad73cf5f0e741a7f87abc3cfa2418f094f70164 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 30 Jul 2024 10:24:26 +0200 Subject: [PATCH 52/72] Google Pay: Prevent field validation from being triggered on checkout page load --- .../resources/js/Context/CheckoutHandler.js | 6 ++--- .../resources/js/GooglepayButton.js | 25 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js b/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js index 251b4f3c4..82d04f578 100644 --- a/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js +++ b/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js @@ -4,7 +4,7 @@ import CheckoutActionHandler from '../../../../ppcp-button/resources/js/modules/ import FormValidator from '../../../../ppcp-button/resources/js/modules/Helper/FormValidator'; class CheckoutHandler extends BaseHandler { - transactionInfo() { + formValidator() { return new Promise( async ( resolve, reject ) => { try { const spinner = new Spinner(); @@ -23,7 +23,7 @@ class CheckoutHandler extends BaseHandler { : null; if ( ! formValidator ) { - resolve( super.transactionInfo() ); + resolve(); return; } @@ -42,7 +42,7 @@ class CheckoutHandler extends BaseHandler { reject(); } else { - resolve( super.transactionInfo() ); + resolve(); } } ); } catch ( error ) { diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 87bc642f0..2c15571b3 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -292,17 +292,24 @@ class GooglepayButton { onButtonClick() { this.log( 'onButtonClick', this.context ); - const paymentDataRequest = this.paymentDataRequest(); + this.contextHandler.formValidator().then( + () => { + window.ppcpFundingSource = 'googlepay'; - this.log( - 'onButtonClick: paymentDataRequest', - paymentDataRequest, - this.context + const paymentDataRequest = this.paymentDataRequest(); + + this.log( + 'onButtonClick: paymentDataRequest', + paymentDataRequest, + this.context + ); + + this.paymentsClient.loadPaymentData( paymentDataRequest ); + }, + () => { + console.error( '[GooglePayButton] Form validation failed.' ); + } ); - - window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler. - - this.paymentsClient.loadPaymentData( paymentDataRequest ); } paymentDataRequest() { From a301d901dd4a530032769e37f55f407d9e44ad37 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 30 Jul 2024 10:31:44 +0200 Subject: [PATCH 53/72] Google Pay: Update the formValidator name --- modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js | 2 +- modules/ppcp-googlepay/resources/js/GooglepayButton.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js b/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js index 82d04f578..b4a07709e 100644 --- a/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js +++ b/modules/ppcp-googlepay/resources/js/Context/CheckoutHandler.js @@ -4,7 +4,7 @@ import CheckoutActionHandler from '../../../../ppcp-button/resources/js/modules/ import FormValidator from '../../../../ppcp-button/resources/js/modules/Helper/FormValidator'; class CheckoutHandler extends BaseHandler { - formValidator() { + validateForm() { return new Promise( async ( resolve, reject ) => { try { const spinner = new Spinner(); diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 2c15571b3..4363c67be 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -292,7 +292,7 @@ class GooglepayButton { onButtonClick() { this.log( 'onButtonClick', this.context ); - this.contextHandler.formValidator().then( + this.contextHandler.validateForm().then( () => { window.ppcpFundingSource = 'googlepay'; From bea17db5881dbc80c058fe7eca812f0d82ff4cdc Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 09:25:28 +0200 Subject: [PATCH 54/72] Use client credentials --- modules/ppcp-api-client/services.php | 14 +++++-- .../src/Authentication/ClientCredentials.php | 41 +++++++++++++++++++ .../src/Authentication/SdkClientToken.php | 26 ++++++------ .../src/Authentication/UserIdToken.php | 26 ++++++------ 4 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 modules/ppcp-api-client/src/Authentication/ClientCredentials.php diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 241240ce2..7cb890c18 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\ClientCredentials; use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint; @@ -1656,18 +1657,23 @@ return array( return new PurchaseUnitSanitizer( $behavior, $line_name ); } ), + 'api.client-credentials' => static function(ContainerInterface $container): ClientCredentials { + return new ClientCredentials( + $container->get( 'wcgateway.settings' ) + ); + }, 'api.user-id-token' => static function( ContainerInterface $container ): UserIdToken { return new UserIdToken( $container->get( 'api.host' ), - $container->get( 'api.bearer' ), - $container->get( 'woocommerce.logger.woocommerce' ) + $container->get( 'woocommerce.logger.woocommerce' ), + $container->get( 'api.client-credentials' ) ); }, 'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken { return new SdkClientToken( $container->get( 'api.host' ), - $container->get( 'api.bearer' ), - $container->get( 'woocommerce.logger.woocommerce' ) + $container->get( 'woocommerce.logger.woocommerce' ), + $container->get( 'api.client-credentials' ) ); }, ); diff --git a/modules/ppcp-api-client/src/Authentication/ClientCredentials.php b/modules/ppcp-api-client/src/Authentication/ClientCredentials.php new file mode 100644 index 000000000..7ec4628a5 --- /dev/null +++ b/modules/ppcp-api-client/src/Authentication/ClientCredentials.php @@ -0,0 +1,41 @@ +settings = $settings; + } + + public function credentials(): string { + $client_id = $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : ''; + $client_secret = $this->settings->has( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : ''; + + return 'Basic ' . base64_encode($client_id . ':' . $client_secret); + } +} diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index ba0f3a373..c55847d30 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -27,13 +27,6 @@ class SdkClientToken { */ private $host; - /** - * The bearer. - * - * @var Bearer - */ - private $bearer; - /** * The logger. * @@ -41,21 +34,28 @@ class SdkClientToken { */ private $logger; + /** + * The client credentials. + * + * @var ClientCredentials + */ + private $client_credentials; + /** * SdkClientToken constructor. * * @param string $host The host. - * @param Bearer $bearer The bearer. * @param LoggerInterface $logger The logger. + * @param ClientCredentials $client_credentials The client credentials. */ public function __construct( string $host, - Bearer $bearer, - LoggerInterface $logger + LoggerInterface $logger, + ClientCredentials $client_credentials ) { $this->host = $host; - $this->bearer = $bearer; $this->logger = $logger; + $this->client_credentials = $client_credentials; } /** @@ -69,8 +69,6 @@ class SdkClientToken { * @throws RuntimeException If something unexpected happens. */ public function sdk_client_token( string $target_customer_id = '' ): string { - $bearer = $this->bearer->bearer(); - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ); $domain = preg_replace( '/^www\./', '', $domain ); @@ -89,7 +87,7 @@ class SdkClientToken { $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), + 'Authorization' => $this->client_credentials->credentials(), 'Content-Type' => 'application/x-www-form-urlencoded', ), ); diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index cae8cb58a..05dc62282 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -27,13 +27,6 @@ class UserIdToken { */ private $host; - /** - * The bearer. - * - * @var Bearer - */ - private $bearer; - /** * The logger. * @@ -41,21 +34,28 @@ class UserIdToken { */ private $logger; + /** + * The client credentials. + * + * @var ClientCredentials + */ + private $client_credentials; + /** * UserIdToken constructor. * * @param string $host The host. - * @param Bearer $bearer The bearer. * @param LoggerInterface $logger The logger. + * @param ClientCredentials $client_credentials The client credentials. */ public function __construct( string $host, - Bearer $bearer, - LoggerInterface $logger + LoggerInterface $logger, + ClientCredentials $client_credentials ) { $this->host = $host; - $this->bearer = $bearer; $this->logger = $logger; + $this->client_credentials = $client_credentials; } /** @@ -69,8 +69,6 @@ class UserIdToken { * @throws RuntimeException If something unexpected happens. */ public function id_token( string $target_customer_id = '' ): string { - $bearer = $this->bearer->bearer(); - $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; if ( $target_customer_id ) { $url = add_query_arg( @@ -84,7 +82,7 @@ class UserIdToken { $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), + 'Authorization' => $this->client_credentials->credentials(), 'Content-Type' => 'application/x-www-form-urlencoded', ), ); From 54657ed649352f3d5b8172c245bc4e9499bd9104 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 10:44:54 +0200 Subject: [PATCH 55/72] Add tokens cache --- modules/ppcp-api-client/services.php | 8 +++-- .../src/Authentication/SdkClientToken.php | 32 +++++++++++++---- .../src/Authentication/UserIdToken.php | 32 +++++++++++++---- modules/ppcp-onboarding/services.php | 6 ++-- .../src/Endpoint/LoginSellerEndpoint.php | 34 ++++++++++++++----- modules/ppcp-wc-gateway/services.php | 3 +- .../src/Settings/SettingsListener.php | 20 ++++++++++- 7 files changed, 107 insertions(+), 28 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 7cb890c18..dd2c786a1 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -1657,7 +1657,7 @@ return array( return new PurchaseUnitSanitizer( $behavior, $line_name ); } ), - 'api.client-credentials' => static function(ContainerInterface $container): ClientCredentials { + 'api.client-credentials' => static function( ContainerInterface $container ): ClientCredentials { return new ClientCredentials( $container->get( 'wcgateway.settings' ) ); @@ -1666,14 +1666,16 @@ return array( return new UserIdToken( $container->get( 'api.host' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'api.client-credentials' ) + $container->get( 'api.client-credentials' ), + new Cache( 'ppcp-client-credentials-cache' ) ); }, 'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken { return new SdkClientToken( $container->get( 'api.host' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'api.client-credentials' ) + $container->get( 'api.client-credentials' ), + new Cache( 'ppcp-client-credentials-cache' ) ); }, ); diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index c55847d30..2e1f3a7ca 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WP_Error; /** @@ -20,6 +21,8 @@ class SdkClientToken { use RequestTrait; + const CACHE_KEY = 'sdk-client-token-key'; + /** * The host. * @@ -41,21 +44,31 @@ class SdkClientToken { */ private $client_credentials; + /** + * The cache. + * + * @var Cache + */ + private $cache; + /** * SdkClientToken constructor. * - * @param string $host The host. - * @param LoggerInterface $logger The logger. + * @param string $host The host. + * @param LoggerInterface $logger The logger. * @param ClientCredentials $client_credentials The client credentials. + * @param Cache $cache The cache. */ public function __construct( string $host, LoggerInterface $logger, - ClientCredentials $client_credentials + ClientCredentials $client_credentials, + Cache $cache ) { - $this->host = $host; - $this->logger = $logger; + $this->host = $host; + $this->logger = $logger; $this->client_credentials = $client_credentials; + $this->cache = $cache; } /** @@ -69,6 +82,10 @@ class SdkClientToken { * @throws RuntimeException If something unexpected happens. */ public function sdk_client_token( string $target_customer_id = '' ): string { + if ( $this->cache->has( self::CACHE_KEY ) ) { + return $this->cache->get( self::CACHE_KEY ); + } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ); $domain = preg_replace( '/^www\./', '', $domain ); @@ -103,6 +120,9 @@ class SdkClientToken { throw new PayPalApiException( $json, $status_code ); } - return $json->access_token; + $access_token = $json->access_token; + $this->cache->set( self::CACHE_KEY, $access_token ); + + return $access_token; } } diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 05dc62282..2d18d0507 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WP_Error; /** @@ -20,6 +21,8 @@ class UserIdToken { use RequestTrait; + const CACHE_KEY = 'user-id-token-key'; + /** * The host. * @@ -41,21 +44,31 @@ class UserIdToken { */ private $client_credentials; + /** + * The cache. + * + * @var Cache + */ + private $cache; + /** * UserIdToken constructor. * - * @param string $host The host. - * @param LoggerInterface $logger The logger. + * @param string $host The host. + * @param LoggerInterface $logger The logger. * @param ClientCredentials $client_credentials The client credentials. + * @param Cache $cache The cache. */ public function __construct( string $host, LoggerInterface $logger, - ClientCredentials $client_credentials + ClientCredentials $client_credentials, + Cache $cache ) { - $this->host = $host; - $this->logger = $logger; + $this->host = $host; + $this->logger = $logger; $this->client_credentials = $client_credentials; + $this->cache = $cache; } /** @@ -69,6 +82,10 @@ class UserIdToken { * @throws RuntimeException If something unexpected happens. */ public function id_token( string $target_customer_id = '' ): string { + if ( $this->cache->has( self::CACHE_KEY ) ) { + return $this->cache->get( self::CACHE_KEY ); + } + $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; if ( $target_customer_id ) { $url = add_query_arg( @@ -98,6 +115,9 @@ class UserIdToken { throw new PayPalApiException( $json, $status_code ); } - return $json->id_token; + $id_token = $json->id_token; + $this->cache->set( self::CACHE_KEY, $id_token ); + + return $id_token; } } diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index b76519f62..e7f493999 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -189,9 +189,8 @@ return array( $login_seller_sandbox = $container->get( 'api.endpoint.login-seller-sandbox' ); $partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' ); $settings = $container->get( 'wcgateway.settings' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); - $cache = new Cache( 'ppcp-paypal-bearer' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new LoginSellerEndpoint( $request_data, $login_seller_production, @@ -199,7 +198,8 @@ return array( $partner_referrals_data, $settings, $cache, - $logger + $logger, + new Cache( 'ppcp-client-credentials-cache' ) ); }, 'onboarding.endpoint.pui' => static function( ContainerInterface $container ) : UpdateSignupLinksEndpoint { diff --git a/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php b/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php index 20ab7b95a..2a30ad6b1 100644 --- a/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php +++ b/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php @@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\Onboarding\Endpoint; use Exception; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData; @@ -76,6 +78,13 @@ class LoginSellerEndpoint implements EndpointInterface { */ protected $logger; + /** + * The client credentials cache. + * + * @var Cache + */ + private $client_credentials_cache; + /** * LoginSellerEndpoint constructor. * @@ -86,6 +95,7 @@ class LoginSellerEndpoint implements EndpointInterface { * @param Settings $settings The Settings. * @param Cache $cache The Cache. * @param LoggerInterface $logger The logger. + * @param Cache $client_credentials_cache The client credentials cache. */ public function __construct( RequestData $request_data, @@ -94,16 +104,18 @@ class LoginSellerEndpoint implements EndpointInterface { PartnerReferralsData $partner_referrals_data, Settings $settings, Cache $cache, - LoggerInterface $logger + LoggerInterface $logger, + Cache $client_credentials_cache ) { - $this->request_data = $request_data; - $this->login_seller_production = $login_seller_production; - $this->login_seller_sandbox = $login_seller_sandbox; - $this->partner_referrals_data = $partner_referrals_data; - $this->settings = $settings; - $this->cache = $cache; - $this->logger = $logger; + $this->request_data = $request_data; + $this->login_seller_production = $login_seller_production; + $this->login_seller_sandbox = $login_seller_sandbox; + $this->partner_referrals_data = $partner_referrals_data; + $this->settings = $settings; + $this->cache = $cache; + $this->logger = $logger; + $this->client_credentials_cache = $client_credentials_cache; } /** @@ -175,6 +187,12 @@ class LoginSellerEndpoint implements EndpointInterface { if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) { $this->cache->delete( PayPalBearer::CACHE_KEY ); } + if ( $this->client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { + $this->client_credentials_cache->delete( UserIdToken::CACHE_KEY ); + } + if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { + $this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); + } wp_schedule_single_event( time() + 5, diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index ce41619ca..52a41576e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -366,7 +366,8 @@ return array( $container->get( 'api.partner_merchant_id-production' ), $container->get( 'api.partner_merchant_id-sandbox' ), $container->get( 'api.endpoint.billing-agreements' ), - $logger + $logger, + new Cache( 'ppcp-client-credentials-cache' ) ); }, 'wcgateway.order-processor' => static function ( ContainerInterface $container ): OrderProcessor { diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 9986d5edc..e7e39a9e6 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; @@ -164,6 +166,13 @@ class SettingsListener { */ private $logger; + /** + * The client credentials cache. + * + * @var Cache + */ + private $client_credentials_cache; + /** * SettingsListener constructor. * @@ -183,6 +192,7 @@ class SettingsListener { * @param string $partner_merchant_id_sandbox Partner merchant ID sandbox. * @param BillingAgreementsEndpoint $billing_agreements_endpoint Billing Agreements endpoint. * @param ?LoggerInterface $logger The logger. + * @param Cache $client_credentials_cache The client credentials cache. */ public function __construct( Settings $settings, @@ -200,7 +210,8 @@ class SettingsListener { string $partner_merchant_id_production, string $partner_merchant_id_sandbox, BillingAgreementsEndpoint $billing_agreements_endpoint, - LoggerInterface $logger = null + LoggerInterface $logger = null, + Cache $client_credentials_cache ) { $this->settings = $settings; @@ -219,6 +230,7 @@ class SettingsListener { $this->partner_merchant_id_sandbox = $partner_merchant_id_sandbox; $this->billing_agreements_endpoint = $billing_agreements_endpoint; $this->logger = $logger ?: new NullLogger(); + $this->client_credentials_cache = $client_credentials_cache; } /** @@ -490,6 +502,12 @@ class SettingsListener { if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) { $this->cache->delete( PayPalBearer::CACHE_KEY ); } + if ( $this->client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { + $this->client_credentials_cache->delete( UserIdToken::CACHE_KEY ); + } + if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { + $this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); + } if ( $this->pui_status_cache->has( PayUponInvoiceProductStatus::PUI_STATUS_CACHE_KEY ) ) { $this->pui_status_cache->delete( PayUponInvoiceProductStatus::PUI_STATUS_CACHE_KEY ); From bc58fce8a0853178fa04bf9398f3d89320e4af1b Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 6 Aug 2024 10:56:37 +0200 Subject: [PATCH 56/72] Apple Pay: Fix the shipping callback --- .../ppcp-applepay/resources/js/Context/BaseHandler.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js index f763ac5d9..00d7bd463 100644 --- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js +++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js @@ -1,6 +1,5 @@ import ErrorHandler from '../../../../ppcp-button/resources/js/modules/ErrorHandler'; import CartActionHandler from '../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler'; -import { isPayPalSubscription } from '../../../../ppcp-blocks/resources/js/Helper/Subscription'; class BaseHandler { constructor( buttonConfig, ppcpConfig ) { @@ -24,7 +23,7 @@ class BaseHandler { } shippingAllowed() { - return this.buttonConfig.product.needsShipping; + return this.buttonConfig.product.needShipping; } transactionInfo() { @@ -76,13 +75,6 @@ class BaseHandler { document.querySelector( '.woocommerce-notices-wrapper' ) ); } - - errorHandler() { - return new ErrorHandler( - this.ppcpConfig.labels.error.generic, - document.querySelector( '.woocommerce-notices-wrapper' ) - ); - } } export default BaseHandler; From 9c380917c55ac75c4d394264da9c9dd2f705c8b0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 11:14:46 +0200 Subject: [PATCH 57/72] Fix phpunit --- tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php index c34146f3e..c747b19b8 100644 --- a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php +++ b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php @@ -43,6 +43,7 @@ class SettingsListenerTest extends ModularTestCase $billing_agreement_endpoint = Mockery::mock(BillingAgreementsEndpoint::class); $subscription_helper = Mockery::mock(SubscriptionHelper::class); $logger = Mockery::mock(LoggerInterface::class); + $client_credentials_cache = Mockery::mock(Cache::class); $testee = new SettingsListener( $settings, @@ -60,7 +61,8 @@ class SettingsListenerTest extends ModularTestCase '', '', $billing_agreement_endpoint, - $logger + $logger, + $client_credentials_cache ); $_GET['section'] = PayPalGateway::ID; From 86860d4fd6c285d2497cc0b6f5458a89516b24c3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 11:23:50 +0200 Subject: [PATCH 58/72] Fix phpunit --- tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php index c747b19b8..efa1b7de6 100644 --- a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php +++ b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php @@ -96,6 +96,9 @@ class SettingsListenerTest extends ModularTestCase ->andReturn(false); $dcc_status_cache->shouldReceive('has') ->andReturn(false); + $client_credentials_cache->shouldReceive('has')->andReturn(true); + $client_credentials_cache->shouldReceive('delete'); + $testee->listen(); } From c68db5c1a11f961ad7890fb9695e58e33edc594c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 11:35:28 +0200 Subject: [PATCH 59/72] Fix phpcs --- .../src/Authentication/ClientCredentials.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/ClientCredentials.php b/modules/ppcp-api-client/src/Authentication/ClientCredentials.php index 7ec4628a5..08d37bead 100644 --- a/modules/ppcp-api-client/src/Authentication/ClientCredentials.php +++ b/modules/ppcp-api-client/src/Authentication/ClientCredentials.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Authentication; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; /** * Class ClientCredentials @@ -28,14 +29,21 @@ class ClientCredentials { * * @param Settings $settings The settings. */ - public function __construct(Settings $settings) { + public function __construct( Settings $settings ) { $this->settings = $settings; } + /** + * Returns encoded client credentials. + * + * @return string + * @throws NotFoundException If setting does not found. + */ public function credentials(): string { - $client_id = $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : ''; + $client_id = $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : ''; $client_secret = $this->settings->has( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : ''; - return 'Basic ' . base64_encode($client_id . ':' . $client_secret); + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode + return 'Basic ' . base64_encode( $client_id . ':' . $client_secret ); } } From 22c2b5afd4703ae02f602dcf99d18b72cacf61fb Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 12:04:58 +0200 Subject: [PATCH 60/72] Delete transients stored while user is logged in --- modules/ppcp-api-client/services.php | 7 +++++-- modules/ppcp-api-client/src/ApiModule.php | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index dd2c786a1..7f62da5f6 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -1662,12 +1662,15 @@ return array( $container->get( 'wcgateway.settings' ) ); }, + 'api.client-credentials-cache' => static function( ContainerInterface $container ): Cache { + return new Cache( 'ppcp-client-credentials-cache' ); + }, 'api.user-id-token' => static function( ContainerInterface $container ): UserIdToken { return new UserIdToken( $container->get( 'api.host' ), $container->get( 'woocommerce.logger.woocommerce' ), $container->get( 'api.client-credentials' ), - new Cache( 'ppcp-client-credentials-cache' ) + $container->get( 'api.client-credentials-cache' ) ); }, 'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken { @@ -1675,7 +1678,7 @@ return array( $container->get( 'api.host' ), $container->get( 'woocommerce.logger.woocommerce' ), $container->get( 'api.client-credentials' ), - new Cache( 'ppcp-client-credentials-cache' ) + $container->get( 'api.client-credentials-cache' ) ); }, ); diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index 42bc1f117..5c3ec56bd 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -10,6 +10,9 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; use WC_Order; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; +use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -94,6 +97,21 @@ class ApiModule implements ModuleInterface { 10, 2 ); + + add_action( + 'wp_logout', + function() use ( $c ) { + $client_credentials_cache = $c->get( 'api.client-credentials-cache' ); + assert( $client_credentials_cache instanceof Cache ); + + if ( $client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { + $client_credentials_cache->delete( UserIdToken::CACHE_KEY ); + } + if ( $client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { + $client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); + } + } + ); } /** From a1413782e98dd78ccd2cecb66d056d7f3917a311 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 12:32:40 +0200 Subject: [PATCH 61/72] Invalidate cache when switching user --- .../src/Authentication/SdkClientToken.php | 15 +++++++++++++-- .../src/Authentication/UserIdToken.php | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 2e1f3a7ca..85a447176 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -83,7 +83,12 @@ class SdkClientToken { */ public function sdk_client_token( string $target_customer_id = '' ): string { if ( $this->cache->has( self::CACHE_KEY ) ) { - return $this->cache->get( self::CACHE_KEY ); + $user_id = $this->cache->get( self::CACHE_KEY )['user_id'] ?? 0; + $access_token = $this->cache->get( self::CACHE_KEY )['access_token'] ?? ''; + + if ( $user_id === get_current_user_id() && $access_token ) { + return $access_token; + } } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized @@ -121,7 +126,13 @@ class SdkClientToken { } $access_token = $json->access_token; - $this->cache->set( self::CACHE_KEY, $access_token ); + + $data = array( + 'access_token' => $access_token, + 'user_id' => get_current_user_id(), + ); + + $this->cache->set( self::CACHE_KEY, $data ); return $access_token; } diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 2d18d0507..c60801dd5 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -83,7 +83,12 @@ class UserIdToken { */ public function id_token( string $target_customer_id = '' ): string { if ( $this->cache->has( self::CACHE_KEY ) ) { - return $this->cache->get( self::CACHE_KEY ); + $user_id = $this->cache->get( self::CACHE_KEY )['user_id'] ?? 0; + $id_token = $this->cache->get( self::CACHE_KEY )['id_token'] ?? ''; + + if ( $user_id === get_current_user_id() && $id_token ) { + return $id_token; + } } $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; @@ -116,7 +121,13 @@ class UserIdToken { } $id_token = $json->id_token; - $this->cache->set( self::CACHE_KEY, $id_token ); + + $data = array( + 'id_token' => $id_token, + 'user_id' => get_current_user_id(), + ); + + $this->cache->set( self::CACHE_KEY, $data ); return $id_token; } From b42e9436ae88ed159b645b65f07cfdd4cccc8320 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 6 Aug 2024 15:30:50 +0200 Subject: [PATCH 62/72] Add id token cache key for each user --- modules/ppcp-api-client/src/ApiModule.php | 10 ++----- .../src/Authentication/SdkClientToken.php | 30 +++---------------- .../src/Authentication/UserIdToken.php | 16 ++-------- modules/ppcp-axo/src/AxoModule.php | 10 +------ .../js/modules/Helper/ScriptLoading.js | 5 +++- .../src/Endpoint/LoginSellerEndpoint.php | 3 -- .../src/Settings/SettingsListener.php | 3 -- 7 files changed, 15 insertions(+), 62 deletions(-) diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index 5c3ec56bd..2d63bd80f 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; use WC_Order; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; @@ -100,15 +99,12 @@ class ApiModule implements ModuleInterface { add_action( 'wp_logout', - function() use ( $c ) { + function( int $user_id ) use ( $c ) { $client_credentials_cache = $c->get( 'api.client-credentials-cache' ); assert( $client_credentials_cache instanceof Cache ); - if ( $client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { - $client_credentials_cache->delete( UserIdToken::CACHE_KEY ); - } - if ( $client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { - $client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); + if ( $client_credentials_cache->has( UserIdToken::CACHE_KEY . '-' . (string) $user_id ) ) { + $client_credentials_cache->delete( UserIdToken::CACHE_KEY . '-' . (string) $user_id ); } } ); diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 85a447176..6236869e2 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -72,23 +72,16 @@ class SdkClientToken { } /** - * Returns `sdk_client_token` which uniquely identifies the payer. - * - * @param string $target_customer_id Vaulted customer id. + * Returns the client token for SDK `data-sdk-client-token`. * * @return string * * @throws PayPalApiException If the request fails. * @throws RuntimeException If something unexpected happens. */ - public function sdk_client_token( string $target_customer_id = '' ): string { + public function sdk_client_token(): string { if ( $this->cache->has( self::CACHE_KEY ) ) { - $user_id = $this->cache->get( self::CACHE_KEY )['user_id'] ?? 0; - $access_token = $this->cache->get( self::CACHE_KEY )['access_token'] ?? ''; - - if ( $user_id === get_current_user_id() && $access_token ) { - return $access_token; - } + $this->cache->get( self::CACHE_KEY ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized @@ -97,15 +90,6 @@ class SdkClientToken { $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=' . $domain; - if ( $target_customer_id ) { - $url = add_query_arg( - array( - 'target_customer_id' => $target_customer_id, - ), - $url - ); - } - $args = array( 'method' => 'POST', 'headers' => array( @@ -126,13 +110,7 @@ class SdkClientToken { } $access_token = $json->access_token; - - $data = array( - 'access_token' => $access_token, - 'user_id' => get_current_user_id(), - ); - - $this->cache->set( self::CACHE_KEY, $data ); + $this->cache->set( self::CACHE_KEY, $access_token ); return $access_token; } diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index c60801dd5..3c215a11f 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -82,13 +82,8 @@ class UserIdToken { * @throws RuntimeException If something unexpected happens. */ public function id_token( string $target_customer_id = '' ): string { - if ( $this->cache->has( self::CACHE_KEY ) ) { - $user_id = $this->cache->get( self::CACHE_KEY )['user_id'] ?? 0; - $id_token = $this->cache->get( self::CACHE_KEY )['id_token'] ?? ''; - - if ( $user_id === get_current_user_id() && $id_token ) { - return $id_token; - } + if ( $this->cache->has( self::CACHE_KEY . '-' . (string) get_current_user_id() ) ) { + return $this->cache->get( self::CACHE_KEY . '-' . (string) get_current_user_id() ); } $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; @@ -122,12 +117,7 @@ class UserIdToken { $id_token = $json->id_token; - $data = array( - 'id_token' => $id_token, - 'user_id' => get_current_user_id(), - ); - - $this->cache->set( self::CACHE_KEY, $data ); + $this->cache->set( self::CACHE_KEY . '-' . (string) get_current_user_id(), $id_token ); return $id_token; } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3505ff555..c0fc34b24 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -280,15 +280,7 @@ class AxoModule implements ModuleInterface { array $localized_script_data ): array { try { - $target_customer_id = ''; - if ( is_user_logged_in() ) { - $target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); - if ( ! $target_customer_id ) { - $target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true ); - } - } - - $sdk_client_token = $api->sdk_client_token( $target_customer_id ); + $sdk_client_token = $api->sdk_client_token(); $localized_script_data['axo'] = array( 'sdk_client_token' => $sdk_client_token, ); diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index 0aa70f793..588e14cd6 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -71,7 +71,10 @@ export const loadPaypalScript = ( config, onLoaded, onError = null ) => { } // Load PayPal script for special case with data-client-token - if ( config.data_client_id?.set_attribute ) { + if ( + config.data_client_id?.set_attribute && + config.vault_v3_enabled !== '1' + ) { dataClientIdAttributeHandler( scriptOptions, config.data_client_id, diff --git a/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php b/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php index 2a30ad6b1..e1d23234f 100644 --- a/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php +++ b/modules/ppcp-onboarding/src/Endpoint/LoginSellerEndpoint.php @@ -187,9 +187,6 @@ class LoginSellerEndpoint implements EndpointInterface { if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) { $this->cache->delete( PayPalBearer::CACHE_KEY ); } - if ( $this->client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { - $this->client_credentials_cache->delete( UserIdToken::CACHE_KEY ); - } if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { $this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index e7e39a9e6..6c01830d6 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -502,9 +502,6 @@ class SettingsListener { if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) { $this->cache->delete( PayPalBearer::CACHE_KEY ); } - if ( $this->client_credentials_cache->has( UserIdToken::CACHE_KEY ) ) { - $this->client_credentials_cache->delete( UserIdToken::CACHE_KEY ); - } if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) { $this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY ); } From 8411ef2106890a0ad6c282d0513244509e08dae7 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Aug 2024 09:51:12 +0200 Subject: [PATCH 63/72] Fix not returning the cached token --- modules/ppcp-api-client/src/Authentication/SdkClientToken.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 6236869e2..10b1eb0eb 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -81,7 +81,7 @@ class SdkClientToken { */ public function sdk_client_token(): string { if ( $this->cache->has( self::CACHE_KEY ) ) { - $this->cache->get( self::CACHE_KEY ); + return $this->cache->get( self::CACHE_KEY ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized From 4f47b382c571ead74c5f5cf768e37ecae79389ea Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Aug 2024 10:36:05 +0200 Subject: [PATCH 64/72] Add token expiration --- .../ppcp-api-client/src/Authentication/SdkClientToken.php | 4 +++- modules/ppcp-api-client/src/Authentication/UserIdToken.php | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 10b1eb0eb..5031f1291 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -110,7 +110,9 @@ class SdkClientToken { } $access_token = $json->access_token; - $this->cache->set( self::CACHE_KEY, $access_token ); + $expires_in = (int) $json->expires_in; + + $this->cache->set( self::CACHE_KEY, $access_token, $expires_in ); return $access_token; } diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 3c215a11f..9487a7238 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -115,9 +115,10 @@ class UserIdToken { throw new PayPalApiException( $json, $status_code ); } - $id_token = $json->id_token; + $id_token = $json->id_token; + $expires_in = (int) $json->expires_in; - $this->cache->set( self::CACHE_KEY . '-' . (string) get_current_user_id(), $id_token ); + $this->cache->set( self::CACHE_KEY . '-' . (string) get_current_user_id(), $id_token, $expires_in ); return $id_token; } From 7778cfafed613b6286d08e38ed9eb9b2ec1f48c0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Aug 2024 12:45:44 +0200 Subject: [PATCH 65/72] Revert #2283 --- modules/ppcp-api-client/src/Authentication/SdkClientToken.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 5031f1291..b85490acf 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -80,10 +80,6 @@ class SdkClientToken { * @throws RuntimeException If something unexpected happens. */ public function sdk_client_token(): string { - if ( $this->cache->has( self::CACHE_KEY ) ) { - return $this->cache->get( self::CACHE_KEY ); - } - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ); $domain = preg_replace( '/^www\./', '', $domain ); From 939991c42f794ff26813c6d811e412fe355793b4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Aug 2024 12:47:11 +0200 Subject: [PATCH 66/72] Revert #2283 --- modules/ppcp-api-client/src/Authentication/SdkClientToken.php | 4 ++++ .../src/SavePaymentMethodsModule.php | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index b85490acf..5031f1291 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -80,6 +80,10 @@ class SdkClientToken { * @throws RuntimeException If something unexpected happens. */ public function sdk_client_token(): string { + if ( $this->cache->has( self::CACHE_KEY ) ) { + return $this->cache->get( self::CACHE_KEY ); + } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ); $domain = preg_replace( '/^www\./', '', $domain ); diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index db7335cf5..b667fb10b 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -84,10 +84,6 @@ class SavePaymentMethodsModule implements ModuleInterface { add_filter( 'woocommerce_paypal_payments_localized_script_data', function( array $localized_script_data ) use ( $c ) { - if ( ! is_user_logged_in() ) { - return $localized_script_data; - } - $api = $c->get( 'api.user-id-token' ); assert( $api instanceof UserIdToken ); From c0bd7a5c46be17e589f90a2cc73a272c9feb091e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Aug 2024 14:24:44 +0200 Subject: [PATCH 67/72] Release 2.8.3-rc1 --- changelog.txt | 17 +++++++++++++++++ package.json | 2 +- readme.txt | 19 ++++++++++++++++++- woocommerce-paypal-payments.php | 4 ++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index aaf167948..e28d7a258 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,22 @@ *** Changelog *** += 2.8.3 - xxxx-xx-xx = +* Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474 +* Fix - Do not add tax info into order meta during order creation #2471 +* Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425 +* Fix - PayPal js files loaded on non PayPal pages #2411 +* Fix - Google Pay: Fix the incorrect popup triggering #2414 +* Fix - Add tax configurator when programmatically creating WC orders #2431 +* Fix - Shipping callback compatibility with WC Name Your Price plugin #2402 +* Fix - Uncaught Error: Cannot use object of type ...\Settings as array in .../AbstractPaymentMethodType.php (3253) #2334 +* Fix - Prevent displaying smart button multiple times on variable product page #2420 +* Fix - Prevent enabling Standard Card Button when ACDC is enabled #2404 +* Fix - Use client credentials for user tokens #2491 +* Fix - Apple Pay: Fix the shipping callback #2492 +* Enhancement - Separate Google Pay button for Classic Checkout #2430 +* Enhancement - Add Apple Pay and Google Pay support for China, simplify country-currency matrix #2468 +* Enhancement - Add AMEX support for Advanced Card Processing in China #2469 + = 2.8.2 - 2024-07-22 = * Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415 * Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400 diff --git a/package.json b/package.json index 61c625ca9..72aad99db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "woocommerce-paypal-payments", - "version": "2.8.2", + "version": "2.8.3", "description": "WooCommerce PayPal Payments", "repository": "https://github.com/woocommerce/woocommerce-paypal-payments", "license": "GPL-2.0", diff --git a/readme.txt b/readme.txt index e4404042e..fc1976e15 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple Requires at least: 5.3 Tested up to: 6.6 Requires PHP: 7.2 -Stable tag: 2.8.2 +Stable tag: 2.8.3 License: GPLv2 License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -179,6 +179,23 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == += 2.8.3 - xxxx-xx-xx = +* Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474 +* Fix - Do not add tax info into order meta during order creation #2471 +* Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425 +* Fix - PayPal js files loaded on non PayPal pages #2411 +* Fix - Google Pay: Fix the incorrect popup triggering #2414 +* Fix - Add tax configurator when programmatically creating WC orders #2431 +* Fix - Shipping callback compatibility with WC Name Your Price plugin #2402 +* Fix - Uncaught Error: Cannot use object of type ...\Settings as array in .../AbstractPaymentMethodType.php (3253) #2334 +* Fix - Prevent displaying smart button multiple times on variable product page #2420 +* Fix - Prevent enabling Standard Card Button when ACDC is enabled #2404 +* Fix - Use client credentials for user tokens #2491 +* Fix - Apple Pay: Fix the shipping callback #2492 +* Enhancement - Separate Google Pay button for Classic Checkout #2430 +* Enhancement - Add Apple Pay and Google Pay support for China, simplify country-currency matrix #2468 +* Enhancement - Add AMEX support for Advanced Card Processing in China #2469 + = 2.8.2 - 2024-07-22 = * Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415 * Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400 diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index 2cdf4f8ed..fcf66ae12 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce PayPal Payments * Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/ * Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage. - * Version: 2.8.2 + * Version: 2.8.3 * Author: WooCommerce * Author URI: https://woocommerce.com/ * License: GPL-2.0 @@ -26,7 +26,7 @@ define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' ); define( 'PAYPAL_URL', 'https://www.paypal.com' ); define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' ); define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' ); -define( 'PAYPAL_INTEGRATION_DATE', '2024-07-17' ); +define( 'PAYPAL_INTEGRATION_DATE', '2024-08-07' ); define( 'PPCP_PAYPAL_BN_CODE', 'Woo_PPCP' ); ! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' ); From e41470b159c0879586d521cdbdf8c021c8d274b9 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 7 Aug 2024 15:01:27 +0200 Subject: [PATCH 68/72] =?UTF-8?q?=F0=9F=90=9B=20Fix=20Google=20Pay=20butto?= =?UTF-8?q?n=20on=20non-checkout=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/GooglepayButton.js | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 4363c67be..ac477404b 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -292,24 +292,33 @@ class GooglepayButton { onButtonClick() { this.log( 'onButtonClick', this.context ); - this.contextHandler.validateForm().then( - () => { - window.ppcpFundingSource = 'googlepay'; + const initiatePaymentRequest = () => { + window.ppcpFundingSource = 'googlepay'; - const paymentDataRequest = this.paymentDataRequest(); + const paymentDataRequest = this.paymentDataRequest(); - this.log( - 'onButtonClick: paymentDataRequest', - paymentDataRequest, - this.context - ); + this.log( + 'onButtonClick: paymentDataRequest', + paymentDataRequest, + this.context + ); - this.paymentsClient.loadPaymentData( paymentDataRequest ); - }, - () => { - console.error( '[GooglePayButton] Form validation failed.' ); - } - ); + this.paymentsClient.loadPaymentData( paymentDataRequest ); + }; + + if ( 'function' === typeof this.contextHandler.validateForm ) { + // During regular checkout, validate the checkout form before initiating the payment. + this.contextHandler + .validateForm() + .then( initiatePaymentRequest, () => { + console.error( + '[GooglePayButton] Form validation failed.' + ); + } ); + } else { + // This is the flow on product page, cart, and other non-checkout pages. + initiatePaymentRequest(); + } } paymentDataRequest() { From f52117c196de1cba1ef3c3e89c9991d6b1e4b538 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 8 Aug 2024 15:38:08 +0200 Subject: [PATCH 69/72] Apple Pay: Fix shipping method switching inside of the Apple Pay modal in Classic Checkout --- .../resources/js/ApplepayButton.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 2bcac16fc..281a08ae1 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -644,10 +644,12 @@ class ApplepayButton { return { action: 'ppcp_update_shipping_method', shipping_method: event.shippingMethod, - simplified_contact: - this.updatedContactInfo || - this.initialPaymentRequest.shippingContact || - this.initialPaymentRequest.billingContact, + simplified_contact: this.hasValidContactInfo( + this.updatedContactInfo + ) + ? this.updatedContactInfo + : this.initialPaymentRequest?.shippingContact ?? + this.initialPaymentRequest?.billingContact, product_id, products: JSON.stringify( this.products ), caller_page: 'productDetail', @@ -662,10 +664,12 @@ class ApplepayButton { return { action: 'ppcp_update_shipping_method', shipping_method: event.shippingMethod, - simplified_contact: - this.updatedContactInfo || - this.initialPaymentRequest.shippingContact || - this.initialPaymentRequest.billingContact, + simplified_contact: this.hasValidContactInfo( + this.updatedContactInfo + ) + ? this.updatedContactInfo + : this.initialPaymentRequest?.shippingContact ?? + this.initialPaymentRequest?.billingContact, caller_page: 'cart', 'woocommerce-process-checkout-nonce': this.nonce, }; @@ -948,6 +952,12 @@ class ApplepayButton { return btoa( utf8Str ); } + + hasValidContactInfo( value ) { + return Array.isArray( value ) + ? value.length > 0 + : Object.keys( value || {} ).length > 0; + } } export default ApplepayButton; From dbf303b2f6befab55e9e6653eacb2106a27012e4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 9 Aug 2024 12:08:24 +0200 Subject: [PATCH 70/72] Do not cache access tokens for `data-user-id-token` --- .../src/Authentication/UserIdToken.php | 11 +---------- .../src/SavePaymentMethodsModule.php | 7 +++++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 9487a7238..2552483ee 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -82,10 +82,6 @@ class UserIdToken { * @throws RuntimeException If something unexpected happens. */ public function id_token( string $target_customer_id = '' ): string { - if ( $this->cache->has( self::CACHE_KEY . '-' . (string) get_current_user_id() ) ) { - return $this->cache->get( self::CACHE_KEY . '-' . (string) get_current_user_id() ); - } - $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; if ( $target_customer_id ) { $url = add_query_arg( @@ -115,11 +111,6 @@ class UserIdToken { throw new PayPalApiException( $json, $status_code ); } - $id_token = $json->id_token; - $expires_in = (int) $json->expires_in; - - $this->cache->set( self::CACHE_KEY . '-' . (string) get_current_user_id(), $id_token, $expires_in ); - - return $id_token; + return $json->id_token; } } diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index b667fb10b..f82095bf4 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -31,6 +31,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod; +use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; /** * Class SavePaymentMethodsModule @@ -84,6 +85,12 @@ class SavePaymentMethodsModule implements ModuleInterface { add_filter( 'woocommerce_paypal_payments_localized_script_data', function( array $localized_script_data ) use ( $c ) { + $subscriptions_helper = $c->get( 'wc-subscriptions.helper' ); + assert( $subscriptions_helper instanceof SubscriptionHelper ); + if ( ! is_user_logged_in() && ! $subscriptions_helper->cart_contains_subscription() ) { + return $localized_script_data; + } + $api = $c->get( 'api.user-id-token' ); assert( $api instanceof UserIdToken ); From 627328df9c58147594f8a0a2739c0904047970cf Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 9 Aug 2024 12:33:05 +0200 Subject: [PATCH 71/72] Remove cache references --- modules/ppcp-api-client/services.php | 3 +-- modules/ppcp-api-client/src/ApiModule.php | 12 ------------ .../src/Authentication/UserIdToken.php | 15 +-------------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 7f62da5f6..70720e0b4 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -1669,8 +1669,7 @@ return array( return new UserIdToken( $container->get( 'api.host' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'api.client-credentials' ), - $container->get( 'api.client-credentials-cache' ) + $container->get( 'api.client-credentials' ) ); }, 'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken { diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index 2d63bd80f..8b2f43911 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -96,18 +96,6 @@ class ApiModule implements ModuleInterface { 10, 2 ); - - add_action( - 'wp_logout', - function( int $user_id ) use ( $c ) { - $client_credentials_cache = $c->get( 'api.client-credentials-cache' ); - assert( $client_credentials_cache instanceof Cache ); - - if ( $client_credentials_cache->has( UserIdToken::CACHE_KEY . '-' . (string) $user_id ) ) { - $client_credentials_cache->delete( UserIdToken::CACHE_KEY . '-' . (string) $user_id ); - } - } - ); } /** diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 2552483ee..dde47c223 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -11,7 +11,6 @@ use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; -use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WP_Error; /** @@ -21,8 +20,6 @@ class UserIdToken { use RequestTrait; - const CACHE_KEY = 'user-id-token-key'; - /** * The host. * @@ -44,31 +41,21 @@ class UserIdToken { */ private $client_credentials; - /** - * The cache. - * - * @var Cache - */ - private $cache; - /** * UserIdToken constructor. * * @param string $host The host. * @param LoggerInterface $logger The logger. * @param ClientCredentials $client_credentials The client credentials. - * @param Cache $cache The cache. */ public function __construct( string $host, LoggerInterface $logger, - ClientCredentials $client_credentials, - Cache $cache + ClientCredentials $client_credentials ) { $this->host = $host; $this->logger = $logger; $this->client_credentials = $client_credentials; - $this->cache = $cache; } /** From f59bdf28825c013d11cdb4399f454dd3e821020f Mon Sep 17 00:00:00 2001 From: Diego Curbelo Date: Mon, 12 Aug 2024 18:51:48 -0300 Subject: [PATCH 72/72] woorelease: Product version bump update --- changelog.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index e28d7a258..8d811ad35 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ *** Changelog *** -= 2.8.3 - xxxx-xx-xx = += 2.8.3 - 2024-08-12 = * Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474 * Fix - Do not add tax info into order meta during order creation #2471 * Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425 diff --git a/readme.txt b/readme.txt index fc1976e15..1aed175cb 100644 --- a/readme.txt +++ b/readme.txt @@ -179,7 +179,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == -= 2.8.3 - xxxx-xx-xx = += 2.8.3 - 2024-08-12 = * Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474 * Fix - Do not add tax info into order meta during order creation #2471 * Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425