Merge branch 'trunk' of github.com:woocommerce/woocommerce-paypal-payments into PCP-3942-add-help-center-components-ver2

This commit is contained in:
Daniel Dudzic 2025-01-02 14:42:41 +01:00
commit b7ec9747a5
No known key found for this signature in database
GPG key ID: 31B40D33E3465483
52 changed files with 2269 additions and 1453 deletions

View file

@ -1 +1,3 @@
export { default as openSignup } from './Icons/open-signup';
export const NOTIFICATION_SUCCESS = '✔️';
export const NOTIFICATION_ERROR = '❌';

View file

@ -5,26 +5,29 @@ import { countryPriceInfo } from '../../utils/countryPriceInfo';
import { formatPrice } from '../../utils/formatPrice';
import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge';
const getFixedAmount = ( currency, priceList ) => {
if ( priceList[ currency ] ) {
return formatPrice( priceList[ currency ], currency );
const getFixedAmount = ( currency, priceList, itemFixedAmount ) => {
if ( priceList[ currency ] ) {
const sum = priceList[ currency ] + itemFixedAmount;
return formatPrice( sum, currency );
}
const [ defaultCurrency, defaultPrice ] = Object.entries( priceList )[ 0 ];
return formatPrice( defaultPrice, defaultCurrency );
const sum = defaultPrice + itemFixedAmount;
return formatPrice( sum, defaultCurrency );
};
const PricingTitleBadge = ( { item } ) => {
const { storeCountry } = CommonHooks.useWooSettings();
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
const infos = countryPriceInfo[ storeCountry ];
const itemKey = item.split(' ')[0]; // Extract the first word, fastlane has more than one
if ( ! infos || ! infos[ item ] ) {
if ( ! infos || ! infos[ itemKey ] ) {
return null;
}
const percentage = infos[ item ].toFixed( 2 );
const fixedAmount = getFixedAmount( storeCountry, infos.fixedFee );
const percentage = typeof infos[itemKey] === 'number' ? infos[itemKey].toFixed(2) : infos[itemKey]['percentage'].toFixed(2);
const itemFixedAmount = infos[itemKey]['fixedFee'] ? infos[itemKey]['fixedFee'] : 0;
const fixedAmount = getFixedAmount( storeCurrency, infos.fixedFee, itemFixedAmount );
const label = sprintf(
__( 'from %1$s%% + %2$s', 'woocommerce-paypal-payments' ),

View file

@ -1,6 +1,6 @@
import { Button } from '@wordpress/components';
import SettingsBlock from './SettingsBlock';
import { Header, Title, Action, Description } from './SettingsBlockElements';
import { Action, Description, Header, Title } from './SettingsBlockElements';
const ButtonSettingsBlock = ( { title, description, ...props } ) => (
<SettingsBlock { ...props } className="ppcp-r-settings-block__button">
@ -10,6 +10,7 @@ const ButtonSettingsBlock = ( { title, description, ...props } ) => (
</Header>
<Action>
<Button
isBusy={ props.actionProps?.isBusy }
variant={ props.actionProps?.buttonType }
onClick={
props.actionProps?.callback

View file

@ -22,6 +22,7 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
const renderButton = ( button ) => {
const buttonElement = (
<Button
className={ button.class ? button.class : '' }
key={ button.text }
variant={ button.type }
onClick={ button.onClick }

View file

@ -53,6 +53,9 @@ const AcdcFlow = ( { isFastlane, isPayLater, storeCountry } ) => {
imageBadge={ [
'icon-payment-method-paypal-small.svg',
] }
textBadge={
<PricingTitleBadge item="plater" />
}
description={ sprintf(
// translators: %s: Link to PayPal business fees guide
__(

View file

@ -23,6 +23,7 @@ const Features = {
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -33,6 +34,7 @@ const Features = {
live: 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -41,6 +43,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
},
@ -69,6 +72,7 @@ const Features = {
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -79,6 +83,7 @@ const Features = {
live: 'https://www.paypal.com/bizsignup/entry?product=ppcp',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -87,6 +92,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
},
@ -111,6 +117,7 @@ const Features = {
);
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -120,6 +127,7 @@ const Features = {
live: '#',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -128,6 +136,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
},
@ -151,6 +160,7 @@ const Features = {
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -161,6 +171,7 @@ const Features = {
live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -169,6 +180,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
notes: [
@ -198,6 +210,7 @@ const Features = {
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -211,6 +224,7 @@ const Features = {
live: 'https://www.paypal.com/uccservicing/apm/applepay',
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -221,6 +235,7 @@ const Features = {
live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -229,6 +244,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
},
@ -252,6 +268,7 @@ const Features = {
} );
},
showWhen: 'enabled',
class: 'small-button',
},
{
type: 'secondary',
@ -261,6 +278,7 @@ const Features = {
live: '#',
},
showWhen: 'disabled',
class: 'small-button',
},
{
type: 'tertiary',
@ -269,6 +287,7 @@ const Features = {
sandbox: '#',
live: '#',
},
class: 'small-button',
},
],
},

View file

@ -1,168 +0,0 @@
import { __ } from '@wordpress/i18n';
import {
Header,
Title,
Description,
AccordionSettingsBlock,
ToggleSettingsBlock,
ButtonSettingsBlock,
} from '../../../../ReusableComponents/SettingsBlocks';
import SettingsBlock from '../../../../ReusableComponents/SettingsBlocks/SettingsBlock';
const Troubleshooting = ( { updateFormValue, settings } ) => {
return (
<AccordionSettingsBlock
className="ppcp-r-settings-block--troubleshooting"
title={ __( 'Troubleshooting', 'woocommerce-paypal-payments' ) }
description={ __(
'Access tools to help debug and resolve issues.',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'payNowExperience',
value: settings.payNowExperience,
} }
>
<ToggleSettingsBlock
title={ __( 'Logging', 'woocommerce-paypal-payments' ) }
description={ __(
'Log additional debugging information in the WooCommerce logs that can assist technical staff to determine issues.',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'logging',
value: settings.logging,
} }
/>
<SettingsBlock>
<Header>
<Title>
{ __(
'Subscribed PayPal webhooks',
'woocommerce-paypal-payments'
) }
</Title>
<Description>
{ __(
'The following PayPal webhooks are subscribed. More information about the webhooks is available in the',
'woocommerce-paypal-payments'
) }{ ' ' }
<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status">
{ __(
'Webhook Status documentation',
'woocommerce-paypal-payments'
) }
</a>
.
</Description>
</Header>
<HooksTable data={ hooksExampleData() } />
</SettingsBlock>
<ButtonSettingsBlock
title={ __(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
) }
description={ __(
'Click to remove the current webhook subscription and subscribe again, for example, if the website domain or URL structure changed.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttonType: 'secondary',
callback: () =>
console.log(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
),
value: __(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
),
} }
/>
<ButtonSettingsBlock
title={ __(
'Simulate webhooks',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttonType: 'secondary',
callback: () =>
console.log(
'Simulate webhooks',
'woocommerce-paypal-payments'
),
value: __(
'Simulate webhooks',
'woocommerce-paypal-payments'
),
} }
/>
</AccordionSettingsBlock>
);
};
const hooksExampleData = () => {
return {
url: 'https://www.rt3.tech/wordpress/paypal-ux-testin/index.php?rest_route=/paypal/v1/incoming',
hooks: [
'billing plan pricing-change activated',
'billing plan updated',
'billing subscription cancelled',
'catalog product updated',
'checkout order approved',
'checkout order completed',
'checkout payment-approval reversed',
'payment authorization voided',
'payment capture completed',
'payment capture denied',
'payment capture pending',
'payment capture refunded',
'payment capture reversed',
'payment order cancelled',
'payment sale completed',
'payment sale refunded',
'vault payment-token created',
'vault payment-token deleted',
],
};
};
const HooksTable = ( { data } ) => {
return (
<table className="ppcp-r-table">
<thead>
<tr>
<th className="ppcp-r-table__hooks-url">
{ __( 'URL', 'woocommerce-paypal-payments' ) }
</th>
<th className="ppcp-r-table__hooks-events">
{ __(
'Tracked events',
'woocommerce-paypal-payments'
) }
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="ppcp-r-table__hooks-url">{ data?.url }</td>
<td className="ppcp-r-table__hooks-events">
{ data.hooks.map( ( hook, index ) => (
<span key={ hook }>
{ hook }{ ' ' }
{ index !== data.hooks.length - 1 && ',' }
</span>
) ) }
</td>
</tr>
</tbody>
</table>
);
};
export default Troubleshooting;

View file

@ -0,0 +1,37 @@
import { __ } from '@wordpress/i18n';
import { CommonHooks } from '../../../../../../data';
const HooksTableBlock = () => {
const { webhooks } = CommonHooks.useWebhooks();
return (
<table className="ppcp-r-table">
<thead>
<tr>
<th className="ppcp-r-table__hooks-url">
{ __( 'URL', 'woocommerce-paypal-payments' ) }
</th>
<th className="ppcp-r-table__hooks-events">
{ __(
'Tracked events',
'woocommerce-paypal-payments'
) }
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="ppcp-r-table__hooks-url">
{ webhooks?.url }
</td>
<td
className="ppcp-r-table__hooks-events"
dangerouslySetInnerHTML={ { __html: webhooks?.events } }
></td>
</tr>
</tbody>
</table>
);
};
export default HooksTableBlock;

View file

@ -0,0 +1,72 @@
import { useState } from '@wordpress/element';
import { STORE_NAME } from '../../../../../../data/common';
import { ButtonSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
import { __ } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import {
NOTIFICATION_ERROR,
NOTIFICATION_SUCCESS,
} from '../../../../../ReusableComponents/Icons';
const ResubscribeBlock = () => {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const [ resubscribing, setResubscribing ] = useState( false );
const { resubscribeWebhooks } = useDispatch( STORE_NAME );
const startResubscribingWebhooks = async () => {
setResubscribing( true );
try {
await resubscribeWebhooks();
} catch ( error ) {
setResubscribing( false );
createErrorNotice(
__(
'Operation failed. Check WooCommerce logs for more details.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_ERROR,
}
);
return;
}
setResubscribing( false );
createSuccessNotice(
__(
'Webhooks were successfully re-subscribed.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_SUCCESS,
}
);
};
return (
<ButtonSettingsBlock
title={ __(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
) }
description={ __(
'Click to remove the current webhook subscription and subscribe again, for example, if the website domain or URL structure changed.',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttonType: 'secondary',
isBusy: resubscribing,
callback: () => startResubscribingWebhooks(),
value: __(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
),
} }
/>
);
};
export default ResubscribeBlock;

View file

@ -0,0 +1,129 @@
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { ButtonSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import { CommonHooks } from '../../../../../../data';
import {
NOTIFICATION_ERROR,
NOTIFICATION_SUCCESS,
} from '../../../../../ReusableComponents/Icons';
const SimulationBlock = () => {
const {
createSuccessNotice,
createInfoNotice,
createErrorNotice,
removeNotice,
} = useDispatch( noticesStore );
const { startWebhookSimulation, checkWebhookSimulationState } =
CommonHooks.useWebhooks();
const [ simulating, setSimulating ] = useState( false );
const sleep = ( ms ) => {
return new Promise( ( resolve ) => setTimeout( resolve, ms ) );
};
const startSimulation = async ( maxRetries ) => {
const webhookInfoNoticeId = 'paypal-webhook-simulation-info-notice';
const triggerWebhookInfoNotice = () => {
createInfoNotice(
__(
'Waiting for the webhook to arrive…',
'woocommerce-paypal-payments'
),
{
id: webhookInfoNoticeId,
}
);
};
const stopSimulation = () => {
removeNotice( webhookInfoNoticeId );
setSimulating( false );
};
setSimulating( true );
triggerWebhookInfoNotice();
try {
await startWebhookSimulation();
} catch ( error ) {
console.error( error );
setSimulating( false );
createErrorNotice(
__(
'Operation failed. Check WooCommerce logs for more details.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_ERROR,
}
);
return;
}
for ( let i = 0; i < maxRetries; i++ ) {
await sleep( 2000 );
const simulationStateResponse = await checkWebhookSimulationState();
try {
if ( ! simulationStateResponse.success ) {
console.error(
'Simulation state query failed: ' +
simulationStateResponse?.data
);
continue;
}
if ( simulationStateResponse?.data?.state === 'received' ) {
createSuccessNotice(
__(
'The webhook was received successfully.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_SUCCESS,
}
);
stopSimulation();
return;
}
removeNotice( webhookInfoNoticeId );
triggerWebhookInfoNotice();
} catch ( error ) {
console.error( error );
}
}
stopSimulation();
createErrorNotice(
__(
'Looks like the webhook cannot be received. Check that your website is accessible from the internet.',
'woocommerce-paypal-payments'
),
{
icon: NOTIFICATION_ERROR,
}
);
};
return (
<>
<ButtonSettingsBlock
title={ __(
'Simulate webhooks',
'woocommerce-paypal-payments'
) }
actionProps={ {
buttonType: 'secondary',
isBusy: simulating,
callback: () => startSimulation( 30 ),
value: __(
'Simulate webhooks',
'woocommerce-paypal-payments'
),
} }
/>
</>
);
};
export default SimulationBlock;

View file

@ -0,0 +1,72 @@
import { __ } from '@wordpress/i18n';
import {
AccordionSettingsBlock,
Description,
Header,
Title,
ToggleSettingsBlock,
} from '../../../../../ReusableComponents/SettingsBlocks';
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlocks/SettingsBlock';
import SimulationBlock from './SimulationBlock';
import ResubscribeBlock from './ResubscribeBlock';
import HooksTableBlock from './HooksTableBlock';
const Troubleshooting = ( { updateFormValue, settings } ) => {
return (
<AccordionSettingsBlock
className="ppcp-r-settings-block--troubleshooting"
title={ __( 'Troubleshooting', 'woocommerce-paypal-payments' ) }
description={ __(
'Access tools to help debug and resolve issues.',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'payNowExperience',
value: settings.payNowExperience,
} }
>
<ToggleSettingsBlock
title={ __( 'Logging', 'woocommerce-paypal-payments' ) }
description={ __(
'Log additional debugging information in the WooCommerce logs that can assist technical staff to determine issues.',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'logging',
value: settings.logging,
} }
/>
<SettingsBlock>
<Header>
<Title>
{ __(
'Subscribed PayPal webhooks',
'woocommerce-paypal-payments'
) }
</Title>
<Description>
{ __(
'The following PayPal webhooks are subscribed. More information about the webhooks is available in the',
'woocommerce-paypal-payments'
) }{ ' ' }
<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status">
{ __(
'Webhook Status documentation',
'woocommerce-paypal-payments'
) }
</a>
.
</Description>
</Header>
<HooksTableBlock />
<ResubscribeBlock />
<SimulationBlock />
</SettingsBlock>
</AccordionSettingsBlock>
);
};
export default Troubleshooting;

View file

@ -5,7 +5,7 @@ import {
ContentWrapper,
} from '../../../ReusableComponents/SettingsBlocks';
import Sandbox from './Blocks/Sandbox';
import Troubleshooting from './Blocks/Troubleshooting';
import Troubleshooting from './Blocks/Troubleshooting/Troubleshooting';
import PaypalSettings from './Blocks/PaypalSettings';
import OtherSettings from './Blocks/OtherSettings';