+
data_for_product_page(
- $shop_country_code,
- $currency_code,
- $total_label
- );
+ return $this->data_for_product_page();
}
- return $this->data_for_cart_page(
- $shop_country_code,
- $currency_code,
- $total_label
- );
+ return $this->data_for_cart_page();
}
/**
* 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 {
+ return $this->data_for_admin_page();
+ }
+
+ /**
+ * Returns the full config array for the Apple Pay integration with default values.
+ *
+ * @param array $product - Optional. Product details for the payment button.
+ *
+ * @return array
+ */
+ private function get_apple_pay_data( array $product = [] ) : array {
+ // true: Use Apple Pay as distinct gateway.
+ // false: integrate it with the smart buttons.
+ $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
+ $is_wc_gateway_enabled = isset( $available_gateways[ ApplePayGateway::ID ] );
+
+ // use_wc: Use WC checkout data
+ // use_applepay: Use data provided by Apple Pay.
+ $checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' )
+ ? $this->settings->get( 'applepay_checkout_data_mode' )
+ : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
+
+ // Store country, currency and name.
$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
+ // Button layout (label, color, language).
+ $type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
+ $color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
+ $lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
+ $lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
+ $is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
+
+ return array(
+ 'sdk_url' => $this->sdk_url,
+ 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
+ 'is_admin' => false,
+ 'is_enabled' => $is_enabled,
+ 'is_wc_gateway_enabled' => $is_wc_gateway_enabled,
+ 'preferences' => array(
+ 'checkout_data_mode' => $checkout_data_mode,
+ ),
+ 'button' => array(
+ 'wrapper' => 'ppc-button-applepay-container',
+ 'mini_cart_wrapper' => 'ppc-button-applepay-container-minicart',
+ 'type' => $type,
+ 'color' => $color,
+ 'lang' => $lang,
+ ),
+ 'product' => $product,
+ 'shop' => array(
+ 'countryCode' => $shop_country_code,
+ 'currencyCode' => $currency_code,
+ 'totalLabel' => $total_label,
+ ),
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
);
}
/**
* Check if the product needs shipping
*
- * @param \WC_Product $product The product.
- *
* @return bool
*/
- protected function check_if_need_shipping( $product ) {
+ protected function check_if_need_shipping( WC_Product $product ) : bool {
if (
! wc_shipping_enabled()
|| 0 === wc_get_shipping_method_count(
@@ -104,30 +139,20 @@ class DataToAppleButtonScripts {
) {
return false;
}
- $needs_shipping = false;
if ( $product->needs_shipping() ) {
- $needs_shipping = true;
+ return true;
}
- return $needs_shipping;
+ return false;
}
/**
* Prepares the data for the product page.
*
- * @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
- * @throws NotFoundException When the setting is not found.
*/
- protected function data_for_product_page(
- $shop_country_code,
- $currency_code,
- $total_label
- ) {
+ protected function data_for_product_page() : array {
$product = wc_get_product( get_the_id() );
if ( ! $product ) {
return array();
@@ -136,146 +161,53 @@ class DataToAppleButtonScripts {
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
$is_variation = true;
}
+
$product_need_shipping = $this->check_if_need_shipping( $product );
$product_id = get_the_id();
$product_price = $product->get_price();
$product_stock = $product->get_stock_status();
- $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' ) : '';
- $checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
- return array(
- 'sdk_url' => $this->sdk_url,
- 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
- 'is_admin' => false,
- 'preferences' => array(
- 'checkout_data_mode' => $checkout_data_mode,
- ),
- 'button' => array(
- 'wrapper' => 'applepay-container',
- 'mini_cart_wrapper' => 'applepay-container-minicart',
- 'type' => $type,
- 'color' => $color,
- 'lang' => $lang,
- ),
- 'product' => array(
- 'needShipping' => $product_need_shipping,
- 'id' => $product_id,
- 'price' => $product_price,
- 'isVariation' => $is_variation,
- 'stock' => $product_stock,
- ),
- 'shop' => array(
- 'countryCode' => $shop_country_code,
- 'currencyCode' => $currency_code,
- 'totalLabel' => $total_label,
- ),
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
- );
+ return $this->get_apple_pay_data( array(
+ 'needShipping' => $product_need_shipping,
+ 'id' => $product_id,
+ 'price' => $product_price,
+ 'isVariation' => $is_variation,
+ 'stock' => $product_stock,
+ ) );
}
/**
* Prepares the data for the cart page.
*
- * @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_cart_page(
- $shop_country_code,
- $currency_code,
- $total_label
- ) {
+ protected function data_for_cart_page() : array {
$cart = WC()->cart;
if ( ! $cart ) {
return array();
}
- $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 );
- $checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
-
- return array(
- 'sdk_url' => $this->sdk_url,
- 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
- 'is_admin' => false,
- 'preferences' => array(
- 'checkout_data_mode' => $checkout_data_mode,
- ),
- 'button' => array(
- 'wrapper' => 'applepay-container',
- 'mini_cart_wrapper' => 'applepay-container-minicart',
- 'type' => $type,
- 'color' => $color,
- 'lang' => $lang,
- ),
- 'product' => array(
- 'needShipping' => $cart->needs_shipping(),
- 'subtotal' => $cart->get_subtotal(),
- ),
- 'shop' => array(
- 'countryCode' => $shop_country_code,
- 'currencyCode' => $currency_code,
- 'totalLabel' => $total_label,
- ),
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
- );
+ return $this->get_apple_pay_data( array(
+ 'needShipping' => $cart->needs_shipping(),
+ 'subtotal' => $cart->get_subtotal(),
+ ) );
}
/**
* 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.
+ * Consider refactoring this method along with data_for_cart_page() and data_for_product_page()
+ * methods.
*
* @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 );
- $checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
- $is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
+ protected function data_for_admin_page() : array {
+ $data = $this->get_apple_pay_data( array(
+ 'needShipping' => false,
+ 'subtotal' => 0,
+ ) );
- return array(
- 'sdk_url' => $this->sdk_url,
- 'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
- 'is_admin' => true,
- 'is_enabled' => $is_enabled,
- 'preferences' => array(
- 'checkout_data_mode' => $checkout_data_mode,
- ),
- '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' ),
- );
+ $data['is_admin'] = true;
+
+ return $data;
}
}
diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js
index 848285815..0ec918667 100644
--- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js
+++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js
@@ -1,3 +1,5 @@
+/* global PayPalCommerceGateway */
+
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
import { setVisible, setVisibleByClass } from '../Helper/Hiding';
import {
@@ -181,9 +183,14 @@ class CheckoutBootstap {
const isSeparateButtonGateway = [ PaymentMethods.CARD_BUTTON ].includes(
currentPaymentMethod
);
+ const isApplePayMethod =
+ currentPaymentMethod === PaymentMethods.APPLEPAY;
const isSavedCard = isCard && isSavedCardSelected();
const isNotOurGateway =
- ! isPaypal && ! isCard && ! isSeparateButtonGateway;
+ ! isPaypal &&
+ ! isCard &&
+ ! isSeparateButtonGateway &&
+ ! isApplePayMethod;
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
const hasVaultedPaypal =
PayPalCommerceGateway.vaulted_paypal_email !== '';
@@ -227,6 +234,8 @@ class CheckoutBootstap {
}
}
+ setVisible( '#ppc-button-ppcp-applepay', isApplePayMethod );
+
jQuery( document.body ).trigger( 'ppcp_checkout_rendered' );
}
diff --git a/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js b/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js
index 0ea05f255..6fa3d6c73 100644
--- a/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js
+++ b/modules/ppcp-button/resources/js/modules/Helper/CheckoutMethodState.js
@@ -3,6 +3,7 @@ export const PaymentMethods = {
CARDS: 'ppcp-credit-card-gateway',
OXXO: 'ppcp-oxxo-gateway',
CARD_BUTTON: 'ppcp-card-button-gateway',
+ APPLEPAY: 'ppcp-applepay',
};
export const ORDER_BUTTON_SELECTOR = '#place_order';
diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php
index 25fcb808d..e91256653 100644
--- a/modules/ppcp-wc-gateway/src/Settings/Settings.php
+++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php
@@ -117,19 +117,16 @@ class Settings implements ContainerInterface {
/**
* Stores the settings to the database.
*/
- public function persist() {
-
+ public function persist() : bool {
return update_option( self::KEY, $this->settings );
}
-
/**
* Loads the settings.
*
* @return bool
*/
private function load(): bool {
-
if ( $this->settings ) {
return false;
}
From a473459ddbf377f3f9d01f596365f89a8e4287fd Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 23 Jul 2024 17:36:10 +0200
Subject: [PATCH 03/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor=20code=20clean?=
=?UTF-8?q?up=20and=20documentation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 192 +++++++++++-------
.../resources/js/ApplepayManager.js | 15 +-
.../js/ApplepayManagerBlockEditor.js | 10 +-
.../resources/js/Context/BaseHandler.js | 8 -
.../resources/js/Context/PreviewHandler.js | 6 +-
.../ppcp-applepay/resources/js/boot-admin.js | 4 +-
.../ppcp-applepay/resources/js/boot-block.js | 8 +-
modules/ppcp-applepay/resources/js/boot.js | 4 +-
8 files changed, 142 insertions(+), 105 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index b703a9565..3a1a13a00 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -10,6 +10,23 @@ import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
+/**
+ * List of valid context values that the button can have.
+ *
+ * @type {Object}
+ */
+const CONTEXT = {
+ Product: 'product',
+ Cart: 'cart',
+ Checkout: 'checkout',
+ PayNow: 'pay-now',
+ MiniCart: 'mini-cart',
+ BlockCart: 'cart-block',
+ BlockCheckout: 'checkout-block',
+ Preview: 'preview',
+ Blocks: [ 'cart-block', 'checkout-block' ],
+};
+
/**
* A payment button for Apple Pay.
*
@@ -32,6 +49,20 @@ class ApplePayButton {
*/
context = '';
+ externalHandler = null;
+ buttonConfig = null;
+ ppcpConfig = null;
+ paymentsClient = null;
+ formData = null;
+ contextHandler = null;
+ updatedContactInfo = [];
+ selectedShippingMethod = [];
+
+ /**
+ * Stores initialization data sent to the button.
+ */
+ initialPaymentRequest = null;
+
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
apmButtonsInit( ppcpConfig );
@@ -39,8 +70,6 @@ class ApplePayButton {
this.externalHandler = externalHandler;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
- this.paymentsClient = null;
- this.formData = null;
this.contextHandler = ContextHandlerFactory.create(
this.context,
@@ -48,18 +77,6 @@ class ApplePayButton {
this.ppcpConfig
);
- this.updatedContactInfo = [];
- this.selectedShippingMethod = [];
- this.nonce =
- document.getElementById( 'woocommerce-process-checkout-nonce' )
- ?.value || buttonConfig.nonce;
-
- // Stores initialization data sent to the button.
- this.initialPaymentRequest = null;
-
- // Default eligibility status.
- this.isEligible = true;
-
this.log = function () {
if ( this.buttonConfig.is_debug ) {
//console.log('[ApplePayButton]', ...arguments);
@@ -76,6 +93,39 @@ class ApplePayButton {
document.ppcpApplepayButtons[ this.context ] = this;
}
+ /**
+ * The nonce for ajax requests.
+ *
+ * @return {string} The nonce value
+ */
+ get nonce() {
+ const input = document.getElementById(
+ 'woocommerce-process-checkout-nonce'
+ );
+
+ return input?.value || this.buttonConfig.nonce;
+ }
+
+ /**
+ * Whether the current page qualifies to use the Apple Pay button.
+ *
+ * In admin, the button is always eligible, to display an accurate preview.
+ * On front-end, PayPal's response decides if customers can use Apple Pay.
+ *
+ * @return {boolean} True, if the button can be displayed.
+ */
+ get isEligible() {
+ if ( ! this.isInitialized ) {
+ return true;
+ }
+
+ if ( this.buttonConfig.is_admin ) {
+ return true;
+ }
+
+ return !! ( this.applePayConfig.isEligible && window.ApplePaySession );
+ }
+
init( config ) {
if ( this.isInitialized ) {
return;
@@ -90,9 +140,6 @@ class ApplePayButton {
this.isInitialized = true;
this.applePayConfig = config;
- this.isEligible =
- ( this.applePayConfig.isEligible && window.ApplePaySession ) ||
- this.buttonConfig.is_admin;
const idMinicart = this.buttonConfig.button.mini_cart_wrapper;
const idButton = this.buttonConfig.button.wrapper;
@@ -118,7 +165,7 @@ class ApplePayButton {
this.fetchTransactionInfo().then( () => {
this.addButton();
- if ( this.context === 'mini-cart' ) {
+ if ( CONTEXT.MiniCart === this.context ) {
setupButtonEvents( idMinicart );
} else {
setupButtonEvents( idButton );
@@ -143,23 +190,22 @@ class ApplePayButton {
* Returns configurations relative to this button context.
*/
contextConfig() {
- const config = {
- wrapper: this.buttonConfig.button.wrapper,
- ppcpStyle: this.ppcpConfig.button.style,
- buttonStyle: this.buttonConfig.button.style,
- ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
- };
+ const config = {};
- if ( this.context === 'mini-cart' ) {
+ if ( CONTEXT.MiniCart === this.context ) {
config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
config.buttonStyle = this.buttonConfig.button.mini_cart_style;
config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
+ } else {
+ config.wrapper = this.buttonConfig.button.wrapper;
+ config.ppcpStyle = this.ppcpConfig.button.style;
+ config.buttonStyle = this.buttonConfig.button.style;
+ config.ppcpButtonWrapper = this.ppcpConfig.button.wrapper;
}
- if (
- [ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
- ) {
+ // Block editor configuration.
+ if ( CONTEXT.Blocks.includes( this.context ) ) {
config.ppcpButtonWrapper =
'#express-payment-method-ppcp-gateway-paypal';
}
@@ -203,8 +249,9 @@ class ApplePayButton {
}
/**
- * Starts an ApplePay session.
- * @param paymentRequest
+ * Starts an Apple Pay session.
+ *
+ * @param {Object} paymentRequest The payment request object.
*/
applePaySession( paymentRequest ) {
this.log( 'applePaySession', paymentRequest );
@@ -269,7 +316,7 @@ class ApplePayButton {
window.ppcpFundingSource = 'apple_pay';
// Trigger woocommerce validation if we are in the checkout page.
- if ( this.context === 'checkout' ) {
+ if ( CONTEXT.Checkout === this.context ) {
const checkoutFormSelector = 'form.woocommerce-checkout';
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
@@ -323,13 +370,13 @@ class ApplePayButton {
/**
* If the button should show the shipping fields.
*
- * @return {false|*}
+ * @return {boolean} True, if shipping fields should be captured by ApplePay.
*/
shouldRequireShippingInButton() {
return (
this.contextHandler.shippingAllowed() &&
this.buttonConfig.product.needShipping &&
- ( this.context !== 'checkout' ||
+ ( CONTEXT.Checkout !== this.context ||
this.shouldUpdateButtonWithFormData() )
);
}
@@ -337,10 +384,10 @@ class ApplePayButton {
/**
* If the button should be updated with the form addresses.
*
- * @return {boolean}
+ * @return {boolean} True, when Apple Pay data should be submitted to WooCommerce.
*/
shouldUpdateButtonWithFormData() {
- if ( this.context !== 'checkout' ) {
+ if ( CONTEXT.Checkout !== this.context ) {
return false;
}
return (
@@ -353,7 +400,7 @@ class ApplePayButton {
* Indicates how payment completion should be handled if with the context handler default
* actions. Or with Apple Pay module specific completion.
*
- * @return {boolean}
+ * @return {boolean} True, when the Apple Pay data should be submitted to WooCommerce.
*/
shouldCompletePaymentWithContextHandler() {
// Data already handled, ex: PayNow
@@ -363,7 +410,7 @@ class ApplePayButton {
// Use WC form data mode in Checkout.
return (
- this.context === 'checkout' &&
+ CONTEXT.Checkout === this.context &&
! this.shouldUpdateButtonWithFormData()
);
}
@@ -371,7 +418,7 @@ class ApplePayButton {
/**
* Updates Apple Pay paymentRequest with form data.
*
- * @param paymentRequest
+ * @param {Object} paymentRequest Object to extend with form data.
*/
updateRequestDataWithForm( paymentRequest ) {
if ( ! this.shouldUpdateButtonWithFormData() ) {
@@ -481,11 +528,11 @@ class ApplePayButton {
}
refreshContextData() {
- if ( 'product' === this.context ) {
- // Refresh product data that makes the price change.
+ if ( CONTEXT.Product === this.context ) {
+ // Refresh product data that makes the price change.
this.productQuantity = document.querySelector( 'input.qty' )?.value;
- this.products = this.contextHandler.products();
- this.log( 'Products updated', this.products );
+ this.products = this.contextHandler.products();
+ this.log( 'Products updated', this.products );
}
}
@@ -518,8 +565,9 @@ class ApplePayButton {
*
* @see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778021-onvalidatemerchant
*
- * @param session
- * @return {(function(*): void)|*}
+ * @param {Object} session The ApplePaySession object.
+ *
+ * @return {(function(*): void)|*} Callback that runs after the merchant validation
*/
onValidateMerchant( session ) {
return ( applePayValidateMerchantEvent ) => {
@@ -548,14 +596,14 @@ class ApplePayButton {
onShippingMethodSelected( session ) {
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
- const ajax_url = this.buttonConfig.ajax_url;
+ const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingmethodselected call' );
const data = this.getShippingMethodData( event );
jQuery.ajax( {
- url: ajax_url,
+ url: ajaxUrl,
method: 'POST',
data,
success: (
@@ -599,7 +647,7 @@ class ApplePayButton {
onShippingContactSelected( session ) {
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
- const ajax_url = this.buttonConfig.ajax_url;
+ const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingcontactselected call' );
@@ -607,7 +655,7 @@ class ApplePayButton {
const data = this.getShippingContactData( event );
jQuery.ajax( {
- url: ajax_url,
+ url: ajaxUrl,
method: 'POST',
data,
success: (
@@ -637,15 +685,15 @@ class ApplePayButton {
}
getShippingContactData( event ) {
- const product_id = this.buttonConfig.product.id;
+ const productId = this.buttonConfig.product.id;
this.refreshContextData();
switch ( this.context ) {
- case 'product':
+ case CONTEXT.Product:
return {
action: 'ppcp_update_shipping_contact',
- product_id,
+ product_id: productId,
products: JSON.stringify( this.products ),
caller_page: 'productDetail',
product_quantity: this.productQuantity,
@@ -653,11 +701,12 @@ class ApplePayButton {
need_shipping: this.shouldRequireShippingInButton(),
'woocommerce-process-checkout-nonce': this.nonce,
};
- case 'cart':
- case 'checkout':
- case 'cart-block':
- case 'checkout-block':
- case 'mini-cart':
+
+ case CONTEXT.Cart:
+ case CONTEXT.Checkout:
+ case CONTEXT.BlockCart:
+ case CONTEXT.BlockCheckout:
+ case CONTEXT.MiniCart:
return {
action: 'ppcp_update_shipping_contact',
simplified_contact: event.shippingContact,
@@ -669,12 +718,12 @@ class ApplePayButton {
}
getShippingMethodData( event ) {
- const product_id = this.buttonConfig.product.id;
+ const productId = this.buttonConfig.product.id;
this.refreshContextData();
switch ( this.context ) {
- case 'product':
+ case CONTEXT.Product:
return {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
@@ -682,17 +731,18 @@ class ApplePayButton {
this.updatedContactInfo ||
this.initialPaymentRequest.shippingContact ||
this.initialPaymentRequest.billingContact,
- product_id,
+ product_id: productId,
products: JSON.stringify( this.products ),
caller_page: 'productDetail',
product_quantity: this.productQuantity,
'woocommerce-process-checkout-nonce': this.nonce,
};
- case 'cart':
- case 'checkout':
- case 'cart-block':
- case 'checkout-block':
- case 'mini-cart':
+
+ case CONTEXT.Cart:
+ case CONTEXT.Checkout:
+ case CONTEXT.BlockCart:
+ case CONTEXT.BlockCheckout:
+ case CONTEXT.MiniCart:
return {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
@@ -711,10 +761,6 @@ class ApplePayButton {
return async ( event ) => {
this.log( 'onpaymentauthorized call' );
- function form() {
- return document.querySelector( 'form.cart' );
- }
-
const processInWooAndCapture = async ( data ) => {
return new Promise( ( resolve, reject ) => {
try {
@@ -729,7 +775,7 @@ class ApplePayButton {
( this.initialPaymentRequest.shippingMethods ||
[] )[ 0 ];
- const request_data = {
+ const requestData = {
action: 'ppcp_create_order',
caller_page: this.context,
product_id: this.buttonConfig.product.id ?? null,
@@ -754,7 +800,7 @@ class ApplePayButton {
jQuery.ajax( {
url: this.buttonConfig.ajax_url,
method: 'POST',
- data: request_data,
+ data: requestData,
complete: ( jqXHR, textStatus ) => {
this.log( 'onpaymentauthorized complete' );
},
@@ -829,15 +875,15 @@ class ApplePayButton {
restart: () =>
new Promise(
( resolve, reject ) => {
- approveFailed = true;
- resolve();
+ approveFailed = true;
+ resolve();
}
),
order: {
get: () =>
new Promise(
( resolve, reject ) => {
- resolve( null );
+ resolve( null );
}
),
},
diff --git a/modules/ppcp-applepay/resources/js/ApplepayManager.js b/modules/ppcp-applepay/resources/js/ApplepayManager.js
index 0992d9e7d..264610f28 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayManager.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayManager.js
@@ -1,7 +1,9 @@
-import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
-import ApplepayButton from './ApplepayButton';
+/* global paypal */
-class ApplepayManager {
+import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
+import ApplePayButton from './ApplepayButton';
+
+class ApplePayManager {
constructor( buttonConfig, ppcpConfig ) {
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
@@ -9,7 +11,7 @@ class ApplepayManager {
this.buttons = [];
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
- const button = new ApplepayButton(
+ const button = new ApplePayButton(
bootstrap.context,
bootstrap.handler,
buttonConfig,
@@ -40,8 +42,7 @@ class ApplepayManager {
}
/**
- * Gets ApplePay configuration of the PayPal merchant.
- * @return {Promise}
+ * Gets Apple Pay configuration of the PayPal merchant.
*/
async config() {
this.ApplePayConfig = await paypal.Applepay().config();
@@ -49,4 +50,4 @@ class ApplepayManager {
}
}
-export default ApplepayManager;
+export default ApplePayManager;
diff --git a/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js b/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js
index 2f4db9d41..1c10bb997 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js
@@ -1,6 +1,8 @@
-import ApplepayButton from './ApplepayButton';
+/* global paypal */
-class ApplepayManagerBlockEditor {
+import ApplePayButton from './ApplepayButton';
+
+class ApplePayManagerBlockEditor {
constructor( buttonConfig, ppcpConfig ) {
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
@@ -17,7 +19,7 @@ class ApplepayManagerBlockEditor {
try {
this.applePayConfig = await paypal.Applepay().config();
- const button = new ApplepayButton(
+ const button = new ApplePayButton(
this.ppcpConfig.context,
null,
this.buttonConfig,
@@ -31,4 +33,4 @@ class ApplepayManagerBlockEditor {
}
}
-export default ApplepayManagerBlockEditor;
+export default ApplePayManagerBlockEditor;
diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
index f763ac5d9..cc3a3aeb2 100644
--- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
@@ -1,6 +1,5 @@
import ErrorHandler from '../../../../ppcp-button/resources/js/modules/ErrorHandler';
import CartActionHandler from '../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler';
-import { isPayPalSubscription } from '../../../../ppcp-blocks/resources/js/Helper/Subscription';
class BaseHandler {
constructor( buttonConfig, ppcpConfig ) {
@@ -76,13 +75,6 @@ class BaseHandler {
document.querySelector( '.woocommerce-notices-wrapper' )
);
}
-
- errorHandler() {
- return new ErrorHandler(
- this.ppcpConfig.labels.error.generic,
- document.querySelector( '.woocommerce-notices-wrapper' )
- );
- }
}
export default BaseHandler;
diff --git a/modules/ppcp-applepay/resources/js/Context/PreviewHandler.js b/modules/ppcp-applepay/resources/js/Context/PreviewHandler.js
index 8740705e9..5febfe0c3 100644
--- a/modules/ppcp-applepay/resources/js/Context/PreviewHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/PreviewHandler.js
@@ -1,10 +1,6 @@
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 {
@@ -19,7 +15,7 @@ class PreviewHandler extends BaseHandler {
throw new Error( 'Create order fail. This is just a preview.' );
}
- approveOrder( data, actions ) {
+ approveOrder() {
throw new Error( 'Approve order fail. This is just a preview.' );
}
diff --git a/modules/ppcp-applepay/resources/js/boot-admin.js b/modules/ppcp-applepay/resources/js/boot-admin.js
index 080d7c8aa..81d8c0b7b 100644
--- a/modules/ppcp-applepay/resources/js/boot-admin.js
+++ b/modules/ppcp-applepay/resources/js/boot-admin.js
@@ -1,4 +1,4 @@
-import ApplepayButton from './ApplepayButton';
+import ApplePayButton from './ApplepayButton';
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
@@ -86,7 +86,7 @@ class ApplePayPreviewButton extends PreviewButton {
}
createButton( buttonConfig ) {
- const button = new ApplepayButton(
+ const button = new ApplePayButton(
'preview',
null,
buttonConfig,
diff --git a/modules/ppcp-applepay/resources/js/boot-block.js b/modules/ppcp-applepay/resources/js/boot-block.js
index 8445466eb..c447cb064 100644
--- a/modules/ppcp-applepay/resources/js/boot-block.js
+++ b/modules/ppcp-applepay/resources/js/boot-block.js
@@ -4,8 +4,8 @@ import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Help
import { cartHasSubscriptionProducts } from '../../../ppcp-blocks/resources/js/Helper/Subscription';
import { loadCustomScript } from '@paypal/paypal-js';
import CheckoutHandler from './Context/CheckoutHandler';
-import ApplepayManager from './ApplepayManager';
-import ApplepayManagerBlockEditor from './ApplepayManagerBlockEditor';
+import ApplePayManager from './ApplepayManager';
+import ApplePayManagerBlockEditor from './ApplepayManagerBlockEditor';
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
const ppcpConfig = ppcpData.scriptData;
@@ -24,8 +24,8 @@ const ApplePayComponent = ( props ) => {
const bootstrap = function () {
const ManagerClass = props.isEditing
- ? ApplepayManagerBlockEditor
- : ApplepayManager;
+ ? ApplePayManagerBlockEditor
+ : ApplePayManager;
const manager = new ManagerClass( buttonConfig, ppcpConfig );
manager.init();
};
diff --git a/modules/ppcp-applepay/resources/js/boot.js b/modules/ppcp-applepay/resources/js/boot.js
index aee735231..8eddafbcb 100644
--- a/modules/ppcp-applepay/resources/js/boot.js
+++ b/modules/ppcp-applepay/resources/js/boot.js
@@ -1,13 +1,13 @@
import { loadCustomScript } from '@paypal/paypal-js';
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
-import ApplepayManager from './ApplepayManager';
+import ApplePayManager from './ApplepayManager';
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
let manager;
const bootstrap = function () {
- manager = new ApplepayManager( buttonConfig, ppcpConfig );
+ manager = new ApplePayManager( buttonConfig, ppcpConfig );
manager.init();
};
From f4abf7028e1bfbac98de2ec7e3d6199f563a3829 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 14:20:40 +0200
Subject: [PATCH 04/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Chore:=20Make=20isIn?=
=?UTF-8?q?itialized=20flag=20private?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reason: The button instance is added to the global document scope, this helps to protect internal component state
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 3a1a13a00..d1a56d616 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -40,7 +40,7 @@ class ApplePayButton {
*
* @type {boolean}
*/
- isInitialized = false;
+ #isInitialized = false;
/**
* Context describes the button's location on the website and what details it submits.
@@ -115,7 +115,7 @@ class ApplePayButton {
* @return {boolean} True, if the button can be displayed.
*/
get isEligible() {
- if ( ! this.isInitialized ) {
+ if ( ! this.#isInitialized ) {
return true;
}
@@ -127,7 +127,7 @@ class ApplePayButton {
}
init( config ) {
- if ( this.isInitialized ) {
+ if ( this.#isInitialized ) {
return;
}
@@ -138,7 +138,7 @@ class ApplePayButton {
this.log( 'Init', this.context );
this.initEventHandlers();
- this.isInitialized = true;
+ this.#isInitialized = true;
this.applePayConfig = config;
const idMinicart = this.buttonConfig.button.mini_cart_wrapper;
@@ -178,7 +178,7 @@ class ApplePayButton {
return;
}
- this.isInitialized = false;
+ this.#isInitialized = false;
this.init( this.applePayConfig );
}
From f75c3610d813522c6a2e35608e244f855ee7438f Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 14:21:26 +0200
Subject: [PATCH 05/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Chore:=20Slightly=20?=
=?UTF-8?q?improve=20the=20debug-log=20output?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 28 +++++++++++--------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index d1a56d616..63368e305 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -77,20 +77,24 @@ class ApplePayButton {
this.ppcpConfig
);
- this.log = function () {
- if ( this.buttonConfig.is_debug ) {
- //console.log('[ApplePayButton]', ...arguments);
- }
- };
-
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;
+
+ this.log = function () {
+ if ( ! this.buttonConfig.is_debug ) {
+ return;
+ }
+ console.log( `[ApplePayButton | ${ this.context }]`, ...arguments );
+ };
+
+ if ( this.buttonConfig.is_debug ) {
+ jQuery( document ).on( 'ppcp-applepay-debug', () => {
+ this.log( this );
+ } );
+ }
}
/**
@@ -135,7 +139,7 @@ class ApplePayButton {
return;
}
- this.log( 'Init', this.context );
+ this.log( 'Init' );
this.initEventHandlers();
this.#isInitialized = true;
@@ -274,7 +278,7 @@ class ApplePayButton {
* Adds an Apple Pay purchase button.
*/
addButton() {
- this.log( 'addButton', this.context );
+ this.log( 'addButton' );
const { wrapper, ppcpStyle } = this.contextConfig();
@@ -308,7 +312,7 @@ class ApplePayButton {
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
async onButtonClick() {
- this.log( 'onButtonClick', this.context );
+ this.log( 'onButtonClick' );
const paymentRequest = this.paymentRequest();
From 43d7e0788fe4e5eeb58cd22546644ebb0e8d093c Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 15:19:35 +0200
Subject: [PATCH 06/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Chore:=20Minor=20cod?=
=?UTF-8?q?e=20cleanup?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 1 +
modules/ppcp-applepay/src/ApplePayGateway.php | 13 +++---
.../src/Assets/ApplePayButton.php | 43 ++++++++-----------
3 files changed, 25 insertions(+), 32 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 63368e305..4f09d28d2 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -1,3 +1,4 @@
+/* global jQuery */
/* global ApplePaySession */
/* global PayPalCommerceGateway */
diff --git a/modules/ppcp-applepay/src/ApplePayGateway.php b/modules/ppcp-applepay/src/ApplePayGateway.php
index 327ccae53..8b46dda64 100644
--- a/modules/ppcp-applepay/src/ApplePayGateway.php
+++ b/modules/ppcp-applepay/src/ApplePayGateway.php
@@ -9,17 +9,17 @@ declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Applepay;
+use Exception;
+use WC_Order;
use WC_Payment_Gateway;
+use WooCommerce\PayPalCommerce\Session\SessionHandler;
+use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
-use WooCommerce\PayPalCommerce\Session\SessionHandler;
-use WC_Order;
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
-use Exception;
use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException;
-use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages;
/**
@@ -114,10 +114,7 @@ class ApplePayGateway extends WC_Payment_Gateway {
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
- array(
- $this,
- 'process_admin_options',
- )
+ array( $this, 'process_admin_options' )
);
}
diff --git a/modules/ppcp-applepay/src/Assets/ApplePayButton.php b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
index b710e75ca..d9a9d5393 100644
--- a/modules/ppcp-applepay/src/Assets/ApplePayButton.php
+++ b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
@@ -341,7 +341,7 @@ class ApplePayButton implements ButtonInterface {
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
- } catch ( \Exception $e ) {
+ } catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
@@ -383,7 +383,7 @@ class ApplePayButton implements ButtonInterface {
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
- } catch ( \Exception $e ) {
+ } catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
@@ -400,7 +400,7 @@ class ApplePayButton implements ButtonInterface {
* On error returns an array of errors to be handled by the script
* On success returns the new order data
*
- * @throws \Exception When validation fails.
+ * @throws Exception When validation fails.
*/
public function create_wc_order(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
@@ -421,15 +421,18 @@ class ApplePayButton implements ButtonInterface {
$applepay_request_data_object->order_data( $context );
$this->update_posted_data( $applepay_request_data_object );
+
if ( $context === 'product' ) {
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
$cart = WC()->cart;
$address = $applepay_request_data_object->shipping_address();
+
$this->calculate_totals_single_product(
$cart,
$address,
$applepay_request_data_object->shipping_method()
);
+
if ( ! $cart_item_key ) {
$this->response_templates->response_with_data_errors(
array(
@@ -439,20 +442,17 @@ class ApplePayButton implements ButtonInterface {
),
)
);
- return;
- }
+ } else {
add_filter(
'woocommerce_payment_successful_result',
function ( array $result ) use ( $cart, $cart_item_key ) : array {
- if ( ! is_string( $cart_item_key ) ) {
- return $result;
- }
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
return $result;
}
);
}
+ }
WC()->checkout()->process_checkout();
}
@@ -461,17 +461,20 @@ class ApplePayButton implements ButtonInterface {
/**
* Checks if the nonce in the data object is valid
*
- * @return bool|int
+ * @return bool
*/
protected function is_nonce_valid(): bool {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $nonce ) {
return false;
}
- return wp_verify_nonce(
+
+ // Return value 1 indicates "valid nonce, generated in past 12 hours".
+ // Return value 2 also indicated valid nonce, but older than 12 hours.
+ return 1 === wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
- ) === 1;
+ );
}
/**
@@ -512,7 +515,7 @@ class ApplePayButton implements ButtonInterface {
$address,
$applepay_request_data_object->shipping_method()
);
- if ( is_string( $cart_item_key ) ) {
+ if ( $cart_item_key ) {
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
}
@@ -820,9 +823,9 @@ class ApplePayButton implements ButtonInterface {
/**
* Removes the old cart, saves it, and creates a new one
*
+ * @throws Exception If it cannot be added to cart.
* @param ApplePayDataObjectHttp $applepay_request_data_object The request data object.
- * @return bool | string The cart item key after adding to the new cart.
- * @throws \Exception If it cannot be added to cart.
+ * @return string The cart item key after adding to the new cart.
*/
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
$this->save_old_cart();
@@ -839,7 +842,7 @@ class ApplePayButton implements ButtonInterface {
);
$this->cart_products->add_products( array( $product ) );
- return $this->cart_products->cart_item_keys()[0];
+ return $this->cart_products->cart_item_keys()[0] ?? '';
}
/**
@@ -982,6 +985,7 @@ class ApplePayButton implements ButtonInterface {
return true;
}
+
/**
* ApplePay button markup
*/
@@ -993,15 +997,6 @@ class ApplePayButton implements ButtonInterface {
Date: Wed, 24 Jul 2024 15:21:09 +0200
Subject: [PATCH 07/22] =?UTF-8?q?=E2=9C=A8=20Hide=20Apple=20Pay=20gateway?=
=?UTF-8?q?=20on=20ineligible=20devices?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 24 ++++++++++++--
.../src/Assets/ApplePayButton.php | 32 +++++++++++++------
2 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 4f09d28d2..21b410427 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -150,13 +150,31 @@ class ApplePayButton {
const idButton = this.buttonConfig.button.wrapper;
if ( ! this.isEligible ) {
- jQuery( '#' + idButton ).hide();
- jQuery( '#' + idMinicart ).hide();
- jQuery( '#express-payment-method-ppcp-applepay' ).hide();
+ const hideContainers = [
+ // Payment button (Pay now, smart button block)
+ `#${ idButton }`,
+ // Mini Cart button
+ `#${ idMinicart }`,
+ // Block Checkout: Express checkout button.
+ '#express-payment-method-ppcp-applepay',
+ ];
+
+ hideContainers.forEach( ( selector ) => {
+ const elements = document.querySelectorAll( selector );
+
+ elements.forEach( ( element ) => {
+ element.style.display = 'none';
+ } );
+ } );
return;
}
+ // Classic Checkout: Make the Apple Pay gateway visible.
+ document
+ .querySelectorAll( 'style#ppcp-hide-apple-pay' )
+ .forEach( ( el ) => el.remove() );
+
// Add click-handler to the button.
const setupButtonEvents = ( id ) => {
document
diff --git a/modules/ppcp-applepay/src/Assets/ApplePayButton.php b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
index d9a9d5393..22adce359 100644
--- a/modules/ppcp-applepay/src/Assets/ApplePayButton.php
+++ b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
@@ -443,15 +443,15 @@ class ApplePayButton implements ButtonInterface {
)
);
} else {
- add_filter(
- 'woocommerce_payment_successful_result',
- function ( array $result ) use ( $cart, $cart_item_key ) : array {
- $this->clear_current_cart( $cart, $cart_item_key );
- $this->reload_cart( $cart );
- return $result;
- }
- );
- }
+ add_filter(
+ 'woocommerce_payment_successful_result',
+ function ( array $result ) use ( $cart, $cart_item_key ) : array {
+ $this->clear_current_cart( $cart, $cart_item_key );
+ $this->reload_cart( $cart );
+ return $result;
+ }
+ );
+ }
}
WC()->checkout()->process_checkout();
@@ -953,6 +953,7 @@ class ApplePayButton implements ButtonInterface {
$render_placeholder,
function () {
$this->applepay_button();
+ $this->hide_gateway_until_eligible();
},
21
);
@@ -997,6 +998,19 @@ class ApplePayButton implements ButtonInterface {
+
+
Date: Wed, 24 Jul 2024 18:27:04 +0200
Subject: [PATCH 08/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Entangle=20code=20in?=
=?UTF-8?q?to=20getters=20and=20atomic=20functions?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 290 +++++++++++++-----
1 file changed, 206 insertions(+), 84 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 21b410427..28ab2e990 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -1,4 +1,6 @@
+/* eslint-env browser */
/* global jQuery */
+
/* global ApplePaySession */
/* global PayPalCommerceGateway */
@@ -11,6 +13,25 @@ import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
+/**
+ * Plugin-specific styling.
+ *
+ * Note that most properties of this object do not apply to the Apple Pay button.
+ *
+ * @typedef {Object} PPCPStyle
+ * @property {string} shape - Outline shape.
+ * @property {?number} height - Button height in pixel.
+ */
+
+/**
+ * Style options that are defined by the Apple Pay SDK and are required to render the button.
+ *
+ * @typedef {Object} ApplePayStyle
+ * @property {string} type - Defines the button label.
+ * @property {string} color - Button color
+ * @property {string} lang - The locale; an empty string will apply the user-agent's language.
+ */
+
/**
* List of valid context values that the button can have.
*
@@ -43,6 +64,9 @@ class ApplePayButton {
*/
#isInitialized = false;
+ #wrapperId = '';
+ #ppcpButtonWrapperId = '';
+
/**
* Context describes the button's location on the website and what details it submits.
*
@@ -92,9 +116,9 @@ class ApplePayButton {
};
if ( this.buttonConfig.is_debug ) {
- jQuery( document ).on( 'ppcp-applepay-debug', () => {
+ jQuery( document ).on( 'ppcp-applepay-debug', () => {
this.log( this );
- } );
+ } );
}
}
@@ -131,6 +155,127 @@ class ApplePayButton {
return !! ( this.applePayConfig.isEligible && window.ApplePaySession );
}
+ /**
+ * Returns the wrapper ID for the current button context.
+ * The ID varies for the MiniCart context.
+ *
+ * @return {string} The wrapper-element's ID (without the `#` prefix).
+ */
+ get wrapperId() {
+ if ( ! this.#wrapperId ) {
+ let id;
+
+ if ( CONTEXT.MiniCart === this.context ) {
+ id = this.buttonConfig.button.mini_cart_wrapper;
+ } else {
+ id = this.buttonConfig.button.wrapper;
+ }
+
+ this.#wrapperId = id.replace( /^#/, '' );
+ }
+
+ return this.#wrapperId;
+ }
+
+ /**
+ * Returns the wrapper ID for the ppcpButton
+ *
+ * @return {string} The wrapper-element's ID (without the `#` prefix).
+ */
+ get ppcpButtonWrapperId() {
+ if ( ! this.#ppcpButtonWrapperId ) {
+ let id;
+
+ if ( CONTEXT.MiniCart === this.context ) {
+ id = this.ppcpConfig.button.mini_cart_wrapper;
+ } else if ( CONTEXT.Blocks.includes( this.context ) ) {
+ id = '#express-payment-method-ppcp-gateway-paypal';
+ } else {
+ id = this.ppcpConfig.button.wrapper;
+ }
+
+ this.#ppcpButtonWrapperId = id.replace( /^#/, '' );
+ }
+
+ return this.#ppcpButtonWrapperId;
+ }
+
+ /**
+ * Returns the context-relevant PPCP style object.
+ * The style for the MiniCart context can be different.
+ *
+ * The PPCP style are custom style options, that are provided by this plugin.
+ *
+ * @return {PPCPStyle} The style object.
+ */
+ get ppcpStyle() {
+ if ( CONTEXT.MiniCart === this.context ) {
+ return this.ppcpConfig.button.mini_cart_style;
+ }
+
+ return this.ppcpConfig.button.style;
+ }
+
+ /**
+ * Returns default style options that are propagated to and rendered by the Apple Pay button.
+ *
+ * These styles are the official style options provided by the Apple Pay SDK.
+ *
+ * @return {ApplePayStyle} The style object.
+ */
+ get buttonStyle() {
+ return {
+ type: this.buttonConfig.button.type,
+ lang: this.buttonConfig.button.lang,
+ color: this.buttonConfig.button.color,
+ };
+ }
+
+ /**
+ * Returns the HTML element that wraps the current button
+ *
+ * @return {HTMLElement|null} The wrapper element, or null.
+ */
+ get wrapperElement() {
+ return document.getElementById( this.wrapperId );
+ }
+
+ /**
+ * Returns an array of HTMLElements that belong to the payment button.
+ *
+ * @return {HTMLElement[]} List of payment button wrapper elements.
+ */
+ get allElements() {
+ const selectors = [];
+
+ // Payment button (Pay now, smart button block)
+ selectors.push( `#${ this.wrapperId }` );
+
+ // Block Checkout: Express checkout button.
+ if ( CONTEXT.Blocks.includes( this.context ) ) {
+ selectors.push( '#express-payment-method-ppcp-applepay' );
+ }
+
+ // Classic Checkout: Apple Pay gateway.
+ if ( CONTEXT.Checkout === this.context ) {
+ selectors.push( '.wc_payment_method.payment_method_ppcp-applepay' );
+ }
+
+ this.log( 'Wrapper Elements:', selectors );
+ return /** @type {HTMLElement[]} */ selectors.flatMap( ( selector ) =>
+ Array.from( document.querySelectorAll( selector ) )
+ );
+ }
+
+ /**
+ * Checks whether the main button-wrapper is present in the current DOM.
+ *
+ * @return {boolean} True, if the button context (wrapper element) is found.
+ */
+ get isPresent() {
+ return this.wrapperElement instanceof HTMLElement;
+ }
+
init( config ) {
if ( this.#isInitialized ) {
return;
@@ -146,54 +291,24 @@ class ApplePayButton {
this.#isInitialized = true;
this.applePayConfig = config;
- const idMinicart = this.buttonConfig.button.mini_cart_wrapper;
- const idButton = this.buttonConfig.button.wrapper;
-
if ( ! this.isEligible ) {
- const hideContainers = [
- // Payment button (Pay now, smart button block)
- `#${ idButton }`,
- // Mini Cart button
- `#${ idMinicart }`,
- // Block Checkout: Express checkout button.
- '#express-payment-method-ppcp-applepay',
- ];
+ this.hide();
+ } else {
+ this.show();
- hideContainers.forEach( ( selector ) => {
- const elements = document.querySelectorAll( selector );
+ this.fetchTransactionInfo().then( () => {
+ const button = this.addButton();
- elements.forEach( ( element ) => {
- element.style.display = 'none';
- } );
- } );
+ if ( ! button ) {
+ return;
+ }
- return;
- }
-
- // Classic Checkout: Make the Apple Pay gateway visible.
- document
- .querySelectorAll( 'style#ppcp-hide-apple-pay' )
- .forEach( ( el ) => el.remove() );
-
- // Add click-handler to the button.
- const setupButtonEvents = ( id ) => {
- document
- .getElementById( id )
- ?.addEventListener( 'click', ( evt ) => {
+ button.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();
} );
- };
-
- this.fetchTransactionInfo().then( () => {
- this.addButton();
-
- if ( CONTEXT.MiniCart === this.context ) {
- setupButtonEvents( idMinicart );
- } else {
- setupButtonEvents( idButton );
- }
- } );
+ } );
+ }
}
reinit() {
@@ -205,40 +320,43 @@ class ApplePayButton {
this.init( this.applePayConfig );
}
+ /**
+ * Hides all wrappers that belong to this ApplePayButton instance.
+ */
+ hide() {
+ this.allElements.forEach( ( element ) => {
+ element.style.display = 'none';
+ } );
+ }
+
+ /**
+ * Ensures all wrapper elements of this ApplePayButton instance are visible.
+ */
+ show() {
+ if ( ! this.isPresent ) {
+ this.log( 'Cannot show button, wrapper is not present' );
+ return;
+ }
+
+ // Classic Checkout: Make the Apple Pay gateway visible after page load.
+ if ( CONTEXT.Checkout === this.context ) {
+ document
+ .querySelectorAll( 'style#ppcp-hide-apple-pay' )
+ .forEach( ( el ) => el.remove() );
+ }
+
+ this.allElements.forEach( ( element ) => {
+ element.style.display = '';
+ } );
+ }
+
async fetchTransactionInfo() {
this.transactionInfo = await this.contextHandler.transactionInfo();
}
- /**
- * Returns configurations relative to this button context.
- */
- contextConfig() {
- const config = {};
-
- if ( CONTEXT.MiniCart === this.context ) {
- config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
- config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
- config.buttonStyle = this.buttonConfig.button.mini_cart_style;
- config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
- } else {
- config.wrapper = this.buttonConfig.button.wrapper;
- config.ppcpStyle = this.ppcpConfig.button.style;
- config.buttonStyle = this.buttonConfig.button.style;
- config.ppcpButtonWrapper = this.ppcpConfig.button.wrapper;
- }
-
- // Block editor configuration.
- if ( CONTEXT.Blocks.includes( this.context ) ) {
- config.ppcpButtonWrapper =
- '#express-payment-method-ppcp-gateway-paypal';
- }
-
- return config;
- }
-
initEventHandlers() {
- const { wrapper, ppcpButtonWrapper } = this.contextConfig();
- const wrapperId = '#' + wrapper;
+ const ppcpButtonWrapper = `#${ this.ppcpButtonWrapperId }`;
+ const wrapperId = `#${ this.wrapperId }`;
if ( wrapperId === ppcpButtonWrapper ) {
throw new Error(
@@ -295,23 +413,23 @@ class ApplePayButton {
/**
* Adds an Apple Pay purchase button.
+ *
+ * @return {HTMLElement|null} The newly created `` element. Null on failure.
*/
addButton() {
this.log( 'addButton' );
- const { wrapper, ppcpStyle } = this.contextConfig();
-
- const appleContainer = document.getElementById( wrapper );
- const type = this.buttonConfig.button.type;
- const language = this.buttonConfig.button.lang;
- const color = this.buttonConfig.button.color;
- const id = 'apple-' + wrapper;
+ const appleContainer = document.getElementById( this.wrapperId );
+ const style = this.buttonStyle;
+ const id = 'apple-' + this.wrapperId;
if ( ! appleContainer ) {
- return;
+ return null;
}
- appleContainer.innerHTML = ``;
+ const ppcpStyle = this.ppcpStyle;
+
+ appleContainer.innerHTML = ``;
appleContainer.classList.add( 'ppcp-button-' + ppcpStyle.shape );
if ( ppcpStyle.height ) {
@@ -321,6 +439,8 @@ class ApplePayButton {
);
appleContainer.style.height = `${ ppcpStyle.height }px`;
}
+
+ return appleContainer.querySelector( 'apple-pay-button' );
}
//------------------------
@@ -345,6 +465,7 @@ class ApplePayButton {
PayPalCommerceGateway.labels.error.generic,
document.querySelector( '.woocommerce-notices-wrapper' )
);
+
try {
const formData = new FormData(
document.querySelector( checkoutFormSelector )
@@ -366,6 +487,7 @@ class ApplePayButton {
PayPalCommerceGateway.ajax.validate_checkout.nonce
)
: null;
+
if ( formValidator ) {
try {
const errors = await formValidator.validate(
@@ -898,15 +1020,15 @@ class ApplePayButton {
restart: () =>
new Promise(
( resolve, reject ) => {
- approveFailed = true;
- resolve();
+ approveFailed = true;
+ resolve();
}
),
order: {
get: () =>
new Promise(
( resolve, reject ) => {
- resolve( null );
+ resolve( null );
}
),
},
From 4a0e410f52487afb31d76816e1a96af2565a153d Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 18:55:22 +0200
Subject: [PATCH 09/22] =?UTF-8?q?=F0=9F=94=A7=20Add=20missing=20config=20t?=
=?UTF-8?q?o=20eslintrc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- env.browser: Linter recognizes browser elements, like `HTMLElement`
- globals.jQuery: Library is present on all pages
---
.eslintrc | 6 +++++-
modules/ppcp-applepay/resources/js/ApplepayButton.js | 3 ---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index 7a97d815d..947e3acbe 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,7 +1,11 @@
{
"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
+ "env": {
+ "browser": true
+ },
"globals": {
- "wc": true
+ "wc": true,
+ "jQuery": "readonly"
},
"rules": {
"no-console": ["error", { "allow": ["warn", "error"] }]
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 28ab2e990..3c9f0acf7 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -1,6 +1,3 @@
-/* eslint-env browser */
-/* global jQuery */
-
/* global ApplePaySession */
/* global PayPalCommerceGateway */
From 5df93b2dd48d5f90c6e7d61bb778eda4182ec359 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 19:42:37 +0200
Subject: [PATCH 10/22] =?UTF-8?q?=F0=9F=92=AC=20Improve=20the=20gateway=20?=
=?UTF-8?q?description=20for=20admins?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-applepay/src/ApplePayGateway.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/ppcp-applepay/src/ApplePayGateway.php b/modules/ppcp-applepay/src/ApplePayGateway.php
index 8b46dda64..1cccda0f2 100644
--- a/modules/ppcp-applepay/src/ApplePayGateway.php
+++ b/modules/ppcp-applepay/src/ApplePayGateway.php
@@ -96,7 +96,7 @@ class ApplePayGateway extends WC_Payment_Gateway {
$this->id = self::ID;
$this->method_title = __( 'Apple Pay (via PayPal) ', 'woocommerce-paypal-payments' );
- $this->method_description = __( 'The separate payment gateway with the Apple Pay button. If disabled, the button is included in the PayPal gateway.', 'woocommerce-paypal-payments' );
+ $this->method_description = __( 'Display Apple Pay as a standalone payment option instead of bundling it with PayPal.', 'woocommerce-paypal-payments' );
$this->title = $this->get_option( 'title', __( 'Apple Pay', 'woocommerce-paypal-payments' ) );
$this->description = $this->get_option( 'description', '' );
From f1c5b70877f79880c17356ecaa6f3a81a293a599 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:02:27 +0200
Subject: [PATCH 11/22] =?UTF-8?q?=E2=9C=A8=20Extract=20payment=20button=20?=
=?UTF-8?q?to=20own=20gateway?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 3c9f0acf7..024782a1d 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -43,7 +43,10 @@ const CONTEXT = {
BlockCart: 'cart-block',
BlockCheckout: 'checkout-block',
Preview: 'preview',
+ // Block editor contexts.
Blocks: [ 'cart-block', 'checkout-block' ],
+ // Custom gateway contexts.
+ Gateways: [ 'checkout', 'pay-now' ],
};
/**
@@ -152,6 +155,22 @@ class ApplePayButton {
return !! ( this.applePayConfig.isEligible && window.ApplePaySession );
}
+ /**
+ * Determines if the current payment button should be rendered as a stand-alone gateway.
+ * The return value `false` usually means, that the payment button is bundled with all available
+ * payment buttons.
+ *
+ * The decision depends on the button context (placement) and the plugin settings.
+ *
+ * @return {boolean} True, if the current button represents a stand-alone gateway.
+ */
+ get isSeparateGateway() {
+ return (
+ this.buttonConfig.is_wc_gateway_enabled &&
+ CONTEXT.Gateways.includes( this.context )
+ );
+ }
+
/**
* Returns the wrapper ID for the current button context.
* The ID varies for the MiniCart context.
@@ -164,6 +183,8 @@ class ApplePayButton {
if ( CONTEXT.MiniCart === this.context ) {
id = this.buttonConfig.button.mini_cart_wrapper;
+ } else if ( this.isSeparateGateway ) {
+ id = 'ppc-button-ppcp-applepay';
} else {
id = this.buttonConfig.button.wrapper;
}
From cffaa7a92f7a14462753536cba8889cd58c145bd Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:03:23 +0200
Subject: [PATCH 12/22] =?UTF-8?q?=F0=9F=92=84=20Fix=20button=20size=20in?=
=?UTF-8?q?=20stand-alone=20gateway?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 024782a1d..4d135bce5 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -437,28 +437,32 @@ class ApplePayButton {
addButton() {
this.log( 'addButton' );
- const appleContainer = document.getElementById( this.wrapperId );
+ const wrapper = this.wrapperElement;
const style = this.buttonStyle;
const id = 'apple-' + this.wrapperId;
- if ( ! appleContainer ) {
+ if ( ! wrapper ) {
return null;
}
const ppcpStyle = this.ppcpStyle;
- appleContainer.innerHTML = ``;
- appleContainer.classList.add( 'ppcp-button-' + ppcpStyle.shape );
+ wrapper.innerHTML = ``;
+ wrapper.classList.add(
+ `ppcp-button-${ ppcpStyle.shape }`,
+ 'ppcp-button-apm',
+ 'ppcp-button-applepay'
+ );
if ( ppcpStyle.height ) {
- appleContainer.style.setProperty(
+ wrapper.style.setProperty(
'--apple-pay-button-height',
`${ ppcpStyle.height }px`
);
- appleContainer.style.height = `${ ppcpStyle.height }px`;
+ wrapper.style.height = `${ ppcpStyle.height }px`;
}
- return appleContainer.querySelector( 'apple-pay-button' );
+ return wrapper.querySelector( 'apple-pay-button' );
}
//------------------------
From 354a9ff175ce72105034612af2bee7aa917db618 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:13:14 +0200
Subject: [PATCH 13/22] =?UTF-8?q?=F0=9F=92=A1=20Better=20debug=20logs=20fo?=
=?UTF-8?q?r=20show/hide=20events?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 4d135bce5..68edf2e6a 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -312,6 +312,12 @@ class ApplePayButton {
if ( ! this.isEligible ) {
this.hide();
} else {
+ // Bail if the button wrapper is not present; handles mini-cart logic on checkout page.
+ if ( ! this.isPresent ) {
+ this.log( 'Abort init (no wrapper found)' );
+ return;
+ }
+
this.show();
this.fetchTransactionInfo().then( () => {
@@ -342,6 +348,7 @@ class ApplePayButton {
* Hides all wrappers that belong to this ApplePayButton instance.
*/
hide() {
+ this.log( 'Hide button' );
this.allElements.forEach( ( element ) => {
element.style.display = 'none';
} );
@@ -351,8 +358,9 @@ class ApplePayButton {
* Ensures all wrapper elements of this ApplePayButton instance are visible.
*/
show() {
+ this.log( 'Show button' );
if ( ! this.isPresent ) {
- this.log( 'Cannot show button, wrapper is not present' );
+ this.log( '!! Cannot show button, wrapper is not present' );
return;
}
From a4d3af848c378ae296e2da950e42433227a7f535 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:13:30 +0200
Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=A9=B9=20Hide=20gateway=20on=20PayN?=
=?UTF-8?q?ow=20page=20when=20ineligible?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 12 +++++-------
modules/ppcp-applepay/src/Assets/ApplePayButton.php | 1 +
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 68edf2e6a..227f1533b 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -275,7 +275,7 @@ class ApplePayButton {
}
// Classic Checkout: Apple Pay gateway.
- if ( CONTEXT.Checkout === this.context ) {
+ if ( CONTEXT.Gateways.includes( this.context ) ) {
selectors.push( '.wc_payment_method.payment_method_ppcp-applepay' );
}
@@ -364,12 +364,10 @@ class ApplePayButton {
return;
}
- // Classic Checkout: Make the Apple Pay gateway visible after page load.
- if ( CONTEXT.Checkout === this.context ) {
- document
- .querySelectorAll( 'style#ppcp-hide-apple-pay' )
- .forEach( ( el ) => el.remove() );
- }
+ // Classic Checkout/PayNow: Make the Apple Pay gateway visible after page load.
+ document
+ .querySelectorAll( 'style#ppcp-hide-apple-pay' )
+ .forEach( ( el ) => el.remove() );
this.allElements.forEach( ( element ) => {
element.style.display = '';
diff --git a/modules/ppcp-applepay/src/Assets/ApplePayButton.php b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
index 22adce359..bfcc88133 100644
--- a/modules/ppcp-applepay/src/Assets/ApplePayButton.php
+++ b/modules/ppcp-applepay/src/Assets/ApplePayButton.php
@@ -966,6 +966,7 @@ class ApplePayButton implements ButtonInterface {
$render_placeholder,
function () {
$this->applepay_button();
+ $this->hide_gateway_until_eligible();
},
21
);
From b9b13d7b05718e9cc55ba76618e6b4a8d2f64a8e Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:42:57 +0200
Subject: [PATCH 15/22] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20Remove=20unnecessary?=
=?UTF-8?q?=20styles=20again?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-applepay/resources/css/styles.scss | 5 -----
1 file changed, 5 deletions(-)
diff --git a/modules/ppcp-applepay/resources/css/styles.scss b/modules/ppcp-applepay/resources/css/styles.scss
index 26c43b63e..3818b8db5 100644
--- a/modules/ppcp-applepay/resources/css/styles.scss
+++ b/modules/ppcp-applepay/resources/css/styles.scss
@@ -52,8 +52,3 @@
}
}
}
-
-// Initially hide the APM button until it's explicitly activated.
-#ppc-button-ppcp-applepay {
- display: none;
-}
From 75f4a6f94a154fdc66fbc5ba586c237c92e235ac Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 24 Jul 2024 20:47:59 +0200
Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=8E=A8=20Fully=20disable=20debug=20?=
=?UTF-8?q?logic=20via=20WP=5FDEBUG?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 227f1533b..ce998f04c 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -104,18 +104,20 @@ class ApplePayButton {
this.refreshContextData();
+ this.log = () => {};
+
// Debug helpers
- document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
- document.ppcpApplepayButtons[ this.context ] = this;
-
- this.log = function () {
- if ( ! this.buttonConfig.is_debug ) {
- return;
- }
- console.log( `[ApplePayButton | ${ this.context }]`, ...arguments );
- };
-
if ( this.buttonConfig.is_debug ) {
+ document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
+ document.ppcpApplepayButtons[ this.context ] = this;
+
+ this.log = function () {
+ console.log(
+ `[ApplePayButton | ${ this.context }]`,
+ ...arguments
+ );
+ };
+
jQuery( document ).on( 'ppcp-applepay-debug', () => {
this.log( this );
} );
From 64fae60da44ee071c27c14733acfdbb5f5b262a3 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Thu, 25 Jul 2024 15:48:35 +0200
Subject: [PATCH 17/22] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Undo=20a=20type-hint?=
=?UTF-8?q?=20that=20causes=20CI=20failure?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Our Stub object has a void return value; CI fails with this type-hint, as the Stub is not compatible with the actual object.
---
modules/ppcp-wc-gateway/src/Settings/Settings.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php
index e91256653..e083a91d1 100644
--- a/modules/ppcp-wc-gateway/src/Settings/Settings.php
+++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php
@@ -117,7 +117,7 @@ class Settings implements ContainerInterface {
/**
* Stores the settings to the database.
*/
- public function persist() : bool {
+ public function persist() {
return update_option( self::KEY, $this->settings );
}
From a14276bf528f89fe8ddc7bbf2530b7b365986ee9 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Thu, 25 Jul 2024 16:50:00 +0200
Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Fix=20all=20reported?=
=?UTF-8?q?=20phpcs=20issues?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/Assets/DataToAppleButtonScripts.php | 40 +++++++++++--------
1 file changed, 24 insertions(+), 16 deletions(-)
diff --git a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php
index 004500355..52663a4f0 100644
--- a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php
+++ b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php
@@ -73,7 +73,7 @@ class DataToAppleButtonScripts {
*
* @return array
*/
- private function get_apple_pay_data( array $product = [] ) : array {
+ private function get_apple_pay_data( array $product = array() ) : array {
// true: Use Apple Pay as distinct gateway.
// false: integrate it with the smart buttons.
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
@@ -128,6 +128,8 @@ class DataToAppleButtonScripts {
/**
* Check if the product needs shipping
*
+ * @param WC_Product $product Product to check.
+ *
* @return bool
*/
protected function check_if_need_shipping( WC_Product $product ) : bool {
@@ -167,13 +169,15 @@ class DataToAppleButtonScripts {
$product_price = $product->get_price();
$product_stock = $product->get_stock_status();
- return $this->get_apple_pay_data( array(
- 'needShipping' => $product_need_shipping,
- 'id' => $product_id,
- 'price' => $product_price,
- 'isVariation' => $is_variation,
- 'stock' => $product_stock,
- ) );
+ return $this->get_apple_pay_data(
+ array(
+ 'needShipping' => $product_need_shipping,
+ 'id' => $product_id,
+ 'price' => $product_price,
+ 'isVariation' => $is_variation,
+ 'stock' => $product_stock,
+ )
+ );
}
/**
@@ -187,10 +191,12 @@ class DataToAppleButtonScripts {
return array();
}
- return $this->get_apple_pay_data( array(
- 'needShipping' => $cart->needs_shipping(),
- 'subtotal' => $cart->get_subtotal(),
- ) );
+ return $this->get_apple_pay_data(
+ array(
+ 'needShipping' => $cart->needs_shipping(),
+ 'subtotal' => $cart->get_subtotal(),
+ )
+ );
}
/**
@@ -201,10 +207,12 @@ class DataToAppleButtonScripts {
* @return array
*/
protected function data_for_admin_page() : array {
- $data = $this->get_apple_pay_data( array(
- 'needShipping' => false,
- 'subtotal' => 0,
- ) );
+ $data = $this->get_apple_pay_data(
+ array(
+ 'needShipping' => false,
+ 'subtotal' => 0,
+ )
+ );
$data['is_admin'] = true;
From cbf9bff80836946b28b8f31027668e82f9ec3399 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Fri, 26 Jul 2024 17:06:42 +0200
Subject: [PATCH 19/22] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20with=20undefin?=
=?UTF-8?q?ed=20log()=20method?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 45 ++++++++++++-------
1 file changed, 29 insertions(+), 16 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index ce998f04c..d84870fd9 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -43,8 +43,10 @@ const CONTEXT = {
BlockCart: 'cart-block',
BlockCheckout: 'checkout-block',
Preview: 'preview',
+
// Block editor contexts.
Blocks: [ 'cart-block', 'checkout-block' ],
+
// Custom gateway contexts.
Gateways: [ 'checkout', 'pay-now' ],
};
@@ -89,6 +91,8 @@ class ApplePayButton {
initialPaymentRequest = null;
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
+ this._initDebug( !! buttonConfig?.is_debug );
+
apmButtonsInit( ppcpConfig );
this.context = context;
@@ -103,25 +107,34 @@ class ApplePayButton {
);
this.refreshContextData();
+ }
- this.log = () => {};
+ /**
+ * NOOP log function to avoid errors when debugging is disabled.
+ */
+ log() {}
- // Debug helpers
- if ( this.buttonConfig.is_debug ) {
- document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
- document.ppcpApplepayButtons[ this.context ] = this;
-
- this.log = function () {
- console.log(
- `[ApplePayButton | ${ this.context }]`,
- ...arguments
- );
- };
-
- jQuery( document ).on( 'ppcp-applepay-debug', () => {
- this.log( this );
- } );
+ /**
+ * Enables debugging tools, when the button's is_debug flag is set.
+ *
+ * @param {boolean} enableDebugging If debugging features should be enabled for this instance.
+ * @private
+ */
+ _initDebug( enableDebugging ) {
+ if ( ! enableDebugging || this.#isInitialized ) {
+ return;
}
+
+ document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
+ document.ppcpApplepayButtons[ this.context ] = this;
+
+ this.log = ( ...args ) => {
+ console.log( `[ApplePayButton | ${ this.context }]`, ...args );
+ };
+
+ jQuery( document ).on( 'ppcp-applepay-debug', () => {
+ this.log( this );
+ } );
}
/**
From 09308f15b65183f145b06461db309be3aed2d773 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Fri, 26 Jul 2024 17:20:48 +0200
Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=90=9B=20Hide=20Apple=20Pay=20butto?=
=?UTF-8?q?n=20until=20gateway=20is=20selected?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-applepay/resources/css/styles.scss | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/modules/ppcp-applepay/resources/css/styles.scss b/modules/ppcp-applepay/resources/css/styles.scss
index 3818b8db5..3402b1a20 100644
--- a/modules/ppcp-applepay/resources/css/styles.scss
+++ b/modules/ppcp-applepay/resources/css/styles.scss
@@ -52,3 +52,7 @@
}
}
}
+
+#ppc-button-ppcp-applepay {
+ display: none;
+}
From c15715a02f5e2fb30502479b90894ab291998130 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Fri, 26 Jul 2024 17:28:30 +0200
Subject: [PATCH 21/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20More=20reliable=20el?=
=?UTF-8?q?igibility-check?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Eligibility better aligns with the ApplePay SDK logic and will not insert a hidden button anymore.
---
.../resources/js/ApplepayButton.js | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index d84870fd9..fb853182c 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -163,11 +163,26 @@ class ApplePayButton {
return true;
}
- if ( this.buttonConfig.is_admin ) {
+ if ( CONTEXT.Preview === this.context ) {
return true;
}
- return !! ( this.applePayConfig.isEligible && window.ApplePaySession );
+ /**
+ * Ensure the ApplePaySession is available and accepts payments
+ * This check is required when using Apple Pay SDK v1; canMakePayments() returns false
+ * if the current device is not liked to iCloud or the Apple Wallet is not available
+ * for a different reason.
+ */
+ try {
+ if ( ! window.ApplePaySession?.canMakePayments() ) {
+ return false;
+ }
+ } catch ( error ) {
+ console.warn( error );
+ return false;
+ }
+
+ return !! this.applePayConfig.isEligible;
}
/**
From 1ac9b460b6aae0ee35dad718411628194c42f905 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Fri, 26 Jul 2024 18:32:33 +0200
Subject: [PATCH 22/22] =?UTF-8?q?=F0=9F=90=9B=20Display=20the=20PayPal=20p?=
=?UTF-8?q?ayment=20tabs=20in=20Gateway?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-wc-gateway/services.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php
index ce41619ca..92acc6b0c 100644
--- a/modules/ppcp-wc-gateway/services.php
+++ b/modules/ppcp-wc-gateway/services.php
@@ -71,6 +71,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
+use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
return array(
'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway {
@@ -198,6 +199,7 @@ return array(
Settings::PAY_LATER_TAB_ID,
AxoGateway::ID,
GooglePayGateway::ID,
+ ApplePayGateway::ID,
),
true
);
@@ -220,6 +222,7 @@ return array(
Settings::PAY_LATER_TAB_ID,
Settings::CONNECTION_TAB_ID,
GooglePayGateway::ID,
+ ApplePayGateway::ID,
),
true
);