Merge branch 'trunk'

This commit is contained in:
Philipp Stracker 2025-01-09 17:40:58 +01:00
commit 3e82b17cac
No known key found for this signature in database
20 changed files with 788 additions and 562 deletions

View file

@ -18,6 +18,15 @@
gap: 18px;
width: 100%;
&:hover {
cursor: pointer;
.ppcp-r-todo-item__inner {
.ppcp-r-todo-item__description {
color: $color-text-text;
}
}
}
&:not(:last-child) {
border-bottom: 1px solid $color-gray-400;
padding-bottom: 16px;
@ -62,6 +71,14 @@
@include font(13, 20, 400);
color: $color-blueberry;
}
&__icon {
border: 1px dashed #949494;
background: #fff;
border-radius: 50%;
width: 24px;
height: 24px;
}
}
.ppcp-r-feature-item {

View file

@ -18,7 +18,7 @@ const ButtonSettingsBlock = ( { title, description, ...props } ) => (
: undefined
}
>
{ props.actionProps.value }
{ props?.actionProps?.value }
</Button>
</Action>
</SettingsBlock>

View file

@ -2,7 +2,7 @@ import { ToggleControl } from '@wordpress/components';
import SettingsBlock from './SettingsBlock';
import PaymentMethodIcon from '../PaymentMethodIcon';
import data from '../../../utils/data';
import { MODAL_CONFIG } from '../../Screens/Overview/Modals/Modal';
import { hasSettings } from '../../Screens/Overview/TabSettingsElements/Blocks/PaymentMethods';
const PaymentMethodItemBlock = ( {
id,
@ -13,8 +13,8 @@ const PaymentMethodItemBlock = ( {
onSelect,
isSelected,
} ) => {
// Only show settings icon if this method has a modal configured
const hasModal = Boolean( MODAL_CONFIG[ id ] );
// Only show settings icon if this method has fields configured
const hasModal = hasSettings( id );
return (
<SettingsBlock className="ppcp-r-settings-block__payment-methods__item">

View file

@ -7,13 +7,16 @@ const PaymentMethodsBlock = ( {
className = '',
onTriggerModal,
} ) => {
const [ selectedMethod, setSelectedMethod ] = useState( null );
const [ selectedMethods, setSelectedMethods ] = useState( {} );
const handleSelect = useCallback( ( methodId, isSelected ) => {
setSelectedMethod( isSelected ? methodId : null );
setSelectedMethods( ( prev ) => ( {
...prev,
[ methodId ]: isSelected,
} ) );
}, [] );
if ( paymentMethods.length === 0 ) {
if ( ! paymentMethods?.length ) {
return null;
}
@ -25,7 +28,9 @@ const PaymentMethodsBlock = ( {
<PaymentMethodItemBlock
key={ paymentMethod.id }
{ ...paymentMethod }
isSelected={ selectedMethod === paymentMethod.id }
isSelected={ Boolean(
selectedMethods[ paymentMethod.id ]
) }
onSelect={ ( checked ) =>
handleSelect( paymentMethod.id, checked )
}

View file

@ -33,8 +33,10 @@ export const Header = ( { children, className = '' } ) => (
);
// Card Elements
export const Content = ( { children } ) => (
<div className="ppcp-r-settings-card__content">{ children }</div>
export const Content = ( { children, id = '' } ) => (
<div id={ id } className="ppcp-r-settings-card__content">
{ children }
</div>
);
export const ContentWrapper = ( { children } ) => (

View file

@ -1,13 +1,4 @@
import { PayPalCheckbox, handleCheckboxState } from '../Fields';
import data from '../../../utils/data';
const TodoSettingsBlock = ( {
todos,
setTodos,
todosData,
setTodosData,
className = '',
} ) => {
const TodoSettingsBlock = ( { todosData, className = '' } ) => {
if ( todosData.length === 0 ) {
return null;
}
@ -16,54 +7,33 @@ const TodoSettingsBlock = ( {
<div
className={ `ppcp-r-settings-block__todo ppcp-r-todo-items ${ className }` }
>
{ todosData.map( ( todo ) => (
<TodoItem
name="todo_items"
key={ todo.value }
value={ todo.value }
currentValue={ todos }
changeCallback={ setTodos }
description={ todo.description }
changeTodos={ setTodosData }
todosData={ todosData }
/>
) ) }
{ todosData
.slice( 0, 5 )
.filter( ( todo ) => {
return ! todo.isCompleted();
} )
.map( ( todo ) => (
<TodoItem
key={ todo.id }
title={ todo.title }
onClick={ todo.onClick }
/>
) ) }
</div>
);
};
const TodoItem = ( props ) => {
return (
<div className="ppcp-r-todo-item">
<div className="ppcp-r-todo-item" onClick={ props.onClick }>
<div className="ppcp-r-todo-item__inner">
<PayPalCheckbox
{ ...{
...props,
handleCheckboxState,
} }
/>
<div className="ppcp-r-todo-item__icon"></div>
<div className="ppcp-r-todo-item__description">
{ props.description }
{ props.title }
</div>
</div>
<div
className="ppcp-r-todo-item__close"
onClick={ () =>
removeTodo(
props.value,
props.todosData,
props.changeTodos
)
}
>
{ data().getImage( 'icon-close.svg' ) }
</div>
</div>
);
};
const removeTodo = ( todoValue, todosData, changeTodos ) => {
changeTodos( todosData.filter( ( todo ) => todo.value !== todoValue ) );
};
export default TodoSettingsBlock;

View file

@ -18,8 +18,10 @@ const SettingsCard = ( {
if ( contentItems ) {
return (
<ContentWrapper>
{ contentItems.map( ( item, index ) => (
<Content key={ index }>{ item }</Content>
{ contentItems.map( ( item ) => (
<Content key={ item.key } id={ item.key }>
{ item }
</Content>
) ) }
</ContentWrapper>
);

View file

@ -1,51 +0,0 @@
import { __ } from '@wordpress/i18n';
import ModalPayPal from './ModalPayPal';
import ModalFastlane from './ModalFastlane';
import ModalAcdc from './ModalAcdc';
import { useActiveModal } from '../../../../data/common/hooks';
export const MODAL_CONFIG = {
paypal: {
component: ModalPayPal,
icon: 'payment-method-paypal-big',
title: __( 'PayPal', 'woocommerce-paypal-payments' ),
},
fastlane: {
component: ModalFastlane,
icon: 'payment-method-fastlane-big',
title: __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ),
size: 'small',
},
advanced_credit_and_debit_card_payments: {
component: ModalAcdc,
icon: 'payment-method-cards-big',
title: __(
'Advanced Credit and Debit Card Payments',
'woocommerce-paypal-payments'
),
},
};
const Modal = () => {
const { activeModal, setActiveModal } = useActiveModal();
const handleCloseModal = () => {
setActiveModal( '' );
};
if ( ! activeModal || ! MODAL_CONFIG[ activeModal ] ) {
return null;
}
const { component: ModalComponent, ...modalProps } =
MODAL_CONFIG[ activeModal ];
return (
<ModalComponent
setModalIsVisible={ handleCloseModal }
{ ...modalProps }
/>
);
};
export default Modal;

View file

@ -1,62 +0,0 @@
import PaymentMethodModal from '../../../ReusableComponents/PaymentMethodModal';
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { RadioControl } from '@wordpress/components';
const ModalAcdc = ( { setModalIsVisible } ) => {
const [ threeDSecure, setThreeDSecure ] = useState( 'no-3d-secure' );
const acdcOptions = [
{
label: __( 'No 3D Secure', 'woocommerce-paypal-payments' ),
value: 'no-3d-secure',
},
{
label: __( 'Only when required', 'woocommerce-paypal-payments' ),
value: 'only-required-3d-secure',
},
{
label: __(
'Always require 3D Secure',
'woocommerce-paypal-payments'
),
value: 'always-3d-secure',
},
];
return (
<PaymentMethodModal
setModalIsVisible={ setModalIsVisible }
icon="payment-method-cards-big"
title={ __(
'Advanced Credit and Debit Card Payments',
'woocommerce-paypal-payments'
) }
>
<strong className="ppcp-r-modal__content-title">
{ __( '3D Secure', 'woocommerce-paypal-payments' ) }
</strong>
<p className="ppcp-r-modal__description">
{ __(
'Authenticate cardholders through their card issuers to reduce fraud and improve transaction security. Successful 3D Secure authentication can shift liability for fraudulent chargebacks to the card issuer.',
'woocommerce-paypal-payments'
) }
</p>
<div className="ppcp-r-modal__field-rows ppcp-r-modal__field-rows--acdc">
<RadioControl
onChange={ setThreeDSecure }
selected={ threeDSecure }
options={ acdcOptions }
/>
<div className="ppcp-r-modal__field-row ppcp-r-modal__field-row--save">
<Button variant="primary">
{ __( 'Save changes', 'woocommerce-paypal-payments' ) }
</Button>
</div>
</div>
</PaymentMethodModal>
);
};
export default ModalAcdc;

View file

@ -1,63 +0,0 @@
import PaymentMethodModal from '../../../ReusableComponents/PaymentMethodModal';
import { __ } from '@wordpress/i18n';
import { Button, ToggleControl } from '@wordpress/components';
import { PayPalRdb } from '../../../ReusableComponents/Fields';
import { useState } from '@wordpress/element';
const ModalFastlane = ( { setModalIsVisible } ) => {
const [ fastlaneSettings, setFastlaneSettings ] = useState( {
cardholderName: false,
displayWatermark: false,
} );
const updateFormValue = ( key, value ) => {
setFastlaneSettings( { ...fastlaneSettings, [ key ]: value } );
};
return (
<PaymentMethodModal
setModalIsVisible={ setModalIsVisible }
icon="payment-method-fastlane-big"
title={ __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ) }
size="small"
>
<div className="ppcp-r-modal__field-rows ppcp-r-modal__field-rows--fastlane">
<div className="ppcp-r-modal__field-row">
<ToggleControl
className="ppcp-r-modal__inverted-toggle-control"
checked={ fastlaneSettings.cardholderName }
onChange={ ( newValue ) =>
updateFormValue( 'cardholderName', newValue )
}
label={ __(
'Display cardholder name',
'woocommerce-paypal-payments'
) }
id="ppcp-r-fastlane-settings-cardholder"
/>
</div>
<div className="ppcp-r-modal__field-row">
<ToggleControl
className="ppcp-r-modal__inverted-toggle-control"
checked={ fastlaneSettings.displayWatermark }
onChange={ ( newValue ) =>
updateFormValue( 'displayWatermark', newValue )
}
label={ __(
'Display Fastlane Watermark',
'woocommerce-paypal-payments'
) }
id="ppcp-r-fastlane-settings-watermark"
/>
</div>
<div className="ppcp-r-modal__field-row ppcp-r-modal__field-row--save">
<Button variant="primary">
{ __( 'Save changes', 'woocommerce-paypal-payments' ) }
</Button>
</div>
</div>
</PaymentMethodModal>
);
};
export default ModalFastlane;

View file

@ -1,76 +0,0 @@
import PaymentMethodModal from '../../../ReusableComponents/PaymentMethodModal';
import { __ } from '@wordpress/i18n';
import { ToggleControl, Button, TextControl } from '@wordpress/components';
import { useState } from '@wordpress/element';
const ModalPayPal = ( { setModalIsVisible } ) => {
const [ paypalSettings, setPaypalSettings ] = useState( {
checkoutPageTitle: 'PayPal',
checkoutPageDescription: 'Pay via PayPal',
showLogo: false,
} );
const updateFormValue = ( key, value ) => {
setPaypalSettings( { ...paypalSettings, [ key ]: value } );
};
return (
<PaymentMethodModal
setModalIsVisible={ setModalIsVisible }
icon="payment-method-paypal-big"
title={ __( 'PayPal', 'woocommerce-paypal-payments' ) }
>
<div className="ppcp-r-modal__field-rows">
<div className="ppcp-r-modal__field-row">
<TextControl
className="ppcp-r-vertical-text-control"
label={ __(
'Checkout page title',
'woocommerce-paypal-payments'
) }
value={ paypalSettings.checkoutPageTitle }
onChange={ ( newValue ) =>
updateFormValue( 'checkoutPageTitle', newValue )
}
/>
</div>
<div className="ppcp-r-modal__field-row">
<TextControl
className="ppcp-r-vertical-text-control"
label={ __(
'Checkout page description',
'woocommerce-paypal-payments'
) }
value={ paypalSettings.checkoutPageDescription }
onChange={ ( newValue ) =>
updateFormValue(
'checkoutPageDescription',
newValue
)
}
/>
</div>
<div className="ppcp-r-modal__field-row">
<ToggleControl
label={ __(
'Show logo',
'woocommerce-paypal-payments'
) }
id="ppcp-r-paypal-settings-show-logo"
checked={ paypalSettings.showLogo }
onChange={ ( newValue ) => {
updateFormValue( 'showLogo', newValue );
} }
/>
</div>
<div className="ppcp-r-modal__field-row ppcp-r-modal__field-row--save">
<Button variant="primary">
{ __( 'Save changes', 'woocommerce-paypal-payments' ) }
</Button>
</div>
</div>
</PaymentMethodModal>
);
};
export default ModalPayPal;

View file

@ -11,10 +11,9 @@ import { TITLE_BADGE_POSITIVE } from '../../ReusableComponents/TitleBadge';
import { useMerchantInfo } from '../../../data/common/hooks';
import { STORE_NAME } from '../../../data/common';
import Features from './TabSettingsElements/Blocks/Features';
import { todosData } from '../../../data/settings/tab-overview-todos-data';
const TabOverview = () => {
const [ todos, setTodos ] = useState( [] );
const [ todosData, setTodosData ] = useState( todosDataDefault );
const [ isRefreshing, setIsRefreshing ] = useState( false );
const { merchantFeatures } = useMerchantInfo();
@ -67,12 +66,7 @@ const TabOverview = () => {
'woocommerce-paypal-payments'
) }
>
<TodoSettingsBlock
todos={ todos }
setTodos={ setTodos }
todosData={ todosData }
setTodosData={ setTodosData }
/>
<TodoSettingsBlock todosData={ todosData } />
</SettingsCard>
) }
@ -210,40 +204,4 @@ const TabOverview = () => {
);
};
// TODO: This list should be refactored into a separate module, maybe utils/thingsToDoNext.js
const todosDataDefault = [
{
value: 'paypal_later_messaging',
description: __(
'Enable Pay Later messaging',
'woocommerce-paypal-payments'
),
},
{
value: 'capture_authorized_payments',
description: __(
'Capture authorized payments',
'woocommerce-paypal-payments'
),
},
{
value: 'enable_google_pay',
description: __( 'Enable Google Pay', 'woocommerce-paypal-payments' ),
},
{
value: 'paypal_shortcut',
description: __(
'Add PayPal shortcut to the Cart page',
'woocommerce-paypal-payments'
),
},
{
value: 'advanced_cards',
description: __(
'Add Advanced Cards to Blocks Checkout',
'woocommerce-paypal-payments'
),
},
];
export default TabOverview;

View file

@ -5,11 +5,11 @@ import SettingsCard from '../../ReusableComponents/SettingsCard';
import PaymentMethodsBlock from '../../ReusableComponents/SettingsBlocks/PaymentMethodsBlock';
import { CommonHooks } from '../../../data';
import { useActiveModal } from '../../../data/common/hooks';
import Modal from './Modals/Modal';
import Modal from './TabSettingsElements/Blocks/Modal';
const TabPaymentMethods = () => {
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
const { setActiveModal } = useActiveModal();
const { activeModal, setActiveModal } = useActiveModal();
const filteredPaymentMethods = useMemo( () => {
const contextProps = { storeCountry, storeCurrency };
@ -30,6 +30,20 @@ const TabPaymentMethods = () => {
};
}, [ storeCountry, storeCurrency ] );
const getActiveMethod = () => {
if ( ! activeModal ) {
return null;
}
const allMethods = [
...filteredPaymentMethods.payPalCheckout,
...filteredPaymentMethods.onlineCardPayments,
...filteredPaymentMethods.alternative,
];
return allMethods.find( ( method ) => method.id === activeModal );
};
return (
<div className="ppcp-r-payment-methods">
<SettingsCard
@ -84,7 +98,20 @@ const TabPaymentMethods = () => {
/>
</SettingsCard>
<Modal />
{ activeModal && (
<Modal
method={ getActiveMethod() }
setModalIsVisible={ () => setActiveModal( null ) }
onSave={ ( methodId, settings ) => {
console.log(
'Saving settings for:',
methodId,
settings
);
setActiveModal( null );
} }
/>
) }
</div>
);
};
@ -162,7 +189,7 @@ const paymentMethodsOnlineCardPayments = [
icon: 'payment-method-fastlane',
},
{
id: 'apply_pay',
id: 'apple_pay',
title: __( 'Apple Pay', 'woocommerce-paypal-payments' ),
description: __(
'Allow customers to pay via their Apple Pay digital wallet.',

View file

@ -0,0 +1,41 @@
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import {
AccordionSettingsBlock,
RadioSettingsBlock,
InputSettingsBlock,
} from '../../../../ReusableComponents/SettingsBlocks';
import {
sandboxData,
productionData,
} from '../../../../../data/settings/connection-details-data';
const ConnectionDetails = ( { settings, updateFormValue } ) => {
const isSandbox = settings.sandboxConnected;
const modeConfig = isSandbox
? productionData( { settings, updateFormValue } )
: sandboxData( { settings, updateFormValue } );
const modeKey = isSandbox ? 'productionMode' : 'sandboxMode';
return (
<AccordionSettingsBlock
title={ modeConfig.title }
description={ modeConfig.description }
>
<RadioSettingsBlock
title={ modeConfig.connectTitle }
description={ modeConfig.connectDescription }
options={ modeConfig.options }
actionProps={ {
key: modeKey,
currentValue: settings[ modeKey ],
callback: updateFormValue,
} }
/>
</AccordionSettingsBlock>
);
};
export default ConnectionDetails;

View file

@ -0,0 +1,131 @@
import { __ } from '@wordpress/i18n';
import {
Button,
TextControl,
ToggleControl,
RadioControl,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import PaymentMethodModal from '../../../../ReusableComponents/PaymentMethodModal';
import { getPaymentMethods } from './PaymentMethods';
const Modal = ( { method, setModalIsVisible, onSave } ) => {
const [ settings, setSettings ] = useState( () => {
if ( ! method?.id ) {
return {};
}
const methodConfig = getPaymentMethods( method );
if ( ! methodConfig?.fields ) {
return {};
}
const initialSettings = {};
Object.entries( methodConfig.fields ).forEach( ( [ key, field ] ) => {
initialSettings[ key ] = field.default;
} );
return initialSettings;
} );
if ( ! method?.id ) {
return null;
}
const methodConfig = getPaymentMethods( method );
if ( ! methodConfig?.fields ) {
return null;
}
const renderField = ( key, field ) => {
switch ( field.type ) {
case 'text':
return (
<div className="ppcp-r-modal__field-row">
<TextControl
className="ppcp-r-vertical-text-control"
label={ field.label }
value={ settings[ key ] }
onChange={ ( value ) =>
setSettings( ( prev ) => ( {
...prev,
[ key ]: value,
} ) )
}
/>
</div>
);
case 'toggle':
return (
<div className="ppcp-r-modal__field-row">
<ToggleControl
label={ field.label }
checked={ settings[ key ] }
onChange={ ( value ) =>
setSettings( ( prev ) => ( {
...prev,
[ key ]: value,
} ) )
}
/>
</div>
);
case 'radio':
return (
<>
<strong className="ppcp-r-modal__content-title">
{ field.label }
</strong>
{ field.description && (
<p className="ppcp-r-modal__description">
{ field.description }
</p>
) }
<div className="ppcp-r-modal__field-row">
<RadioControl
selected={ settings[ key ] }
options={ field.options }
onChange={ ( value ) =>
setSettings( ( prev ) => ( {
...prev,
[ key ]: value,
} ) )
}
/>
</div>
</>
);
default:
return null;
}
};
const handleSave = () => {
onSave?.( method.id, settings );
setModalIsVisible( false );
};
return (
<PaymentMethodModal
setModalIsVisible={ setModalIsVisible }
icon={ methodConfig.icon }
title={ method.title }
>
<div className="ppcp-r-modal__field-rows">
{ Object.entries( methodConfig.fields ).map(
( [ key, field ] ) => renderField( key, field )
) }
<div className="ppcp-r-modal__field-row ppcp-r-modal__field-row--save">
<Button variant="primary" onClick={ handleSave }>
{ __( 'Save changes', 'woocommerce-paypal-payments' ) }
</Button>
</div>
</div>
</PaymentMethodModal>
);
};
export default Modal;

View file

@ -0,0 +1,179 @@
import { __, sprintf } from '@wordpress/i18n';
const createStandardFields = ( methodId, defaultTitle ) => ( {
checkoutPageTitle: {
type: 'text',
default: defaultTitle,
label: __( 'Checkout page title', 'woocommerce-paypal-payments' ),
},
checkoutPageDescription: {
type: 'text',
default: sprintf(
/* translators: %s: payment method title */
__( 'Pay with %s', 'woocommerce-paypal-payments' ),
defaultTitle
),
label: __( 'Checkout page description', 'woocommerce-paypal-payments' ),
},
} );
const paymentMethods = {
// PayPal Checkout methods
paypal: {
fields: {
...createStandardFields( 'paypal', 'PayPal' ),
showLogo: {
type: 'toggle',
default: false,
label: __( 'Show logo', 'woocommerce-paypal-payments' ),
},
},
},
venmo: {
fields: createStandardFields( 'venmo', 'Venmo' ),
},
paypal_credit: {
fields: createStandardFields( 'paypal_credit', 'PayPal Credit' ),
},
credit_and_debit_card_payments: {
fields: createStandardFields(
'credit_and_debit_card_payments',
__(
'Credit and debit card payments',
'woocommerce-paypal-payments'
)
),
},
// Online Card Payments
advanced_credit_and_debit_card_payments: {
fields: {
...createStandardFields(
'advanced_credit_and_debit_card_payments',
__(
'Advanced Credit and Debit Card Payments',
'woocommerce-paypal-payments'
)
),
threeDSecure: {
type: 'radio',
default: 'no-3d-secure',
label: __( '3D Secure', 'woocommerce-paypal-payments' ),
description: __(
'Authenticate cardholders through their card issuers to reduce fraud and improve transaction security. Successful 3D Secure authentication can shift liability for fraudulent chargebacks to the card issuer.',
'woocommerce-paypal-payments'
),
options: [
{
label: __(
'No 3D Secure',
'woocommerce-paypal-payments'
),
value: 'no-3d-secure',
},
{
label: __(
'Only when required',
'woocommerce-paypal-payments'
),
value: 'only-required-3d-secure',
},
{
label: __(
'Always require 3D Secure',
'woocommerce-paypal-payments'
),
value: 'always-3d-secure',
},
],
},
},
},
fastlane: {
fields: {
...createStandardFields( 'fastlane', 'Fastlane by PayPal' ),
cardholderName: {
type: 'toggle',
default: false,
label: __(
'Display cardholder name',
'woocommerce-paypal-payments'
),
},
displayWatermark: {
type: 'toggle',
default: false,
label: __(
'Display Fastlane Watermark',
'woocommerce-paypal-payments'
),
},
},
},
// Digital Wallets
apple_pay: {
fields: createStandardFields( 'apple_pay', 'Apple Pay' ),
},
google_pay: {
fields: createStandardFields( 'google_pay', 'Google Pay' ),
},
// Alternative Payment Methods
bancontact: {
fields: createStandardFields( 'bancontact', 'Bancontact' ),
},
ideal: {
fields: createStandardFields( 'ideal', 'iDEAL' ),
},
eps: {
fields: createStandardFields( 'eps', 'eps' ),
},
blik: {
fields: createStandardFields( 'blik', 'BLIK' ),
},
mybank: {
fields: createStandardFields( 'mybank', 'MyBank' ),
},
przelewy24: {
fields: createStandardFields( 'przelewy24', 'Przelewy24' ),
},
trustly: {
fields: createStandardFields( 'trustly', 'Trustly' ),
},
multibanco: {
fields: createStandardFields( 'multibanco', 'Multibanco' ),
},
pui: {
fields: createStandardFields( 'pui', 'Pay upon Invoice' ),
},
oxxo: {
fields: createStandardFields( 'oxxo', 'OXXO' ),
},
};
// Function to get configuration for a payment method
export const getPaymentMethods = ( method ) => {
if ( ! method?.id ) {
return null;
}
// If method has specific config, return it
if ( paymentMethods[ method.id ] ) {
return {
...paymentMethods[ method.id ],
icon: method.icon,
};
}
// Return standard config for new payment methods
return {
fields: createStandardFields( method.id, method.title ),
icon: method.icon,
};
};
// Function to check if a method has settings defined
export const hasSettings = ( methodId ) => {
return Boolean( methodId && paymentMethods[ methodId ] );
};

View file

@ -1,202 +0,0 @@
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import {
AccordionSettingsBlock,
ButtonSettingsBlock,
RadioSettingsBlock,
ToggleSettingsBlock,
InputSettingsBlock,
} from '../../../../ReusableComponents/SettingsBlocks';
import TitleBadge, {
TITLE_BADGE_POSITIVE,
} from '../../../../ReusableComponents/TitleBadge';
import ConnectionInfo, {
connectionStatusDataDefault,
} from '../../../../ReusableComponents/ConnectionInfo';
const Sandbox = ( { settings, updateFormValue } ) => {
const className = settings.sandboxConnected
? 'ppcp-r-settings-block--sandbox-connected'
: 'ppcp-r-settings-block--sandbox-disconnected';
return (
<AccordionSettingsBlock
title={ __( 'Sandbox', 'woocommerce-paypal-payments' ) }
className={ className }
description={ __(
"Test your site in PayPal's Sandbox environment.",
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'payNowExperience',
value: settings.payNowExperience,
} }
>
{ settings.sandboxConnected && (
<ButtonSettingsBlock
title={ __(
'Sandbox account credentials',
'woocommerce-paypal-payments'
) }
description={ __(
'Your account is connected to sandbox, no real charging takes place. To accept live payments, turn off sandbox mode and connect your live PayPal account.',
'woocommerce-paypal-payments'
) }
tag={
<TitleBadge
type={ TITLE_BADGE_POSITIVE }
text={ __(
'Connected',
'woocommerce-paypal-payments'
) }
/>
}
>
<div className="ppcp-r-settings-block--sandbox">
<ToggleSettingsBlock
title={ __(
'Enable sandbox mode',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'sandboxEnabled',
value: settings.sandboxEnabled,
} }
/>
<ConnectionInfo
connectionStatusDataDefault={
connectionStatusDataDefault
}
/>
<Button
variant="secondary"
onClick={ () =>
updateFormValue( 'sandboxConnected', false )
}
>
{ __(
'Disconnect Sandbox',
'woocommerce-paypal-payments'
) }
</Button>
</div>
</ButtonSettingsBlock>
) }
{ ! settings.sandboxConnected && (
<RadioSettingsBlock
title={ __(
'Connect Sandbox Account',
'woocommerce-paypal-payments'
) }
description={ __(
'Connect a PayPal Sandbox account in order to test your website. Transactions made will not result in actual money movement. Do not fulfil orders completed in Sandbox mode.',
'woocommerce-paypal-payments'
) }
options={ [
{
id: 'sandbox_mode',
value: 'sandbox_mode',
label: __(
'Sandbox Mode',
'woocommerce-paypal-payments'
),
description: __(
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
'woocommerce-paypal-payments'
),
additionalContent: (
<Button
variant="primary"
onClick={ () =>
updateFormValue(
'sandboxConnected',
true
)
}
>
{ __(
'Connect Sandbox Account',
'woocommerce-paypal-payments'
) }
</Button>
),
},
{
id: 'manual_connect',
value: 'manual_connect',
label: __(
'Manual Connect',
'woocommerce-paypal-payments'
),
description: sprintf(
__(
'For advanced users: Connect a custom PayPal REST app for full control over your integration. For more information on creating a PayPal REST application, <a target="_blank" href="%s">click here</a>.',
'woocommerce-paypal-payments'
),
'#'
),
additionalContent: (
<>
<InputSettingsBlock
title={ __(
'Sandbox Client ID',
'woocommerce-paypal-payments'
) }
actionProps={ {
value: settings.sandboxClientId, // Add this to settings if not present
callback: updateFormValue,
key: 'sandboxClientId',
placeholder: __(
'Enter Client ID',
'woocommerce-paypal-payments'
),
} }
/>
<InputSettingsBlock
title={ __(
'Sandbox Secret Key',
'woocommerce-paypal-payments'
) }
actionProps={ {
value: settings.sandboxSecretKey, // Add this to settings if not present
callback: updateFormValue,
key: 'sandboxSecretKey',
placeholder: __(
'Enter Secret Key',
'woocommerce-paypal-payments'
),
} }
/>
<Button
variant="primary"
onClick={ () =>
updateFormValue(
'sandboxManuallyConnected',
true
)
} // Add this handler if needed
>
{ __(
'Connect Account',
'woocommerce-paypal-payments'
) }
</Button>
</>
),
},
] }
actionProps={ {
name: 'paypal_connect_sandbox',
key: 'sandboxMode',
currentValue: settings.sandboxMode,
callback: updateFormValue,
} }
/>
) }
</AccordionSettingsBlock>
);
};
export default Sandbox;

View file

@ -4,7 +4,7 @@ import {
Content,
ContentWrapper,
} from '../../../ReusableComponents/SettingsBlocks';
import Sandbox from './Blocks/Sandbox';
import ConnectionDetails from './Blocks/ConnectionDetails';
import Troubleshooting from './Blocks/Troubleshooting/Troubleshooting';
import PaypalSettings from './Blocks/PaypalSettings';
import OtherSettings from './Blocks/OtherSettings';
@ -27,7 +27,7 @@ const ExpertSettings = ( { updateFormValue, settings } ) => {
>
<ContentWrapper>
<Content>
<Sandbox
<ConnectionDetails
updateFormValue={ updateFormValue }
settings={ settings }
/>

View file

@ -0,0 +1,178 @@
import { __, sprintf } from '@wordpress/i18n';
import { InputSettingsBlock } from '../../Components/ReusableComponents/SettingsBlocks';
import { Button } from '@wordpress/components';
/**
* Generates options for the environment mode settings.
*
* @param {Object} config - Configuration for the mode.
* @param {Object} settings - Current settings.
* @param {Function} updateFormValue - Callback to update settings.
* @return {Array} Options array.
*/
const generateOptions = ( config, settings, updateFormValue ) => [
{
id: `${ config.mode }_mode`,
value: `${ config.mode }_mode`,
label: config.labelTitle,
description: config.labelDescription,
additionalContent: (
<Button
variant="primary"
onClick={ () => {
updateFormValue( `${ config.mode }Connected`, true );
if ( config.mode === 'production' ) {
global.ppcpSettings.startOnboarding();
}
} }
>
{ config.buttonText }
</Button>
),
},
{
id: 'manual_connect',
value: 'manual_connect',
label: __( 'Manual Connect', 'woocommerce-paypal-payments' ),
description: sprintf(
__(
'For advanced users: Connect a custom PayPal REST app for full control over your integration. For more information on creating a PayPal REST application, <a target="_blank" href="%s">click here</a>.',
'woocommerce-paypal-payments'
),
'#'
),
additionalContent: (
<>
<InputSettingsBlock
title={ config.clientIdTitle }
actionProps={ {
value: settings[ `${ config.mode }ClientId` ],
callback: updateFormValue,
key: `${ config.mode }ClientId`,
placeholder: __(
'Enter Client ID',
'woocommerce-paypal-payments'
),
} }
/>
<InputSettingsBlock
title={ config.secretKeyTitle }
actionProps={ {
value: settings[ `${ config.mode }SecretKey` ],
callback: updateFormValue,
key: `${ config.mode }SecretKey`,
placeholder: __(
'Enter Secret Key',
'woocommerce-paypal-payments'
),
} }
/>
<Button
variant="primary"
onClick={ () =>
updateFormValue(
`${ config.mode }ManuallyConnected`,
true
)
}
>
{ __( 'Connect Account', 'woocommerce-paypal-payments' ) }
</Button>
</>
),
},
];
/**
* Generates data for a given mode (sandbox or production).
*
* @param {Object} config - Configuration for the mode.
* @param {Object} settings - Current settings.
* @param {Function} updateFormValue - Callback to update settings.
* @return {Object} Mode configuration.
*/
const generateModeData = ( config, settings, updateFormValue ) => ( {
title: config.title,
description: config.description,
connectTitle: __(
`Connect ${ config.label } Account`,
'woocommerce-paypal-payments'
),
connectDescription: config.connectDescription,
options: generateOptions( config, settings, updateFormValue ),
} );
export const sandboxData = ( { settings = {}, updateFormValue = () => {} } ) =>
generateModeData(
{
mode: 'sandbox',
label: 'Sandbox',
title: __( 'Sandbox', 'woocommerce-paypal-payments' ),
description: __(
"Test your site in PayPal's Sandbox environment.",
'woocommerce-paypal-payments'
),
connectDescription: __(
'Connect a PayPal Sandbox account in order to test your website. Transactions made will not result in actual money movement. Do not fulfil orders completed in Sandbox mode.',
'woocommerce-paypal-payments'
),
labelTitle: __( 'Sandbox Mode', 'woocommerce-paypal-payments' ),
labelDescription: __(
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
'woocommerce-paypal-payments'
),
buttonText: __(
'Connect Sandbox Account',
'woocommerce-paypal-payments'
),
clientIdTitle: __(
'Sandbox Client ID',
'woocommerce-paypal-payments'
),
secretKeyTitle: __(
'Sandbox Secret Key',
'woocommerce-paypal-payments'
),
},
settings,
updateFormValue
);
export const productionData = ( {
settings = {},
updateFormValue = () => {},
} ) =>
generateModeData(
{
mode: 'production',
label: 'Live',
title: __( 'Live Payments', 'woocommerce-paypal-payments' ),
description: __(
'Your site is currently configured in Sandbox mode to test payments. When you are ready, launch your site and receive live payments via PayPal.',
'woocommerce-paypal-payments'
),
connectDescription: __(
'Connect a live PayPal account to launch your site and receive live payments via PayPal. PayPal will guide you through the setup process.',
'woocommerce-paypal-payments'
),
labelTitle: __( 'Production Mode', 'woocommerce-paypal-payments' ),
labelDescription: __(
'Activate Production mode to connect your live account and receive live payments via PayPal. Stay connected in Sandbox mode to continue testing payments before going live.',
'woocommerce-paypal-payments'
),
buttonText: __(
'Set up and connect live PayPal Account',
'woocommerce-paypal-payments'
),
clientIdTitle: __(
'Live Account Client ID',
'woocommerce-paypal-payments'
),
secretKeyTitle: __(
'Live Account Secret Key',
'woocommerce-paypal-payments'
),
},
settings,
updateFormValue
);

View file

@ -0,0 +1,170 @@
import { __ } from '@wordpress/i18n';
import { selectTab, TAB_IDS } from '../../utils/tabSelector';
export const todosData = [
{
id: 'enable_fastlane',
title: __( 'Enable Fastlane', 'woocommerce-paypal-payments' ),
description: __(
'Accelerate your guest checkout with Fastlane by PayPal.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.PAYMENT_METHODS, 'ppcp-card-payments-card' );
},
},
{
id: 'enable_credit_debit_cards',
title: __(
'Enable Credit and Debit Cards on your checkout',
'woocommerce-paypal-payments'
),
description: __(
'Credit and Debit Cards is now available for Blocks checkout pages.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.PAYMENT_METHODS, 'ppcp-card-payments-card' );
},
},
{
id: 'enable_pay_later_messaging',
title: __(
'Enable Pay Later messaging',
'woocommerce-paypal-payments'
),
description: __(
'Show Pay Later messaging to boost conversion rate and increase cart size.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.OVERVIEW, 'pay_later_messaging' );
},
},
{
id: 'configure_paypal_subscription',
title: __(
'Configure a PayPal Subscription',
'woocommerce-paypal-payments'
),
description: __(
'Connect a subscriptions-type product from WooCommerce with PayPal.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
console.log(
'Take merchant to product list, filtered with subscription-type products'
);
},
},
{
id: 'register_domain_apple_pay',
title: __(
'Register Domain for Apple Pay',
'woocommerce-paypal-payments'
),
description: __(
'To enable Apple Pay, you must register your domain with PayPal.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.OVERVIEW, 'apple_pay' );
},
},
{
id: 'add_digital_wallets_to_account',
title: __(
'Add digital wallets to your account',
'woocommerce-paypal-payments'
),
description: __(
'Add the ability to accept Apple Pay & Google Pay to your PayPal account.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
console.log(
'Take merchant to PayPal to enable Apple Pay & Google Pay'
);
},
},
{
id: 'add_apple_pay_to_account',
title: __(
'Add Apple Pay to your account',
'woocommerce-paypal-payments'
),
description: __(
'Add the ability to accept Apple Pay to your PayPal account.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
console.log( 'Take merchant to PayPal to enable Apple Pay' );
},
},
{
id: 'add_google_pay_to_account',
title: __(
'Add Google Pay to your account',
'woocommerce-paypal-payments'
),
description: __(
'Add the ability to accept Google Pay to your PayPal account.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
console.log( 'Take merchant to PayPal to enable Google Pay' );
},
},
{
id: 'enable_apple_pay',
title: __( 'Enable Apple Pay', 'woocommerce-paypal-payments' ),
description: __(
'Allow your buyers to check out via Apple Pay.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.OVERVIEW, 'apple_pay' );
},
},
{
id: 'enable_google_pay',
title: __( 'Enable Google Pay', 'woocommerce-paypal-payments' ),
description: __(
'Allow your buyers to check out via Google Pay.',
'woocommerce-paypal-payments'
),
isCompleted: () => {
return false;
},
onClick: () => {
selectTab( TAB_IDS.OVERVIEW, 'google_pay' );
},
},
];