mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 12:25:15 +08:00
Merge branch 'trunk' into PCP-2273-block-buttons
This commit is contained in:
commit
27768fdeda
45 changed files with 1549 additions and 544 deletions
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.3', '7.4', '8.1']
|
php-versions: ['7.3', '7.4', '8.2']
|
||||||
wc-versions: ['5.9.5', '7.7.2']
|
wc-versions: ['5.9.5', '7.7.2']
|
||||||
|
|
||||||
name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }}
|
name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }}
|
||||||
|
|
2
.github/workflows/php.yml
vendored
2
.github/workflows/php.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
|
||||||
|
|
||||||
name: PHP ${{ matrix.php-versions }}
|
name: PHP ${{ matrix.php-versions }}
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
*** Changelog ***
|
*** Changelog ***
|
||||||
|
|
||||||
= 2.4.1 - xxxx-xx-xx =
|
= 2.4.1 - 2023-11-14 =
|
||||||
* Fix - Error "PayPal order ID not found in meta" prevents automations from triggering when buying subscription via third-party payment gateway #1822
|
* Fix - Error "PayPal order ID not found in meta" prevents automations from triggering when buying subscription via third-party payment gateway #1822
|
||||||
* Fix - Card button subscription support declaration #1796
|
* Fix - Card button subscription support declaration #1796
|
||||||
* Fix - Pay Later messaging disappears when updating shipping option on cart page #1807
|
* Fix - Pay Later messaging disappears when updating shipping option on cart page #1807
|
||||||
|
|
|
@ -52,5 +52,13 @@ return function ( string $root_dir ): iterable {
|
||||||
$modules[] = ( require "$modules_dir/ppcp-saved-payment-checker/module.php" )();
|
$modules[] = ( require "$modules_dir/ppcp-saved-payment-checker/module.php" )();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( apply_filters(
|
||||||
|
//phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||||
|
'woocommerce.feature-flags.woocommerce_paypal_payments.card_fields_enabled',
|
||||||
|
getenv( 'PCP_CARD_FIELDS_ENABLED' ) === '1'
|
||||||
|
) ) {
|
||||||
|
$modules[] = ( require "$modules_dir/ppcp-card-fields/module.php" )();
|
||||||
|
}
|
||||||
|
|
||||||
return $modules;
|
return $modules;
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,7 +54,7 @@ return array(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device eligibility.
|
// Device eligibility.
|
||||||
$device_eligibility_text = __( 'Your current browser/device does not seem to support Apple Pay ❌.', 'woocommerce-paypal-payments' );
|
$device_eligibility_text = __( 'Status: Your current browser/device does not seem to support Apple Pay ❌', 'woocommerce-paypal-payments' );
|
||||||
$device_eligibility_notes = sprintf(
|
$device_eligibility_notes = sprintf(
|
||||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||||
__( 'Though the button may display in previews, it won\'t appear in the shop. For details, refer to the %1$sApple Pay requirements%2$s.', 'woocommerce-paypal-payments' ),
|
__( 'Though the button may display in previews, it won\'t appear in the shop. For details, refer to the %1$sApple Pay requirements%2$s.', 'woocommerce-paypal-payments' ),
|
||||||
|
@ -62,7 +62,7 @@ return array(
|
||||||
'</a>'
|
'</a>'
|
||||||
);
|
);
|
||||||
if ( $container->get( 'applepay.is_browser_supported' ) ) {
|
if ( $container->get( 'applepay.is_browser_supported' ) ) {
|
||||||
$device_eligibility_text = __( 'Your browser/device supports Apple Pay ✔️.', 'woocommerce-paypal-payments' );
|
$device_eligibility_text = __( 'Status: Your current browser/device seems to support Apple Pay ✔️', 'woocommerce-paypal-payments' );
|
||||||
$device_eligibility_notes = __( 'The Apple Pay button will be visible both in previews and below the PayPal buttons in the shop.', 'woocommerce-paypal-payments' );
|
$device_eligibility_notes = __( 'The Apple Pay button will be visible both in previews and below the PayPal buttons in the shop.', 'woocommerce-paypal-payments' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ return array(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'applepay_button_domain_registration' => array(
|
'applepay_button_domain_registration' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Domain Registration', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Domain Registration', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'ppcp-text',
|
'type' => 'ppcp-text',
|
||||||
'text' =>
|
'text' =>
|
||||||
'<a href="' . $domain_registration_url . '" class="button" target="_blank">'
|
'<a href="' . $domain_registration_url . '" class="button" target="_blank">'
|
||||||
|
@ -177,13 +177,14 @@ return array(
|
||||||
'Registering the website domain on the PayPal site is mandated by Apple. Payments will fail if the Apple Pay button is used on an unregistered domain.',
|
'Registering the website domain on the PayPal site is mandated by Apple. Payments will fail if the Apple Pay button is used on an unregistered domain.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'paypal',
|
'gateway' => 'paypal',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_domain_validation' => array(
|
'applepay_button_domain_validation' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Domain Validation', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Domain Validation', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'ppcp-text',
|
'type' => 'ppcp-text',
|
||||||
'text' => $domain_validation_text
|
'text' => $domain_validation_text
|
||||||
. '<p class="description">'
|
. '<p class="description">'
|
||||||
|
@ -199,13 +200,14 @@ return array(
|
||||||
'Apple requires the website domain to be registered and validated. PayPal Payments automatically presents your domain association file for Apple to validate the manually registered domain.',
|
'Apple requires the website domain to be registered and validated. PayPal Payments automatically presents your domain association file for Apple to validate the manually registered domain.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'paypal',
|
'gateway' => 'paypal',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_device_eligibility' => array(
|
'applepay_button_device_eligibility' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Device Eligibility', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Device Eligibility', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'ppcp-text',
|
'type' => 'ppcp-text',
|
||||||
'text' => $device_eligibility_text
|
'text' => $device_eligibility_text
|
||||||
. '<p class="description">'
|
. '<p class="description">'
|
||||||
|
@ -216,19 +218,21 @@ return array(
|
||||||
'Apple Pay demands certain Apple devices for secure payment execution. This helps determine if your current device is compliant.',
|
'Apple Pay demands certain Apple devices for secure payment execution. This helps determine if your current device is compliant.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'screens' => array( State::STATE_ONBOARDED ),
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
'gateway' => 'paypal',
|
'gateway' => 'paypal',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_type' => array(
|
'applepay_button_type' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( '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' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'pay',
|
'default' => 'pay',
|
||||||
|
@ -238,7 +242,7 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_color' => array(
|
'applepay_button_color' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Button Color', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
|
@ -247,6 +251,7 @@ return array(
|
||||||
),
|
),
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'default' => 'black',
|
'default' => 'black',
|
||||||
'options' => PropertiesDictionary::button_colors(),
|
'options' => PropertiesDictionary::button_colors(),
|
||||||
|
@ -255,13 +260,14 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'applepay_button_language' => array(
|
'applepay_button_language' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( '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' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'en',
|
'default' => 'en',
|
||||||
|
@ -270,6 +276,20 @@ return array(
|
||||||
'gateway' => 'paypal',
|
'gateway' => 'paypal',
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
|
'applepay_checkout_data_mode' => array(
|
||||||
|
'title' => __( 'Send checkout billing and shipping data to Apple Pay', 'woocommerce-paypal-payments' ),
|
||||||
|
'type' => 'select',
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
|
'class' => array(),
|
||||||
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __( 'Using the WC form data increases convenience for the customers, but can cause issues if Apple Pay details do not match the billing and shipping data in the checkout form.', 'woocommerce-paypal-payments' ),
|
||||||
|
'default' => PropertiesDictionary::BILLING_DATA_MODE_DEFAULT,
|
||||||
|
'options' => PropertiesDictionary::billing_data_modes(),
|
||||||
|
'screens' => array( State::STATE_ONBOARDED ),
|
||||||
|
'gateway' => 'paypal',
|
||||||
|
'requirements' => array(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ApplepayButton {
|
||||||
this.buttonConfig = buttonConfig;
|
this.buttonConfig = buttonConfig;
|
||||||
this.ppcpConfig = ppcpConfig;
|
this.ppcpConfig = ppcpConfig;
|
||||||
this.paymentsClient = null;
|
this.paymentsClient = null;
|
||||||
this.form_saved = false;
|
this.formData = null;
|
||||||
|
|
||||||
this.contextHandler = ContextHandlerFactory.create(
|
this.contextHandler = ContextHandlerFactory.create(
|
||||||
this.context,
|
this.context,
|
||||||
|
@ -24,10 +24,13 @@ class ApplepayButton {
|
||||||
this.ppcpConfig
|
this.ppcpConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updated_contact_info = []
|
this.updatedContactInfo = []
|
||||||
this.selectedShippingMethod = []
|
this.selectedShippingMethod = []
|
||||||
this.nonce = document.getElementById('woocommerce-process-checkout-nonce')?.value || buttonConfig.nonce
|
this.nonce = document.getElementById('woocommerce-process-checkout-nonce')?.value || buttonConfig.nonce
|
||||||
|
|
||||||
|
// Stores initialization data sent to the button.
|
||||||
|
this.initialPaymentRequest = null;
|
||||||
|
|
||||||
this.log = function() {
|
this.log = function() {
|
||||||
if ( this.buttonConfig.is_debug ) {
|
if ( this.buttonConfig.is_debug ) {
|
||||||
console.log('[ApplePayButton]', ...arguments);
|
console.log('[ApplePayButton]', ...arguments);
|
||||||
|
@ -35,6 +38,13 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshContextData();
|
this.refreshContextData();
|
||||||
|
|
||||||
|
// Debug helpers
|
||||||
|
jQuery(document).on('ppcp-applepay-debug', () => {
|
||||||
|
console.log('ApplePayButton', this.context, this);
|
||||||
|
});
|
||||||
|
document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
|
||||||
|
document.ppcpApplepayButtons[this.context] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
init(config) {
|
init(config) {
|
||||||
|
@ -42,11 +52,16 @@ class ApplepayButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.contextHandler.validateContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.log('Init', this.context);
|
this.log('Init', this.context);
|
||||||
this.initEventHandlers();
|
this.initEventHandlers();
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
this.applePayConfig = config;
|
this.applePayConfig = config;
|
||||||
const isEligible = this.applePayConfig.isEligible;
|
const isEligible = this.applePayConfig.isEligible;
|
||||||
|
|
||||||
if (isEligible) {
|
if (isEligible) {
|
||||||
this.fetchTransactionInfo().then(() => {
|
this.fetchTransactionInfo().then(() => {
|
||||||
const isSubscriptionProduct = this.ppcpConfig?.data_client_id?.has_subscriptions === true;
|
const isSubscriptionProduct = this.ppcpConfig?.data_client_id?.has_subscriptions === true;
|
||||||
|
@ -68,11 +83,6 @@ class ApplepayButton {
|
||||||
this.onButtonClick();
|
this.onButtonClick();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for changes on any input within the WooCommerce checkout form
|
|
||||||
jQuery('form.checkout').on('change', 'input, select, textarea', () => {
|
|
||||||
this.fetchTransactionInfo();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +99,7 @@ class ApplepayButton {
|
||||||
async fetchTransactionInfo() {
|
async fetchTransactionInfo() {
|
||||||
this.transactionInfo = await this.contextHandler.transactionInfo();
|
this.transactionInfo = await this.contextHandler.transactionInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns configurations relative to this button context.
|
* Returns configurations relative to this button context.
|
||||||
*/
|
*/
|
||||||
|
@ -113,6 +124,7 @@ class ApplepayButton {
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
initEventHandlers() {
|
initEventHandlers() {
|
||||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||||
const wrapper_id = '#' + wrapper;
|
const wrapper_id = '#' + wrapper;
|
||||||
|
@ -132,22 +144,25 @@ class ApplepayButton {
|
||||||
syncButtonVisibility();
|
syncButtonVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an ApplePay session.
|
||||||
|
*/
|
||||||
applePaySession(paymentRequest) {
|
applePaySession(paymentRequest) {
|
||||||
this.log('applePaySession', paymentRequest);
|
this.log('applePaySession', paymentRequest);
|
||||||
const session = new ApplePaySession(4, paymentRequest)
|
const session = new ApplePaySession(4, paymentRequest);
|
||||||
session.begin()
|
session.begin();
|
||||||
|
|
||||||
if (this.buttonConfig.product.needShipping) {
|
if (this.shouldRequireShippingInButton()) {
|
||||||
session.onshippingmethodselected = this.onshippingmethodselected(session)
|
session.onshippingmethodselected = this.onShippingMethodSelected(session);
|
||||||
session.onshippingcontactselected = this.onshippingcontactselected(session)
|
session.onshippingcontactselected = this.onShippingContactSelected(session);
|
||||||
}
|
}
|
||||||
session.onvalidatemerchant = this.onvalidatemerchant(session);
|
session.onvalidatemerchant = this.onValidateMerchant(session);
|
||||||
session.onpaymentauthorized = this.onpaymentauthorized(session);
|
session.onpaymentauthorized = this.onPaymentAuthorized(session);
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a Apple Pay purchase button
|
* Adds an Apple Pay purchase button.
|
||||||
*/
|
*/
|
||||||
addButton() {
|
addButton() {
|
||||||
this.log('addButton', this.context);
|
this.log('addButton', this.context);
|
||||||
|
@ -184,8 +199,9 @@ class ApplepayButton {
|
||||||
async onButtonClick() {
|
async onButtonClick() {
|
||||||
this.log('onButtonClick', this.context);
|
this.log('onButtonClick', this.context);
|
||||||
|
|
||||||
const paymentDataRequest = this.paymentDataRequest();
|
const paymentRequest = this.paymentRequest();
|
||||||
// trigger woocommerce validation if we are in the checkout page
|
|
||||||
|
// Trigger woocommerce validation if we are in the checkout page.
|
||||||
if (this.context === 'checkout') {
|
if (this.context === 'checkout') {
|
||||||
const checkoutFormSelector = 'form.woocommerce-checkout';
|
const checkoutFormSelector = 'form.woocommerce-checkout';
|
||||||
const errorHandler = new ErrorHandler(
|
const errorHandler = new ErrorHandler(
|
||||||
|
@ -194,13 +210,16 @@ class ApplepayButton {
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
const formData = new FormData(document.querySelector(checkoutFormSelector));
|
const formData = new FormData(document.querySelector(checkoutFormSelector));
|
||||||
this.form_saved = Object.fromEntries(formData.entries());
|
this.formData = Object.fromEntries(formData.entries());
|
||||||
// This line should be reviewed, the widgetBuilder.paypal.Applepay().confirmOrder fails if we add it.
|
|
||||||
//this.update_request_data_with_form(paymentDataRequest);
|
this.updateRequestDataWithForm(paymentRequest);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
const session = this.applePaySession(paymentDataRequest)
|
|
||||||
|
this.log('=== paymentRequest', paymentRequest);
|
||||||
|
|
||||||
|
const session = this.applePaySession(paymentRequest);
|
||||||
const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ?
|
const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ?
|
||||||
new FormValidator(
|
new FormValidator(
|
||||||
PayPalCommerceGateway.ajax.validate_checkout.endpoint,
|
PayPalCommerceGateway.ajax.validate_checkout.endpoint,
|
||||||
|
@ -221,19 +240,115 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.applePaySession(paymentDataRequest)
|
|
||||||
|
// Default session initialization.
|
||||||
|
this.applePaySession(paymentRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_request_data_with_form(paymentDataRequest) {
|
/**
|
||||||
paymentDataRequest.billingContact = this.fill_billing_contact(this.form_saved);
|
* If the button should show the shipping fields.
|
||||||
paymentDataRequest.applicationData = this.fill_application_data(this.form_saved);
|
*
|
||||||
if (!this.buttonConfig.product.needShipping) {
|
* @returns {false|*}
|
||||||
|
*/
|
||||||
|
shouldRequireShippingInButton() {
|
||||||
|
return this.contextHandler.shippingAllowed()
|
||||||
|
&& this.buttonConfig.product.needShipping
|
||||||
|
&& (this.context !== 'checkout' || this.shouldUpdateButtonWithFormData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the button should be updated with the form addresses.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
shouldUpdateButtonWithFormData() {
|
||||||
|
if (this.context !== 'checkout') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.buttonConfig?.preferences?.checkout_data_mode === 'use_applepay';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates how payment completion should be handled if with the context handler default actions.
|
||||||
|
* Or with ApplePay module specific completion.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
shouldCompletePaymentWithContextHandler() {
|
||||||
|
// Data already handled, ex: PayNow
|
||||||
|
if (!this.contextHandler.shippingAllowed()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Use WC form data mode in Checkout.
|
||||||
|
if (this.context === 'checkout' && !this.shouldUpdateButtonWithFormData()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates ApplePay paymentRequest with form data.
|
||||||
|
*/
|
||||||
|
updateRequestDataWithForm(paymentRequest) {
|
||||||
|
if (!this.shouldUpdateButtonWithFormData()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paymentDataRequest.shippingContact = this.fill_shipping_contact(this.form_saved);
|
|
||||||
|
// Add billing address.
|
||||||
|
paymentRequest.billingContact = this.fillBillingContact(this.formData);
|
||||||
|
|
||||||
|
// Add custom data.
|
||||||
|
// "applicationData" is originating a "PayPalApplePayError: An internal server error has occurred" on paypal.Applepay().confirmOrder().
|
||||||
|
// paymentRequest.applicationData = this.fillApplicationData(this.formData);
|
||||||
|
|
||||||
|
if (!this.shouldRequireShippingInButton()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add shipping address.
|
||||||
|
paymentRequest.shippingContact = this.fillShippingContact(this.formData);
|
||||||
|
|
||||||
|
// Get shipping methods.
|
||||||
|
const rate = this.transactionInfo.chosenShippingMethods[0];
|
||||||
|
paymentRequest.shippingMethods = [];
|
||||||
|
|
||||||
|
// Add selected shipping method.
|
||||||
|
for (const shippingPackage of this.transactionInfo.shippingPackages) {
|
||||||
|
if (rate === shippingPackage.id) {
|
||||||
|
const shippingMethod = {
|
||||||
|
'label' : shippingPackage.label,
|
||||||
|
'detail' : '',
|
||||||
|
'amount' : shippingPackage.cost_str,
|
||||||
|
'identifier' : shippingPackage.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remember this shipping method as the selected one.
|
||||||
|
this.selectedShippingMethod = shippingMethod;
|
||||||
|
|
||||||
|
paymentRequest.shippingMethods.push(shippingMethod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other shipping methods.
|
||||||
|
for (const shippingPackage of this.transactionInfo.shippingPackages) {
|
||||||
|
if (rate !== shippingPackage.id) {
|
||||||
|
paymentRequest.shippingMethods.push({
|
||||||
|
'label' : shippingPackage.label,
|
||||||
|
'detail' : '',
|
||||||
|
'amount' : shippingPackage.cost_str,
|
||||||
|
'identifier' : shippingPackage.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store for reuse in case this data is not provided by ApplePay on authorization.
|
||||||
|
this.initialPaymentRequest = paymentRequest;
|
||||||
|
|
||||||
|
this.log('=== paymentRequest.shippingMethods', paymentRequest.shippingMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentDataRequest() {
|
paymentRequest() {
|
||||||
const applepayConfig = this.applePayConfig
|
const applepayConfig = this.applePayConfig
|
||||||
const buttonConfig = this.buttonConfig
|
const buttonConfig = this.buttonConfig
|
||||||
let baseRequest = {
|
let baseRequest = {
|
||||||
|
@ -241,29 +356,35 @@ class ApplepayButton {
|
||||||
merchantCapabilities: applepayConfig.merchantCapabilities,
|
merchantCapabilities: applepayConfig.merchantCapabilities,
|
||||||
supportedNetworks: applepayConfig.supportedNetworks,
|
supportedNetworks: applepayConfig.supportedNetworks,
|
||||||
requiredShippingContactFields: ["postalAddress", "email", "phone"],
|
requiredShippingContactFields: ["postalAddress", "email", "phone"],
|
||||||
requiredBillingContactFields: ["postalAddress", "email", "phone"],
|
requiredBillingContactFields: ["postalAddress"], // ApplePay does not implement billing email and phone fields.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.contextHandler.shippingAllowed()) {
|
if (!this.shouldRequireShippingInButton()) {
|
||||||
baseRequest.requiredShippingContactFields = [];
|
if (this.shouldCompletePaymentWithContextHandler()) {
|
||||||
|
// Data needs handled externally.
|
||||||
|
baseRequest.requiredShippingContactFields = [];
|
||||||
|
} else {
|
||||||
|
// Minimum data required for order creation.
|
||||||
|
baseRequest.requiredShippingContactFields = ["email", "phone"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentDataRequest = Object.assign({}, baseRequest);
|
const paymentRequest = Object.assign({}, baseRequest);
|
||||||
paymentDataRequest.currencyCode = buttonConfig.shop.currencyCode;
|
paymentRequest.currencyCode = buttonConfig.shop.currencyCode;
|
||||||
paymentDataRequest.total = {
|
paymentRequest.total = {
|
||||||
label: buttonConfig.shop.totalLabel,
|
label: buttonConfig.shop.totalLabel,
|
||||||
type: "final",
|
type: "final",
|
||||||
amount: this.transactionInfo.totalPrice,
|
amount: this.transactionInfo.totalPrice,
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentDataRequest
|
return paymentRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshContextData() {
|
refreshContextData() {
|
||||||
switch (this.context) {
|
switch (this.context) {
|
||||||
case 'product':
|
case 'product':
|
||||||
// Refresh product data that makes the price change.
|
// Refresh product data that makes the price change.
|
||||||
this.productQuantity = document.querySelector('input.qty').value;
|
this.productQuantity = document.querySelector('input.qty')?.value;
|
||||||
this.products = this.contextHandler.products();
|
this.products = this.contextHandler.products();
|
||||||
this.log('Products updated', this.products);
|
this.log('Products updated', this.products);
|
||||||
break;
|
break;
|
||||||
|
@ -274,7 +395,7 @@ class ApplepayButton {
|
||||||
// Payment process
|
// Payment process
|
||||||
//------------------------
|
//------------------------
|
||||||
|
|
||||||
onvalidatemerchant(session) {
|
onValidateMerchant(session) {
|
||||||
this.log('onvalidatemerchant', this.buttonConfig.ajax_url);
|
this.log('onvalidatemerchant', this.buttonConfig.ajax_url);
|
||||||
return (applePayValidateMerchantEvent) => {
|
return (applePayValidateMerchantEvent) => {
|
||||||
this.log('onvalidatemerchant call');
|
this.log('onvalidatemerchant call');
|
||||||
|
@ -308,83 +429,90 @@ class ApplepayButton {
|
||||||
validation: false,
|
validation: false,
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
this.log('onvalidatemerchant session abort');
|
this.log('onvalidatemerchant session abort');
|
||||||
session.abort();
|
session.abort();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
onshippingmethodselected(session) {
|
|
||||||
|
onShippingMethodSelected(session) {
|
||||||
this.log('onshippingmethodselected', this.buttonConfig.ajax_url);
|
this.log('onshippingmethodselected', this.buttonConfig.ajax_url);
|
||||||
const ajax_url = this.buttonConfig.ajax_url
|
const ajax_url = this.buttonConfig.ajax_url;
|
||||||
return (event) => {
|
return (event) => {
|
||||||
this.log('onshippingmethodselected call');
|
this.log('onshippingmethodselected call');
|
||||||
|
|
||||||
const data = this.getShippingMethodData(event);
|
const data = this.getShippingMethodData(event);
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
url: ajax_url,
|
url: ajax_url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data,
|
data: data,
|
||||||
success: (applePayShippingMethodUpdate, textStatus, jqXHR) => {
|
success: (applePayShippingMethodUpdate, textStatus, jqXHR) => {
|
||||||
this.log('onshippingmethodselected ok');
|
this.log('onshippingmethodselected ok');
|
||||||
let response = applePayShippingMethodUpdate.data
|
let response = applePayShippingMethodUpdate.data;
|
||||||
if (applePayShippingMethodUpdate.success === false) {
|
if (applePayShippingMethodUpdate.success === false) {
|
||||||
response.errors = createAppleErrors(response.errors)
|
response.errors = createAppleErrors(response.errors);
|
||||||
}
|
}
|
||||||
this.selectedShippingMethod = event.shippingMethod
|
this.selectedShippingMethod = event.shippingMethod;
|
||||||
//order the response shipping methods, so that the selected shipping method is the first one
|
|
||||||
let orderedShippingMethods = response.newShippingMethods.sort((a, b) => {
|
// Sort the response shipping methods, so that the selected shipping method is the first one.
|
||||||
|
response.newShippingMethods = response.newShippingMethods.sort((a, b) => {
|
||||||
if (a.label === this.selectedShippingMethod.label) {
|
if (a.label === this.selectedShippingMethod.label) {
|
||||||
return -1
|
return -1;
|
||||||
}
|
}
|
||||||
return 1
|
return 1;
|
||||||
})
|
});
|
||||||
//update the response.newShippingMethods with the ordered shipping methods
|
|
||||||
response.newShippingMethods = orderedShippingMethods
|
|
||||||
if (applePayShippingMethodUpdate.success === false) {
|
if (applePayShippingMethodUpdate.success === false) {
|
||||||
response.errors = createAppleErrors(response.errors)
|
response.errors = createAppleErrors(response.errors);
|
||||||
}
|
}
|
||||||
session.completeShippingMethodSelection(response)
|
session.completeShippingMethodSelection(response);
|
||||||
},
|
},
|
||||||
error: (jqXHR, textStatus, errorThrown) => {
|
error: (jqXHR, textStatus, errorThrown) => {
|
||||||
this.log('onshippingmethodselected error', textStatus);
|
this.log('onshippingmethodselected error', textStatus);
|
||||||
console.warn(textStatus, errorThrown)
|
console.warn(textStatus, errorThrown);
|
||||||
session.abort()
|
session.abort();
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
onshippingcontactselected(session) {
|
|
||||||
|
onShippingContactSelected(session) {
|
||||||
this.log('onshippingcontactselected', this.buttonConfig.ajax_url);
|
this.log('onshippingcontactselected', this.buttonConfig.ajax_url);
|
||||||
const ajax_url = this.buttonConfig.ajax_url
|
|
||||||
|
const ajax_url = this.buttonConfig.ajax_url;
|
||||||
|
|
||||||
return (event) => {
|
return (event) => {
|
||||||
this.log('onshippingcontactselected call');
|
this.log('onshippingcontactselected call');
|
||||||
|
|
||||||
const data = this.getShippingContactData(event);
|
const data = this.getShippingContactData(event);
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
url: ajax_url,
|
url: ajax_url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data,
|
data: data,
|
||||||
success: (applePayShippingContactUpdate, textStatus, jqXHR) => {
|
success: (applePayShippingContactUpdate, textStatus, jqXHR) => {
|
||||||
this.log('onshippingcontactselected ok');
|
this.log('onshippingcontactselected ok');
|
||||||
let response = applePayShippingContactUpdate.data
|
let response = applePayShippingContactUpdate.data;
|
||||||
this.updated_contact_info = event.shippingContact
|
this.updatedContactInfo = event.shippingContact;
|
||||||
if (applePayShippingContactUpdate.success === false) {
|
if (applePayShippingContactUpdate.success === false) {
|
||||||
response.errors = createAppleErrors(response.errors)
|
response.errors = createAppleErrors(response.errors);
|
||||||
}
|
}
|
||||||
if (response.newShippingMethods) {
|
if (response.newShippingMethods) {
|
||||||
this.selectedShippingMethod = response.newShippingMethods[0]
|
this.selectedShippingMethod = response.newShippingMethods[0];
|
||||||
}
|
}
|
||||||
session.completeShippingContactSelection(response)
|
session.completeShippingContactSelection(response);
|
||||||
},
|
},
|
||||||
error: (jqXHR, textStatus, errorThrown) => {
|
error: (jqXHR, textStatus, errorThrown) => {
|
||||||
this.log('onshippingcontactselected error', textStatus);
|
this.log('onshippingcontactselected error', textStatus);
|
||||||
console.warn(textStatus, errorThrown)
|
console.warn(textStatus, errorThrown);
|
||||||
session.abort()
|
session.abort();
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getShippingContactData(event) {
|
getShippingContactData(event) {
|
||||||
const product_id = this.buttonConfig.product.id;
|
const product_id = this.buttonConfig.product.id;
|
||||||
|
|
||||||
|
@ -399,7 +527,7 @@ class ApplepayButton {
|
||||||
caller_page: 'productDetail',
|
caller_page: 'productDetail',
|
||||||
product_quantity: this.productQuantity,
|
product_quantity: this.productQuantity,
|
||||||
simplified_contact: event.shippingContact,
|
simplified_contact: event.shippingContact,
|
||||||
need_shipping: this.buttonConfig.product.needShipping,
|
need_shipping: this.shouldRequireShippingInButton(),
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
};
|
};
|
||||||
case 'cart':
|
case 'cart':
|
||||||
|
@ -411,11 +539,12 @@ class ApplepayButton {
|
||||||
action: 'ppcp_update_shipping_contact',
|
action: 'ppcp_update_shipping_contact',
|
||||||
simplified_contact: event.shippingContact,
|
simplified_contact: event.shippingContact,
|
||||||
caller_page: 'cart',
|
caller_page: 'cart',
|
||||||
need_shipping: this.buttonConfig.product.needShipping,
|
need_shipping: this.shouldRequireShippingInButton(),
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getShippingMethodData(event) {
|
getShippingMethodData(event) {
|
||||||
const product_id = this.buttonConfig.product.id;
|
const product_id = this.buttonConfig.product.id;
|
||||||
|
|
||||||
|
@ -425,11 +554,11 @@ class ApplepayButton {
|
||||||
case 'product': return {
|
case 'product': return {
|
||||||
action: 'ppcp_update_shipping_method',
|
action: 'ppcp_update_shipping_method',
|
||||||
shipping_method: event.shippingMethod,
|
shipping_method: event.shippingMethod,
|
||||||
|
simplified_contact: this.updatedContactInfo || this.initialPaymentRequest.shippingContact || this.initialPaymentRequest.billingContact,
|
||||||
product_id: product_id,
|
product_id: product_id,
|
||||||
products: JSON.stringify(this.products),
|
products: JSON.stringify(this.products),
|
||||||
caller_page: 'productDetail',
|
caller_page: 'productDetail',
|
||||||
product_quantity: this.productQuantity,
|
product_quantity: this.productQuantity,
|
||||||
simplified_contact: this.updated_contact_info,
|
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
}
|
}
|
||||||
case 'cart':
|
case 'cart':
|
||||||
|
@ -440,14 +569,14 @@ class ApplepayButton {
|
||||||
return {
|
return {
|
||||||
action: 'ppcp_update_shipping_method',
|
action: 'ppcp_update_shipping_method',
|
||||||
shipping_method: event.shippingMethod,
|
shipping_method: event.shippingMethod,
|
||||||
|
simplified_contact: this.updatedContactInfo || this.initialPaymentRequest.shippingContact || this.initialPaymentRequest.billingContact,
|
||||||
caller_page: 'cart',
|
caller_page: 'cart',
|
||||||
simplified_contact: this.updated_contact_info,
|
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onpaymentauthorized(session) {
|
onPaymentAuthorized(session) {
|
||||||
this.log('onpaymentauthorized');
|
this.log('onpaymentauthorized');
|
||||||
return async (event) => {
|
return async (event) => {
|
||||||
this.log('onpaymentauthorized call');
|
this.log('onpaymentauthorized call');
|
||||||
|
@ -458,8 +587,10 @@ class ApplepayButton {
|
||||||
const processInWooAndCapture = async (data) => {
|
const processInWooAndCapture = async (data) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const billingContact = data.billing_contact
|
const billingContact = data.billing_contact || this.initialPaymentRequest.billingContact;
|
||||||
const shippingContact = data.shipping_contact
|
const shippingContact = data.shipping_contact || this.initialPaymentRequest.shippingContact;
|
||||||
|
const shippingMethod = this.selectedShippingMethod || (this.initialPaymentRequest.shippingMethods || [])[0];
|
||||||
|
|
||||||
let request_data = {
|
let request_data = {
|
||||||
action: 'ppcp_create_order',
|
action: 'ppcp_create_order',
|
||||||
'caller_page': this.context,
|
'caller_page': this.context,
|
||||||
|
@ -469,7 +600,7 @@ class ApplepayButton {
|
||||||
'shipping_contact': shippingContact,
|
'shipping_contact': shippingContact,
|
||||||
'billing_contact': billingContact,
|
'billing_contact': billingContact,
|
||||||
'token': event.payment.token,
|
'token': event.payment.token,
|
||||||
'shipping_method': this.selectedShippingMethod,
|
'shipping_method': shippingMethod,
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
'funding_source': 'applepay',
|
'funding_source': 'applepay',
|
||||||
'_wp_http_referer': '/?wc-ajax=update_order_review',
|
'_wp_http_referer': '/?wc-ajax=update_order_review',
|
||||||
|
@ -487,16 +618,16 @@ class ApplepayButton {
|
||||||
},
|
},
|
||||||
success: (authorizationResult, textStatus, jqXHR) => {
|
success: (authorizationResult, textStatus, jqXHR) => {
|
||||||
this.log('onpaymentauthorized ok');
|
this.log('onpaymentauthorized ok');
|
||||||
resolve(authorizationResult)
|
resolve(authorizationResult);
|
||||||
},
|
},
|
||||||
error: (jqXHR, textStatus, errorThrown) => {
|
error: (jqXHR, textStatus, errorThrown) => {
|
||||||
this.log('onpaymentauthorized error', textStatus);
|
this.log('onpaymentauthorized error', textStatus);
|
||||||
reject(new Error(errorThrown));
|
reject(new Error(errorThrown));
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log('onpaymentauthorized catch', error);
|
this.log('onpaymentauthorized catch', error);
|
||||||
console.log(error) // handle error
|
console.log(error); // handle error
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -518,8 +649,8 @@ class ApplepayButton {
|
||||||
if (confirmOrderResponse.approveApplePayPayment.status === "APPROVED") {
|
if (confirmOrderResponse.approveApplePayPayment.status === "APPROVED") {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!this.contextHandler.shippingAllowed()) {
|
if (this.shouldCompletePaymentWithContextHandler()) {
|
||||||
// No shipping, expect immediate capture, ex: PayNow.
|
// No shipping, expect immediate capture, ex: PayNow, Checkout with form data.
|
||||||
|
|
||||||
let approveFailed = false;
|
let approveFailed = false;
|
||||||
await this.contextHandler.approveOrder({
|
await this.contextHandler.approveOrder({
|
||||||
|
@ -542,7 +673,7 @@ class ApplepayButton {
|
||||||
} else {
|
} else {
|
||||||
this.log('onpaymentauthorized approveOrder FAIL');
|
this.log('onpaymentauthorized approveOrder FAIL');
|
||||||
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
||||||
session.abort()
|
session.abort();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,17 +687,17 @@ class ApplepayButton {
|
||||||
};
|
};
|
||||||
let authorizationResult = await processInWooAndCapture(data);
|
let authorizationResult = await processInWooAndCapture(data);
|
||||||
if (authorizationResult.result === "success") {
|
if (authorizationResult.result === "success") {
|
||||||
session.completePayment(ApplePaySession.STATUS_SUCCESS)
|
session.completePayment(ApplePaySession.STATUS_SUCCESS);
|
||||||
window.location.href = authorizationResult.redirect
|
window.location.href = authorizationResult.redirect;
|
||||||
} else {
|
} else {
|
||||||
session.completePayment(ApplePaySession.STATUS_FAILURE)
|
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
||||||
session.abort()
|
session.abort();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -580,43 +711,44 @@ class ApplepayButton {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error confirming order with applepay token', error);
|
console.error('Error confirming order with applepay token', error);
|
||||||
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
||||||
session.abort()
|
session.abort();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fill_billing_contact(form_saved) {
|
fillBillingContact(data) {
|
||||||
return {
|
return {
|
||||||
givenName: form_saved.billing_first_name ?? '',
|
givenName: data.billing_first_name ?? '',
|
||||||
familyName: form_saved.billing_last_name ?? '',
|
familyName: data.billing_last_name ?? '',
|
||||||
emailAddress: form_saved.billing_email ?? '',
|
emailAddress: data.billing_email ?? '',
|
||||||
phoneNumber: form_saved.billing_phone ?? '',
|
phoneNumber: data.billing_phone ?? '',
|
||||||
addressLines: [form_saved.billing_address_1, form_saved.billing_address_2],
|
addressLines: [data.billing_address_1, data.billing_address_2],
|
||||||
locality: form_saved.billing_city ?? '',
|
locality: data.billing_city ?? '',
|
||||||
postalCode: form_saved.billing_postcode ?? '',
|
postalCode: data.billing_postcode ?? '',
|
||||||
countryCode: form_saved.billing_country ?? '',
|
countryCode: data.billing_country ?? '',
|
||||||
administrativeArea: form_saved.billing_state ?? '',
|
administrativeArea: data.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) {
|
fillShippingContact(data) {
|
||||||
const jsonString = JSON.stringify(form_saved);
|
if (data.shipping_first_name === "") {
|
||||||
|
return this.fillBillingContact(data);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
givenName: (data?.shipping_first_name && data.shipping_first_name !== "") ? data.shipping_first_name : data?.billing_first_name,
|
||||||
|
familyName: (data?.shipping_last_name && data.shipping_last_name !== "") ? data.shipping_last_name : data?.billing_last_name,
|
||||||
|
emailAddress: (data?.shipping_email && data.shipping_email !== "") ? data.shipping_email : data?.billing_email,
|
||||||
|
phoneNumber: (data?.shipping_phone && data.shipping_phone !== "") ? data.shipping_phone : data?.billing_phone,
|
||||||
|
addressLines: [data.shipping_address_1 ?? '', data.shipping_address_2 ?? ''],
|
||||||
|
locality: (data?.shipping_city && data.shipping_city !== "") ? data.shipping_city : data?.billing_city,
|
||||||
|
postalCode: (data?.shipping_postcode && data.shipping_postcode !== "") ? data.shipping_postcode : data?.billing_postcode,
|
||||||
|
countryCode: (data?.shipping_country && data.shipping_country !== "") ? data.shipping_country : data?.billing_country,
|
||||||
|
administrativeArea: (data?.shipping_state && data.shipping_state !== "") ? data.shipping_state : data?.billing_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fillApplicationData(data) {
|
||||||
|
const jsonString = JSON.stringify(data);
|
||||||
let utf8Str = encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (match, p1) => {
|
let utf8Str = encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (match, p1) => {
|
||||||
return String.fromCharCode('0x' + p1);
|
return String.fromCharCode('0x' + p1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,18 +9,27 @@ class BaseHandler {
|
||||||
this.ppcpConfig = ppcpConfig;
|
this.ppcpConfig = ppcpConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.cart ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
shippingAllowed() {
|
shippingAllowed() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionInfo() {
|
transactionInfo() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const endpoint = this.ppcpConfig.ajax.cart_script_params.endpoint;
|
||||||
|
const separator = (endpoint.indexOf('?') !== -1) ? '&' : '?';
|
||||||
|
|
||||||
fetch(
|
fetch(
|
||||||
this.ppcpConfig.ajax.cart_script_params.endpoint,
|
endpoint + separator + 'shipping=1',
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(result => result.json())
|
.then(result => result.json())
|
||||||
|
@ -36,7 +45,9 @@ class BaseHandler {
|
||||||
countryCode: data.country_code,
|
countryCode: data.country_code,
|
||||||
currencyCode: data.currency_code,
|
currencyCode: data.currency_code,
|
||||||
totalPriceStatus: 'FINAL',
|
totalPriceStatus: 'FINAL',
|
||||||
totalPrice: data.total_str
|
totalPrice: data.total_str,
|
||||||
|
chosenShippingMethods: data.chosen_shipping_methods || null,
|
||||||
|
shippingPackages: data.shipping_packages || null,
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,28 +1,17 @@
|
||||||
import Spinner from "../../../../ppcp-button/resources/js/modules/Helper/Spinner";
|
import Spinner from "../../../../ppcp-button/resources/js/modules/Helper/Spinner";
|
||||||
import CheckoutActionHandler
|
import CheckoutActionHandler
|
||||||
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler";
|
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler";
|
||||||
import ErrorHandler from "../../../../ppcp-button/resources/js/modules/ErrorHandler";
|
|
||||||
import BaseHandler from "./BaseHandler";
|
import BaseHandler from "./BaseHandler";
|
||||||
|
|
||||||
class CheckoutHandler extends BaseHandler {
|
class CheckoutHandler extends BaseHandler {
|
||||||
|
|
||||||
createOrder() {
|
actionHandler() {
|
||||||
const errorHandler = new ErrorHandler(
|
return new CheckoutActionHandler(
|
||||||
this.ppcpConfig.labels.error.generic,
|
|
||||||
document.querySelector('.woocommerce-notices-wrapper')
|
|
||||||
);
|
|
||||||
|
|
||||||
const spinner = new Spinner();
|
|
||||||
|
|
||||||
const actionHandler = new CheckoutActionHandler(
|
|
||||||
this.ppcpConfig,
|
this.ppcpConfig,
|
||||||
errorHandler,
|
this.errorHandler(),
|
||||||
spinner
|
new Spinner()
|
||||||
);
|
);
|
||||||
|
|
||||||
return actionHandler.configuration().createOrder(null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CheckoutHandler;
|
export default CheckoutHandler;
|
||||||
|
|
|
@ -5,6 +5,13 @@ import CheckoutActionHandler
|
||||||
|
|
||||||
class PayNowHandler extends BaseHandler {
|
class PayNowHandler extends BaseHandler {
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.payorder ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
shippingAllowed() {
|
shippingAllowed() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,13 @@ import BaseHandler from "./BaseHandler";
|
||||||
|
|
||||||
class SingleProductHandler extends BaseHandler {
|
class SingleProductHandler extends BaseHandler {
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.product ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
transactionInfo() {
|
transactionInfo() {
|
||||||
const errorHandler = new ErrorHandler(
|
const errorHandler = new ErrorHandler(
|
||||||
this.ppcpConfig.labels.error.generic,
|
this.ppcpConfig.labels.error.generic,
|
||||||
|
|
|
@ -93,11 +93,29 @@ return array(
|
||||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||||
$user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' );
|
$user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' );
|
||||||
if ( $user_agent ) {
|
if ( $user_agent ) {
|
||||||
foreach ( PropertiesDictionary::ALLOWED_USER_AGENTS as $allowed_agent ) {
|
foreach ( PropertiesDictionary::DISALLOWED_USER_AGENTS as $disallowed_agent ) {
|
||||||
if ( strpos( $user_agent, $allowed_agent ) !== false ) {
|
if ( strpos( $user_agent, $disallowed_agent ) !== false ) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$browser_allowed = false;
|
||||||
|
foreach ( PropertiesDictionary::ALLOWED_USER_BROWSERS as $allowed_browser ) {
|
||||||
|
if ( strpos( $user_agent, $allowed_browser ) !== false ) {
|
||||||
|
$browser_allowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$device_allowed = false;
|
||||||
|
foreach ( PropertiesDictionary::ALLOWED_USER_DEVICES as $allowed_devices ) {
|
||||||
|
if ( strpos( $user_agent, $allowed_devices ) !== false ) {
|
||||||
|
$device_allowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $browser_allowed && $device_allowed;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
|
@ -164,6 +164,7 @@ class ApplepayModule implements ModuleInterface {
|
||||||
assert( $smart_button instanceof SmartButtonInterface );
|
assert( $smart_button instanceof SmartButtonInterface );
|
||||||
if ( $smart_button->should_load_ppcp_script() ) {
|
if ( $smart_button->should_load_ppcp_script() ) {
|
||||||
$button->enqueue();
|
$button->enqueue();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
|
if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
|
||||||
|
@ -196,7 +197,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() ) {
|
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-standard-payments-page' ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,6 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$user_country,
|
$user_country,
|
||||||
$allowed_shipping_countries
|
$allowed_shipping_countries
|
||||||
);
|
);
|
||||||
$product_need_shipping = $applepay_request_data_object->need_shipping();
|
|
||||||
|
|
||||||
if ( ! $is_allowed_selling_country ) {
|
if ( ! $is_allowed_selling_country ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
|
@ -320,7 +319,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( $product_need_shipping && ! $is_allowed_shipping_country ) {
|
if ( $applepay_request_data_object->need_shipping() && ! $is_allowed_shipping_country ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
array( array( 'errorCode' => 'addressUnserviceable' ) )
|
array( array( 'errorCode' => 'addressUnserviceable' ) )
|
||||||
);
|
);
|
||||||
|
@ -405,6 +404,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
public function create_wc_order(): void {
|
public function create_wc_order(): void {
|
||||||
$applepay_request_data_object = $this->applepay_data_object_http();
|
$applepay_request_data_object = $this->applepay_data_object_http();
|
||||||
//phpcs:disable WordPress.Security.NonceVerification
|
//phpcs:disable WordPress.Security.NonceVerification
|
||||||
|
|
||||||
$context = wc_clean( wp_unslash( $_POST['caller_page'] ?? '' ) );
|
$context = wc_clean( wp_unslash( $_POST['caller_page'] ?? '' ) );
|
||||||
if ( ! is_string( $context ) ) {
|
if ( ! is_string( $context ) ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
|
@ -418,6 +418,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$applepay_request_data_object->order_data( $context );
|
$applepay_request_data_object->order_data( $context );
|
||||||
|
|
||||||
$this->update_posted_data( $applepay_request_data_object );
|
$this->update_posted_data( $applepay_request_data_object );
|
||||||
if ( $context === 'product' ) {
|
if ( $context === 'product' ) {
|
||||||
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
|
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
|
||||||
|
@ -451,7 +452,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->add_addresses_to_order( $applepay_request_data_object );
|
|
||||||
WC()->checkout()->process_checkout();
|
WC()->checkout()->process_checkout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,10 +597,10 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$address['country'] ?? $shop_country_code
|
$address['country'] ?? $shop_country_code
|
||||||
);
|
);
|
||||||
WC()->customer->set_shipping_postcode(
|
WC()->customer->set_shipping_postcode(
|
||||||
$address['postcode'] ?? $shop_country_code
|
$address['postcode'] ?? ''
|
||||||
);
|
);
|
||||||
WC()->customer->set_shipping_city(
|
WC()->customer->set_shipping_city(
|
||||||
$address['city'] ?? $shop_country_code
|
$address['city'] ?? ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,32 +766,6 @@ class ApplePayButton implements ButtonInterface {
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add address billing and shipping data to order
|
|
||||||
*
|
|
||||||
* @param ApplePayDataObjectHttp $applepay_request_data_object ApplePayDataObjectHttp.
|
|
||||||
*/
|
|
||||||
protected function add_addresses_to_order(
|
|
||||||
ApplePayDataObjectHttp $applepay_request_data_object
|
|
||||||
): void {
|
|
||||||
add_action(
|
|
||||||
'woocommerce_checkout_create_order',
|
|
||||||
static function ( WC_Order $order, array $data ) use ( $applepay_request_data_object ) {
|
|
||||||
if ( ! empty( $applepay_request_data_object->shipping_method() ) ) {
|
|
||||||
$billing_address = $applepay_request_data_object->billing_address();
|
|
||||||
$shipping_address = $applepay_request_data_object->shipping_address();
|
|
||||||
// apple puts email in shipping_address while we get it from WC's billing_address.
|
|
||||||
$billing_address['email'] = $shipping_address['email'];
|
|
||||||
$billing_address['phone'] = $shipping_address['phone'];
|
|
||||||
|
|
||||||
$order->set_address( $billing_address, 'billing' );
|
|
||||||
$order->set_address( $shipping_address, 'shipping' );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Empty the cart to use for calculations
|
* Empty the cart to use for calculations
|
||||||
* while saving its contents in a field
|
* while saving its contents in a field
|
||||||
|
@ -867,10 +842,11 @@ class ApplePayButton implements ButtonInterface {
|
||||||
* @param ApplePayDataObjectHttp $applepay_request_data_object The Apple Pay request data.
|
* @param ApplePayDataObjectHttp $applepay_request_data_object The Apple Pay request data.
|
||||||
*/
|
*/
|
||||||
protected function update_posted_data( $applepay_request_data_object ): void {
|
protected function update_posted_data( $applepay_request_data_object ): void {
|
||||||
|
// TODO : get checkout form data in here to fill more fields like: ensure billing email and phone are filled.
|
||||||
|
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_checkout_posted_data',
|
'woocommerce_checkout_posted_data',
|
||||||
function ( array $data ) use ( $applepay_request_data_object ): array {
|
function ( array $data ) use ( $applepay_request_data_object ): array {
|
||||||
|
|
||||||
$data['payment_method'] = 'ppcp-gateway';
|
$data['payment_method'] = 'ppcp-gateway';
|
||||||
$data['shipping_method'] = $applepay_request_data_object->shipping_method();
|
$data['shipping_method'] = $applepay_request_data_object->shipping_method();
|
||||||
$data['billing_first_name'] = $applepay_request_data_object->billing_address()['first_name'] ?? '';
|
$data['billing_first_name'] = $applepay_request_data_object->billing_address()['first_name'] ?? '';
|
||||||
|
@ -882,10 +858,18 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$data['billing_city'] = $applepay_request_data_object->billing_address()['city'] ?? '';
|
$data['billing_city'] = $applepay_request_data_object->billing_address()['city'] ?? '';
|
||||||
$data['billing_state'] = $applepay_request_data_object->billing_address()['state'] ?? '';
|
$data['billing_state'] = $applepay_request_data_object->billing_address()['state'] ?? '';
|
||||||
$data['billing_postcode'] = $applepay_request_data_object->billing_address()['postcode'] ?? '';
|
$data['billing_postcode'] = $applepay_request_data_object->billing_address()['postcode'] ?? '';
|
||||||
|
$data['billing_email'] = $applepay_request_data_object->billing_address()['email'] ?? '';
|
||||||
|
$data['billing_phone'] = $applepay_request_data_object->billing_address()['phone'] ?? '';
|
||||||
|
|
||||||
|
// ApplePay doesn't send us a billing email or phone, use the shipping contacts instead.
|
||||||
|
if ( ! ( $data['billing_email'] ?? false ) ) {
|
||||||
|
$data['billing_email'] = $applepay_request_data_object->shipping_address()['email'] ?? '';
|
||||||
|
}
|
||||||
|
if ( ! ( $data['billing_phone'] ?? false ) ) {
|
||||||
|
$data['billing_phone'] = $applepay_request_data_object->shipping_address()['phone'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! empty( $applepay_request_data_object->shipping_method() ) ) {
|
if ( ! empty( $applepay_request_data_object->shipping_method() ) ) {
|
||||||
$data['billing_email'] = $applepay_request_data_object->shipping_address()['email'] ?? '';
|
|
||||||
$data['billing_phone'] = $applepay_request_data_object->shipping_address()['phone'] ?? '';
|
|
||||||
$data['shipping_first_name'] = $applepay_request_data_object->shipping_address()['first_name'] ?? '';
|
$data['shipping_first_name'] = $applepay_request_data_object->shipping_address()['first_name'] ?? '';
|
||||||
$data['shipping_last_name'] = $applepay_request_data_object->shipping_address()['last_name'] ?? '';
|
$data['shipping_last_name'] = $applepay_request_data_object->shipping_address()['last_name'] ?? '';
|
||||||
$data['shipping_company'] = $applepay_request_data_object->shipping_address()['company'] ?? '';
|
$data['shipping_company'] = $applepay_request_data_object->shipping_address()['company'] ?? '';
|
||||||
|
|
|
@ -144,31 +144,35 @@ class DataToAppleButtonScripts {
|
||||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
$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' ) : '';
|
$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 = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||||
|
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||||
|
|
||||||
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 ? true : false,
|
||||||
'button' => array(
|
'preferences' => array(
|
||||||
|
'checkout_data_mode' => $checkout_data_mode,
|
||||||
|
),
|
||||||
|
'button' => array(
|
||||||
'wrapper' => 'applepay-container',
|
'wrapper' => 'applepay-container',
|
||||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'lang' => $lang,
|
'lang' => $lang,
|
||||||
),
|
),
|
||||||
'product' => array(
|
'product' => array(
|
||||||
'needShipping' => $product_need_shipping,
|
'needShipping' => $product_need_shipping,
|
||||||
'id' => $product_id,
|
'id' => $product_id,
|
||||||
'price' => $product_price,
|
'price' => $product_price,
|
||||||
'isVariation' => $is_variation,
|
'isVariation' => $is_variation,
|
||||||
'stock' => $product_stock,
|
'stock' => $product_stock,
|
||||||
),
|
),
|
||||||
'shop' => array(
|
'shop' => array(
|
||||||
'countryCode' => $shop_country_code,
|
'countryCode' => $shop_country_code,
|
||||||
'currencyCode' => $currency_code,
|
'currencyCode' => $currency_code,
|
||||||
'totalLabel' => $total_label,
|
'totalLabel' => $total_label,
|
||||||
),
|
),
|
||||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||||
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,32 +195,36 @@ class DataToAppleButtonScripts {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
$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' ) : '';
|
$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 = $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;
|
||||||
|
|
||||||
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 ? true : false,
|
||||||
'button' => array(
|
'preferences' => array(
|
||||||
|
'checkout_data_mode' => $checkout_data_mode,
|
||||||
|
),
|
||||||
|
'button' => array(
|
||||||
'wrapper' => 'applepay-container',
|
'wrapper' => 'applepay-container',
|
||||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'lang' => $lang,
|
'lang' => $lang,
|
||||||
),
|
),
|
||||||
'product' => array(
|
'product' => array(
|
||||||
'needShipping' => $cart->needs_shipping(),
|
'needShipping' => $cart->needs_shipping(),
|
||||||
'subtotal' => $cart->get_subtotal(),
|
'subtotal' => $cart->get_subtotal(),
|
||||||
),
|
),
|
||||||
'shop' => array(
|
'shop' => array(
|
||||||
'countryCode' => $shop_country_code,
|
'countryCode' => $shop_country_code,
|
||||||
'currencyCode' => $currency_code,
|
'currencyCode' => $currency_code,
|
||||||
'totalLabel' => $total_label,
|
'totalLabel' => $total_label,
|
||||||
),
|
),
|
||||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||||
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,31 +243,35 @@ class DataToAppleButtonScripts {
|
||||||
$currency_code,
|
$currency_code,
|
||||||
$total_label
|
$total_label
|
||||||
) {
|
) {
|
||||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
$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' ) : '';
|
$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 = $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;
|
||||||
|
|
||||||
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 ? true : false,
|
||||||
'button' => array(
|
'preferences' => array(
|
||||||
|
'checkout_data_mode' => $checkout_data_mode,
|
||||||
|
),
|
||||||
|
'button' => array(
|
||||||
'wrapper' => 'applepay-container',
|
'wrapper' => 'applepay-container',
|
||||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'lang' => $lang,
|
'lang' => $lang,
|
||||||
),
|
),
|
||||||
'product' => array(
|
'product' => array(
|
||||||
'needShipping' => false,
|
'needShipping' => false,
|
||||||
'subtotal' => 0,
|
'subtotal' => 0,
|
||||||
),
|
),
|
||||||
'shop' => array(
|
'shop' => array(
|
||||||
'countryCode' => $shop_country_code,
|
'countryCode' => $shop_country_code,
|
||||||
'currencyCode' => $currency_code,
|
'currencyCode' => $currency_code,
|
||||||
'totalLabel' => $total_label,
|
'totalLabel' => $total_label,
|
||||||
),
|
),
|
||||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,22 @@ namespace WooCommerce\PayPalCommerce\Applepay\Assets;
|
||||||
* Class PropertiesDictionary
|
* Class PropertiesDictionary
|
||||||
*/
|
*/
|
||||||
class PropertiesDictionary {
|
class PropertiesDictionary {
|
||||||
public const ALLOWED_USER_AGENTS = array( 'Safari', 'Macintosh', 'iPhone', 'iPad', 'iPod' );
|
public const DISALLOWED_USER_AGENTS = array(
|
||||||
|
'Chrome/',
|
||||||
|
'CriOS/', // Chrome on iOS.
|
||||||
|
'Firefox/',
|
||||||
|
'OPR/', // Opera.
|
||||||
|
'Edg/', // Edge.
|
||||||
|
);
|
||||||
|
public const ALLOWED_USER_BROWSERS = array( 'Safari' );
|
||||||
|
public const ALLOWED_USER_DEVICES = array( 'Macintosh', 'iPhone', 'iPad', 'iPod' );
|
||||||
|
|
||||||
public const BILLING_CONTACT_INVALID = 'billing Contact Invalid';
|
public const BILLING_CONTACT_INVALID = 'billing Contact Invalid';
|
||||||
|
|
||||||
|
public const BILLING_DATA_MODE_USE_WC = 'use_wc';
|
||||||
|
public const BILLING_DATA_MODE_USE_APPLEPAY = 'use_applepay';
|
||||||
|
public const BILLING_DATA_MODE_DEFAULT = self::BILLING_DATA_MODE_USE_WC;
|
||||||
|
|
||||||
public const CREATE_ORDER_SINGLE_PROD_REQUIRED_FIELDS =
|
public const CREATE_ORDER_SINGLE_PROD_REQUIRED_FIELDS =
|
||||||
array(
|
array(
|
||||||
self::WCNONCE,
|
self::WCNONCE,
|
||||||
|
@ -186,4 +198,16 @@ class PropertiesDictionary {
|
||||||
'zh-TW' => __( 'Chinese (Traditional)', 'woocommerce-paypal-payments' ),
|
'zh-TW' => __( 'Chinese (Traditional)', 'woocommerce-paypal-payments' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the possible list of billing data modes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function billing_data_modes(): array {
|
||||||
|
return array(
|
||||||
|
self::BILLING_DATA_MODE_USE_WC => __( 'Use WC checkout form data (do not show shipping address fields)', 'woocommerce-paypal-payments' ),
|
||||||
|
self::BILLING_DATA_MODE_USE_APPLEPAY => __( 'Do not use WC checkout form data (request billing and shipping addresses on Apple Pay)', 'woocommerce-paypal-payments' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
|
||||||
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
|
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
|
||||||
import Renderer from './modules/Renderer/Renderer';
|
import Renderer from './modules/Renderer/Renderer';
|
||||||
import ErrorHandler from './modules/ErrorHandler';
|
import ErrorHandler from './modules/ErrorHandler';
|
||||||
import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
|
import HostedFieldsRenderer from "./modules/Renderer/HostedFieldsRenderer";
|
||||||
|
import CardFieldsRenderer from "./modules/Renderer/CardFieldsRenderer";
|
||||||
import MessageRenderer from "./modules/Renderer/MessageRenderer";
|
import MessageRenderer from "./modules/Renderer/MessageRenderer";
|
||||||
import Spinner from "./modules/Helper/Spinner";
|
import Spinner from "./modules/Helper/Spinner";
|
||||||
import {
|
import {
|
||||||
|
@ -37,7 +38,11 @@ const bootstrap = () => {
|
||||||
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
|
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
|
||||||
);
|
);
|
||||||
const spinner = new Spinner();
|
const spinner = new Spinner();
|
||||||
const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
|
||||||
|
let creditCardRenderer = new HostedFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||||
|
if (typeof paypal.CardFields !== 'undefined') {
|
||||||
|
creditCardRenderer = new CardFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||||
|
}
|
||||||
|
|
||||||
const formSaver = new FormSaver(
|
const formSaver = new FormSaver(
|
||||||
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
|
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
import {show} from "../Helper/Hiding";
|
||||||
|
|
||||||
|
class CardFieldsRenderer {
|
||||||
|
|
||||||
|
constructor(defaultConfig, errorHandler, spinner) {
|
||||||
|
this.defaultConfig = defaultConfig;
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
|
this.spinner = spinner;
|
||||||
|
this.cardValid = false;
|
||||||
|
this.formValid = false;
|
||||||
|
this.emptyFields = new Set(['number', 'cvv', 'expirationDate']);
|
||||||
|
this.currentHostedFieldsInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(wrapper, contextConfig) {
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
this.defaultConfig.context !== 'checkout'
|
||||||
|
&& this.defaultConfig.context !== 'pay-now'
|
||||||
|
)
|
||||||
|
|| wrapper === null
|
||||||
|
|| document.querySelector(wrapper) === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonSelector = wrapper + ' button';
|
||||||
|
|
||||||
|
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
|
||||||
|
if (!gateWayBox) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldDisplayStyle = gateWayBox.style.display;
|
||||||
|
gateWayBox.style.display = 'block';
|
||||||
|
|
||||||
|
const hideDccGateway = document.querySelector('#ppcp-hide-dcc');
|
||||||
|
if (hideDccGateway) {
|
||||||
|
hideDccGateway.parentNode.removeChild(hideDccGateway);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardField = paypal.CardFields({
|
||||||
|
createOrder: contextConfig.createOrder,
|
||||||
|
onApprove: function (data) {
|
||||||
|
return contextConfig.onApprove(data);
|
||||||
|
},
|
||||||
|
onError: function (error) {
|
||||||
|
console.error(error)
|
||||||
|
this.spinner.unblock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cardField.isEligible()) {
|
||||||
|
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
|
||||||
|
if (nameField) {
|
||||||
|
let styles = this.cardFieldStyles(nameField);
|
||||||
|
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
|
||||||
|
nameField.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
|
||||||
|
if (numberField) {
|
||||||
|
let styles = this.cardFieldStyles(numberField);
|
||||||
|
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
|
||||||
|
numberField.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
|
||||||
|
if (expiryField) {
|
||||||
|
let styles = this.cardFieldStyles(expiryField);
|
||||||
|
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
|
||||||
|
expiryField.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
|
||||||
|
if (cvvField) {
|
||||||
|
let styles = this.cardFieldStyles(cvvField);
|
||||||
|
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
|
||||||
|
cvvField.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
|
||||||
|
}
|
||||||
|
|
||||||
|
gateWayBox.style.display = oldDisplayStyle;
|
||||||
|
|
||||||
|
show(buttonSelector);
|
||||||
|
|
||||||
|
document.querySelector(buttonSelector).addEventListener("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.spinner.block();
|
||||||
|
this.errorHandler.clear();
|
||||||
|
|
||||||
|
cardField.submit()
|
||||||
|
.catch((error) => {
|
||||||
|
this.spinner.unblock();
|
||||||
|
console.error(error)
|
||||||
|
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.fields_not_valid);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cardFieldStyles(field) {
|
||||||
|
const allowedProperties = [
|
||||||
|
'appearance',
|
||||||
|
'color',
|
||||||
|
'direction',
|
||||||
|
'font',
|
||||||
|
'font-family',
|
||||||
|
'font-size',
|
||||||
|
'font-size-adjust',
|
||||||
|
'font-stretch',
|
||||||
|
'font-style',
|
||||||
|
'font-variant',
|
||||||
|
'font-variant-alternates',
|
||||||
|
'font-variant-caps',
|
||||||
|
'font-variant-east-asian',
|
||||||
|
'font-variant-ligatures',
|
||||||
|
'font-variant-numeric',
|
||||||
|
'font-weight',
|
||||||
|
'letter-spacing',
|
||||||
|
'line-height',
|
||||||
|
'opacity',
|
||||||
|
'outline',
|
||||||
|
'padding',
|
||||||
|
'padding-bottom',
|
||||||
|
'padding-left',
|
||||||
|
'padding-right',
|
||||||
|
'padding-top',
|
||||||
|
'text-shadow',
|
||||||
|
'transition',
|
||||||
|
'-moz-appearance',
|
||||||
|
'-moz-osx-font-smoothing',
|
||||||
|
'-moz-tap-highlight-color',
|
||||||
|
'-moz-transition',
|
||||||
|
'-webkit-appearance',
|
||||||
|
'-webkit-osx-font-smoothing',
|
||||||
|
'-webkit-tap-highlight-color',
|
||||||
|
'-webkit-transition',
|
||||||
|
];
|
||||||
|
|
||||||
|
const stylesRaw = window.getComputedStyle(field);
|
||||||
|
const styles = {};
|
||||||
|
Object.values(stylesRaw).forEach((prop) => {
|
||||||
|
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
styles[prop] = '' + stylesRaw[prop];
|
||||||
|
});
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardFieldsRenderer;
|
|
@ -1,275 +0,0 @@
|
||||||
import dccInputFactory from "../Helper/DccInputFactory";
|
|
||||||
import {show} from "../Helper/Hiding";
|
|
||||||
import Product from "../Entity/Product";
|
|
||||||
|
|
||||||
class CreditCardRenderer {
|
|
||||||
|
|
||||||
constructor(defaultConfig, errorHandler, spinner) {
|
|
||||||
this.defaultConfig = defaultConfig;
|
|
||||||
this.errorHandler = errorHandler;
|
|
||||||
this.spinner = spinner;
|
|
||||||
this.cardValid = false;
|
|
||||||
this.formValid = false;
|
|
||||||
this.emptyFields = new Set(['number', 'cvv', 'expirationDate']);
|
|
||||||
this.currentHostedFieldsInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(wrapper, contextConfig) {
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
this.defaultConfig.context !== 'checkout'
|
|
||||||
&& this.defaultConfig.context !== 'pay-now'
|
|
||||||
)
|
|
||||||
|| wrapper === null
|
|
||||||
|| document.querySelector(wrapper) === null
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof paypal.HostedFields === 'undefined'
|
|
||||||
|| ! paypal.HostedFields.isEligible()
|
|
||||||
) {
|
|
||||||
const wrapperElement = document.querySelector(wrapper);
|
|
||||||
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonSelector = wrapper + ' button';
|
|
||||||
|
|
||||||
if (this.currentHostedFieldsInstance) {
|
|
||||||
this.currentHostedFieldsInstance.teardown()
|
|
||||||
.catch(err => console.error(`Hosted fields teardown error: ${err}`));
|
|
||||||
this.currentHostedFieldsInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
|
|
||||||
if(! gateWayBox) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const oldDisplayStyle = gateWayBox.style.display;
|
|
||||||
gateWayBox.style.display = 'block';
|
|
||||||
|
|
||||||
const hideDccGateway = document.querySelector('#ppcp-hide-dcc');
|
|
||||||
if (hideDccGateway) {
|
|
||||||
hideDccGateway.parentNode.removeChild(hideDccGateway);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cardNumberField = document.querySelector('#ppcp-credit-card-gateway-card-number');
|
|
||||||
|
|
||||||
const stylesRaw = window.getComputedStyle(cardNumberField);
|
|
||||||
let styles = {};
|
|
||||||
Object.values(stylesRaw).forEach( (prop) => {
|
|
||||||
if (! stylesRaw[prop]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
styles[prop] = '' + stylesRaw[prop];
|
|
||||||
});
|
|
||||||
|
|
||||||
const cardNumber = dccInputFactory(cardNumberField);
|
|
||||||
cardNumberField.parentNode.replaceChild(cardNumber, cardNumberField);
|
|
||||||
|
|
||||||
const cardExpiryField = document.querySelector('#ppcp-credit-card-gateway-card-expiry');
|
|
||||||
const cardExpiry = dccInputFactory(cardExpiryField);
|
|
||||||
cardExpiryField.parentNode.replaceChild(cardExpiry, cardExpiryField);
|
|
||||||
|
|
||||||
const cardCodeField = document.querySelector('#ppcp-credit-card-gateway-card-cvc');
|
|
||||||
const cardCode = dccInputFactory(cardCodeField);
|
|
||||||
cardCodeField.parentNode.replaceChild(cardCode, cardCodeField);
|
|
||||||
|
|
||||||
gateWayBox.style.display = oldDisplayStyle;
|
|
||||||
|
|
||||||
const formWrapper = '.payment_box payment_method_ppcp-credit-card-gateway';
|
|
||||||
if (
|
|
||||||
this.defaultConfig.enforce_vault
|
|
||||||
&& document.querySelector(formWrapper + ' .ppcp-credit-card-vault')
|
|
||||||
) {
|
|
||||||
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').checked = true;
|
|
||||||
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').setAttribute('disabled', true);
|
|
||||||
}
|
|
||||||
paypal.HostedFields.render({
|
|
||||||
createOrder: contextConfig.createOrder,
|
|
||||||
styles: {
|
|
||||||
'input': styles
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
number: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-number',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.credit_card_number,
|
|
||||||
},
|
|
||||||
cvv: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-cvc',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.cvv,
|
|
||||||
},
|
|
||||||
expirationDate: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-expiry',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.mm_yy,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(hostedFields => {
|
|
||||||
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
|
|
||||||
this.currentHostedFieldsInstance = hostedFields;
|
|
||||||
|
|
||||||
hostedFields.on('inputSubmitRequest', () => {
|
|
||||||
this._submit(contextConfig);
|
|
||||||
});
|
|
||||||
hostedFields.on('cardTypeChange', (event) => {
|
|
||||||
if ( ! event.cards.length ) {
|
|
||||||
this.cardValid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const validCards = this.defaultConfig.hosted_fields.valid_cards;
|
|
||||||
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
|
|
||||||
|
|
||||||
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
|
||||||
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
|
||||||
if (event.cards.length === 1) {
|
|
||||||
cardNumber.classList.add(className);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
hostedFields.on('validityChange', (event) => {
|
|
||||||
this.formValid = Object.keys(event.fields).every(function (key) {
|
|
||||||
return event.fields[key].isValid;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
hostedFields.on('empty', (event) => {
|
|
||||||
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
|
||||||
this.emptyFields.add(event.emittedBy);
|
|
||||||
});
|
|
||||||
hostedFields.on('notEmpty', (event) => {
|
|
||||||
this.emptyFields.delete(event.emittedBy);
|
|
||||||
});
|
|
||||||
|
|
||||||
show(buttonSelector);
|
|
||||||
|
|
||||||
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
|
||||||
document.querySelector(buttonSelector).addEventListener(
|
|
||||||
'click',
|
|
||||||
event => {
|
|
||||||
event.preventDefault();
|
|
||||||
this._submit(contextConfig);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('#payment_method_ppcp-credit-card-gateway').addEventListener(
|
|
||||||
'click',
|
|
||||||
() => {
|
|
||||||
document.querySelector('label[for=ppcp-credit-card-gateway-card-number]').click();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
disableFields() {
|
|
||||||
if (this.currentHostedFieldsInstance) {
|
|
||||||
this.currentHostedFieldsInstance.setAttribute({
|
|
||||||
field: 'number',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
this.currentHostedFieldsInstance.setAttribute({
|
|
||||||
field: 'cvv',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
this.currentHostedFieldsInstance.setAttribute({
|
|
||||||
field: 'expirationDate',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableFields() {
|
|
||||||
if (this.currentHostedFieldsInstance) {
|
|
||||||
this.currentHostedFieldsInstance.removeAttribute({
|
|
||||||
field: 'number',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
this.currentHostedFieldsInstance.removeAttribute({
|
|
||||||
field: 'cvv',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
this.currentHostedFieldsInstance.removeAttribute({
|
|
||||||
field: 'expirationDate',
|
|
||||||
attribute: 'disabled'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_submit(contextConfig) {
|
|
||||||
this.spinner.block();
|
|
||||||
this.errorHandler.clear();
|
|
||||||
|
|
||||||
if (this.formValid && this.cardValid) {
|
|
||||||
const save_card = this.defaultConfig.can_save_vault_token ? true : false;
|
|
||||||
let vault = document.getElementById('ppcp-credit-card-vault') ?
|
|
||||||
document.getElementById('ppcp-credit-card-vault').checked : save_card;
|
|
||||||
if (this.defaultConfig.enforce_vault) {
|
|
||||||
vault = true;
|
|
||||||
}
|
|
||||||
const contingency = this.defaultConfig.hosted_fields.contingency;
|
|
||||||
const hostedFieldsData = {
|
|
||||||
vault: vault
|
|
||||||
};
|
|
||||||
if (contingency !== 'NO_3D_SECURE') {
|
|
||||||
hostedFieldsData.contingencies = [contingency];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.defaultConfig.payer) {
|
|
||||||
hostedFieldsData.cardholderName = this.defaultConfig.payer.name.given_name + ' ' + this.defaultConfig.payer.name.surname;
|
|
||||||
}
|
|
||||||
if (!hostedFieldsData.cardholderName) {
|
|
||||||
const firstName = document.getElementById('billing_first_name') ? document.getElementById('billing_first_name').value : '';
|
|
||||||
const lastName = document.getElementById('billing_last_name') ? document.getElementById('billing_last_name').value : '';
|
|
||||||
|
|
||||||
hostedFieldsData.cardholderName = firstName + ' ' + lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentHostedFieldsInstance.submit(hostedFieldsData).then((payload) => {
|
|
||||||
payload.orderID = payload.orderId;
|
|
||||||
this.spinner.unblock();
|
|
||||||
return contextConfig.onApprove(payload);
|
|
||||||
}).catch(err => {
|
|
||||||
this.spinner.unblock();
|
|
||||||
this.errorHandler.clear();
|
|
||||||
|
|
||||||
if (err.data?.details?.length) {
|
|
||||||
this.errorHandler.message(err.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
|
|
||||||
} else if (err.details?.length) {
|
|
||||||
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
|
|
||||||
} else if (err.data?.errors?.length > 0) {
|
|
||||||
this.errorHandler.messages(err.data.errors);
|
|
||||||
} else if (err.data?.message) {
|
|
||||||
this.errorHandler.message(err.data.message);
|
|
||||||
} else if (err.message) {
|
|
||||||
this.errorHandler.message(err.message);
|
|
||||||
} else {
|
|
||||||
this.errorHandler.genericError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.spinner.unblock();
|
|
||||||
|
|
||||||
let message = this.defaultConfig.labels.error.generic;
|
|
||||||
if (this.emptyFields.size > 0) {
|
|
||||||
message = this.defaultConfig.hosted_fields.labels.fields_empty;
|
|
||||||
} else if (!this.cardValid) {
|
|
||||||
message = this.defaultConfig.hosted_fields.labels.card_not_supported;
|
|
||||||
} else if (!this.formValid) {
|
|
||||||
message = this.defaultConfig.hosted_fields.labels.fields_not_valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.errorHandler.message(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_cardNumberFiledCLassNameByCardType(cardType) {
|
|
||||||
return cardType === 'american-express' ? 'amex' : cardType.replace('-', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
_recreateElementClassAttribute(element, newClassName) {
|
|
||||||
element.removeAttribute('class')
|
|
||||||
element.setAttribute('class', newClassName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default CreditCardRenderer;
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
import dccInputFactory from "../Helper/DccInputFactory";
|
||||||
|
import {show} from "../Helper/Hiding";
|
||||||
|
|
||||||
|
class HostedFieldsRenderer {
|
||||||
|
|
||||||
|
constructor(defaultConfig, errorHandler, spinner) {
|
||||||
|
this.defaultConfig = defaultConfig;
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
|
this.spinner = spinner;
|
||||||
|
this.cardValid = false;
|
||||||
|
this.formValid = false;
|
||||||
|
this.emptyFields = new Set(['number', 'cvv', 'expirationDate']);
|
||||||
|
this.currentHostedFieldsInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(wrapper, contextConfig) {
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
this.defaultConfig.context !== 'checkout'
|
||||||
|
&& this.defaultConfig.context !== 'pay-now'
|
||||||
|
)
|
||||||
|
|| wrapper === null
|
||||||
|
|| document.querySelector(wrapper) === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof paypal.HostedFields !== 'undefined' && paypal.HostedFields.isEligible()) {
|
||||||
|
const buttonSelector = wrapper + ' button';
|
||||||
|
|
||||||
|
if (this.currentHostedFieldsInstance) {
|
||||||
|
this.currentHostedFieldsInstance.teardown()
|
||||||
|
.catch(err => console.error(`Hosted fields teardown error: ${err}`));
|
||||||
|
this.currentHostedFieldsInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
|
||||||
|
if (!gateWayBox) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const oldDisplayStyle = gateWayBox.style.display;
|
||||||
|
gateWayBox.style.display = 'block';
|
||||||
|
|
||||||
|
const hideDccGateway = document.querySelector('#ppcp-hide-dcc');
|
||||||
|
if (hideDccGateway) {
|
||||||
|
hideDccGateway.parentNode.removeChild(hideDccGateway);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardNumberField = document.querySelector('#ppcp-credit-card-gateway-card-number');
|
||||||
|
|
||||||
|
const stylesRaw = window.getComputedStyle(cardNumberField);
|
||||||
|
let styles = {};
|
||||||
|
Object.values(stylesRaw).forEach((prop) => {
|
||||||
|
if (!stylesRaw[prop]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
styles[prop] = '' + stylesRaw[prop];
|
||||||
|
});
|
||||||
|
|
||||||
|
const cardNumber = dccInputFactory(cardNumberField);
|
||||||
|
cardNumberField.parentNode.replaceChild(cardNumber, cardNumberField);
|
||||||
|
|
||||||
|
const cardExpiryField = document.querySelector('#ppcp-credit-card-gateway-card-expiry');
|
||||||
|
const cardExpiry = dccInputFactory(cardExpiryField);
|
||||||
|
cardExpiryField.parentNode.replaceChild(cardExpiry, cardExpiryField);
|
||||||
|
|
||||||
|
const cardCodeField = document.querySelector('#ppcp-credit-card-gateway-card-cvc');
|
||||||
|
const cardCode = dccInputFactory(cardCodeField);
|
||||||
|
cardCodeField.parentNode.replaceChild(cardCode, cardCodeField);
|
||||||
|
|
||||||
|
gateWayBox.style.display = oldDisplayStyle;
|
||||||
|
|
||||||
|
const formWrapper = '.payment_box payment_method_ppcp-credit-card-gateway';
|
||||||
|
if (
|
||||||
|
this.defaultConfig.enforce_vault
|
||||||
|
&& document.querySelector(formWrapper + ' .ppcp-credit-card-vault')
|
||||||
|
) {
|
||||||
|
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').checked = true;
|
||||||
|
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
paypal.HostedFields.render({
|
||||||
|
createOrder: contextConfig.createOrder,
|
||||||
|
styles: {
|
||||||
|
'input': styles
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
number: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-number',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.credit_card_number,
|
||||||
|
},
|
||||||
|
cvv: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-cvc',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.cvv,
|
||||||
|
},
|
||||||
|
expirationDate: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-expiry',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.mm_yy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then(hostedFields => {
|
||||||
|
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
|
||||||
|
this.currentHostedFieldsInstance = hostedFields;
|
||||||
|
|
||||||
|
hostedFields.on('inputSubmitRequest', () => {
|
||||||
|
this._submit(contextConfig);
|
||||||
|
});
|
||||||
|
hostedFields.on('cardTypeChange', (event) => {
|
||||||
|
if (!event.cards.length) {
|
||||||
|
this.cardValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const validCards = this.defaultConfig.hosted_fields.valid_cards;
|
||||||
|
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
|
||||||
|
|
||||||
|
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
||||||
|
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||||
|
if (event.cards.length === 1) {
|
||||||
|
cardNumber.classList.add(className);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
hostedFields.on('validityChange', (event) => {
|
||||||
|
this.formValid = Object.keys(event.fields).every(function (key) {
|
||||||
|
return event.fields[key].isValid;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
hostedFields.on('empty', (event) => {
|
||||||
|
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||||
|
this.emptyFields.add(event.emittedBy);
|
||||||
|
});
|
||||||
|
hostedFields.on('notEmpty', (event) => {
|
||||||
|
this.emptyFields.delete(event.emittedBy);
|
||||||
|
});
|
||||||
|
|
||||||
|
show(buttonSelector);
|
||||||
|
|
||||||
|
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
||||||
|
document.querySelector(buttonSelector).addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
event.preventDefault();
|
||||||
|
this._submit(contextConfig);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#payment_method_ppcp-credit-card-gateway').addEventListener(
|
||||||
|
'click',
|
||||||
|
() => {
|
||||||
|
document.querySelector('label[for=ppcp-credit-card-gateway-card-number]').click();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperElement = document.querySelector(wrapper);
|
||||||
|
wrapperElement.parentNode.removeChild(wrapperElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
disableFields() {
|
||||||
|
if (this.currentHostedFieldsInstance) {
|
||||||
|
this.currentHostedFieldsInstance.setAttribute({
|
||||||
|
field: 'number',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
this.currentHostedFieldsInstance.setAttribute({
|
||||||
|
field: 'cvv',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
this.currentHostedFieldsInstance.setAttribute({
|
||||||
|
field: 'expirationDate',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enableFields() {
|
||||||
|
if (this.currentHostedFieldsInstance) {
|
||||||
|
this.currentHostedFieldsInstance.removeAttribute({
|
||||||
|
field: 'number',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
this.currentHostedFieldsInstance.removeAttribute({
|
||||||
|
field: 'cvv',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
this.currentHostedFieldsInstance.removeAttribute({
|
||||||
|
field: 'expirationDate',
|
||||||
|
attribute: 'disabled'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_submit(contextConfig) {
|
||||||
|
this.spinner.block();
|
||||||
|
this.errorHandler.clear();
|
||||||
|
|
||||||
|
if (this.formValid && this.cardValid) {
|
||||||
|
const save_card = this.defaultConfig.can_save_vault_token ? true : false;
|
||||||
|
let vault = document.getElementById('ppcp-credit-card-vault') ?
|
||||||
|
document.getElementById('ppcp-credit-card-vault').checked : save_card;
|
||||||
|
if (this.defaultConfig.enforce_vault) {
|
||||||
|
vault = true;
|
||||||
|
}
|
||||||
|
const contingency = this.defaultConfig.hosted_fields.contingency;
|
||||||
|
const hostedFieldsData = {
|
||||||
|
vault: vault
|
||||||
|
};
|
||||||
|
if (contingency !== 'NO_3D_SECURE') {
|
||||||
|
hostedFieldsData.contingencies = [contingency];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.defaultConfig.payer) {
|
||||||
|
hostedFieldsData.cardholderName = this.defaultConfig.payer.name.given_name + ' ' + this.defaultConfig.payer.name.surname;
|
||||||
|
}
|
||||||
|
if (!hostedFieldsData.cardholderName) {
|
||||||
|
const firstName = document.getElementById('billing_first_name') ? document.getElementById('billing_first_name').value : '';
|
||||||
|
const lastName = document.getElementById('billing_last_name') ? document.getElementById('billing_last_name').value : '';
|
||||||
|
|
||||||
|
hostedFieldsData.cardholderName = firstName + ' ' + lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentHostedFieldsInstance.submit(hostedFieldsData).then((payload) => {
|
||||||
|
payload.orderID = payload.orderId;
|
||||||
|
this.spinner.unblock();
|
||||||
|
return contextConfig.onApprove(payload);
|
||||||
|
}).catch(err => {
|
||||||
|
this.spinner.unblock();
|
||||||
|
this.errorHandler.clear();
|
||||||
|
|
||||||
|
if (err.data?.details?.length) {
|
||||||
|
this.errorHandler.message(err.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
|
||||||
|
} else if (err.details?.length) {
|
||||||
|
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
|
||||||
|
} else if (err.data?.errors?.length > 0) {
|
||||||
|
this.errorHandler.messages(err.data.errors);
|
||||||
|
} else if (err.data?.message) {
|
||||||
|
this.errorHandler.message(err.data.message);
|
||||||
|
} else if (err.message) {
|
||||||
|
this.errorHandler.message(err.message);
|
||||||
|
} else {
|
||||||
|
this.errorHandler.genericError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.spinner.unblock();
|
||||||
|
|
||||||
|
let message = this.defaultConfig.labels.error.generic;
|
||||||
|
if (this.emptyFields.size > 0) {
|
||||||
|
message = this.defaultConfig.hosted_fields.labels.fields_empty;
|
||||||
|
} else if (!this.cardValid) {
|
||||||
|
message = this.defaultConfig.hosted_fields.labels.card_not_supported;
|
||||||
|
} else if (!this.formValid) {
|
||||||
|
message = this.defaultConfig.hosted_fields.labels.fields_not_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errorHandler.message(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cardNumberFiledCLassNameByCardType(cardType) {
|
||||||
|
return cardType === 'american-express' ? 'amex' : cardType.replace('-', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
_recreateElementClassAttribute(element, newClassName) {
|
||||||
|
element.removeAttribute('class')
|
||||||
|
element.setAttribute('class', newClassName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HostedFieldsRenderer;
|
|
@ -973,6 +973,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
||||||
'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(),
|
'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(),
|
||||||
'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(),
|
'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(),
|
||||||
'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(),
|
'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(),
|
||||||
|
'locations_with_subscription_product' => $this->subscription_helper->locations_with_subscription_product(),
|
||||||
'enforce_vault' => $this->has_subscriptions(),
|
'enforce_vault' => $this->has_subscriptions(),
|
||||||
'can_save_vault_token' => $this->can_save_vault_token(),
|
'can_save_vault_token' => $this->can_save_vault_token(),
|
||||||
'is_free_trial_cart' => $is_free_trial_cart,
|
'is_free_trial_cart' => $is_free_trial_cart,
|
||||||
|
@ -1006,8 +1007,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
||||||
'id' => CardButtonGateway::ID,
|
'id' => CardButtonGateway::ID,
|
||||||
'wrapper' => '#ppc-button-' . CardButtonGateway::ID,
|
'wrapper' => '#ppc-button-' . CardButtonGateway::ID,
|
||||||
'style' => array(
|
'style' => array(
|
||||||
'shape' => $this->style_for_context( 'shape', $this->context() ),
|
'shape' => $this->style_for_apm( 'shape', 'card' ),
|
||||||
// TODO: color black, white from the gateway settings.
|
'color' => $this->style_for_apm( 'color', 'card', 'black' ),
|
||||||
|
'layout' => $this->style_for_apm( 'poweredby_tagline', 'card', false ) === $this->normalize_style_value( true ) ? 'vertical' : 'horizontal',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1341,9 +1343,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the style for a given indicator in a given context.
|
* Determines the style for a given property in a given context.
|
||||||
*
|
*
|
||||||
* @param string $style The style.
|
* @param string $style The name of the style property.
|
||||||
* @param string $context The context.
|
* @param string $context The context.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -1366,13 +1368,46 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
||||||
$context = 'general';
|
$context = 'general';
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = isset( $defaults[ $style ] ) ?
|
return $this->get_style_value( "button_{$context}_${style}" )
|
||||||
$defaults[ $style ] : '';
|
?? $this->get_style_value( "button_${style}" )
|
||||||
$value = $this->settings->has( 'button_' . $style ) ?
|
?? $this->normalize_style_value( $defaults[ $style ] ?? '' );
|
||||||
$this->settings->get( 'button_' . $style ) : $value;
|
}
|
||||||
$value = $this->settings->has( 'button_' . $context . '_' . $style ) ?
|
|
||||||
$this->settings->get( 'button_' . $context . '_' . $style ) : $value;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the style for a given property in a given APM.
|
||||||
|
*
|
||||||
|
* @param string $style The name of the style property.
|
||||||
|
* @param string $apm The APM name, such as 'card'.
|
||||||
|
* @param ?mixed $default The default value.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function style_for_apm( string $style, string $apm, $default = null ): string {
|
||||||
|
return $this->get_style_value( "${apm}_button_${style}" )
|
||||||
|
?? ( $default ? $this->normalize_style_value( $default ) : null )
|
||||||
|
?? $this->style_for_context( $style, 'checkout' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the style property value or null.
|
||||||
|
*
|
||||||
|
* @param string $key The style property key in the settings.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function get_style_value( string $key ): ?string {
|
||||||
|
if ( ! $this->settings->has( $key ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $this->normalize_style_value( $this->settings->get( $key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the style property value to string.
|
||||||
|
*
|
||||||
|
* @param mixed $value The style property value.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function normalize_style_value( $value ): string {
|
||||||
if ( is_bool( $value ) ) {
|
if ( is_bool( $value ) ) {
|
||||||
$value = $value ? 'true' : 'false';
|
$value = $value ? 'true' : 'false';
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
||||||
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$include_shipping = (bool) wc_clean( wp_unslash( $_GET['shipping'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||||
|
|
||||||
$script_data = $this->smart_button->script_data();
|
$script_data = $this->smart_button->script_data();
|
||||||
|
|
||||||
$total = (float) WC()->cart->get_total( 'numeric' );
|
$total = (float) WC()->cart->get_total( 'numeric' );
|
||||||
|
@ -79,20 +81,23 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
||||||
$shop_country_code = $base_location['country'] ?? '';
|
$shop_country_code = $base_location['country'] ?? '';
|
||||||
$currency_code = get_woocommerce_currency();
|
$currency_code = get_woocommerce_currency();
|
||||||
|
|
||||||
wp_send_json_success(
|
$response = array(
|
||||||
array(
|
'url_params' => $script_data['url_params'],
|
||||||
'url_params' => $script_data['url_params'],
|
'button' => $script_data['button'],
|
||||||
'button' => $script_data['button'],
|
'messages' => $script_data['messages'],
|
||||||
'messages' => $script_data['messages'],
|
'amount' => WC()->cart->get_total( 'raw' ),
|
||||||
'amount' => WC()->cart->get_total( 'raw' ),
|
|
||||||
|
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'total_str' => ( new Money( $total, $currency_code ) )->value_str(),
|
'total_str' => ( new Money( $total, $currency_code ) )->value_str(),
|
||||||
'currency_code' => $currency_code,
|
'currency_code' => $currency_code,
|
||||||
'country_code' => $shop_country_code,
|
'country_code' => $shop_country_code,
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( $include_shipping ) {
|
||||||
|
$response = $this->append_shipping_data( $response, $currency_code );
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_success( $response );
|
||||||
return true;
|
return true;
|
||||||
} catch ( Throwable $error ) {
|
} catch ( Throwable $error ) {
|
||||||
$this->logger->error( "CartScriptParamsEndpoint execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" );
|
$this->logger->error( "CartScriptParamsEndpoint execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" );
|
||||||
|
@ -101,4 +106,45 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends shipping data to response.
|
||||||
|
*
|
||||||
|
* @param array $response The response array.
|
||||||
|
* @param string $currency_code The currency code.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function append_shipping_data( array $response, string $currency_code ): array {
|
||||||
|
$calculated_packages = WC()->shipping->calculate_shipping(
|
||||||
|
WC()->cart->get_shipping_packages()
|
||||||
|
);
|
||||||
|
|
||||||
|
$shipping_packages = array();
|
||||||
|
|
||||||
|
foreach ( $calculated_packages[0]['rates'] as $rate ) {
|
||||||
|
$rate_cost = $rate->get_cost();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shipping rate.
|
||||||
|
*
|
||||||
|
* @var \WC_Shipping_Rate $rate
|
||||||
|
*/
|
||||||
|
$shipping_packages[] = array(
|
||||||
|
'id' => $rate->get_id(),
|
||||||
|
'label' => $rate->get_label(),
|
||||||
|
'cost' => (float) $rate_cost,
|
||||||
|
'cost_str' => ( new Money( (float) $rate_cost, $currency_code ) )->value_str(),
|
||||||
|
'description' => html_entity_decode(
|
||||||
|
wp_strip_all_tags(
|
||||||
|
wc_price( (float) $rate->get_cost(), array( 'currency' => get_woocommerce_currency() ) )
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['chosen_shipping_methods'] = WC()->session->get( 'chosen_shipping_methods' );
|
||||||
|
$response['shipping_packages'] = $shipping_packages;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
17
modules/ppcp-card-fields/composer.json
Normal file
17
modules/ppcp-card-fields/composer.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "woocommerce/ppcp-card-fields",
|
||||||
|
"type": "dhii-mod",
|
||||||
|
"description": "Advanced Checkout Card Fields module",
|
||||||
|
"license": "GPL-2.0",
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 | ^8.0",
|
||||||
|
"dhii/module-interface": "^0.3.0-alpha1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"WooCommerce\\PayPalCommerce\\CardFields\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
12
modules/ppcp-card-fields/extensions.php
Normal file
12
modules/ppcp-card-fields/extensions.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module extensions.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
return array();
|
16
modules/ppcp-card-fields/module.php
Normal file
16
modules/ppcp-card-fields/module.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||||
|
|
||||||
|
return static function (): ModuleInterface {
|
||||||
|
return new CardFieldsModule();
|
||||||
|
};
|
44
modules/ppcp-card-fields/services.php
Normal file
44
modules/ppcp-card-fields/services.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module services.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\CardFields\Helper\CardFieldsApplies;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'card-fields.eligible' => static function ( ContainerInterface $container ): bool {
|
||||||
|
$save_payment_methods_applies = $container->get( 'card-fields.helpers.save-payment-methods-applies' );
|
||||||
|
assert( $save_payment_methods_applies instanceof CardFieldsApplies );
|
||||||
|
|
||||||
|
return $save_payment_methods_applies->for_country_currency();
|
||||||
|
},
|
||||||
|
'card-fields.helpers.save-payment-methods-applies' => static function ( ContainerInterface $container ) : CardFieldsApplies {
|
||||||
|
return new CardFieldsApplies(
|
||||||
|
$container->get( 'card-fields.supported-country-currency-matrix' ),
|
||||||
|
$container->get( 'api.shop.currency' ),
|
||||||
|
$container->get( 'api.shop.country' )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'card-fields.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||||
|
return apply_filters(
|
||||||
|
'woocommerce_paypal_payments_card_fields_supported_country_currency_matrix',
|
||||||
|
array(
|
||||||
|
'US' => array(
|
||||||
|
'AUD',
|
||||||
|
'CAD',
|
||||||
|
'EUR',
|
||||||
|
'GBP',
|
||||||
|
'JPY',
|
||||||
|
'USD',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
115
modules/ppcp-card-fields/src/CardFieldsModule.php
Normal file
115
modules/ppcp-card-fields/src/CardFieldsModule.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CardFieldsModule
|
||||||
|
*/
|
||||||
|
class CardFieldsModule implements ModuleInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function setup(): ServiceProviderInterface {
|
||||||
|
return new ServiceProvider(
|
||||||
|
require __DIR__ . '/../services.php',
|
||||||
|
require __DIR__ . '/../extensions.php'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function run( ContainerInterface $c ): void {
|
||||||
|
if ( ! $c->get( 'card-fields.eligible' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Param types removed to avoid third-party issues.
|
||||||
|
*
|
||||||
|
* @psalm-suppress MissingClosureParamType
|
||||||
|
*/
|
||||||
|
add_filter(
|
||||||
|
'woocommerce_paypal_payments_sdk_components_hook',
|
||||||
|
function( $components ) {
|
||||||
|
if ( in_array( 'hosted-fields', $components, true ) ) {
|
||||||
|
$key = array_search( 'hosted-fields', $components, true );
|
||||||
|
if ( $key !== false ) {
|
||||||
|
unset( $components[ $key ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$components[] = 'card-fields';
|
||||||
|
|
||||||
|
return $components;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
add_filter(
|
||||||
|
'woocommerce_credit_card_form_fields',
|
||||||
|
/**
|
||||||
|
* Return/Param types removed to avoid third-party issues.
|
||||||
|
*
|
||||||
|
* @psalm-suppress MissingClosureReturnType
|
||||||
|
* @psalm-suppress MissingClosureParamType
|
||||||
|
*/
|
||||||
|
function( $default_fields, $id ) {
|
||||||
|
if ( CreditCardGateway::ID === $id && apply_filters( 'woocommerce_paypal_payments_enable_cardholder_name_field', false ) ) {
|
||||||
|
$default_fields['card-name-field'] = '<p class="form-row form-row-wide">
|
||||||
|
<label for="ppcp-credit-card-gateway-card-name">' . esc_attr__( 'Cardholder Name', 'woocommerce-paypal-payments' ) . '</label>
|
||||||
|
<input id="ppcp-credit-card-gateway-card-name" class="input-text wc-credit-card-form-card-expiry" type="text" placeholder="' . esc_attr__( 'Cardholder Name (optional)', 'woocommerce-paypal-payments' ) . '" name="ppcp-credit-card-gateway-card-name">
|
||||||
|
</p>';
|
||||||
|
|
||||||
|
// Moves new item to first position.
|
||||||
|
$new_field = $default_fields['card-name-field'];
|
||||||
|
unset( $default_fields['card-name-field'] );
|
||||||
|
array_unshift( $default_fields, $new_field );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default_fields;
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
add_filter(
|
||||||
|
'ppcp_create_order_request_body_data',
|
||||||
|
function( array $data ) use ( $c ): array {
|
||||||
|
$settings = $c->get( 'wcgateway.settings' );
|
||||||
|
assert( $settings instanceof Settings );
|
||||||
|
|
||||||
|
if (
|
||||||
|
$settings->has( '3d_secure_contingency' )
|
||||||
|
&& (
|
||||||
|
$settings->get( '3d_secure_contingency' ) === 'SCA_ALWAYS'
|
||||||
|
|| $settings->get( '3d_secure_contingency' ) === 'SCA_WHEN_REQUIRED'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$data['payment_source']['card'] = array(
|
||||||
|
'attributes' => array(
|
||||||
|
'verification' => array(
|
||||||
|
'method' => $settings->get( '3d_secure_contingency' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
modules/ppcp-card-fields/src/Helper/CardFieldsApplies.php
Normal file
66
modules/ppcp-card-fields/src/Helper/CardFieldsApplies.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Service for checking whether Card Fields can be used in the current country and the current currency.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields\Helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields\Helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CardFieldsApplies
|
||||||
|
*/
|
||||||
|
class CardFieldsApplies {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The matrix which countries and currency combinations can be used.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $allowed_country_currency_matrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3-letter currency code of the shop.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2-letter country code of the shop.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $country;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CardFieldsApplies constructor.
|
||||||
|
*
|
||||||
|
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used.
|
||||||
|
* @param string $currency 3-letter currency code of the shop.
|
||||||
|
* @param string $country 2-letter country code of the shop.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
array $allowed_country_currency_matrix,
|
||||||
|
string $currency,
|
||||||
|
string $country
|
||||||
|
) {
|
||||||
|
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||||
|
$this->currency = $currency;
|
||||||
|
$this->country = $country;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether Card Fields can be used in the current country and the current currency.
|
||||||
|
*
|
||||||
|
* @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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -273,7 +273,12 @@ class CompatModule implements ModuleInterface {
|
||||||
add_action(
|
add_action(
|
||||||
'init',
|
'init',
|
||||||
function() {
|
function() {
|
||||||
if ( $this->is_elementor_pro_active() || $this->is_divi_theme_active() ) {
|
if (
|
||||||
|
$this->is_block_theme_active()
|
||||||
|
|| $this->is_elementor_pro_active()
|
||||||
|
|| $this->is_divi_theme_active()
|
||||||
|
|| $this->is_divi_child_theme_active()
|
||||||
|
) {
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_paypal_payments_single_product_renderer_hook',
|
'woocommerce_paypal_payments_single_product_renderer_hook',
|
||||||
function(): string {
|
function(): string {
|
||||||
|
@ -286,6 +291,15 @@ class CompatModule implements ModuleInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the current theme is a blocks theme.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function is_block_theme_active(): bool {
|
||||||
|
return function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the Elementor Pro plugins (allowing integrations with WC) is active.
|
* Checks whether the Elementor Pro plugins (allowing integrations with WC) is active.
|
||||||
*
|
*
|
||||||
|
@ -304,4 +318,15 @@ class CompatModule implements ModuleInterface {
|
||||||
$theme = wp_get_theme();
|
$theme = wp_get_theme();
|
||||||
return $theme->get( 'Name' ) === 'Divi';
|
return $theme->get( 'Name' ) === 'Divi';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a Divi child theme is currently used.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function is_divi_child_theme_active(): bool {
|
||||||
|
$theme = wp_get_theme();
|
||||||
|
$parent = $theme->parent();
|
||||||
|
return ( $parent && $parent->get( 'Name' ) === 'Divi' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,13 +135,14 @@ return array(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'googlepay_button_type' => array(
|
'googlepay_button_type' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( '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' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'pay',
|
'default' => 'pay',
|
||||||
|
@ -151,7 +152,7 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'googlepay_button_color' => array(
|
'googlepay_button_color' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Button Color', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Button Color', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
|
@ -160,6 +161,7 @@ return array(
|
||||||
),
|
),
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
|
'classes' => array( 'ppcp-field-indent' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'default' => 'black',
|
'default' => 'black',
|
||||||
'options' => PropertiesDictionary::button_colors(),
|
'options' => PropertiesDictionary::button_colors(),
|
||||||
|
@ -168,13 +170,14 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'googlepay_button_language' => array(
|
'googlepay_button_language' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( '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' ),
|
||||||
'class' => array(),
|
'class' => array(),
|
||||||
'input_class' => array( 'wc-enhanced-select' ),
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
'default' => 'en',
|
'default' => 'en',
|
||||||
|
@ -184,13 +187,14 @@ return array(
|
||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
),
|
),
|
||||||
'googlepay_button_shipping_enabled' => array(
|
'googlepay_button_shipping_enabled' => array(
|
||||||
'title' => str_repeat( ' ', 6 ) . __( 'Shipping Callback', 'woocommerce-paypal-payments' ),
|
'title' => __( 'Shipping Callback', 'woocommerce-paypal-payments' ),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'desc_tip' => true,
|
'desc_tip' => true,
|
||||||
'description' => __(
|
'description' => __(
|
||||||
'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' ),
|
||||||
'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 ),
|
||||||
|
|
|
@ -10,6 +10,13 @@ class BaseHandler {
|
||||||
this.externalHandler = externalHandler;
|
this.externalHandler = externalHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.cart ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
shippingAllowed() {
|
shippingAllowed() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,13 @@ import CheckoutActionHandler
|
||||||
|
|
||||||
class PayNowHandler extends BaseHandler {
|
class PayNowHandler extends BaseHandler {
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.payorder ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
shippingAllowed() {
|
shippingAllowed() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,13 @@ import BaseHandler from "./BaseHandler";
|
||||||
|
|
||||||
class SingleProductHandler extends BaseHandler {
|
class SingleProductHandler extends BaseHandler {
|
||||||
|
|
||||||
|
validateContext() {
|
||||||
|
if ( this.ppcpConfig?.locations_with_subscription_product?.product ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
transactionInfo() {
|
transactionInfo() {
|
||||||
const errorHandler = new ErrorHandler(
|
const errorHandler = new ErrorHandler(
|
||||||
this.ppcpConfig.labels.error.generic,
|
this.ppcpConfig.labels.error.generic,
|
||||||
|
|
|
@ -40,6 +40,10 @@ class GooglepayButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.contextHandler.validateContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.googlePayConfig = config;
|
this.googlePayConfig = config;
|
||||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[0];
|
this.baseCardPaymentMethod = this.allowedPaymentMethods[0];
|
||||||
|
|
|
@ -94,6 +94,7 @@ class GooglepayModule implements ModuleInterface {
|
||||||
assert( $smart_button instanceof SmartButtonInterface );
|
assert( $smart_button instanceof SmartButtonInterface );
|
||||||
if ( $smart_button->should_load_ppcp_script() ) {
|
if ( $smart_button->should_load_ppcp_script() ) {
|
||||||
$button->enqueue();
|
$button->enqueue();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
|
if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
|
||||||
|
@ -111,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() ) {
|
if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-standard-payments-page' ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,16 @@ use WooCommerce\PayPalCommerce\OrderTracking\Assets\OrderEditPageAssets;
|
||||||
use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint;
|
use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'order-tracking.assets' => function( ContainerInterface $container ) : OrderEditPageAssets {
|
'order-tracking.assets' => function( ContainerInterface $container ) : OrderEditPageAssets {
|
||||||
return new OrderEditPageAssets(
|
return new OrderEditPageAssets(
|
||||||
$container->get( 'order-tracking.module.url' ),
|
$container->get( 'order-tracking.module.url' ),
|
||||||
$container->get( 'ppcp.asset-version' )
|
$container->get( 'ppcp.asset-version' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.shipment.factory' => static function ( ContainerInterface $container ) : ShipmentFactoryInterface {
|
'order-tracking.shipment.factory' => static function ( ContainerInterface $container ) : ShipmentFactoryInterface {
|
||||||
return new ShipmentFactory();
|
return new ShipmentFactory();
|
||||||
},
|
},
|
||||||
'order-tracking.endpoint.controller' => static function ( ContainerInterface $container ) : OrderTrackingEndpoint {
|
'order-tracking.endpoint.controller' => static function ( ContainerInterface $container ) : OrderTrackingEndpoint {
|
||||||
return new OrderTrackingEndpoint(
|
return new OrderTrackingEndpoint(
|
||||||
$container->get( 'api.host' ),
|
$container->get( 'api.host' ),
|
||||||
$container->get( 'api.bearer' ),
|
$container->get( 'api.bearer' ),
|
||||||
|
@ -38,10 +38,10 @@ return array(
|
||||||
$container->get( 'button.request-data' ),
|
$container->get( 'button.request-data' ),
|
||||||
$container->get( 'order-tracking.shipment.factory' ),
|
$container->get( 'order-tracking.shipment.factory' ),
|
||||||
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
||||||
$container->get( 'order-tracking.is-merchant-country-us' )
|
$container->get( 'order-tracking.should-use-second-version-of-api' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.module.url' => static function ( ContainerInterface $container ): string {
|
'order-tracking.module.url' => static function ( ContainerInterface $container ): string {
|
||||||
/**
|
/**
|
||||||
* The path cannot be false.
|
* The path cannot be false.
|
||||||
*
|
*
|
||||||
|
@ -52,15 +52,15 @@ return array(
|
||||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.meta-box.renderer' => static function ( ContainerInterface $container ): MetaBoxRenderer {
|
'order-tracking.meta-box.renderer' => static function ( ContainerInterface $container ): MetaBoxRenderer {
|
||||||
return new MetaBoxRenderer(
|
return new MetaBoxRenderer(
|
||||||
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
||||||
$container->get( 'order-tracking.available-carriers' ),
|
$container->get( 'order-tracking.available-carriers' ),
|
||||||
$container->get( 'order-tracking.endpoint.controller' ),
|
$container->get( 'order-tracking.endpoint.controller' ),
|
||||||
$container->get( 'order-tracking.is-merchant-country-us' )
|
$container->get( 'order-tracking.should-use-second-version-of-api' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.allowed-shipping-statuses' => static function ( ContainerInterface $container ): array {
|
'order-tracking.allowed-shipping-statuses' => static function ( ContainerInterface $container ): array {
|
||||||
return (array) apply_filters(
|
return (array) apply_filters(
|
||||||
'woocommerce_paypal_payments_tracking_statuses',
|
'woocommerce_paypal_payments_tracking_statuses',
|
||||||
array(
|
array(
|
||||||
|
@ -71,10 +71,10 @@ return array(
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.allowed-carriers' => static function ( ContainerInterface $container ): array {
|
'order-tracking.allowed-carriers' => static function ( ContainerInterface $container ): array {
|
||||||
return require __DIR__ . '/carriers.php';
|
return require __DIR__ . '/carriers.php';
|
||||||
},
|
},
|
||||||
'order-tracking.available-carriers' => static function ( ContainerInterface $container ): array {
|
'order-tracking.available-carriers' => static function ( ContainerInterface $container ): array {
|
||||||
$api_shop_country = $container->get( 'api.shop.country' );
|
$api_shop_country = $container->get( 'api.shop.country' );
|
||||||
$allowed_carriers = $container->get( 'order-tracking.allowed-carriers' );
|
$allowed_carriers = $container->get( 'order-tracking.allowed-carriers' );
|
||||||
$selected_country_carriers = $allowed_carriers[ $api_shop_country ] ?? array();
|
$selected_country_carriers = $allowed_carriers[ $api_shop_country ] ?? array();
|
||||||
|
@ -90,10 +90,26 @@ return array(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.is-merchant-country-us' => static function ( ContainerInterface $container ): bool {
|
|
||||||
return $container->get( 'api.shop.country' ) === 'US';
|
/**
|
||||||
|
* The list of country codes, for which the 2nd version of PayPal tracking API is supported.
|
||||||
|
*/
|
||||||
|
'order-tracking.second-version-api-supported-countries' => static function (): array {
|
||||||
|
/**
|
||||||
|
* Returns codes of countries, for which the 2nd version of PayPal tracking API is supported.
|
||||||
|
*/
|
||||||
|
return apply_filters(
|
||||||
|
'woocommerce_paypal_payments_supported_country_codes_for_second_version_of_tracking_api',
|
||||||
|
array( 'US', 'AU', 'CA', 'FR', 'DE', 'IT', 'ES' )
|
||||||
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.integrations' => static function ( ContainerInterface $container ): array {
|
'order-tracking.should-use-second-version-of-api' => static function ( ContainerInterface $container ): bool {
|
||||||
|
$supported_county_codes = $container->get( 'order-tracking.second-version-api-supported-countries' );
|
||||||
|
$selected_country_code = $container->get( 'api.shop.country' );
|
||||||
|
|
||||||
|
return in_array( $selected_country_code, $supported_county_codes, true );
|
||||||
|
},
|
||||||
|
'order-tracking.integrations' => static function ( ContainerInterface $container ): array {
|
||||||
$shipment_factory = $container->get( 'order-tracking.shipment.factory' );
|
$shipment_factory = $container->get( 'order-tracking.shipment.factory' );
|
||||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||||
$endpoint = $container->get( 'order-tracking.endpoint.controller' );
|
$endpoint = $container->get( 'order-tracking.endpoint.controller' );
|
||||||
|
|
|
@ -271,4 +271,17 @@ class SubscriptionHelper {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locations on the page which have subscription products.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function locations_with_subscription_product(): array {
|
||||||
|
return array(
|
||||||
|
'product' => is_product() && $this->current_product_is_subscription(),
|
||||||
|
'payorder' => is_wc_endpoint_url( 'order-pay' ) && $this->order_pay_contains_subscription(),
|
||||||
|
'cart' => $this->cart_contains_subscription(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,17 +62,27 @@ return array(
|
||||||
},
|
},
|
||||||
|
|
||||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||||
$path_to_settings_fields = __DIR__ . '/src/Settings/Fields';
|
$files = array(
|
||||||
|
'paypal-smart-button-fields.php',
|
||||||
|
'connection-tab-fields.php',
|
||||||
|
'pay-later-tab-fields.php',
|
||||||
|
'card-button-fields.php',
|
||||||
|
);
|
||||||
|
|
||||||
$get_paypal_button_fields = require $path_to_settings_fields . '/paypal-smart-button-fields.php';
|
return array_merge(
|
||||||
$paypal_button_fields = $get_paypal_button_fields( $container, $fields ) ?? array();
|
...array_map(
|
||||||
|
function ( string $file ) use ( $container, $fields ): array {
|
||||||
$get_connection_tab_fields = require $path_to_settings_fields . '/connection-tab-fields.php';
|
$path_to_settings_fields = __DIR__ . '/src/Settings/Fields/';
|
||||||
$connection_tab_fields = $get_connection_tab_fields( $container, $fields ) ?? array();
|
/**
|
||||||
|
* Skip path check.
|
||||||
$get_pay_later_tab_fields = require $path_to_settings_fields . '/pay-later-tab-fields.php';
|
*
|
||||||
$pay_later_tab_fields = $get_pay_later_tab_fields( $container, $fields ) ?? array();
|
* @psalm-suppress UnresolvableInclude
|
||||||
|
*/
|
||||||
return array_merge( $paypal_button_fields, $connection_tab_fields, $pay_later_tab_fields );
|
$get_fields = require $path_to_settings_fields . $file;
|
||||||
|
return $get_fields( $container, $fields ) ?? array();
|
||||||
|
},
|
||||||
|
$files
|
||||||
|
)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,3 +9,9 @@
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ppcp-field-indent {
|
||||||
|
th {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -77,7 +77,8 @@ document.addEventListener(
|
||||||
|
|
||||||
function createButtonPreview(settingsCallback) {
|
function createButtonPreview(settingsCallback) {
|
||||||
const render = (settings) => {
|
const render = (settings) => {
|
||||||
const wrapper = document.querySelector(settings.button.wrapper);
|
const wrapperSelector = Object.values(settings.separate_buttons).length > 0 ? Object.values(settings.separate_buttons)[0].wrapper : settings.button.wrapper;
|
||||||
|
const wrapper = document.querySelector(wrapperSelector);
|
||||||
if (!wrapper) {
|
if (!wrapper) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +97,11 @@ document.addEventListener(
|
||||||
renderPreview(settingsCallback, render);
|
renderPreview(settingsCallback, render);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function currentTabId() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
return params.has('ppcp-tab') ? params.get('ppcp-tab') : params.get('section');
|
||||||
|
}
|
||||||
|
|
||||||
function shouldShowPayLaterButton() {
|
function shouldShowPayLaterButton() {
|
||||||
const payLaterButtonLocations = document.querySelector('[name="ppcp[pay_later_button_locations][]"]');
|
const payLaterButtonLocations = document.querySelector('[name="ppcp[pay_later_button_locations][]"]');
|
||||||
|
|
||||||
|
@ -106,6 +112,14 @@ document.addEventListener(
|
||||||
return payLaterButtonInput.checked && payLaterButtonLocations.selectedOptions.length > 0
|
return payLaterButtonInput.checked && payLaterButtonLocations.selectedOptions.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldDisableCardButton() {
|
||||||
|
if (currentTabId() === 'ppcp-card-button-gateway') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PayPalCommerceGatewaySettings.is_acdc_enabled || jQuery('#ppcp-allow_card_button_gateway').is(':checked');
|
||||||
|
}
|
||||||
|
|
||||||
function getPaypalScriptSettings() {
|
function getPaypalScriptSettings() {
|
||||||
const disableFundingInput = jQuery('[name="ppcp[disable_funding][]"]');
|
const disableFundingInput = jQuery('[name="ppcp[disable_funding][]"]');
|
||||||
let disabledSources = disableFundingInput.length > 0 ? disableFundingInput.val() : PayPalCommerceGatewaySettings.disabled_sources;
|
let disabledSources = disableFundingInput.length > 0 ? disableFundingInput.val() : PayPalCommerceGatewaySettings.disabled_sources;
|
||||||
|
@ -130,6 +144,10 @@ document.addEventListener(
|
||||||
disabledSources = disabledSources.concat('credit')
|
disabledSources = disabledSources.concat('credit')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldDisableCardButton()) {
|
||||||
|
disabledSources = disabledSources.concat('card');
|
||||||
|
}
|
||||||
|
|
||||||
if (disabledSources?.length) {
|
if (disabledSources?.length) {
|
||||||
settings['disable-funding'] = disabledSources;
|
settings['disable-funding'] = disabledSources;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +172,7 @@ document.addEventListener(
|
||||||
.catch((error) => console.error('failed to load the PayPal JS SDK script', error));
|
.catch((error) => console.error('failed to load the PayPal JS SDK script', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getButtonSettings(wrapperSelector, fields) {
|
function getButtonSettings(wrapperSelector, fields, apm = null) {
|
||||||
const layoutElement = jQuery(fields['layout']);
|
const layoutElement = jQuery(fields['layout']);
|
||||||
const layout = (layoutElement.length && layoutElement.is(':visible')) ? layoutElement.val() : 'vertical';
|
const layout = (layoutElement.length && layoutElement.is(':visible')) ? layoutElement.val() : 'vertical';
|
||||||
const style = {
|
const style = {
|
||||||
|
@ -167,13 +185,24 @@ document.addEventListener(
|
||||||
if ('height' in fields) {
|
if ('height' in fields) {
|
||||||
style['height'] = parseInt(jQuery(fields['height']).val());
|
style['height'] = parseInt(jQuery(fields['height']).val());
|
||||||
}
|
}
|
||||||
return {
|
if ('poweredby_tagline' in fields) {
|
||||||
|
style['layout'] = jQuery(fields['poweredby_tagline']).is(':checked') ? 'vertical' : 'horizontal';
|
||||||
|
}
|
||||||
|
const settings = {
|
||||||
'button': {
|
'button': {
|
||||||
'wrapper': wrapperSelector,
|
'wrapper': wrapperSelector,
|
||||||
'style': style,
|
'style': style,
|
||||||
},
|
},
|
||||||
'separate_buttons': {},
|
'separate_buttons': {},
|
||||||
};
|
};
|
||||||
|
if (apm) {
|
||||||
|
settings.separate_buttons[apm] = {
|
||||||
|
'wrapper': wrapperSelector,
|
||||||
|
'style': style,
|
||||||
|
};
|
||||||
|
settings.button.wrapper = null;
|
||||||
|
}
|
||||||
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMessagesPreview(settingsCallback) {
|
function createMessagesPreview(settingsCallback) {
|
||||||
|
@ -316,6 +345,13 @@ document.addEventListener(
|
||||||
});
|
});
|
||||||
|
|
||||||
createButtonPreview(() => getButtonDefaultSettings('#ppcpPayLaterButtonPreview'));
|
createButtonPreview(() => getButtonDefaultSettings('#ppcpPayLaterButtonPreview'));
|
||||||
|
|
||||||
|
const apmFieldPrefix = '#ppcp-card_button_';
|
||||||
|
createButtonPreview(() => getButtonSettings('#ppcpCardButtonPreview', {
|
||||||
|
'color': apmFieldPrefix + 'color',
|
||||||
|
'shape': apmFieldPrefix + 'shape',
|
||||||
|
'poweredby_tagline': apmFieldPrefix + 'poweredby_tagline',
|
||||||
|
}, 'card'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,11 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'wcgateway.is-ppcp-settings-standard-payments-page' => static function ( ContainerInterface $container ): bool {
|
||||||
|
return $container->get( 'wcgateway.is-ppcp-settings-page' )
|
||||||
|
&& $container->get( 'wcgateway.current-ppcp-settings-page-id' ) === PayPalGateway::ID;
|
||||||
|
},
|
||||||
|
|
||||||
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
|
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
|
||||||
if ( ! $container->get( 'wcgateway.is-ppcp-settings-page' ) ) {
|
if ( ! $container->get( 'wcgateway.is-ppcp-settings-page' ) ) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SettingsPageAssets
|
* Class SettingsPageAssets
|
||||||
|
@ -94,6 +96,13 @@ class SettingsPageAssets {
|
||||||
*/
|
*/
|
||||||
private $is_settings_page;
|
private $is_settings_page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the ACDC gateway is enabled.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $is_acdc_enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assets constructor.
|
* Assets constructor.
|
||||||
*
|
*
|
||||||
|
@ -108,6 +117,7 @@ class SettingsPageAssets {
|
||||||
* @param array $disabled_sources The list of disabled funding sources.
|
* @param array $disabled_sources The list of disabled funding sources.
|
||||||
* @param array $all_funding_sources The list of all existing funding sources.
|
* @param array $all_funding_sources The list of all existing funding sources.
|
||||||
* @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.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $module_url,
|
string $module_url,
|
||||||
|
@ -120,7 +130,8 @@ class SettingsPageAssets {
|
||||||
bool $is_pay_later_button_enabled,
|
bool $is_pay_later_button_enabled,
|
||||||
array $disabled_sources,
|
array $disabled_sources,
|
||||||
array $all_funding_sources,
|
array $all_funding_sources,
|
||||||
bool $is_settings_page
|
bool $is_settings_page,
|
||||||
|
bool $is_acdc_enabled
|
||||||
) {
|
) {
|
||||||
$this->module_url = $module_url;
|
$this->module_url = $module_url;
|
||||||
$this->version = $version;
|
$this->version = $version;
|
||||||
|
@ -133,6 +144,7 @@ class SettingsPageAssets {
|
||||||
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,7 +193,7 @@ class SettingsPageAssets {
|
||||||
$section = wc_clean( wp_unslash( $_GET['section'] ?? '' ) );
|
$section = wc_clean( wp_unslash( $_GET['section'] ?? '' ) );
|
||||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||||
|
|
||||||
return 'checkout' === $tab && 'ppcp-gateway' === $section;
|
return 'checkout' === $tab && in_array( $section, array( PayPalGateway::ID, CardButtonGateway::ID ), true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -221,6 +233,7 @@ class SettingsPageAssets {
|
||||||
'environment' => $this->environment->current_environment(),
|
'environment' => $this->environment->current_environment(),
|
||||||
'integration_date' => PAYPAL_INTEGRATION_DATE,
|
'integration_date' => PAYPAL_INTEGRATION_DATE,
|
||||||
'is_pay_later_button_enabled' => $this->is_pay_later_button_enabled,
|
'is_pay_later_button_enabled' => $this->is_pay_later_button_enabled,
|
||||||
|
'is_acdc_enabled' => $this->is_acdc_enabled,
|
||||||
'disabled_sources' => $this->disabled_sources,
|
'disabled_sources' => $this->disabled_sources,
|
||||||
'all_funding_sources' => $this->all_funding_sources,
|
'all_funding_sources' => $this->all_funding_sources,
|
||||||
'components' => array( 'buttons', 'funding-eligibility', 'messages' ),
|
'components' => array( 'buttons', 'funding-eligibility', 'messages' ),
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The services of the Gateway module.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\WcGateway
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||||
|
|
||||||
|
return function ( ContainerInterface $container, array $fields ): array {
|
||||||
|
|
||||||
|
$current_page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
|
||||||
|
|
||||||
|
if ( $current_page_id !== CardButtonGateway::ID ) {
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$render_preview_element = function ( string $id ): string {
|
||||||
|
return '
|
||||||
|
<div class="ppcp-preview ppcp-button-preview">
|
||||||
|
<h4>' . __( 'Standard Card Button Styling Preview', 'woocommerce-paypal-payments' ) . '</h4>
|
||||||
|
<div id="' . $id . '" class="ppcp-button-preview-inner"></div>
|
||||||
|
</div>';
|
||||||
|
};
|
||||||
|
|
||||||
|
$new_fields = array(
|
||||||
|
'card_button_styling_heading' => array(
|
||||||
|
'heading' => __( 'Standard Card Button Styling', 'woocommerce-paypal-payments' ),
|
||||||
|
'description' => sprintf(
|
||||||
|
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||||
|
__(
|
||||||
|
'Customize the appearance of the Standard Card Button on the %1$sCheckout page%2$s.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#button-on-checkout" target="_blank">',
|
||||||
|
'</a>',
|
||||||
|
'</ br>'
|
||||||
|
),
|
||||||
|
'type' => 'ppcp-heading',
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_START,
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
'card_button_poweredby_tagline' => array(
|
||||||
|
'title' => __( 'Tagline', 'woocommerce-paypal-payments' ),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __( 'Enable "Powered by PayPal" tagline', 'woocommerce-paypal-payments' ),
|
||||||
|
'default' => false,
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'Add the "Powered by PayPal" line below the button.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_START,
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
'card_button_color' => array(
|
||||||
|
'title' => __( 'Color', 'woocommerce-paypal-payments' ),
|
||||||
|
'type' => 'select',
|
||||||
|
'class' => array(),
|
||||||
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
|
'default' => 'black',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'Controls the background color of the button. Change it to match your site design or aesthetic.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
'options' => array(
|
||||||
|
'black' => __( 'Black', 'woocommerce-paypal-payments' ),
|
||||||
|
'white' => __( 'White', 'woocommerce-paypal-payments' ),
|
||||||
|
),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_START,
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
'card_button_shape' => array(
|
||||||
|
'title' => __( 'Shape', 'woocommerce-paypal-payments' ),
|
||||||
|
'type' => 'select',
|
||||||
|
'class' => array(),
|
||||||
|
'input_class' => array( 'wc-enhanced-select' ),
|
||||||
|
'default' => 'rect',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
'options' => array(
|
||||||
|
'pill' => __( 'Pill', 'woocommerce-paypal-payments' ),
|
||||||
|
'rect' => __( 'Rectangle', 'woocommerce-paypal-payments' ),
|
||||||
|
),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_START,
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
'card_button_preview' => array(
|
||||||
|
'type' => 'ppcp-text',
|
||||||
|
'text' => $render_preview_element( 'ppcpCardButtonPreview' ),
|
||||||
|
'screens' => array(
|
||||||
|
State::STATE_ONBOARDED,
|
||||||
|
),
|
||||||
|
'requirements' => array(),
|
||||||
|
'gateway' => CardButtonGateway::ID,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_merge( $fields, $new_fields );
|
||||||
|
};
|
|
@ -185,7 +185,8 @@ class WCGatewayModule implements ModuleInterface {
|
||||||
$settings_status->is_pay_later_button_enabled(),
|
$settings_status->is_pay_later_button_enabled(),
|
||||||
$settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(),
|
$settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(),
|
||||||
$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' )
|
||||||
);
|
);
|
||||||
$assets->register_assets();
|
$assets->register_assets();
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
|
||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
= 2.4.1 - xxxx-xx-xx =
|
= 2.4.1 - 2023-11-14 =
|
||||||
* Fix - Error "PayPal order ID not found in meta" prevents automations from triggering when buying subscription via third-party payment gateway #1822
|
* Fix - Error "PayPal order ID not found in meta" prevents automations from triggering when buying subscription via third-party payment gateway #1822
|
||||||
* Fix - Card button subscription support declaration #1796
|
* Fix - Card button subscription support declaration #1796
|
||||||
* Fix - Pay Later messaging disappears when updating shipping option on cart page #1807
|
* Fix - Pay Later messaging disappears when updating shipping option on cart page #1807
|
||||||
|
|
|
@ -28,7 +28,8 @@ class SettingsPagesAssetsTest extends TestCase
|
||||||
true,
|
true,
|
||||||
array(),
|
array(),
|
||||||
array(),
|
array(),
|
||||||
true
|
true,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
when('is_admin')
|
when('is_admin')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue