mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge pull request #2325 from woocommerce/PCP-3179-apple-pay-google-pay-buttons-no-longer-visible-in-standard-payments-button-previews-after-moving-them-to-advanced-card-processing-tab
Apple Pay & Google Pay buttons no longer visible in Standard Payments button previews after moving them to Advanced Card Processing tab (3179)
This commit is contained in:
commit
f5803f1c99
19 changed files with 950 additions and 393 deletions
|
@ -20,6 +20,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
return array(
|
return array(
|
||||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||||
|
|
||||||
|
// Used in various places to mark fields for the preview button.
|
||||||
|
$apm_name = 'ApplePay';
|
||||||
|
|
||||||
// Eligibility check.
|
// Eligibility check.
|
||||||
if ( ! $container->has( 'applepay.eligible' ) || ! $container->get( 'applepay.eligible' ) ) {
|
if ( ! $container->has( 'applepay.eligible' ) || ! $container->get( 'applepay.eligible' ) ) {
|
||||||
return $fields;
|
return $fields;
|
||||||
|
@ -171,7 +174,7 @@ return array(
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
'custom_attributes' => array(
|
'custom_attributes' => array(
|
||||||
'data-ppcp-display' => wp_json_encode(
|
'data-ppcp-display' => wp_json_encode(
|
||||||
array(
|
array(
|
||||||
$display_manager
|
$display_manager
|
||||||
->rule()
|
->rule()
|
||||||
|
@ -183,10 +186,13 @@ return array(
|
||||||
->action_visible( 'applepay_button_type' )
|
->action_visible( 'applepay_button_type' )
|
||||||
->action_visible( 'applepay_button_language' )
|
->action_visible( 'applepay_button_language' )
|
||||||
->action_visible( 'applepay_checkout_data_mode' )
|
->action_visible( 'applepay_checkout_data_mode' )
|
||||||
|
->action_visible( 'applepay_button_preview' )
|
||||||
->action_class( 'applepay_button_enabled', 'active' )
|
->action_class( 'applepay_button_enabled', 'active' )
|
||||||
->to_array(),
|
->to_array(),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'is_enabled',
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||||
),
|
),
|
||||||
|
@ -253,56 +259,68 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_type' => array(
|
'applepay_button_type' => array(
|
||||||
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'This controls the label of the Apple Pay button.',
|
'This controls the label of the Apple Pay button.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'pay',
|
'default' => 'pay',
|
||||||
'options' => PropertiesDictionary::button_types(),
|
'options' => PropertiesDictionary::button_types(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'type',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'applepay_button_color' => array(
|
'applepay_button_color' => array(
|
||||||
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'The Apple Pay Button may appear as a black button with white lettering, white button with black lettering, or a white button with black lettering and a black outline.',
|
'The Apple Pay Button may appear as a black button with white lettering, white button with black lettering, or a white button with black lettering and a black outline.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'default' => 'black',
|
'default' => 'black',
|
||||||
'options' => PropertiesDictionary::button_colors(),
|
'options' => PropertiesDictionary::button_colors(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'color',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'applepay_button_language' => array(
|
'applepay_button_language' => array(
|
||||||
'title' => __( 'Button Language', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Language', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'The language and region used for the displayed Apple Pay button. The default value is the current language and region setting in a browser.',
|
'The language and region used for the displayed Apple Pay button. The default value is the current language and region setting in a browser.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'en',
|
'default' => 'en',
|
||||||
'options' => PropertiesDictionary::button_languages(),
|
'options' => PropertiesDictionary::button_languages(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'language',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'applepay_checkout_data_mode' => array(
|
'applepay_checkout_data_mode' => array(
|
||||||
'title' => __( 'Send checkout billing and shipping data to Apple Pay', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Send checkout billing and shipping data to Apple Pay', 'woocommerce-paypal-payments' ),
|
||||||
|
@ -318,6 +336,22 @@ return array(
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
|
'applepay_button_preview' => array(
|
||||||
|
'type' => 'ppcp-text',
|
||||||
|
'text' => sprintf(
|
||||||
|
'
|
||||||
|
<div class="ppcp-preview ppcp-button-preview" data-ppcp-apm-preview="%1$s">
|
||||||
|
<h4>' . __( 'Button Styling Preview', 'woocommerce-paypal-payments' ) . '</h4>
|
||||||
|
<div id="ppcp%1$sButtonPreview" class="ppcp-button-preview-inner"></div>
|
||||||
|
</div>',
|
||||||
|
$apm_name
|
||||||
|
),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => 'dcc',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -135,6 +135,10 @@ class ApplepayButton {
|
||||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||||
const wrapper_id = '#' + wrapper;
|
const wrapper_id = '#' + wrapper;
|
||||||
|
|
||||||
|
if (wrapper_id === ppcpButtonWrapper) {
|
||||||
|
throw new Error(`[ApplePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${wrapper_id}"`);
|
||||||
|
}
|
||||||
|
|
||||||
const syncButtonVisibility = () => {
|
const syncButtonVisibility = () => {
|
||||||
if (!this.isEligible) {
|
if (!this.isEligible) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,148 +1,112 @@
|
||||||
import {loadCustomScript} from "@paypal/paypal-js";
|
import ApplepayButton from './ApplepayButton';
|
||||||
import ApplepayButton from "./ApplepayButton";
|
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||||
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
|
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
||||||
|
|
||||||
(function ({
|
/**
|
||||||
buttonConfig,
|
* Accessor that creates and returns a single PreviewButtonManager instance.
|
||||||
jQuery
|
*/
|
||||||
}) {
|
const buttonManager = () => {
|
||||||
|
if (!ApplePayPreviewButtonManager.instance) {
|
||||||
let applePayConfig;
|
ApplePayPreviewButtonManager.instance = new ApplePayPreviewButtonManager();
|
||||||
let buttonQueue = [];
|
|
||||||
let activeButtons = {};
|
|
||||||
let bootstrapped = false;
|
|
||||||
|
|
||||||
// React to PayPal config changes.
|
|
||||||
jQuery(document).on('ppcp_paypal_render_preview', (ev, ppcpConfig) => {
|
|
||||||
if (bootstrapped) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
} else {
|
|
||||||
buttonQueue.push({
|
|
||||||
ppcpConfig: JSON.parse(JSON.stringify(ppcpConfig))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// React to ApplePay config changes.
|
|
||||||
jQuery([
|
|
||||||
'#ppcp-applepay_button_enabled',
|
|
||||||
'#ppcp-applepay_button_type',
|
|
||||||
'#ppcp-applepay_button_color',
|
|
||||||
'#ppcp-applepay_button_language'
|
|
||||||
].join(',')).on('change', () => {
|
|
||||||
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Maybe we can find a more elegant reload method when transitioning from styling modes.
|
|
||||||
jQuery([
|
|
||||||
'#ppcp-smart_button_enable_styling_per_location'
|
|
||||||
].join(',')).on('change', () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
const applyConfigOptions = function (buttonConfig) {
|
|
||||||
buttonConfig.button = buttonConfig.button || {};
|
|
||||||
buttonConfig.button.type = jQuery('#ppcp-applepay_button_type').val();
|
|
||||||
buttonConfig.button.color = jQuery('#ppcp-applepay_button_color').val();
|
|
||||||
buttonConfig.button.lang = jQuery('#ppcp-applepay_button_language').val();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createButton = function (ppcpConfig) {
|
return ApplePayPreviewButtonManager.instance;
|
||||||
const selector = ppcpConfig.button.wrapper + 'ApplePay';
|
};
|
||||||
|
|
||||||
if (!jQuery('#ppcp-applepay_button_enabled').is(':checked')) {
|
|
||||||
jQuery(selector).remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonConfig = JSON.parse(JSON.stringify(buttonConfig));
|
/**
|
||||||
buttonConfig.button.wrapper = selector.replace('#', '');
|
* Manages all Apple Pay preview buttons on this page.
|
||||||
applyConfigOptions(buttonConfig);
|
*/
|
||||||
|
class ApplePayPreviewButtonManager extends PreviewButtonManager {
|
||||||
|
constructor() {
|
||||||
|
const args = {
|
||||||
|
methodName: 'ApplePay',
|
||||||
|
buttonConfig: window.wc_ppcp_applepay_admin,
|
||||||
|
};
|
||||||
|
|
||||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-apm ppcp-button-applepay"></div>`;
|
super(args);
|
||||||
|
|
||||||
if (!jQuery(selector).length) {
|
|
||||||
jQuery(ppcpConfig.button.wrapper).after(wrapperElement);
|
|
||||||
} else {
|
|
||||||
jQuery(selector).replaceWith(wrapperElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
const button = new ApplepayButton(
|
|
||||||
'preview',
|
|
||||||
null,
|
|
||||||
buttonConfig,
|
|
||||||
ppcpConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
button.init(applePayConfig);
|
|
||||||
|
|
||||||
activeButtons[selector] = ppcpConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrap = async function () {
|
/**
|
||||||
if (!widgetBuilder.paypal) {
|
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||||
return;
|
* method.
|
||||||
|
*
|
||||||
|
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||||
|
* @return {Promise<{}>}
|
||||||
|
*/
|
||||||
|
async fetchConfig(payPal) {
|
||||||
|
const apiMethod = payPal?.Applepay()?.config;
|
||||||
|
|
||||||
|
if (!apiMethod) {
|
||||||
|
this.error('configuration object cannot be retrieved from PayPal');
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
applePayConfig = await widgetBuilder.paypal.Applepay().config();
|
return await apiMethod();
|
||||||
|
}
|
||||||
|
|
||||||
// We need to set bootstrapped here otherwise applePayConfig may not be set.
|
/**
|
||||||
bootstrapped = true;
|
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||||
|
*
|
||||||
|
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||||
|
* @return {ApplePayPreviewButton}
|
||||||
|
*/
|
||||||
|
createButtonInstance(wrapperId) {
|
||||||
|
return new ApplePayPreviewButton({
|
||||||
|
selector: wrapperId,
|
||||||
|
apiConfig: this.apiConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let options;
|
|
||||||
while (options = buttonQueue.pop()) {
|
/**
|
||||||
createButton(options.ppcpConfig);
|
* A single Apple Pay preview button instance.
|
||||||
|
*/
|
||||||
|
class ApplePayPreviewButton extends PreviewButton {
|
||||||
|
constructor(args) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
this.selector = `${args.selector}ApplePay`;
|
||||||
|
this.defaultAttributes = {
|
||||||
|
button: {
|
||||||
|
type: 'pay',
|
||||||
|
color: 'black',
|
||||||
|
lang: 'en',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewWrapper() {
|
||||||
|
const element = super.createNewWrapper();
|
||||||
|
element.addClass('ppcp-button-applepay');
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
createButton(buttonConfig) {
|
||||||
|
const button = new ApplepayButton('preview', null, buttonConfig, this.ppcpConfig);
|
||||||
|
|
||||||
|
button.init(this.apiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge form details into the config object for preview.
|
||||||
|
* Mutates the previewConfig object; no return value.
|
||||||
|
*/
|
||||||
|
dynamicPreviewConfig(buttonConfig, ppcpConfig) {
|
||||||
|
// The Apple Pay button expects the "wrapper" to be an ID without `#` prefix!
|
||||||
|
buttonConfig.button.wrapper = buttonConfig.button.wrapper.replace(/^#/, '');
|
||||||
|
|
||||||
|
// Merge the current form-values into the preview-button configuration.
|
||||||
|
if (ppcpConfig.button) {
|
||||||
|
buttonConfig.button.type = ppcpConfig.button.style.type;
|
||||||
|
buttonConfig.button.color = ppcpConfig.button.style.color;
|
||||||
|
buttonConfig.button.lang =
|
||||||
|
ppcpConfig.button.style?.lang || ppcpConfig.button.style.language;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!window.ApplePaySession) {
|
// Initialize the preview button manager.
|
||||||
jQuery('body').addClass('ppcp-non-ios-device')
|
buttonManager();
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'DOMContentLoaded',
|
|
||||||
() => {
|
|
||||||
|
|
||||||
if (typeof (buttonConfig) === 'undefined') {
|
|
||||||
console.error('PayPal button could not be configured.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let paypalLoaded = false;
|
|
||||||
let applePayLoaded = false;
|
|
||||||
|
|
||||||
const tryToBoot = () => {
|
|
||||||
if (!bootstrapped && paypalLoaded && applePayLoaded) {
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load ApplePay SDK
|
|
||||||
loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
|
|
||||||
applePayLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for PayPal to be loaded externally
|
|
||||||
if (typeof widgetBuilder.paypal !== 'undefined') {
|
|
||||||
paypalLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(document).on('ppcp-paypal-loaded', () => {
|
|
||||||
paypalLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
})({
|
|
||||||
buttonConfig: window.wc_ppcp_applepay_admin,
|
|
||||||
jQuery: window.jQuery
|
|
||||||
});
|
|
||||||
|
|
|
@ -205,7 +205,7 @@ class ApplepayModule implements ModuleInterface {
|
||||||
add_action(
|
add_action(
|
||||||
'admin_enqueue_scripts',
|
'admin_enqueue_scripts',
|
||||||
static function () use ( $c, $button ) {
|
static function () use ( $c, $button ) {
|
||||||
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-standard-payments-page' ) ) {
|
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-payment-methods-page' ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,14 +69,13 @@ class DataToAppleButtonScripts {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the appropriate admin data to send to ApplePay script
|
* Returns the appropriate admin data to send to ApplePay script
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @throws NotFoundException When the setting is not found.
|
* @throws NotFoundException When the setting is not found.
|
||||||
*/
|
*/
|
||||||
public function apple_pay_script_data_for_admin(): array {
|
public function apple_pay_script_data_for_admin() : array {
|
||||||
$base_location = wc_get_base_location();
|
$base_location = wc_get_base_location();
|
||||||
$shop_country_code = $base_location['country'];
|
$shop_country_code = $base_location['country'];
|
||||||
$currency_code = get_woocommerce_currency();
|
$currency_code = get_woocommerce_currency();
|
||||||
|
@ -250,11 +249,13 @@ class DataToAppleButtonScripts {
|
||||||
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||||
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
|
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
|
||||||
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||||
|
$is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'sdk_url' => $this->sdk_url,
|
'sdk_url' => $this->sdk_url,
|
||||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||||
'is_admin' => true,
|
'is_admin' => true,
|
||||||
|
'is_enabled' => $is_enabled,
|
||||||
'preferences' => array(
|
'preferences' => array(
|
||||||
'checkout_data_mode' => $checkout_data_mode,
|
'checkout_data_mode' => $checkout_data_mode,
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
import merge from 'deepmerge';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for APM button previews, used on the plugin's settings page.
|
||||||
|
*/
|
||||||
|
class PreviewButton {
|
||||||
|
/**
|
||||||
|
* @param {string} selector - CSS ID of the wrapper, including the `#`
|
||||||
|
* @param {object} apiConfig - PayPal configuration object; retrieved via a
|
||||||
|
* widgetBuilder API method
|
||||||
|
*/
|
||||||
|
constructor({
|
||||||
|
selector,
|
||||||
|
apiConfig,
|
||||||
|
}) {
|
||||||
|
this.apiConfig = apiConfig;
|
||||||
|
this.defaultAttributes = {};
|
||||||
|
this.buttonConfig = {};
|
||||||
|
this.ppcpConfig = {};
|
||||||
|
this.isDynamic = true;
|
||||||
|
|
||||||
|
// The selector is usually overwritten in constructor of derived class.
|
||||||
|
this.selector = selector;
|
||||||
|
this.wrapper = selector;
|
||||||
|
|
||||||
|
this.domWrapper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DOM node to contain the preview button.
|
||||||
|
*
|
||||||
|
* @return {jQuery} Always a single jQuery element with the new DOM node.
|
||||||
|
*/
|
||||||
|
createNewWrapper() {
|
||||||
|
const previewId = this.selector.replace('#', '');
|
||||||
|
const previewClass = 'ppcp-button-apm';
|
||||||
|
|
||||||
|
return jQuery(`<div id='${previewId}' class='${previewClass}'>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the "dynamic" nature of the preview.
|
||||||
|
* When the button is dynamic, it will reflect current form values. A static button always
|
||||||
|
* uses the settings that were provided via PHP.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
setDynamic(state) {
|
||||||
|
this.isDynamic = state;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets server-side configuration for the button.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
setButtonConfig(config) {
|
||||||
|
this.buttonConfig = merge(this.defaultAttributes, config);
|
||||||
|
this.buttonConfig.button.wrapper = this.selector;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the button configuration with current details from the form.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
setPpcpConfig(config) {
|
||||||
|
this.ppcpConfig = merge({}, config);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge form details into the config object for preview.
|
||||||
|
* Mutates the previewConfig object; no return value.
|
||||||
|
*/
|
||||||
|
dynamicPreviewConfig(previewConfig, formConfig) {
|
||||||
|
// Implement in derived class.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for creating the actual payment button preview.
|
||||||
|
* Called by the `render()` method, after the wrapper DOM element is ready.
|
||||||
|
*/
|
||||||
|
createButton(previewConfig) {
|
||||||
|
throw new Error('The "createButton" method must be implemented by the derived class');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the button in the DOM.
|
||||||
|
* Will always create a new button in the DOM.
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
// The APM button is disabled and cannot be enabled on the current page: Do not render it.
|
||||||
|
if (!this.isDynamic && !this.buttonConfig.is_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.domWrapper) {
|
||||||
|
if (!this.wrapper) {
|
||||||
|
console.error('Skip render, button is not configured yet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.domWrapper = this.createNewWrapper();
|
||||||
|
this.domWrapper.insertAfter(this.wrapper);
|
||||||
|
} else {
|
||||||
|
this.domWrapper.empty().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isVisible = true;
|
||||||
|
const previewButtonConfig = merge({}, this.buttonConfig);
|
||||||
|
const previewPpcpConfig = this.isDynamic ? merge({}, this.ppcpConfig) : {};
|
||||||
|
previewButtonConfig.button.wrapper = this.selector;
|
||||||
|
|
||||||
|
this.dynamicPreviewConfig(previewButtonConfig, previewPpcpConfig);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* previewButtonConfig.button.wrapper must be different from this.ppcpConfig.button.wrapper!
|
||||||
|
* If both selectors point to the same element, an infinite loop is triggered.
|
||||||
|
*/
|
||||||
|
const buttonWrapper = previewButtonConfig.button.wrapper.replace(/^#/, '');
|
||||||
|
const ppcpWrapper = this.ppcpConfig.button.wrapper.replace(/^#/, '');
|
||||||
|
|
||||||
|
if (buttonWrapper === ppcpWrapper) {
|
||||||
|
throw new Error(`[APM Preview Button] Infinite loop detected. Provide different selectors for the button/ppcp wrapper elements! Selector: "#${buttonWrapper}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createButton(previewButtonConfig);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unfortunately, a hacky way that is required to guarantee that this preview button is
|
||||||
|
* actually visible after calling the `render()` method. On some sites, we've noticed that
|
||||||
|
* certain JS events (like `ppcp-hidden`) do not fire in the expected order. This causes
|
||||||
|
* problems with preview buttons not being displayed instantly.
|
||||||
|
*
|
||||||
|
* Using a timeout here will make the button visible again at the end of the current
|
||||||
|
* event queue.
|
||||||
|
*/
|
||||||
|
setTimeout(() => this.domWrapper.show());
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.isVisible = false;
|
||||||
|
|
||||||
|
if (this.domWrapper) {
|
||||||
|
this.domWrapper.hide().empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreviewButton;
|
|
@ -0,0 +1,286 @@
|
||||||
|
import { loadCustomScript } from '@paypal/paypal-js';
|
||||||
|
import widgetBuilder from './WidgetBuilder';
|
||||||
|
import { debounce } from '../../../../../ppcp-blocks/resources/js/Helper/debounce';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all PreviewButton instances of a certain payment method on the page.
|
||||||
|
*/
|
||||||
|
class PreviewButtonManager {
|
||||||
|
/**
|
||||||
|
* Resolves the promise.
|
||||||
|
* Used by `this.boostrap()` to process enqueued initialization logic.
|
||||||
|
*/
|
||||||
|
#onInitResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A deferred Promise that is resolved once the page is ready.
|
||||||
|
* Deferred init logic can be added by using `this.#onInit.then(...)`
|
||||||
|
*
|
||||||
|
* @param {Promise<void>|null}
|
||||||
|
*/
|
||||||
|
#onInit;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
methodName,
|
||||||
|
buttonConfig,
|
||||||
|
defaultAttributes,
|
||||||
|
}) {
|
||||||
|
// Define the payment method name in the derived class.
|
||||||
|
this.methodName = methodName;
|
||||||
|
|
||||||
|
this.buttonConfig = buttonConfig;
|
||||||
|
this.defaultAttributes = defaultAttributes;
|
||||||
|
|
||||||
|
this.isEnabled = true;
|
||||||
|
this.buttons = {};
|
||||||
|
this.apiConfig = null;
|
||||||
|
|
||||||
|
this.#onInit = new Promise(resolve => {
|
||||||
|
this.#onInitResolver = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.bootstrap = this.bootstrap.bind(this);
|
||||||
|
this.renderPreview = this.renderPreview.bind(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "configureAllButtons" method applies ppcpConfig to all buttons that were created
|
||||||
|
* by this PreviewButtonManager instance. We debounce this method, as it should invoke
|
||||||
|
* only once, even if called multiple times in a row.
|
||||||
|
*
|
||||||
|
* This is required, as the `ppcp_paypal_render_preview` event does not fire for all
|
||||||
|
* buttons, but only a single time, passing in a random button's wrapper-ID; however,
|
||||||
|
* that event should always refresh all preview buttons, not only that single button.
|
||||||
|
*/
|
||||||
|
this._configureAllButtons = debounce(this._configureAllButtons.bind(this), 100);
|
||||||
|
|
||||||
|
this.registerEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected method that needs to be implemented by the derived class.
|
||||||
|
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||||
|
* @return {Promise<{}>}
|
||||||
|
*/
|
||||||
|
async fetchConfig(payPal) {
|
||||||
|
throw new Error('The "fetchConfig" method must be implemented by the derived class');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected method that needs to be implemented by the derived class.
|
||||||
|
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||||
|
*
|
||||||
|
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||||
|
* @return {PreviewButton}
|
||||||
|
*/
|
||||||
|
createButtonInstance(wrapperId) {
|
||||||
|
throw new Error('The "createButtonInstance" method must be implemented by the derived class');
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
jQuery(document).one('DOMContentLoaded', this.bootstrap);
|
||||||
|
|
||||||
|
// General event that all APM buttons react to.
|
||||||
|
jQuery(document).on('ppcp_paypal_render_preview', this.renderPreview);
|
||||||
|
|
||||||
|
// Specific event to only (re)render the current APM button type.
|
||||||
|
jQuery(document).on(`ppcp_paypal_render_preview_${this.methodName}`, this.renderPreview);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output an error message to the console, with a module-specific prefix.
|
||||||
|
*/
|
||||||
|
error(message, ...args) {
|
||||||
|
console.error(`${this.methodName} ${message}`, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this is a dynamic preview of the APM button.
|
||||||
|
* A dynamic preview adjusts to the current form settings, while a static preview uses the
|
||||||
|
* style settings that were provided from server-side.
|
||||||
|
*/
|
||||||
|
isDynamic() {
|
||||||
|
return !!document.querySelector(`[data-ppcp-apm-name="${this.methodName}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load dependencies and bootstrap the module.
|
||||||
|
* Returns a Promise that resolves once all dependencies were loaded and the module can be
|
||||||
|
* used without limitation.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async bootstrap() {
|
||||||
|
const MAX_WAIT_TIME = 10000; // Fail, if PayPal SDK is unavailable after 10 seconds.
|
||||||
|
const RESOLVE_INTERVAL = 200;
|
||||||
|
|
||||||
|
if (!this.buttonConfig || !widgetBuilder) {
|
||||||
|
this.error('Button could not be configured.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a localization object of "gateway-settings.js". If it's missing, the script was
|
||||||
|
// not loaded.
|
||||||
|
if (!window.PayPalCommerceGatewaySettings) {
|
||||||
|
this.error(
|
||||||
|
'PayPal settings are not fully loaded. Please clear the cache and reload the page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper function that clears the interval and resolves/rejects the promise.
|
||||||
|
const resolveOrReject = (resolve, reject, id, success = true) => {
|
||||||
|
clearInterval(id);
|
||||||
|
success ? resolve() : reject('Timeout while waiting for widgetBuilder.paypal');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait for the PayPal SDK to be ready.
|
||||||
|
const paypalPromise = new Promise((resolve, reject) => {
|
||||||
|
let elapsedTime = 0;
|
||||||
|
|
||||||
|
const id = setInterval(() => {
|
||||||
|
if (widgetBuilder.paypal) {
|
||||||
|
resolveOrReject(resolve, reject, id);
|
||||||
|
} else if (elapsedTime >= MAX_WAIT_TIME) {
|
||||||
|
resolveOrReject(resolve, reject, id, false);
|
||||||
|
}
|
||||||
|
elapsedTime += RESOLVE_INTERVAL;
|
||||||
|
}, RESOLVE_INTERVAL);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load the custom SDK script.
|
||||||
|
const customScriptPromise = loadCustomScript({ url: this.buttonConfig.sdk_url });
|
||||||
|
|
||||||
|
// Wait for both promises to resolve before continuing.
|
||||||
|
await Promise
|
||||||
|
.all([customScriptPromise, paypalPromise])
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`Failed to load ${this.methodName} dependencies:`, err);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
The fetchConfig method requires two objects to succeed:
|
||||||
|
(a) the SDK custom-script
|
||||||
|
(b) the `widgetBuilder.paypal` object
|
||||||
|
*/
|
||||||
|
this.apiConfig = await this.fetchConfig(widgetBuilder.paypal);
|
||||||
|
await this.#onInitResolver();
|
||||||
|
|
||||||
|
this.#onInit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler, fires on `ppcp_paypal_render_preview`
|
||||||
|
*
|
||||||
|
* @param ev - Ignored
|
||||||
|
* @param ppcpConfig - The button settings for the preview.
|
||||||
|
*/
|
||||||
|
renderPreview(ev, ppcpConfig) {
|
||||||
|
const id = ppcpConfig.button.wrapper;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
this.error('Button did not provide a wrapper ID', ppcpConfig);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.buttons[id]) {
|
||||||
|
this._addButton(id, ppcpConfig);
|
||||||
|
} else {
|
||||||
|
// This is a debounced method, that fires after 100ms.
|
||||||
|
this._configureAllButtons(ppcpConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a new configuration to an existing preview button.
|
||||||
|
*/
|
||||||
|
_configureButton(id, ppcpConfig) {
|
||||||
|
this.buttons[id]
|
||||||
|
.setDynamic(this.isDynamic())
|
||||||
|
.setPpcpConfig(ppcpConfig)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apples the provided configuration to all existing preview buttons.
|
||||||
|
*/
|
||||||
|
_configureAllButtons(ppcpConfig) {
|
||||||
|
Object.entries(this.buttons).forEach(([id, button]) => {
|
||||||
|
this._configureButton(id, {
|
||||||
|
...ppcpConfig,
|
||||||
|
button: {
|
||||||
|
...ppcpConfig.button,
|
||||||
|
|
||||||
|
// The ppcpConfig object might refer to a different wrapper.
|
||||||
|
// Fix the selector, to avoid unintentionally hidden preview buttons.
|
||||||
|
wrapper: button.wrapper,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new preview button, that is rendered once the bootstrapping Promise resolves.
|
||||||
|
*/
|
||||||
|
_addButton(id, ppcpConfig) {
|
||||||
|
const createButton = () => {
|
||||||
|
if (!this.buttons[id]) {
|
||||||
|
this.buttons[id] = this.createButtonInstance(id).setButtonConfig(this.buttonConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._configureButton(id, ppcpConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.#onInit) {
|
||||||
|
this.#onInit.then(createButton);
|
||||||
|
} else {
|
||||||
|
createButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes all buttons using the latest buttonConfig.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
renderButtons() {
|
||||||
|
if (this.isEnabled) {
|
||||||
|
Object.values(this.buttons).forEach(button => button.render());
|
||||||
|
} else {
|
||||||
|
Object.values(this.buttons).forEach(button => button.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables this payment method, which re-creates or refreshes all buttons.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
enable() {
|
||||||
|
if (!this.isEnabled) {
|
||||||
|
this.isEnabled = true;
|
||||||
|
this.renderButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables this payment method, effectively removing all preview buttons.
|
||||||
|
*
|
||||||
|
* @return {this} Reference to self, for chaining.
|
||||||
|
*/
|
||||||
|
disable() {
|
||||||
|
if (!this.isEnabled) {
|
||||||
|
this.isEnabled = false;
|
||||||
|
this.renderButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreviewButtonManager;
|
|
@ -20,6 +20,9 @@ return array(
|
||||||
|
|
||||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||||
|
|
||||||
|
// Used in various places to mark fields for the preview button.
|
||||||
|
$apm_name = 'GooglePay';
|
||||||
|
|
||||||
// Eligibility check.
|
// Eligibility check.
|
||||||
if ( ! $container->has( 'googlepay.eligible' ) || ! $container->get( 'googlepay.eligible' ) ) {
|
if ( ! $container->has( 'googlepay.eligible' ) || ! $container->get( 'googlepay.eligible' ) ) {
|
||||||
return $fields;
|
return $fields;
|
||||||
|
@ -133,7 +136,7 @@ return array(
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
'custom_attributes' => array(
|
'custom_attributes' => array(
|
||||||
'data-ppcp-display' => wp_json_encode(
|
'data-ppcp-display' => wp_json_encode(
|
||||||
array(
|
array(
|
||||||
$display_manager
|
$display_manager
|
||||||
->rule()
|
->rule()
|
||||||
|
@ -142,64 +145,79 @@ return array(
|
||||||
->action_visible( 'googlepay_button_color' )
|
->action_visible( 'googlepay_button_color' )
|
||||||
->action_visible( 'googlepay_button_language' )
|
->action_visible( 'googlepay_button_language' )
|
||||||
->action_visible( 'googlepay_button_shipping_enabled' )
|
->action_visible( 'googlepay_button_shipping_enabled' )
|
||||||
|
->action_visible( 'googlepay_button_preview' )
|
||||||
->action_class( 'googlepay_button_enabled', 'active' )
|
->action_class( 'googlepay_button_enabled', 'active' )
|
||||||
->to_array(),
|
->to_array(),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'is_enabled',
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||||
),
|
),
|
||||||
'googlepay_button_type' => array(
|
'googlepay_button_type' => array(
|
||||||
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'This controls the label of the Google Pay button.',
|
'This controls the label of the Google Pay button.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'pay',
|
'default' => 'pay',
|
||||||
'options' => PropertiesDictionary::button_types(),
|
'options' => PropertiesDictionary::button_types(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'type',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'googlepay_button_color' => array(
|
'googlepay_button_color' => array(
|
||||||
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'Google Pay payment buttons exist in two styles: dark and light. To provide contrast, use dark buttons on light backgrounds and light buttons on dark or colorful backgrounds.',
|
'Google Pay payment buttons exist in two styles: dark and light. To provide contrast, use dark buttons on light backgrounds and light buttons on dark or colorful backgrounds.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'default' => 'black',
|
'default' => 'black',
|
||||||
'options' => PropertiesDictionary::button_colors(),
|
'options' => PropertiesDictionary::button_colors(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'color',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'googlepay_button_language' => array(
|
'googlepay_button_language' => array(
|
||||||
'title' => __( 'Button Language', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Language', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'The language and region used for the displayed Google Pay button. The default value is the current language and region setting in a browser.',
|
'The language and region used for the displayed Google Pay button. The default value is the current language and region setting in a browser.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'en',
|
'default' => 'en',
|
||||||
'options' => PropertiesDictionary::button_languages(),
|
'options' => PropertiesDictionary::button_languages(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'data-ppcp-apm-name' => $apm_name,
|
||||||
|
'data-ppcp-field-name' => 'language',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'googlepay_button_shipping_enabled' => array(
|
'googlepay_button_shipping_enabled' => array(
|
||||||
'title' => __( 'Shipping Callback', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Shipping Callback', 'woocommerce-paypal-payments' ),
|
||||||
|
@ -209,13 +227,29 @@ return array(
|
||||||
'Synchronizes your available shipping options with Google Pay. Enabling this may impact the buyer experience.',
|
'Synchronizes your available shipping options with Google Pay. Enabling this may impact the buyer experience.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'classes' => array( 'ppcp-field-indent' ),
|
'classes' => array( 'ppcp-field-indent ppcp' ),
|
||||||
'label' => __( 'Enable Google Pay shipping callback', 'woocommerce-paypal-payments' ),
|
'label' => __( 'Enable Google Pay shipping callback', 'woocommerce-paypal-payments' ),
|
||||||
'default' => 'no',
|
'default' => 'no',
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'dcc',
|
'gateway' => 'dcc',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
|
'googlepay_button_preview' => array(
|
||||||
|
'type' => 'ppcp-text',
|
||||||
|
'text' => sprintf(
|
||||||
|
'
|
||||||
|
<div class="ppcp-preview ppcp-button-preview" data-ppcp-apm-preview="%1$s">
|
||||||
|
<h4>' . __( 'Button Styling Preview', 'woocommerce-paypal-payments' ) . '</h4>
|
||||||
|
<div id="ppcp%1$sButtonPreview" class="ppcp-button-preview-inner"></div>
|
||||||
|
</div>',
|
||||||
|
$apm_name
|
||||||
|
),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => 'dcc',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -139,6 +139,10 @@ class GooglepayButton {
|
||||||
initEventHandlers() {
|
initEventHandlers() {
|
||||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||||
|
|
||||||
|
if (wrapper === ppcpButtonWrapper) {
|
||||||
|
throw new Error(`[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${wrapper}"`);
|
||||||
|
}
|
||||||
|
|
||||||
const syncButtonVisibility = () => {
|
const syncButtonVisibility = () => {
|
||||||
const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper);
|
const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper);
|
||||||
setVisible(wrapper, $ppcpButtonWrapper.is(':visible'));
|
setVisible(wrapper, $ppcpButtonWrapper.is(':visible'));
|
||||||
|
|
|
@ -1,146 +1,108 @@
|
||||||
import {loadCustomScript} from "@paypal/paypal-js";
|
import GooglepayButton from './GooglepayButton';
|
||||||
import GooglepayButton from "./GooglepayButton";
|
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||||
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
|
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
||||||
|
|
||||||
(function ({
|
/**
|
||||||
buttonConfig,
|
* Accessor that creates and returns a single PreviewButtonManager instance.
|
||||||
jQuery
|
*/
|
||||||
}) {
|
const buttonManager = () => {
|
||||||
|
if (!GooglePayPreviewButtonManager.instance) {
|
||||||
let googlePayConfig;
|
GooglePayPreviewButtonManager.instance = new GooglePayPreviewButtonManager();
|
||||||
let buttonQueue = [];
|
|
||||||
let activeButtons = {};
|
|
||||||
let bootstrapped = false;
|
|
||||||
|
|
||||||
// React to PayPal config changes.
|
|
||||||
jQuery(document).on('ppcp_paypal_render_preview', (ev, ppcpConfig) => {
|
|
||||||
if (bootstrapped) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
} else {
|
|
||||||
buttonQueue.push({
|
|
||||||
ppcpConfig: JSON.parse(JSON.stringify(ppcpConfig))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// React to GooglePay config changes.
|
|
||||||
jQuery([
|
|
||||||
'#ppcp-googlepay_button_enabled',
|
|
||||||
'#ppcp-googlepay_button_type',
|
|
||||||
'#ppcp-googlepay_button_color',
|
|
||||||
'#ppcp-googlepay_button_language',
|
|
||||||
'#ppcp-googlepay_button_shipping_enabled'
|
|
||||||
].join(',')).on('change', () => {
|
|
||||||
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Maybe we can find a more elegant reload method when transitioning from styling modes.
|
|
||||||
jQuery([
|
|
||||||
'#ppcp-smart_button_enable_styling_per_location'
|
|
||||||
].join(',')).on('change', () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
|
|
||||||
createButton(ppcpConfig);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
const applyConfigOptions = function (buttonConfig) {
|
|
||||||
buttonConfig.button = buttonConfig.button || {};
|
|
||||||
buttonConfig.button.style = buttonConfig.button.style || {};
|
|
||||||
buttonConfig.button.style.type = jQuery('#ppcp-googlepay_button_type').val();
|
|
||||||
buttonConfig.button.style.color = jQuery('#ppcp-googlepay_button_color').val();
|
|
||||||
buttonConfig.button.style.language = jQuery('#ppcp-googlepay_button_language').val();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createButton = function (ppcpConfig) {
|
return GooglePayPreviewButtonManager.instance;
|
||||||
const selector = ppcpConfig.button.wrapper + 'GooglePay';
|
};
|
||||||
|
|
||||||
if (!jQuery('#ppcp-googlepay_button_enabled').is(':checked')) {
|
|
||||||
jQuery(selector).remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonConfig = JSON.parse(JSON.stringify(buttonConfig));
|
/**
|
||||||
buttonConfig.button.wrapper = selector;
|
* Manages all GooglePay preview buttons on this page.
|
||||||
applyConfigOptions(buttonConfig);
|
*/
|
||||||
|
class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
||||||
|
constructor() {
|
||||||
|
const args = {
|
||||||
|
methodName: 'GooglePay',
|
||||||
|
buttonConfig: window.wc_ppcp_googlepay_admin,
|
||||||
|
};
|
||||||
|
|
||||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-apm ppcp-button-googlepay"></div>`;
|
super(args);
|
||||||
|
|
||||||
if (!jQuery(selector).length) {
|
|
||||||
jQuery(ppcpConfig.button.wrapper).after(wrapperElement);
|
|
||||||
} else {
|
|
||||||
jQuery(selector).replaceWith(wrapperElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
const button = new GooglepayButton(
|
|
||||||
'preview',
|
|
||||||
null,
|
|
||||||
buttonConfig,
|
|
||||||
ppcpConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
button.init(googlePayConfig);
|
|
||||||
|
|
||||||
activeButtons[selector] = ppcpConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrap = async function () {
|
/**
|
||||||
if (!widgetBuilder.paypal) {
|
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||||
return;
|
* method.
|
||||||
|
*
|
||||||
|
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||||
|
* @return {Promise<{}>}
|
||||||
|
*/
|
||||||
|
async fetchConfig(payPal) {
|
||||||
|
const apiMethod = payPal?.Googlepay()?.config;
|
||||||
|
|
||||||
|
if (!apiMethod) {
|
||||||
|
this.error('configuration object cannot be retrieved from PayPal');
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
googlePayConfig = await widgetBuilder.paypal.Googlepay().config();
|
return await apiMethod();
|
||||||
|
}
|
||||||
|
|
||||||
// We need to set bootstrapped here otherwise googlePayConfig may not be set.
|
/**
|
||||||
bootstrapped = true;
|
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||||
|
*
|
||||||
|
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||||
|
* @return {GooglePayPreviewButton}
|
||||||
|
*/
|
||||||
|
createButtonInstance(wrapperId) {
|
||||||
|
return new GooglePayPreviewButton({
|
||||||
|
selector: wrapperId,
|
||||||
|
apiConfig: this.apiConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let options;
|
|
||||||
while (options = buttonQueue.pop()) {
|
/**
|
||||||
createButton(options.ppcpConfig);
|
* A single GooglePay preview button instance.
|
||||||
|
*/
|
||||||
|
class GooglePayPreviewButton extends PreviewButton {
|
||||||
|
constructor(args) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
this.selector = `${args.selector}GooglePay`;
|
||||||
|
this.defaultAttributes = {
|
||||||
|
button: {
|
||||||
|
style: {
|
||||||
|
type: 'pay',
|
||||||
|
color: 'black',
|
||||||
|
language: 'en',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewWrapper() {
|
||||||
|
const element = super.createNewWrapper();
|
||||||
|
element.addClass('ppcp-button-googlepay');
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
createButton(buttonConfig) {
|
||||||
|
const button = new GooglepayButton('preview', null, buttonConfig, this.ppcpConfig);
|
||||||
|
|
||||||
|
button.init(this.apiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge form details into the config object for preview.
|
||||||
|
* Mutates the previewConfig object; no return value.
|
||||||
|
*/
|
||||||
|
dynamicPreviewConfig(buttonConfig, ppcpConfig) {
|
||||||
|
// Merge the current form-values into the preview-button configuration.
|
||||||
|
if (ppcpConfig.button && buttonConfig.button) {
|
||||||
|
Object.assign(buttonConfig.button.style, ppcpConfig.button.style);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener(
|
// Initialize the preview button manager.
|
||||||
'DOMContentLoaded',
|
buttonManager();
|
||||||
() => {
|
|
||||||
|
|
||||||
if (typeof (buttonConfig) === 'undefined') {
|
|
||||||
console.error('PayPal button could not be configured.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let paypalLoaded = false;
|
|
||||||
let googlePayLoaded = false;
|
|
||||||
|
|
||||||
const tryToBoot = () => {
|
|
||||||
if (!bootstrapped && paypalLoaded && googlePayLoaded) {
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load GooglePay SDK
|
|
||||||
loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
|
|
||||||
googlePayLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for PayPal to be loaded externally
|
|
||||||
if (typeof widgetBuilder.paypal !== 'undefined') {
|
|
||||||
paypalLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(document).on('ppcp-paypal-loaded', () => {
|
|
||||||
paypalLoaded = true;
|
|
||||||
tryToBoot();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
})({
|
|
||||||
buttonConfig: window.wc_ppcp_googlepay_admin,
|
|
||||||
jQuery: window.jQuery
|
|
||||||
});
|
|
||||||
|
|
|
@ -418,9 +418,12 @@ class Button implements ButtonInterface {
|
||||||
$shipping['countries'] = array_keys( $this->wc_countries()->get_shipping_countries() );
|
$shipping['countries'] = array_keys( $this->wc_countries()->get_shipping_countries() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$is_enabled = $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' );
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION',
|
'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION',
|
||||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||||
|
'is_enabled' => $is_enabled,
|
||||||
'sdk_url' => $this->sdk_url,
|
'sdk_url' => $this->sdk_url,
|
||||||
'button' => array(
|
'button' => array(
|
||||||
'wrapper' => '#ppc-button-googlepay-container',
|
'wrapper' => '#ppc-button-googlepay-container',
|
||||||
|
|
|
@ -112,7 +112,7 @@ class GooglepayModule implements ModuleInterface {
|
||||||
add_action(
|
add_action(
|
||||||
'admin_enqueue_scripts',
|
'admin_enqueue_scripts',
|
||||||
static function () use ( $c, $button ) {
|
static function () use ( $c, $button ) {
|
||||||
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-standard-payments-page' ) ) {
|
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-payment-methods-page' ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,24 @@
|
||||||
width: 350px;
|
width: 350px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
|
background: #eeeeef;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
box-shadow: 0 2px 10px 1px #ddd;
|
box-shadow: 0 2px 10px 1px #ddd;
|
||||||
margin-right: -28px;
|
margin-right: -28px;
|
||||||
|
|
||||||
|
// Preview box showing a single button.
|
||||||
|
&[data-ppcp-apm-preview] {
|
||||||
|
height: 82px;
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
margin-top: -149px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 601px) and (max-width: 1399px) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { loadScript } from "@paypal/paypal-js";
|
import { loadScript } from "@paypal/paypal-js";
|
||||||
import {debounce} from "./helper/debounce";
|
import {debounce} from "./helper/debounce";
|
||||||
|
import { buttonRefreshTriggerFactory, buttonSettingsGetterFactory } from './helper/preview-button';
|
||||||
import Renderer from '../../../ppcp-button/resources/js/modules/Renderer/Renderer'
|
import Renderer from '../../../ppcp-button/resources/js/modules/Renderer/Renderer'
|
||||||
import MessageRenderer from "../../../ppcp-button/resources/js/modules/Renderer/MessageRenderer";
|
import MessageRenderer from "../../../ppcp-button/resources/js/modules/Renderer/MessageRenderer";
|
||||||
import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/modules/Helper/Hiding";
|
import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/modules/Helper/Hiding";
|
||||||
|
@ -312,6 +313,7 @@ document.addEventListener(
|
||||||
const payLaterMessagingLocations = ['product', 'cart', 'checkout', 'shop', 'home', 'general'];
|
const payLaterMessagingLocations = ['product', 'cart', 'checkout', 'shop', 'home', 'general'];
|
||||||
const paypalButtonLocations = ['product', 'cart', 'checkout', 'mini-cart', 'cart-block', 'checkout-block-express', 'general'];
|
const paypalButtonLocations = ['product', 'cart', 'checkout', 'mini-cart', 'cart-block', 'checkout-block-express', 'general'];
|
||||||
|
|
||||||
|
// Default preview buttons; on "Standard Payments" tab.
|
||||||
paypalButtonLocations.forEach((location) => {
|
paypalButtonLocations.forEach((location) => {
|
||||||
const inputNamePrefix = location === 'checkout' ? '#ppcp-button' : '#ppcp-button_' + location;
|
const inputNamePrefix = location === 'checkout' ? '#ppcp-button' : '#ppcp-button_' + location;
|
||||||
const wrapperName = location.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
|
const wrapperName = location.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
|
||||||
|
@ -330,6 +332,31 @@ document.addEventListener(
|
||||||
createButtonPreview(() => getButtonSettings('#ppcp' + wrapperName + 'ButtonPreview', fields));
|
createButtonPreview(() => getButtonSettings('#ppcp' + wrapperName + 'ButtonPreview', fields));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect DOM to find APM button previews; on tabs like "Advanced Card Payments".
|
||||||
|
*
|
||||||
|
* How it works:
|
||||||
|
*
|
||||||
|
* 1. Add a <div> to hold the preview button to the settings page:
|
||||||
|
* - `id="ppcp[NAME]ButtonPreview"`
|
||||||
|
* - `data-ppc-apm-preview="[NAME]"`
|
||||||
|
* 2. Mark all fields that are relevant for the preview button:
|
||||||
|
* - custom_attribute: `data-ppcp-apm-name="[NAME]"`
|
||||||
|
* - custom_attribute: `data-ppcp-field-name="[FIELD]"`
|
||||||
|
*
|
||||||
|
* This block will find all marked input fields and trigger a re-render of the
|
||||||
|
* preview button when one of those fields value changes.
|
||||||
|
*
|
||||||
|
* Example: See the ppcp-google-pay "extensions.php" file.
|
||||||
|
*/
|
||||||
|
document.querySelectorAll('[data-ppcp-apm-preview]').forEach(item => {
|
||||||
|
const apmName = item.dataset.ppcpApmPreview;
|
||||||
|
const getSettings = buttonSettingsGetterFactory(apmName)
|
||||||
|
const renderButtonPreview = buttonRefreshTriggerFactory(apmName);
|
||||||
|
|
||||||
|
renderPreview(getSettings, renderButtonPreview)
|
||||||
|
});
|
||||||
|
|
||||||
payLaterMessagingLocations.forEach((location) => {
|
payLaterMessagingLocations.forEach((location) => {
|
||||||
const inputNamePrefix = '#ppcp-pay_later_' + location + '_message';
|
const inputNamePrefix = '#ppcp-pay_later_' + location + '_message';
|
||||||
const wrapperName = location.charAt(0).toUpperCase() + location.slice(1);
|
const wrapperName = location.charAt(0).toUpperCase() + location.slice(1);
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Returns a Map with all input fields that are relevant to render the preview of the
|
||||||
|
* given payment button.
|
||||||
|
*
|
||||||
|
* @param {string} apmName - Value of the custom attribute `data-ppcp-apm-name`.
|
||||||
|
* @return {Map<string, {val:Function, el:HTMLInputElement}>}
|
||||||
|
*/
|
||||||
|
export function getButtonFormFields(apmName) {
|
||||||
|
const inputFields = document.querySelectorAll(`[data-ppcp-apm-name="${apmName}"]`);
|
||||||
|
|
||||||
|
return [...inputFields].reduce((fieldMap, el) => {
|
||||||
|
const key = el.dataset.ppcpFieldName;
|
||||||
|
let getter = () => el.value;
|
||||||
|
|
||||||
|
if ('LABEL' === el.tagName) {
|
||||||
|
el = el.querySelector('input[type="checkbox"]');
|
||||||
|
getter = () => el.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldMap.set(key, {
|
||||||
|
val: getter,
|
||||||
|
el,
|
||||||
|
});
|
||||||
|
}, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that triggers an update of the specified preview button, when invoked.
|
||||||
|
|
||||||
|
* @param {string} apmName
|
||||||
|
* @return {((object) => void)}
|
||||||
|
*/
|
||||||
|
export function buttonRefreshTriggerFactory(apmName) {
|
||||||
|
const eventName = `ppcp_paypal_render_preview_${apmName}`;
|
||||||
|
|
||||||
|
return (settings) => {
|
||||||
|
jQuery(document).trigger(eventName, settings);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that gets the current form values of the specified preview button.
|
||||||
|
*
|
||||||
|
* @param {string} apmName
|
||||||
|
* @return {() => {button: {wrapper:string, is_enabled:boolean, style:{}}}}
|
||||||
|
*/
|
||||||
|
export function buttonSettingsGetterFactory(apmName) {
|
||||||
|
const fields = getButtonFormFields(apmName);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const buttonConfig = {
|
||||||
|
wrapper: `#ppcp${apmName}ButtonPreview`,
|
||||||
|
'is_enabled': true,
|
||||||
|
style: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
fields.forEach((item, name) => {
|
||||||
|
if ('is_enabled' === name) {
|
||||||
|
buttonConfig[name] = item.val();
|
||||||
|
} else {
|
||||||
|
buttonConfig.style[name] = item.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { button: buttonConfig };
|
||||||
|
};
|
||||||
|
}
|
|
@ -201,9 +201,23 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
'wcgateway.is-ppcp-settings-standard-payments-page' => static function ( ContainerInterface $container ): bool {
|
// Checks, if the current admin page contains settings for this plugin's payment methods.
|
||||||
return $container->get( 'wcgateway.is-ppcp-settings-page' )
|
'wcgateway.is-ppcp-settings-payment-methods-page' => static function ( ContainerInterface $container ) : bool {
|
||||||
&& $container->get( 'wcgateway.current-ppcp-settings-page-id' ) === PayPalGateway::ID;
|
if ( ! $container->get( 'wcgateway.is-ppcp-settings-page' ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$active_tab = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
|
||||||
|
|
||||||
|
return in_array(
|
||||||
|
$active_tab,
|
||||||
|
array(
|
||||||
|
PayPalGateway::ID,
|
||||||
|
CreditCardGateway::ID,
|
||||||
|
CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
|
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
|
||||||
|
|
|
@ -112,6 +112,13 @@ class SettingsPageAssets {
|
||||||
*/
|
*/
|
||||||
private $billing_agreements_endpoint;
|
private $billing_agreements_endpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we're on a settings page for our plugin's payment methods.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $is_paypal_payment_method_page;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assets constructor.
|
* Assets constructor.
|
||||||
*
|
*
|
||||||
|
@ -128,6 +135,7 @@ class SettingsPageAssets {
|
||||||
* @param bool $is_settings_page Whether it's a settings page of this plugin.
|
* @param bool $is_settings_page Whether it's a settings page of this plugin.
|
||||||
* @param bool $is_acdc_enabled Whether the ACDC gateway is enabled.
|
* @param bool $is_acdc_enabled Whether the ACDC gateway is enabled.
|
||||||
* @param BillingAgreementsEndpoint $billing_agreements_endpoint Billing Agreements endpoint.
|
* @param BillingAgreementsEndpoint $billing_agreements_endpoint Billing Agreements endpoint.
|
||||||
|
* @param bool $is_paypal_payment_method_page Whether we're on a settings page for our plugin's payment methods.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $module_url,
|
string $module_url,
|
||||||
|
@ -142,21 +150,23 @@ class SettingsPageAssets {
|
||||||
array $all_funding_sources,
|
array $all_funding_sources,
|
||||||
bool $is_settings_page,
|
bool $is_settings_page,
|
||||||
bool $is_acdc_enabled,
|
bool $is_acdc_enabled,
|
||||||
BillingAgreementsEndpoint $billing_agreements_endpoint
|
BillingAgreementsEndpoint $billing_agreements_endpoint,
|
||||||
|
bool $is_paypal_payment_method_page
|
||||||
) {
|
) {
|
||||||
$this->module_url = $module_url;
|
$this->module_url = $module_url;
|
||||||
$this->version = $version;
|
$this->version = $version;
|
||||||
$this->subscription_helper = $subscription_helper;
|
$this->subscription_helper = $subscription_helper;
|
||||||
$this->client_id = $client_id;
|
$this->client_id = $client_id;
|
||||||
$this->currency = $currency;
|
$this->currency = $currency;
|
||||||
$this->country = $country;
|
$this->country = $country;
|
||||||
$this->environment = $environment;
|
$this->environment = $environment;
|
||||||
$this->is_pay_later_button_enabled = $is_pay_later_button_enabled;
|
$this->is_pay_later_button_enabled = $is_pay_later_button_enabled;
|
||||||
$this->disabled_sources = $disabled_sources;
|
$this->disabled_sources = $disabled_sources;
|
||||||
$this->all_funding_sources = $all_funding_sources;
|
$this->all_funding_sources = $all_funding_sources;
|
||||||
$this->is_settings_page = $is_settings_page;
|
$this->is_settings_page = $is_settings_page;
|
||||||
$this->is_acdc_enabled = $is_acdc_enabled;
|
$this->is_acdc_enabled = $is_acdc_enabled;
|
||||||
$this->billing_agreements_endpoint = $billing_agreements_endpoint;
|
$this->billing_agreements_endpoint = $billing_agreements_endpoint;
|
||||||
|
$this->is_paypal_payment_method_page = $is_paypal_payment_method_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,7 +186,7 @@ class SettingsPageAssets {
|
||||||
$this->register_admin_assets();
|
$this->register_admin_assets();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->is_paypal_payment_method_page() ) {
|
if ( $this->is_paypal_payment_method_page ) {
|
||||||
$this->register_paypal_admin_assets();
|
$this->register_paypal_admin_assets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,30 +194,6 @@ class SettingsPageAssets {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the current page is PayPal payment method settings.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function is_paypal_payment_method_page(): bool {
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_current_screen' ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$screen = get_current_screen();
|
|
||||||
if ( ! $screen || $screen->id !== 'woocommerce_page_wc-settings' ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
|
||||||
$tab = wc_clean( wp_unslash( $_GET['tab'] ?? '' ) );
|
|
||||||
$section = wc_clean( wp_unslash( $_GET['section'] ?? '' ) );
|
|
||||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
|
||||||
|
|
||||||
return 'checkout' === $tab && in_array( $section, array( PayPalGateway::ID, CardButtonGateway::ID ), true );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register assets for PayPal admin pages.
|
* Register assets for PayPal admin pages.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -183,7 +183,8 @@ class WCGatewayModule implements ModuleInterface {
|
||||||
$c->get( 'wcgateway.settings.funding-sources' ),
|
$c->get( 'wcgateway.settings.funding-sources' ),
|
||||||
$c->get( 'wcgateway.is-ppcp-settings-page' ),
|
$c->get( 'wcgateway.is-ppcp-settings-page' ),
|
||||||
$settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ),
|
$settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ),
|
||||||
$c->get( 'api.endpoint.billing-agreements' )
|
$c->get( 'api.endpoint.billing-agreements' ),
|
||||||
|
$c->get( 'wcgateway.is-ppcp-settings-payment-methods-page' )
|
||||||
);
|
);
|
||||||
$assets->register_assets();
|
$assets->register_assets();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,8 @@ class SettingsPagesAssetsTest extends TestCase
|
||||||
array(),
|
array(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
$billingAgreementEndpoint
|
$billingAgreementEndpoint,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
when('is_admin')
|
when('is_admin')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue