From 42805c4fb5836107b0beb90425206ca481e9677a Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 16 Oct 2024 12:51:26 +0200 Subject: [PATCH 01/56] Add support for card icons to Axo Block and move the Change Card Link handling to the store. Also migrate the Change Card Link markup management logic to React --- .../ppcp-axo-block/resources/css/gateway.scss | 24 ++++++--- .../js/components/Card/CardChangeButton.js | 26 +++++++--- .../Card/CardChangeButtonManager.js | 51 ------------------- .../resources/js/components/Card/index.js | 2 - .../resources/js/components/Card/utils.js | 32 ------------ .../js/components/TitleLabel/TitleLabel.js | 24 +++++++++ .../js/components/TitleLabel/index.js | 1 + .../resources/js/events/emailLookupManager.js | 8 +-- .../resources/js/hooks/useAxoCleanup.js | 2 - .../resources/js/hooks/useAxoSetup.js | 5 +- .../resources/js/hooks/useCardOptions.js | 36 +++++++++++++ .../resources/js/hooks/useFastlaneSdk.js | 10 +++- modules/ppcp-axo-block/resources/js/index.js | 8 +-- .../resources/js/stores/axoStore.js | 17 +++++++ modules/ppcp-axo-block/services.php | 3 +- .../src/AxoBlockPaymentMethod.php | 17 ++++++- modules/ppcp-axo/services.php | 26 +++++++++- 17 files changed, 172 insertions(+), 120 deletions(-) delete mode 100644 modules/ppcp-axo-block/resources/js/components/Card/CardChangeButtonManager.js delete mode 100644 modules/ppcp-axo-block/resources/js/components/Card/utils.js create mode 100644 modules/ppcp-axo-block/resources/js/components/TitleLabel/TitleLabel.js create mode 100644 modules/ppcp-axo-block/resources/js/components/TitleLabel/index.js create mode 100644 modules/ppcp-axo-block/resources/js/hooks/useCardOptions.js diff --git a/modules/ppcp-axo-block/resources/css/gateway.scss b/modules/ppcp-axo-block/resources/css/gateway.scss index b1296ea1b..4611bfa35 100644 --- a/modules/ppcp-axo-block/resources/css/gateway.scss +++ b/modules/ppcp-axo-block/resources/css/gateway.scss @@ -17,10 +17,19 @@ $fast-transition-duration: 0.5s; } // 1. AXO Block Radio Label -#ppcp-axo-block-radio-label { - @include flex-space-between; +.wc-block-checkout__payment-method label[for="radio-control-wc-payment-method-options-ppcp-axo-gateway"] { + padding-right: .875em; +} + +#radio-control-wc-payment-method-options-ppcp-axo-gateway__label { + display: flex; + align-items: center; width: 100%; padding-right: 1em; + + .wc-block-components-payment-method-icons { + margin: 0; + } } // 2. AXO Block Card @@ -70,15 +79,16 @@ $fast-transition-duration: 0.5s; } &__edit { - background-color: transparent; + flex-grow: 1; + margin-left: auto; + text-align: right; border: 0; - color: inherit; - cursor: pointer; - display: block; font-family: inherit; - margin: 0 0 0 auto; font-size: 0.875em; font-weight: normal; + color: inherit; + background-color: transparent; + cursor: pointer; &:hover { text-decoration: underline; diff --git a/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButton.js b/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButton.js index c2a4eaa65..d3b27b17a 100644 --- a/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButton.js +++ b/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButton.js @@ -1,15 +1,28 @@ import { createElement } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; +import { STORE_NAME } from '../../stores/axoStore'; /** * Renders a button to change the selected card in the checkout process. * - * @param {Object} props - * @param {Function} props.onChangeButtonClick - Callback function to handle the click event. - * @return {JSX.Element} The rendered button as an anchor tag. + * @return {JSX.Element|null} The rendered button as an anchor tag, or null if conditions aren't met. */ -const CardChangeButton = ( { onChangeButtonClick } ) => - createElement( +const CardChangeButton = () => { + const { isGuest, cardDetails, cardChangeHandler } = useSelect( + ( select ) => ( { + isGuest: select( STORE_NAME ).getIsGuest(), + cardDetails: select( STORE_NAME ).getCardDetails(), + cardChangeHandler: select( STORE_NAME ).getCardChangeHandler(), + } ), + [] + ); + + if ( isGuest || ! cardDetails || ! cardChangeHandler ) { + return null; + } + + return createElement( 'a', { className: @@ -19,10 +32,11 @@ const CardChangeButton = ( { onChangeButtonClick } ) => // Prevent default anchor behavior event.preventDefault(); // Call the provided click handler - onChangeButtonClick(); + cardChangeHandler(); }, }, __( 'Choose a different card', 'woocommerce-paypal-payments' ) ); +}; export default CardChangeButton; diff --git a/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButtonManager.js b/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButtonManager.js deleted file mode 100644 index 2b6deba4f..000000000 --- a/modules/ppcp-axo-block/resources/js/components/Card/CardChangeButtonManager.js +++ /dev/null @@ -1,51 +0,0 @@ -import { createElement, createRoot, useEffect } from '@wordpress/element'; -import CardChangeButton from './CardChangeButton'; - -/** - * Manages the insertion and removal of the CardChangeButton in the DOM. - * - * @param {Object} props - * @param {Function} props.onChangeButtonClick - Callback function for when the card change button is clicked. - * @return {null} This component doesn't render any visible elements directly. - */ -const CardChangeButtonManager = ( { onChangeButtonClick } ) => { - useEffect( () => { - const radioLabelElement = document.getElementById( - 'ppcp-axo-block-radio-label' - ); - - if ( radioLabelElement ) { - // Check if the change button doesn't already exist - if ( - ! radioLabelElement.querySelector( - '.wc-block-checkout-axo-block-card__edit' - ) - ) { - // Create a new container for the button - const buttonContainer = document.createElement( 'div' ); - radioLabelElement.appendChild( buttonContainer ); - - // Create a React root and render the CardChangeButton - const root = createRoot( buttonContainer ); - root.render( - createElement( CardChangeButton, { onChangeButtonClick } ) - ); - } - } - - // Cleanup function to remove the button when the component unmounts - return () => { - const button = document.querySelector( - '.wc-block-checkout-axo-block-card__edit' - ); - if ( button && button.parentNode ) { - button.parentNode.remove(); - } - }; - }, [ onChangeButtonClick ] ); - - // This component doesn't render anything directly - return null; -}; - -export default CardChangeButtonManager; diff --git a/modules/ppcp-axo-block/resources/js/components/Card/index.js b/modules/ppcp-axo-block/resources/js/components/Card/index.js index c9250078e..7c44148bc 100644 --- a/modules/ppcp-axo-block/resources/js/components/Card/index.js +++ b/modules/ppcp-axo-block/resources/js/components/Card/index.js @@ -1,4 +1,2 @@ export { default as Card } from './Card'; export { default as CardChangeButton } from './CardChangeButton'; -export { default as CardChangeButtonManager } from './CardChangeButtonManager'; -export { injectCardChangeButton, removeCardChangeButton } from './utils'; diff --git a/modules/ppcp-axo-block/resources/js/components/Card/utils.js b/modules/ppcp-axo-block/resources/js/components/Card/utils.js deleted file mode 100644 index 915511885..000000000 --- a/modules/ppcp-axo-block/resources/js/components/Card/utils.js +++ /dev/null @@ -1,32 +0,0 @@ -import { createElement, createRoot } from '@wordpress/element'; -import CardChangeButtonManager from './CardChangeButtonManager'; - -/** - * Injects a card change button into the DOM. - * - * @param {Function} onChangeButtonClick - Callback function for when the card change button is clicked. - */ -export const injectCardChangeButton = ( onChangeButtonClick ) => { - // Create a container for the button - const container = document.createElement( 'div' ); - document.body.appendChild( container ); - - // Render the CardChangeButtonManager in the new container - createRoot( container ).render( - createElement( CardChangeButtonManager, { onChangeButtonClick } ) - ); -}; - -/** - * Removes the card change button from the DOM if it exists. - */ -export const removeCardChangeButton = () => { - const button = document.querySelector( - '.wc-block-checkout-axo-block-card__edit' - ); - - // Remove the button's parent node if it exists - if ( button && button.parentNode ) { - button.parentNode.remove(); - } -}; diff --git a/modules/ppcp-axo-block/resources/js/components/TitleLabel/TitleLabel.js b/modules/ppcp-axo-block/resources/js/components/TitleLabel/TitleLabel.js new file mode 100644 index 000000000..6435e23de --- /dev/null +++ b/modules/ppcp-axo-block/resources/js/components/TitleLabel/TitleLabel.js @@ -0,0 +1,24 @@ +import CardChangeButton from './../Card/CardChangeButton'; + +/** + * TitleLabel component for displaying a payment method title with icons and a change card button. + * + * @param {Object} props - Component props + * @param {Object} props.components - Object containing WooCommerce components + * @param {Object} props.config - Configuration object for the payment method + * @return {JSX.Element} WordPress element + */ +const TitleLabel = ( { components, config } ) => { + const axoConfig = window.wc_ppcp_axo; + const { PaymentMethodIcons } = components; + + return ( + <> + + + + + ); +}; + +export default TitleLabel; diff --git a/modules/ppcp-axo-block/resources/js/components/TitleLabel/index.js b/modules/ppcp-axo-block/resources/js/components/TitleLabel/index.js new file mode 100644 index 000000000..9ca79db8f --- /dev/null +++ b/modules/ppcp-axo-block/resources/js/components/TitleLabel/index.js @@ -0,0 +1 @@ +export { default as TitleLabel } from './TitleLabel'; diff --git a/modules/ppcp-axo-block/resources/js/events/emailLookupManager.js b/modules/ppcp-axo-block/resources/js/events/emailLookupManager.js index 563f9510d..8cc887668 100644 --- a/modules/ppcp-axo-block/resources/js/events/emailLookupManager.js +++ b/modules/ppcp-axo-block/resources/js/events/emailLookupManager.js @@ -1,7 +1,6 @@ import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug'; import { populateWooFields } from '../helpers/fieldHelpers'; import { injectShippingChangeButton } from '../components/Shipping'; -import { injectCardChangeButton } from '../components/Card'; import { setIsGuest, setIsEmailLookupCompleted } from '../stores/axoStore'; /** @@ -16,7 +15,6 @@ import { setIsGuest, setIsEmailLookupCompleted } from '../stores/axoStore'; * @param {Function} setWooShippingAddress - Function to update WooCommerce shipping address. * @param {Function} setWooBillingAddress - Function to update WooCommerce billing address. * @param {Function} onChangeShippingAddressClick - Handler for shipping address change. - * @param {Function} onChangeCardButtonClick - Handler for card change. * @return {Function} The email lookup handler function. */ export const createEmailLookupHandler = ( @@ -28,8 +26,7 @@ export const createEmailLookupHandler = ( wooBillingAddress, setWooShippingAddress, setWooBillingAddress, - onChangeShippingAddressClick, - onChangeCardButtonClick + onChangeShippingAddressClick ) => { return async ( email ) => { try { @@ -102,9 +99,8 @@ export const createEmailLookupHandler = ( setWooBillingAddress ); - // Inject change buttons for shipping and card + // Inject the change button for shipping injectShippingChangeButton( onChangeShippingAddressClick ); - injectCardChangeButton( onChangeCardButtonClick ); } else { log( 'Authentication failed or did not succeed', 'warn' ); } diff --git a/modules/ppcp-axo-block/resources/js/hooks/useAxoCleanup.js b/modules/ppcp-axo-block/resources/js/hooks/useAxoCleanup.js index 1da3cca85..07247f2e3 100644 --- a/modules/ppcp-axo-block/resources/js/hooks/useAxoCleanup.js +++ b/modules/ppcp-axo-block/resources/js/hooks/useAxoCleanup.js @@ -3,7 +3,6 @@ import { useDispatch } from '@wordpress/data'; import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug'; import { STORE_NAME } from '../stores/axoStore'; import { removeShippingChangeButton } from '../components/Shipping'; -import { removeCardChangeButton } from '../components/Card'; import { removeWatermark } from '../components/Watermark'; import { removeEmailFunctionality, @@ -50,7 +49,6 @@ const useAxoCleanup = () => { // Remove AXO UI elements removeShippingChangeButton(); - removeCardChangeButton(); removeWatermark(); // Remove email functionality if it was set up diff --git a/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js b/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js index af1aaca38..629ce05d4 100644 --- a/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js +++ b/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js @@ -35,6 +35,7 @@ const useAxoSetup = ( setIsAxoScriptLoaded, setShippingAddress, setCardDetails, + setCardChangeHandler, } = useDispatch( STORE_NAME ); // Check if PayPal script has loaded @@ -73,6 +74,7 @@ const useAxoSetup = ( if ( paypalLoaded && fastlaneSdk ) { setIsAxoScriptLoaded( true ); setIsAxoActive( true ); + setCardChangeHandler( onChangeCardButtonClick ); // Create and set up email lookup handler const emailLookupHandler = createEmailLookupHandler( @@ -84,8 +86,7 @@ const useAxoSetup = ( wooBillingAddress, setWooShippingAddress, setWooBillingAddress, - onChangeShippingAddressClick, - onChangeCardButtonClick + onChangeShippingAddressClick ); setupEmailFunctionality( emailLookupHandler ); } diff --git a/modules/ppcp-axo-block/resources/js/hooks/useCardOptions.js b/modules/ppcp-axo-block/resources/js/hooks/useCardOptions.js new file mode 100644 index 000000000..94c727784 --- /dev/null +++ b/modules/ppcp-axo-block/resources/js/hooks/useCardOptions.js @@ -0,0 +1,36 @@ +import { useMemo } from '@wordpress/element'; + +const DEFAULT_ALLOWED_CARDS = [ 'VISA', 'MASTERCARD', 'AMEX', 'DISCOVER' ]; + +/** + * Custom hook to determine the allowed card options based on configuration. + * + * @param {Object} axoConfig - The AXO configuration object. + * @return {Array} The final list of allowed card options. + */ +const useCardOptions = ( axoConfig ) => { + const merchantCountry = axoConfig.merchant_country || 'US'; + + return useMemo( () => { + const allowedCards = new Set( + axoConfig.allowed_cards?.[ merchantCountry ] || + DEFAULT_ALLOWED_CARDS + ); + + // Create a Set of disabled cards, converting each to uppercase + const disabledCards = new Set( + ( axoConfig.disable_cards || [] ).map( ( card ) => + card.toUpperCase() + ) + ); + + // Filter out disabled cards from the allowed cards + const finalCardOptions = [ ...allowedCards ].filter( + ( card ) => ! disabledCards.has( card ) + ); + + return finalCardOptions; + }, [ axoConfig.allowed_cards, axoConfig.disable_cards, merchantCountry ] ); +}; + +export default useCardOptions; diff --git a/modules/ppcp-axo-block/resources/js/hooks/useFastlaneSdk.js b/modules/ppcp-axo-block/resources/js/hooks/useFastlaneSdk.js index 7e68ccab1..a32c90c3b 100644 --- a/modules/ppcp-axo-block/resources/js/hooks/useFastlaneSdk.js +++ b/modules/ppcp-axo-block/resources/js/hooks/useFastlaneSdk.js @@ -3,6 +3,7 @@ import { useSelect } from '@wordpress/data'; import Fastlane from '../../../../ppcp-axo/resources/js/Connection/Fastlane'; import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug'; import { useDeleteEmptyKeys } from './useDeleteEmptyKeys'; +import useCardOptions from './useCardOptions'; import { STORE_NAME } from '../stores/axoStore'; /** @@ -26,6 +27,8 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => { [] ); + const cardOptions = useCardOptions( axoConfig ); + const styleOptions = useMemo( () => { return deleteEmptyKeys( configRef.current.axoConfig.style_options ); }, [ deleteEmptyKeys ] ); @@ -48,10 +51,13 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => { window.localStorage.setItem( 'axoEnv', 'sandbox' ); } - // Connect to Fastlane with locale and style options + // Connect to Fastlane with locale, style options, and allowed card brands await fastlane.connect( { locale: configRef.current.ppcpConfig.locale, styles: styleOptions, + cardOptions: { + allowedBrands: cardOptions, + }, } ); // Set locale (hardcoded to 'en_us' for now) @@ -66,7 +72,7 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => { }; initFastlane(); - }, [ fastlaneSdk, styleOptions, isPayPalLoaded, namespace ] ); + }, [ fastlaneSdk, styleOptions, isPayPalLoaded, namespace, cardOptions ] ); // Effect to update the config ref when configs change useEffect( () => { diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js index a45473e50..f58279af5 100644 --- a/modules/ppcp-axo-block/resources/js/index.js +++ b/modules/ppcp-axo-block/resources/js/index.js @@ -13,6 +13,7 @@ import usePayPalCommerceGateway from './hooks/usePayPalCommerceGateway'; // Components import { Payment } from './components/Payment/Payment'; +import { TitleLabel } from './components/TitleLabel'; const gatewayHandle = 'ppcp-axo-gateway'; const namespace = 'ppcpBlocksPaypalAxo'; @@ -89,12 +90,7 @@ const Axo = ( props ) => { registerPaymentMethod( { name: initialConfig.id, - label: ( -
- ), + label: , content: , edit: createElement( initialConfig.title ), ariaLabel: initialConfig.title, diff --git a/modules/ppcp-axo-block/resources/js/stores/axoStore.js b/modules/ppcp-axo-block/resources/js/stores/axoStore.js index f779f983c..c7afb91ef 100644 --- a/modules/ppcp-axo-block/resources/js/stores/axoStore.js +++ b/modules/ppcp-axo-block/resources/js/stores/axoStore.js @@ -12,6 +12,7 @@ const DEFAULT_STATE = { shippingAddress: null, cardDetails: null, phoneNumber: '', + cardChangeHandler: null, }; // Action creators for updating the store state @@ -52,6 +53,10 @@ const actions = { type: 'SET_PHONE_NUMBER', payload: phoneNumber, } ), + setCardChangeHandler: ( cardChangeHandler ) => ( { + type: 'SET_CARD_CHANGE_HANDLER', + payload: cardChangeHandler, + } ), }; /** @@ -81,6 +86,8 @@ const reducer = ( state = DEFAULT_STATE, action ) => { return { ...state, cardDetails: action.payload }; case 'SET_PHONE_NUMBER': return { ...state, phoneNumber: action.payload }; + case 'SET_CARD_CHANGE_HANDLER': + return { ...state, cardChangeHandler: action.payload }; default: return state; } @@ -97,6 +104,7 @@ const selectors = { getShippingAddress: ( state ) => state.shippingAddress, getCardDetails: ( state ) => state.cardDetails, getPhoneNumber: ( state ) => state.phoneNumber, + getCardChangeHandler: ( state ) => state.cardChangeHandler, }; // Create and register the Redux store for the AXO block @@ -163,3 +171,12 @@ export const setCardDetails = ( cardDetails ) => { export const setPhoneNumber = ( phoneNumber ) => { dispatch( STORE_NAME ).setPhoneNumber( phoneNumber ); }; + +/** + * Action dispatcher to update the card change handler in the store. + * + * @param {Function} cardChangeHandler - The card change handler function. + */ +export const setCardChangeHandler = ( cardChangeHandler ) => { + dispatch( STORE_NAME ).setCardChangeHandler( cardChangeHandler ); +}; diff --git a/modules/ppcp-axo-block/services.php b/modules/ppcp-axo-block/services.php index 270b69260..04a2f37ea 100644 --- a/modules/ppcp-axo-block/services.php +++ b/modules/ppcp-axo-block/services.php @@ -37,7 +37,8 @@ return array( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.configuration.dcc' ), $container->get( 'onboarding.environment' ), - $container->get( 'wcgateway.url' ) + $container->get( 'wcgateway.url' ), + $container->get( 'axo.supported-country-card-type-matrix' ), ); }, ); diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index 136db8233..c9712ab89 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -79,6 +79,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { */ private $wcgateway_module_url; + /** + * The supported country card type matrix. + * + * @var array + */ + private $supported_country_card_type_matrix; + /** * AdvancedCardPaymentMethod constructor. * @@ -91,6 +98,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. * @param Environment $environment The environment object. * @param string $wcgateway_module_url The WcGateway module URL. + * @param array $supported_country_card_type_matrix The supported country card type matrix for Axo. */ public function __construct( string $module_url, @@ -100,7 +108,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { Settings $settings, DCCGatewayConfiguration $dcc_configuration, Environment $environment, - string $wcgateway_module_url + string $wcgateway_module_url, + array $supported_country_card_type_matrix ) { $this->name = AxoGateway::ID; $this->module_url = $module_url; @@ -111,7 +120,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { $this->dcc_configuration = $dcc_configuration; $this->environment = $environment; $this->wcgateway_module_url = $wcgateway_module_url; - + $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; } /** @@ -207,6 +216,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { : null, // Set to null if WC()->cart is null or get_total doesn't exist. ), ), + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'style_options' => array( 'root' => array( 'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '', @@ -243,6 +254,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { ), 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), + 'merchant_country' => WC()->countries->get_base_country(), ); } } diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index ebeb0f394..b9af329f8 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -110,7 +110,31 @@ return array( ) ); }, - + /** + * The matrix which countries and card type combinations can be used for AXO. + */ + 'axo.supported-country-card-type-matrix' => static function ( ContainerInterface $container ) : array { + /** + * Returns which countries and card type combinations can be used for AXO. + */ + return apply_filters( + 'woocommerce_paypal_payments_axo_supported_country_card_type_matrix', + array( + 'US' => array( + 'VISA', + 'MASTERCARD', + 'AMEX', + 'DISCOVER', + ), + 'CA' => array( + 'VISA', + 'MASTERCARD', + 'AMEX', + 'DISCOVER', + ), + ) + ); + }, 'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string { $settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' ); assert( $settings_notice_generator instanceof SettingsNoticeGenerator ); From 17fd55d3c0131863cb9a7261097f94a147ac19f2 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 16 Oct 2024 13:33:48 +0200 Subject: [PATCH 02/56] Don't display methods before paypal is connected --- .../src/LocalAlternativePaymentMethodsModule.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php index 6db9de464..a2d3f66eb 100644 --- a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php +++ b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods; use WC_Order; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; +use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; @@ -58,6 +59,11 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo * @psalm-suppress MissingClosureParamType */ function ( $methods ) use ( $c ) { + $onboarding_state = $c->get( 'onboarding.state' ); + if ( $onboarding_state->current_state() >= State::STATE_START ) { + return $methods; + } + if ( ! is_array( $methods ) ) { return $methods; } From 910bffed03d7bcb37ff1f31745c648cb248602f0 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 16 Oct 2024 13:42:27 +0200 Subject: [PATCH 03/56] Fix state check --- .../src/LocalAlternativePaymentMethodsModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php index a2d3f66eb..17680864c 100644 --- a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php +++ b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php @@ -60,7 +60,7 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo */ function ( $methods ) use ( $c ) { $onboarding_state = $c->get( 'onboarding.state' ); - if ( $onboarding_state->current_state() >= State::STATE_START ) { + if ( $onboarding_state->current_state() === State::STATE_START ) { return $methods; } From bbde7f7890e41aba9e4c17396da04a6056fa51e3 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 16 Oct 2024 14:01:11 +0200 Subject: [PATCH 04/56] Axo: Add support for the card type limiting in the Fastlane classic checkout --- modules/ppcp-axo/resources/js/AxoManager.js | 32 ++++++++++++++++++++- modules/ppcp-axo/services.php | 3 +- modules/ppcp-axo/src/Assets/AxoManager.php | 29 +++++++++++++------ 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 0bd204672..fd22006a1 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -53,7 +53,7 @@ class AxoManager { cardView = null; constructor( namespace, axoConfig, ppcpConfig ) { - this.namespace = namespace; + this.namespace = namespace; this.axoConfig = axoConfig; this.ppcpConfig = ppcpConfig; @@ -85,6 +85,8 @@ class AxoManager { }, }; + this.cardOptions = this.getCardOptions(); + this.registerEventHandlers(); this.shippingView = new ShippingView( @@ -661,6 +663,9 @@ class AxoManager { await this.fastlane.connect( { locale: this.locale, styles: this.styles, + cardOptions: { + allowedBrands: this.cardOptions, + }, } ); this.fastlane.setLocale( 'en_us' ); @@ -1245,6 +1250,31 @@ class AxoManager { return this.axoConfig?.widgets?.email === 'use_widget'; } + getCardOptions() { + const DEFAULT_ALLOWED_CARDS = [ + 'VISA', + 'MASTERCARD', + 'AMEX', + 'DISCOVER', + ]; + const merchantCountry = this.axoConfig.merchant_country || 'US'; + + const allowedCards = new Set( + this.axoConfig.allowed_cards?.[ merchantCountry ] || + DEFAULT_ALLOWED_CARDS + ); + + const disabledCards = new Set( + ( this.axoConfig.disable_cards || [] ).map( ( card ) => + card.toUpperCase() + ) + ); + + return [ ...allowedCards ].filter( + ( card ) => ! disabledCards.has( card ) + ); + } + deleteKeysWithEmptyString = ( obj ) => { for ( const key of Object.keys( obj ) ) { if ( obj[ key ] === '' ) { diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index b9af329f8..657805172 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -67,7 +67,8 @@ return array( $container->get( 'wcgateway.settings.status' ), $container->get( 'api.shop.currency.getter' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'wcgateway.url' ) + $container->get( 'wcgateway.url' ), + $container->get( 'axo.supported-country-card-type-matrix' ) ); }, diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 6fa196719..cedfafac1 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -29,28 +29,28 @@ class AxoManager { * * @var string */ - private $module_url; + private string $module_url; /** * The assets version. * * @var string */ - private $version; + private string $version; /** * The settings. * * @var Settings */ - private $settings; + private Settings $settings; /** * The environment object. * * @var Environment */ - private $environment; + private Environment $environment; /** * The Settings status helper. @@ -71,22 +71,27 @@ class AxoManager { * * @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; /** * Session handler. * * @var SessionHandler */ - private $session_handler; + private SessionHandler $session_handler; /** * The WcGateway module URL. * * @var string */ - private $wcgateway_module_url; - + private string $wcgateway_module_url; + /** + * The supported country card type matrix. + * + * @var array + */ + private array $supported_country_card_type_matrix; /** * AxoManager constructor. * @@ -99,6 +104,7 @@ class AxoManager { * @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop. * @param LoggerInterface $logger The logger. * @param string $wcgateway_module_url The WcGateway module URL. + * @param array $supported_country_card_type_matrix The supported country card type matrix for Axo. */ public function __construct( string $module_url, @@ -109,7 +115,8 @@ class AxoManager { SettingsStatus $settings_status, CurrencyGetter $currency, LoggerInterface $logger, - string $wcgateway_module_url + string $wcgateway_module_url, + array $supported_country_card_type_matrix ) { $this->module_url = $module_url; @@ -121,6 +128,7 @@ class AxoManager { $this->currency = $currency; $this->logger = $logger; $this->wcgateway_module_url = $wcgateway_module_url; + $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; } /** @@ -183,6 +191,8 @@ class AxoManager { 'value' => WC()->cart->get_total( 'numeric' ), ), ), + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'style_options' => array( 'root' => array( 'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '', @@ -220,6 +230,7 @@ class AxoManager { 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ), + 'merchant_country' => WC()->countries->get_base_country(), ); } From 46f49574e7a8bd4d5c00cdb4af80993cdb5bb9ba Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 16 Oct 2024 14:26:08 +0200 Subject: [PATCH 05/56] Add PHPCS fixes --- .../src/AxoBlockPaymentMethod.php | 46 +++++++++---------- modules/ppcp-axo/services.php | 2 +- modules/ppcp-axo/src/Assets/AxoManager.php | 26 +++++------ 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index c9712ab89..5eb0191be 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -111,15 +111,15 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { string $wcgateway_module_url, array $supported_country_card_type_matrix ) { - $this->name = AxoGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; - $this->smart_button = $smart_button; - $this->settings = $settings; - $this->dcc_configuration = $dcc_configuration; - $this->environment = $environment; - $this->wcgateway_module_url = $wcgateway_module_url; + $this->name = AxoGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; + $this->settings = $settings; + $this->dcc_configuration = $dcc_configuration; + $this->environment = $environment; + $this->wcgateway_module_url = $wcgateway_module_url; $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; } @@ -196,13 +196,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { } return array( - 'environment' => array( + 'environment' => array( 'is_sandbox' => $this->environment->current_environment() === 'sandbox', ), - 'widgets' => array( + 'widgets' => array( 'email' => 'render', ), - 'insights' => array( + 'insights' => array( 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), 'session_id' => @@ -216,9 +216,9 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { : null, // Set to null if WC()->cart is null or get_total doesn't exist. ), ), - 'allowed_cards' => $this->supported_country_card_type_matrix, - 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), - 'style_options' => array( + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), + 'style_options' => array( 'root' => array( 'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '', 'errorColor' => $this->settings->has( 'axo_style_root_error_color' ) ? $this->settings->get( 'axo_style_root_error_color' ) : '', @@ -237,24 +237,24 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'focusBorderColor' => $this->settings->has( 'axo_style_input_focus_border_color' ) ? $this->settings->get( 'axo_style_input_focus_border_color' ) : '', ), ), - 'name_on_card' => $this->dcc_configuration->show_name_on_card(), - 'woocommerce' => array( + 'name_on_card' => $this->dcc_configuration->show_name_on_card(), + 'woocommerce' => array( 'states' => array( 'US' => WC()->countries->get_states( 'US' ), 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'icons_directory' => esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/', - 'module_url' => untrailingslashit( $this->module_url ), - 'ajax' => array( + 'icons_directory' => esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/', + 'module_url' => untrailingslashit( $this->module_url ), + 'ajax' => array( 'frontend_logger' => array( 'endpoint' => \WC_AJAX::get_endpoint( FrontendLoggerEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ), ), ), - 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', - 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), + 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', + 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), 'merchant_country' => WC()->countries->get_base_country(), ); } diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 657805172..61a034fcc 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -114,7 +114,7 @@ return array( /** * The matrix which countries and card type combinations can be used for AXO. */ - 'axo.supported-country-card-type-matrix' => static function ( ContainerInterface $container ) : array { + 'axo.supported-country-card-type-matrix' => static function ( ContainerInterface $container ) : array { /** * Returns which countries and card type combinations can be used for AXO. */ diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index cedfafac1..0d3459a7a 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -104,7 +104,7 @@ class AxoManager { * @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop. * @param LoggerInterface $logger The logger. * @param string $wcgateway_module_url The WcGateway module URL. - * @param array $supported_country_card_type_matrix The supported country card type matrix for Axo. + * @param array $supported_country_card_type_matrix The supported country card type matrix for Axo. */ public function __construct( string $module_url, @@ -119,15 +119,15 @@ class AxoManager { array $supported_country_card_type_matrix ) { - $this->module_url = $module_url; - $this->version = $version; - $this->session_handler = $session_handler; - $this->settings = $settings; - $this->environment = $environment; - $this->settings_status = $settings_status; - $this->currency = $currency; - $this->logger = $logger; - $this->wcgateway_module_url = $wcgateway_module_url; + $this->module_url = $module_url; + $this->version = $version; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->environment = $environment; + $this->settings_status = $settings_status; + $this->currency = $currency; + $this->logger = $logger; + $this->wcgateway_module_url = $wcgateway_module_url; $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; } @@ -191,8 +191,8 @@ class AxoManager { 'value' => WC()->cart->get_total( 'numeric' ), ), ), - 'allowed_cards' => $this->supported_country_card_type_matrix, - 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'style_options' => array( 'root' => array( 'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '', @@ -230,7 +230,7 @@ class AxoManager { 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ), - 'merchant_country' => WC()->countries->get_base_country(), + 'merchant_country' => WC()->countries->get_base_country(), ); } From de609be21b40cda90192e1642f96e46f357b52fb Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Fri, 18 Oct 2024 08:14:50 +0200 Subject: [PATCH 06/56] Add paypal & ratepay default icons to classic checkout --- modules/ppcp-wc-gateway/assets/images/paypal.svg | 3 +++ modules/ppcp-wc-gateway/assets/images/ratepay.svg | 3 +++ modules/ppcp-wc-gateway/services.php | 6 ++++-- .../ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 13 ++++++++++++- .../PayUponInvoice/PayUponInvoiceGateway.php | 14 +++++++++++++- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 modules/ppcp-wc-gateway/assets/images/paypal.svg create mode 100644 modules/ppcp-wc-gateway/assets/images/ratepay.svg diff --git a/modules/ppcp-wc-gateway/assets/images/paypal.svg b/modules/ppcp-wc-gateway/assets/images/paypal.svg new file mode 100644 index 000000000..0d82f0b74 --- /dev/null +++ b/modules/ppcp-wc-gateway/assets/images/paypal.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ppcp-wc-gateway/assets/images/ratepay.svg b/modules/ppcp-wc-gateway/assets/images/ratepay.svg new file mode 100644 index 000000000..1e8030e33 --- /dev/null +++ b/modules/ppcp-wc-gateway/assets/images/ratepay.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 9ed1a83a8..8d2c8e051 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -118,7 +118,8 @@ return array( $container->get( 'wcgateway.place-order-button-text' ), $container->get( 'api.endpoint.payment-tokens' ), $container->get( 'vaulting.vault-v3-enabled' ), - $container->get( 'vaulting.wc-payment-tokens' ) + $container->get( 'vaulting.wc-payment-tokens' ), + $container->get( 'wcgateway.url' ) ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { @@ -1376,7 +1377,8 @@ return array( $container->get( 'wcgateway.pay-upon-invoice-helper' ), $container->get( 'wcgateway.checkout-helper' ), $container->get( 'onboarding.state' ), - $container->get( 'wcgateway.processor.refunds' ) + $container->get( 'wcgateway.processor.refunds' ), + $container->get( 'wcgateway.url' ) ); }, 'wcgateway.fraudnet-source-website-id' => static function ( ContainerInterface $container ): FraudNetSourceWebsiteId { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 4a00b8d5e..3012d5885 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -202,6 +202,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $wc_payment_tokens; + /** + * The module URL + * + * @var string + */ + private $module_url; + /** * PayPalGateway constructor. * @@ -225,6 +232,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. + * @param string $module_url The module URL */ public function __construct( SettingsRenderer $settings_renderer, @@ -246,7 +254,8 @@ class PayPalGateway extends \WC_Payment_Gateway { string $place_order_button_text, PaymentTokensEndpoint $payment_tokens_endpoint, bool $vault_v3_enabled, - WooCommercePaymentTokens $wc_payment_tokens + WooCommercePaymentTokens $wc_payment_tokens, + string $module_url ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -270,6 +279,8 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->payment_tokens_endpoint = $payment_tokens_endpoint; $this->vault_v3_enabled = $vault_v3_enabled; $this->wc_payment_tokens = $wc_payment_tokens; + $this->module_url = $module_url; + $this->icon = apply_filters('woocommerce_paypal_payments_paypal_gateway_icon', esc_url( $this->module_url ) . 'assets/images/paypal.svg'); $default_support = array( 'products', diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php index d4ca29121..a1a4e4113 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php @@ -103,6 +103,13 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { */ protected $refund_processor; + /** + * The module URL + * + * @var string + */ + private $module_url; + /** * PayUponInvoiceGateway constructor. * @@ -116,6 +123,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { * @param CheckoutHelper $checkout_helper The checkout helper. * @param State $state The onboarding state. * @param RefundProcessor $refund_processor The refund processor. + * @param string $module_url The module URL */ public function __construct( PayUponInvoiceOrderEndpoint $order_endpoint, @@ -127,7 +135,8 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { PayUponInvoiceHelper $pui_helper, CheckoutHelper $checkout_helper, State $state, - RefundProcessor $refund_processor + RefundProcessor $refund_processor, + string $module_url ) { $this->id = self::ID; @@ -157,6 +166,9 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { $this->transaction_url_provider = $transaction_url_provider; $this->pui_helper = $pui_helper; $this->checkout_helper = $checkout_helper; + $this->module_url = $module_url; + $this->icon = apply_filters('woocommerce_paypal_payments_pay_upon_invoice_gateway_icon', esc_url( $this->module_url ) . 'assets/images/ratepay.svg'); + $this->state = $state; if ( $state->current_state() === State::STATE_ONBOARDED ) { From 82faf9370b5148045ad5d9920ed5e946ac865ae7 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Fri, 18 Oct 2024 09:47:42 +0200 Subject: [PATCH 07/56] Add paypal icon to the block checkout --- .../ppcp-blocks/resources/js/checkout-block.js | 17 ++++++++++++++++- modules/ppcp-blocks/src/PayPalPaymentMethod.php | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index 97590838e..fb012dbde 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -768,9 +768,24 @@ if ( block_enabled && config.enabled ) { ); } + const PaypalLabel = ( { components, config } ) => { + const { PaymentMethodIcons } = components; + + return ( + <> + + + + ); + }; + registerPaymentMethod( { name: config.id, - label:
, + label: , content: descriptionElement, edit: descriptionElement, placeOrderButtonLabel: config.placeOrderButtonText, diff --git a/modules/ppcp-blocks/src/PayPalPaymentMethod.php b/modules/ppcp-blocks/src/PayPalPaymentMethod.php index 50340b574..d6a617af0 100644 --- a/modules/ppcp-blocks/src/PayPalPaymentMethod.php +++ b/modules/ppcp-blocks/src/PayPalPaymentMethod.php @@ -248,6 +248,13 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType { return array( 'id' => $this->gateway->id, 'title' => $this->gateway->title, + 'icon' => [ + [ + 'id' => 'paypal', + 'alt' => 'PayPal', + 'src' => $this->gateway->icon + ] + ], 'description' => $this->gateway->description, 'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ?? 'checkout' ), 'fundingSource' => $this->session_handler->funding_source(), From 6f354828476769d87e4bb212d4833b7413c6fd98 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Fri, 18 Oct 2024 11:29:07 +0200 Subject: [PATCH 08/56] Update icons, fix errors --- modules/ppcp-blocks/src/PayPalPaymentMethod.php | 12 ++++++------ modules/ppcp-wc-gateway/assets/images/paypal.png | Bin modules/ppcp-wc-gateway/assets/images/paypal.svg | 2 +- .../ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 6 +++--- .../PayUponInvoice/PayUponInvoiceGateway.php | 7 +++---- 5 files changed, 13 insertions(+), 14 deletions(-) mode change 100755 => 100644 modules/ppcp-wc-gateway/assets/images/paypal.png diff --git a/modules/ppcp-blocks/src/PayPalPaymentMethod.php b/modules/ppcp-blocks/src/PayPalPaymentMethod.php index d6a617af0..3cb122cbc 100644 --- a/modules/ppcp-blocks/src/PayPalPaymentMethod.php +++ b/modules/ppcp-blocks/src/PayPalPaymentMethod.php @@ -248,13 +248,13 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType { return array( 'id' => $this->gateway->id, 'title' => $this->gateway->title, - 'icon' => [ - [ - 'id' => 'paypal', + 'icon' => array( + array( + 'id' => 'paypal', 'alt' => 'PayPal', - 'src' => $this->gateway->icon - ] - ], + 'src' => $this->gateway->icon, + ), + ), 'description' => $this->gateway->description, 'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ?? 'checkout' ), 'fundingSource' => $this->session_handler->funding_source(), diff --git a/modules/ppcp-wc-gateway/assets/images/paypal.png b/modules/ppcp-wc-gateway/assets/images/paypal.png old mode 100755 new mode 100644 diff --git a/modules/ppcp-wc-gateway/assets/images/paypal.svg b/modules/ppcp-wc-gateway/assets/images/paypal.svg index 0d82f0b74..9aa54566c 100644 --- a/modules/ppcp-wc-gateway/assets/images/paypal.svg +++ b/modules/ppcp-wc-gateway/assets/images/paypal.svg @@ -1,3 +1,3 @@ - + diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 3012d5885..aafb50cb2 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -232,7 +232,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. - * @param string $module_url The module URL + * @param string $module_url The module URL. */ public function __construct( SettingsRenderer $settings_renderer, @@ -279,8 +279,8 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->payment_tokens_endpoint = $payment_tokens_endpoint; $this->vault_v3_enabled = $vault_v3_enabled; $this->wc_payment_tokens = $wc_payment_tokens; - $this->module_url = $module_url; - $this->icon = apply_filters('woocommerce_paypal_payments_paypal_gateway_icon', esc_url( $this->module_url ) . 'assets/images/paypal.svg'); + $this->module_url = $module_url; + $this->icon = apply_filters( 'woocommerce_paypal_payments_paypal_gateway_icon', esc_url( $this->module_url ) . 'assets/images/paypal.svg' ); $default_support = array( 'products', diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php index a1a4e4113..69ae3b522 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php @@ -123,7 +123,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { * @param CheckoutHelper $checkout_helper The checkout helper. * @param State $state The onboarding state. * @param RefundProcessor $refund_processor The refund processor. - * @param string $module_url The module URL + * @param string $module_url The module URL */ public function __construct( PayUponInvoiceOrderEndpoint $order_endpoint, @@ -166,9 +166,8 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { $this->transaction_url_provider = $transaction_url_provider; $this->pui_helper = $pui_helper; $this->checkout_helper = $checkout_helper; - $this->module_url = $module_url; - $this->icon = apply_filters('woocommerce_paypal_payments_pay_upon_invoice_gateway_icon', esc_url( $this->module_url ) . 'assets/images/ratepay.svg'); - + $this->module_url = $module_url; + $this->icon = apply_filters( 'woocommerce_paypal_payments_pay_upon_invoice_gateway_icon', esc_url( $this->module_url ) . 'assets/images/ratepay.svg' ); $this->state = $state; if ( $state->current_state() === State::STATE_ONBOARDED ) { From 2a9d028308118014547c04190eb185621755f618 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Fri, 18 Oct 2024 11:36:33 +0200 Subject: [PATCH 09/56] Fix tests --- .../Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php | 3 ++- tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php index 0161f8c71..82c88449f 100644 --- a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php @@ -64,7 +64,8 @@ class PayUponInvoiceGatewayTest extends TestCase $this->pui_helper, $this->checkout_helper, $this->state, - $this->refund_processor + $this->refund_processor, + '' ); } diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index 041141492..5884572e7 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -123,7 +123,8 @@ class WcGatewayTest extends TestCase 'Pay via PayPal', $this->paymentTokensEndpoint, $this->vaultV3Enabled, - $this->wcPaymentTokens + $this->wcPaymentTokens, + '' ); } From 89a87c2f37d3f144b0f06b9c0a7ed09721526b67 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Fri, 18 Oct 2024 11:46:23 +0200 Subject: [PATCH 10/56] Add a dot to the param comment --- .../src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php index 69ae3b522..dbd6629bd 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php @@ -123,7 +123,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { * @param CheckoutHelper $checkout_helper The checkout helper. * @param State $state The onboarding state. * @param RefundProcessor $refund_processor The refund processor. - * @param string $module_url The module URL + * @param string $module_url The module URL. */ public function __construct( PayUponInvoiceOrderEndpoint $order_endpoint, From c89da49e8f064eb68d80c98e403d11728ce8d1a6 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 23 Oct 2024 18:45:33 +0200 Subject: [PATCH 11/56] Add PayPalInsightsLoader for Axo Block --- .../js/plugins/PayPalInsightsLoader.js | 35 ++++++++++++++++ modules/ppcp-axo-block/src/AxoBlockModule.php | 42 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js diff --git a/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js new file mode 100644 index 000000000..9f114593b --- /dev/null +++ b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js @@ -0,0 +1,35 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useSelect } from '@wordpress/data'; +import { useEffect, useState } from '@wordpress/element'; +import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights'; + +const PayPalInsightsLoader = () => { + + // Subscribe to checkout store data + const checkoutData = useSelect( ( select ) => { + return { + paymentMethod: select( 'wc/store/checkout' ).getPaymentMethod(), + orderTotal: select( 'wc/store/checkout' ).getOrderTotal(), + // Add other data points you need + }; + } ); + + // Watch for changes and trigger analytics events + useEffect( () => { + if ( isScriptLoaded && window.YourAnalyticsObject ) { + // Example tracking calls + window.YourAnalyticsObject.track( 'checkout_updated', { + payment_method: checkoutData.paymentMethod, + order_total: checkoutData.orderTotal, + } ); + } + }, [ isScriptLoaded, checkoutData ] ); + + return null; // This component doesn't render anything +}; + +// Register the plugin to run with the checkout block +registerPlugin( 'wc-ppcp-paypal-insights', { + render: PayPalInsightsLoader, + scope: 'woocommerce-checkout', +} ); diff --git a/modules/ppcp-axo-block/src/AxoBlockModule.php b/modules/ppcp-axo-block/src/AxoBlockModule.php index 669cc7cc5..2a7ee73b6 100644 --- a/modules/ppcp-axo-block/src/AxoBlockModule.php +++ b/modules/ppcp-axo-block/src/AxoBlockModule.php @@ -133,6 +133,15 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule wp_enqueue_style( 'wc-ppcp-axo-block' ); } ); + + // Enqueue the PayPal Insights script + add_action( + 'wp_enqueue_scripts', + function () use ($c) { + $this->enqueue_paypal_insights_script($c); + } + ); + return true; } @@ -166,4 +175,37 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule return $localized_script_data; } + + /** + * Enqueues PayPal Insights analytics script for the Checkout block. + * + * @param ContainerInterface $c The service container. + * @return void + */ + private function enqueue_paypal_insights_script( ContainerInterface $c ): void { + if ( ! has_block( 'woocommerce/checkout' ) ) { + return; + } + + $module_url = $c->get( 'axoblock.url' ); + $asset_version = $c->get( 'ppcp.asset-version' ); + + wp_register_script( + 'wc-ppcp-paypal-insights', + untrailingslashit( $module_url ) . '/resources/js/plugins/PayPalInsights.js', + array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ), + $asset_version, + true + ); + + wp_localize_script( + 'wc-ppcp-paypal-insights', + 'ppcpPayPalInsightsData', + array( + 'isAxoEnabled' => true, + ) + ); + + wp_enqueue_script( 'wc-ppcp-paypal-insights' ); + } } From 1204edb1ca1b7a1d4b9efa2412bc572aad94dc50 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 29 Oct 2024 11:04:41 +0100 Subject: [PATCH 12/56] Axo: Add PayPal Insights to the Block Checkout and fix the existing Classic Checkout integration --- .../js/plugins/PayPalInsightsLoader.js | 266 ++++++++++++++++-- .../resources/js/stores/axoStore.js | 16 +- modules/ppcp-axo-block/services.php | 1 + modules/ppcp-axo-block/src/AxoBlockModule.php | 10 +- .../src/AxoBlockPaymentMethod.php | 39 ++- modules/ppcp-axo-block/webpack.config.js | 5 +- modules/ppcp-axo/resources/js/AxoManager.js | 38 ++- .../js/Insights/EndCheckoutTracker.js | 99 +++++++ modules/ppcp-axo/services.php | 48 ++++ modules/ppcp-axo/src/Assets/AxoManager.php | 30 +- modules/ppcp-axo/src/AxoModule.php | 66 ++++- modules/ppcp-axo/webpack.config.js | 71 ++--- 12 files changed, 570 insertions(+), 119 deletions(-) create mode 100644 modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js diff --git a/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js index 9f114593b..b831bd45b 100644 --- a/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js +++ b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js @@ -1,35 +1,259 @@ import { registerPlugin } from '@wordpress/plugins'; +import { useEffect, useCallback, useState, useRef } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; -import { useEffect, useState } from '@wordpress/element'; +import { PAYMENT_STORE_KEY } from '@woocommerce/block-data'; import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights'; +import { STORE_NAME } from '../stores/axoStore'; +import usePayPalCommerceGateway from '../hooks/usePayPalCommerceGateway'; -const PayPalInsightsLoader = () => { +const GATEWAY_HANDLE = 'ppcp-axo-gateway'; - // Subscribe to checkout store data - const checkoutData = useSelect( ( select ) => { - return { - paymentMethod: select( 'wc/store/checkout' ).getPaymentMethod(), - orderTotal: select( 'wc/store/checkout' ).getOrderTotal(), - // Add other data points you need - }; +const useEventTracking = () => { + const [ triggeredEvents, setTriggeredEvents ] = useState( { + initialized: false, + jsLoaded: false, + beginCheckout: false, + emailSubmitted: false, } ); - // Watch for changes and trigger analytics events - useEffect( () => { - if ( isScriptLoaded && window.YourAnalyticsObject ) { - // Example tracking calls - window.YourAnalyticsObject.track( 'checkout_updated', { - payment_method: checkoutData.paymentMethod, - order_total: checkoutData.orderTotal, - } ); - } - }, [ isScriptLoaded, checkoutData ] ); + const currentPaymentMethod = useRef( null ); - return null; // This component doesn't render anything + const setEventTriggered = useCallback( ( eventName, value = true ) => { + setTriggeredEvents( ( prev ) => ( { + ...prev, + [ eventName ]: value, + } ) ); + }, [] ); + + const isEventTriggered = useCallback( + ( eventName ) => triggeredEvents[ eventName ], + [ triggeredEvents ] + ); + + const setCurrentPaymentMethod = useCallback( ( methodName ) => { + currentPaymentMethod.current = methodName; + }, [] ); + + const getCurrentPaymentMethod = useCallback( + () => currentPaymentMethod.current, + [] + ); + + return { + setEventTriggered, + isEventTriggered, + setCurrentPaymentMethod, + getCurrentPaymentMethod, + }; +}; + +const waitForPayPalInsight = () => { + return new Promise( ( resolve, reject ) => { + // If already loaded, resolve immediately + if ( window.paypalInsight ) { + resolve( window.paypalInsight ); + return; + } + + // Set a reasonable timeout + const timeoutId = setTimeout( () => { + observer.disconnect(); + reject( new Error( 'PayPal Insights script load timeout' ) ); + }, 10000 ); + + // Create MutationObserver to watch for script initialization + const observer = new MutationObserver( () => { + if ( window.paypalInsight ) { + observer.disconnect(); + clearTimeout( timeoutId ); + resolve( window.paypalInsight ); + } + } ); + + // Start observing + observer.observe( document, { + childList: true, + subtree: true, + } ); + } ); +}; + +const usePayPalInsightsInit = ( axoConfig, ppcpConfig, eventTracking ) => { + const { setEventTriggered, isEventTriggered } = eventTracking; + const initialized = useRef( false ); + + useEffect( () => { + if ( + ! axoConfig?.insights?.enabled || + ! axoConfig?.insights?.client_id || + ! axoConfig?.insights?.session_id || + initialized.current || + isEventTriggered( 'initialized' ) + ) { + return; + } + + const initializePayPalInsights = async () => { + try { + await waitForPayPalInsight(); + + if ( initialized.current ) { + return; + } + + // Track JS load first + PayPalInsights.trackJsLoad(); + setEventTriggered( 'jsLoaded' ); + + PayPalInsights.config( axoConfig.insights.client_id, { + debug: axoConfig?.wp_debug === '1', + } ); + + PayPalInsights.setSessionId( axoConfig.insights.session_id ); + initialized.current = true; + setEventTriggered( 'initialized' ); + + if ( + isEventTriggered( 'jsLoaded' ) && + ! isEventTriggered( 'beginCheckout' ) + ) { + PayPalInsights.trackBeginCheckout( { + amount: axoConfig.insights.amount, + page_type: 'checkout', + user_data: { + country: 'US', + is_store_member: false, + }, + } ); + setEventTriggered( 'beginCheckout' ); + } + } catch ( error ) { + console.error( + 'PayPal Insights initialization failed:', + error + ); + } + }; + + initializePayPalInsights(); + + return () => { + initialized.current = false; + }; + }, [ axoConfig, ppcpConfig, setEventTriggered, isEventTriggered ] ); +}; + +const usePaymentMethodTracking = ( axoConfig, eventTracking ) => { + const { setCurrentPaymentMethod } = eventTracking; + const lastPaymentMethod = useRef( null ); + const isInitialMount = useRef( true ); + + const activePaymentMethod = useSelect( ( select ) => { + return select( PAYMENT_STORE_KEY )?.getActivePaymentMethod(); + }, [] ); + + const handlePaymentMethodChange = useCallback( + async ( paymentMethod ) => { + // Skip if no payment method or same as last one + if ( + ! paymentMethod || + paymentMethod === lastPaymentMethod.current + ) { + return; + } + + try { + await waitForPayPalInsight(); + + // Only track if it's not the initial mount, and we have a previous payment method + if ( ! isInitialMount.current && lastPaymentMethod.current ) { + PayPalInsights.trackSelectPaymentMethod( { + payment_method_selected: + axoConfig?.insights?.payment_method_selected_map[ + paymentMethod + ] || 'other', + page_type: 'checkout', + } ); + } + + lastPaymentMethod.current = paymentMethod; + setCurrentPaymentMethod( paymentMethod ); + } catch ( error ) { + console.error( 'Failed to track payment method:', error ); + } + }, + [ + axoConfig?.insights?.payment_method_selected_map, + setCurrentPaymentMethod, + ] + ); + + useEffect( () => { + if ( activePaymentMethod ) { + if ( isInitialMount.current ) { + // Just set the initial payment method without tracking + lastPaymentMethod.current = activePaymentMethod; + setCurrentPaymentMethod( activePaymentMethod ); + isInitialMount.current = false; + } else { + handlePaymentMethodChange( activePaymentMethod ); + } + } + }, [ + activePaymentMethod, + handlePaymentMethodChange, + setCurrentPaymentMethod, + ] ); + + useEffect( () => { + return () => { + lastPaymentMethod.current = null; + isInitialMount.current = true; + }; + }, [] ); +}; + +const PayPalInsightsLoader = () => { + const eventTracking = useEventTracking(); + const { setEventTriggered, isEventTriggered } = eventTracking; + + const initialConfig = + window?.wc?.wcSettings?.getSetting( `${ GATEWAY_HANDLE }_data` ) || {}; + + const { ppcpConfig } = usePayPalCommerceGateway( initialConfig ); + const axoConfig = window?.wc_ppcp_axo; + + const { isEmailSubmitted } = useSelect( ( select ) => { + const storeSelect = select( STORE_NAME ); + return { + isEmailSubmitted: storeSelect?.getIsEmailSubmitted?.() ?? false, + }; + }, [] ); + + usePayPalInsightsInit( axoConfig, ppcpConfig, eventTracking ); + usePaymentMethodTracking( axoConfig, eventTracking ); + + useEffect( () => { + const trackEmail = async () => { + if ( isEmailSubmitted && ! isEventTriggered( 'emailSubmitted' ) ) { + try { + await waitForPayPalInsight(); + PayPalInsights.trackSubmitCheckoutEmail(); + setEventTriggered( 'emailSubmitted' ); + } catch ( error ) { + console.error( 'Failed to track email submission:', error ); + } + } + }; + trackEmail(); + }, [ isEmailSubmitted, setEventTriggered, isEventTriggered ] ); + + return null; }; -// Register the plugin to run with the checkout block registerPlugin( 'wc-ppcp-paypal-insights', { render: PayPalInsightsLoader, scope: 'woocommerce-checkout', } ); + +export default PayPalInsightsLoader; diff --git a/modules/ppcp-axo-block/resources/js/stores/axoStore.js b/modules/ppcp-axo-block/resources/js/stores/axoStore.js index f779f983c..6235e58c2 100644 --- a/modules/ppcp-axo-block/resources/js/stores/axoStore.js +++ b/modules/ppcp-axo-block/resources/js/stores/axoStore.js @@ -1,4 +1,4 @@ -import { createReduxStore, register, dispatch } from '@wordpress/data'; +import { createReduxStore, register, dispatch, select } from '@wordpress/data'; export const STORE_NAME = 'woocommerce-paypal-payments/axo-block'; @@ -100,13 +100,15 @@ const selectors = { }; // Create and register the Redux store for the AXO block -const store = createReduxStore( STORE_NAME, { - reducer, - actions, - selectors, -} ); +if ( ! select( STORE_NAME ) ) { + const store = createReduxStore( STORE_NAME, { + reducer, + actions, + selectors, + } ); -register( store ); + register( store ); +} // Action dispatchers diff --git a/modules/ppcp-axo-block/services.php b/modules/ppcp-axo-block/services.php index 270b69260..fddf07f80 100644 --- a/modules/ppcp-axo-block/services.php +++ b/modules/ppcp-axo-block/services.php @@ -37,6 +37,7 @@ return array( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.configuration.dcc' ), $container->get( 'onboarding.environment' ), + $container->get( 'axo.payment_method_selected_map' ), $container->get( 'wcgateway.url' ) ); }, diff --git a/modules/ppcp-axo-block/src/AxoBlockModule.php b/modules/ppcp-axo-block/src/AxoBlockModule.php index 2a7ee73b6..1ebb068ed 100644 --- a/modules/ppcp-axo-block/src/AxoBlockModule.php +++ b/modules/ppcp-axo-block/src/AxoBlockModule.php @@ -134,11 +134,11 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule } ); - // Enqueue the PayPal Insights script + // Enqueue the PayPal Insights script. add_action( 'wp_enqueue_scripts', - function () use ($c) { - $this->enqueue_paypal_insights_script($c); + function () use ( $c ) { + $this->enqueue_paypal_insights_script( $c ); } ); @@ -183,7 +183,7 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule * @return void */ private function enqueue_paypal_insights_script( ContainerInterface $c ): void { - if ( ! has_block( 'woocommerce/checkout' ) ) { + if ( ! has_block( 'woocommerce/checkout' ) || WC()->cart->is_empty() ) { return; } @@ -192,7 +192,7 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule wp_register_script( 'wc-ppcp-paypal-insights', - untrailingslashit( $module_url ) . '/resources/js/plugins/PayPalInsights.js', + untrailingslashit( $module_url ) . '/assets/js/PayPalInsightsLoader.js', array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ), $asset_version, true diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index 136db8233..bee0c68fd 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -72,6 +72,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { */ private $environment; + /** + * Mapping of payment methods to the PayPal Insights 'payment_method_selected' types. + * + * @var array + */ + private array $payment_method_selected_map; + /** * The WcGateway module URL. * @@ -90,6 +97,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { * @param Settings $settings The settings. * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. * @param Environment $environment The environment object. + * @param array $payment_method_selected_map Mapping of payment methods to the PayPal Insights 'payment_method_selected' types. * @param string $wcgateway_module_url The WcGateway module URL. */ public function __construct( @@ -100,17 +108,19 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { Settings $settings, DCCGatewayConfiguration $dcc_configuration, Environment $environment, + array $payment_method_selected_map, string $wcgateway_module_url ) { - $this->name = AxoGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; - $this->smart_button = $smart_button; - $this->settings = $settings; - $this->dcc_configuration = $dcc_configuration; - $this->environment = $environment; - $this->wcgateway_module_url = $wcgateway_module_url; + $this->name = AxoGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; + $this->settings = $settings; + $this->dcc_configuration = $dcc_configuration; + $this->environment = $environment; + $this->payment_method_selected_map = $payment_method_selected_map; + $this->wcgateway_module_url = $wcgateway_module_url; } @@ -194,18 +204,19 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'email' => 'render', ), 'insights' => array( - 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), - 'session_id' => + 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), + 'session_id' => ( WC()->session && method_exists( WC()->session, 'get_customer_unique_id' ) ) ? substr( md5( WC()->session->get_customer_unique_id() ), 0, 16 ) : '', - 'amount' => array( + 'amount' => array( 'currency_code' => get_woocommerce_currency(), 'value' => ( WC()->cart && method_exists( WC()->cart, 'get_total' ) ) ? WC()->cart->get_total( 'numeric' ) - : null, // Set to null if WC()->cart is null or get_total doesn't exist. + : null, ), + 'payment_method_selected_map' => $this->payment_method_selected_map, ), 'style_options' => array( 'root' => array( diff --git a/modules/ppcp-axo-block/webpack.config.js b/modules/ppcp-axo-block/webpack.config.js index b5db53234..86e2e2087 100644 --- a/modules/ppcp-axo-block/webpack.config.js +++ b/modules/ppcp-axo-block/webpack.config.js @@ -9,7 +9,10 @@ module.exports = { target: 'web', plugins: [ new DependencyExtractionWebpackPlugin() ], entry: { - 'index': path.resolve( './resources/js/index.js' ), + index: path.resolve( './resources/js/index.js' ), + PayPalInsightsLoader: path.resolve( + './resources/js/plugins/PayPalInsightsLoader.js' + ), gateway: path.resolve( './resources/css/gateway.scss' ), }, output: { diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 0bd204672..68667532a 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -53,7 +53,7 @@ class AxoManager { cardView = null; constructor( namespace, axoConfig, ppcpConfig ) { - this.namespace = namespace; + this.namespace = namespace; this.axoConfig = axoConfig; this.ppcpConfig = ppcpConfig; @@ -116,7 +116,7 @@ class AxoManager { this.axoConfig?.insights?.session_id ) { PayPalInsights.config( this.axoConfig?.insights?.client_id, { - debug: true, + debug: axoConfig?.wp_debug === '1', } ); PayPalInsights.setSessionId( this.axoConfig?.insights?.session_id ); PayPalInsights.trackJsLoad(); @@ -159,19 +159,25 @@ class AxoManager { } registerEventHandlers() { + // Payment method change tracking with duplicate prevention + let lastSelectedPaymentMethod = document.querySelector( + 'input[name=payment_method]:checked' + )?.value; this.$( document ).on( 'change', 'input[name=payment_method]', ( ev ) => { - const map = { - 'ppcp-axo-gateway': 'card', - 'ppcp-gateway': 'paypal', - }; - - PayPalInsights.trackSelectPaymentMethod( { - payment_method_selected: map[ ev.target.value ] || 'other', - page_type: 'checkout', - } ); + if ( lastSelectedPaymentMethod !== ev.target.value ) { + PayPalInsights.trackSelectPaymentMethod( { + payment_method_selected: + this.axoConfig?.insights + ?.payment_method_selected_map[ + ev.target.value + ] || 'other', + page_type: 'checkout', + } ); + lastSelectedPaymentMethod = ev.target.value; + } } ); @@ -1155,16 +1161,6 @@ class AxoManager { this.el.axoNonceInput.get().value = nonce; - PayPalInsights.trackEndCheckout( { - amount: this.axoConfig?.insights?.amount, - page_type: 'checkout', - payment_method_selected: 'card', - user_data: { - country: 'US', - is_store_member: false, - }, - } ); - if ( data ) { // Ryan flow. const form = document.querySelector( 'form.woocommerce-checkout' ); diff --git a/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js b/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js new file mode 100644 index 000000000..e21a9d0e4 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js @@ -0,0 +1,99 @@ +import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights'; + +class EndCheckoutTracker { + constructor() { + this.initialize(); + } + + async initialize() { + const axoConfig = window.wc_ppcp_axo_insights_data || {}; + + if ( + axoConfig?.enabled === '1' && + axoConfig?.client_id && + axoConfig?.session_id && + axoConfig?.orderTotal && + axoConfig?.orderCurrency + ) { + try { + await this.waitForPayPalInsight(); + + PayPalInsights.config( axoConfig?.client_id, { + debug: axoConfig?.wp_debug === '1', + } ); + PayPalInsights.setSessionId( axoConfig.session_id ); + PayPalInsights.trackJsLoad(); + + const trackingData = { + amount: { + currency_code: axoConfig?.orderCurrency, + value: axoConfig?.orderTotal, + }, + page_type: 'checkout', + payment_method_selected: + axoConfig?.payment_method_selected_map[ + axoConfig?.paymentMethod + ] || 'other', + user_data: { + country: 'US', + is_store_member: false, + }, + order_id: axoConfig?.orderId, + order_key: axoConfig?.orderKey, + }; + + PayPalInsights.trackEndCheckout( trackingData ); + } catch ( error ) { + console.error( + 'EndCheckoutTracker: Error during tracking:', + error + ); + console.error( 'PayPalInsights object:', window.paypalInsight ); + } + } else { + console.warn( + 'EndCheckoutTracker: Missing required configuration', + { + enabled: axoConfig?.enabled, + hasClientId: !! axoConfig?.client_id, + hasSessionId: !! axoConfig?.session_id, + hasOrderTotal: !! axoConfig?.orderTotal, + hasOrderCurrency: !! axoConfig?.orderCurrency, + } + ); + } + } + + waitForPayPalInsight() { + return new Promise( ( resolve, reject ) => { + // If already loaded, resolve immediately + if ( window.paypalInsight ) { + resolve( window.paypalInsight ); + return; + } + + const timeoutId = setTimeout( () => { + observer.disconnect(); + reject( new Error( 'PayPal Insights script load timeout' ) ); + }, 10000 ); + + // Create MutationObserver to watch for script initialization + const observer = new MutationObserver( () => { + if ( window.paypalInsight ) { + observer.disconnect(); + clearTimeout( timeoutId ); + resolve( window.paypalInsight ); + } + } ); + + observer.observe( document, { + childList: true, + subtree: true, + } ); + } ); + } +} + +document.addEventListener( 'DOMContentLoaded', () => { + new EndCheckoutTracker(); +} ); diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index ebeb0f394..e92cbe3c9 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; +use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter; return array( @@ -64,6 +65,7 @@ return array( $container->get( 'session.handler' ), $container->get( 'wcgateway.settings' ), $container->get( 'onboarding.environment' ), + $container->get( 'axo.insights' ), $container->get( 'wcgateway.settings.status' ), $container->get( 'api.shop.currency.getter' ), $container->get( 'woocommerce.logger.woocommerce' ), @@ -89,6 +91,52 @@ return array( ); }, + // Data needed for the PayPal Insights. + 'axo.insights' => static function ( ContainerInterface $container ): array { + $settings = $container->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + $currency = $container->get( 'api.shop.currency.getter' ); + assert( $currency instanceof CurrencyGetter ); + + return array( + 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'client_id' => ( $settings->has( 'client_id' ) ? $settings->get( 'client_id' ) : null ), + 'session_id' => substr( + method_exists( WC()->session, 'get_customer_unique_id' ) ? + md5( WC()->session->get_customer_unique_id() ) : + '', + 0, + 16 + ), + 'amount' => array( + 'currency_code' => $currency->get(), + ), + 'payment_method_selected_map' => $container->get( 'axo.payment_method_selected_map' ), + 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + ); + }, + + // The mapping of payment methods to the PayPal Insights 'payment_method_selected' types. + 'axo.payment_method_selected_map' => static function ( ContainerInterface $container ): array { + return array( + 'ppcp-axo-gateway' => 'card', + 'ppcp-credit-card-gateway' => 'card', + 'ppcp-gateway' => 'paypal', + 'ppcp-googlepay' => 'google_pay', + 'ppcp-applepay' => 'apple_pay', + 'ppcp-multibanco' => 'other', + 'ppcp-trustly' => 'other', + 'ppcp-p24' => 'other', + 'ppcp-mybank' => 'other', + 'ppcp-ideal' => 'other', + 'ppcp-eps' => 'other', + 'ppcp-blik' => 'other', + 'ppcp-bancontact' => 'other', + 'ppcp-card-button-gateway' => 'card', + ); + }, + /** * The matrix which countries and currency combinations can be used for AXO. */ diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 6fa196719..dbc7c5920 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -52,6 +52,13 @@ class AxoManager { */ private $environment; + /** + * Data needed for the PayPal Insights. + * + * @var array + */ + private array $insights_data; + /** * The Settings status helper. * @@ -95,6 +102,7 @@ class AxoManager { * @param SessionHandler $session_handler The Session handler. * @param Settings $settings The Settings. * @param Environment $environment The environment object. + * @param array $insights_data Data needed for the PayPal Insights. * @param SettingsStatus $settings_status The Settings status helper. * @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop. * @param LoggerInterface $logger The logger. @@ -106,6 +114,7 @@ class AxoManager { SessionHandler $session_handler, Settings $settings, Environment $environment, + array $insights_data, SettingsStatus $settings_status, CurrencyGetter $currency, LoggerInterface $logger, @@ -117,6 +126,7 @@ class AxoManager { $this->session_handler = $session_handler; $this->settings = $settings; $this->environment = $environment; + $this->insights_data = $insights_data; $this->settings_status = $settings_status; $this->currency = $currency; $this->logger = $logger; @@ -161,7 +171,7 @@ class AxoManager { * * @return array */ - private function script_data() { + private function script_data(): array { return array( 'environment' => array( 'is_sandbox' => $this->environment->current_environment() === 'sandbox', @@ -169,20 +179,10 @@ class AxoManager { 'widgets' => array( 'email' => 'render', ), - 'insights' => array( - 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), - 'session_id' => - substr( - method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '', - 0, - 16 - ), - 'amount' => array( - 'currency_code' => $this->currency->get(), - 'value' => WC()->cart->get_total( 'numeric' ), - ), - ), + // The amount is not available when setting the insights data, so we need to merge it here. + 'insights' => ( function( array $data ): array { + $data['amount']['value'] = WC()->cart->get_total( 'numeric' ); + return $data; } )( $this->insights_data ), 'style_options' => array( 'root' => array( 'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '', diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index a0470aeac..c8c316780 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -318,6 +318,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { $endpoint->handle_request(); } ); + + // Enqueue the PayPal Insights script. + add_action( + 'wp_enqueue_scripts', + function () use ( $c ) { + $this->enqueue_paypal_insights_script_on_order_received( $c ); + } + ); + return true; } @@ -429,8 +438,8 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { * @return bool */ private function is_excluded_endpoint(): bool { - // Exclude the Order Pay endpoint. - return is_wc_endpoint_url( 'order-pay' ); + // Exclude the Order Pay and Order Received endpoints. + return is_wc_endpoint_url( 'order-pay' ) || is_wc_endpoint_url( 'order-received' ); } /** @@ -451,4 +460,57 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { $axo_enabled ? 'enabled' : 'disabled' ); } + + /** + * Enqueues PayPal Insights on the Order Received endpoint. + * + * @param ContainerInterface $c The service container. + * @return void + */ + private function enqueue_paypal_insights_script_on_order_received( ContainerInterface $c ): void { + global $wp; + + if ( ! isset( $wp->query_vars['order-received'] ) ) { + return; + } + + $order_id = absint( $wp->query_vars['order-received'] ); + if ( ! $order_id ) { + return; + } + + $order = wc_get_order( $order_id ); + if ( ! $order || ! $order instanceof \WC_Order ) { + return; + } + + $module_url = $c->get( 'axo.url' ); + $asset_version = $c->get( 'ppcp.asset-version' ); + $insights_data = $c->get( 'axo.insights' ); + + wp_register_script( + 'wc-ppcp-paypal-insights-end-checkout', + untrailingslashit( $module_url ) . '/assets/js/TrackEndCheckout.js', + array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ), + $asset_version, + true + ); + + wp_localize_script( + 'wc-ppcp-paypal-insights-end-checkout', + 'wc_ppcp_axo_insights_data', + array_merge( + $insights_data, + array( + 'orderId' => $order_id, + 'orderTotal' => (string) $order->get_total(), + 'orderCurrency' => (string) $order->get_currency(), + 'paymentMethod' => (string) $order->get_payment_method(), + 'orderKey' => (string) $order->get_order_key(), + ) + ) + ); + + wp_enqueue_script( 'wc-ppcp-paypal-insights-end-checkout' ); + } } diff --git a/modules/ppcp-axo/webpack.config.js b/modules/ppcp-axo/webpack.config.js index 95c7f0fc6..1af3ed0bc 100644 --- a/modules/ppcp-axo/webpack.config.js +++ b/modules/ppcp-axo/webpack.config.js @@ -1,39 +1,44 @@ -const path = require('path'); +const path = require( 'path' ); const isProduction = process.env.NODE_ENV === 'production'; const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' ); module.exports = { - devtool: isProduction ? 'source-map' : 'eval-source-map', - mode: isProduction ? 'production' : 'development', - target: 'web', - plugins: [ new DependencyExtractionWebpackPlugin() ], - entry: { - 'boot': path.resolve('./resources/js/boot.js'), - 'styles': path.resolve('./resources/css/styles.scss') - }, - output: { - path: path.resolve(__dirname, 'assets/'), - filename: 'js/[name].js', - }, - module: { - rules: [{ - test: /\.js?$/, - exclude: /node_modules/, - loader: 'babel-loader', - }, - { - test: /\.scss$/, - exclude: /node_modules/, - use: [ - { - loader: 'file-loader', - options: { - name: 'css/[name].css', - } - }, - {loader:'sass-loader'} - ] - }] - } + devtool: isProduction ? 'source-map' : 'eval-source-map', + mode: isProduction ? 'production' : 'development', + target: 'web', + plugins: [ new DependencyExtractionWebpackPlugin() ], + entry: { + boot: path.resolve( './resources/js/boot.js' ), + styles: path.resolve( './resources/css/styles.scss' ), + TrackEndCheckout: path.resolve( + './resources/js/Insights/TrackEndCheckout.js' + ), + }, + output: { + path: path.resolve( __dirname, 'assets/' ), + filename: 'js/[name].js', + }, + module: { + rules: [ + { + test: /\.js?$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.scss$/, + exclude: /node_modules/, + use: [ + { + loader: 'file-loader', + options: { + name: 'css/[name].css', + }, + }, + { loader: 'sass-loader' }, + ], + }, + ], + }, }; From 4a688fb351f9d322795533e9be368d271f735b11 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 30 Oct 2024 22:25:39 +0100 Subject: [PATCH 13/56] Load ACDC for Classic and Block checkouts for subscription products --- modules/ppcp-axo/src/AxoModule.php | 6 +- .../js/advanced-card-checkout-block.js | 74 ++++++++++++------- .../src/AdvancedCardPaymentMethod.php | 1 + modules/ppcp-blocks/src/BlocksModule.php | 6 +- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index a0470aeac..2c9b8c640 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -375,11 +375,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { $dcc_configuration = $c->get( 'wcgateway.configuration.dcc' ); assert( $dcc_configuration instanceof DCCGatewayConfiguration ); + $subscription_helper = $c->get( 'wc-subscriptions.helper' ); + assert( $subscription_helper instanceof SubscriptionHelper ); + return ! is_user_logged_in() && CartCheckoutDetector::has_classic_checkout() && $dcc_configuration->use_fastlane() && ! $this->is_excluded_endpoint() - && is_checkout(); + && is_checkout() + && ! $subscription_helper->cart_contains_subscription(); } /** diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 24c53c5a5..92aa645a6 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -1,30 +1,50 @@ -import {registerPaymentMethod} from '@woocommerce/blocks-registry'; -import {CardFields} from './Components/card-fields'; +import { registerPaymentMethod } from '@woocommerce/blocks-registry'; +import { CardFields } from './Components/card-fields'; -const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); +const config = wc.wcSettings.getSetting( 'ppcp-credit-card-gateway_data' ); +const isUserLoggedIn = config?.scriptData?.is_user_logged_in; +const axoConfig = wc.wcSettings.getSetting( 'ppcp-axo-gateway_data' ); +const axoEnabled = axoConfig !== false; -const Label = ({components, config}) => { - const {PaymentMethodIcons} = components; - return <> - - - -} +const Label = ( { components } ) => { + const { PaymentMethodIcons } = components; + return ( + <> + + + + ); +}; -registerPaymentMethod({ - name: config.id, - label:
+ ); }; +const languagesExample = [ + { + value: '', + label: __( 'Browser language', 'woocommerce-paypal-payments' ), + }, + { value: 'en', label: 'English' }, + { value: 'de', label: 'German' }, + { value: 'es', label: 'Spanish' }, + { value: 'it', label: 'Italian' }, +]; export default PaypalSettings; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Dashboard/TabSettingsElements/ExpertSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Dashboard/TabSettingsElements/ExpertSettings.js index 4405863c4..18e0b9ded 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Dashboard/TabSettingsElements/ExpertSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Dashboard/TabSettingsElements/ExpertSettings.js @@ -1,6 +1,8 @@ import { __ } from '@wordpress/i18n'; import SettingsBlock, { SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, + SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, + SETTINGS_BLOCK_TYPE_SELECT, SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, } from '../../../ReusableComponents/SettingsBlock'; import SettingsCard from '../../../ReusableComponents/SettingsCard'; @@ -49,13 +51,47 @@ const ExpertSettings = ( { updateFormValue, settings } ) => { style={ SETTINGS_BLOCK_STYLING_TYPE_PRIMARY } actionProps={ { type: SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, - callback: updateFormValue, - key: 'payNowExperience', - value: settings.payNowExperience, } } - /> + > + + ); }; +const creditCardExamples = [ + { value: '', label: __( 'Select', 'woocommerce-paypal-payments' ) }, + { + value: 'mastercard', + label: __( 'Mastercard', 'woocommerce-paypal-payments' ), + }, + { value: 'visa', label: __( 'Visa', 'woocommerce-paypal-payments' ) }, + { + value: 'amex', + label: __( 'American Express', 'woocommerce-paypal-payments' ), + }, + { value: 'jcb', label: __( 'JCB', 'woocommerce-paypal-payments' ) }, + { + value: 'diners-club', + label: __( 'Diners Club', 'woocommerce-paypal-payments' ), + }, +]; + export default ExpertSettings; From a5ae046021c98201ae5faf56b66530d1cc02d97f Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 12 Nov 2024 15:01:29 +0100 Subject: [PATCH 20/56] Add components --- modules/ppcp-settings/node_modules/.gitkeep | 0 .../reusable-components/_button.scss | 2 - .../_onboarding-header.scss | 2 +- .../Screens/Onboarding/Onboarding.js | 39 +- .../Screens/Onboarding/StepCompleteSetup.js | 43 +++ .../Screens/Onboarding/StepWelcome.js | 343 +++++++++++------- .../Screens/Onboarding/availableSteps.js | 8 +- node_modules/.gitkeep | 0 8 files changed, 274 insertions(+), 163 deletions(-) delete mode 100644 modules/ppcp-settings/node_modules/.gitkeep create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js delete mode 100644 node_modules/.gitkeep diff --git a/modules/ppcp-settings/node_modules/.gitkeep b/modules/ppcp-settings/node_modules/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss index dbf41614c..e33085763 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss @@ -12,8 +12,6 @@ button.components-button, a.components-button { border-radius: 2px; padding: 14px 17px; height: auto; - - } &.is-primary { diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss index d6ea2b3f4..b73908da3 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss @@ -29,7 +29,7 @@ &__description { @include font(14, 22, 400); - margin: 0; + margin: 0 20%; text-align: center; } } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index 587d59253..a91eabb48 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -1,8 +1,7 @@ import Container from '../../ReusableComponents/Container'; import { useOnboardingStep } from '../../../data'; import { getSteps } from './availableSteps'; -import {__} from "@wordpress/i18n"; -import Navigation from "../../ReusableComponents/Navigation"; +import Navigation from '../../ReusableComponents/Navigation'; const getCurrentStep = ( requestedStep, steps ) => { const isValidStep = ( step ) => @@ -22,24 +21,24 @@ const Onboarding = () => { const CurrentStepComponent = getCurrentStep( step, steps ); return ( - <> - - -
- -
-
- + <> + + +
+ +
+
+ ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js new file mode 100644 index 000000000..90650be72 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js @@ -0,0 +1,43 @@ +import OnboardingHeader from '../../ReusableComponents/OnboardingHeader'; +import { __ } from '@wordpress/i18n'; +import { Button, Icon } from '@wordpress/components'; + +const StepCompleteSetup = ( { + setStep, + currentStep, + stepperOrder, + setCompleted, +} ) => { + const ButtonIcon = () => ; + + return ( +
+ +
+
+ +
+
+
+ ); +}; + +export default StepCompleteSetup; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js index 759fe28bc..7eb276361 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -19,7 +19,7 @@ const StepWelcome = ( { setStep, currentStep, setCompleted } ) => { 'woocommerce-paypal-payments' ) } description={ __( - 'Your all-in-one integration for PayPal checkout solutions that enable buyers
to pay via PayPal, Pay Later, all major credit/debit cards, Apple Pay, Google Pay, and more.', + 'Your all-in-one integration for PayPal checkout solutions that enable buyers to pay via PayPal, Pay Later, all major credit/debit cards, Apple Pay, Google Pay, and more.', 'woocommerce-paypal-payments' ) } /> @@ -100,144 +100,209 @@ const WelcomeDocs = () => { 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' ); - return ( -
-

{ __( `Want to know more about PayPal Payments?`, 'woocommerce-paypal-payments' ) }

-
-
- 1', 'woocommerce-paypal-payments' ) } - description={ __( - 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', - 'woocommerce-paypal-payments' - ) } - /> - - Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> -
-
- - 1', 'woocommerce-paypal-payments' ) } - description={ sprintf( - // translators: %s: Link to PayPal REST application guide - __( - 'Style the credit card fields to match your own style. Includes advanced processing with risk management, 3D Secure, fraud protection options, and chargeback protection. Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - 1', 'woocommerce-paypal-payments' ) } - description={ sprintf( - // translators: %s: Link to PayPal REST application guide - __( - 'Accept Apple Pay on eligible devices and Google Pay through mobile and web. Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - 1', 'woocommerce-paypal-payments' ) } - description={ sprintf( - // translators: %s: Link to PayPal REST application guide - __( - 'Seamless payments for customers across the globe using their preferred payment methods. Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> - - 1', 'woocommerce-paypal-payments' ) } - description={ sprintf( - // translators: %s: Link to PayPal REST application guide - __( - 'Speed up guest checkout with Fatslane. Link a customer\'s email address to their payment details. Learn more', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ) } - /> -
-
-

-
- ); + return ( +
+

+ { __( + `Want to know more about PayPal Payments?`, + 'woocommerce-paypal-payments' + ) } +

+
+
+ 1', + 'woocommerce-paypal-payments' + ) } + description={ __( + 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', + 'woocommerce-paypal-payments' + ) } + /> + + Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> +
+
+ + 1', + 'woocommerce-paypal-payments' + ) } + description={ sprintf( + // translators: %s: Link to PayPal REST application guide + __( + 'Style the credit card fields to match your own style. Includes advanced processing with risk management, 3D Secure, fraud protection options, and chargeback protection. Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + 1', + 'woocommerce-paypal-payments' + ) } + description={ sprintf( + // translators: %s: Link to PayPal REST application guide + __( + 'Accept Apple Pay on eligible devices and Google Pay through mobile and web. Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + 1', + 'woocommerce-paypal-payments' + ) } + description={ sprintf( + // translators: %s: Link to PayPal REST application guide + __( + 'Seamless payments for customers across the globe using their preferred payment methods. Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> + + 1', + 'woocommerce-paypal-payments' + ) } + description={ sprintf( + // translators: %s: Link to PayPal REST application guide + __( + 'Speed up guest checkout with Fatslane. Link a customer\'s email address to their payment details. Learn more', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' + ) } + /> +
+
+

+
+ ); }; export default StepWelcome; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js index 32034ec57..a5555180d 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js @@ -1,9 +1,15 @@ import StepWelcome from './StepWelcome'; import StepBusiness from './StepBusiness'; import StepProducts from './StepProducts'; +import StepCompleteSetup from './StepCompleteSetup'; export const getSteps = ( flags ) => { - const allSteps = [ StepWelcome, StepBusiness, StepProducts ]; + const allSteps = [ + StepWelcome, + StepBusiness, + StepProducts, + StepCompleteSetup, + ]; if ( ! flags.canUseCasualSelling ) { return allSteps.filter( ( step ) => step !== StepBusiness ); diff --git a/node_modules/.gitkeep b/node_modules/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From e25a773b0e985a1d5bfe88efa35b3b5a6ddc1387 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 12 Nov 2024 15:10:55 +0100 Subject: [PATCH 21/56] Fix PHPCS errors --- .../src/AxoBlockPaymentMethod.php | 24 +++++++++---------- modules/ppcp-axo/src/Assets/AxoManager.php | 14 +++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index d99eae4c0..373c61023 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -130,7 +130,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { $this->environment = $environment; $this->wcgateway_module_url = $wcgateway_module_url; $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; - $this->enabled_shipping_locations = $enabled_shipping_locations; + $this->enabled_shipping_locations = $enabled_shipping_locations; } /** @@ -206,13 +206,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { } return array( - 'environment' => array( + 'environment' => array( 'is_sandbox' => $this->environment->current_environment() === 'sandbox', ), - 'widgets' => array( + 'widgets' => array( 'email' => 'render', ), - 'insights' => array( + 'insights' => array( 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), 'session_id' => @@ -226,8 +226,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { : null, // Set to null if WC()->cart is null or get_total doesn't exist. ), ), - 'allowed_cards' => $this->supported_country_card_type_matrix, - 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'enabled_shipping_locations' => $this->enabled_shipping_locations, 'style_options' => array( 'root' => array( @@ -248,8 +248,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'focusBorderColor' => $this->settings->has( 'axo_style_input_focus_border_color' ) ? $this->settings->get( 'axo_style_input_focus_border_color' ) : '', ), ), - 'name_on_card' => $this->dcc_configuration->show_name_on_card(), - 'woocommerce' => array( + 'name_on_card' => $this->dcc_configuration->show_name_on_card(), + 'woocommerce' => array( 'states' => array( 'US' => WC()->countries->get_states( 'US' ), 'CA' => WC()->countries->get_states( 'CA' ), @@ -263,10 +263,10 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ), ), ), - 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', - 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), - 'merchant_country' => WC()->countries->get_base_country(), + 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', + 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), + 'merchant_country' => WC()->countries->get_base_country(), ); } } diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 402193799..d2d4351a7 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -138,7 +138,7 @@ class AxoManager { $this->logger = $logger; $this->wcgateway_module_url = $wcgateway_module_url; $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; - $this->enabled_shipping_locations = $enabled_shipping_locations; + $this->enabled_shipping_locations = $enabled_shipping_locations; } /** @@ -201,8 +201,8 @@ class AxoManager { 'value' => WC()->cart->get_total( 'numeric' ), ), ), - 'allowed_cards' => $this->supported_country_card_type_matrix, - 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), + 'allowed_cards' => $this->supported_country_card_type_matrix, + 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'enabled_shipping_locations' => $this->enabled_shipping_locations, 'style_options' => array( 'root' => array( @@ -238,10 +238,10 @@ class AxoManager { 'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ), ), ), - 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', - 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ), - 'merchant_country' => WC()->countries->get_base_country(), + 'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '', + 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ), + 'merchant_country' => WC()->countries->get_base_country(), ); } From 5892a0fb2fa19f1ce975ea8684a82d0dedab0690 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 12 Nov 2024 15:47:59 +0100 Subject: [PATCH 22/56] Add external icon to button --- .../ppcp-settings/images/icon-external.svg | 4 ++++ .../Screens/Onboarding/StepCompleteSetup.js | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 modules/ppcp-settings/images/icon-external.svg diff --git a/modules/ppcp-settings/images/icon-external.svg b/modules/ppcp-settings/images/icon-external.svg new file mode 100644 index 000000000..230942d95 --- /dev/null +++ b/modules/ppcp-settings/images/icon-external.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js index 90650be72..ccbcab149 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js @@ -8,7 +8,28 @@ const StepCompleteSetup = ( { stepperOrder, setCompleted, } ) => { - const ButtonIcon = () => ; + const ButtonIcon = () => ( + ( + + + + + ) } + /> + ); return (
From 4fe7117fc91320a1817286672e2bba6c4af883f2 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 13 Nov 2024 10:50:35 +0100 Subject: [PATCH 23/56] Fix PHPCS errors --- .../src/AxoBlockPaymentMethod.php | 40 +++++++++---------- modules/ppcp-axo/src/Assets/AxoManager.php | 22 +++++----- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index 6e1107698..f1ed91527 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -96,16 +96,16 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { /** * AdvancedCardPaymentMethod constructor. * - * @param string $module_url The URL of this module. - * @param string $version The assets version. - * @param WC_Payment_Gateway $gateway Credit card gateway. - * @param SmartButtonInterface|callable $smart_button The smart button script loading - * handler. - * @param Settings $settings The settings. - * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. - * @param Environment $environment The environment object. + * @param string $module_url The URL of this module. + * @param string $version The assets version. + * @param WC_Payment_Gateway $gateway Credit card gateway. + * @param SmartButtonInterface|callable $smart_button The smart button script loading + * handler. + * @param Settings $settings The settings. + * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. + * @param Environment $environment The environment object. + * @param string $wcgateway_module_url The WcGateway module URL. * @param array $payment_method_selected_map Mapping of payment methods to the PayPal Insights 'payment_method_selected' types. - * @param string $wcgateway_module_url The WcGateway module URL. * @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations. */ public function __construct( @@ -120,17 +120,17 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { array $payment_method_selected_map, array $enabled_shipping_locations ) { - $this->name = AxoGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; - $this->smart_button = $smart_button; - $this->settings = $settings; - $this->dcc_configuration = $dcc_configuration; - $this->environment = $environment; - $this->wcgateway_module_url = $wcgateway_module_url; + $this->name = AxoGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; + $this->settings = $settings; + $this->dcc_configuration = $dcc_configuration; + $this->environment = $environment; + $this->wcgateway_module_url = $wcgateway_module_url; $this->payment_method_selected_map = $payment_method_selected_map; - $this->enabled_shipping_locations = $enabled_shipping_locations; + $this->enabled_shipping_locations = $enabled_shipping_locations; } /** @@ -212,7 +212,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'widgets' => array( 'email' => 'render', ), - 'insights' => array( + 'insights' => array( 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), 'session_id' => diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 9ee15ffaa..8b9a59aeb 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -130,16 +130,16 @@ class AxoManager { array $enabled_shipping_locations ) { - $this->module_url = $module_url; - $this->version = $version; - $this->session_handler = $session_handler; - $this->settings = $settings; - $this->environment = $environment; - $this->insights_data = $insights_data; - $this->settings_status = $settings_status; - $this->currency = $currency; - $this->logger = $logger; - $this->wcgateway_module_url = $wcgateway_module_url; + $this->module_url = $module_url; + $this->version = $version; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->environment = $environment; + $this->insights_data = $insights_data; + $this->settings_status = $settings_status; + $this->currency = $currency; + $this->logger = $logger; + $this->wcgateway_module_url = $wcgateway_module_url; $this->enabled_shipping_locations = $enabled_shipping_locations; } @@ -190,7 +190,7 @@ class AxoManager { 'email' => 'render', ), // The amount is not available when setting the insights data, so we need to merge it here. - 'insights' => ( function( array $data ): array { + 'insights' => ( function( array $data ): array { $data['amount']['value'] = WC()->cart->get_total( 'numeric' ); return $data; } )( $this->insights_data ), 'enabled_shipping_locations' => $this->enabled_shipping_locations, From e455561eb8bad3116f0e965634246f07faf6ff81 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 13 Nov 2024 11:24:31 +0100 Subject: [PATCH 24/56] Fix parameters being passed in the wrong order --- modules/ppcp-axo-block/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-axo-block/services.php b/modules/ppcp-axo-block/services.php index 3d899ac03..810634586 100644 --- a/modules/ppcp-axo-block/services.php +++ b/modules/ppcp-axo-block/services.php @@ -37,8 +37,8 @@ return array( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.configuration.dcc' ), $container->get( 'onboarding.environment' ), - $container->get( 'axo.payment_method_selected_map' ), $container->get( 'wcgateway.url' ), + $container->get( 'axo.payment_method_selected_map' ), $container->get( 'axo.shipping-wc-enabled-locations' ) ); }, From 0b73f672a32bc6dec4ffcb239ecfa6a196030d25 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 13 Nov 2024 14:40:35 +0100 Subject: [PATCH 25/56] Restore gitkeep files --- modules/ppcp-settings/node_modules/.gitkeep | 0 node_modules/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 modules/ppcp-settings/node_modules/.gitkeep create mode 100644 node_modules/.gitkeep diff --git a/modules/ppcp-settings/node_modules/.gitkeep b/modules/ppcp-settings/node_modules/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/node_modules/.gitkeep b/node_modules/.gitkeep new file mode 100644 index 000000000..e69de29bb From b23a9a0fabd75f21ac8e351acd0c7c39378e0ba5 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 13 Nov 2024 17:29:11 +0100 Subject: [PATCH 26/56] Finish settings page --- modules/ppcp-settings/package.json | 3 +- .../ppcp-settings/resources/css/_global.scss | 15 +- .../ppcp-settings/resources/css/_mixins.scss | 17 +- .../reusable-components/_fields.scss | 50 ++- .../_payment-method-modal.scss | 5 - .../reusable-components/_text-control.scss | 17 - .../screens/dashboard/_tab-settings.scss | 326 ++++++++++-------- .../ppcp-settings/resources/css/style.scss | 3 +- .../Components/ReusableComponents/Fields.js | 26 +- .../ReusableComponents/SettingsBlock.js | 126 +++---- .../Screens/Dashboard/Modals/ModalPayPal.js | 31 +- .../Screens/Dashboard/TabSettings.js | 6 +- .../Blocks/OtherSettings.js | 66 ++++ .../Blocks/PaypalSettings.js | 9 +- .../TabSettingsElements/Blocks/Sandbox.js | 211 +++++++++--- .../Blocks/SavePaymentMethods.js | 3 +- .../Blocks/Troubleshooting.js | 79 ++--- .../TabSettingsElements/ExpertSettings.js | 56 +-- modules/ppcp-settings/yarn.lock | 75 +++- woocommerce-paypal-payments.php | 1 - 20 files changed, 692 insertions(+), 433 deletions(-) delete mode 100644 modules/ppcp-settings/resources/css/components/reusable-components/_text-control.scss create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Dashboard/TabSettingsElements/Blocks/OtherSettings.js diff --git a/modules/ppcp-settings/package.json b/modules/ppcp-settings/package.json index ccdd55043..126b3e754 100644 --- a/modules/ppcp-settings/package.json +++ b/modules/ppcp-settings/package.json @@ -13,6 +13,7 @@ "@wordpress/scripts": "^30.3.0" }, "dependencies": { - "@woocommerce/settings": "^1.0.0" + "@woocommerce/settings": "^1.0.0", + "react-select": "^5.8.3" } } diff --git a/modules/ppcp-settings/resources/css/_global.scss b/modules/ppcp-settings/resources/css/_global.scss index cd62ab8ac..209dfb2db 100644 --- a/modules/ppcp-settings/resources/css/_global.scss +++ b/modules/ppcp-settings/resources/css/_global.scss @@ -40,6 +40,9 @@ font-style: italic; } +@mixin primaryFont{ + font-family: "PayPalPro", sans-serif; +} * { font-family: "PayPalPro", sans-serif; @@ -56,16 +59,4 @@ a:not(.button) { color: $color-blueberry; } -input[type='text'] { - padding: 7px 11px; - @include font(14, 20, 400); -} -select { - padding: 7px 27px 7px 11px; - @include font(14, 20, 400); -} - -.components-form-toggle.is-checked > .components-form-toggle__track { - background-color: $color-blueberry; -} diff --git a/modules/ppcp-settings/resources/css/_mixins.scss b/modules/ppcp-settings/resources/css/_mixins.scss index aae30a2a6..d2fac6cd2 100644 --- a/modules/ppcp-settings/resources/css/_mixins.scss +++ b/modules/ppcp-settings/resources/css/_mixins.scss @@ -7,7 +7,7 @@ } } -@mixin hide-input-field(){ +@mixin hide-input-field() { width: 100%; height: 100%; position: absolute; @@ -18,7 +18,7 @@ border-radius: 0; } -@mixin fake-input-field($border-radius:0){ +@mixin fake-input-field($border-radius:0) { width: 20px; height: 20px; border: 1px solid $color-gray-600; @@ -27,8 +27,19 @@ border-radius: $border-radius; } -@mixin small-button{ +@mixin small-button { border-radius: 2px; padding: 6px 12px; @include font(13, 20, 400); } + +@mixin vertical-layout-event-gap($gap:0) { + display: flex; + flex-direction: column; + gap: $gap; +} + +@mixin horizontal-layout-even-gap($gap:0) { + display: flex; + gap: $gap; +} diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss index 9feda7962..f548ab12f 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss @@ -55,10 +55,56 @@ &__radio-description { @include font(14, 20, 400); - margin: 4px 0 0 0; - color:$color-gray-800; + margin: 0; + color: $color-gray-800; + } + + &__radio-content-additional { + padding-left: 38px; + padding-top: 18px; + } +} + +.components-base-control { + &__label { + color: $color-gray-900; + @include font(13, 16, 600); + text-transform: none; + } + + &__input { + border: 1px solid $color-gray-700; + border-radius: 2px; + box-shadow: none; + + &:focus { + border-color: $color-blueberry; + } } } +input[type='text'] { + padding: 7px 11px; + @include font(14, 20, 400); + @include primaryFont; + border-radius: 2px; +} +select { + padding: 7px 27px 7px 11px; + @include font(14, 20, 400); +} + +.components-form-toggle.is-checked > .components-form-toggle__track { + background-color: $color-blueberry; +} + +.ppcp-r-vertical-text-control { + .components-base-control__field { + display: flex; + flex-direction: column; + gap: 8px; + margin: 0; + } +} diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss index d0ce72e1a..41455053a 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss @@ -72,11 +72,6 @@ margin: 0; border-color: $color-gray-700; } - - label { - @include font(14, 16, 400); - color: $color-gray-900; - } } &__field-rows { diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_text-control.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_text-control.scss deleted file mode 100644 index 3b476f89c..000000000 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_text-control.scss +++ /dev/null @@ -1,17 +0,0 @@ -.components-base-control { - &__label { - color: $color-gray-900; - @include font(13, 16, 600); - margin: 0 0 8px 0; - text-transform: none; - } - - &__input { - border: 1px solid $color-gray-700; - border-radius: 2px; - box-shadow: none; - &:focus{ - border-color:$color-blueberry; - } - } -} diff --git a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss b/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss index c13c2f50e..197d575ea 100644 --- a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss +++ b/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss @@ -1,43 +1,39 @@ +// Global settings styles .ppcp-r-settings { - display: flex; - flex-direction: column; - gap: 48px; + @include vertical-layout-event-gap(48px); } -.ppcp-r-settings-card { - &.ppcp-r-settings-card--expert-settings { - .ppcp-r-settings-card__header { - margin-bottom: 48px; - } - > .ppcp-r-settings-card__content > .ppcp-r-settings-block:not(.ppcp-r-settings-block--tertiary) { - padding-bottom: 32px; - margin-bottom: 32px; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border-bottom: none; - } +.ppcp-r-settings-card__content { + > .ppcp-r-settings-block { + &:not(:last-child) { + border-bottom: 1.5px solid $color-gray-700; } } } .ppcp-r-settings-block { - &--primary { - padding-bottom: 48px; - margin-bottom: 48px; - border-bottom: 1px solid $color-gray-700; + .ppcp-r-settings-block__header { + display: flex; + gap: 48px; + &-inner { + display: flex; + flex-direction: column; + gap: 4px; + } + } + + &__action { + margin-left: auto; + } + + &--primary { > .ppcp-r-settings-block__header { .ppcp-r-settings-block__title { @include font(16, 20, 700); color: $color-black; } - - .ppcp-r-settings-block__description { - margin-top: 8px; - } } } @@ -45,8 +41,7 @@ > .ppcp-r-settings-block__header { .ppcp-r-settings-block__title { @include font(16, 20, 600); - color: $color-gray-900; - margin-bottom: 8px; + color: $color-gray-800; } } } @@ -65,64 +60,7 @@ } } - &--separated-settings { - > .ppcp-r-settings-block__content { - gap: 0; - - > .ppcp-r-settings-block { - border-bottom: 1px solid $color-gray-500; - padding-bottom: 32px; - margin-bottom: 32px; - - &:last-child { - border: none; - padding: 0; - margin: 0; - } - } - } - } - - &__title { - display: flex; - align-items: center; - gap: 12px; - } - - &--toggle-content { - > .ppcp-r-settings-block__header { - align-items: center; - cursor: pointer; - - * { - user-select: none; - } - - .ppcp-r-settings-block__action { - transition: 0.2s transform ease-out; - } - - &:hover { - .ppcp-r-settings-block__action { - transform: scale(1.2); - } - } - } - } - - &--button { - > .ppcp-r-settings-block__header { - align-items: center; - } - } - - &--content-visible { - .ppcp-r-settings-block__toggle-content { - transform: rotate(180deg); - } - } - - &__description { + .ppcp-r-settings-block__description { margin: 0; @include font(14, 20, 400); color: $color-gray-800; @@ -136,55 +74,77 @@ } } - &__action { - margin-left: auto; + // Types + &--toggle-content { + &.ppcp-r-settings-block--content-visible { + .ppcp-r-settings-block__toggle-content { + transform: rotate(180deg); + } + } + + .ppcp-r-settings-block__header { + user-select: none; + + &:hover { + cursor: pointer; + } + } + } + + &--sandbox-connected { + .ppcp-r-settings-block__content { + margin-top: 24px; + } button.is-secondary { - padding: 6px 12px; - min-width: 166px; - @include font(13, 20, 400); - justify-content: center; + @include small-button; + } + + .ppcp-r-connection-status__data { + margin-bottom: 20px; } } - &__toggle { - .components-form-toggle { - order: 1; + &--expert-rdb{ + @include vertical-layout-event-gap(24px); + } + &--connect-sandbox { + button.components-button { + @include small-button; + } + + .ppcp-r__radio-content-additional { + .ppcp-r-vertical-text-control { + width: 100%; + } + + @include vertical-layout-event-gap(24px); + align-items: flex-start; + + input[type='text'] { + width: 100%; + } } } - &__content { - display: flex; - flex-direction: column; - gap: 32px; - margin-top: 32px; - } - - &__header { - display: flex; - gap: 48px; - - &-inner { - display: flex; - flex-direction: column; + &--troubleshooting { + > .ppcp-r-settings-block__content > *:not(:last-child) { + padding-bottom: 32px; + margin-bottom: 32px; + border-bottom: 1px solid $color-gray-500; } } - &__mismatch-wrapper { - display: flex; - flex-direction: column; - gap: 24px; - } - - &--order-intent { - .ppcp-r-settings-block__content .ppcp-r-settings-block__title { - font-size: 14px; - margin-bottom: 4px; - color: $color-gray-800; + &--settings{ + > .ppcp-r-settings-block__content > *:not(:last-child){ + padding-bottom: 32px; + margin-bottom: 32px; + border-bottom: 1px solid $color-gray-500; } } - input[type='text'], select { + // Fields + input[type='text'] { border-color: $color-gray-700; width: 282px; max-width: 100%; @@ -197,37 +157,75 @@ } } - .ppcp-r-select-no-value-assigned > div > select { - color: $color-gray-600; - } - - &__sandbox { - button.is-secondary { - @include small-button; + .ppcp-r { + &__radio-wrapper { + align-items: flex-start; + gap: 12px; } - .ppcp-r-connection-status__data { - margin-bottom: 24px; + &__radio-content { + display: flex; + flex-direction: column; + gap: 4px; + + label { + font-weight: 600; + } + } + + &__radio-content-additional { + padding-left: 32px; } } - - .ppcp-r__radio-wrapper { - align-items: flex-start; - } - - .ppcp-r__radio-content { - label { - font-weight: 600; + // MultiSelect control + .ppcp-r { + &__control { + border-radius: 2px; + border-color: $color-gray-700; + width: 282px; + min-height: auto; + padding: 0; } - } - .ppcp-r__radio { - padding-top: 2px; + &__input-container { + padding: 0; + margin: 0; + } + + &__value-container { + padding: 0 0 0 7px; + } + + &__indicator { + padding: 5px; + } + + &__indicator-separator { + display: none; + } + + &__value-container--has-value { + .ppcp-r__single-value { + color: $color-gray-800; + } + } + + &__placeholde, &__single-value { + @include font(13, 20, 400); + } + + &__option { + &--is-selected { + background-color: $color-gray-200; + } + } } } +// Special settings styles +// Hooks table .ppcp-r-table { &__hooks-url { width: 70%; @@ -264,3 +262,51 @@ padding-bottom: 4px; } } + +// Common settings have 48px margin&padding bottom between blocks +.ppcp-r-settings-card--common-settings .ppcp-r-settings-card__content { + > .ppcp-r-settings-block { + &:not(:last-child) { + padding-bottom: 48px; + margin-bottom: 48px; + } + } +} + +// Expert settings have 32px margin&padding bottom between blocks +.ppcp-r-settings-card--expert-settings .ppcp-r-settings-card__content { + > .ppcp-r-settings-block { + &:not(:last-child) { + padding-bottom: 32px; + margin-bottom: 32px; + } + } +} + + +// Order intent block has 32px gap and no lines in between +// Save payment methods block has 32px gap and no lines in between +.ppcp-r-settings-block { + &--order-intent, &--save-payment-methods { + @include vertical-layout-event-gap(32px); + + > .ppcp-r-settings-block__content { + @include vertical-layout-event-gap(32px); + } + } +} + + +// Most primary settings block in the expert settings have 32px space after description +.ppcp-r-settings-block--toggle-content { + .ppcp-r-settings-block__content { + margin-top: 32px; + } +} + +// Common settings have actions aligned top with the text, Expert settings have actions alligned middle with the text +.ppcp-r-settings-card--expert-settings { + .ppcp-r-settings-block__header { + align-items: center; + } +} diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index 9c3b6822a..1476e7746 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -6,7 +6,6 @@ @import './components/reusable-components/onboarding-header'; @import './components/reusable-components/button'; @import './components/reusable-components/settings-toggle-block'; - @import './components/reusable-components/text-control'; @import './components/reusable-components/separator'; @import './components/reusable-components/payment-method-icons'; @import "./components/reusable-components/payment-method-item"; @@ -20,7 +19,7 @@ @import './components/screens/onboarding'; @import './components/screens/dashboard/tab-dashboard'; @import './components/screens/dashboard/tab-payment-methods'; - @import './components/screens/dashboard/tab-settings'; + @import 'components/screens/dashboard/tab-settings'; } @import './components/reusable-components/payment-method-modal'; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields.js index c18c0ae44..0bcedc74b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields.js @@ -45,21 +45,25 @@ export const PayPalRdbWithContent = ( props ) => { } return ( -
- -
- - { props.description && ( -

- { props.description } -

- ) } - { props.children && ( +
+
+ +
+ + { props.description && ( +

+ { props.description } +

+ ) } +
+
+ { props?.toggleAdditionalContent && + props.children && + props.value === props.currentValue && (
{ props.children }
) } -
); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js index 87ca4a8f9..d23860b38 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js @@ -1,6 +1,7 @@ -import { Button, ToggleControl, SelectControl } from '@wordpress/components'; +import { Button, ToggleControl, TextControl } from '@wordpress/components'; import data from '../../utils/data'; import { useState } from '@wordpress/element'; +import Select, { components } from 'react-select'; export const SETTINGS_BLOCK_TYPE_EMPTY = 'empty'; export const SETTINGS_BLOCK_TYPE_TOGGLE = 'toggle'; @@ -65,72 +66,65 @@ const SettingsBlock = ( { dangerouslySetInnerHTML={ { __html: description } } />
-
- { actionProps?.type === SETTINGS_BLOCK_TYPE_TOGGLE && ( - - actionProps?.callback( - actionProps?.key, - newValue - ) - } - /> - ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_INPUT && ( -
- + { actionProps?.type !== SETTINGS_BLOCK_TYPE_EMPTY && ( +
+ { actionProps?.type === SETTINGS_BLOCK_TYPE_TOGGLE && ( + actionProps?.callback( actionProps?.key, - e.target.value + newValue ) } /> -
- ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_BUTTON && ( - - ) } - { actionProps?.type === - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT && ( -
- { data().getImage( 'icon-arrow-down.svg' ) } -
- ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_SELECT && ( - - actionProps?.callback && - actionProps.callback( - actionProps?.key, - newValue - ) - } - /> - ) } -
+ ) } + { actionProps?.type === SETTINGS_BLOCK_TYPE_INPUT && ( + <> + + actionProps?.callback( + actionProps?.key, + newValue + ) + } + /> + + ) } + { actionProps?.type === SETTINGS_BLOCK_TYPE_BUTTON && ( + + ) } + { actionProps?.type === + SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT && ( +
+ { data().getImage( 'icon-arrow-down.svg' ) } +
+ ) } + { actionProps?.type === SETTINGS_BLOCK_TYPE_SELECT && ( + - updateFormValue( - 'checkoutPageTitle', - e.target.value - ) + onChange={ ( newValue ) => + updateFormValue( 'checkoutPageTitle', newValue ) } />
-