From 02baae890fc4d248362f8f5d7d9a6707dbb3a747 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 9 Jan 2025 00:16:12 +0100 Subject: [PATCH 1/2] Consolidate payment method modals into unified config-driven system --- .../SettingsBlocks/PaymentMethodItemBlock.js | 6 +- .../SettingsBlocks/PaymentMethodsBlock.js | 13 +- .../Screens/Overview/Modals/Modal.js | 51 ----- .../Screens/Overview/Modals/ModalAcdc.js | 62 ------ .../Screens/Overview/Modals/ModalFastlane.js | 63 ------ .../Screens/Overview/Modals/ModalPayPal.js | 76 -------- .../Screens/Overview/TabPaymentMethods.js | 35 +++- .../TabSettingsElements/Blocks/Modal.js | 131 +++++++++++++ .../Blocks/PaymentMethods.js | 179 ++++++++++++++++++ 9 files changed, 353 insertions(+), 263 deletions(-) delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalAcdc.js delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalFastlane.js delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalPayPal.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Modal.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaymentMethods.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js index c2a7c5a70..cf26431a3 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -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 ( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js index 1caae6ce6..17f610660 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js @@ -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 = ( { handleSelect( paymentMethod.id, checked ) } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js deleted file mode 100644 index a0c401d44..000000000 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js +++ /dev/null @@ -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 ( - - ); -}; - -export default Modal; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalAcdc.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalAcdc.js deleted file mode 100644 index 3d924dcbe..000000000 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalAcdc.js +++ /dev/null @@ -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 ( - - - { __( '3D Secure', 'woocommerce-paypal-payments' ) } - -

- { __( - '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' - ) } -

-
- - -
- -
-
-
- ); -}; - -export default ModalAcdc; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalFastlane.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalFastlane.js deleted file mode 100644 index 466f3b57f..000000000 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalFastlane.js +++ /dev/null @@ -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 ( - -
-
- - updateFormValue( 'cardholderName', newValue ) - } - label={ __( - 'Display cardholder name', - 'woocommerce-paypal-payments' - ) } - id="ppcp-r-fastlane-settings-cardholder" - /> -
-
- - updateFormValue( 'displayWatermark', newValue ) - } - label={ __( - 'Display Fastlane Watermark', - 'woocommerce-paypal-payments' - ) } - id="ppcp-r-fastlane-settings-watermark" - /> -
-
- -
-
-
- ); -}; - -export default ModalFastlane; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalPayPal.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalPayPal.js deleted file mode 100644 index 780f7d9f1..000000000 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/ModalPayPal.js +++ /dev/null @@ -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 ( - -
-
- - updateFormValue( 'checkoutPageTitle', newValue ) - } - /> -
-
- - updateFormValue( - 'checkoutPageDescription', - newValue - ) - } - /> -
-
-
-
- -
-
-
- ); -}; - -export default ModalPayPal; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js index d9b0da327..e16109146 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js @@ -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 (
{ /> - + { activeModal && ( + setActiveModal( null ) } + onSave={ ( methodId, settings ) => { + console.log( + 'Saving settings for:', + methodId, + settings + ); + setActiveModal( null ); + } } + /> + ) }
); }; @@ -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.', diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Modal.js new file mode 100644 index 000000000..c8be1c761 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Modal.js @@ -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 ( +
+ + setSettings( ( prev ) => ( { + ...prev, + [ key ]: value, + } ) ) + } + /> +
+ ); + + case 'toggle': + return ( +
+ + setSettings( ( prev ) => ( { + ...prev, + [ key ]: value, + } ) ) + } + /> +
+ ); + + case 'radio': + return ( + <> + + { field.label } + + { field.description && ( +

+ { field.description } +

+ ) } +
+ + setSettings( ( prev ) => ( { + ...prev, + [ key ]: value, + } ) ) + } + /> +
+ + ); + + default: + return null; + } + }; + + const handleSave = () => { + onSave?.( method.id, settings ); + setModalIsVisible( false ); + }; + + return ( + +
+ { Object.entries( methodConfig.fields ).map( + ( [ key, field ] ) => renderField( key, field ) + ) } + +
+ +
+
+
+ ); +}; + +export default Modal; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaymentMethods.js new file mode 100644 index 000000000..21ae3f028 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaymentMethods.js @@ -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 ] ); +}; From bbe2dcf5d0eccb21eadefb0a4b5e38bb77f4e978 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 9 Jan 2025 11:16:34 +0100 Subject: [PATCH 2/2] Revert "woorelease: Product version bump update" This reverts commit e0f2dcbc9fae5ff291b53fadc8d599ca92be1c1a. --- changelog.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 2e1d3420c..1efeb16a3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ *** Changelog *** -= 2.9.6 - 2025-01-06 = += 2.9.6 - XXXX-XX-XX = * Fix - NOT_ENABLED_TO_VAULT_PAYMENT_SOURCE on PayPal transactions when using ACDC Vaulting without PayPal Vault approval #2955 * Fix - Express buttons for Free Trial Subscription products on Block Cart/Checkout trigger CANNOT_BE_ZERO_OR_NEGATIVE error #2872 * Fix - String translations not applied to Card Fields on Block Checkout #2934 diff --git a/readme.txt b/readme.txt index fc2caf2ed..c35d7f244 100644 --- a/readme.txt +++ b/readme.txt @@ -179,7 +179,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == -= 2.9.6 - 2025-01-06 = += 2.9.6 - XXXX-XX-XX = * Fix - NOT_ENABLED_TO_VAULT_PAYMENT_SOURCE on PayPal transactions when using ACDC Vaulting without PayPal Vault approval #2955 * Fix - Express buttons for Free Trial Subscription products on Block Cart/Checkout trigger CANNOT_BE_ZERO_OR_NEGATIVE error #2872 * Fix - String translations not applied to Card Fields on Block Checkout #2934