Clean up the Features store a bit and split the TabOverview into separate files

This commit is contained in:
Daniel Dudzic 2025-02-24 15:46:34 +01:00
parent 1497cbae52
commit 84854e737a
No known key found for this signature in database
GPG key ID: 31B40D33E3465483
18 changed files with 643 additions and 901 deletions

View file

@ -0,0 +1,36 @@
import { __ } from '@wordpress/i18n';
import { Button, Icon } from '@wordpress/components';
import { reusableBlock } from '@wordpress/icons';
const FeatureDescription = ( { refreshHandler, isRefreshing } ) => {
const buttonLabel = isRefreshing
? __( 'Refreshing…', 'woocommerce-paypal-payments' )
: __( 'Refresh', 'woocommerce-paypal-payments' );
return (
<>
<p>
{ __(
'Enable additional features and capabilities on your WooCommerce store.',
'woocommerce-paypal-payments'
) }
</p>
<p>
{ __(
'Click Refresh to update your current features after making changes.',
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ refreshHandler }
disabled={ isRefreshing }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ buttonLabel }
</Button>
</>
);
};
export default FeatureDescription;

View file

@ -0,0 +1,70 @@
import { __ } from '@wordpress/i18n';
import { FeatureSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
import { Content } from '../../../../../ReusableComponents/Elements';
import { TITLE_BADGE_POSITIVE } from '../../../../../ReusableComponents/TitleBadge';
import { selectTab, TAB_IDS } from '../../../../../../utils/tabSelector';
import { setActiveModal } from '../../../../../../data/common/actions';
const FeatureItem = ( {
isBusy,
isSandbox,
title,
description,
buttons,
enabled,
notes,
} ) => {
const getButtonUrl = ( button ) => {
if ( button.urls ) {
return isSandbox ? button.urls.sandbox : button.urls.live;
}
return button.url;
};
const visibleButtons = buttons.filter(
( button ) =>
! button.showWhen || // Learn more buttons
( enabled && button.showWhen === 'enabled' ) ||
( ! enabled && button.showWhen === 'disabled' )
);
const handleClick = async ( feature ) => {
if ( feature.action?.type === 'tab' ) {
const tabId = TAB_IDS[ feature.action.tab.toUpperCase() ];
await selectTab( tabId, feature.action.section );
}
if ( feature.action?.modal ) {
setActiveModal( feature.action.modal );
}
};
const actionProps = {
isBusy,
enabled,
notes,
buttons: visibleButtons.map( ( button ) => ( {
...button,
url: getButtonUrl( button ),
onClick: () => handleClick( button ),
} ) ),
};
if ( enabled ) {
actionProps.badge = {
text: __( 'Active', 'woocommerce-paypal-payments' ),
type: TITLE_BADGE_POSITIVE,
};
}
return (
<Content>
<FeatureSettingsBlock
title={ title }
description={ description }
actionProps={ actionProps }
/>
</Content>
);
};
export default FeatureItem;

View file

@ -0,0 +1,95 @@
import { __, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
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 { STORE_NAME as COMMON_STORE_NAME } from '../../../../../../data/common';
import {
NOTIFICATION_ERROR,
NOTIFICATION_SUCCESS,
} from '../../../../../ReusableComponents/Icons';
import { useFeatures } from '../../../../../../data/features/hooks';
const Features = () => {
const [ isRefreshing, setIsRefreshing ] = useState( false );
const { merchant } = useMerchantInfo();
const { features, fetchFeatures } = useFeatures();
const { refreshFeatureStatuses } = useDispatch( COMMON_STORE_NAME );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
if ( ! features || features.length === 0 ) {
return null;
}
const refreshHandler = async () => {
setIsRefreshing( true );
try {
const statusResult = await refreshFeatureStatuses();
if ( ! statusResult?.success ) {
throw new Error(
statusResult?.message || 'Failed to refresh status'
);
}
const featuresResult = await fetchFeatures();
if ( featuresResult.success ) {
createSuccessNotice(
__(
'Features refreshed successfully.',
'woocommerce-paypal-payments'
),
{ icon: NOTIFICATION_SUCCESS }
);
} else {
throw new Error(
featuresResult?.message || 'Failed to fetch features'
);
}
} catch ( error ) {
createErrorNotice(
sprintf(
/* translators: %s: error message */
__( 'Operation failed: %s', 'woocommerce-paypal-payments' ),
error.message ||
__( 'Unknown error', 'woocommerce-paypal-payments' )
),
{ icon: NOTIFICATION_ERROR }
);
} finally {
setIsRefreshing( false );
}
};
return (
<SettingsCard
className="ppcp-r-tab-overview-features"
title={ __( 'Features', 'woocommerce-paypal-payments' ) }
description={
<FeatureDescription
refreshHandler={ refreshHandler }
isRefreshing={ isRefreshing }
/>
}
contentContainer={ false }
>
<ContentWrapper>
{ features.map( ( { id, isEligible, ...feature } ) => (
<FeatureItem
key={ id }
isBusy={ isRefreshing }
isSandbox={ merchant.isSandbox }
enabled={ isEligible }
{ ...feature }
/>
) ) }
</ContentWrapper>
</SettingsCard>
);
};
export default Features;

View file

@ -0,0 +1,72 @@
import { __ } from '@wordpress/i18n';
import { FeatureSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
import {
Content,
ContentWrapper,
} from '../../../../../ReusableComponents/Elements';
import SettingsCard from '../../../../../ReusableComponents/SettingsCard';
const Help = () => {
return (
<SettingsCard
className="ppcp-r-tab-overview-help"
title={ __( 'Help Center', 'woocommerce-paypal-payments' ) }
description={ __(
'Access detailed guides and responsive support to streamline setup and enhance your experience.',
'woocommerce-paypal-payments'
) }
contentContainer={ false }
>
<ContentWrapper>
<Content>
<FeatureSettingsBlock
title={ __(
'Documentation',
'woocommerce-paypal-payments'
) }
description={ __(
'Find detailed guides and resources to help you set up, manage, and optimize your PayPal integration.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttons: [
{
type: 'tertiary',
text: __(
'View full documentation',
'woocommerce-paypal-payments'
),
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/',
},
],
} }
/>
</Content>
<Content>
<FeatureSettingsBlock
title={ __( 'Support', 'woocommerce-paypal-payments' ) }
description={ __(
'Need help? Access troubleshooting tips or contact our support team for personalized assistance.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttons: [
{
type: 'tertiary',
text: __(
'View support options',
'woocommerce-paypal-payments'
),
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/#get-help ',
},
],
} }
/>
</Content>
</ContentWrapper>
</SettingsCard>
);
};
export default Help;

View file

@ -0,0 +1,86 @@
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { Button, Icon } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { reusableBlock } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
import { TodoSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
import SettingsCard from '../../../../../ReusableComponents/SettingsCard';
import { useTodos } from '../../../../../../data/todos/hooks';
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../../../data/common';
import { STORE_NAME as TODOS_STORE_NAME } from '../../../../../../data/todos';
import { NOTIFICATION_SUCCESS } from '../../../../../ReusableComponents/Icons';
const Todos = () => {
const [ isResetting, setIsResetting ] = useState( false );
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
// eslint-disable-next-line no-shadow
const { setActiveModal, setActiveHighlight } =
useDispatch( COMMON_STORE_NAME );
const { resetDismissedTodos, setDismissedTodos } =
useDispatch( TODOS_STORE_NAME );
const { createSuccessNotice } = useDispatch( noticesStore );
const showTodos = areTodosReady && todos.length > 0;
const resetHandler = async () => {
setIsResetting( true );
try {
await setDismissedTodos( [] );
await resetDismissedTodos();
createSuccessNotice(
__(
'Dismissed items restored successfully.',
'woocommerce-paypal-payments'
),
{ icon: NOTIFICATION_SUCCESS }
);
} finally {
setIsResetting( false );
}
};
if ( ! showTodos ) {
return null;
}
return (
<SettingsCard
className="ppcp-r-tab-overview-todo"
title={ __( 'Things to do next', 'woocommerce-paypal-payments' ) }
description={
<>
<p>
{ __(
'Complete these tasks to keep your store updated with the latest products and services.',
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ resetHandler }
disabled={ isResetting }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ isResetting
? __( 'Restoring…', 'woocommerce-paypal-payments' )
: __(
'Restore dismissed Things To Do',
'woocommerce-paypal-payments'
) }
</Button>
</>
}
>
<TodoSettingsBlock
todosData={ todos }
setActiveModal={ setActiveModal }
setActiveHighlight={ setActiveHighlight }
onDismissTodo={ dismissTodo }
/>
</SettingsCard>
);
};
export default Todos;

View file

@ -1,278 +0,0 @@
import { __ } from '@wordpress/i18n';
import { TAB_IDS, selectTab } from '../../../../../utils/tabSelector';
import { payLaterMessaging } from './pay-later-messaging';
export const getFeatures = ( setActiveModal ) => {
const storeCountry = ppcpSettings?.storeCountry;
const features = [
{
id: 'save_paypal_and_venmo',
title: __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ),
description: __(
'Securely save PayPal and Venmo payment methods for subscriptions or return buyers.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab(
TAB_IDS.SETTINGS,
'ppcp--save-payment-methods'
);
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __( 'Sign up', 'woocommerce-paypal-payments' ),
urls: {
sandbox:
'https://www.sandbox.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
live: 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: 'https://www.paypal.com/us/enterprise/payment-processing/accept-venmo',
class: 'small-button',
},
],
},
{
id: 'advanced_credit_and_debit_cards',
title: __(
'Advanced Credit and Debit Cards',
'woocommerce-paypal-payments'
),
description: __(
'Process major credit and debit cards including Visa, Mastercard, American Express and Discover.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab(
TAB_IDS.PAYMENT_METHODS,
'ppcp-card-payments-card'
).then( () => {
setActiveModal( 'ppcp-credit-card-gateway' );
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __( 'Sign up', 'woocommerce-paypal-payments' ),
urls: {
sandbox:
'https://www.sandbox.paypal.com/bizsignup/entry?product=ppcp',
live: 'https://www.paypal.com/bizsignup/entry?product=ppcp',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: 'https://developer.paypal.com/studio/checkout/advanced',
class: 'small-button',
},
],
},
{
id: 'alternative_payment_methods',
title: __(
'Alternative Payment Methods',
'woocommerce-paypal-payments'
),
description: __(
'Offer global, country-specific payment options for your customers.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab(
TAB_IDS.PAYMENT_METHODS,
'ppcp-alternative-payments-card'
);
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __( 'Sign up', 'woocommerce-paypal-payments' ),
url: 'https://developer.paypal.com/docs/checkout/apm/',
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: 'https://developer.paypal.com/docs/checkout/apm/',
class: 'small-button',
},
],
},
{
id: 'google_pay',
title: __( 'Google Pay', 'woocommerce-paypal-payments' ),
description: __(
'Let customers pay using their Google Pay wallet.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab(
TAB_IDS.PAYMENT_METHODS,
'ppcp-card-payments-card'
).then( () => {
setActiveModal( 'ppcp-googlepay' );
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __( 'Sign up', 'woocommerce-paypal-payments' ),
urls: {
sandbox:
'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: 'https://developer.paypal.com/docs/checkout/apm/google-pay/',
class: 'small-button',
},
],
notes: [
__(
'¹PayPal Q2 Earnings-2021.',
'woocommerce-paypal-payments'
),
],
},
{
id: 'apple_pay',
title: __( 'Apple Pay', 'woocommerce-paypal-payments' ),
description: __(
'Let customers pay using their Apple Pay wallet.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab(
TAB_IDS.PAYMENT_METHODS,
'ppcp-card-payments-card'
).then( () => {
setActiveModal( 'ppcp-applepay' );
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __(
'Domain registration',
'woocommerce-paypal-payments'
),
urls: {
sandbox:
'https://www.sandbox.paypal.com/uccservicing/apm/applepay',
live: 'https://www.paypal.com/uccservicing/apm/applepay',
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
text: __( 'Sign up', 'woocommerce-paypal-payments' ),
urls: {
sandbox:
'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: 'https://developer.paypal.com/docs/checkout/apm/apple-pay/',
class: 'small-button',
},
],
},
];
const countryData = payLaterMessaging[ storeCountry ] || {};
if (
!! window.ppcpSettings?.isPayLaterConfiguratorAvailable &&
countryData
) {
const countryLocation = [
'UK',
'ES',
'IT',
'FR',
'US',
'DE',
'AU',
].includes( storeCountry )
? storeCountry.toLowerCase()
: 'us';
features.push( {
id: 'pay_later_messaging',
title: __( 'Pay Later Messaging', 'woocommerce-paypal-payments' ),
description: __(
'Let customers know they can buy now and pay later with PayPal. Adding this messaging can boost conversion rates and increase cart sizes by 39%¹, with no extra cost to you—plus, you get paid up front.',
'woocommerce-paypal-payments'
),
buttons: [
{
type: 'secondary',
text: __( 'Configure', 'woocommerce-paypal-payments' ),
onClick: () => {
selectTab( TAB_IDS.PAY_LATER_MESSAGING );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'tertiary',
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
url: `https://www.paypal.com/${ countryLocation }/business/accept-payments/checkout/installments`,
class: 'small-button',
},
],
} );
}
return features;
};

View file

@ -1,356 +1,25 @@
import { __, sprintf } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { Button, Icon } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { reusableBlock } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
import {
TodoSettingsBlock,
FeatureSettingsBlock,
} from '../../../ReusableComponents/SettingsBlocks';
import { Content, ContentWrapper } from '../../../ReusableComponents/Elements';
import SettingsCard from '../../../ReusableComponents/SettingsCard';
import { TITLE_BADGE_POSITIVE } from '../../../ReusableComponents/TitleBadge';
import { useTodos } from '../../../../data/todos/hooks';
import { useMerchantInfo } from '../../../../data/common/hooks';
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../data/common';
import { STORE_NAME as TODOS_STORE_NAME } from '../../../../data/todos';
import { CommonHooks, TodosHooks } from '../../../../data';
import {
NOTIFICATION_ERROR,
NOTIFICATION_SUCCESS,
} from '../../../ReusableComponents/Icons';
import Todos from '../Components/Overview/Todos/Todos';
import Features from '../Components/Overview/Features/Features';
import Help from '../Components/Overview/Help/Help';
import { TodosHooks, CommonHooks, FeaturesHooks } from '../../../../data';
import SpinnerOverlay from '../../../ReusableComponents/SpinnerOverlay';
import { useFeatures } from '../../../../data/features/hooks';
import { selectTab, TAB_IDS } from '../../../../utils/tabSelector';
import { setActiveModal } from '../../../../data/common/actions';
const TabOverview = () => {
const { isReady: areTodosReady } = TodosHooks.useTodos();
const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo();
const { isReady: featuresIsReady } = FeaturesHooks.useFeatures();
if ( ! areTodosReady || ! merchantIsReady ) {
if ( ! areTodosReady || ! merchantIsReady || ! featuresIsReady ) {
return <SpinnerOverlay asModal={ true } />;
}
return (
<div className="ppcp-r-tab-overview">
<OverviewTodos />
<OverviewFeatures />
<OverviewHelp />
<Todos />
<Features />
<Help />
</div>
);
};
export default TabOverview;
const OverviewTodos = () => {
const [ isResetting, setIsResetting ] = useState( false );
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
// eslint-disable-next-line no-shadow
const { setActiveModal, setActiveHighlight } =
useDispatch( COMMON_STORE_NAME );
const { resetDismissedTodos, setDismissedTodos } =
useDispatch( TODOS_STORE_NAME );
const { createSuccessNotice } = useDispatch( noticesStore );
const showTodos = areTodosReady && todos.length > 0;
const resetHandler = async () => {
setIsResetting( true );
try {
await setDismissedTodos( [] );
await resetDismissedTodos();
createSuccessNotice(
__(
'Dismissed items restored successfully.',
'woocommerce-paypal-payments'
),
{ icon: NOTIFICATION_SUCCESS }
);
} finally {
setIsResetting( false );
}
};
if ( ! showTodos ) {
return null;
}
return (
<SettingsCard
className="ppcp-r-tab-overview-todo"
title={ __( 'Things to do next', 'woocommerce-paypal-payments' ) }
description={
<>
<p>
{ __(
'Complete these tasks to keep your store updated with the latest products and services.',
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ resetHandler }
disabled={ isResetting }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ isResetting
? __( 'Restoring…', 'woocommerce-paypal-payments' )
: __(
'Restore dismissed Things To Do',
'woocommerce-paypal-payments'
) }
</Button>
</>
}
>
<TodoSettingsBlock
todosData={ todos }
setActiveModal={ setActiveModal }
setActiveHighlight={ setActiveHighlight }
onDismissTodo={ dismissTodo }
/>
</SettingsCard>
);
};
const OverviewFeatures = () => {
const [ isRefreshing, setIsRefreshing ] = useState( false );
const { merchant } = useMerchantInfo();
const { refreshFeatureStatuses } = useDispatch( COMMON_STORE_NAME );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { features, fetchFeatures } = useFeatures();
useEffect( () => {
fetchFeatures();
}, [ fetchFeatures ] );
const refreshHandler = async () => {
setIsRefreshing( true );
try {
const result = await refreshFeatureStatuses();
if ( result && ! result.success ) {
const errorMessage = sprintf(
/* translators: %s: error message */
__(
'Operation failed: %s Check WooCommerce logs for more details.',
'woocommerce-paypal-payments'
),
result.message ||
__( 'Unknown error', 'woocommerce-paypal-payments' )
);
createErrorNotice( errorMessage, {
icon: NOTIFICATION_ERROR,
} );
console.error(
'Failed to refresh features:',
result.message || 'Unknown error'
);
} else {
createSuccessNotice(
__(
'Features refreshed successfully.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_SUCCESS,
}
);
}
} finally {
setIsRefreshing( false );
}
};
return (
<SettingsCard
className="ppcp-r-tab-overview-features"
title={ __( 'Features', 'woocommerce-paypal-payments' ) }
description={
<OverviewFeatureDescription
refreshHandler={ refreshHandler }
isRefreshing={ isRefreshing }
/>
}
contentContainer={ false }
>
<ContentWrapper>
{ features.map( ( { id, ...feature } ) => (
<OverviewFeatureItem
key={ id }
isBusy={ isRefreshing }
isSandbox={ merchant.isSandbox }
title={ feature.title }
description={ feature.description }
buttons={ feature.buttons }
enabled={ feature.isEligible }
notes={ feature.notes }
/>
) ) }
</ContentWrapper>
</SettingsCard>
);
};
const OverviewFeatureItem = ( {
isBusy,
isSandbox,
title,
description,
buttons,
enabled,
notes,
} ) => {
const getButtonUrl = ( button ) => {
if ( button.urls ) {
return isSandbox ? button.urls.sandbox : button.urls.live;
}
return button.url;
};
const visibleButtons = buttons.filter(
( button ) =>
! button.showWhen || // Learn more buttons
( enabled && button.showWhen === 'enabled' ) ||
( ! enabled && button.showWhen === 'disabled' )
);
const handleClick = async ( feature ) => {
if ( feature.action?.type === 'tab' ) {
const tabId = TAB_IDS[ feature.action.tab.toUpperCase() ];
await selectTab( tabId, feature.action.section );
}
if ( feature.action?.modal ) {
setActiveModal( feature.action.modal );
}
};
const actionProps = {
isBusy,
enabled,
notes,
buttons: visibleButtons.map( ( button ) => ( {
...button,
url: getButtonUrl( button ),
onClick: () => handleClick( button ),
} ) ),
};
if ( enabled ) {
actionProps.badge = {
text: __( 'Active', 'woocommerce-paypal-payments' ),
type: TITLE_BADGE_POSITIVE,
};
}
return (
<Content>
<FeatureSettingsBlock
title={ title }
description={ description }
actionProps={ actionProps }
/>
</Content>
);
};
const OverviewFeatureDescription = ( { refreshHandler, isRefreshing } ) => {
const buttonLabel = isRefreshing
? __( 'Refreshing…', 'woocommerce-paypal-payments' )
: __( 'Refresh', 'woocommerce-paypal-payments' );
return (
<>
<p>
{ __(
'Enable additional features and capabilities on your WooCommerce store.',
'woocommerce-paypal-payments'
) }
</p>
<p>
{ __(
'Click Refresh to update your current features after making changes.',
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ refreshHandler }
disabled={ isRefreshing }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ buttonLabel }
</Button>
</>
);
};
const OverviewHelp = () => {
return (
<SettingsCard
className="ppcp-r-tab-overview-help"
title={ __( 'Help Center', 'woocommerce-paypal-payments' ) }
description={ __(
'Access detailed guides and responsive support to streamline setup and enhance your experience.',
'woocommerce-paypal-payments'
) }
contentContainer={ false }
>
<ContentWrapper>
<Content>
<FeatureSettingsBlock
title={ __(
'Documentation',
'woocommerce-paypal-payments'
) }
description={ __(
'Find detailed guides and resources to help you set up, manage, and optimize your PayPal integration.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttons: [
{
type: 'tertiary',
text: __(
'View full documentation',
'woocommerce-paypal-payments'
),
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/',
},
],
} }
/>
</Content>
<Content>
<FeatureSettingsBlock
title={ __( 'Support', 'woocommerce-paypal-payments' ) }
description={ __(
'Need help? Access troubleshooting tips or contact our support team for personalized assistance.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttons: [
{
type: 'tertiary',
text: __(
'View support options',
'woocommerce-paypal-payments'
),
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/#get-help ',
},
],
} }
/>
</Content>
</ContentWrapper>
</SettingsCard>
);
};

View file

@ -8,8 +8,7 @@ export default {
// Transient data
SET_TRANSIENT: 'ppcp/features/SET_TRANSIENT',
// Persistent data
SET_PERSISTENT: 'ppcp/features/SET_PERSISTENT',
RESET: 'ppcp/features/RESET',
// Persistant data
SET_FEATURES: 'ppcp/features/SET_FEATURES',
HYDRATE: 'ppcp/features/HYDRATE',
};

View file

@ -2,16 +2,14 @@
* Action Creators: Define functions to create action objects.
*
* These functions update state or trigger side effects (e.g., async operations).
* Actions are categorized as Transient, Persistent, or Side effect.
* Actions are categorized as Transient or Side effect.
*
* @file
*/
import apiFetch from '@wordpress/api-fetch';
import ACTION_TYPES from './action-types';
import { REST_PERSIST_PATH } from './constants';
import { dispatch } from '@wordpress/data';
import { REST_PATH } from './constants';
/**
* @typedef {Object} Action An action object that is handled by a reducer or control.
@ -20,14 +18,7 @@ import { dispatch } from '@wordpress/data';
*/
/**
* Special. Resets all values in the store to initial defaults.
*
* @return {Action} The action.
*/
export const reset = () => ( { type: ACTION_TYPES.RESET } );
/**
* Persistent. Set the full store details during app initialization.
* Set the full store details during app initialization.
*
* @param {{data: {}, flags?: {}}} payload
* @return {Action} The action.
@ -49,18 +40,6 @@ export const setTransient = ( prop, value ) => ( {
payload: { [ prop ]: value },
} );
/**
* Generic persistent-data updater.
*
* @param {string} prop Name of the property to update.
* @param {any} value The new value of the property.
* @return {Action} The action.
*/
export const setPersistent = ( prop, value ) => ( {
type: ACTION_TYPES.SET_PERSISTENT,
payload: { [ prop ]: value },
} );
/**
* Transient. Marks the store as "ready", i.e., fully initialized.
*
@ -70,24 +49,39 @@ export const setPersistent = ( prop, value ) => ( {
export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady );
/**
* Thunk action creator. Triggers the persistence of store data to the server.
* Sets the features in the store.
*
* @return {Function} The thunk function.
* @param {Array} features The features to set.
* @return {Action} The action.
*/
export function persist() {
return async ( { select } ) => {
await apiFetch( {
path: REST_PERSIST_PATH,
method: 'POST',
data: select.persistentData(),
} );
};
}
export const setFeatures = ( features ) => ( {
type: ACTION_TYPES.SET_FEATURES,
payload: features,
} );
export function fetchFeatures() {
return async () => {
const response = await apiFetch( { path: REST_PERSIST_PATH } );
const features = response?.data || [];
dispatch( setFeatures( features ) );
};
}
/**
* Fetches features from the server.
*
* @return {Promise<Array>} The features data.
*/
export const fetchFeatures = async () => {
try {
const response = await apiFetch( { path: REST_PATH } );
if ( response?.data ) {
return {
success: true,
features: response.data.features,
};
}
return {
success: false,
features: [],
};
} catch ( e ) {
return {
success: false,
error: e,
message: e.message,
};
}
};

View file

@ -5,23 +5,4 @@
*/
export const STORE_NAME = 'wc/paypal/features';
/**
* REST path to hydrate data of this module by loading data from the WP DB.
*
* Used by: Resolvers
* See: <UNKNOWN>.php
*
* @type {string}
*/
export const REST_HYDRATE_PATH = '/wc/v3/wc_paypal/features';
/**
* REST path to persist data of this module to the WP DB.
*
* Used by: Controls
* See: <UNKNOWN>.php
*
* @type {string}
*/
export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/features';
export const REST_PATH = '/wc/v3/wc_paypal/features';

View file

@ -1,5 +1,5 @@
/**
* Hooks: Provide the main API for components to interact with the store.
* Hooks: Provide the main API for components to interact with the features store.
*
* These encapsulate store interactions, offering a consistent interface.
* Hooks simplify data access and manipulation for components.
@ -7,49 +7,61 @@
* @file
*/
import { useMemo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { createHooksForStore } from '../utils';
import { STORE_NAME } from './constants';
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
export const useStore = () => {
const { dispatch, useTransient } = useStoreData();
const [ isReady ] = useTransient( 'isReady' );
return { persist: dispatch.persist, isReady };
};
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { STORE_NAME, REST_PATH } from './constants';
export const useFeatures = () => {
const { usePersistent } = useStoreData();
const [ features, fetchFeatures ] = usePersistent( 'features' );
const { features, isReady } = useSelect( ( select ) => {
const store = select( STORE_NAME );
return {
features: store.getFeatures() || [],
isReady: select( STORE_NAME ).transientData()?.isReady || false,
};
}, [] );
const { setFeatures, setIsReady } = useDispatch( STORE_NAME );
useEffect( () => {
const loadInitialFeatures = async () => {
try {
const response = await apiFetch( { path: REST_PATH } );
if ( response?.data?.features ) {
const featuresData = response.data.features;
if ( featuresData.length > 0 ) {
await setFeatures( featuresData );
await setIsReady( true );
}
}
} catch ( error ) {}
};
if ( ! isReady ) {
loadInitialFeatures();
}
}, [ isReady, setFeatures, setIsReady ] );
return {
features,
fetchFeatures,
isReady,
fetchFeatures: async () => {
try {
const response = await apiFetch( { path: REST_PATH } );
const featuresData = response.data?.features || [];
if ( featuresData.length > 0 ) {
await setFeatures( featuresData );
await setIsReady( true );
return { success: true, features: featuresData };
}
return { success: false, features: [] };
} catch ( error ) {
return { success: false, error, message: error.message };
}
},
};
};

View file

@ -22,7 +22,7 @@ const defaultTransient = Object.freeze( {
/**
* Persistent: Values that are loaded from and saved to the DB.
* These represent the core todos configuration.
* These represent the core features configuration.
*/
const defaultPersistent = Object.freeze( {
features: [],
@ -50,10 +50,10 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
changeTransient( state, payload ),
/**
* Updates todos list
* Updates features list
*
* @param {Object} state Current state
* @param {Object} payload Update payload
* @param {Object} payload Update payload containing features array
* @return {Object} Updated state
*/
[ ACTION_TYPES.SET_FEATURES ]: ( state, payload ) => {
@ -65,7 +65,7 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
*
* @param {Object} state Current state
* @param {Object} payload Hydration payload containing server data
* @param {Object} payload.data The todos data to hydrate
* @param {Object} payload.data The features data to hydrate
* @return {Object} Hydrated state
*/
[ ACTION_TYPES.HYDRATE ]: ( state, payload ) =>

View file

@ -11,26 +11,24 @@
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { REST_HYDRATE_PATH } from './constants';
import { REST_PATH } from './constants';
/**
* Retrieve settings from the site's REST API.
* Hydrates the features data from the API.
*
* @return {Object} Action to dispatch.
*/
export function persistentData() {
return async ( { dispatch, registry } ) => {
export function getFeatures() {
return async ( { dispatch } ) => {
try {
const result = await apiFetch( { path: REST_HYDRATE_PATH } );
await dispatch.hydrate( result );
await dispatch.setIsReady( true );
} catch ( e ) {
await registry
.dispatch( 'core/notices' )
.createErrorNotice(
__(
'Error retrieving features details.',
'woocommerce-paypal-payments'
)
);
const response = await apiFetch( { path: REST_PATH } );
if ( response?.features ) {
dispatch.setFeatures( response.features );
dispatch.setIsReady( true );
}
} catch ( error ) {
console.error( 'Error fetching features:', error );
}
};
}

View file

@ -8,6 +8,7 @@
*/
const EMPTY_OBJ = Object.freeze( {} );
const EMPTY_ARR = Object.freeze( [] );
const getState = ( state ) => state || EMPTY_OBJ;
@ -19,3 +20,8 @@ export const transientData = ( state ) => {
const { data, ...transientState } = getState( state );
return transientState || EMPTY_OBJ;
};
export const getFeatures = ( state ) => {
const features = state?.features || persistentData( state ).features;
return features || EMPTY_ARR;
};

View file

@ -378,25 +378,25 @@ return array(
$capabilities['google_pay'] && ! $gateways['google_pay'], // Enable Google Pay.
);
},
'settings.rest.features' => static function ( ContainerInterface $container ) : FeaturesRestEndpoint {
'settings.rest.features' => static function ( ContainerInterface $container ) : FeaturesRestEndpoint {
return new FeaturesRestEndpoint(
$container->get( 'settings.data.definition.features' ),
$container->get( 'settings.rest.settings' )
);
},
'settings.data.definition.features' => static function ( ContainerInterface $container ) : FeaturesDefinition {
'settings.data.definition.features' => static function ( ContainerInterface $container ) : FeaturesDefinition {
return new FeaturesDefinition(
$container->get( 'settings.service.features_eligibilities' ),
$container->get( 'settings.data.general' )
);
},
'settings.service.features_eligibilities' => static function( ContainerInterface $container ): FeaturesEligibilityService {
'settings.service.features_eligibilities' => static function( ContainerInterface $container ): FeaturesEligibilityService {
$features = apply_filters(
'woocommerce_paypal_payments_rest_common_merchant_features',
array()
);
$payment_endpoint = $container->get('settings.rest.payment');
$payment_endpoint = $container->get( 'settings.rest.payment' );
$settings = $payment_endpoint->get_details()->get_data();
// Settings status.
@ -418,8 +418,8 @@ return array(
$capabilities['save_paypal'], // Save PayPal and Venmo eligibility.
$capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility.
$capabilities['apm'], // Alternative payment methods eligibility.
$capabilities['acdc'] && ! $capabilities['google_pay'], // Google Pay eligibility.
$capabilities['acdc'] && ! $capabilities['apple_pay'], // Apple Pay eligibility.
$capabilities['acdc'] && $capabilities['google_pay'], // Google Pay eligibility.
$capabilities['acdc'] && $capabilities['apple_pay'], // Apple Pay eligibility.
$capabilities['paylater'], // Pay Later eligibility.
);
},

View file

@ -18,8 +18,8 @@ use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;
* Provides the definitions for all available features in the system.
* Each feature has a title, description, eligibility condition, and associated action.
*/
class FeaturesDefinition
{
class FeaturesDefinition {
/**
* The features eligibility service.
@ -39,15 +39,14 @@ class FeaturesDefinition
* Constructor.
*
* @param FeaturesEligibilityService $eligibilities The features eligibility service.
* @param GeneralSettings $settings The general settings service.
* @param GeneralSettings $settings The general settings service.
*/
public function __construct(
FeaturesEligibilityService $eligibilities,
GeneralSettings $settings
)
{
GeneralSettings $settings
) {
$this->eligibilities = $eligibilities;
$this->settings = $settings;
$this->settings = $settings;
}
/**
@ -55,10 +54,9 @@ class FeaturesDefinition
*
* @return array The array of feature definitions.
*/
public function get(): array
{
public function get(): array {
$eligibility_checks = $this->eligibilities->get_eligibility_checks();
$paylaterCountries = [
$paylater_countries = array(
'UK',
'ES',
'IT',
@ -66,216 +64,216 @@ class FeaturesDefinition
'US',
'DE',
'AU',
];
$storeCountry = $this->settings->get_woo_settings()['country'];
$countryLocation = in_array($storeCountry, $paylaterCountries) ? strtolower($storeCountry) : 'us';
);
$store_country = $this->settings->get_woo_settings()['country'];
$country_location = in_array( $store_country, $paylater_countries, true ) ? strtolower( $store_country ) : 'us';
return array(
'save_paypal_and_venmo' => array(
'title' => __('Save PayPal and Venmo', 'woocommerce-paypal-payments'),
'description' => __('Securely save PayPal and Venmo payment methods for subscriptions or return buyers.', 'woocommerce-paypal-payments'),
'isEligible' => $eligibility_checks['save_paypal_and_venmo'],
'buttons' => array(
'save_paypal_and_venmo' => array(
'title' => __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ),
'description' => __( 'Securely save PayPal and Venmo payment methods for subscriptions or return buyers.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['save_paypal_and_venmo'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'tab',
'tab' => 'settings',
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'settings',
'section' => 'ppcp--save-payment-methods',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'),
'urls' => array(
'type' => 'secondary',
'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
'live' => 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
'live' => 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
),
'showWhen' => 'disabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => 'https://www.paypal.com/us/enterprise/payment-processing/accept-venmo',
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://www.paypal.com/us/enterprise/payment-processing/accept-venmo',
'class' => 'small-button',
),
),
),
'advanced_credit_and_debit_cards' => array(
'title' => __('Advanced Credit and Debit Cards', 'woocommerce-paypal-payments'),
'description' => __('Process major credit and debit cards including Visa, Mastercard, American Express and Discover.', 'woocommerce-paypal-payments'),
'isEligible' => $eligibility_checks['advanced_credit_and_debit_cards'],
'buttons' => array(
'title' => __( 'Advanced Credit and Debit Cards', 'woocommerce-paypal-payments' ),
'description' => __( 'Process major credit and debit cards including Visa, Mastercard, American Express and Discover.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['advanced_credit_and_debit_cards'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-credit-card-gateway',
'modal' => 'ppcp-credit-card-gateway',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'),
'urls' => array(
'type' => 'secondary',
'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/entry?product=ppcp',
'live' => 'https://www.paypal.com/bizsignup/entry?product=ppcp',
'live' => 'https://www.paypal.com/bizsignup/entry?product=ppcp',
),
'showWhen' => 'disabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => 'https://developer.paypal.com/studio/checkout/advanced',
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/studio/checkout/advanced',
'class' => 'small-button',
),
),
),
'alternative_payment_methods' => array(
'title' => __('Alternative Payment Methods', 'woocommerce-paypal-payments'),
'description' => __('Offer global, country-specific payment options for your customers.', 'woocommerce-paypal-payments'),
'isEligible' => $eligibility_checks['alternative_payment_methods'],
'buttons' => array(
'alternative_payment_methods' => array(
'title' => __( 'Alternative Payment Methods', 'woocommerce-paypal-payments' ),
'description' => __( 'Offer global, country-specific payment options for your customers.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['alternative_payment_methods'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'section' => 'ppcp-alternative-payments-card',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'),
'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'type' => 'secondary',
'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'showWhen' => 'disabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'class' => 'small-button',
),
),
),
'google_pay' => array(
'title' => __('Google Pay', 'woocommerce-paypal-payments'),
'description' => __('Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments'),
'isEligible' => $eligibility_checks['google_pay'],
'buttons' => array(
'google_pay' => array(
'title' => __( 'Google Pay', 'woocommerce-paypal-payments' ),
'description' => __( 'Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['google_pay'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-googlepay',
'modal' => 'ppcp-googlepay',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'),
'urls' => array(
'type' => 'secondary',
'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
'live' => 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
'live' => 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
),
'showWhen' => 'disabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => 'https://developer.paypal.com/docs/checkout/apm/google-pay/',
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/google-pay/',
'class' => 'small-button',
),
),
'notes' => array(
__('¹PayPal Q2 Earnings-2021.', 'woocommerce-paypal-payments'),
'notes' => array(
__( '¹PayPal Q2 Earnings-2021.', 'woocommerce-paypal-payments' ),
),
),
'apple_pay' => array(
'title' => __('Apple Pay', 'woocommerce-paypal-payments'),
'description' => __('Let customers pay using their Apple Pay wallet.', 'woocommerce-paypal-payments'),
'isEligible' => $eligibility_checks['apple_pay'],
'buttons' => array(
'apple_pay' => array(
'title' => __( 'Apple Pay', 'woocommerce-paypal-payments' ),
'description' => __( 'Let customers pay using their Apple Pay wallet.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['apple_pay'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-applepay',
'modal' => 'ppcp-applepay',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Domain registration', 'woocommerce-paypal-payments'),
'urls' => array(
'type' => 'secondary',
'text' => __( 'Domain registration', 'woocommerce-paypal-payments' ),
'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/uccservicing/apm/applepay',
'live' => 'https://www.paypal.com/uccservicing/apm/applepay',
'live' => 'https://www.paypal.com/uccservicing/apm/applepay',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'),
'urls' => array(
'type' => 'secondary',
'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
'live' => 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
'live' => 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
),
'showWhen' => 'disabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => 'https://developer.paypal.com/docs/checkout/apm/apple-pay/',
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/apple-pay/',
'class' => 'small-button',
),
),
),
'pay_later' => array(
'title' => __('Pay Later Messaging', 'woocommerce-paypal-payments'),
'pay_later' => array(
'title' => __( 'Pay Later Messaging', 'woocommerce-paypal-payments' ),
'description' => __(
'Let customers know they can buy now and pay later with PayPal. Adding this messaging can boost conversion rates and increase cart sizes by 39%¹, with no extra cost to you—plus, you get paid up front.',
'woocommerce-paypal-payments'
),
'isEligible' => $eligibility_checks['pay_later'],
'buttons' => array(
'isEligible' => $eligibility_checks['pay_later'],
'buttons' => array(
array(
'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'),
'action' => array(
'type' => 'secondary',
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array(
'type' => 'tab',
'tab' => 'pay_later_messaging',
),
'showWhen' => 'enabled',
'class' => 'small-button',
'class' => 'small-button',
),
array(
'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'),
'url' => "https://www.paypal.com/$countryLocation/business/accept-payments/checkout/installments",
'type' => 'tertiary',
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => "https://www.paypal.com/{$country_location}/business/accept-payments/checkout/installments",
'class' => 'small-button',
),
),

View file

@ -1,9 +1,8 @@
<?php
/**
* REST endpoint to manage the features items.
* REST endpoint to manage features.
*
* Provides endpoints for retrieving features
* via WP REST API routes.
* Provides endpoints for retrieving features via WP REST API routes.
*
* @package WooCommerce\PayPalCommerce\Settings\Endpoint
*/
@ -17,7 +16,7 @@ use WP_REST_Response;
use WooCommerce\PayPalCommerce\Settings\Data\Definition\FeaturesDefinition;
/**
* REST controller for the features items in the Overview tab.
* REST controller for the features in the Overview tab.
*
* This API acts as the intermediary between the "external world" and our
* internal data model. It's responsible for checking eligibility and
@ -48,8 +47,8 @@ class FeaturesRestEndpoint extends RestEndpoint {
/**
* FeaturesRestEndpoint constructor.
*
* @param FeaturesDefinition $features_definition The features definition instance.
* @param SettingsRestEndpoint $settings The settings endpoint instance.
* @param FeaturesDefinition $features_definition The features definition instance.
* @param SettingsRestEndpoint $settings The settings endpoint instance.
*/
public function __construct(
FeaturesDefinition $features_definition,
@ -83,16 +82,21 @@ class FeaturesRestEndpoint extends RestEndpoint {
* @return WP_REST_Response The response containing features data.
*/
public function get_features(): WP_REST_Response {
$features = array();
foreach ( $this->features_definition->get() as $id => $feature ) {
// Check eligibility and add to features if eligible.
if ( $feature['isEligible']() ) {
$features[] = array_merge(
array( 'id' => $id ),
array_diff_key( $feature, array( 'isEligible' => true ) )
);
// Evaluate eligibility check.
if ( is_callable( $feature['isEligible'] ) ) {
$is_eligible = $feature['isEligible']();
} else {
$is_eligible = (bool) $feature['isEligible'];
}
// Include all features with their eligibility state.
$features[] = array_merge(
array( 'id' => $id ),
array_diff_key( $feature, array( 'isEligible' => true ) ),
array( 'isEligible' => $is_eligible )
);
}
return $this->return_success(

View file

@ -77,12 +77,12 @@ class FeaturesEligibilityService {
bool $is_apple_pay_eligible,
bool $is_pay_later_eligible
) {
$this->is_save_paypal_and_venmo_eligible = $is_save_paypal_and_venmo_eligible;
$this->is_advanced_credit_and_debit_cards_eligible = $is_advanced_credit_and_debit_cards_eligible;
$this->is_alternative_payment_methods_eligible = $is_alternative_payment_methods_eligible;
$this->is_google_pay_eligible = $is_google_pay_eligible;
$this->is_apple_pay_eligible = $is_apple_pay_eligible;
$this->is_pay_later_eligible = $is_pay_later_eligible;
$this->is_save_paypal_and_venmo_eligible = $is_save_paypal_and_venmo_eligible;
$this->is_advanced_credit_and_debit_cards_eligible = $is_advanced_credit_and_debit_cards_eligible;
$this->is_alternative_payment_methods_eligible = $is_alternative_payment_methods_eligible;
$this->is_google_pay_eligible = $is_google_pay_eligible;
$this->is_apple_pay_eligible = $is_apple_pay_eligible;
$this->is_pay_later_eligible = $is_pay_later_eligible;
}
/**
@ -92,12 +92,12 @@ class FeaturesEligibilityService {
*/
public function get_eligibility_checks(): array {
return array(
'save_paypal_and_venmo' => fn() => $this->is_save_paypal_and_venmo_eligible,
'advanced_credit_and_debit_cards' => fn() => $this->is_advanced_credit_and_debit_cards_eligible,
'alternative_payment_methods' => fn() => $this->is_alternative_payment_methods_eligible,
'google_pay' => fn() => $this->is_google_pay_eligible,
'apple_pay' => fn() => $this->is_apple_pay_eligible,
'pay_later' => fn() => $this->is_pay_later_eligible,
'save_paypal_and_venmo' => fn() => $this->is_save_paypal_and_venmo_eligible,
'advanced_credit_and_debit_cards' => fn() => $this->is_advanced_credit_and_debit_cards_eligible,
'alternative_payment_methods' => fn() => $this->is_alternative_payment_methods_eligible,
'google_pay' => fn() => $this->is_google_pay_eligible,
'apple_pay' => fn() => $this->is_apple_pay_eligible,
'pay_later' => fn() => $this->is_pay_later_eligible,
);
}
}