From 0cddf83b9d952bae75a0a59df50fed4cb20f5bf0 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 14 Sep 2023 17:48:46 +0100 Subject: [PATCH 01/17] Add FieldDisplayManager. Refactor admin field show/hide. --- modules/ppcp-applepay/extensions.php | 22 ++-- modules/ppcp-googlepay/extensions.php | 26 ++-- .../ppcp-wc-gateway/resources/css/common.scss | 11 ++ .../js/SettingsHandler/SubElementsHandler.js | 51 ------- .../ppcp-wc-gateway/resources/js/common.js | 14 ++ .../js/common/FieldDisplayManager.js | 74 +++++++++++ .../resources/js/gateway-settings.js | 12 -- modules/ppcp-wc-gateway/services.php | 19 +-- .../src/Assets/SettingsPageAssets.php | 7 + .../src/Helper/FieldDisplayManager.php | 41 ++++++ .../src/Helper/FieldDisplayRule.php | 124 ++++++++++++++++++ .../Settings/Fields/connection-tab-fields.php | 18 +-- modules/ppcp-wc-gateway/webpack.config.js | 1 + 13 files changed, 320 insertions(+), 100 deletions(-) create mode 100644 modules/ppcp-wc-gateway/resources/css/common.scss delete mode 100644 modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js create mode 100644 modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php create mode 100644 modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 976a1a7c9..995354504 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Applepay; use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; return array( @@ -24,6 +25,9 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; + $fields_manager = $container->get( 'wcgateway.field-display-manager' ); + assert( $fields_manager instanceof FieldDisplayManager ); + return $insert_after( $fields, 'allow_card_button_gateway', @@ -45,15 +49,17 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), 'custom_attributes' => array( - 'data-ppcp-handlers' => wp_json_encode( + 'data-ppcp-display' => wp_json_encode( array( - array( - 'handler' => 'SubElementsHandler', - 'options' => array( - 'values' => array( '1' ), - 'elements' => array( '#field-applepay_sandbox_validation_file', '#field-applepay_live_validation_file', '#field-applepay_button_color', '#field-applepay_button_type', '#field-applepay_button_language' ), - ), - ), + $fields_manager + ->rule() + ->condition( 'applepay_button_enabled', 'equals', '1' ) + ->action( 'applepay_sandbox_validation_file', 'visible' ) + ->action( 'applepay_live_validation_file', 'visible' ) + ->action( 'applepay_button_color', 'visible' ) + ->action( 'applepay_button_type', 'visible' ) + ->action( 'applepay_button_language', 'visible' ) + ->to_array(), ) ), ), diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index 33367d273..d5c0837b6 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay; use WooCommerce\PayPalCommerce\Googlepay\Helper\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; return array( @@ -31,6 +32,9 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; + $fields_manager = $container->get( 'wcgateway.field-display-manager' ); + assert( $fields_manager instanceof FieldDisplayManager ); + return $insert_after( $fields, 'allow_card_button_gateway', @@ -52,20 +56,16 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), 'custom_attributes' => array( - 'data-ppcp-handlers' => wp_json_encode( + 'data-ppcp-display' => wp_json_encode( array( - array( - 'handler' => 'SubElementsHandler', - 'options' => array( - 'values' => array( '1' ), - 'elements' => array( - '#field-googlepay_button_color', - '#field-googlepay_button_type', - '#field-googlepay_button_language', - '#field-googlepay_button_shipping_enabled', - ), - ), - ), + $fields_manager + ->rule() + ->condition( 'googlepay_button_enabled', 'equals', '1' ) + ->action( 'googlepay_button_type', 'visible' ) + ->action( 'googlepay_button_color', 'visible' ) + ->action( 'googlepay_button_language', 'visible' ) + ->action( 'googlepay_button_shipping_enabled', 'visible' ) + ->to_array(), ) ), ), diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss new file mode 100644 index 000000000..299375005 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -0,0 +1,11 @@ + +.ppcp-field-hidden { + display: none !important; +} + +.ppcp-field-disabled { + cursor: not-allowed; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); + opacity: 0.5; +} diff --git a/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js b/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js deleted file mode 100644 index cb0b30252..000000000 --- a/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js +++ /dev/null @@ -1,51 +0,0 @@ - -class SubElementsHandler { - constructor(element, options) { - const fieldSelector = 'input, select, textarea'; - - this.element = element; - this.values = options.values; - this.elements = options.elements; - - this.elementsSelector = this.elements.join(','); - - this.input = jQuery(this.element).is(fieldSelector) - ? this.element - : jQuery(this.element).find(fieldSelector).get(0); - - this.updateElementsVisibility(); - - jQuery(this.input).change(() => { - this.updateElementsVisibility(); - }); - } - - updateElementsVisibility() { - const $elements = jQuery(this.elementsSelector); - - let value = this.getValue(this.input); - value = (value !== null ? value.toString() : value); - - if (this.values.indexOf(value) !== -1) { - $elements.show(); - } else { - $elements.hide(); - } - } - - getValue(element) { - const $el = jQuery(element); - - if ($el.is(':checkbox') || $el.is(':radio')) { - if ($el.is(':checked')) { - return $el.val(); - } else { - return null; - } - } else { - return $el.val(); - } - } -} - -export default SubElementsHandler; diff --git a/modules/ppcp-wc-gateway/resources/js/common.js b/modules/ppcp-wc-gateway/resources/js/common.js index e017594a7..5a8e850b3 100644 --- a/modules/ppcp-wc-gateway/resources/js/common.js +++ b/modules/ppcp-wc-gateway/resources/js/common.js @@ -1,10 +1,24 @@ +import FieldDisplayManager from "./common/FieldDisplayManager"; import moveWrappedElements from "./common/wrapped-elements"; + document.addEventListener( 'DOMContentLoaded', () => { + // Wait for current execution context to end. setTimeout(function () { moveWrappedElements(); }, 0); + + + // Initialize FieldDisplayManager. + const fieldDisplayManager = new FieldDisplayManager(); + + jQuery( '*[data-ppcp-display]' ).each( (index, el) => { + const rules = jQuery(el).data('ppcpDisplay'); + for (const rule of rules) { + fieldDisplayManager.addRule(rule); + } + }); } ); diff --git a/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js b/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js new file mode 100644 index 000000000..d41128eb7 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js @@ -0,0 +1,74 @@ + +class FieldDisplayManager { + + constructor() { + this.rules = []; + + document.ppcpDisplayManagerLog = () => { + console.log('rules', this.rules); + } + } + + addRule(rule) { + this.rules.push(rule); + + for (const condition of rule.conditions) { + jQuery(document).on('change', condition.selector, () => { + this.updateElementsVisibility(condition, rule); + }); + + this.updateElementsVisibility(condition, rule); + } + } + + updateElementsVisibility(condition, rule) { + let value = this.getValue(condition.selector); + value = (value !== null ? value.toString() : value); + + if (condition.value === value) { + for (const action of rule.actions) { + if (action.action === 'visible') { + jQuery(action.selector).removeClass('ppcp-field-hidden'); + } + if (action.action === 'enable') { + jQuery(action.selector).removeClass('ppcp-field-disabled') + .off('mouseup') + .find('> *') + .css('pointer-events', ''); + } + } + + } else { + for (const action of rule.actions) { + if (action.action === 'visible') { + jQuery(action.selector).addClass('ppcp-field-hidden'); + } + if (action.action === 'enable') { + jQuery(action.selector).addClass('ppcp-field-disabled') + .on('mouseup', function(event) { + event.stopImmediatePropagation(); + }) + .find('> *') + .css('pointer-events', 'none'); + } + } + } + } + + getValue(element) { + const $el = jQuery(element); + + if ($el.is(':checkbox') || $el.is(':radio')) { + if ($el.is(':checked')) { + return $el.val(); + } else { + return null; + } + } else { + return $el.val(); + } + } + +} + +export default FieldDisplayManager; diff --git a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js index bbb455be9..c20432d48 100644 --- a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js +++ b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js @@ -4,7 +4,6 @@ import Renderer from '../../../ppcp-button/resources/js/modules/Renderer/Rendere import MessageRenderer from "../../../ppcp-button/resources/js/modules/Renderer/MessageRenderer"; import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/modules/Helper/Hiding"; import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder"; -import SubElementsHandler from "./SettingsHandler/SubElementsHandler"; document.addEventListener( 'DOMContentLoaded', @@ -308,16 +307,5 @@ document.addEventListener( createButtonPreview(() => getButtonDefaultSettings('#ppcpPayLaterButtonPreview')); }); } - - // Generic behaviours, can be moved to common.js once it's on trunk branch. - jQuery( '*[data-ppcp-handlers]' ).each( (index, el) => { - const handlers = jQuery(el).data('ppcpHandlers'); - for (const handlerConfig of handlers) { - new { - SubElementsHandler: SubElementsHandler - }[handlerConfig.handler](el, handlerConfig.options) - } - }); - } ); diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 56a2c8887..43aabbd5e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -11,21 +11,18 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway; -use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; -use WooCommerce\PayPalCommerce\Session\SessionHandler; -use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; -use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers; +use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; +use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer; use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn; use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail; @@ -35,6 +32,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset; use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways; use WooCommerce\PayPalCommerce\WcGateway\Cli\SettingsCommand; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint; +use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet; +use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId; +use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId; use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; @@ -42,15 +42,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository; use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO; use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; -use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet; -use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId; -use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSourceFactory; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoice; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; +use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\RefundFeesUpdater; @@ -1419,4 +1417,9 @@ return array( $container->get( 'wcgateway.settings' ) ); }, + 'wcgateway.field-display-manager' => SingletonDecorator::make( + static function( ContainerInterface $container ): FieldDisplayManager { + return new FieldDisplayManager(); + } + ), ); diff --git a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php index 14c07e90f..dd7af6ed0 100644 --- a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php @@ -229,6 +229,13 @@ class SettingsPageAssets { * Register assets for PayPal admin pages. */ private function register_admin_assets(): void { + wp_enqueue_style( + 'ppcp-admin-common', + trailingslashit( $this->module_url ) . 'assets/css/common.css', + array(), + $this->version + ); + wp_enqueue_script( 'ppcp-admin-common', trailingslashit( $this->module_url ) . 'assets/js/common.js', diff --git a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php new file mode 100644 index 000000000..3d19c077b --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php @@ -0,0 +1,41 @@ +rules ); + } + + $rule = new FieldDisplayRule( $key ); + + $this->rules[ $key ] = $rule; + return $rule; + } + +} diff --git a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php new file mode 100644 index 000000000..648d4bfbd --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php @@ -0,0 +1,124 @@ +key = $key; + } + + /** + * Adds a condition to the rule. + * + * @param string $selector The condition selector. + * @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty). + * @param mixed $value The value to compare against. + * @return self + */ + public function condition( string $selector, string $operation, $value ): self { + if ( $this->add_selector_prefixes ) { + $selector = '#ppcp-' . $selector; // Refers to the input. + } + $this->conditions[] = array( + 'selector' => $selector, + 'operation' => $operation, + 'value' => $value, + ); + return $this; + } + + /** + * Adds a condition to enable the element. + * + * @param string $selector The condition selector. + * @param string $action The action. + */ + public function action( string $selector, string $action ): self { + if ( $this->add_selector_prefixes ) { + $selector = '#field-' . $selector; // Refers to the whole field. + } + $this->actions[] = array( + 'selector' => $selector, + 'action' => $action, + ); + return $this; + } + + /** + * Set if selector prefixes like, "#ppcp-" or "#field-" should be added to condition or action selectors. + * + * @param bool $add_selector_prefixes If should add prefixes. + * @return self + */ + public function should_add_selector_prefixes( bool $add_selector_prefixes = true ): self { + $this->add_selector_prefixes = $add_selector_prefixes; + return $this; + } + + /** + * Returns array representation. + * + * @return array + */ + public function to_array(): array { + return array( + 'key' => $this->key, + 'conditions' => $this->conditions, + 'actions' => $this->actions, + ); + } + + /** + * Returns JSON representation. + * + * @return string + */ + public function json(): string { + return wp_json_encode( $this->to_array() ) ?: ''; + } + +} diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 19e38b99f..d83ccca46 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Onboarding\State; +use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; return function ( ContainerInterface $container, array $fields ): array { @@ -39,6 +40,9 @@ return function ( ContainerInterface $container, array $fields ): array { $module_url = $container->get( 'wcgateway.url' ); + $fields_manager = $container->get( 'wcgateway.field-display-manager' ); + assert( $fields_manager instanceof FieldDisplayManager ); + $connection_fields = array( 'ppcp_onboarading_header' => array( 'type' => 'ppcp-text', @@ -504,15 +508,13 @@ return function ( ContainerInterface $container, array $fields ): array { 'requirements' => array(), 'gateway' => Settings::CONNECTION_TAB_ID, 'custom_attributes' => array( - 'data-ppcp-handlers' => wp_json_encode( + 'data-ppcp-display' => wp_json_encode( array( - array( - 'handler' => 'SubElementsHandler', - 'options' => array( - 'values' => array( PurchaseUnitSanitizer::MODE_EXTRA_LINE ), - 'elements' => array( '#field-subtotal_mismatch_line_name' ), - ), - ), + $fields_manager + ->rule() + ->condition( 'subtotal_mismatch_behavior', 'equals', PurchaseUnitSanitizer::MODE_EXTRA_LINE ) + ->action( 'subtotal_mismatch_line_name', 'visible' ) + ->to_array(), ) ), ), diff --git a/modules/ppcp-wc-gateway/webpack.config.js b/modules/ppcp-wc-gateway/webpack.config.js index a8a78a8bc..394e549fe 100644 --- a/modules/ppcp-wc-gateway/webpack.config.js +++ b/modules/ppcp-wc-gateway/webpack.config.js @@ -11,6 +11,7 @@ module.exports = { 'fraudnet': path.resolve('./resources/js/fraudnet.js'), 'oxxo': path.resolve('./resources/js/oxxo.js'), 'gateway-settings-style': path.resolve('./resources/css/gateway-settings.scss'), + 'common-style': path.resolve('./resources/css/common.scss'), }, output: { path: path.resolve(__dirname, 'assets/'), From 93f7e5dca68e4dea5a02e92886aef003065267bf Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 14 Sep 2023 17:56:32 +0100 Subject: [PATCH 02/17] Fix lint --- modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php index 648d4bfbd..e466a8f51 100644 --- a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php +++ b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php @@ -36,9 +36,9 @@ class FieldDisplayRule { protected $actions = array(); /** - * The actions of this rule. + * Indicates if this class should add selector prefixes. * - * @var array + * @var bool */ protected $add_selector_prefixes = true; From e31e09f0c75b871e72ee38a1bc8c8c876dcc17b2 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 15 Sep 2023 17:32:00 +0100 Subject: [PATCH 03/17] Refactor DisplayManager for admin field display rules centralization. --- modules/ppcp-applepay/extensions.php | 20 +- modules/ppcp-googlepay/extensions.php | 18 +- .../ppcp-wc-gateway/resources/js/common.js | 14 +- .../js/common/FieldDisplayManager.js | 74 ----- .../common/display-manager/ActionFactory.js | 14 + .../display-manager/ConditionFactory.js | 17 ++ .../common/display-manager/DisplayManager.js | 32 +++ .../js/common/display-manager/Rule.js | 65 +++++ .../display-manager/action/BaseAction.js | 21 ++ .../display-manager/action/ElementAction.js | 35 +++ .../condition/BaseCondition.js | 19 ++ .../condition/BoolCondition.js | 15 + .../condition/ElementCondition.js | 27 ++ .../resources/js/helper/form.js | 13 + modules/ppcp-wc-gateway/services.php | 9 +- .../src/Helper/DisplayManager.php | 60 ++++ .../src/Helper/DisplayRule.php | 262 ++++++++++++++++++ .../src/Helper/FieldDisplayManager.php | 41 --- .../src/Helper/FieldDisplayRule.php | 124 --------- .../Settings/Fields/connection-tab-fields.php | 12 +- 20 files changed, 619 insertions(+), 273 deletions(-) delete mode 100644 modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/ConditionFactory.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/DisplayManager.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/action/BaseAction.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BaseCondition.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BoolCondition.js create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/ElementCondition.js create mode 100644 modules/ppcp-wc-gateway/resources/js/helper/form.js create mode 100644 modules/ppcp-wc-gateway/src/Helper/DisplayManager.php create mode 100644 modules/ppcp-wc-gateway/src/Helper/DisplayRule.php delete mode 100644 modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php delete mode 100644 modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 995354504..7880b82c2 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -12,7 +12,7 @@ namespace WooCommerce\PayPalCommerce\Applepay; use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; return array( @@ -25,8 +25,8 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; - $fields_manager = $container->get( 'wcgateway.field-display-manager' ); - assert( $fields_manager instanceof FieldDisplayManager ); + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); return $insert_after( $fields, @@ -51,14 +51,14 @@ return array( 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( array( - $fields_manager + $display_manager ->rule() - ->condition( 'applepay_button_enabled', 'equals', '1' ) - ->action( 'applepay_sandbox_validation_file', 'visible' ) - ->action( 'applepay_live_validation_file', 'visible' ) - ->action( 'applepay_button_color', 'visible' ) - ->action( 'applepay_button_type', 'visible' ) - ->action( 'applepay_button_language', 'visible' ) + ->condition_element( 'applepay_button_enabled', '1' ) + ->action_visible( 'applepay_sandbox_validation_file' ) + ->action_visible( 'applepay_live_validation_file' ) + ->action_visible( 'applepay_button_color' ) + ->action_visible( 'applepay_button_type' ) + ->action_visible( 'applepay_button_language' ) ->to_array(), ) ), diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index d5c0837b6..05a57284a 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -12,7 +12,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay; use WooCommerce\PayPalCommerce\Googlepay\Helper\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; return array( @@ -32,8 +32,8 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; - $fields_manager = $container->get( 'wcgateway.field-display-manager' ); - assert( $fields_manager instanceof FieldDisplayManager ); + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); return $insert_after( $fields, @@ -58,13 +58,13 @@ return array( 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( array( - $fields_manager + $display_manager ->rule() - ->condition( 'googlepay_button_enabled', 'equals', '1' ) - ->action( 'googlepay_button_type', 'visible' ) - ->action( 'googlepay_button_color', 'visible' ) - ->action( 'googlepay_button_language', 'visible' ) - ->action( 'googlepay_button_shipping_enabled', 'visible' ) + ->condition_element( 'googlepay_button_enabled', '1' ) + ->action_visible( 'googlepay_button_type' ) + ->action_visible( 'googlepay_button_color' ) + ->action_visible( 'googlepay_button_language' ) + ->action_visible( 'googlepay_button_shipping_enabled' ) ->to_array(), ) ), diff --git a/modules/ppcp-wc-gateway/resources/js/common.js b/modules/ppcp-wc-gateway/resources/js/common.js index 5a8e850b3..e1aad6028 100644 --- a/modules/ppcp-wc-gateway/resources/js/common.js +++ b/modules/ppcp-wc-gateway/resources/js/common.js @@ -1,4 +1,4 @@ -import FieldDisplayManager from "./common/FieldDisplayManager"; +import DisplayManager from "./common/display-manager/DisplayManager"; import moveWrappedElements from "./common/wrapped-elements"; document.addEventListener( @@ -10,15 +10,19 @@ document.addEventListener( moveWrappedElements(); }, 0); - - // Initialize FieldDisplayManager. - const fieldDisplayManager = new FieldDisplayManager(); + // Initialize DisplayManager. + const displayManager = new DisplayManager(); jQuery( '*[data-ppcp-display]' ).each( (index, el) => { const rules = jQuery(el).data('ppcpDisplay'); + + console.log('rules', rules); + for (const rule of rules) { - fieldDisplayManager.addRule(rule); + displayManager.addRule(rule); } }); + + displayManager.register(); } ); diff --git a/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js b/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js deleted file mode 100644 index d41128eb7..000000000 --- a/modules/ppcp-wc-gateway/resources/js/common/FieldDisplayManager.js +++ /dev/null @@ -1,74 +0,0 @@ - -class FieldDisplayManager { - - constructor() { - this.rules = []; - - document.ppcpDisplayManagerLog = () => { - console.log('rules', this.rules); - } - } - - addRule(rule) { - this.rules.push(rule); - - for (const condition of rule.conditions) { - jQuery(document).on('change', condition.selector, () => { - this.updateElementsVisibility(condition, rule); - }); - - this.updateElementsVisibility(condition, rule); - } - } - - updateElementsVisibility(condition, rule) { - let value = this.getValue(condition.selector); - value = (value !== null ? value.toString() : value); - - if (condition.value === value) { - for (const action of rule.actions) { - if (action.action === 'visible') { - jQuery(action.selector).removeClass('ppcp-field-hidden'); - } - if (action.action === 'enable') { - jQuery(action.selector).removeClass('ppcp-field-disabled') - .off('mouseup') - .find('> *') - .css('pointer-events', ''); - } - } - - } else { - for (const action of rule.actions) { - if (action.action === 'visible') { - jQuery(action.selector).addClass('ppcp-field-hidden'); - } - if (action.action === 'enable') { - jQuery(action.selector).addClass('ppcp-field-disabled') - .on('mouseup', function(event) { - event.stopImmediatePropagation(); - }) - .find('> *') - .css('pointer-events', 'none'); - } - } - } - } - - getValue(element) { - const $el = jQuery(element); - - if ($el.is(':checkbox') || $el.is(':radio')) { - if ($el.is(':checked')) { - return $el.val(); - } else { - return null; - } - } else { - return $el.val(); - } - } - -} - -export default FieldDisplayManager; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js new file mode 100644 index 000000000..8531e17e1 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js @@ -0,0 +1,14 @@ +import ElementAction from "./action/ElementAction"; + +class ActionFactory { + static make(actionConfig) { + switch (actionConfig.type) { + case 'element': + return new ElementAction(actionConfig); + } + + throw new Error('[ActionFactory] Unknown action: ' + actionConfig.type); + } +} + +export default ActionFactory; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/ConditionFactory.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ConditionFactory.js new file mode 100644 index 000000000..ef5461861 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ConditionFactory.js @@ -0,0 +1,17 @@ +import ElementCondition from "./condition/ElementCondition"; +import BoolCondition from "./condition/BoolCondition"; + +class ConditionFactory { + static make(conditionConfig, triggerUpdate) { + switch (conditionConfig.type) { + case 'element': + return new ElementCondition(conditionConfig, triggerUpdate); + case 'bool': + return new BoolCondition(conditionConfig, triggerUpdate); + } + + throw new Error('[ConditionFactory] Unknown condition: ' + conditionConfig.type); + } +} + +export default ConditionFactory; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/DisplayManager.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/DisplayManager.js new file mode 100644 index 000000000..522238686 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/DisplayManager.js @@ -0,0 +1,32 @@ +import Rule from "./Rule"; + +class DisplayManager { + + constructor() { + this.rules = {}; + this.ruleStatus = {}; // The current status for each rule. Maybe not necessary, for now just for logging. + + document.ppcpDisplayManagerLog = () => { + console.log('DisplayManager', this); + } + } + + addRule(ruleConfig) { + const updateStatus = () => { + this.ruleStatus[ruleConfig.key] = this.rules[ruleConfig.key].status; + console.log('ruleStatus', this.ruleStatus); + } + + this.rules[ruleConfig.key] = new Rule(ruleConfig, updateStatus.bind(this)); + console.log('Rule', this.rules[ruleConfig.key]); + } + + register() { + this.ruleStatus = {}; + for (const [key, rule] of Object.entries(this.rules)) { + rule.register(); + } + } +} + +export default DisplayManager; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js new file mode 100644 index 000000000..02ccdbf51 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js @@ -0,0 +1,65 @@ +import ConditionFactory from "./ConditionFactory"; +import ActionFactory from "./ActionFactory"; + +class Rule { + + constructor(config, triggerUpdate) { + this.config = config; + this.conditions = {}; + this.actions = {}; + this.triggerUpdate = triggerUpdate; + + const updateStatus = this.updateStatus.bind(this); + for (const conditionConfig of this.config.conditions) { + const condition = ConditionFactory.make(conditionConfig, updateStatus); + this.conditions[condition.key] = condition; + + console.log('Condition', condition); + } + + for (const actionConfig of this.config.actions) { + const action = ActionFactory.make(actionConfig); + this.actions[action.key] = action; + + console.log('Action', action); + } + } + + get key() { + return this.config.key; + } + + updateStatus() { + let status = true; + + for (const [key, condition] of Object.entries(this.conditions)) { + status &= condition.status; + } + + if (status !== this.status) { + this.status = status; + this.triggerUpdate(); + this.runActions(); + } + } + + runActions() { + for (const [key, action] of Object.entries(this.actions)) { + action.run(this.status); + } + } + + register() { + for (const [key, condition] of Object.entries(this.conditions)) { + condition.register(this.updateStatus.bind(this)); + } + for (const [key, action] of Object.entries(this.actions)) { + action.register(); + } + + this.updateStatus(); + } + +} + +export default Rule; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/BaseAction.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/BaseAction.js new file mode 100644 index 000000000..13b07df4f --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/BaseAction.js @@ -0,0 +1,21 @@ + +class BaseAction { + + constructor(config) { + this.config = config; + } + + get key() { + return this.config.key; + } + + register() { + // To override. + } + + run(status) { + // To override. + } +} + +export default BaseAction; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js new file mode 100644 index 000000000..f32c036a3 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js @@ -0,0 +1,35 @@ +import BaseAction from "./BaseAction"; + +class ElementAction extends BaseAction { + + run(status) { + + if (status) { + if (this.config.action === 'visible') { + jQuery(this.config.selector).removeClass('ppcp-field-hidden'); + } + if (this.config.action === 'enable') { + jQuery(this.config.selector).removeClass('ppcp-field-disabled') + .off('mouseup') + .find('> *') + .css('pointer-events', ''); + } + } else { + if (this.config.action === 'visible') { + jQuery(this.config.selector).addClass('ppcp-field-hidden'); + } + if (this.config.action === 'enable') { + jQuery(this.config.selector).addClass('ppcp-field-disabled') + .on('mouseup', function(event) { + event.stopImmediatePropagation(); + }) + .find('> *') + .css('pointer-events', 'none'); + } + } + + } + +} + +export default ElementAction; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BaseCondition.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BaseCondition.js new file mode 100644 index 000000000..1d126c128 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BaseCondition.js @@ -0,0 +1,19 @@ + +class BaseCondition { + + constructor(config, triggerUpdate) { + this.config = config; + this.status = false; + this.triggerUpdate = triggerUpdate; + } + + get key() { + return this.config.key; + } + + register() { + // To override. + } +} + +export default BaseCondition; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BoolCondition.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BoolCondition.js new file mode 100644 index 000000000..27699d2ca --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/BoolCondition.js @@ -0,0 +1,15 @@ +import BaseCondition from "./BaseCondition"; + +class BoolCondition extends BaseCondition { + + register() { + this.status = this.check(); + } + + check() { + return !! this.config.value; + } + +} + +export default BoolCondition; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/ElementCondition.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/ElementCondition.js new file mode 100644 index 000000000..efb0b4613 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/ElementCondition.js @@ -0,0 +1,27 @@ +import BaseCondition from "./BaseCondition"; +import {inputValue} from "../../../helper/form"; + +class ElementCondition extends BaseCondition { + + register() { + jQuery(document).on('change', this.config.selector, () => { + const status = this.check(); + if (status !== this.status) { + this.status = status; + this.triggerUpdate(); + } + }); + + this.status = this.check(); + } + + check() { + let value = inputValue(this.config.selector); + value = (value !== null ? value.toString() : value); + + return this.config.value === value; + } + +} + +export default ElementCondition; diff --git a/modules/ppcp-wc-gateway/resources/js/helper/form.js b/modules/ppcp-wc-gateway/resources/js/helper/form.js new file mode 100644 index 000000000..4f4891f03 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/helper/form.js @@ -0,0 +1,13 @@ +export const inputValue = (element) => { + const $el = jQuery(element); + + if ($el.is(':checkbox') || $el.is(':radio')) { + if ($el.is(':checked')) { + return $el.val(); + } else { + return null; + } + } else { + return $el.val(); + } +} diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 43aabbd5e..35d25bf23 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -48,7 +48,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGa use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; -use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\RefundFeesUpdater; @@ -1417,9 +1417,10 @@ return array( $container->get( 'wcgateway.settings' ) ); }, - 'wcgateway.field-display-manager' => SingletonDecorator::make( - static function( ContainerInterface $container ): FieldDisplayManager { - return new FieldDisplayManager(); + 'wcgateway.display-manager' => SingletonDecorator::make( + static function( ContainerInterface $container ): DisplayManager { + $settings = $container->get( 'wcgateway.settings' ); + return new DisplayManager( $settings ); } ), ); diff --git a/modules/ppcp-wc-gateway/src/Helper/DisplayManager.php b/modules/ppcp-wc-gateway/src/Helper/DisplayManager.php new file mode 100644 index 000000000..8114c6848 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/DisplayManager.php @@ -0,0 +1,60 @@ +settings = $settings; + } + + /** + * Creates and returns a rule. + * + * @param string|null $key The rule key. + * @return DisplayRule + */ + public function rule( string $key = null ): DisplayRule { + if ( null === $key ) { + $key = '_rule_' . ( (string) count( $this->rules ) ); + } + + $rule = new DisplayRule( $key, $this->settings ); + + $this->rules[ $key ] = $rule; + return $rule; + } + +} diff --git a/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php new file mode 100644 index 000000000..807ab4b36 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php @@ -0,0 +1,262 @@ +key = $key; + $this->settings = $settings; + } + + /** + * Adds a condition related to an HTML element. + * + * @param string $selector The condition selector. + * @param mixed $value The value to compare against. + * @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty). + * @return self + */ + public function condition_element( string $selector, $value, string $operation = self::CONDITION_OPERATION_EQUALS ): self { + $this->add_condition( + array( + 'type' => self::CONDITION_TYPE_ELEMENT, + 'selector' => $selector, + 'operation' => $operation, + 'value' => $value, + ) + ); + return $this; + } + + /** + * Adds a condition related to a bool check. + * + * @param bool $value The value to enable / disable the condition. + * @return self + */ + public function condition_is_true( bool $value ): self { + $this->add_condition( + array( + 'type' => self::CONDITION_TYPE_BOOL, + 'value' => $value, + ) + ); + return $this; + } + + /** + * Adds a condition related to the settings. + * + * @param string $settings_key The settings key. + * @param mixed $value The value to compare against. + * @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty). + * @return self + */ + public function condition_is_settings( string $settings_key, $value, string $operation = self::CONDITION_OPERATION_EQUALS ): self { + $settings_value = null; + + if ( $this->settings->has( $settings_key ) ) { + $settings_value = $this->settings->get( $settings_key ); + } + + $this->condition_is_true( $this->resolve_operation( $settings_value, $value, $operation ) ); + return $this; + } + + /** + * Adds a condition to show/hide the element. + * + * @param string $selector The condition selector. + */ + public function action_visible( string $selector ): self { + $this->add_action( + array( + 'type' => self::ACTION_TYPE_ELEMENT, + 'selector' => $selector, + 'action' => self::ACTION_VISIBLE, + ) + ); + return $this; + } + + /** + * Adds a condition to enable/disable the element. + * + * @param string $selector The condition selector. + */ + public function action_enable( string $selector ): self { + $this->add_action( + array( + 'type' => self::ACTION_TYPE_ELEMENT, + 'selector' => $selector, + 'action' => self::ACTION_ENABLE, + ) + ); + return $this; + } + + /** + * Adds a condition to the rule. + * + * @param array $options The condition options. + * @return void + */ + private function add_condition( array $options ): void { + if ( $this->add_selector_prefixes && isset( $options['selector'] ) ) { + $options['selector'] = '#ppcp-' . $options['selector']; // Refers to the input. + } + + if ( ! isset( $options['key'] ) ) { + $options['key'] = '_condition_' . ( (string) count( $this->conditions ) ); + } + + $this->conditions[] = $options; + } + + /** + * Adds an action to do. + * + * @param array $options The action options. + * @return void + */ + private function add_action( array $options ): void { + if ( $this->add_selector_prefixes && isset( $options['selector'] ) ) { + $options['selector'] = '#field-' . $options['selector']; // Refers to the whole field. + } + + if ( ! isset( $options['key'] ) ) { + $options['key'] = '_action_' . ( (string) count( $this->actions ) ); + } + + $this->actions[] = $options; + } + + /** + * Set if selector prefixes like, "#ppcp-" or "#field-" should be added to condition or action selectors. + * + * @param bool $add_selector_prefixes If should add prefixes. + * @return self + */ + public function should_add_selector_prefixes( bool $add_selector_prefixes = true ): self { + $this->add_selector_prefixes = $add_selector_prefixes; + return $this; + } + + /** + * Adds a condition related to the settings. + * + * @param mixed $value_1 The value 1. + * @param mixed $value_2 The value 2. + * @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty). + * @return bool + */ + private function resolve_operation( $value_1, $value_2, string $operation ): bool { + switch ( $operation ) { + case self::CONDITION_OPERATION_EQUALS: + return $value_1 === $value_2; + case self::CONDITION_OPERATION_NOT_EQUALS: + return $value_1 !== $value_2; + case self::CONDITION_OPERATION_IN: + return in_array( $value_1, $value_2, true ); + case self::CONDITION_OPERATION_NOT_IN: + return ! in_array( $value_1, $value_2, true ); + case self::CONDITION_OPERATION_EMPTY: + return empty( $value_1 ); + case self::CONDITION_OPERATION_NOT_EMPTY: + return ! empty( $value_1 ); + } + return false; + } + + /** + * Returns array representation. + * + * @return array + */ + public function to_array(): array { + return array( + 'key' => $this->key, + 'conditions' => $this->conditions, + 'actions' => $this->actions, + ); + } + + /** + * Returns JSON representation. + * + * @return string + */ + public function json(): string { + return wp_json_encode( $this->to_array() ) ?: ''; + } + +} diff --git a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php deleted file mode 100644 index 3d19c077b..000000000 --- a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayManager.php +++ /dev/null @@ -1,41 +0,0 @@ -rules ); - } - - $rule = new FieldDisplayRule( $key ); - - $this->rules[ $key ] = $rule; - return $rule; - } - -} diff --git a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php deleted file mode 100644 index e466a8f51..000000000 --- a/modules/ppcp-wc-gateway/src/Helper/FieldDisplayRule.php +++ /dev/null @@ -1,124 +0,0 @@ -key = $key; - } - - /** - * Adds a condition to the rule. - * - * @param string $selector The condition selector. - * @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty). - * @param mixed $value The value to compare against. - * @return self - */ - public function condition( string $selector, string $operation, $value ): self { - if ( $this->add_selector_prefixes ) { - $selector = '#ppcp-' . $selector; // Refers to the input. - } - $this->conditions[] = array( - 'selector' => $selector, - 'operation' => $operation, - 'value' => $value, - ); - return $this; - } - - /** - * Adds a condition to enable the element. - * - * @param string $selector The condition selector. - * @param string $action The action. - */ - public function action( string $selector, string $action ): self { - if ( $this->add_selector_prefixes ) { - $selector = '#field-' . $selector; // Refers to the whole field. - } - $this->actions[] = array( - 'selector' => $selector, - 'action' => $action, - ); - return $this; - } - - /** - * Set if selector prefixes like, "#ppcp-" or "#field-" should be added to condition or action selectors. - * - * @param bool $add_selector_prefixes If should add prefixes. - * @return self - */ - public function should_add_selector_prefixes( bool $add_selector_prefixes = true ): self { - $this->add_selector_prefixes = $add_selector_prefixes; - return $this; - } - - /** - * Returns array representation. - * - * @return array - */ - public function to_array(): array { - return array( - 'key' => $this->key, - 'conditions' => $this->conditions, - 'actions' => $this->actions, - ); - } - - /** - * Returns JSON representation. - * - * @return string - */ - public function json(): string { - return wp_json_encode( $this->to_array() ) ?: ''; - } - -} diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index d83ccca46..5a28eec88 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -17,7 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Onboarding\State; -use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; return function ( ContainerInterface $container, array $fields ): array { @@ -40,8 +40,8 @@ return function ( ContainerInterface $container, array $fields ): array { $module_url = $container->get( 'wcgateway.url' ); - $fields_manager = $container->get( 'wcgateway.field-display-manager' ); - assert( $fields_manager instanceof FieldDisplayManager ); + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); $connection_fields = array( 'ppcp_onboarading_header' => array( @@ -510,10 +510,10 @@ return function ( ContainerInterface $container, array $fields ): array { 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( array( - $fields_manager + $display_manager ->rule() - ->condition( 'subtotal_mismatch_behavior', 'equals', PurchaseUnitSanitizer::MODE_EXTRA_LINE ) - ->action( 'subtotal_mismatch_line_name', 'visible' ) + ->condition_element( 'subtotal_mismatch_behavior', PurchaseUnitSanitizer::MODE_EXTRA_LINE ) + ->action_visible( 'subtotal_mismatch_line_name' ) ->to_array(), ) ), From 71fc53f495a841034a3cb20ecaedf67f2a88a8e4 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 18 Sep 2023 08:54:54 +0100 Subject: [PATCH 04/17] Add admin display rules adjustments --- .../resources/js/common/display-manager/Rule.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js index 02ccdbf51..75e88f141 100644 --- a/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/Rule.js @@ -8,6 +8,7 @@ class Rule { this.conditions = {}; this.actions = {}; this.triggerUpdate = triggerUpdate; + this.status = null; const updateStatus = this.updateStatus.bind(this); for (const conditionConfig of this.config.conditions) { @@ -29,7 +30,7 @@ class Rule { return this.config.key; } - updateStatus() { + updateStatus(forceRunActions = false) { let status = true; for (const [key, condition] of Object.entries(this.conditions)) { @@ -40,6 +41,8 @@ class Rule { this.status = status; this.triggerUpdate(); this.runActions(); + } else if (forceRunActions) { + this.runActions(); } } @@ -57,7 +60,7 @@ class Rule { action.register(); } - this.updateStatus(); + this.updateStatus(true); } } From 9a48cd650850c36fde43c2261ae1e5032573cbba Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 11:18:40 +0200 Subject: [PATCH 05/17] Send settings shape data to front --- .../ppcp-applepay/src/Assets/DataToAppleButtonScripts.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php index c74c4cbe8..2372599af 100644 --- a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php +++ b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php @@ -173,11 +173,18 @@ class DataToAppleButtonScripts { '
' . $nonce . '
'; + $type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : ''; + $color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : ''; + $lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : ''; + return array( 'sdk_url' => $this->sdk_url, 'button' => array( 'wrapper' => 'applepay-container', 'mini_cart_wrapper' => 'applepay-container-minicart', + 'type' => $type, + 'color' => $color, + 'lang' => $lang, ), 'product' => array( 'needShipping' => $cart->needs_shipping(), From c550e9005714a4e96052f37839b0c24090c6b29d Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 11:48:48 +0200 Subject: [PATCH 06/17] Add filter for button language --- .../ppcp-applepay/src/Assets/DataToAppleButtonScripts.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php index 2372599af..e87abbd88 100644 --- a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php +++ b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php @@ -173,9 +173,10 @@ class DataToAppleButtonScripts { '
' . $nonce . '
'; - $type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : ''; - $color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : ''; - $lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : ''; + $type = $this->settings->has('applepay_button_type') ? $this->settings->get('applepay_button_type') : ''; + $color = $this->settings->has('applepay_button_color') ? $this->settings->get('applepay_button_color') : ''; + $lang = $this->settings->has('applepay_button_language') ? $this->settings->get('applepay_button_language') : ''; + $lang = apply_filters('woocommerce_paypal_payments_applepay_button_language', $lang); return array( 'sdk_url' => $this->sdk_url, From 2234b895da4642b02d2b77b14b9b38c87d58fb32 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 12:34:34 +0200 Subject: [PATCH 07/17] Send id param to hide --- modules/ppcp-applepay/resources/js/ApplepayButton.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 0a37d7763..ab1560e4a 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -94,14 +94,16 @@ class ApplepayButton { } initEventHandlers() { const { wrapper, ppcpButtonWrapper } = this.contextConfig(); + const wrapper_id = '#' + wrapper; const syncButtonVisibility = () => { const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper); - setVisible(wrapper, $ppcpButtonWrapper.is(':visible')); - setEnabled(wrapper, !$ppcpButtonWrapper.hasClass('ppcp-disabled')); + setVisible(wrapper_id, $ppcpButtonWrapper.is(':visible')); + setEnabled(wrapper_id, !$ppcpButtonWrapper.hasClass('ppcp-disabled')); } jQuery(document).on('ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', (ev, data) => { + console.log('[ApplePayButton] initEventHandlers', ev, data) if (jQuery(data.selector).is(ppcpButtonWrapper)) { syncButtonVisibility(); } From 7cf1105b510949a60cacc5586ad409a651a7d2af Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 13:23:45 +0200 Subject: [PATCH 08/17] Add filter to status check --- modules/ppcp-applepay/services.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 8929b6feb..029a4e465 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -31,12 +31,15 @@ return array( ); }, 'applepay.enabled' => static function ( ContainerInterface $container ): bool { - $status = $container->get( 'applepay.apple-product-status' ); - assert( $status instanceof AppleProductStatus ); - /** - * If merchant isn't onboarded via /v1/customer/partner-referrals this returns false as the API call fails. - */ - return apply_filters( 'woocommerce_paypal_payments_applepay_product_status', $status->apple_is_active() ); + if ( apply_filters( 'woocommerce_paypal_payments_applepay_validate_product_status', false ) ) { + $status = $container->get( 'applepay.apple-product-status' ); + assert( $status instanceof AppleProductStatus ); + /** + * If merchant isn't onboarded via /v1/customer/partner-referrals this returns false as the API call fails. + */ + return apply_filters( 'woocommerce_paypal_payments_applepay_product_status', $status->apple_is_active() ); + } + return true; }, 'applepay.server_supported' => static function ( ContainerInterface $container ): bool { return ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off'; From 09bf938a6b9d2e46e03d0b6709443c78f8515a9b Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 17:36:40 +0200 Subject: [PATCH 09/17] Add new setting if not eligible --- modules/ppcp-applepay/extensions.php | 30 +++++++++ modules/ppcp-applepay/services.php | 30 +++++++++ .../ppcp-applepay/src/Helper/ApmApplies.php | 67 +++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 modules/ppcp-applepay/src/Helper/ApmApplies.php diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 976a1a7c9..e6006dba5 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( 'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array { + $insert_after = function ( array $array, string $key, array $new ): array { $keys = array_keys( $array ); $index = array_search( $key, $keys, true ); @@ -24,6 +25,35 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; + if ( ! $container->has( 'applepay.eligible' ) || ! $container->get( 'applepay.eligible' ) ) { + $connection_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-connection#field-credentials_feature_onboarding_heading' ); + $connection_link = ''; + return $insert_after( + $fields, + 'allow_card_button_gateway', + array( + 'applepay_button_enabled' => array( + 'title' => __('Apple Pay Button', 'woocommerce-paypal-payments'), + 'type' => 'checkbox', + 'class' => array('ppcp-grayed-out-text'), + 'input_class' => array('ppcp-disabled-checkbox'), + 'label' => __('Enable Apple Pay button', 'woocommerce-paypal-payments') + . '

' + . sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __('Your PayPal account %1$srequires additional permissions%2$s to enable Apple Pay.', 'woocommerce-paypal-payments'), + $connection_link, + '' + ) + . '

', + 'default' => 'yes', + 'screens' => array(State::STATE_ONBOARDED), + 'gateway' => 'paypal', + 'requirements' => array(), + ), + ) + ); + } return $insert_after( $fields, 'allow_card_button_gateway', diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 029a4e465..c5f826aa1 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -15,10 +15,24 @@ use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton; use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus; use WooCommerce\PayPalCommerce\Applepay\Assets\DataToAppleButtonScripts; use WooCommerce\PayPalCommerce\Applepay\Assets\BlocksPaymentMethod; +use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; return array( + 'applepay.eligible' => static function ( ContainerInterface $container ): bool { + $apm_applies = $container->get( 'applepay.helpers.apm-applies' ); + assert( $apm_applies instanceof ApmApplies ); + + return $apm_applies->for_country_currency(); + }, + 'applepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { + return new ApmApplies( + $container->get( 'applepay.supported-country-currency-matrix' ), + $container->get( 'api.shop.currency' ), + $container->get( 'api.shop.country' ) + ); + }, 'applepay.status-cache' => static function( ContainerInterface $container ): Cache { return new Cache( 'ppcp-paypal-apple-status-cache' ); }, @@ -81,4 +95,20 @@ return array( $container->get( 'blocks.method' ) ); }, + /** + * The matrix which countries and currency combinations can be used for ApplePay. + */ + 'applepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array { + /** + * Returns which countries and currency combinations can be used for ApplePay. + */ + return apply_filters( + 'woocommerce_paypal_payments_applepay_supported_country_currency_matrix', + array( + 'US' => array( + 'USD', + ), + ) + ); + }, ); diff --git a/modules/ppcp-applepay/src/Helper/ApmApplies.php b/modules/ppcp-applepay/src/Helper/ApmApplies.php new file mode 100644 index 000000000..30c878057 --- /dev/null +++ b/modules/ppcp-applepay/src/Helper/ApmApplies.php @@ -0,0 +1,67 @@ +allowed_country_currency_matrix = $allowed_country_currency_matrix; + $this->currency = $currency; + $this->country = $country; + } + + /** + * Returns whether ApplePay can be used in the current country and the current currency used. + * + * @return bool + */ + public function for_country_currency(): bool { + if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) { + return false; + } + return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); + } + +} From fdd6db0579eaf36a4390156334291927725e6796 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 17:41:44 +0200 Subject: [PATCH 10/17] Add applepay to extra funding sources --- modules/ppcp-wc-gateway/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 56a2c8887..f9c88e38e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -953,6 +953,7 @@ return array( 'wcgateway.extra-funding-sources' => static function( ContainerInterface $container ): array { return array( 'googlepay' => _x( 'Google Pay', 'Name of payment method', 'woocommerce-paypal-payments' ), + 'applepay' => _x( 'Apple Pay', 'Name of payment method', 'woocommerce-paypal-payments' ), ); }, From 48781a9284917f7327254725a1095b9fbc54b0b7 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 18 Sep 2023 17:52:17 +0200 Subject: [PATCH 11/17] Fix cs --- modules/ppcp-applepay/extensions.php | 26 +++++++++---------- modules/ppcp-applepay/services.php | 18 ++++++------- .../src/Assets/DataToAppleButtonScripts.php | 8 +++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index e6006dba5..7349e2e5b 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -32,27 +32,27 @@ return array( $fields, 'allow_card_button_gateway', array( - 'applepay_button_enabled' => array( - 'title' => __('Apple Pay Button', 'woocommerce-paypal-payments'), - 'type' => 'checkbox', - 'class' => array('ppcp-grayed-out-text'), - 'input_class' => array('ppcp-disabled-checkbox'), - 'label' => __('Enable Apple Pay button', 'woocommerce-paypal-payments') + 'applepay_button_enabled' => array( + 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'class' => array( 'ppcp-grayed-out-text' ), + 'input_class' => array( 'ppcp-disabled-checkbox' ), + 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) . '

' . sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. - __('Your PayPal account %1$srequires additional permissions%2$s to enable Apple Pay.', 'woocommerce-paypal-payments'), + __( 'Your PayPal account %1$srequires additional permissions%2$s to enable Apple Pay.', 'woocommerce-paypal-payments' ), $connection_link, '' ) . '

', - 'default' => 'yes', - 'screens' => array(State::STATE_ONBOARDED), - 'gateway' => 'paypal', + 'default' => 'yes', + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', 'requirements' => array(), - ), - ) - ); + ), + ) + ); } return $insert_after( $fields, diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index c5f826aa1..9158dacae 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -33,10 +33,10 @@ return array( $container->get( 'api.shop.country' ) ); }, - 'applepay.status-cache' => static function( ContainerInterface $container ): Cache { + 'applepay.status-cache' => static function( ContainerInterface $container ): Cache { return new Cache( 'ppcp-paypal-apple-status-cache' ); }, - 'applepay.apple-product-status' => static function( ContainerInterface $container ): AppleProductStatus { + 'applepay.apple-product-status' => static function( ContainerInterface $container ): AppleProductStatus { return new AppleProductStatus( $container->get( 'wcgateway.settings' ), $container->get( 'api.endpoint.partners' ), @@ -44,7 +44,7 @@ return array( $container->get( 'onboarding.state' ) ); }, - 'applepay.enabled' => static function ( ContainerInterface $container ): bool { + 'applepay.enabled' => static function ( ContainerInterface $container ): bool { if ( apply_filters( 'woocommerce_paypal_payments_applepay_validate_product_status', false ) ) { $status = $container->get( 'applepay.apple-product-status' ); assert( $status instanceof AppleProductStatus ); @@ -55,10 +55,10 @@ return array( } return true; }, - 'applepay.server_supported' => static function ( ContainerInterface $container ): bool { + 'applepay.server_supported' => static function ( ContainerInterface $container ): bool { return ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off'; }, - 'applepay.url' => static function ( ContainerInterface $container ): string { + 'applepay.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { return ''; @@ -68,13 +68,13 @@ return array( dirname( $path, 3 ) . '/woocommerce-paypal-payments.php' ); }, - 'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string { + 'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string { return 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js'; }, - 'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts { + 'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts { return new DataToAppleButtonScripts( $container->get( 'applepay.sdk_script_url' ), $container->get( 'wcgateway.settings' ) ); }, - 'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton { + 'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton { return new ApplePayButton( $container->get( 'wcgateway.settings' ), @@ -86,7 +86,7 @@ return array( $container->get( 'wcgateway.settings.status' ) ); }, - 'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { + 'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface { return new BlocksPaymentMethod( 'ppcp-applepay', $container->get( 'applepay.url' ), diff --git a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php index e87abbd88..90f25b6c1 100644 --- a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php +++ b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php @@ -173,10 +173,10 @@ class DataToAppleButtonScripts { '
' . $nonce . '
'; - $type = $this->settings->has('applepay_button_type') ? $this->settings->get('applepay_button_type') : ''; - $color = $this->settings->has('applepay_button_color') ? $this->settings->get('applepay_button_color') : ''; - $lang = $this->settings->has('applepay_button_language') ? $this->settings->get('applepay_button_language') : ''; - $lang = apply_filters('woocommerce_paypal_payments_applepay_button_language', $lang); + $type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : ''; + $color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : ''; + $lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : ''; + $lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang ); return array( 'sdk_url' => $this->sdk_url, From 18216c8fea626fc85fdb9e13fa88b1574c6b80f6 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 19 Sep 2023 08:23:23 +0200 Subject: [PATCH 12/17] Add currencies --- modules/ppcp-applepay/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 9158dacae..4ae3ec2b0 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -106,7 +106,7 @@ return array( 'woocommerce_paypal_payments_applepay_supported_country_currency_matrix', array( 'US' => array( - 'USD', + 'AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'USD' ), ) ); From cca21234edb8aeb6249318fba332b8a925aab3df Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 19 Sep 2023 11:17:48 +0200 Subject: [PATCH 13/17] Show validation file from within --- modules/ppcp-applepay/extensions.php | 30 +++----------- modules/ppcp-applepay/services.php | 7 +++- modules/ppcp-applepay/src/ApplepayModule.php | 43 ++++++++++++++++---- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 7349e2e5b..ccbd7fbcd 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -58,7 +58,7 @@ return array( $fields, 'allow_card_button_gateway', array( - 'applepay_button_enabled' => array( + 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) @@ -81,34 +81,14 @@ return array( 'handler' => 'SubElementsHandler', 'options' => array( 'values' => array( '1' ), - 'elements' => array( '#field-applepay_sandbox_validation_file', '#field-applepay_live_validation_file', '#field-applepay_button_color', '#field-applepay_button_type', '#field-applepay_button_language' ), + 'elements' => array( '#field-applepay_button_color', '#field-applepay_button_type', '#field-applepay_button_language' ), ), ), ) ), ), ), - 'applepay_live_validation_file' => array( - 'title' => __( 'Apple Pay Live Validation File', 'woocommerce-paypal-payments' ), - 'type' => 'text', - 'desc_tip' => true, - 'description' => __( 'Paste here the validation file content', 'woocommerce-paypal-payments' ), - 'default' => null, - 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', - 'requirements' => array(), - ), - 'applepay_sandbox_validation_file' => array( - 'title' => __( 'Apple Pay Sandbox Validation File', 'woocommerce-paypal-payments' ), - 'type' => 'text', - 'desc_tip' => true, - 'description' => __( 'Paste here the validation file content', 'woocommerce-paypal-payments' ), - 'default' => null, - 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', - 'requirements' => array(), - ), - 'applepay_button_type' => array( + 'applepay_button_type' => array( 'title' => str_repeat( ' ', 6 ) . __( 'Button Label', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, @@ -124,7 +104,7 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), ), - 'applepay_button_color' => array( + 'applepay_button_color' => array( 'title' => str_repeat( ' ', 6 ) . __( 'Button Color', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, @@ -141,7 +121,7 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), ), - 'applepay_button_language' => array( + 'applepay_button_language' => array( 'title' => str_repeat( ' ', 6 ) . __( 'Button Language', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 4ae3ec2b0..1cc6f81d3 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -106,7 +106,12 @@ return array( 'woocommerce_paypal_payments_applepay_supported_country_currency_matrix', array( 'US' => array( - 'AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'USD' + 'AUD', + 'CAD', + 'EUR', + 'GBP', + 'JPY', + 'USD', ), ) ); diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index 9752642a3..23c492236 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -106,19 +106,15 @@ class ApplepayModule implements ModuleInterface { /** * Loads the validation string. * - * @param boolean $is_sandbox The environment for this merchant. - * @param Settings $settings The settings. + * @param boolean $is_sandbox The environment for this merchant. */ - protected function load_domain_association_file( bool $is_sandbox, Settings $settings ): void { + protected function load_domain_association_file( bool $is_sandbox ): void { if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { return; } $request_uri = (string) filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL ); if ( strpos( $request_uri, '.well-known/apple-developer-merchantid-domain-association' ) !== false ) { - $validation_string = $is_sandbox ? 'applepay_sandbox_validation_file' : 'applepay_live_validation_file'; - $validation_string = $settings->has( $validation_string ) ? $settings->get( $validation_string ) : ''; - $validation_string = preg_replace( '/\s+/', '', $validation_string ); - $validation_string = $validation_string ? preg_replace( '/[^a-zA-Z0-9]/', '', $validation_string ) : ''; + $validation_string = $this->validation_string( $is_sandbox ); nocache_headers(); header( 'Content-Type: text/plain', true, 200 ); echo $validation_string;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped @@ -226,7 +222,36 @@ class ApplepayModule implements ModuleInterface { $env = $c->get( 'onboarding.environment' ); assert( $env instanceof Environment ); $is_sandobx = $env->current_environment_is( Environment::SANDBOX ); - $settings = $c->get( 'wcgateway.settings' ); - $this->load_domain_association_file( $is_sandobx, $settings ); + $this->load_domain_association_file( $is_sandobx ); + } + + /** + * Returns the validation string, depending on the environment. + * + * @param bool $is_sandbox The environment for this merchant. + * @return string + */ + public function validation_string( bool $is_sandbox ) { + $sandbox_string = $this->sandbox_validation_string(); + $live_string = $this->live_validation_string(); + return $is_sandbox ? $sandbox_string : $live_string; + } + + /** + * Returns the sandbox validation string. + * + * @return string + */ + private function sandbox_validation_string(): string { + return '7B227073704964223A2241443631324543383841333039314132314539434132433035304439454130353741414535444341304542413237424243333838463239344231353534434233222C2276657273696F6E223A312C22637265617465644F6E223A313632343438393037393630362C227369676E6174757265223A2233303830303630393261383634383836663730643031303730326130383033303830303230313031333130663330306430363039363038363438303136353033303430323031303530303330383030363039326138363438383666373064303130373031303030306130383033303832303365343330383230333862613030333032303130323032303835396438613162636161663465336364333030613036303832613836343863653364303430333032333037613331326533303263303630333535303430333063323534313730373036633635323034313730373036633639363336313734363936663665323034393665373436353637373236313734363936663665323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303165313730643332333133303334333233303331333933333337333033303561313730643332333633303334333133393331333933333336333533393561333036323331323833303236303630333535303430333063316636353633363332643733366437303264363237323666366236353732326437333639363736653566353534333334326435333431346534343432346635383331313433303132303630333535303430623063306236393466353332303533373937333734363536643733333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303539333031333036303732613836343863653364303230313036303832613836343863653364303330313037303334323030303438323330666461626333396366373565323032633530643939623435313265363337653261393031646436636233653062316364346235323637393866386366346562646538316132356138633231653463333364646365386532613936633266366166613139333033343563346538376134343236636539353162313239356133383230323131333038323032306433303063303630333535316431333031303166663034303233303030333031663036303335353164323330343138333031363830313432336632343963343466393365346566323765366334663632383663336661326262666432653462333034353036303832623036303130353035303730313031303433393330333733303335303630383262303630313035303530373330303138363239363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363536313639363336313333333033323330383230313164303630333535316432303034383230313134333038323031313033303832303130633036303932613836343838366637363336343035303133303831666533303831633330363038326230363031303530353037303230323330383162363063383162333532363536633639363136653633363532303666366532303734363836393733323036333635373237343639363636393633363137343635323036323739323036313665373932303730363137323734373932303631373337333735366436353733323036313633363336353730373436313665363336353230366636363230373436383635323037343638363536653230363137303730366336393633363136323663363532303733373436313665363436313732363432303734363537323664373332303631366536343230363336663665363436393734363936663665373332303666363632303735373336353263323036333635373237343639363636393633363137343635323037303666366336393633373932303631366536343230363336353732373436393636363936333631373436393666366532303730373236313633373436393633363532303733373436313734363536643635366537343733326533303336303630383262303630313035303530373032303131363261363837343734373033613266326637373737373732653631373037303663363532653633366636643266363336353732373436393636363936333631373436353631373537343638366637323639373437393266333033343036303335353164316630343264333032623330323961303237613032353836323336383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635363136393633363133333265363337323663333031643036303335353164306530343136303431343032323433303062396165656564343633313937613461363561323939653432373138323163343533303065303630333535316430663031303166663034303430333032303738303330306630363039326138363438383666373633363430363164303430323035303033303061303630383261383634386365336430343033303230333437303033303434303232303734613162333234646234323439343330646433323734633530373463343830386439613166343830653361383563356331333632353636333235666263613330323230363933363930353361626635306235613532663966363030346463353861616436633530613764363038363833373930653061373361643031653461643938313330383230326565333038323032373561303033303230313032303230383439366432666266336139386461393733303061303630383261383634386365336430343033303233303637333131623330313930363033353530343033306331323431373037303663363532303532366636663734323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303165313730643331333433303335333033363332333333343336333333303561313730643332333933303335333033363332333333343336333333303561333037613331326533303263303630333535303430333063323534313730373036633635323034313730373036633639363336313734363936663665323034393665373436353637373236313734363936663665323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303539333031333036303732613836343863653364303230313036303832613836343863653364303330313037303334323030303466303137313138343139643736343835643531613565323538313037373665383830613265666465376261653464653038646663346239336531333335366435363635623335616532326430393737363064323234653762626130386664373631376365383863623736626236363730626563386538323938346666353434356133383166373330383166343330343630363038326230363031303530353037303130313034336133303338333033363036303832623036303130353035303733303031383632613638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635373236663666373436333631363733333330316430363033353531643065303431363034313432336632343963343466393365346566323765366334663632383663336661326262666432653462333030663036303335353164313330313031666630343035333030333031303166663330316630363033353531643233303431383330313638303134626262306465613135383333383839616134386139396465626562646562616664616362323461623330333730363033353531643166303433303330326533303263613032616130323838363236363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353732366636663734363336313637333332653633373236633330306530363033353531643066303130316666303430343033303230313036333031303036306132613836343838366637363336343036303230653034303230353030333030613036303832613836343863653364303430333032303336373030333036343032333033616366373238333531313639396231383666623335633335366361363262666634313765646439306637353464613238656265663139633831356534326237383966383938663739623539396639386435343130643866396465396332666530323330333232646435343432316230613330353737366335646633333833623930363766643137376332633231366439363466633637323639383231323666353466383761376431623939636239623039383932313631303639393066303939323164303030303331383230313863333038323031383830323031303133303831383633303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333032303835396438613162636161663465336364333030643036303936303836343830313635303330343032303130353030613038313935333031383036303932613836343838366637306430313039303333313062303630393261383634383836663730643031303730313330316330363039326138363438383666373064303130393035333130663137306433323331333033363332333333323332333533373335333935613330326130363039326138363438383666373064303130393334333131643330316233303064303630393630383634383031363530333034303230313035303061313061303630383261383634386365336430343033303233303266303630393261383634383836663730643031303930343331323230343230343239323932333766306638303764346538373932333134643438393635623165626262633038636265386333333432643365643261333939623963336538353330306130363038326138363438636533643034303330323034343733303435303232313030643430333138326637626530396663386265393738316463646461613434623332663362353634386566353666323664323933363738343237393933616530383032323037646663306563316361306461343531653464663031386236376538326231366330313065313930373431363762666632363839356230336563336430396134303030303030303030303030227D'; + } + + /** + * Returns the live validation string. + * + * @return string + */ + private function live_validation_string(): string { + return 'MIIQYQYJKoZIhvcNAQcCoIIQUjCCEE4CAQExCzAJBgUrDgMCGgUAMHAGCSqGSIb3DQEHAaBjBGF7InRlYW1JZCI6IjcyN0s1NldWNjciLCJkb21haW4iOiJ3d3cucGF5cGFsLmNvbSIsImRhdGVDcmVhdGVkIjoiMjAyMy0wNS0yMiwxNjo1NzoyOCIsInZlcnNpb24iOjF9oIINPzCCBDQwggMcoAMCAQICCEc8TGHymYjUMA0GCSqGSIb3DQEBCwUAMHMxLTArBgNVBAMMJEFwcGxlIGlQaG9uZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEgMB4GA1UECwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTIzMDQxMTIyMjM1OVoXDTI4MDQwOTIyMjM1OFowWTE1MDMGA1UEAwwsQXBwbGUgaVBob25lIE9TIFByb3Zpc2lvbmluZyBQcm9maWxlIFNpZ25pbmcxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6kPk_NNTn7aAD_zGbW6KydbD0o773Zv3wEvh-0jpzfMsGDLG7izTMTAX0ahEwvmpZKPijuurp4OCLC3BhgKvBFdTmU0MN5BDdnZ7bpwbaxWx5AH9nZwVm3d_wPQj0a6gskT6IUvSTYi8apgD0VGldPJf7vu-WYFH409ie7ljpU_SjsoQTjFVje1ckUe29jfT3zbfLFvPF05NwwVRPfwZcJ8A-LWJ19fLMBXrudbtTXuSzF2Ayww7K0cNyFtxe7TB28bXZ9DXU4aQlwHe_wa9HZmFpNfP7upB5F7-CmdwIBUdlO40Zu5Cas_fo_UQSsSYstVcaEjt-5qncP4WmbMX9QIDAQABo4HlMIHiMAwGA1UdEwEB_wQCMAAwHwYDVR0jBBgwFoAUb_GVGGJc4Mjxxe1sGMng02RSmCAwQAYIKwYBBQUHAQEENDAyMDAGCCsGAQUFBzABhiRodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFpcGNhMDcwLwYDVR0fBCgwJjAkoCKgIIYeaHR0cDovL2NybC5hcHBsZS5jb20vYWlwY2EuY3JsMB0GA1UdDgQWBBRPsztIfUSNaiCLQOVDMYL5hruH4jAOBgNVHQ8BAf8EBAMCB4AwDwYJKoZIhvdjZAY6BAIFADANBgkqhkiG9w0BAQsFAAOCAQEAP44cFOLwsvb_dVEWxh0DGYwdt-PnsJMYi76pcsUEhG4O2XscXyM6Iy-r_ILncpnbI6B1w2JOhGfLjsAsTsbilw_h0AdfXNiM93uQ7R3QAN6zZwIXbVs-82myx9PJV2TRrgJGzAt6I0LQcv11D6dZC_7VVGZFu6HlvcU9C0AQy19-ifAN32E-p3NBeGVWeZWGZRtm9QxOCq5uX-1HUrDyZ3YBpKIvhXNWFSARqVcSBWu8sPcbRjhdNf__mMZpCRNlH03Fuyutu6zgtIAu3gGzvhIn8SmRhyym5WGVP65-NQtcLg5N8jZPsYEIy6_ZYl6t7zppIJugeLueH3xKgW56JTCCBEQwggMsoAMCAQICCFxjyuRKN1PJMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBcHBsZSBJbmMuMSYwJAYDVQQLEx1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEWMBQGA1UEAxMNQXBwbGUgUm9vdCBDQTAeFw0xNzA1MTAyMTI3MzBaFw0zMDEyMzEwMDAwMDBaMHMxLTArBgNVBAMMJEFwcGxlIGlQaG9uZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEgMB4GA1UECwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyUVqAQ8-gwSGx_y_3F7wHoHuFzBzYyYu3j16JM2TPk85R7p1vvPA0vFZoqsf_gqGPNktmgfyDmu5KZEaXyIKi_FyWAWuTEtExXmngDywiOCMDCeEXRnlhxk2y-PFdrew9EFyUfQFXINLom2mUbjxJt97Xq1lDMaymFGMu30bTMFOyAjH0u1kC7TdG41PQH0bj0iWklvz0Jh-2bykGQ6ZYbtBXQHMW3d6fSTQ3NNT_8PcxZQstlpNjhgjOb3ZxlI-0fL0JYqhKof92AxGKVH_7RdsiSVrh7-KaRSfd5_DFbdos4hFvYTmBgJBZA-tKii4FcngrKeKunIENLJ4jPiyhQIDAQABo4HsMIHpMA8GA1UdEwEB_wQFMAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01_CF4wRAYIKwYBBQUHAQEEODA2MDQGCCsGAQUFBzABhihodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNhMC4GA1UdHwQnMCUwI6AhoB-GHWh0dHA6Ly9jcmwuYXBwbGUuY29tL3Jvb3QuY3JsMB0GA1UdDgQWBBRv8ZUYYlzgyPHF7WwYyeDTZFKYIDAOBgNVHQ8BAf8EBAMCAQYwEAYKKoZIhvdjZAYCEgQCBQAwDQYJKoZIhvcNAQELBQADggEBADrPrJiNvpIgIQmtlfOxXCH6Ni1XIER0c2SSCLOWrPdtl_pbNDgnzxJG0zwR8AfJmZCx0egRCaXjpWtsYwg_niX61ZmcTOblzo6yTWjsi6ujok-KERU-3BQrHMZEtm9nxVtPlSkth1w_3IMed0_t2lSnLecTgcFjxFQLG0sKaigiCNQ3knx_Zyhfrz0_t6xZHTg0ZFruM0oZQkQpxMoYa-HBUy0t9E3CFfYzMhh48SZvik3rlEyj6P8PswOLZdrrLthlUJ_cn4rfMaiEVNxSUkHSshMdMUZHiF8-7sPyjCMEleusij6CbAafLuOLQ5piWzQN9JnPLO66coYZI6X8jrUwggS7MIIDo6ADAgECAgECMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBcHBsZSBJbmMuMSYwJAYDVQQLEx1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEWMBQGA1UEAxMNQXBwbGUgUm9vdCBDQTAeFw0wNjA0MjUyMTQwMzZaFw0zNTAyMDkyMTQwMzZaMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBcHBsZSBJbmMuMSYwJAYDVQQLEx1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEWMBQGA1UEAxMNQXBwbGUgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1eeYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsqwx-VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV_X5vyJQO6VY9NXQ3xZDUjFUsVWR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw-dPfMrSSgayP7OtbkO2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4--NwzeajTEV-H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz_2deZbxJ2HafMxRloXeUyS0CAwEAAaOCAXowggF2MA4GA1UdDwEB_wQEAwIBBjAPBgNVHRMBAf8EBTADAQH_MB0GA1UdDgQWBBQr0GlHlHYJ_vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlHlHYJ_vRrjS5ApvdHTX8IXjCCAREGA1UdIASCAQgwggEEMIIBAAYJKoZIhvdjZAUBMIHyMCoGCCsGAQUFBwIBFh5odHRwczovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDQYJKoZIhvcNAQEFBQADggEBAFw2mUwteLftjJvc83eb8nbSdzBPwR-Fg4UbmT1HN_Kpm0COLNSxkBLYvvRzm-7SZA_LeU802KI--Xj_a8gH7H05g4tTINM4xLG_mk8Ka_8r_FmnBQl8F0BWER5007eLIztHo9VvJOLr0bdw3w9F4SfK8W147ee1Fxeo3H4iNcol1dkP1mvUoiQjEfehrI9zgWDGG1sJL5Ky-ERI8GA4nhX1PSZnIIozavcNgs_e66Mv-VNqW2TAYzN39zoHLFbr2g8hDtq6cxlPtdk2f8GHVdmnmbkyQvvY1XGefqFStxu9k0IkEirHDx22TZxeY8hLgBdQqorV2uT80AkHN7B1dSExggKFMIICgQIBATB_MHMxLTArBgNVBAMMJEFwcGxlIGlQaG9uZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEgMB4GA1UECwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTAghHPExh8pmI1DAJBgUrDgMCGgUAoIHcMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDUyMjE2NTcyOFowIwYJKoZIhvcNAQkEMRYEFBk0Jou5Q9k_Gz261EnWre3Q-OPtMCkGCSqGSIb3DQEJNDEcMBowCQYFKw4DAhoFAKENBgkqhkiG9w0BAQEFADBSBgkqhkiG9w0BCQ8xRTBDMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkqhkiG9w0BAQEFAASCAQDjOe_MP0OcjbZQC0lu6X5sf4MoUVimMKvnJgNLVgYPtZXufz6-_P98rgh5EPtPb-UBi1MPh2w6mHr-i9qZxklMR3sa-CuVwlkFFrPthelMfZU0YsMBv2zAOD5ZWnUrMc1Tc4myrZEK4DoNVtSG5kS5TvU_F_Cd3gPuCML4DIzrfaRMW9u9h_VehOcQIlultruQn2Wl1ihmi2_D8Q5fXF4E1dXe-a5ohdKCLWCXa7IhW818nfFbophoVDN4ymAY_1bMXp4iLa06iXXfZqQJqHnG2rFsUSLsGX49qSnNtR-1N23EtHgJQMqQpTz8C6VKI93ySOC3ijSqvRnm4Dgdl0HZ'; } } From f1ec501311dce8f16970f7f53fbc1d6fc9190110 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 19 Sep 2023 12:22:08 +0200 Subject: [PATCH 14/17] Merge PCP-1970 --- modules/ppcp-applepay/extensions.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index d531564e6..f1e17b10e 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -24,6 +24,8 @@ return array( return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); }; + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); if ( ! $container->has( 'applepay.eligible' ) || ! $container->get( 'applepay.eligible' ) ) { $connection_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-connection#field-credentials_feature_onboarding_heading' ); @@ -50,17 +52,21 @@ return array( 'screens' => array( State::STATE_ONBOARDED ), 'gateway' => 'paypal', 'requirements' => array(), + 'custom_attributes' => array( + 'data-ppcp-display' => wp_json_encode( + array( + $display_manager + ->rule() + ->condition_element( 'applepay_button_enabled', '1' ) + ->action_enable( 'applepay_button_enabled' ) + ->to_array(), + ) + ), + ), ), ) ); } - return $insert_after( - $fields, - 'allow_card_button_gateway', - array( - 'applepay_button_enabled' => array( - $display_manager = $container->get( 'wcgateway.display-manager' ); - assert( $display_manager instanceof DisplayManager ); return $insert_after( $fields, @@ -88,8 +94,6 @@ return array( $display_manager ->rule() ->condition_element( 'applepay_button_enabled', '1' ) - ->action_visible( 'applepay_sandbox_validation_file' ) - ->action_visible( 'applepay_live_validation_file' ) ->action_visible( 'applepay_button_color' ) ->action_visible( 'applepay_button_type' ) ->action_visible( 'applepay_button_language' ) From a45f3c4436b0081472e70f5a107b82c4090763d4 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 19 Sep 2023 12:44:26 +0200 Subject: [PATCH 15/17] Fix cs --- modules/ppcp-applepay/extensions.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index f1e17b10e..64c41b52c 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -35,11 +35,11 @@ return array( 'allow_card_button_gateway', array( 'applepay_button_enabled' => array( - 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), - 'type' => 'checkbox', - 'class' => array( 'ppcp-grayed-out-text' ), - 'input_class' => array( 'ppcp-disabled-checkbox' ), - 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) + 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'class' => array( 'ppcp-grayed-out-text' ), + 'input_class' => array( 'ppcp-disabled-checkbox' ), + 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) . '

' . sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. @@ -48,10 +48,10 @@ return array( '' ) . '

', - 'default' => 'yes', - 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', - 'requirements' => array(), + 'default' => 'yes', + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( array( @@ -72,7 +72,7 @@ return array( $fields, 'allow_card_button_gateway', array( - 'applepay_button_enabled' => array( + 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) From ccf11e4d8c04dbc445b99aa88f52365d8ebb8bb1 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Thu, 21 Sep 2023 09:53:17 +0200 Subject: [PATCH 16/17] Use checkout form data on payment sheet --- .../resources/js/ApplepayButton.js | 121 +++++++++++++++--- 1 file changed, 101 insertions(+), 20 deletions(-) diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index ab1560e4a..af9720636 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -2,6 +2,8 @@ import ContextHandlerFactory from "./Context/ContextHandlerFactory"; import {createAppleErrors} from "./Helper/applePayError"; import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; +import FormValidator from "../../../ppcp-button/resources/js/modules/Helper/FormValidator"; +import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler'; class ApplepayButton { @@ -13,6 +15,7 @@ class ApplepayButton { this.buttonConfig = buttonConfig; this.ppcpConfig = ppcpConfig; this.paymentsClient = null; + this.form_saved = false; this.contextHandler = ContextHandlerFactory.create( this.context, @@ -42,14 +45,14 @@ class ApplepayButton { if (isEligible) { this.fetchTransactionInfo().then(() => { const isSubscriptionProduct = this.ppcpConfig.data_client_id.has_subscriptions === true; - if(isSubscriptionProduct) { + if (isSubscriptionProduct) { return; } this.addButton(); const id_minicart = "#apple-" + this.buttonConfig.button.mini_cart_wrapper; const id = "#apple-" + this.buttonConfig.button.wrapper; - if(this.context === 'mini-cart') { + if (this.context === 'mini-cart') { document.querySelector(id_minicart).addEventListener('click', (evt) => { evt.preventDefault(); this.onButtonClick(); @@ -60,10 +63,13 @@ class ApplepayButton { this.onButtonClick(); }); } + + // Listen for changes on any input within the WooCommerce checkout form + jQuery('form.checkout').on('change', 'input, select, textarea', () => { + this.fetchTransactionInfo(); + }); }); } - console.log('[ApplePayButton] init done', this.buttonConfig.ajax_url); - } async fetchTransactionInfo() { this.transactionInfo = await this.contextHandler.transactionInfo(); @@ -103,7 +109,6 @@ class ApplepayButton { } jQuery(document).on('ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', (ev, data) => { - console.log('[ApplePayButton] initEventHandlers', ev, data) if (jQuery(data.selector).is(ppcpButtonWrapper)) { syncButtonVisibility(); } @@ -122,6 +127,7 @@ class ApplepayButton { } session.onvalidatemerchant = this.onvalidatemerchant(session); session.onpaymentauthorized = this.onpaymentauthorized(session); + return session; } @@ -131,19 +137,15 @@ class ApplepayButton { * Add a Apple Pay purchase button */ addButton() { - console.log('[ApplePayButton] context', this.context); const wrapper = (this.context === 'mini-cart') ? this.buttonConfig.button.mini_cart_wrapper : this.buttonConfig.button.wrapper; -console.log('[ApplePayButton] wrapper', wrapper) const shape = (this.context === 'mini-cart') ? this.ppcpConfig.button.mini_cart_style.shape : this.ppcpConfig.button.style.shape; const appleContainer = this.context === 'mini-cart' ? document.getElementById("applepay-container-minicart") : document.getElementById("applepay-container"); - console.log('[ApplePayButton] shape', shape) - console.log('[ApplePayButton] container', appleContainer) const type = this.buttonConfig.button.type; const language = this.buttonConfig.button.lang; const color = this.buttonConfig.button.color; @@ -152,7 +154,6 @@ console.log('[ApplePayButton] wrapper', wrapper) jQuery('#' + wrapper).addClass('ppcp-button-' + shape); jQuery(wrapper).append(appleContainer); - console.log('[ApplePayButton] addButton', wrapper, appleContainer); } //------------------------ @@ -162,13 +163,58 @@ console.log('[ApplePayButton] wrapper', wrapper) /** * Show Apple Pay payment sheet when Apple Pay payment button is clicked */ - onButtonClick() { + async onButtonClick() { const paymentDataRequest = this.paymentDataRequest(); - console.log('[ApplePayButton] onButtonClick: paymentDataRequest', paymentDataRequest, this.context); - + // trigger woocommerce validation if we are in the checkout page + if (this.context === 'checkout') { + const checkoutFormSelector = 'form.woocommerce-checkout'; + const errorHandler = new ErrorHandler( + PayPalCommerceGateway.labels.error.generic, + document.querySelector('.woocommerce-notices-wrapper') + ); + try { + const formData = new FormData(document.querySelector(checkoutFormSelector)); + this.form_saved = Object.fromEntries(formData.entries()); + this.update_request_data_with_form(paymentDataRequest); + } catch (error) { + console.error(error); + } + const session = this.applePaySession(paymentDataRequest) + console.log("session", session) + const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ? + new FormValidator( + PayPalCommerceGateway.ajax.validate_checkout.endpoint, + PayPalCommerceGateway.ajax.validate_checkout.nonce, + ) : null; + if (formValidator) { + try { + const errors = await formValidator.validate(document.querySelector(checkoutFormSelector)); + if (errors.length > 0) { + errorHandler.messages(errors); + // fire WC event for other plugins + jQuery( document.body ).trigger( 'checkout_error' , [ errorHandler.currentHtml() ] ); + // stop Apple Pay payment sheet from showing + session.abort(); + return; + } + } catch (error) { + console.error(error); + } + } + return; + } this.applePaySession(paymentDataRequest) } + update_request_data_with_form(paymentDataRequest) { + paymentDataRequest.billingContact = this.fill_billing_contact(this.form_saved); + paymentDataRequest.applicationData = this.fill_application_data(this.form_saved); + if (!this.buttonConfig.product.needShipping) { + return; + } + paymentDataRequest.shippingContact = this.fill_shipping_contact(this.form_saved); + } + paymentDataRequest() { const applepayConfig = this.applePayConfig const buttonConfig = this.buttonConfig @@ -176,12 +222,9 @@ console.log('[ApplePayButton] wrapper', wrapper) countryCode: applepayConfig.countryCode, merchantCapabilities: applepayConfig.merchantCapabilities, supportedNetworks: applepayConfig.supportedNetworks, - requiredShippingContactFields: ["name", "phone", - "email", "postalAddress"], - requiredBillingContactFields: ["name", "phone", "email", - "postalAddress"] + requiredShippingContactFields: ["postalAddress"], + requiredBillingContactFields: ["postalAddress"] } - console.log('[ApplePayButton] paymentDataRequest', applepayConfig, buttonConfig); const paymentDataRequest = Object.assign({}, baseRequest); paymentDataRequest.currencyCode = buttonConfig.shop.currencyCode; paymentDataRequest.total = { @@ -199,6 +242,7 @@ console.log('[ApplePayButton] wrapper', wrapper) //------------------------ onvalidatemerchant(session) { + console.log("onvalidatemerchant") return (applePayValidateMerchantEvent) => { paypal.Applepay().validateMerchant({ validationUrl: applePayValidateMerchantEvent.validationURL @@ -236,7 +280,6 @@ console.log('[ApplePayButton] wrapper', wrapper) onshippingmethodselected(session) { const ajax_url = this.buttonConfig.ajax_url console.log('[ApplePayButton] onshippingmethodselected'); - return (event) => { const data = this.getShippingMethodData(event); jQuery.ajax({ @@ -273,7 +316,7 @@ console.log('[ApplePayButton] wrapper', wrapper) } onshippingcontactselected(session) { const ajax_url = this.buttonConfig.ajax_url - + console.log('[ApplePayButton] onshippingcontactselected', ajax_url, session) return (event) => { const data = this.getShippingContactData(event); console.log('shipping contact selected', data, event) @@ -516,6 +559,44 @@ console.log('[ApplePayButton] wrapper', wrapper) return response; }*/ + fill_billing_contact(form_saved) { + return { + givenName: form_saved.billing_first_name ?? '', + familyName: form_saved.billing_last_name ?? '', + emailAddress: form_saved.billing_email ?? '', + phoneNumber: form_saved.billing_phone ?? '', + addressLines: [form_saved.billing_address_1, form_saved.billing_address_2], + locality: form_saved.billing_city ?? '', + postalCode: form_saved.billing_postcode ?? '', + countryCode: form_saved.billing_country ?? '', + administrativeArea: form_saved.billing_state ?? '', + } + } + fill_shipping_contact(form_saved) { + if (form_saved.shipping_first_name === "") { + return this.fill_billing_contact(form_saved) + } + return { + givenName: (form_saved?.shipping_first_name && form_saved.shipping_first_name !== "") ? form_saved.shipping_first_name : form_saved?.billing_first_name, + familyName: (form_saved?.shipping_last_name && form_saved.shipping_last_name !== "") ? form_saved.shipping_last_name : form_saved?.billing_last_name, + emailAddress: (form_saved?.shipping_email && form_saved.shipping_email !== "") ? form_saved.shipping_email : form_saved?.billing_email, + phoneNumber: (form_saved?.shipping_phone && form_saved.shipping_phone !== "") ? form_saved.shipping_phone : form_saved?.billing_phone, + addressLines: [form_saved.shipping_address_1 ?? '', form_saved.shipping_address_2 ?? ''], + locality: (form_saved?.shipping_city && form_saved.shipping_city !== "") ? form_saved.shipping_city : form_saved?.billing_city, + postalCode: (form_saved?.shipping_postcode && form_saved.shipping_postcode !== "") ? form_saved.shipping_postcode : form_saved?.billing_postcode, + countryCode: (form_saved?.shipping_country && form_saved.shipping_country !== "") ? form_saved.shipping_country : form_saved?.billing_country, + administrativeArea: (form_saved?.shipping_state && form_saved.shipping_state !== "") ? form_saved.shipping_state : form_saved?.billing_state, + } + } + + fill_application_data(form_saved) { + const jsonString = JSON.stringify(form_saved); + let utf8Str = encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (match, p1) => { + return String.fromCharCode('0x' + p1); + }); + + return btoa(utf8Str); + } } export default ApplepayButton; From f862ee6be4511b6d167ab5da8ae3050bc5aae2ef Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 21 Sep 2023 14:52:00 +0200 Subject: [PATCH 17/17] Update phpdoc --- modules/ppcp-applepay/src/Helper/ApmApplies.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-applepay/src/Helper/ApmApplies.php b/modules/ppcp-applepay/src/Helper/ApmApplies.php index 30c878057..dec9b1a08 100644 --- a/modules/ppcp-applepay/src/Helper/ApmApplies.php +++ b/modules/ppcp-applepay/src/Helper/ApmApplies.php @@ -1,6 +1,6 @@