From 42805c4fb5836107b0beb90425206ca481e9677a Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 16 Oct 2024 12:51:26 +0200 Subject: [PATCH] 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 );