Merge trunk

This commit is contained in:
Emili Castells Guasch 2023-11-07 09:25:59 +01:00
commit aa1942b604
30 changed files with 589 additions and 48 deletions

View file

@ -1,5 +1,25 @@
*** Changelog ***
= 2.4.1 - xxxx-xx-xx =
* 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 - Pay Later messaging disappears when updating shipping option on cart page #1807
* Fix - Apple Pay payment from single product may fail after changing shipping options in Apple Pay payment sheet #1810
* Enhancement - Extend list of supported countries for Advanced Card Processing #1808
* Enhancement - Extend Apple Pay/Google Pay country eligibility to Italy #1811
* Enhancement - Override language used to display PayPal buttons #600
* Enhancement - Apple Pay button preview #1824
* Enhancement - Add Apple Pay & Google Pay logos on the onboarding page #1823
* Enhancement - Improve Apple Pay compatibility with variable products on single product page #1803
* Enhancement - Apple Pay domain registration & browser eligibility check #1821
* Enhancement - Package Tracking compatibility with WooCommerce Shipping & ShipStation for WooCommerce #1813
* Enhancement - Fill form when continuation in block #1794
* Enhancement - Display Shop location Pay Later messaging on product category pages #1809
* Enhancement - Present apple-developer-merchantid-domain-association file only when Apple Pay is enabled #1818
* Enhancement - Improve Apple Pay compatibility on Pay for Order page #1815
* Enhancement - Display Pay Later messages before the payment methods on the Pay for Order page #1814
* Enhancement - Handle undefined array key warnings on PHP 8.1 #1804
= 2.4.0 - 2023-10-31 =
* Fix - Mini-Cart Bug cause of wrong DOM-Structure in v2.3.1 #1735
* Fix - ACDC disappearing after plugin updates #1751

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;
@ -37,6 +38,34 @@ return array(
$display_manager = $container->get( 'wcgateway.display-manager' );
assert( $display_manager instanceof DisplayManager );
// Domain registration.
$env = $container->get( 'onboarding.environment' );
assert( $env instanceof Environment );
$domain_registration_url = 'https://www.paypal.com/uccservicing/apm/applepay';
if ( $env->current_environment_is( Environment::SANDBOX ) ) {
$domain_registration_url = 'https://www.sandbox.paypal.com/uccservicing/apm/applepay';
}
// Domain validation.
$domain_validation_text = __( 'Status: Domain validation failed ❌', 'woocommerce-paypal-payments' );
if ( $container->get( 'applepay.is_validated' ) ) {
$domain_validation_text = __( 'Status: Domain successfully validated ✔️', 'woocommerce-paypal-payments' );
}
// Device eligibility.
$device_eligibility_text = __( 'Your current browser/device does not seem to support Apple Pay ❌.', 'woocommerce-paypal-payments' );
$device_eligibility_notes = sprintf(
// 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' ),
'<a href="https://woo.com/document/woocommerce-paypal-payments/#apple-pay" target="_blank">',
'</a>'
);
if ( $container->get( 'applepay.is_browser_supported' ) ) {
$device_eligibility_text = __( 'Your browser/device supports 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' );
}
// Connection tab fields.
$fields = $insert_after(
$fields,
@ -100,7 +129,7 @@ return array(
$fields,
'allow_card_button_gateway',
array(
'applepay_button_enabled' => array(
'applepay_button_enabled' => array(
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' )
@ -122,6 +151,9 @@ return array(
$display_manager
->rule()
->condition_element( 'applepay_button_enabled', '1' )
->action_visible( 'applepay_button_domain_registration' )
->action_visible( 'applepay_button_domain_validation' )
->action_visible( 'applepay_button_device_eligibility' )
->action_visible( 'applepay_button_color' )
->action_visible( 'applepay_button_type' )
->action_visible( 'applepay_button_language' )
@ -130,7 +162,66 @@ return array(
),
),
),
'applepay_button_type' => array(
'applepay_button_domain_registration' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Domain Registration', 'woocommerce-paypal-payments' ),
'type' => 'ppcp-text',
'text' =>
'<a href="' . $domain_registration_url . '" class="button" target="_blank">'
. __( 'Manage Domain Registration', 'woocommerce-paypal-payments' )
. '</a>'
. '<p class="description">'
. __( 'Any (sub)domain names showing an Apple Pay button must be registered on the PayPal website. If the domain displaying the Apple Pay button isn\'t registered, the payment method won\'t work.', 'woocommerce-paypal-payments' )
. '</p>',
'desc_tip' => true,
'description' => __(
'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'
),
'class' => array(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
'applepay_button_domain_validation' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Domain Validation', 'woocommerce-paypal-payments' ),
'type' => 'ppcp-text',
'text' => $domain_validation_text
. '<p class="description">'
. sprintf(
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
__( '<strong>Note:</strong> PayPal Payments automatically presents the %1$sdomain association file%2$s for Apple to validate your registered domain.', 'woocommerce-paypal-payments' ),
'<a href="/.well-known/apple-developer-merchantid-domain-association" target="_blank">',
'</a>'
)
. '</p>',
'desc_tip' => true,
'description' => __(
'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'
),
'class' => array(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
'applepay_button_device_eligibility' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Device Eligibility', 'woocommerce-paypal-payments' ),
'type' => 'ppcp-text',
'text' => $device_eligibility_text
. '<p class="description">'
. $device_eligibility_notes
. '</p>',
'desc_tip' => true,
'description' => __(
'Apple Pay demands certain Apple devices for secure payment execution. This helps determine if your current device is compliant.',
'woocommerce-paypal-payments'
),
'class' => array(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
'applepay_button_type' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Button Label', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,
@ -146,7 +237,7 @@ return array(
'gateway' => 'paypal',
'requirements' => array(),
),
'applepay_button_color' => array(
'applepay_button_color' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Button Color', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,
@ -163,7 +254,7 @@ return array(
'gateway' => 'paypal',
'requirements' => array(),
),
'applepay_button_language' => array(
'applepay_button_language' => array(
'title' => str_repeat( '&nbsp;', 6 ) . __( 'Button Language', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,

View file

@ -1,11 +1,11 @@
#applepay-container {
#applepay-container, .ppcp-button-applepay {
--apple-pay-button-height: 45px;
--apple-pay-button-min-height: 40px;
--apple-pay-button-width: 100%;
--apple-pay-button-max-width: 750px;
--apple-pay-button-border-radius: 4px;
--apple-pay-button-overflow: hidden;
--apple-pay-button-margin:7px 0;
margin:7px 0;
&.ppcp-button-pill {
--apple-pay-button-border-radius: 50px;
}
@ -17,7 +17,7 @@
}
.woocommerce-checkout {
#applepay-container {
#applepay-container, .ppcp-button-applepay {
margin-top: 0.5em;
--apple-pay-button-border-radius: 4px;
--apple-pay-button-height: 45px;
@ -30,7 +30,7 @@
.ppcp-has-applepay-block {
.wp-block-woocommerce-checkout {
#applepay-container {
#applepay-container, .ppcp-button-applepay {
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
&.ppcp-button-pill {
@ -40,7 +40,7 @@
}
.wp-block-woocommerce-cart {
#applepay-container {
#applepay-container, .ppcp-button-applepay {
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
}
@ -52,5 +52,17 @@
}
}
}
}
.wp-admin {
.ppcp-button-applepay {
pointer-events: none;
}
&.ppcp-non-ios-device {
.ppcp-button-applepay {
apple-pay-button {
display: block;
}
}
}
}

View file

@ -4,6 +4,7 @@ import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hidin
import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
import FormValidator from "../../../ppcp-button/resources/js/modules/Helper/FormValidator";
import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler';
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
class ApplepayButton {
@ -48,7 +49,7 @@ class ApplepayButton {
const isEligible = this.applePayConfig.isEligible;
if (isEligible) {
this.fetchTransactionInfo().then(() => {
const isSubscriptionProduct = this.ppcpConfig.data_client_id.has_subscriptions === true;
const isSubscriptionProduct = this.ppcpConfig?.data_client_id?.has_subscriptions === true;
if (isSubscriptionProduct) {
return;
}
@ -145,13 +146,12 @@ class ApplepayButton {
return session;
}
/**
* Add a Apple Pay purchase button
*/
addButton() {
this.log('addButton', this.context);
const wrapper =
(this.context === 'mini-cart')
? this.buttonConfig.button.mini_cart_wrapper
@ -160,7 +160,7 @@ class ApplepayButton {
(this.context === 'mini-cart')
? this.ppcpConfig.button.mini_cart_style.shape
: this.ppcpConfig.button.style.shape;
const appleContainer = this.context === 'mini-cart' ? document.getElementById("applepay-container-minicart") : document.getElementById("applepay-container");
const appleContainer = document.getElementById(wrapper);
const type = this.buttonConfig.button.type;
const language = this.buttonConfig.button.lang;
const color = this.buttonConfig.button.color;
@ -195,7 +195,7 @@ class ApplepayButton {
try {
const formData = new FormData(document.querySelector(checkoutFormSelector));
this.form_saved = Object.fromEntries(formData.entries());
// This line should be reviewed, the paypal.Applepay().confirmOrder fails if we add it.
// This line should be reviewed, the widgetBuilder.paypal.Applepay().confirmOrder fails if we add it.
//this.update_request_data_with_form(paymentDataRequest);
} catch (error) {
console.error(error);
@ -279,7 +279,7 @@ class ApplepayButton {
return (applePayValidateMerchantEvent) => {
this.log('onvalidatemerchant call');
paypal.Applepay().validateMerchant({
widgetBuilder.paypal.Applepay().validateMerchant({
validationUrl: applePayValidateMerchantEvent.validationURL
})
.then(validateResult => {
@ -506,7 +506,7 @@ class ApplepayButton {
this.log('onpaymentauthorized paypal order ID', id, event.payment.token, event.payment.billingContact);
try {
const confirmOrderResponse = await paypal.Applepay().confirmOrder({
const confirmOrderResponse = await widgetBuilder.paypal.Applepay().confirmOrder({
orderId: id,
token: event.payment.token,
billingContact: event.payment.billingContact,

View file

@ -65,6 +65,13 @@ class BaseHandler {
);
}
errorHandler() {
return new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
}
}
export default BaseHandler;

View file

@ -4,6 +4,7 @@ import CheckoutHandler from "./CheckoutHandler";
import CartBlockHandler from "./CartBlockHandler";
import CheckoutBlockHandler from "./CheckoutBlockHandler";
import MiniCartHandler from "./MiniCartHandler";
import PreviewHandler from "./PreviewHandler";
import PayNowHandler from "./PayNowHandler";
class ContextHandlerFactory {
@ -24,6 +25,8 @@ class ContextHandlerFactory {
return new CartBlockHandler(buttonConfig, ppcpConfig);
case 'checkout-block':
return new CheckoutBlockHandler(buttonConfig, ppcpConfig);
case 'preview':
return new PreviewHandler(buttonConfig, ppcpConfig);
}
}
}

View file

@ -0,0 +1,37 @@
import BaseHandler from "./BaseHandler";
class PreviewHandler extends BaseHandler {
constructor(buttonConfig, ppcpConfig, externalHandler) {
super(buttonConfig, ppcpConfig, externalHandler);
}
transactionInfo() {
// We need to return something as ApplePay button initialization expects valid data.
return {
countryCode: "US",
currencyCode: "USD",
totalPrice: "10.00",
totalPriceStatus: "FINAL"
}
}
createOrder() {
throw new Error('Create order fail. This is just a preview.');
}
approveOrder(data, actions) {
throw new Error('Approve order fail. This is just a preview.');
}
actionHandler() {
throw new Error('Action handler fail. This is just a preview.');
}
errorHandler() {
throw new Error('Error handler fail. This is just a preview.');
}
}
export default PreviewHandler;

View file

@ -48,6 +48,14 @@ class SingleProductHandler extends BaseHandler {
});
}
createOrder() {
return this.actionHandler().configuration().createOrder(null, null, {
'updateCartOptions': {
'keepShipping': true
}
});
}
actionHandler() {
return new SingleProductActionHandler(
this.ppcpConfig,
@ -60,12 +68,9 @@ class SingleProductHandler extends BaseHandler {
);
}
products() {
return this.actionHandler().getProducts();
}
}
export default SingleProductHandler;

View file

@ -0,0 +1,148 @@
import {loadCustomScript} from "@paypal/paypal-js";
import ApplepayButton from "./ApplepayButton";
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
(function ({
buttonConfig,
jQuery
}) {
let applePayConfig;
let buttonQueue = [];
let activeButtons = {};
let bootstrapped = false;
// React to PayPal config changes.
jQuery(document).on('ppcp_paypal_render_preview', (ev, ppcpConfig) => {
if (bootstrapped) {
createButton(ppcpConfig);
} else {
buttonQueue.push({
ppcpConfig: JSON.parse(JSON.stringify(ppcpConfig))
});
}
});
// React to ApplePay config changes.
jQuery([
'#ppcp-applepay_button_enabled',
'#ppcp-applepay_button_type',
'#ppcp-applepay_button_color',
'#ppcp-applepay_button_language'
].join(',')).on('change', () => {
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
createButton(ppcpConfig);
}
});
// Maybe we can find a more elegant reload method when transitioning from styling modes.
jQuery([
'#ppcp-smart_button_enable_styling_per_location'
].join(',')).on('change', () => {
setTimeout(() => {
for (const [selector, ppcpConfig] of Object.entries(activeButtons)) {
createButton(ppcpConfig);
}
}, 100);
});
const applyConfigOptions = function (buttonConfig) {
buttonConfig.button = buttonConfig.button || {};
buttonConfig.button.type = jQuery('#ppcp-applepay_button_type').val();
buttonConfig.button.color = jQuery('#ppcp-applepay_button_color').val();
buttonConfig.button.lang = jQuery('#ppcp-applepay_button_language').val();
}
const createButton = function (ppcpConfig) {
const selector = ppcpConfig.button.wrapper + 'ApplePay';
if (!jQuery('#ppcp-applepay_button_enabled').is(':checked')) {
jQuery(selector).remove();
return;
}
buttonConfig = JSON.parse(JSON.stringify(buttonConfig));
buttonConfig.button.wrapper = selector.replace('#', '');
applyConfigOptions(buttonConfig);
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-applepay"></div>`;
if (!jQuery(selector).length) {
jQuery(ppcpConfig.button.wrapper).after(wrapperElement);
} else {
jQuery(selector).replaceWith(wrapperElement);
}
const button = new ApplepayButton(
'preview',
null,
buttonConfig,
ppcpConfig,
);
button.init(applePayConfig);
activeButtons[selector] = ppcpConfig;
}
const bootstrap = async function () {
if (!widgetBuilder.paypal) {
return;
}
applePayConfig = await widgetBuilder.paypal.Applepay().config();
// We need to set bootstrapped here otherwise applePayConfig may not be set.
bootstrapped = true;
let options;
while (options = buttonQueue.pop()) {
createButton(options.ppcpConfig);
}
if (!window.ApplePaySession) {
jQuery('body').addClass('ppcp-non-ios-device')
}
};
document.addEventListener(
'DOMContentLoaded',
() => {
if (typeof (buttonConfig) === 'undefined') {
console.error('PayPal button could not be configured.');
return;
}
let paypalLoaded = false;
let applePayLoaded = false;
const tryToBoot = () => {
if (!bootstrapped && paypalLoaded && applePayLoaded) {
bootstrap();
}
}
// Load ApplePay SDK
loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
applePayLoaded = true;
tryToBoot();
});
// Wait for PayPal to be loaded externally
if (typeof widgetBuilder.paypal !== 'undefined') {
paypalLoaded = true;
tryToBoot();
}
jQuery(document).on('ppcp-paypal-loaded', () => {
paypalLoaded = true;
tryToBoot();
});
},
);
})({
buttonConfig: window.wc_ppcp_applepay_admin,
jQuery: window.jQuery
});

View file

@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
use WooCommerce\PayPalCommerce\Applepay\Assets\DataToAppleButtonScripts;
use WooCommerce\PayPalCommerce\Applepay\Assets\BlocksPaymentMethod;
use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies;
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
@ -56,11 +57,16 @@ return array(
$container->get( 'wcgateway.is-ppcp-settings-page' ),
$container->get( 'applepay.available' ) || ( ! $container->get( 'applepay.is_referral' ) ),
$container->get( 'applepay.server_supported' ),
$settings->has( 'applepay_validated' ) ? $settings->get( 'applepay_validated' ) === true : false,
$container->get( 'applepay.is_validated' ),
$container->get( 'applepay.button' )
);
},
'applepay.is_validated' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'applepay_validated' ) ? $settings->get( 'applepay_validated' ) === true : false;
},
'applepay.apple-product-status' => static function( ContainerInterface $container ): AppleProductStatus {
return new AppleProductStatus(
$container->get( 'wcgateway.settings' ),
@ -83,6 +89,18 @@ return array(
'applepay.server_supported' => static function ( ContainerInterface $container ): bool {
return ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off';
},
'applepay.is_browser_supported' => static function ( ContainerInterface $container ): bool {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' );
if ( $user_agent ) {
foreach ( PropertiesDictionary::ALLOWED_USER_AGENTS as $allowed_agent ) {
if ( strpos( $user_agent, $allowed_agent ) !== false ) {
return true;
}
}
}
return false;
},
'applepay.url' => static function ( ContainerInterface $container ): string {
$path = realpath( __FILE__ );
if ( false === $path ) {

View file

@ -90,6 +90,8 @@ class ApplepayModule implements ModuleInterface {
$module->render_buttons( $c, $apple_payment_method );
$apple_payment_method->bootstrap_ajax_request();
}
$module->load_admin_assets( $c, $apple_payment_method );
},
1
);
@ -182,6 +184,43 @@ class ApplepayModule implements ModuleInterface {
);
}
/**
* Registers and enqueues the assets.
*
* @param ContainerInterface $c The container.
* @param ApplePayButton $button The button.
* @return void
*/
public function load_admin_assets( ContainerInterface $c, ApplePayButton $button ): void {
// Enqueue backend scripts.
add_action(
'admin_enqueue_scripts',
static function () use ( $c, $button ) {
if ( ! is_admin() ) {
return;
}
/**
* Should add this to the ButtonInterface.
*
* @psalm-suppress UndefinedInterfaceMethod
*/
$button->enqueue_admin();
}
);
// Adds ApplePay component to the backend button preview settings.
add_action(
'woocommerce_paypal_payments_admin_gateway_settings',
function( array $settings ) use ( $c ): array {
if ( is_array( $settings['components'] ) ) {
$settings['components'][] = 'applepay';
}
return $settings;
}
);
}
/**
* Renders the Apple Pay buttons in the enabled places.
*

View file

@ -942,7 +942,7 @@ class ApplePayButton implements ButtonInterface {
}
if ( $button_enabled_cart ) {
$default_hook_name = 'woocommerce_paypal_payments_cart_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_googlepay_cart_button_render_hook', $default_hook_name );
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_cart_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
@ -954,7 +954,7 @@ class ApplePayButton implements ButtonInterface {
if ( $button_enabled_checkout ) {
$default_hook_name = 'woocommerce_paypal_payments_checkout_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_googlepay_checkout_button_render_hook', $default_hook_name );
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_checkout_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
@ -966,7 +966,7 @@ class ApplePayButton implements ButtonInterface {
}
if ( $button_enabled_payorder ) {
$default_hook_name = 'woocommerce_paypal_payments_payorder_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_googlepay_payorder_button_render_hook', $default_hook_name );
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_payorder_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
@ -979,7 +979,7 @@ class ApplePayButton implements ButtonInterface {
if ( $button_enabled_minicart ) {
$default_hook_name = 'woocommerce_paypal_payments_minicart_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_googlepay_minicart_button_render_hook', $default_hook_name );
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_minicart_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
@ -1059,6 +1059,34 @@ class ApplePayButton implements ButtonInterface {
wp_enqueue_style( 'wc-ppcp-applepay' );
}
/**
* Enqueues scripts/styles for admin.
*/
public function enqueue_admin(): void {
wp_register_style(
'wc-ppcp-applepay-admin',
untrailingslashit( $this->module_url ) . '/assets/css/styles.css',
array(),
$this->version
);
wp_enqueue_style( 'wc-ppcp-applepay-admin' );
wp_register_script(
'wc-ppcp-applepay-admin',
untrailingslashit( $this->module_url ) . '/assets/js/boot-admin.js',
array(),
$this->version,
true
);
wp_enqueue_script( 'wc-ppcp-applepay-admin' );
wp_localize_script(
'wc-ppcp-applepay-admin',
'wc_ppcp_applepay_admin',
$this->script_data_for_admin()
);
}
/**
* Returns the script data.
*
@ -1068,6 +1096,15 @@ class ApplePayButton implements ButtonInterface {
return $this->script_data->apple_pay_script_data();
}
/**
* Returns the admin script data.
*
* @return array
*/
public function script_data_for_admin(): array {
return $this->script_data->apple_pay_script_data_for_admin();
}
/**
* Returns true if the module is enabled.
*

View file

@ -69,6 +69,26 @@ class DataToAppleButtonScripts {
);
}
/**
* Returns the appropriate admin data to send to ApplePay script
*
* @return array
* @throws NotFoundException When the setting is not found.
*/
public function apple_pay_script_data_for_admin(): array {
$base_location = wc_get_base_location();
$shop_country_code = $base_location['country'];
$currency_code = get_woocommerce_currency();
$total_label = get_bloginfo( 'name' );
return $this->data_for_admin_page(
$shop_country_code,
$currency_code,
$total_label
);
}
/**
* Check if the product needs shipping
*
@ -199,4 +219,47 @@ class DataToAppleButtonScripts {
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
);
}
/**
* Prepares the data for the cart page.
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page() methods.
*
* @param string $shop_country_code The shop country code.
* @param string $currency_code The currency code.
* @param string $total_label The label for the total amount.
*
* @return array
*/
protected function data_for_admin_page(
$shop_country_code,
$currency_code,
$total_label
) {
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
return array(
'sdk_url' => $this->sdk_url,
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
'button' => array(
'wrapper' => 'applepay-container',
'mini_cart_wrapper' => 'applepay-container-minicart',
'type' => $type,
'color' => $color,
'lang' => $lang,
),
'product' => array(
'needShipping' => false,
'subtotal' => 0,
),
'shop' => array(
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
);
}
}

View file

@ -13,6 +13,7 @@ namespace WooCommerce\PayPalCommerce\Applepay\Assets;
* Class PropertiesDictionary
*/
class PropertiesDictionary {
public const ALLOWED_USER_AGENTS = array( 'Safari', 'Macintosh', 'iPhone', 'iPad', 'iPod' );
public const BILLING_CONTACT_INVALID = 'billing Contact Invalid';

View file

@ -11,6 +11,7 @@ module.exports = {
entry: {
'boot': path.resolve('./resources/js/boot.js'),
'boot-block': path.resolve('./resources/js/boot-block.js'),
'boot-admin': path.resolve('./resources/js/boot-admin.js'),
"styles": path.resolve('./resources/css/styles.scss')
},
output: {

View file

@ -143,7 +143,7 @@ class SingleProductActionHandler {
{
this.cartHelper = null;
return (data, actions) => {
return (data, actions, options = {}) => {
this.errorHandler.clear();
const onResolve = (purchase_units) => {
@ -178,7 +178,7 @@ class SingleProductActionHandler {
});
};
return this.updateCart.update(onResolve, this.getProducts());
return this.updateCart.update(onResolve, this.getProducts(), options.updateCartOptions || {});
};
}

View file

@ -11,9 +11,10 @@ class UpdateCart {
*
* @param onResolve
* @param {Product[]} products
* @param {Object} options
* @returns {Promise<unknown>}
*/
update(onResolve, products)
update(onResolve, products, options = {})
{
return new Promise((resolve, reject) => {
fetch(
@ -27,6 +28,7 @@ class UpdateCart {
body: JSON.stringify({
nonce: this.nonce,
products,
...options
})
}
).then(

View file

@ -71,6 +71,8 @@ class ChangeCartEndpoint extends AbstractCartEndpoint {
* @throws Exception On error.
*/
protected function handle_data(): bool {
$data = $this->request_data->read_request( $this->nonce() );
$this->cart_products->set_cart( $this->cart );
$products = $this->products_from_request();
@ -79,7 +81,9 @@ class ChangeCartEndpoint extends AbstractCartEndpoint {
return false;
}
$this->shipping->reset_shipping();
if ( ! ( $data['keepShipping'] ?? false ) ) {
$this->shipping->reset_shipping();
}
if ( ! $this->add_products( $products ) ) {
return false;

View file

@ -107,8 +107,8 @@ class CompatAssets {
'ppcp-tracking-compat',
'PayPalCommerceGatewayOrderTrackingCompat',
array(
'gzd_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) && $this->is_gzd_active,
'wc_shipment_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) && $this->is_wc_shipment_active,
'gzd_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) && $this->is_gzd_active,
'wc_shipment_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) && $this->is_wc_shipment_active,
'wc_shipping_tax_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_wc_shipping_tax', true ) && $this->is_wc_shipping_tax_active,
)
);

View file

@ -44,3 +44,9 @@
}
}
.wp-admin {
.ppcp-button-googlepay {
pointer-events: none;
}
}

View file

@ -1,6 +1,6 @@
import BaseHandler from "./BaseHandler";
class CartHandler extends BaseHandler {
class PreviewHandler extends BaseHandler {
constructor(buttonConfig, ppcpConfig, externalHandler) {
super(buttonConfig, ppcpConfig, externalHandler);
@ -28,4 +28,4 @@ class CartHandler extends BaseHandler {
}
export default CartHandler;
export default PreviewHandler;

View file

@ -48,6 +48,14 @@ class SingleProductHandler extends BaseHandler {
});
}
createOrder() {
return this.actionHandler().configuration().createOrder(null, null, {
'updateCartOptions': {
'keepShipping': true
}
});
}
actionHandler() {
return new SingleProductActionHandler(
this.ppcpConfig,

View file

@ -92,6 +92,10 @@ ul.ppcp-onboarding-options-sublist {
margin: 5px;
}
.ppcp-onboarding-header-apm-logos img {
margin: 3px;
}
.ppcp-onboarding-header-cards img {
height: 37px;
}
@ -99,6 +103,9 @@ ul.ppcp-onboarding-options-sublist {
.ppcp-onboarding-header-paypal-logos img {
height: 32px;
}
.ppcp-onboarding-header-apm-logos img {
height: 28px;
}
.ppcp-onboarding-cards-options table {
margin-left: 35px;
@ -158,7 +165,11 @@ ul.ppcp-onboarding-options-sublist {
}
.ppcp-onboarding-header-cards img, .ppcp-onboarding-header-paypal-logos img {
margin: 2px;
margin: 2.5px;
}
.ppcp-onboarding-header-apm-logos img {
margin: 3px;
}
.ppcp-onboarding-header-cards img {
@ -168,6 +179,9 @@ ul.ppcp-onboarding-options-sublist {
.ppcp-onboarding-header-paypal-logos img {
height: 23px;
}
.ppcp-onboarding-header-apm-logos img {
height: 19.8px;
}
}
.ppcp-settings-page-header {

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -55,17 +55,21 @@ return function ( ContainerInterface $container, array $fields ): array {
</div>
<div class="ppcp-onboarding-header-right">
<div class="ppcp-onboarding-header-paypal-logos">
<img alt="PayPal" src="' . esc_url( $module_url ) . 'assets/images/paypal-button.svg"/>
<img alt="Venmo" src="' . esc_url( $module_url ) . 'assets/images/venmo.svg"/>
<img alt="Pay Later" src="' . esc_url( $module_url ) . 'assets/images/paylater.svg"/>
<a href="https://woo.com/document/woocommerce-paypal-payments/#standard-paypal-payments" target="_blank"><img alt="PayPal" src="' . esc_url( $module_url ) . 'assets/images/paypal-button.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#pay-with-venmo" target="_blank"><img alt="Venmo" src="' . esc_url( $module_url ) . 'assets/images/venmo.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#pay-later" target="_blank"><img alt="Pay Later" src="' . esc_url( $module_url ) . 'assets/images/paylater.svg"/></a>
</div>
<div class="ppcp-onboarding-header-cards">
<img alt="Visa" src="' . esc_url( $module_url ) . 'assets/images/visa-dark.svg"/>
<img alt="Mastercard" src="' . esc_url( $module_url ) . 'assets/images/mastercard-dark.svg"/>
<img alt="American Express" src="' . esc_url( $module_url ) . 'assets/images/amex.svg"/>
<img alt="Discover" src="' . esc_url( $module_url ) . 'assets/images/discover.svg"/>
<img alt="iDEAL" src="' . esc_url( $module_url ) . 'assets/images/ideal-dark.svg"/>
<img alt="Sofort" src="' . esc_url( $module_url ) . 'assets/images/sofort.svg"/>
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="Visa" src="' . esc_url( $module_url ) . 'assets/images/visa-dark.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="Mastercard" src="' . esc_url( $module_url ) . 'assets/images/mastercard-dark.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="American Express" src="' . esc_url( $module_url ) . 'assets/images/amex.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="Discover" src="' . esc_url( $module_url ) . 'assets/images/discover.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#alternative-payment-methods" target="_blank"><img alt="iDEAL" src="' . esc_url( $module_url ) . 'assets/images/ideal-dark.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#alternative-payment-methods" target="_blank"><img alt="Sofort" src="' . esc_url( $module_url ) . 'assets/images/sofort.svg"/></a>
</div>
<div class="ppcp-onboarding-header-apm-logos">
<a href="https://woo.com/document/woocommerce-paypal-payments/#apple-pay" target="_blank"><img alt="Apple Pay" src="' . esc_url( $module_url ) . 'assets/images/button-Apple-Pay.png"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#google-pay" target="_blank"><img alt="Google Pay" src="' . esc_url( $module_url ) . 'assets/images/button-Google-Pay.png"/></a>
</div>
</div>
</div>',

View file

@ -1,6 +1,6 @@
{
"name": "woocommerce-paypal-payments",
"version": "2.4.0",
"version": "2.4.1",
"description": "WooCommerce PayPal Payments",
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
"license": "GPL-2.0",

View file

@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple
Requires at least: 5.3
Tested up to: 6.3
Requires PHP: 7.2
Stable tag: 2.4.0
Stable tag: 2.4.1
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -180,6 +180,26 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
== Changelog ==
= 2.4.1 - xxxx-xx-xx =
* 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 - Pay Later messaging disappears when updating shipping option on cart page #1807
* Fix - Apple Pay payment from single product may fail after changing shipping options in Apple Pay payment sheet #1810
* Enhancement - Extend list of supported countries for Advanced Card Processing #1808
* Enhancement - Extend Apple Pay/Google Pay country eligibility to Italy #1811
* Enhancement - Override language used to display PayPal buttons #600
* Enhancement - Apple Pay button preview #1824
* Enhancement - Add Apple Pay & Google Pay logos on the onboarding page #1823
* Enhancement - Improve Apple Pay compatibility with variable products on single product page #1803
* Enhancement - Apple Pay domain registration & browser eligibility check #1821
* Enhancement - Package Tracking compatibility with WooCommerce Shipping & ShipStation for WooCommerce #1813
* Enhancement - Fill form when continuation in block #1794
* Enhancement - Display Shop location Pay Later messaging on product category pages #1809
* Enhancement - Present apple-developer-merchantid-domain-association file only when Apple Pay is enabled #1818
* Enhancement - Improve Apple Pay compatibility on Pay for Order page #1815
* Enhancement - Display Pay Later messages before the payment methods on the Pay for Order page #1814
* Enhancement - Handle undefined array key warnings on PHP 8.1 #1804
= 2.4.0 - 2023-10-31 =
* Fix - Mini-Cart Bug cause of wrong DOM-Structure in v2.3.1 #1735
* Fix - ACDC disappearing after plugin updates #1751

View file

@ -80,6 +80,7 @@ class ChangeCartEndpointTest extends TestCase
$requestData = Mockery::mock(RequestData::class);
$requestData
->expects('read_request')
->times(2)
->with(ChangeCartEndpoint::nonce())
->andReturn($data);

View file

@ -3,7 +3,7 @@
* Plugin Name: WooCommerce PayPal Payments
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
* Version: 2.4.0
* Version: 2.4.1
* Author: WooCommerce
* Author URI: https://woocommerce.com/
* License: GPL-2.0
@ -23,7 +23,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' );
define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' );
define( 'PAYPAL_INTEGRATION_DATE', '2023-10-25' );
define( 'PAYPAL_INTEGRATION_DATE', '2023-11-06' );
! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' );
! defined( 'CONNECT_WOO_SANDBOX_CLIENT_ID' ) && define( 'CONNECT_WOO_SANDBOX_CLIENT_ID', 'AYmOHbt1VHg-OZ_oihPdzKEVbU3qg0qXonBcAztuzniQRaKE0w1Hr762cSFwd4n8wxOl-TCWohEa0XM_' );