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 Todos from '../Components/Overview/Todos/Todos';
import { useState, useEffect } from '@wordpress/element'; import Features from '../Components/Overview/Features/Features';
import { Button, Icon } from '@wordpress/components'; import Help from '../Components/Overview/Help/Help';
import { useDispatch } from '@wordpress/data'; import { TodosHooks, CommonHooks, FeaturesHooks } from '../../../../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 SpinnerOverlay from '../../../ReusableComponents/SpinnerOverlay'; 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 TabOverview = () => {
const { isReady: areTodosReady } = TodosHooks.useTodos(); const { isReady: areTodosReady } = TodosHooks.useTodos();
const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo(); const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo();
const { isReady: featuresIsReady } = FeaturesHooks.useFeatures();
if ( ! areTodosReady || ! merchantIsReady ) { if ( ! areTodosReady || ! merchantIsReady || ! featuresIsReady ) {
return <SpinnerOverlay asModal={ true } />; return <SpinnerOverlay asModal={ true } />;
} }
return ( return (
<div className="ppcp-r-tab-overview"> <div className="ppcp-r-tab-overview">
<OverviewTodos /> <Todos />
<OverviewFeatures /> <Features />
<OverviewHelp /> <Help />
</div> </div>
); );
}; };
export default TabOverview; 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 // Transient data
SET_TRANSIENT: 'ppcp/features/SET_TRANSIENT', SET_TRANSIENT: 'ppcp/features/SET_TRANSIENT',
// Persistent data // Persistant data
SET_PERSISTENT: 'ppcp/features/SET_PERSISTENT', SET_FEATURES: 'ppcp/features/SET_FEATURES',
RESET: 'ppcp/features/RESET',
HYDRATE: 'ppcp/features/HYDRATE', HYDRATE: 'ppcp/features/HYDRATE',
}; };

View file

@ -2,16 +2,14 @@
* Action Creators: Define functions to create action objects. * Action Creators: Define functions to create action objects.
* *
* These functions update state or trigger side effects (e.g., async operations). * 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 * @file
*/ */
import apiFetch from '@wordpress/api-fetch'; import apiFetch from '@wordpress/api-fetch';
import ACTION_TYPES from './action-types'; import ACTION_TYPES from './action-types';
import { REST_PERSIST_PATH } from './constants'; import { REST_PATH } from './constants';
import { dispatch } from '@wordpress/data';
/** /**
* @typedef {Object} Action An action object that is handled by a reducer or control. * @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. * Set the full store details during app initialization.
*
* @return {Action} The action.
*/
export const reset = () => ( { type: ACTION_TYPES.RESET } );
/**
* Persistent. Set the full store details during app initialization.
* *
* @param {{data: {}, flags?: {}}} payload * @param {{data: {}, flags?: {}}} payload
* @return {Action} The action. * @return {Action} The action.
@ -49,18 +40,6 @@ export const setTransient = ( prop, value ) => ( {
payload: { [ 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. * 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 ); 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() { export const setFeatures = ( features ) => ( {
return async ( { select } ) => { type: ACTION_TYPES.SET_FEATURES,
await apiFetch( { payload: features,
path: REST_PERSIST_PATH, } );
method: 'POST',
data: select.persistentData(),
} );
};
}
export function fetchFeatures() { /**
return async () => { * Fetches features from the server.
const response = await apiFetch( { path: REST_PERSIST_PATH } ); *
const features = response?.data || []; * @return {Promise<Array>} The features data.
dispatch( setFeatures( features ) ); */
}; 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'; export const STORE_NAME = 'wc/paypal/features';
export const REST_PATH = '/wc/v3/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';

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. * These encapsulate store interactions, offering a consistent interface.
* Hooks simplify data access and manipulation for components. * Hooks simplify data access and manipulation for components.
@ -7,49 +7,61 @@
* @file * @file
*/ */
import { useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data'; import { useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { createHooksForStore } from '../utils'; import { STORE_NAME, REST_PATH } from './constants';
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 };
};
export const useFeatures = () => { export const useFeatures = () => {
const { usePersistent } = useStoreData(); const { features, isReady } = useSelect( ( select ) => {
const [ features, fetchFeatures ] = usePersistent( 'features' ); 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 { return {
features, 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. * 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( { const defaultPersistent = Object.freeze( {
features: [], features: [],
@ -50,10 +50,10 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
changeTransient( state, payload ), changeTransient( state, payload ),
/** /**
* Updates todos list * Updates features list
* *
* @param {Object} state Current state * @param {Object} state Current state
* @param {Object} payload Update payload * @param {Object} payload Update payload containing features array
* @return {Object} Updated state * @return {Object} Updated state
*/ */
[ ACTION_TYPES.SET_FEATURES ]: ( state, payload ) => { [ ACTION_TYPES.SET_FEATURES ]: ( state, payload ) => {
@ -65,7 +65,7 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
* *
* @param {Object} state Current state * @param {Object} state Current state
* @param {Object} payload Hydration payload containing server data * @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 * @return {Object} Hydrated state
*/ */
[ ACTION_TYPES.HYDRATE ]: ( state, payload ) => [ ACTION_TYPES.HYDRATE ]: ( state, payload ) =>

View file

@ -11,26 +11,24 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch'; 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() { export function getFeatures() {
return async ( { dispatch, registry } ) => { return async ( { dispatch } ) => {
try { try {
const result = await apiFetch( { path: REST_HYDRATE_PATH } ); const response = await apiFetch( { path: REST_PATH } );
await dispatch.hydrate( result );
await dispatch.setIsReady( true ); if ( response?.features ) {
} catch ( e ) { dispatch.setFeatures( response.features );
await registry dispatch.setIsReady( true );
.dispatch( 'core/notices' ) }
.createErrorNotice( } catch ( error ) {
__( console.error( 'Error fetching features:', error );
'Error retrieving features details.',
'woocommerce-paypal-payments'
)
);
} }
}; };
} }

View file

@ -8,6 +8,7 @@
*/ */
const EMPTY_OBJ = Object.freeze( {} ); const EMPTY_OBJ = Object.freeze( {} );
const EMPTY_ARR = Object.freeze( [] );
const getState = ( state ) => state || EMPTY_OBJ; const getState = ( state ) => state || EMPTY_OBJ;
@ -19,3 +20,8 @@ export const transientData = ( state ) => {
const { data, ...transientState } = getState( state ); const { data, ...transientState } = getState( state );
return transientState || EMPTY_OBJ; 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. $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( return new FeaturesRestEndpoint(
$container->get( 'settings.data.definition.features' ), $container->get( 'settings.data.definition.features' ),
$container->get( 'settings.rest.settings' ) $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( return new FeaturesDefinition(
$container->get( 'settings.service.features_eligibilities' ), $container->get( 'settings.service.features_eligibilities' ),
$container->get( 'settings.data.general' ) $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( $features = apply_filters(
'woocommerce_paypal_payments_rest_common_merchant_features', 'woocommerce_paypal_payments_rest_common_merchant_features',
array() array()
); );
$payment_endpoint = $container->get('settings.rest.payment'); $payment_endpoint = $container->get( 'settings.rest.payment' );
$settings = $payment_endpoint->get_details()->get_data(); $settings = $payment_endpoint->get_details()->get_data();
// Settings status. // Settings status.
@ -418,8 +418,8 @@ return array(
$capabilities['save_paypal'], // Save PayPal and Venmo eligibility. $capabilities['save_paypal'], // Save PayPal and Venmo eligibility.
$capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility. $capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility.
$capabilities['apm'], // Alternative payment methods eligibility. $capabilities['apm'], // Alternative payment methods eligibility.
$capabilities['acdc'] && ! $capabilities['google_pay'], // Google Pay eligibility. $capabilities['acdc'] && $capabilities['google_pay'], // Google Pay eligibility.
$capabilities['acdc'] && ! $capabilities['apple_pay'], // Apple Pay eligibility. $capabilities['acdc'] && $capabilities['apple_pay'], // Apple Pay eligibility.
$capabilities['paylater'], // Pay Later 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. * Provides the definitions for all available features in the system.
* Each feature has a title, description, eligibility condition, and associated action. * Each feature has a title, description, eligibility condition, and associated action.
*/ */
class FeaturesDefinition class FeaturesDefinition {
{
/** /**
* The features eligibility service. * The features eligibility service.
@ -39,15 +39,14 @@ class FeaturesDefinition
* Constructor. * Constructor.
* *
* @param FeaturesEligibilityService $eligibilities The features eligibility service. * @param FeaturesEligibilityService $eligibilities The features eligibility service.
* @param GeneralSettings $settings The general settings service. * @param GeneralSettings $settings The general settings service.
*/ */
public function __construct( public function __construct(
FeaturesEligibilityService $eligibilities, FeaturesEligibilityService $eligibilities,
GeneralSettings $settings GeneralSettings $settings
) ) {
{
$this->eligibilities = $eligibilities; $this->eligibilities = $eligibilities;
$this->settings = $settings; $this->settings = $settings;
} }
/** /**
@ -55,10 +54,9 @@ class FeaturesDefinition
* *
* @return array The array of feature definitions. * @return array The array of feature definitions.
*/ */
public function get(): array public function get(): array {
{
$eligibility_checks = $this->eligibilities->get_eligibility_checks(); $eligibility_checks = $this->eligibilities->get_eligibility_checks();
$paylaterCountries = [ $paylater_countries = array(
'UK', 'UK',
'ES', 'ES',
'IT', 'IT',
@ -66,216 +64,216 @@ class FeaturesDefinition
'US', 'US',
'DE', 'DE',
'AU', 'AU',
]; );
$storeCountry = $this->settings->get_woo_settings()['country']; $store_country = $this->settings->get_woo_settings()['country'];
$countryLocation = in_array($storeCountry, $paylaterCountries) ? strtolower($storeCountry) : 'us'; $country_location = in_array( $store_country, $paylater_countries, true ) ? strtolower( $store_country ) : 'us';
return array( return array(
'save_paypal_and_venmo' => array( 'save_paypal_and_venmo' => array(
'title' => __('Save PayPal and Venmo', 'woocommerce-paypal-payments'), 'title' => __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ),
'description' => __('Securely save PayPal and Venmo payment methods for subscriptions or return buyers.', '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'], 'isEligible' => $eligibility_checks['save_paypal_and_venmo'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'settings', 'tab' => 'settings',
'section' => 'ppcp--save-payment-methods', 'section' => 'ppcp--save-payment-methods',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'), 'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array( 'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING', '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', 'showWhen' => 'disabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://www.paypal.com/us/enterprise/payment-processing/accept-venmo', 'url' => 'https://www.paypal.com/us/enterprise/payment-processing/accept-venmo',
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),
), ),
'advanced_credit_and_debit_cards' => array( 'advanced_credit_and_debit_cards' => array(
'title' => __('Advanced Credit and Debit Cards', 'woocommerce-paypal-payments'), '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'), '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'], 'isEligible' => $eligibility_checks['advanced_credit_and_debit_cards'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'payment_methods', 'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card', 'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-credit-card-gateway', 'modal' => 'ppcp-credit-card-gateway',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'), 'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array( 'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/entry?product=ppcp', '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', 'showWhen' => 'disabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/studio/checkout/advanced', 'url' => 'https://developer.paypal.com/studio/checkout/advanced',
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),
), ),
'alternative_payment_methods' => array( 'alternative_payment_methods' => array(
'title' => __('Alternative Payment Methods', 'woocommerce-paypal-payments'), 'title' => __( 'Alternative Payment Methods', 'woocommerce-paypal-payments' ),
'description' => __('Offer global, country-specific payment options for your customers.', 'woocommerce-paypal-payments'), 'description' => __( 'Offer global, country-specific payment options for your customers.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['alternative_payment_methods'], 'isEligible' => $eligibility_checks['alternative_payment_methods'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'payment_methods', 'tab' => 'payment_methods',
'section' => 'ppcp-alternative-payments-card', 'section' => 'ppcp-alternative-payments-card',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'), 'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/', 'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'showWhen' => 'disabled', 'showWhen' => 'disabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/', 'url' => 'https://developer.paypal.com/docs/checkout/apm/',
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),
), ),
'google_pay' => array( 'google_pay' => array(
'title' => __('Google Pay', 'woocommerce-paypal-payments'), 'title' => __( 'Google Pay', 'woocommerce-paypal-payments' ),
'description' => __('Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments'), 'description' => __( 'Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['google_pay'], 'isEligible' => $eligibility_checks['google_pay'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'payment_methods', 'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card', 'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-googlepay', 'modal' => 'ppcp-googlepay',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'), 'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array( 'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY', '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', 'showWhen' => 'disabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/google-pay/', 'url' => 'https://developer.paypal.com/docs/checkout/apm/google-pay/',
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),
'notes' => array( 'notes' => array(
__('¹PayPal Q2 Earnings-2021.', 'woocommerce-paypal-payments'), __( '¹PayPal Q2 Earnings-2021.', 'woocommerce-paypal-payments' ),
), ),
), ),
'apple_pay' => array( 'apple_pay' => array(
'title' => __('Apple Pay', 'woocommerce-paypal-payments'), 'title' => __( 'Apple Pay', 'woocommerce-paypal-payments' ),
'description' => __('Let customers pay using their Apple Pay wallet.', 'woocommerce-paypal-payments'), 'description' => __( 'Let customers pay using their Apple Pay wallet.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['apple_pay'], 'isEligible' => $eligibility_checks['apple_pay'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'payment_methods', 'tab' => 'payment_methods',
'section' => 'ppcp-card-payments-card', 'section' => 'ppcp-card-payments-card',
'modal' => 'ppcp-applepay', 'modal' => 'ppcp-applepay',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Domain registration', 'woocommerce-paypal-payments'), 'text' => __( 'Domain registration', 'woocommerce-paypal-payments' ),
'urls' => array( 'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/uccservicing/apm/applepay', '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', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Sign up', 'woocommerce-paypal-payments'), 'text' => __( 'Sign up', 'woocommerce-paypal-payments' ),
'urls' => array( 'urls' => array(
'sandbox' => 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY', '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', 'showWhen' => 'disabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => 'https://developer.paypal.com/docs/checkout/apm/apple-pay/', 'url' => 'https://developer.paypal.com/docs/checkout/apm/apple-pay/',
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),
), ),
'pay_later' => array( 'pay_later' => array(
'title' => __('Pay Later Messaging', 'woocommerce-paypal-payments'), 'title' => __( 'Pay Later Messaging', 'woocommerce-paypal-payments' ),
'description' => __( '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.', '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' 'woocommerce-paypal-payments'
), ),
'isEligible' => $eligibility_checks['pay_later'], 'isEligible' => $eligibility_checks['pay_later'],
'buttons' => array( 'buttons' => array(
array( array(
'type' => 'secondary', 'type' => 'secondary',
'text' => __('Configure', 'woocommerce-paypal-payments'), 'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
'action' => array( 'action' => array(
'type' => 'tab', 'type' => 'tab',
'tab' => 'pay_later_messaging', 'tab' => 'pay_later_messaging',
), ),
'showWhen' => 'enabled', 'showWhen' => 'enabled',
'class' => 'small-button', 'class' => 'small-button',
), ),
array( array(
'type' => 'tertiary', 'type' => 'tertiary',
'text' => __('Learn more', 'woocommerce-paypal-payments'), 'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
'url' => "https://www.paypal.com/$countryLocation/business/accept-payments/checkout/installments", 'url' => "https://www.paypal.com/{$country_location}/business/accept-payments/checkout/installments",
'class' => 'small-button', 'class' => 'small-button',
), ),
), ),

View file

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

View file

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