diff --git a/modules/ppcp-card-fields/services.php b/modules/ppcp-card-fields/services.php
index 7d8427c68..005b4d07e 100644
--- a/modules/ppcp-card-fields/services.php
+++ b/modules/ppcp-card-fields/services.php
@@ -61,6 +61,7 @@ return array(
'LT',
'LU',
'MT',
+ 'MX',
'NL',
'PL',
'PT',
diff --git a/modules/ppcp-local-alternative-payment-methods/services.php b/modules/ppcp-local-alternative-payment-methods/services.php
index 5408288c0..8a1eef931 100644
--- a/modules/ppcp-local-alternative-payment-methods/services.php
+++ b/modules/ppcp-local-alternative-payment-methods/services.php
@@ -73,7 +73,8 @@ return array(
$container->get( 'wcgateway.settings' ),
$container->get( 'api.endpoint.partners' ),
$container->get( 'settings.flag.is-connected' ),
- $container->get( 'api.helper.failure-registry' )
+ $container->get( 'api.helper.failure-registry' ),
+ $container->get( 'woocommerce.logger.woocommerce' )
);
},
'ppcp-local-apms.bancontact.wc-gateway' => static function ( ContainerInterface $container ): BancontactGateway {
diff --git a/modules/ppcp-settings/images/icon-button-oxxo.svg b/modules/ppcp-settings/images/icon-button-oxxo.svg
new file mode 100644
index 000000000..c271de3c0
--- /dev/null
+++ b/modules/ppcp-settings/images/icon-button-oxxo.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PaymentFlow.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PaymentFlow.js
index 24ee7b33d..ce1125bbc 100644
--- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PaymentFlow.js
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PaymentFlow.js
@@ -42,7 +42,8 @@ const PaymentFlow = ( {
/>
);
}
-const description = useAcdc ? optionalDescription : '';
+ const description =
+ useAcdc && 'MX' !== storeCountry ? optionalDescription : '';
return (
{
+ const { storeCountry } = useWooSettings();
+
+ // Determine which icons to display based on the country code.
+ const imageBadges =
+ storeCountry === 'MX'
+ ? [ 'icon-button-oxxo.svg' ]
+ : [
+ // 'icon-button-sepa.svg', // Enable this when the SEPA-Gateway is ready.
+ 'icon-button-ideal.svg',
+ 'icon-button-blik.svg',
+ 'icon-button-bancontact.svg',
+ ];
+
return (
}
description={ __(
'Seamless payments for customers across the globe using their preferred payment methods.',
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepPaymentMethods.js
index 5d052c03f..1863944bf 100644
--- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepPaymentMethods.js
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepPaymentMethods.js
@@ -10,13 +10,13 @@ import PaymentFlow from '../Components/PaymentFlow';
const StepPaymentMethods = () => {
const { optionalMethods, setOptionalMethods } =
OnboardingHooks.useOptionalPaymentMethods();
- const { ownBrandOnly } = CommonHooks.useWooSettings();
+ const { ownBrandOnly, storeCountry } = CommonHooks.useWooSettings();
const { isCasualSeller } = OnboardingHooks.useBusiness();
const { canUseCardPayments } = OnboardingHooks.useFlags();
const optionalMethodTitle = useMemo( () => {
- // The BCDC flow does not show a title. !acdc does not show a title.
- if ( isCasualSeller || ! canUseCardPayments ) {
+ // The BCDC flow does not show a title. !acdc does not show a title. Mexico does not show a title.
+ if ( isCasualSeller || ! canUseCardPayments || 'MX' === storeCountry ) {
return null;
}
@@ -24,7 +24,7 @@ const StepPaymentMethods = () => {
'Available with additional application',
'woocommerce-paypal-payments'
);
- }, [ isCasualSeller, canUseCardPayments ] );
+ }, [ isCasualSeller, canUseCardPayments, storeCountry ] );
const methodChoices = [
{
@@ -34,7 +34,7 @@ const StepPaymentMethods = () => {
},
{
title:
- ownBrandOnly || ! canUseCardPayments
+ ownBrandOnly || ! canUseCardPayments || 'MX' === storeCountry
? __(
'No thanks, I prefer to use a different provider for local payment methods',
'woocommerce-paypal-payments'
@@ -87,7 +87,9 @@ const OptionalMethodDescription = () => {
return (
{
const { storeCountry, ownBrandOnly } = CommonHooks.useWooSettings();
const { canUseCardPayments, canUseFastlane } = OnboardingHooks.useFlags();
+ const { isCasualSeller } = OnboardingHooks.useBusiness();
const { icons } = usePaymentConfig(
storeCountry,
@@ -23,7 +24,7 @@ const StepWelcome = ( { setStep, currentStep } ) => {
);
const onboardingHeaderDescription =
- canUseCardPayments && ! ownBrandOnly
+ canUseCardPayments && ! ownBrandOnly && 'MX' !== storeCountry
? __(
'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'
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/hooks/usePaymentConfig.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/hooks/usePaymentConfig.js
index 7bb5f4af4..eb08ba4d2 100644
--- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/hooks/usePaymentConfig.js
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/hooks/usePaymentConfig.js
@@ -15,6 +15,8 @@ import {
CreditDebitCards,
} from '../Components/PaymentOptions';
+import { useWooSettings } from '../../../../data/common/hooks';
+
// List of all payment icons and which requirements they have.
const PAYMENT_ICONS = [
{ name: 'paypal', always: true },
@@ -28,6 +30,7 @@ const PAYMENT_ICONS = [
{ name: 'blik', isOwnBrand: true, onlyAcdc: true },
{ name: 'ideal', isOwnBrand: true, onlyAcdc: true },
{ name: 'bancontact', isOwnBrand: true, onlyAcdc: true },
+ { name: 'oxxo', isOwnBrand: true, onlyAcdc: false, countries: [ 'MX' ] },
];
// Default configuration, used for all countries, unless they override individual attributes below.
@@ -211,6 +214,11 @@ const getRelevantIcons = ( country, includeAcdc, onlyBranded ) =>
return true;
}
+ // If we're in Mexico, only show OXXO from the APMs.
+ if ( country === 'MX' && onlyAcdc ) {
+ return false;
+ }
+
if ( onlyBranded && ! isOwnBrand ) {
return false;
}
@@ -251,6 +259,7 @@ export const usePaymentConfig = (
hasFastlane,
ownBrandOnly
) => {
+ const { countryCode } = useWooSettings();
return useMemo( () => {
// eslint-disable-next-line no-console
console.log( '[Payment Config]', {
@@ -277,8 +286,11 @@ export const usePaymentConfig = (
const availableOptionalMethods = filterMethods(
config.extendedMethods,
[
- // Either include Acdc or non-Acdc methods.
- ( method ) => method.isAcdc === canUseCardPayments,
+ // Either include Acdc or non-Acdc methods except for Mexico.
+ ( method ) =>
+ countryCode === 'MX'
+ ? ! method.isAcdc || canUseCardPayments
+ : method.isAcdc === canUseCardPayments,
// Only include own-brand methods when ownBrandOnly is true.
( method ) => ! ownBrandOnly || method.isOwnBrand === true,
// Only include Fastlane when hasFastlane is true.
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features/Features.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features/Features.js
index cd5215b0f..709d1fb64 100644
--- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features/Features.js
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features/Features.js
@@ -6,7 +6,10 @@ import FeatureItem from './FeatureItem';
import FeatureDescription from './FeatureDescription';
import { ContentWrapper } from '../../../../../ReusableComponents/Elements';
import SettingsCard from '../../../../../ReusableComponents/SettingsCard';
-import { useMerchantInfo } from '../../../../../../data/common/hooks';
+import {
+ useMerchantInfo,
+ useWooSettings,
+} from '../../../../../../data/common/hooks';
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../../../data/common';
import {
NOTIFICATION_ERROR,
@@ -17,6 +20,7 @@ import { useFeatures } from '../../../../../../data/features/hooks';
const Features = () => {
const [ isRefreshing, setIsRefreshing ] = useState( false );
const { merchant } = useMerchantInfo();
+ const { storeCountry } = useWooSettings();
const { features, fetchFeatures } = useFeatures();
const { refreshFeatureStatuses } = useDispatch( COMMON_STORE_NAME );
const { createSuccessNotice, createErrorNotice } =
@@ -26,6 +30,13 @@ const Features = () => {
return null;
}
+ // Filter out ACDC for Mexico (when disabled).
+ const filteredFeatures = features.filter(
+ ( feature ) =>
+ feature.id !== 'advanced_credit_and_debit_cards' ||
+ storeCountry !== 'MX'
+ );
+
const refreshHandler = async () => {
setIsRefreshing( true );
try {
@@ -86,7 +97,7 @@ const Features = () => {
aria-busy={ isRefreshing }
>
- { features.map( ( { id, enabled, ...feature } ) => (
+ { filteredFeatures.map( ( { id, enabled, ...feature } ) => (
;
}
- // Process methods with dependencies.
+ // Process methods with dependencies from the pre-computed map.
const processedMethods = methods.map( ( method ) => {
- const paymentDependency = paymentDependencies?.[ method.id ];
- const settingDependency = settingDependencies?.[ method.id ];
-
- let dependencyMessage = null;
- let isMethodDisabled = method.isDisabled || isDisabled;
-
- if ( paymentDependency ) {
- dependencyMessage = (
-
- );
- isMethodDisabled = true;
- } else if ( settingDependency?.isDisabled ) {
- dependencyMessage = (
-
- );
- isMethodDisabled = true;
- }
+ const dependencyInfo = dependencyMessagesMap[ method.id ] || {};
return {
...method,
- isDisabled: isMethodDisabled,
- disabledMessage: dependencyMessage,
+ isDisabled:
+ dependencyInfo.isMethodDisabled ||
+ method.isDisabled ||
+ isDisabled,
+ disabledMessage: dependencyInfo.dependencyMessage,
};
} );
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/PaymentMethodValueDependencyMessage.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/PaymentMethodValueDependencyMessage.js
new file mode 100644
index 000000000..899e16338
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/PaymentMethodValueDependencyMessage.js
@@ -0,0 +1,49 @@
+import { createInterpolateElement } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { scrollAndHighlight } from '../../../../../utils/scrollAndHighlight';
+
+/**
+ * Component to display a payment method value dependency message
+ *
+ * @param {Object} props - Component props
+ * @param {string} props.dependentMethodId - ID of the dependent payment method
+ * @param {string} props.dependentMethodName - Display name of the dependent payment method
+ * @param {boolean} props.requiredValue - Required value (enabled/disabled state) for the dependent method
+ * @return {JSX.Element} The formatted message with link
+ */
+const PaymentMethodValueDependencyMessage = ( {
+ dependentMethodId,
+ dependentMethodName,
+ requiredValue,
+} ) => {
+ const displayName = dependentMethodName || dependentMethodId;
+
+ // Determine appropriate message template based on the required value
+ const template = requiredValue
+ ? __(
+ 'Enable to use this method.',
+ 'woocommerce-paypal-payments'
+ )
+ : __(
+ 'Disable to use this method.',
+ 'woocommerce-paypal-payments'
+ );
+
+ return createInterpolateElement( template, {
+ methodLink: (
+
+ {
+ e.preventDefault();
+ scrollAndHighlight( dependentMethodId );
+ } }
+ >
+ { displayName }
+
+
+ ),
+ } );
+};
+
+export default PaymentMethodValueDependencyMessage;
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabPaymentMethods.js
index 8cc92e017..f93c4040f 100644
--- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabPaymentMethods.js
+++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabPaymentMethods.js
@@ -2,15 +2,17 @@ import { __ } from '@wordpress/i18n';
import { useCallback } from '@wordpress/element';
import { CommonHooks, OnboardingHooks, PaymentHooks } from '../../../../data';
-import { useActiveModal } from '../../../../data/common/hooks';
+import { useActiveModal, useWooSettings } from '../../../../data/common/hooks';
import Modal from '../Components/Payment/Modal';
import PaymentMethodCard from '../Components/Payment/PaymentMethodCard';
+import { useFeatures } from '../../../../data/features/hooks';
const TabPaymentMethods = () => {
const methods = PaymentHooks.usePaymentMethods();
const store = PaymentHooks.useStore();
const { setPersistent, changePaymentSettings } = store;
const { activeModal, setActiveModal } = useActiveModal();
+ const { features } = useFeatures();
// Get all methods as a map for dependency checking
const methodsMap = {};
@@ -52,12 +54,31 @@ const TabPaymentMethods = () => {
);
const merchant = CommonHooks.useMerchant();
+ const { storeCountry } = useWooSettings();
const { canUseCardPayments } = OnboardingHooks.useFlags();
const showCardPayments =
methods.cardPayment.length > 0 &&
merchant.isBusinessSeller &&
- canUseCardPayments;
+ canUseCardPayments &&
+ // Show ACDC if the merchant has the feature enabled in PayPal account.
+ features.some(
+ ( feature ) =>
+ feature.id === 'advanced_credit_and_debit_cards' &&
+ feature.enabled
+ );
+
+ // Hide BCDC for all countries except Mexico when ACDC is turned on.
+ const filteredPayPalMethods = methods.paypal.filter(
+ ( method ) =>
+ method.id !== 'ppcp-card-button-gateway' ||
+ storeCountry === 'MX' ||
+ ! features.some(
+ ( feature ) =>
+ feature.id === 'advanced_credit_and_debit_cards' &&
+ feature.enabled === true
+ )
+ );
const showApms = methods.apm.length > 0 && merchant.isBusinessSeller;
return (
@@ -70,7 +91,7 @@ const TabPaymentMethods = () => {
'woocommerce-paypal-payments'
) }
icon="icon-checkout-standard.svg"
- methods={ methods.paypal }
+ methods={ filteredPayPalMethods }
onTriggerModal={ setActiveModal }
methodsMap={ methodsMap }
/>
diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js
index c7efe1d6b..042835c57 100644
--- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js
+++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js
@@ -162,14 +162,15 @@ export const useNavigationState = () => {
};
};
-export const useDetermineProducts = ( ownBrandOnly ) => {
+export const useDetermineProducts = ( ownBrandOnly, storeCountry ) => {
return useSelect(
( select ) => {
return select( STORE_NAME ).determineProductsAndCaps(
- ownBrandOnly
+ ownBrandOnly,
+ storeCountry
);
},
- [ ownBrandOnly ]
+ [ ownBrandOnly, storeCountry ]
);
};
diff --git a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js
index 0ea630072..452ba9614 100644
--- a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js
+++ b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js
@@ -35,9 +35,14 @@ export const flags = ( state ) => {
*
* @param {{}} state
* @param {boolean} ownBrandOnly
+ * @param {string} storeCountry
* @return {{products:string[], options:{}}} The ISU products, based on choices made in the onboarding wizard.
*/
-export const determineProductsAndCaps = ( state, ownBrandOnly ) => {
+export const determineProductsAndCaps = (
+ state,
+ ownBrandOnly,
+ storeCountry
+) => {
/**
* An array of product-names that are used to build an onboarding URL via the
* PartnerReferrals API. To avoid confusion with the "products" property from the
@@ -80,7 +85,7 @@ export const determineProductsAndCaps = ( state, ownBrandOnly ) => {
if ( canUseVaulting ) {
apiModules.push( PAYPAL_PRODUCTS.VAULTING );
}
- } else if ( isCasualSeller ) {
+ } else if ( isCasualSeller || 'MX' === storeCountry ) {
/**
* Branch 2: Merchant has no business.
* The store uses the Express-checkout product.
diff --git a/modules/ppcp-settings/resources/js/hooks/useDependencyMessages.js b/modules/ppcp-settings/resources/js/hooks/useDependencyMessages.js
new file mode 100644
index 000000000..9789e4184
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/hooks/useDependencyMessages.js
@@ -0,0 +1,83 @@
+// hooks/useDependencyMessages.js
+import { useMemo } from '@wordpress/element';
+import PaymentDependencyMessage from '../Components/Screens/Settings/Components/Payment/PaymentDependencyMessage';
+import PaymentMethodValueDependencyMessage from '../Components/Screens/Settings/Components/Payment/PaymentMethodValueDependencyMessage';
+import SettingDependencyMessage from '../Components/Screens/Settings/Components/Payment/SettingDependencyMessage';
+
+/**
+ * Hook to process dependency messages for all methods
+ *
+ * @param {Array} methods - List of payment methods
+ * @param {Object} paymentDependencies - Payment method dependencies
+ * @param {Object} settingDependencies - Setting dependencies
+ * @param {boolean} isDisabled - Whether methods are globally disabled
+ * @return {Object} Map of method IDs to their dependency messages and disabled states
+ */
+const useDependencyMessages = (
+ methods,
+ paymentDependencies,
+ settingDependencies,
+ isDisabled = false
+) => {
+ return useMemo( () => {
+ const result = {};
+
+ if ( ! methods || ! methods.length ) {
+ return result;
+ }
+
+ // Process each method once to create their dependency messages.
+ methods.forEach( ( method ) => {
+ if ( ! method || ! method.id ) {
+ return;
+ }
+
+ let dependencyMessage = null;
+ let isMethodDisabled = method.isDisabled || isDisabled;
+
+ // Check payment dependencies
+ const dependency = paymentDependencies?.[ method.id ];
+ if ( dependency ) {
+ if ( dependency.type === 'parent' ) {
+ dependencyMessage = (
+
+ );
+ } else if ( dependency.type === 'value' ) {
+ dependencyMessage = (
+
+ );
+ }
+ isMethodDisabled = true;
+ }
+ // Check setting dependencies
+ else if ( settingDependencies?.[ method.id ]?.isDisabled ) {
+ const settingDependency = settingDependencies[ method.id ];
+ dependencyMessage = (
+
+ );
+ isMethodDisabled = true;
+ }
+
+ // Store the results for this method
+ result[ method.id ] = {
+ dependencyMessage,
+ isMethodDisabled,
+ };
+ } );
+
+ return result;
+ }, [ methods, paymentDependencies, settingDependencies, isDisabled ] );
+};
+
+export default useDependencyMessages;
diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js
index d174c26a7..cdfbc5846 100644
--- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js
+++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js
@@ -31,9 +31,11 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
const { onboardingUrl } = isSandbox
? CommonHooks.useSandbox()
: CommonHooks.useProduction();
- const { ownBrandOnly } = CommonHooks.useWooSettings();
- const { products, options } =
- OnboardingHooks.useDetermineProducts( ownBrandOnly );
+ const { ownBrandOnly, storeCountry } = CommonHooks.useWooSettings();
+ const { products, options } = OnboardingHooks.useDetermineProducts(
+ ownBrandOnly,
+ storeCountry
+ );
const { startActivity } = CommonHooks.useBusyState();
const { authenticateWithOAuth } = CommonHooks.useAuthentication();
const [ onboardingUrlState, setOnboardingUrl ] = useState( '' );
diff --git a/modules/ppcp-settings/resources/js/hooks/usePaymentDependencyState.js b/modules/ppcp-settings/resources/js/hooks/usePaymentDependencyState.js
index 3fb087c1f..8bbca2098 100644
--- a/modules/ppcp-settings/resources/js/hooks/usePaymentDependencyState.js
+++ b/modules/ppcp-settings/resources/js/hooks/usePaymentDependencyState.js
@@ -6,15 +6,13 @@ import { useSelect } from '@wordpress/data';
/**
* Gets the display name for a parent payment method
*
- * @param {string} parentId - ID of the parent payment method
+ * @param {string} methodId - ID of the payment method
* @param {Object} methodsMap - Map of all payment methods by ID
- * @return {string} The display name to use for the parent method
+ * @return {string} The display name of the method
*/
-const getParentMethodName = ( parentId, methodsMap ) => {
- const parentMethod = methodsMap[ parentId ];
- return parentMethod
- ? parentMethod.itemTitle || parentMethod.title || ''
- : '';
+const getMethodName = ( methodId, methodsMap ) => {
+ const method = methodsMap[ methodId ];
+ return method ? method.itemTitle || method.title || '' : '';
};
/**
@@ -38,7 +36,51 @@ const findDisabledParents = ( method, methodsMap ) => {
};
/**
- * Hook to evaluate payment method dependencies
+ * Checks if method should be disabled due to value dependencies
+ *
+ * @param {Object} method - The payment method to check
+ * @param {Object} methodsMap - Map of all payment methods by ID
+ * @return {Object|null} Value dependency info if should be disabled, null otherwise
+ */
+const checkValueDependencies = ( method, methodsMap ) => {
+ const valueDependencies = method.depends_on_payment_methods_values;
+
+ if ( ! valueDependencies ) {
+ return null;
+ }
+
+ // Check each dependency against the actual state of the dependent method.
+ for ( const [ dependentId, requiredValue ] of Object.entries(
+ valueDependencies
+ ) ) {
+ const dependent = methodsMap[ dependentId ];
+
+ if ( ! dependent ) {
+ continue;
+ }
+
+ // Example: card-button-gateway depends on credit-card-gateway being FALSE.
+ // So if credit-card-gateway is TRUE (enabled), card-button-gateway should be disabled.
+
+ // If the dependency requires a method to be false but it's enabled (or vice versa).
+ if (
+ typeof requiredValue === 'boolean' &&
+ dependent.enabled !== requiredValue
+ ) {
+ // This dependency is violated - the dependent method is in the wrong state.
+ return {
+ dependentId,
+ dependentName: getMethodName( dependentId, methodsMap ),
+ requiredValue,
+ };
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Hook to evaluate all payment method dependencies
*
* @param {Array} methods - List of payment methods
* @param {Object} methodsMap - Map of payment methods by ID
@@ -51,6 +93,7 @@ const usePaymentDependencyState = ( methods, methodsMap ) => {
if ( methods && methodsMap && Object.keys( methodsMap ).length > 0 ) {
methods.forEach( ( method ) => {
if ( method && method.id ) {
+ // Check regular parent-child dependencies first.
const disabledParents = findDisabledParents(
method,
methodsMap
@@ -59,18 +102,32 @@ const usePaymentDependencyState = ( methods, methodsMap ) => {
if ( disabledParents.length > 0 ) {
const parentId = disabledParents[ 0 ];
result[ method.id ] = {
+ type: 'parent',
isDisabled: true,
parentId,
- parentName: getParentMethodName(
- parentId,
- methodsMap
- ),
+ parentName: getMethodName( parentId, methodsMap ),
+ };
+ return; // Skip other checks if already disabled.
+ }
+
+ // Check value dependencies.
+ const valueDependency = checkValueDependencies(
+ method,
+ methodsMap
+ );
+
+ if ( valueDependency ) {
+ result[ method.id ] = {
+ type: 'value',
+ isDisabled: true,
+ dependentId: valueDependency.dependentId,
+ dependentName: valueDependency.dependentName,
+ requiredValue: valueDependency.requiredValue,
};
}
}
} );
}
-
return result;
}, [ methods, methodsMap ] );
};
diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php
index 10e257cda..08d326b25 100644
--- a/modules/ppcp-settings/services.php
+++ b/modules/ppcp-settings/services.php
@@ -568,16 +568,17 @@ return array(
);
// Merchant capabilities, serve to show active or inactive badge and buttons.
$capabilities = array(
- 'apple_pay' => $features['apple_pay']['enabled'] ?? false,
- 'google_pay' => $features['google_pay']['enabled'] ?? false,
- 'acdc' => $features['advanced_credit_and_debit_cards']['enabled'] ?? false,
- 'save_paypal' => $features['save_paypal_and_venmo']['enabled'] ?? false,
+ 'apple_pay' => $features['apple_pay']['enabled'] ?? false,
+ 'google_pay' => $features['google_pay']['enabled'] ?? false,
+ 'acdc' => $features['advanced_credit_and_debit_cards']['enabled'] ?? false,
+ 'save_paypal' => $features['save_paypal_and_venmo']['enabled'] ?? false,
+ 'alternative_payment_methods' => $features['alternative_payment_methods']['enabled'] ?? false,
);
$merchant_capabilities = array(
'save_paypal' => $capabilities['save_paypal'], // Save PayPal and Venmo eligibility.
- 'acdc' => $capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility.
- 'apm' => ! $gateways['card-button'], // Alternative payment methods eligibility.
+ 'acdc' => $capabilities['acdc'], // Advanced credit and debit cards eligibility.
+ 'apm' => $capabilities['alternative_payment_methods'], // Alternative payment methods eligibility.
'google_pay' => $capabilities['acdc'] && $capabilities['google_pay'], // Google Pay eligibility.
'apple_pay' => $capabilities['acdc'] && $capabilities['apple_pay'], // Apple Pay eligibility.
'pay_later' => $capabilities['acdc'] && ! $gateways['card-button'], // Pay Later eligibility.
diff --git a/modules/ppcp-settings/src/Data/Definition/PaymentMethodsDependenciesDefinition.php b/modules/ppcp-settings/src/Data/Definition/PaymentMethodsDependenciesDefinition.php
index 9c7771be9..9bec748a9 100644
--- a/modules/ppcp-settings/src/Data/Definition/PaymentMethodsDependenciesDefinition.php
+++ b/modules/ppcp-settings/src/Data/Definition/PaymentMethodsDependenciesDefinition.php
@@ -107,6 +107,27 @@ class PaymentMethodsDependenciesDefinition {
);
}
+ /**
+ * Get payment method value dependencies for a specific method
+ *
+ * @return array Value dependencies for the method or empty array if none exist
+ */
+ public function get_payment_method_value_dependencies(): array {
+ $dependencies = array(
+ CardButtonGateway::ID => array(
+ CreditCardGateway::ID => false,
+ ),
+ CreditCardGateway::ID => array(
+ CardButtonGateway::ID => false,
+ ),
+ );
+
+ return apply_filters(
+ 'woocommerce_paypal_payments_method_value_dependencies',
+ $dependencies
+ );
+ }
+
/**
* Get method setting dependencies
*
@@ -139,6 +160,15 @@ class PaymentMethodsDependenciesDefinition {
$method['depends_on_payment_methods'] = $payment_method_dependencies;
}
+ // Add payment method value dependency info if applicable.
+ $all_payment_method_value_dependencies = $this->get_payment_method_value_dependencies();
+
+ // Only add dependencies that directly apply to this method.
+ if ( isset( $all_payment_method_value_dependencies[ $method_id ] ) ) {
+ // Direct dependencies on other method values.
+ $method['depends_on_payment_methods_values'] = $all_payment_method_value_dependencies[ $method_id ];
+ }
+
// Check if this method has setting dependencies.
$method_setting_dependencies = $this->get_method_setting_dependencies( $method_id );
if ( ! empty( $method_setting_dependencies ) ) {
diff --git a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php
index 428e05c9a..189179910 100644
--- a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php
+++ b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php
@@ -197,6 +197,10 @@ class PaymentRestEndpoint extends RestEndpoint {
$gateway_settings[ $key ]['depends_on_payment_methods'] = $payment_method['depends_on_payment_methods'];
}
+ if ( isset( $payment_method['depends_on_payment_methods_values'] ) ) {
+ $gateway_settings[ $key ]['depends_on_payment_methods_values'] = $payment_method['depends_on_payment_methods_values'];
+ }
+
if ( isset( $payment_method['depends_on_settings'] ) ) {
$gateway_settings[ $key ]['depends_on_settings'] = $payment_method['depends_on_settings'];
}
diff --git a/modules/ppcp-settings/src/Service/SettingsDataManager.php b/modules/ppcp-settings/src/Service/SettingsDataManager.php
index d2d49f67a..b0ecdcc71 100644
--- a/modules/ppcp-settings/src/Service/SettingsDataManager.php
+++ b/modules/ppcp-settings/src/Service/SettingsDataManager.php
@@ -270,10 +270,14 @@ class SettingsDataManager {
$this->payment_methods->toggle_method_state( CardButtonGateway::ID, true );
}
- // Enable all APM methods.
- foreach ( $methods_apm as $method ) {
- $this->payment_methods->toggle_method_state( $method['id'], true );
- }
+ /**
+ * Allow plugins to modify apm payment gateway states before saving.
+ *
+ * @param PaymentSettings $payment_methods The payment methods object.
+ * @param PaymentSettings $methods_apm List of APM methods.
+ * @param ConfigurationFlagsDTO $flags Configuration flags that determine which gateways to enable.
+ */
+ do_action( 'woocommerce_paypal_payments_toggle_payment_gateways_apms', $this->payment_methods, $methods_apm, $flags );
}
/**
diff --git a/modules/ppcp-settings/src/SettingsModule.php b/modules/ppcp-settings/src/SettingsModule.php
index 621961bec..0cbb0fa54 100644
--- a/modules/ppcp-settings/src/SettingsModule.php
+++ b/modules/ppcp-settings/src/SettingsModule.php
@@ -13,8 +13,10 @@ use WC_Payment_Gateway;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Helper\PartnerAttribution;
+use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
+use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\BancontactGateway;
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\BlikGateway;
@@ -321,11 +323,13 @@ class SettingsModule implements ServiceModule, ExecutableModule {
// Unset BCDC if merchant is eligible for ACDC and country is eligible for card fields.
$card_fields_eligible = $container->get( 'card-fields.eligible' );
- if ( $dcc_product_status->is_active() && $card_fields_eligible ) {
- unset( $payment_methods[ CardButtonGateway::ID ] );
- } else {
- // For non-ACDC regions unset ACDC.
- unset( $payment_methods[ CreditCardGateway::ID ] );
+ if ( 'MX' !== $container->get( 'api.shop.country' ) ) {
+ if ( $dcc_product_status->is_active() && $card_fields_eligible ) {
+ unset( $payment_methods[ CardButtonGateway::ID ] );
+ } else {
+ // For non-ACDC regions unset ACDC.
+ unset( $payment_methods[ CreditCardGateway::ID ] );
+ }
}
// Unset Venmo when store location is not United States.
@@ -358,6 +362,18 @@ class SettingsModule implements ServiceModule, ExecutableModule {
unset( $payment_methods[ PayUponInvoiceGateway::ID ] );
}
+ // Unset all APMs other than OXXO for Mexico.
+ if ( 'MX' === $merchant_country ) {
+ unset( $payment_methods[ BancontactGateway::ID ] );
+ unset( $payment_methods[ BlikGateway::ID ] );
+ unset( $payment_methods[ EPSGateway::ID ] );
+ unset( $payment_methods[ IDealGateway::ID ] );
+ unset( $payment_methods[ MyBankGateway::ID ] );
+ unset( $payment_methods[ P24Gateway::ID ] );
+ unset( $payment_methods[ TrustlyGateway::ID ] );
+ unset( $payment_methods[ MultibancoGateway::ID ] );
+ }
+
return $payment_methods;
}
);
@@ -539,6 +555,62 @@ class SettingsModule implements ServiceModule, ExecutableModule {
$payment_methods->toggle_method_state( AxoGateway::ID, true );
}
}
+
+ $general_settings = $container->get( 'settings.data.general' );
+ assert( $general_settings instanceof GeneralSettings );
+
+ $merchant_data = $general_settings->get_merchant_data();
+ $merchant_country = $merchant_data->merchant_country;
+
+ // Disable all extended checkout card methods if the store is in Mexico.
+ if ( 'MX' === $merchant_country ) {
+ $payment_methods->toggle_method_state( CreditCardGateway::ID, false );
+ $payment_methods->toggle_method_state( ApplePayGateway::ID, false );
+ $payment_methods->toggle_method_state( GooglePayGateway::ID, false );
+ }
+ },
+ 10,
+ 2
+ );
+
+ // Enable APMs after onboarding if the country is compatible.
+ add_action(
+ 'woocommerce_paypal_payments_toggle_payment_gateways_apms',
+ function ( PaymentSettings $payment_methods, array $methods_apm ) use ( $container ) {
+
+ $general_settings = $container->get( 'settings.data.general' );
+ assert( $general_settings instanceof GeneralSettings );
+
+ $merchant_data = $general_settings->get_merchant_data();
+ $merchant_country = $merchant_data->merchant_country;
+
+ $logger = $container->get( 'woocommerce.logger.woocommerce' );
+ assert( $logger instanceof LoggerInterface );
+
+ $logger->info( 'Merchant country: ' . $merchant_country );
+ $logger->info( 'Merchant data: ' . json_encode( $merchant_data ) );
+ $logger->info( '$methods_apm: ' . json_encode( $methods_apm ) );
+
+ // Enable all APM methods.
+ foreach ( $methods_apm as $method ) {
+ // Skip PayUponInvoice if merchant is not in Germany.
+ if ( PayUponInvoiceGateway::ID === $method['id'] && 'DE' !== $merchant_country ) {
+ continue;
+ }
+
+ // For OXXO: enable ONLY if merchant is in Mexico.
+ if ( OXXO::ID === $method['id'] ) {
+ if ( 'MX' === $merchant_country ) {
+ $payment_methods->toggle_method_state( $method['id'], true );
+ }
+ continue;
+ }
+
+ // For all other APMs: enable only if merchant is NOT in Mexico.
+ if ( 'MX' !== $merchant_country ) {
+ $payment_methods->toggle_method_state( $method['id'], true );
+ }
+ }
},
10,
2