From 0038922fe84bf7e881db5c430b170cacc1c2536f Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 9 Jul 2024 15:40:16 +0400 Subject: [PATCH 01/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] =?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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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