mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 09:08:09 +08:00
🔀 Merge branch 'trunk’
# Conflicts: # modules/ppcp-settings/resources/js/data/common/hooks.js
This commit is contained in:
commit
8ec852952f
20 changed files with 567 additions and 225 deletions
|
@ -1018,6 +1018,10 @@ class ApplePayButton implements ButtonInterface {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function enqueue(): void {
|
public function enqueue(): void {
|
||||||
|
if ( ! $this->is_enabled() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wp_register_script(
|
wp_register_script(
|
||||||
'wc-ppcp-applepay',
|
'wc-ppcp-applepay',
|
||||||
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
|
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
|
||||||
|
|
|
@ -93,6 +93,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ppcp-r-payment-methods {
|
||||||
|
.ppcp-highlight {
|
||||||
|
animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
border: 1px solid $color-blueberry;
|
||||||
|
border-radius: var(--container-border-radius);
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ppcp-highlight-fade {
|
||||||
|
0%, 20% {
|
||||||
|
background-color: rgba($color-blueberry, 0.08);
|
||||||
|
border-color: $color-blueberry;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: $color-gray-300;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Disabled state styling.
|
// Disabled state styling.
|
||||||
.ppcp--method-item--disabled {
|
.ppcp--method-item--disabled {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -226,25 +226,91 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payment Methods
|
.ppcp-r-settings {
|
||||||
|
.ppcp-highlight {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
.ppcp-highlight {
|
&::before {
|
||||||
animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1);
|
content: '';
|
||||||
border: 1px solid $color-blueberry;
|
position: absolute;
|
||||||
border-radius: var(--container-border-radius);
|
top: -8px;
|
||||||
position: relative;
|
left: -12px;
|
||||||
z-index: 1;
|
right: -12px;
|
||||||
}
|
bottom: -8px;
|
||||||
|
border: 1px solid $color-blueberry;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: ppcp-setting-highlight-bg 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes ppcp-highlight-fade {
|
&::after {
|
||||||
0%, 20% {
|
content: '';
|
||||||
background-color: rgba($color-blueberry, 0.08);
|
position: absolute;
|
||||||
border-color: $color-blueberry;
|
top: -8px;
|
||||||
border-width: 1px;
|
left: -12px;
|
||||||
|
width: 4px;
|
||||||
|
bottom: -8px;
|
||||||
|
background-color: $color-blueberry;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: ppcp-setting-highlight-accent 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
100% {
|
|
||||||
background-color: transparent;
|
@keyframes ppcp-setting-highlight-bg {
|
||||||
border-color: $color-gray-300;
|
0%, 15% {
|
||||||
border-width: 1px;
|
background-color: rgba($color-blueberry, 0.08);
|
||||||
|
border-color: $color-blueberry;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ppcp-setting-highlight-accent {
|
||||||
|
0%, 15% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp-r-settings-section {
|
||||||
|
.ppcp--setting-row {
|
||||||
|
position: relative;
|
||||||
|
padding: 12px;
|
||||||
|
margin: 0 -12px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($color-gray-100, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTL support
|
||||||
|
html[dir="rtl"] {
|
||||||
|
.ppcp-highlight {
|
||||||
|
&::after {
|
||||||
|
left: auto;
|
||||||
|
right: -12px;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@ import { ToggleControl } from '@wordpress/components';
|
||||||
import { Action, Description } from '../Elements';
|
import { Action, Description } from '../Elements';
|
||||||
|
|
||||||
const ControlToggleButton = ( {
|
const ControlToggleButton = ( {
|
||||||
|
id = '',
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
} ) => (
|
} ) => (
|
||||||
<Action>
|
<Action id={ id }>
|
||||||
<ToggleControl
|
<ToggleControl
|
||||||
className="ppcp--control-toggle"
|
className="ppcp--control-toggle"
|
||||||
__nextHasNoMarginBottom
|
__nextHasNoMarginBottom
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
const Action = ( { children } ) => (
|
const Action = ( { id, children } ) => (
|
||||||
<div className="ppcp--action">{ children }</div>
|
<div className="ppcp--action" { ...( id ? { id } : {} ) }>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Action;
|
export default Action;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { ToggleControl, Icon, Button } from '@wordpress/components';
|
import { ToggleControl, Icon, Button } from '@wordpress/components';
|
||||||
import { cog } from '@wordpress/icons';
|
import { cog } from '@wordpress/icons';
|
||||||
import { useEffect } from '@wordpress/element';
|
|
||||||
import { useActiveHighlight } from '../../../data/common/hooks';
|
|
||||||
|
|
||||||
import SettingsBlock from '../SettingsBlock';
|
import SettingsBlock from '../SettingsBlock';
|
||||||
import PaymentMethodIcon from '../PaymentMethodIcon';
|
import PaymentMethodIcon from '../PaymentMethodIcon';
|
||||||
|
@ -16,26 +14,12 @@ const PaymentMethodItemBlock = ( {
|
||||||
disabledMessage,
|
disabledMessage,
|
||||||
warningMessages,
|
warningMessages,
|
||||||
} ) => {
|
} ) => {
|
||||||
const { activeHighlight, setActiveHighlight } = useActiveHighlight();
|
|
||||||
const isHighlighted = activeHighlight === paymentMethod.id;
|
|
||||||
const hasWarning =
|
const hasWarning =
|
||||||
warningMessages && Object.keys( warningMessages ).length > 0;
|
warningMessages && Object.keys( warningMessages ).length > 0;
|
||||||
|
|
||||||
// Reset the active highlight after 2 seconds
|
|
||||||
useEffect( () => {
|
|
||||||
if ( isHighlighted ) {
|
|
||||||
const timer = setTimeout( () => {
|
|
||||||
setActiveHighlight( null );
|
|
||||||
}, 2000 );
|
|
||||||
|
|
||||||
return () => clearTimeout( timer );
|
|
||||||
}
|
|
||||||
}, [ isHighlighted, setActiveHighlight ] );
|
|
||||||
|
|
||||||
// Determine class names based on states
|
// Determine class names based on states
|
||||||
const methodItemClasses = [
|
const methodItemClasses = [
|
||||||
'ppcp--method-item',
|
'ppcp--method-item',
|
||||||
isHighlighted ? 'ppcp-highlight' : '',
|
|
||||||
isDisabled ? 'ppcp--method-item--disabled' : '',
|
isDisabled ? 'ppcp--method-item--disabled' : '',
|
||||||
hasWarning && ! isDisabled ? 'ppcp--method-item--warning' : '',
|
hasWarning && ! isDisabled ? 'ppcp--method-item--warning' : '',
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,7 +7,6 @@ const TodoSettingsBlock = ( {
|
||||||
todosData,
|
todosData,
|
||||||
className = '',
|
className = '',
|
||||||
setActiveModal,
|
setActiveModal,
|
||||||
setActiveHighlight,
|
|
||||||
onDismissTodo,
|
onDismissTodo,
|
||||||
} ) => {
|
} ) => {
|
||||||
const [ dismissingIds, setDismissingIds ] = useState( new Set() );
|
const [ dismissingIds, setDismissingIds ] = useState( new Set() );
|
||||||
|
@ -58,9 +57,6 @@ const TodoSettingsBlock = ( {
|
||||||
if ( todo.action.modal ) {
|
if ( todo.action.modal ) {
|
||||||
setActiveModal( todo.action.modal );
|
setActiveModal( todo.action.modal );
|
||||||
}
|
}
|
||||||
if ( todo.action.highlight ) {
|
|
||||||
setActiveHighlight( todo.action.highlight );
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter out dismissed todos for display and limit to 5.
|
// Filter out dismissed todos for display and limit to 5.
|
||||||
|
|
|
@ -15,8 +15,7 @@ const Todos = () => {
|
||||||
const [ isResetting, setIsResetting ] = useState( false );
|
const [ isResetting, setIsResetting ] = useState( false );
|
||||||
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
|
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
const { setActiveModal, setActiveHighlight } =
|
const { setActiveModal } = useDispatch( COMMON_STORE_NAME );
|
||||||
useDispatch( COMMON_STORE_NAME );
|
|
||||||
const { resetDismissedTodos, setDismissedTodos } =
|
const { resetDismissedTodos, setDismissedTodos } =
|
||||||
useDispatch( TODOS_STORE_NAME );
|
useDispatch( TODOS_STORE_NAME );
|
||||||
const { createSuccessNotice } = useDispatch( noticesStore );
|
const { createSuccessNotice } = useDispatch( noticesStore );
|
||||||
|
@ -76,7 +75,6 @@ const Todos = () => {
|
||||||
<TodoSettingsBlock
|
<TodoSettingsBlock
|
||||||
todosData={ todos }
|
todosData={ todos }
|
||||||
setActiveModal={ setActiveModal }
|
setActiveModal={ setActiveModal }
|
||||||
setActiveHighlight={ setActiveHighlight }
|
|
||||||
onDismissTodo={ dismissTodo }
|
onDismissTodo={ dismissTodo }
|
||||||
/>
|
/>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
|
@ -10,8 +10,9 @@ import { scrollAndHighlight } from '../../../../../utils/scrollAndHighlight';
|
||||||
* @param {string} props.parentName - Display name of the parent payment method
|
* @param {string} props.parentName - Display name of the parent payment method
|
||||||
* @return {JSX.Element} The formatted message with link
|
* @return {JSX.Element} The formatted message with link
|
||||||
*/
|
*/
|
||||||
const DependencyMessage = ( { parentId, parentName } ) => {
|
const PaymentDependencyMessage = ( { parentId, parentName } ) => {
|
||||||
// Using WordPress createInterpolateElement with proper React elements
|
const displayName = parentName || parentId;
|
||||||
|
|
||||||
return createInterpolateElement(
|
return createInterpolateElement(
|
||||||
/* translators: %s: payment method name */
|
/* translators: %s: payment method name */
|
||||||
__(
|
__(
|
||||||
|
@ -28,7 +29,7 @@ const DependencyMessage = ( { parentId, parentName } ) => {
|
||||||
scrollAndHighlight( parentId );
|
scrollAndHighlight( parentId );
|
||||||
} }
|
} }
|
||||||
>
|
>
|
||||||
{ parentName }
|
{ displayName }
|
||||||
</a>
|
</a>
|
||||||
</strong>
|
</strong>
|
||||||
),
|
),
|
||||||
|
@ -36,4 +37,4 @@ const DependencyMessage = ( { parentId, parentName } ) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DependencyMessage;
|
export default PaymentDependencyMessage;
|
|
@ -1,7 +1,11 @@
|
||||||
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
||||||
import { PaymentMethodsBlock } from '../../../../ReusableComponents/SettingsBlocks';
|
import { PaymentMethodsBlock } from '../../../../ReusableComponents/SettingsBlocks';
|
||||||
import usePaymentDependencyState from '../../../../../hooks/usePaymentDependencyState';
|
import usePaymentDependencyState from '../../../../../hooks/usePaymentDependencyState';
|
||||||
import DependencyMessage from './DependencyMessage';
|
import useSettingDependencyState from '../../../../../hooks/useSettingDependencyState';
|
||||||
|
import PaymentDependencyMessage from './PaymentDependencyMessage';
|
||||||
|
import SettingDependencyMessage from './SettingDependencyMessage';
|
||||||
|
import SpinnerOverlay from '../../../../ReusableComponents/SpinnerOverlay';
|
||||||
|
import { PaymentHooks, SettingsHooks } from '../../../../../data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a payment method card with dependency handling
|
* Renders a payment method card with dependency handling
|
||||||
|
@ -27,9 +31,20 @@ const PaymentMethodCard = ( {
|
||||||
methodsMap = {},
|
methodsMap = {},
|
||||||
onTriggerModal,
|
onTriggerModal,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
disabledMessage,
|
|
||||||
} ) => {
|
} ) => {
|
||||||
const dependencyState = usePaymentDependencyState( methods, methodsMap );
|
const { isReady: isPaymentStoreReady } = PaymentHooks.useStore();
|
||||||
|
const { isReady: isSettingsStoreReady } = SettingsHooks.useStore();
|
||||||
|
|
||||||
|
const paymentDependencies = usePaymentDependencyState(
|
||||||
|
methods,
|
||||||
|
methodsMap
|
||||||
|
);
|
||||||
|
|
||||||
|
const settingDependencies = useSettingDependencyState( methods );
|
||||||
|
|
||||||
|
if ( ! isPaymentStoreReady || ! isSettingsStoreReady ) {
|
||||||
|
return <SpinnerOverlay asModal={ true } />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
|
@ -41,25 +56,39 @@ const PaymentMethodCard = ( {
|
||||||
>
|
>
|
||||||
<PaymentMethodsBlock
|
<PaymentMethodsBlock
|
||||||
paymentMethods={ methods.map( ( method ) => {
|
paymentMethods={ methods.map( ( method ) => {
|
||||||
const dependency = dependencyState[ method.id ];
|
const paymentDependency =
|
||||||
|
paymentDependencies?.[ method.id ];
|
||||||
|
const settingDependency =
|
||||||
|
settingDependencies?.[ method.id ];
|
||||||
|
|
||||||
const dependencyMessage = dependency ? (
|
let dependencyMessage = null;
|
||||||
<DependencyMessage
|
let isMethodDisabled = method.isDisabled || isDisabled;
|
||||||
parentId={ dependency.parentId }
|
|
||||||
parentName={ dependency.parentName }
|
if ( paymentDependency ) {
|
||||||
/>
|
dependencyMessage = (
|
||||||
) : null;
|
<PaymentDependencyMessage
|
||||||
|
parentId={ paymentDependency.parentId }
|
||||||
|
parentName={ paymentDependency.parentName }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
isMethodDisabled = true;
|
||||||
|
} else if ( settingDependency?.isDisabled ) {
|
||||||
|
dependencyMessage = (
|
||||||
|
<SettingDependencyMessage
|
||||||
|
settingId={ settingDependency.settingId }
|
||||||
|
requiredValue={
|
||||||
|
settingDependency.requiredValue
|
||||||
|
}
|
||||||
|
methodId={ method.id }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
isMethodDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...method,
|
...method,
|
||||||
isDisabled:
|
isDisabled: isMethodDisabled,
|
||||||
method.isDisabled ||
|
disabledMessage: dependencyMessage,
|
||||||
isDisabled ||
|
|
||||||
Boolean( dependency?.isDisabled ),
|
|
||||||
disabledMessage:
|
|
||||||
method.disabledMessage ||
|
|
||||||
dependencyMessage ||
|
|
||||||
disabledMessage,
|
|
||||||
};
|
};
|
||||||
} ) }
|
} ) }
|
||||||
onTriggerModal={ onTriggerModal }
|
onTriggerModal={ onTriggerModal }
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { createInterpolateElement } from '@wordpress/element';
|
||||||
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
|
import { selectTab, TAB_IDS } from '../../../../../utils/tabSelector';
|
||||||
|
import { scrollAndHighlight } from '../../../../../utils/scrollAndHighlight';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms camelCase section IDs to kebab-case with ppcp prefix
|
||||||
|
*
|
||||||
|
* @param {string} sectionId - The original section ID in camelCase
|
||||||
|
* @return {string} The transformed section ID in kebab-case with ppcp prefix
|
||||||
|
*/
|
||||||
|
const transformSectionId = ( sectionId ) => {
|
||||||
|
if ( ! sectionId ) {
|
||||||
|
return sectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert camelCase to kebab-case.
|
||||||
|
// This regex finds capital letters and replaces them with "-lowercase".
|
||||||
|
const kebabCase = sectionId.replace( /([A-Z])/g, '-$1' ).toLowerCase();
|
||||||
|
|
||||||
|
// Add ppcp- prefix if it doesn't already have it.
|
||||||
|
const prefixed = kebabCase.startsWith( 'ppcp-' )
|
||||||
|
? kebabCase
|
||||||
|
: `ppcp-${ kebabCase }`;
|
||||||
|
|
||||||
|
return prefixed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a setting link element
|
||||||
|
*
|
||||||
|
* @param {Object} props - Component props
|
||||||
|
* @param {string} props.settingName - Display name for the setting
|
||||||
|
* @param {string} props.sectionId - Section ID to scroll to
|
||||||
|
* @return {JSX.Element} The formatted link element
|
||||||
|
*/
|
||||||
|
const SettingLink = ( { settingName, sectionId } ) => (
|
||||||
|
<strong>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={ ( e ) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if ( sectionId ) {
|
||||||
|
const tabId = TAB_IDS.SETTINGS;
|
||||||
|
|
||||||
|
// Transform the section ID before passing to selectTab.
|
||||||
|
const transformedSectionId =
|
||||||
|
transformSectionId( sectionId );
|
||||||
|
|
||||||
|
selectTab( tabId );
|
||||||
|
|
||||||
|
setTimeout( () => {
|
||||||
|
scrollAndHighlight( transformedSectionId );
|
||||||
|
}, 100 );
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
{ settingName }
|
||||||
|
</a>
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to display a setting dependency message
|
||||||
|
*
|
||||||
|
* @param {Object} props - Component props
|
||||||
|
* @param {string} props.settingId - ID of the required setting
|
||||||
|
* @param {*} props.requiredValue - Required value for the setting
|
||||||
|
* @return {JSX.Element} The formatted message
|
||||||
|
*/
|
||||||
|
const SettingDependencyMessage = ( { settingId, requiredValue } ) => {
|
||||||
|
// Setting names mapping.
|
||||||
|
const settingNames = {
|
||||||
|
savePaypalAndVenmo: 'Save PayPal and Venmo',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a human-friendly setting name.
|
||||||
|
const settingName = settingNames[ settingId ] || settingId;
|
||||||
|
|
||||||
|
const settingLink = (
|
||||||
|
<SettingLink settingName={ settingName } sectionId={ settingId } />
|
||||||
|
);
|
||||||
|
|
||||||
|
const templates = {
|
||||||
|
true: __(
|
||||||
|
'This payment method requires <settingLink /> to be enabled.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
false: __(
|
||||||
|
'This payment method requires <settingLink /> to be disabled.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return typeof requiredValue === 'boolean'
|
||||||
|
? createInterpolateElement( templates[ requiredValue ], {
|
||||||
|
settingLink,
|
||||||
|
} )
|
||||||
|
: createInterpolateElement(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: required setting value */
|
||||||
|
__(
|
||||||
|
'This payment method requires <settingLink /> to be set to "%s".',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
requiredValue
|
||||||
|
),
|
||||||
|
{ settingLink }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingDependencyMessage;
|
|
@ -28,18 +28,18 @@ const SavePaymentMethods = () => {
|
||||||
className="ppcp--save-payment-methods"
|
className="ppcp--save-payment-methods"
|
||||||
>
|
>
|
||||||
<ControlToggleButton
|
<ControlToggleButton
|
||||||
|
id="ppcp-save-paypal-and-venmo"
|
||||||
label={ __(
|
label={ __(
|
||||||
'Save PayPal and Venmo',
|
'Save PayPal and Venmo',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
description={ sprintf(
|
description={ sprintf(
|
||||||
/* translators: 1: URL to Pay Later documentation, 2: URL to Alternative Payment Methods documentation */
|
/* translators: 1: URL to Pay Later documentation */
|
||||||
__(
|
__(
|
||||||
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable all <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> features and <a target="_blank" rel="noreferrer" href="%2$s">Alternative Payment Methods</a> on your site.',
|
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable the <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> payment method on your site.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later',
|
'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later'
|
||||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods'
|
|
||||||
) }
|
) }
|
||||||
value={
|
value={
|
||||||
features.save_paypal_and_venmo.enabled
|
features.save_paypal_and_venmo.enabled
|
||||||
|
|
|
@ -74,15 +74,6 @@ export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady );
|
||||||
export const setActiveModal = ( activeModal ) =>
|
export const setActiveModal = ( activeModal ) =>
|
||||||
setTransient( 'activeModal', activeModal );
|
setTransient( 'activeModal', activeModal );
|
||||||
|
|
||||||
/**
|
|
||||||
* Transient. Sets the active settings highlight.
|
|
||||||
*
|
|
||||||
* @param {string} activeHighlight
|
|
||||||
* @return {Action} The action.
|
|
||||||
*/
|
|
||||||
export const setActiveHighlight = ( activeHighlight ) =>
|
|
||||||
setTransient( 'activeHighlight', activeHighlight );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persistent. Sets the sandbox mode on or off.
|
* Persistent. Sets the sandbox mode on or off.
|
||||||
*
|
*
|
||||||
|
|
|
@ -49,8 +49,6 @@ const useHooks = () => {
|
||||||
|
|
||||||
// Transient accessors.
|
// Transient accessors.
|
||||||
const [ activeModal, setActiveModal ] = useTransient( 'activeModal' );
|
const [ activeModal, setActiveModal ] = useTransient( 'activeModal' );
|
||||||
const [ activeHighlight, setActiveHighlight ] =
|
|
||||||
useTransient( 'activeHighlight' );
|
|
||||||
|
|
||||||
// Persistent accessors.
|
// Persistent accessors.
|
||||||
const [ isManualConnectionMode, setManualConnectionMode ] = usePersistent(
|
const [ isManualConnectionMode, setManualConnectionMode ] = usePersistent(
|
||||||
|
@ -70,8 +68,6 @@ const useHooks = () => {
|
||||||
return {
|
return {
|
||||||
activeModal,
|
activeModal,
|
||||||
setActiveModal,
|
setActiveModal,
|
||||||
activeHighlight,
|
|
||||||
setActiveHighlight,
|
|
||||||
isManualConnectionMode,
|
isManualConnectionMode,
|
||||||
setManualConnectionMode: ( state ) => {
|
setManualConnectionMode: ( state ) => {
|
||||||
return savePersistent( setManualConnectionMode, state );
|
return savePersistent( setManualConnectionMode, state );
|
||||||
|
@ -227,11 +223,6 @@ export const useActiveModal = () => {
|
||||||
return { activeModal, setActiveModal };
|
return { activeModal, setActiveModal };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useActiveHighlight = () => {
|
|
||||||
const { activeHighlight, setActiveHighlight } = useHooks();
|
|
||||||
return { activeHighlight, setActiveHighlight };
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Busy state management hooks
|
* Busy state management hooks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
/**
|
||||||
|
* Custom hook to handle payment-method-based dependencies
|
||||||
|
*/
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,60 +25,54 @@ const getParentMethodName = ( parentId, methodsMap ) => {
|
||||||
* @return {Array} List of disabled parent IDs, empty if none
|
* @return {Array} List of disabled parent IDs, empty if none
|
||||||
*/
|
*/
|
||||||
const findDisabledParents = ( method, methodsMap ) => {
|
const findDisabledParents = ( method, methodsMap ) => {
|
||||||
if ( ! method.depends_on?.length && ! method._disabledByDependency ) {
|
const dependencies = method.depends_on_payment_methods;
|
||||||
|
|
||||||
|
if ( ! dependencies || ! Array.isArray( dependencies ) ) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const parents = method.depends_on || [];
|
return dependencies.filter( ( parentId ) => {
|
||||||
|
|
||||||
return parents.filter( ( parentId ) => {
|
|
||||||
const parent = methodsMap[ parentId ];
|
const parent = methodsMap[ parentId ];
|
||||||
return parent && ! parent.enabled;
|
return parent && ! parent.enabled;
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook to handle payment method dependencies
|
* Hook to evaluate payment method dependencies
|
||||||
*
|
*
|
||||||
* @param {Array} methods - List of payment methods
|
* @param {Array} methods - List of payment methods
|
||||||
* @param {Object} methodsMap - Map of payment methods by ID
|
* @param {Object} methodsMap - Map of payment methods by ID
|
||||||
* @return {Object} Dependency state object with methods that should be disabled
|
* @return {Object} Dependency state object keyed by method ID
|
||||||
*/
|
*/
|
||||||
const usePaymentDependencyState = ( methods, methodsMap ) => {
|
const usePaymentDependencyState = ( methods, methodsMap ) => {
|
||||||
return useSelect(
|
return useSelect( () => {
|
||||||
( select ) => {
|
const result = {};
|
||||||
const paymentStore = select( 'wc/paypal/payment' );
|
|
||||||
if ( ! paymentStore ) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = {};
|
|
||||||
|
|
||||||
|
if ( methods && methodsMap && Object.keys( methodsMap ).length > 0 ) {
|
||||||
methods.forEach( ( method ) => {
|
methods.forEach( ( method ) => {
|
||||||
const disabledParents = findDisabledParents(
|
if ( method && method.id ) {
|
||||||
method,
|
const disabledParents = findDisabledParents(
|
||||||
methodsMap
|
method,
|
||||||
);
|
|
||||||
|
|
||||||
if ( disabledParents.length > 0 ) {
|
|
||||||
const parentId = disabledParents[ 0 ];
|
|
||||||
const parentName = getParentMethodName(
|
|
||||||
parentId,
|
|
||||||
methodsMap
|
methodsMap
|
||||||
);
|
);
|
||||||
|
|
||||||
result[ method.id ] = {
|
if ( disabledParents.length > 0 ) {
|
||||||
isDisabled: true,
|
const parentId = disabledParents[ 0 ];
|
||||||
parentId,
|
result[ method.id ] = {
|
||||||
parentName,
|
isDisabled: true,
|
||||||
};
|
parentId,
|
||||||
|
parentName: getParentMethodName(
|
||||||
|
parentId,
|
||||||
|
methodsMap
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
}, [ methods, methodsMap ] );
|
||||||
[ methods, methodsMap ]
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default usePaymentDependencyState;
|
export default usePaymentDependencyState;
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Custom hook to handle setting-based payment method dependencies
|
||||||
|
*/
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check setting dependencies for methods
|
||||||
|
*
|
||||||
|
* @param {Array} methods - Array of methods to check
|
||||||
|
* @return {Object} Setting dependency states mapped by method ID
|
||||||
|
*/
|
||||||
|
const useSettingDependencyState = ( methods ) => {
|
||||||
|
const dependencyState = useSelect(
|
||||||
|
( select ) => {
|
||||||
|
const settingsStore = select( 'wc/paypal/settings' );
|
||||||
|
|
||||||
|
if ( ! settingsStore || ! methods?.length ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get settings data
|
||||||
|
const persistentData = settingsStore.persistentData();
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
// Process each method
|
||||||
|
methods.forEach( ( method ) => {
|
||||||
|
if ( ! method?.id || ! method.depends_on_settings ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the settings object structure
|
||||||
|
if ( method.depends_on_settings.settings ) {
|
||||||
|
const settingsObj = method.depends_on_settings.settings;
|
||||||
|
|
||||||
|
for ( const [ settingId, settingData ] of Object.entries(
|
||||||
|
settingsObj
|
||||||
|
) ) {
|
||||||
|
const requiredId = settingData.id;
|
||||||
|
const requiredValue = settingData.value;
|
||||||
|
|
||||||
|
const actualValue = persistentData[ requiredId ];
|
||||||
|
|
||||||
|
// Check if dependency is satisfied
|
||||||
|
if ( actualValue !== requiredValue ) {
|
||||||
|
result[ method.id ] = {
|
||||||
|
isDisabled: true,
|
||||||
|
settingId: requiredId,
|
||||||
|
requiredValue,
|
||||||
|
};
|
||||||
|
break; // Stop checking once we find a failed dependency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[ methods ]
|
||||||
|
);
|
||||||
|
|
||||||
|
return dependencyState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSettingDependencyState;
|
|
@ -181,7 +181,8 @@ return array(
|
||||||
'settings.rest.payment' => static function ( ContainerInterface $container ) : PaymentRestEndpoint {
|
'settings.rest.payment' => static function ( ContainerInterface $container ) : PaymentRestEndpoint {
|
||||||
return new PaymentRestEndpoint(
|
return new PaymentRestEndpoint(
|
||||||
$container->get( 'settings.data.payment' ),
|
$container->get( 'settings.data.payment' ),
|
||||||
$container->get( 'settings.data.definition.methods' )
|
$container->get( 'settings.data.definition.methods' ),
|
||||||
|
$container->get( 'settings.data.definition.method_dependencies' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'settings.rest.styling' => static function ( ContainerInterface $container ) : StylingRestEndpoint {
|
'settings.rest.styling' => static function ( ContainerInterface $container ) : StylingRestEndpoint {
|
||||||
|
@ -380,12 +381,11 @@ return array(
|
||||||
|
|
||||||
return new PaymentMethodsDefinition(
|
return new PaymentMethodsDefinition(
|
||||||
$container->get( 'settings.data.payment' ),
|
$container->get( 'settings.data.payment' ),
|
||||||
$container->get( 'settings.data.definition.method_dependencies' ),
|
|
||||||
$axo_notices
|
$axo_notices
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'settings.data.definition.method_dependencies' => static function ( ContainerInterface $container ) : PaymentMethodsDependenciesDefinition {
|
'settings.data.definition.method_dependencies' => static function ( ContainerInterface $container ) : PaymentMethodsDependenciesDefinition {
|
||||||
return new PaymentMethodsDependenciesDefinition();
|
return new PaymentMethodsDependenciesDefinition( $container->get( 'wcgateway.settings' ) );
|
||||||
},
|
},
|
||||||
'settings.service.pay_later_status' => static function ( ContainerInterface $container ) : array {
|
'settings.service.pay_later_status' => static function ( ContainerInterface $container ) : array {
|
||||||
$pay_later_endpoint = $container->get( 'settings.rest.pay_later_messaging' );
|
$pay_later_endpoint = $container->get( 'settings.rest.pay_later_messaging' );
|
||||||
|
|
|
@ -40,13 +40,6 @@ class PaymentMethodsDefinition {
|
||||||
*/
|
*/
|
||||||
private PaymentSettings $settings;
|
private PaymentSettings $settings;
|
||||||
|
|
||||||
/**
|
|
||||||
* Payment method dependencies definition.
|
|
||||||
*
|
|
||||||
* @var PaymentMethodsDependenciesDefinition
|
|
||||||
*/
|
|
||||||
private PaymentMethodsDependenciesDefinition $dependencies_definition;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conflict notices for Axo gateway.
|
* Conflict notices for Axo gateway.
|
||||||
*
|
*
|
||||||
|
@ -64,18 +57,15 @@ class PaymentMethodsDefinition {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param PaymentSettings $settings Payment methods data model.
|
* @param PaymentSettings $settings Payment methods data model.
|
||||||
* @param PaymentMethodsDependenciesDefinition $dependencies_definition Payment dependencies definition.
|
* @param array $axo_conflicts_notices Conflicts notices for Axo.
|
||||||
* @param array $axo_conflicts_notices Conflicts notices for Axo.
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
PaymentSettings $settings,
|
PaymentSettings $settings,
|
||||||
PaymentMethodsDependenciesDefinition $dependencies_definition,
|
|
||||||
array $axo_conflicts_notices = array()
|
array $axo_conflicts_notices = array()
|
||||||
) {
|
) {
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
$this->dependencies_definition = $dependencies_definition;
|
$this->axo_conflicts_notices = $axo_conflicts_notices;
|
||||||
$this->axo_conflicts_notices = $axo_conflicts_notices;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,26 +84,16 @@ class PaymentMethodsDefinition {
|
||||||
$result = array();
|
$result = array();
|
||||||
foreach ( $all_methods as $method ) {
|
foreach ( $all_methods as $method ) {
|
||||||
$method_id = $method['id'];
|
$method_id = $method['id'];
|
||||||
// Add dependency info if applicable.
|
|
||||||
$depends_on = $this->dependencies_definition->get_parent_methods( $method_id );
|
|
||||||
if ( ! empty( $depends_on ) ) {
|
|
||||||
$method['depends_on'] = $depends_on;
|
|
||||||
}
|
|
||||||
$result[ $method_id ] = $this->build_method_definition(
|
$result[ $method_id ] = $this->build_method_definition(
|
||||||
$method_id,
|
$method_id,
|
||||||
$method['title'],
|
$method['title'],
|
||||||
$method['description'],
|
$method['description'],
|
||||||
$method['icon'],
|
$method['icon'],
|
||||||
$method['fields'] ?? array(),
|
$method['fields'] ?? array(),
|
||||||
$depends_on,
|
$method['warningMessages'] ?? array(),
|
||||||
$method['warningMessages'] ?? array()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Add dependency maps to metadata.
|
|
||||||
$result['__meta'] = array(
|
|
||||||
'dependencies' => $this->dependencies_definition->get_dependencies(),
|
|
||||||
'dependents' => $this->dependencies_definition->get_dependents_map(),
|
|
||||||
);
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,14 +101,13 @@ class PaymentMethodsDefinition {
|
||||||
* Returns a new payment method configuration array that contains all
|
* Returns a new payment method configuration array that contains all
|
||||||
* common attributes which must be present in every method definition.
|
* common attributes which must be present in every method definition.
|
||||||
*
|
*
|
||||||
* @param string $gateway_id The payment method ID.
|
* @param string $gateway_id The payment method ID.
|
||||||
* @param string $title Admin-side payment method title.
|
* @param string $title Admin-side payment method title.
|
||||||
* @param string $description Admin-side info about the payment method.
|
* @param string $description Admin-side info about the payment method.
|
||||||
* @param string $icon Admin-side icon of the payment method.
|
* @param string $icon Admin-side icon of the payment method.
|
||||||
* @param array|false $fields Optional. Additional fields to display in the edit modal.
|
* @param array|false $fields Optional. Additional fields to display in the edit modal.
|
||||||
* Setting this to false omits all fields.
|
* Setting this to false omits all fields.
|
||||||
* @param array $depends_on Optional. IDs of payment methods that this depends on.
|
* @param array $warning_messages Optional. Warning messages to display in the UI.
|
||||||
* @param array $warning_messages Optional. Warning messages to display in the UI.
|
|
||||||
* @return array Payment method definition.
|
* @return array Payment method definition.
|
||||||
*/
|
*/
|
||||||
private function build_method_definition(
|
private function build_method_definition(
|
||||||
|
@ -136,8 +115,7 @@ class PaymentMethodsDefinition {
|
||||||
string $title,
|
string $title,
|
||||||
string $description,
|
string $description,
|
||||||
string $icon,
|
string $icon,
|
||||||
$fields = array(),
|
$fields = array(),
|
||||||
array $depends_on = array(),
|
|
||||||
array $warning_messages = array()
|
array $warning_messages = array()
|
||||||
) : array {
|
) : array {
|
||||||
$gateway = $this->wc_gateways[ $gateway_id ] ?? null;
|
$gateway = $this->wc_gateways[ $gateway_id ] ?? null;
|
||||||
|
@ -156,11 +134,6 @@ class PaymentMethodsDefinition {
|
||||||
'warningMessages' => $warning_messages,
|
'warningMessages' => $warning_messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add dependency information if provided - ensure it's included directly in the config.
|
|
||||||
if ( ! empty( $depends_on ) ) {
|
|
||||||
$config['depends_on'] = $depends_on;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( is_array( $fields ) ) {
|
if ( is_array( $fields ) ) {
|
||||||
$config['fields'] = array_merge(
|
$config['fields'] = array_merge(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -9,6 +9,8 @@ declare( strict_types = 1 );
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Settings\Data\Definition;
|
namespace WooCommerce\PayPalCommerce\Settings\Data\Definition;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||||
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
||||||
|
@ -29,19 +31,35 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGa
|
||||||
/**
|
/**
|
||||||
* Class PaymentMethodsDependenciesDefinition
|
* Class PaymentMethodsDependenciesDefinition
|
||||||
*
|
*
|
||||||
* Defines dependency relationships between payment methods.
|
* Defines dependency relationships between payment methods and settings.
|
||||||
*/
|
*/
|
||||||
class PaymentMethodsDependenciesDefinition {
|
class PaymentMethodsDependenciesDefinition {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all payment method dependencies
|
* Current settings values
|
||||||
|
*
|
||||||
|
* @var Settings
|
||||||
|
*/
|
||||||
|
private Settings $settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Settings $settings Settings instance.
|
||||||
|
*/
|
||||||
|
public function __construct( Settings $settings ) {
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get payment method to payment method dependencies
|
||||||
*
|
*
|
||||||
* Maps dependent method ID => array of parent method IDs.
|
* Maps dependent method ID => array of parent method IDs.
|
||||||
* A dependent method is disabled if ANY of its required parents is disabled.
|
* A dependent method is disabled if ANY of its required parents is disabled.
|
||||||
*
|
*
|
||||||
* @return array The dependency relationships between payment methods
|
* @return array The dependency relationships between payment methods
|
||||||
*/
|
*/
|
||||||
public function get_dependencies(): array {
|
public function get_payment_method_dependencies(): array {
|
||||||
$dependencies = array(
|
$dependencies = array(
|
||||||
CardButtonGateway::ID => array( PayPalGateway::ID ),
|
CardButtonGateway::ID => array( PayPalGateway::ID ),
|
||||||
CreditCardGateway::ID => array( PayPalGateway::ID ),
|
CreditCardGateway::ID => array( PayPalGateway::ID ),
|
||||||
|
@ -69,43 +87,114 @@ class PaymentMethodsDependenciesDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mapping from parent methods to their dependent methods
|
* Get setting to payment method dependencies.
|
||||||
*
|
*
|
||||||
* @return array Parent-to-child dependency map
|
* Maps method ID => array of required settings with their values.
|
||||||
|
* A method is disabled if ANY of its required settings doesn't match the required value.
|
||||||
|
*
|
||||||
|
* @return array The dependency relationships between settings and payment methods
|
||||||
*/
|
*/
|
||||||
public function get_dependents_map(): array {
|
public function get_setting_dependencies(): array {
|
||||||
$result = array();
|
$dependencies = array(
|
||||||
$dependencies = $this->get_dependencies();
|
'pay-later' => array(
|
||||||
|
'savePaypalAndVenmo' => false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $dependencies as $child_id => $parent_ids ) {
|
return apply_filters(
|
||||||
foreach ( $parent_ids as $parent_id ) {
|
'woocommerce_paypal_payments_setting_dependencies',
|
||||||
if ( ! isset( $result[ $parent_id ] ) ) {
|
$dependencies
|
||||||
$result[ $parent_id ] = array();
|
);
|
||||||
}
|
|
||||||
$result[ $parent_id ][] = $child_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all parent methods that a method depends on
|
* Get method setting dependencies
|
||||||
|
*
|
||||||
|
* Returns the setting dependencies for a specific method ID.
|
||||||
|
*
|
||||||
|
* @param string $method_id Method ID to check.
|
||||||
|
* @return array Setting dependencies for the method or empty array if none exist
|
||||||
|
*/
|
||||||
|
public function get_method_setting_dependencies( string $method_id ): array {
|
||||||
|
$setting_dependencies = $this->get_setting_dependencies();
|
||||||
|
return $setting_dependencies[ $method_id ] ?? array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add dependency information to the payment method definitions
|
||||||
|
*
|
||||||
|
* @param array $methods Payment method definitions.
|
||||||
|
* @return array Payment method definitions with dependency information
|
||||||
|
*/
|
||||||
|
public function add_dependency_info_to_methods( array $methods ): array {
|
||||||
|
foreach ( $methods as $method_id => &$method ) {
|
||||||
|
// Skip the __meta key.
|
||||||
|
if ( $method_id === '__meta' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add payment method dependency info if applicable.
|
||||||
|
$payment_method_dependencies = $this->get_method_payment_method_dependencies( $method_id );
|
||||||
|
if ( ! empty( $payment_method_dependencies ) ) {
|
||||||
|
$method['depends_on_payment_methods'] = $payment_method_dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this method has setting dependencies.
|
||||||
|
$method_setting_dependencies = $this->get_method_setting_dependencies( $method_id );
|
||||||
|
if ( ! empty( $method_setting_dependencies ) ) {
|
||||||
|
$settings = array();
|
||||||
|
foreach ( $method_setting_dependencies as $setting_id => $required_value ) {
|
||||||
|
$settings[ $setting_id ] = array(
|
||||||
|
'id' => $setting_id,
|
||||||
|
'value' => $required_value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$method['depends_on_settings'] = array(
|
||||||
|
'settings' => $settings,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add global metadata about settings that affect dependencies.
|
||||||
|
if ( ! isset( $methods['__meta'] ) ) {
|
||||||
|
$methods['__meta'] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$methods['__meta']['settings_affecting_methods'] = $this->get_all_dependent_settings();
|
||||||
|
|
||||||
|
return $methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get payment method dependencies for a specific method
|
||||||
*
|
*
|
||||||
* @param string $method_id Method ID to check.
|
* @param string $method_id Method ID to check.
|
||||||
* @return array Array of parent method IDs
|
* @return array Array of parent method IDs
|
||||||
*/
|
*/
|
||||||
public function get_parent_methods( string $method_id ): array {
|
public function get_method_payment_method_dependencies( string $method_id ): array {
|
||||||
return $this->get_dependencies()[ $method_id ] ?? array();
|
return $this->get_payment_method_dependencies()[ $method_id ] ?? array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get methods that depend on a parent method
|
* Get all settings that affect payment methods
|
||||||
*
|
*
|
||||||
* @param string $parent_id Parent method ID.
|
* @return array Array of unique setting keys that affect payment methods
|
||||||
* @return array Array of dependent method IDs
|
|
||||||
*/
|
*/
|
||||||
public function get_dependent_methods( string $parent_id ): array {
|
public function get_all_dependent_settings(): array {
|
||||||
return $this->get_dependents_map()[ $parent_id ] ?? array();
|
$settings = array();
|
||||||
|
$dependencies = $this->get_setting_dependencies();
|
||||||
|
|
||||||
|
foreach ( $dependencies as $method_settings ) {
|
||||||
|
if ( isset( $method_settings['settings'] ) ) {
|
||||||
|
foreach ( $method_settings['settings'] as $setting_data ) {
|
||||||
|
if ( ! in_array( $setting_data['id'], $settings, true ) ) {
|
||||||
|
$settings[] = $setting_data['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $settings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ use WP_REST_Request;
|
||||||
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\EPSGateway;
|
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\EPSGateway;
|
||||||
use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDefinition;
|
use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDefinition;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDependenciesDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* REST controller for the "Payment Methods" settings tab.
|
* REST controller for the "Payment Methods" settings tab.
|
||||||
|
@ -50,14 +51,21 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
*
|
*
|
||||||
* @var PaymentSettings
|
* @var PaymentSettings
|
||||||
*/
|
*/
|
||||||
protected PaymentSettings $settings;
|
protected PaymentSettings $payment_settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The payment method details.
|
* The payment method details.
|
||||||
*
|
*
|
||||||
* @var PaymentMethodsDefinition
|
* @var PaymentMethodsDefinition
|
||||||
*/
|
*/
|
||||||
protected PaymentMethodsDefinition $methods_definition;
|
protected PaymentMethodsDefinition $payment_methods_definition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The payment method dependencies.
|
||||||
|
*
|
||||||
|
* @var PaymentMethodsDependenciesDefinition
|
||||||
|
*/
|
||||||
|
protected PaymentMethodsDependenciesDefinition $payment_methods_dependencies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field mapping for request to profile transformation.
|
* Field mapping for request to profile transformation.
|
||||||
|
@ -86,12 +94,18 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param PaymentSettings $settings The settings instance.
|
* @param PaymentSettings $payment_settings The settings instance.
|
||||||
* @param PaymentMethodsDefinition $methods_definition Payment Method details.
|
* @param PaymentMethodsDefinition $payment_methods_definition Payment Method details.
|
||||||
|
* @param PaymentMethodsDependenciesDefinition $payment_methods_dependencies The payment method dependencies.
|
||||||
*/
|
*/
|
||||||
public function __construct( PaymentSettings $settings, PaymentMethodsDefinition $methods_definition ) {
|
public function __construct(
|
||||||
$this->settings = $settings;
|
PaymentSettings $payment_settings,
|
||||||
$this->methods_definition = $methods_definition;
|
PaymentMethodsDefinition $payment_methods_definition,
|
||||||
|
PaymentMethodsDependenciesDefinition $payment_methods_dependencies
|
||||||
|
) {
|
||||||
|
$this->payment_settings = $payment_settings;
|
||||||
|
$this->payment_methods_definition = $payment_methods_definition;
|
||||||
|
$this->payment_methods_dependencies = $payment_methods_dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,7 +114,10 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
protected function gateways() : array {
|
protected function gateways() : array {
|
||||||
return $this->methods_definition->get_definitions();
|
$methods = $this->payment_methods_definition->get_definitions();
|
||||||
|
|
||||||
|
// Add dependency information to the methods.
|
||||||
|
return $this->payment_methods_dependencies->add_dependency_info_to_methods( $methods );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,45 +164,48 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
* @return WP_REST_Response The current payment methods details.
|
* @return WP_REST_Response The current payment methods details.
|
||||||
*/
|
*/
|
||||||
public function get_details() : WP_REST_Response {
|
public function get_details() : WP_REST_Response {
|
||||||
$gateway_settings = array();
|
$gateway_settings = array();
|
||||||
$all_methods = $this->gateways();
|
$all_payment_methods = $this->gateways();
|
||||||
|
|
||||||
// First extract __meta if present.
|
// First extract __meta if present.
|
||||||
if ( isset( $all_methods['__meta'] ) ) {
|
if ( isset( $all_payment_methods['__meta'] ) ) {
|
||||||
$gateway_settings['__meta'] = $all_methods['__meta'];
|
$gateway_settings['__meta'] = $all_payment_methods['__meta'];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $all_methods as $key => $method ) {
|
foreach ( $all_payment_methods as $key => $payment_method ) {
|
||||||
// Skip the __meta key as we've already handled it.
|
// Skip the __meta key as we've already handled it.
|
||||||
if ( $key === '__meta' ) {
|
if ( $key === '__meta' ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$gateway_settings[ $key ] = array(
|
$gateway_settings[ $key ] = array(
|
||||||
'id' => $method['id'],
|
'id' => $payment_method['id'],
|
||||||
'title' => $method['title'],
|
'title' => $payment_method['title'],
|
||||||
'description' => $method['description'],
|
'description' => $payment_method['description'],
|
||||||
'enabled' => $method['enabled'],
|
'enabled' => $payment_method['enabled'],
|
||||||
'icon' => $method['icon'],
|
'icon' => $payment_method['icon'],
|
||||||
'itemTitle' => $method['itemTitle'],
|
'itemTitle' => $payment_method['itemTitle'],
|
||||||
'itemDescription' => $method['itemDescription'],
|
'itemDescription' => $payment_method['itemDescription'],
|
||||||
'warningMessages' => $method['warningMessages'],
|
'warningMessages' => $payment_method['warningMessages'],
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( isset( $method['fields'] ) ) {
|
if ( isset( $payment_method['fields'] ) ) {
|
||||||
$gateway_settings[ $key ]['fields'] = $method['fields'];
|
$gateway_settings[ $key ]['fields'] = $payment_method['fields'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve dependency information.
|
if ( isset( $payment_method['depends_on_payment_methods'] ) ) {
|
||||||
if ( isset( $method['depends_on'] ) ) {
|
$gateway_settings[ $key ]['depends_on_payment_methods'] = $payment_method['depends_on_payment_methods'];
|
||||||
$gateway_settings[ $key ]['depends_on'] = $method['depends_on'];
|
}
|
||||||
|
|
||||||
|
if ( isset( $payment_method['depends_on_settings'] ) ) {
|
||||||
|
$gateway_settings[ $key ]['depends_on_settings'] = $payment_method['depends_on_settings'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$gateway_settings['paypalShowLogo'] = $this->settings->get_paypal_show_logo();
|
$gateway_settings['paypalShowLogo'] = $this->payment_settings->get_paypal_show_logo();
|
||||||
$gateway_settings['threeDSecure'] = $this->settings->get_three_d_secure();
|
$gateway_settings['threeDSecure'] = $this->payment_settings->get_three_d_secure();
|
||||||
$gateway_settings['fastlaneCardholderName'] = $this->settings->get_fastlane_cardholder_name();
|
$gateway_settings['fastlaneCardholderName'] = $this->payment_settings->get_fastlane_cardholder_name();
|
||||||
$gateway_settings['fastlaneDisplayWatermark'] = $this->settings->get_fastlane_display_watermark();
|
$gateway_settings['fastlaneDisplayWatermark'] = $this->payment_settings->get_fastlane_display_watermark();
|
||||||
|
|
||||||
return $this->return_success( apply_filters( 'woocommerce_paypal_payments_payment_methods', $gateway_settings ) );
|
return $this->return_success( apply_filters( 'woocommerce_paypal_payments_payment_methods', $gateway_settings ) );
|
||||||
}
|
}
|
||||||
|
@ -202,21 +222,21 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
$all_methods = $this->gateways();
|
$all_methods = $this->gateways();
|
||||||
|
|
||||||
foreach ( $all_methods as $key => $value ) {
|
foreach ( $all_methods as $key => $value ) {
|
||||||
$new_data = $request_data[ $key ];
|
$new_data = $request_data[ $key ] ?? null;
|
||||||
if ( ! $new_data ) {
|
if ( ! $new_data ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $new_data['enabled'] ) ) {
|
if ( isset( $new_data['enabled'] ) ) {
|
||||||
$this->settings->toggle_method_state( $key, $new_data['enabled'] );
|
$this->payment_settings->toggle_method_state( $key, $new_data['enabled'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $new_data['title'] ) ) {
|
if ( isset( $new_data['title'] ) ) {
|
||||||
$this->settings->set_method_title( $key, sanitize_text_field( $new_data['title'] ) );
|
$this->payment_settings->set_method_title( $key, sanitize_text_field( $new_data['title'] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $new_data['description'] ) ) {
|
if ( isset( $new_data['description'] ) ) {
|
||||||
$this->settings->set_method_description( $key, wp_kses_post( $new_data['description'] ) );
|
$this->payment_settings->set_method_description( $key, wp_kses_post( $new_data['description'] ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,8 +245,8 @@ class PaymentRestEndpoint extends RestEndpoint {
|
||||||
$this->field_map
|
$this->field_map
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->settings->from_array( $wp_data );
|
$this->payment_settings->from_array( $wp_data );
|
||||||
$this->settings->save();
|
$this->payment_settings->save();
|
||||||
|
|
||||||
return $this->get_details();
|
return $this->get_details();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue