From 3a5e748bc4a41ffd9bd515a7fc6961ac548bbaf9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Jan 2025 15:39:30 +0100 Subject: [PATCH 01/78] Add oxxo logo and do not render icon if not exist --- .../SettingsBlocks/PaymentMethodItemBlock.js | 10 ++++++---- .../ppcp-settings/src/Endpoint/PaymentRestEndpoint.php | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) 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 516851977..6daa6ed48 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -14,10 +14,12 @@ const PaymentMethodItemBlock = ( {
- + { paymentMethod?.icon && ( + + ) } { paymentMethod.itemTitle } diff --git a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php index fcbc6d6bc..5b03b8ab8 100644 --- a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php @@ -576,7 +576,7 @@ class PaymentRestEndpoint extends RestEndpoint { 'OXXO is a Mexican chain of convenience stores. *Get PayPal account permission to use OXXO payment functionality by contacting us at (+52) 800–925–0304', 'woocommerce-paypal-payments' ), - 'icon' => '', + 'icon' => 'payment-method-oxxo', 'itemTitle' => __( 'OXXO', 'woocommerce-paypal-payments' ), 'itemDescription' => __( 'OXXO is a Mexican chain of convenience stores. *Get PayPal account permission to use OXXO payment functionality by contacting us at (+52) 800–925–0304', From 108d70e879cb8fc7e90e2dbabc70d9484644fd2d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Jan 2025 16:43:55 +0100 Subject: [PATCH 02/78] Replace encoded ampersand with a decoded one --- modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php index 5b03b8ab8..7ea87b743 100644 --- a/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/PaymentRestEndpoint.php @@ -669,7 +669,7 @@ class PaymentRestEndpoint extends RestEndpoint { $gateway_settings[ $key ] = array( 'enabled' => 'yes' === $gateway->enabled, - 'title' => $gateway->get_title(), + 'title' => str_replace( '&', '&', $gateway->get_title() ), 'description' => $gateway->get_description(), 'id' => $this->gateways()[ $key ]['id'] ?? $key, 'icon' => $this->gateways()[ $key ]['icon'] ?? '', From fe412d117e4904ab7f73bd2ee7ef041563f61da0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 Jan 2025 16:39:15 +0100 Subject: [PATCH 03/78] Add configure action to save PayPal and Venmo item --- .../js/Components/ReusableComponents/SettingsBlock.js | 2 +- .../Screens/Settings/Components/Overview/Features.js | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js index 2c92942c7..e17a8ccd4 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js @@ -32,7 +32,7 @@ const SettingsBlock = ( { }; return ( -
+
{ selectTab( - TAB_IDS.PAYMENT_METHODS, - 'ppcp-paypal-checkout-card' - ).then( () => { - setActiveModal( 'paypal' ); - } ); + TAB_IDS.SETTINGS, + 'ppcp--save-payment-methods' + ); }, showWhen: 'enabled', class: 'small-button', From 34ca8db61fde042dbcba6ac9d97b58344a80c5a5 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 28 Jan 2025 08:59:06 +0100 Subject: [PATCH 04/78] Move tabpanel navigation up --- .../resources/js/Components/App.js | 12 ++++++++-- .../ReusableComponents/TabNavigation.js | 22 +++++-------------- .../ReusableComponents/TopNavigation.js | 10 +++++++++ .../Screens/Settings/Components/Navigation.js | 10 +++++++-- .../js/Components/Screens/Settings/index.js | 17 +++++++------- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/App.js b/modules/ppcp-settings/resources/js/Components/App.js index f0791e2fa..6db75a377 100644 --- a/modules/ppcp-settings/resources/js/Components/App.js +++ b/modules/ppcp-settings/resources/js/Components/App.js @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from '@wordpress/element'; +import { useEffect, useMemo, useState } from '@wordpress/element'; import classNames from 'classnames'; import { OnboardingHooks, CommonHooks } from '../data'; @@ -31,6 +31,8 @@ const SettingsApp = () => { loading: ! onboardingIsReady, } ); + const [ activePanel, setActivePanel ] = useState( 'overview' ); + const Content = useMemo( () => { if ( ! onboardingIsReady || ! merchantIsReady ) { return ; @@ -44,12 +46,18 @@ const SettingsApp = () => { return ; } - return ; + return ( + + ); }, [ isSendOnlyCountry, merchantIsReady, onboardingCompleted, onboardingIsReady, + activePanel, ] ); return
{ Content }
; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js index 0ae4e001e..4a98860fd 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js @@ -1,26 +1,14 @@ -import { useCallback, useEffect, useState } from '@wordpress/element'; +import { useCallback, useEffect } from '@wordpress/element'; // TODO: Migrate to Tabs (TabPanel v2) once its API is publicly available, as it provides programmatic tab switching support: https://github.com/WordPress/gutenberg/issues/52997 import { TabPanel } from '@wordpress/components'; -import { getQuery, updateQueryString } from '../../utils/navigation'; - -const TabNavigation = ( { tabs } ) => { - const { panel } = getQuery(); +import { updateQueryString } from '../../utils/navigation'; +const TabNavigation = ( { tabs, activePanel, setActivePanel } ) => { const isValidTab = ( tabsList, checkTab ) => { return tabsList.some( ( tab ) => tab.name === checkTab ); }; - - const getValidInitialPanel = () => { - if ( ! panel || ! isValidTab( tabs, panel ) ) { - return tabs[ 0 ].name; - } - return panel; - }; - - const [ activePanel, setActivePanel ] = useState( getValidInitialPanel ); - const updateActivePanel = useCallback( ( tabName ) => { if ( isValidTab( tabs, tabName ) ) { @@ -29,7 +17,7 @@ const TabNavigation = ( { tabs } ) => { console.warn( `Invalid tab name: ${ tabName }` ); } }, - [ tabs ] + [ tabs, setActivePanel ] ); useEffect( () => { @@ -43,7 +31,7 @@ const TabNavigation = ( { tabs } ) => { onSelect={ updateActivePanel } tabs={ tabs } > - { ( { Component } ) => Component } + { () => '' } ); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js index bbffaaaa5..f12b092d7 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js @@ -6,6 +6,8 @@ import classNames from 'classnames'; import useIsScrolled from '../../hooks/useIsScrolled'; import { useNavigation } from '../../hooks/useNavigation'; import BusyStateWrapper from './BusyStateWrapper'; +import { getSettingsTabs } from '../Screens/Settings/Tabs'; +import TabNavigation from './TabNavigation'; const TopNavigation = ( { title, @@ -15,6 +17,9 @@ const TopNavigation = ( { onTitleClick = null, showProgressBar = false, progressBarPercent = 0, + tabs, + activePanel, + setActivePanel, } ) => { const { goToWooCommercePaymentsTab } = useNavigation(); const { isScrolled } = useIsScrolled(); @@ -63,6 +68,11 @@ const TopNavigation = ( { > { children } + { showProgressBar && ( diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js index 9767730b7..ea8e6c67e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js @@ -5,13 +5,19 @@ import TopNavigation from '../../../ReusableComponents/TopNavigation'; import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper'; import { useSaveSettings } from '../../../../hooks/useSaveSettings'; -const SettingsNavigation = () => { +const SettingsNavigation = ( { tabs, activePanel, setActivePanel } ) => { const { persistAll } = useSaveSettings(); const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' ); return ( - + - + + - - { children } - + + { children } + +
Date: Tue, 28 Jan 2025 12:42:43 +0100 Subject: [PATCH 09/78] =?UTF-8?q?=F0=9F=90=9B=20Fix=20the=20JS=20error=20a?= =?UTF-8?q?bout=20=E2=80=9CButton=20not=20defined=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SettingsBlocks/PaymentMethodItemBlock.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 df2c5159c..d8a0bb775 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -1,4 +1,5 @@ -import { ToggleControl } from '@wordpress/components'; +import { ToggleControl, Icon, Button } from '@wordpress/components'; +import { cog } from '@wordpress/icons'; import SettingsBlock from '../SettingsBlock'; import PaymentMethodIcon from '../PaymentMethodIcon'; @@ -13,12 +14,12 @@ const PaymentMethodItemBlock = ( {
- { paymentMethod?.icon && ( - - ) } + { paymentMethod?.icon && ( + + ) } { paymentMethod.itemTitle } From dc6c8e2316e5ba3e0561521c148bea82d828eee2 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 12:44:30 +0100 Subject: [PATCH 10/78] =?UTF-8?q?=F0=9F=90=9B=20Fix=20JS=20error=20in=20th?= =?UTF-8?q?e=20payment=20method=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/Screens/Settings/Components/Payment/Modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js index 2865e8777..8930167f3 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js @@ -11,7 +11,7 @@ import PaymentMethodModal from '../../../../ReusableComponents/PaymentMethodModa import { PaymentHooks } from '../../../../../data'; const Modal = ( { method, setModalIsVisible, onSave } ) => { - const { paymentMethods } = PaymentHooks.usePaymentMethods(); + const { all: paymentMethods } = PaymentHooks.usePaymentMethods(); const { paypalShowLogo, threeDSecure, From d547a3b21836b5739ffd8efd20721e49f8c4a318 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 12:53:33 +0100 Subject: [PATCH 11/78] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20React=20deprecation?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/Controls/ControlToggleButton.js | 2 +- .../SettingsBlocks/PaymentMethodItemBlock.js | 2 +- .../js/Components/ReusableComponents/SettingsToggleBlock.js | 1 + .../js/Components/Screens/Settings/Components/Payment/Modal.js | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js index 86350f5c7..2d648e20b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js @@ -5,7 +5,7 @@ const ControlToggleButton = ( { label, description, value, onChange } ) => (
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js index 4a7cf1a20..614ad0a3b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js @@ -43,6 +43,7 @@ const SettingsToggleBlock = ( {
setToggled( newState ) } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js index 8930167f3..1db3ae16a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js @@ -66,7 +66,7 @@ const Modal = ( { method, setModalIsVisible, onSave } ) => { return (
{ return (
From 34ccd47bbc63f6050bd334c0c491aeb5238e5645 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 12:53:48 +0100 Subject: [PATCH 12/78] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20React=20warning=20ab?= =?UTF-8?q?out=20missing=20key-props?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Screens/Settings/Components/Payment/Modal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js index 1db3ae16a..a749c7420 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Payment/Modal.js @@ -64,7 +64,7 @@ const Modal = ( { method, setModalIsVisible, onSave } ) => { switch ( field.type ) { case 'text': return ( -
+
{ case 'toggle': return ( -
+
Date: Tue, 28 Jan 2025 13:26:44 +0100 Subject: [PATCH 13/78] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20psalm=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/src/Data/TodosModel.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-settings/src/Data/TodosModel.php b/modules/ppcp-settings/src/Data/TodosModel.php index fbd78fbf1..4cda1a75c 100644 --- a/modules/ppcp-settings/src/Data/TodosModel.php +++ b/modules/ppcp-settings/src/Data/TodosModel.php @@ -5,7 +5,7 @@ * @package WooCommerce\PayPalCommerce\Settings\Data */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Settings\Data; @@ -14,7 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; /** * Class TodosModel * - * Handles the storage and retrieval of todo completion states in WordPress options table. + * Handles the storage and retrieval of task completion states in WordPress options table. * Provides methods to get and update completion states with proper type casting. */ class TodosModel { @@ -23,7 +23,7 @@ class TodosModel { * * @var string */ - const OPTION_NAME = 'ppcp_todos'; + protected const OPTION_NAME = 'ppcp_todos'; /** * Retrieves the formatted completion states from WordPress options. @@ -33,12 +33,16 @@ class TodosModel { * * @return array The formatted completion states array. */ - public function get(): array { + public function get() : array { $completion_states = get_option( self::OPTION_NAME, array() ); + return array_map( - static function ( $state ) { - return (bool) $state; - }, + /** + * Ensures the task completion states are boolean values. + * + * @param mixed $state value to sanitize, as stored in the DB. + */ + static fn( $state ) => (bool) $state, $completion_states ); } @@ -49,11 +53,11 @@ class TodosModel { * Converts the provided data array and saves it to wp_options table. * Throws an exception if update fails. * - * @param array $states Array of todo IDs and their completion states. + * @param array $states Array of task IDs and their completion states. * @return void * @throws RuntimeException When the completion states update fails. */ - public function update( array $states ): void { + public function update( array $states ) : void { $completion_states = array_map( static function ( $state ) { return (bool) $state; From ede2cb51b3251fcbc145e346802f3341b0286d0d Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 28 Jan 2025 13:31:37 +0100 Subject: [PATCH 14/78] Remove lines --- .../css/components/reusable-components/_navigation.scss | 3 --- .../css/components/reusable-components/_tab-navigation.scss | 3 --- 2 files changed, 6 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 878cbff90..7eb7fd419 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -64,9 +64,6 @@ align-items: center; width: 100%; padding-bottom: 10px; - box-shadow: 0 -1px 0 0 $color-gray-300 inset; - background: var(--ppcp-color-app-bg); - transition: box-shadow 0.3s; } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_tab-navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_tab-navigation.scss index a2dd4c5a8..05cd48e36 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_tab-navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_tab-navigation.scss @@ -7,13 +7,10 @@ width: 100%; .components-tab-panel__tabs { - - margin-bottom: 48px; gap: 0; overflow: auto; .components-button { padding: 16px 20px; - box-shadow: 0 -1px 0 0 $color-gray-400 inset; &.is-active { background-color: #fff4; } From 2f998662678a6f432a7d81a33248253897fd1aed Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Tue, 28 Jan 2025 13:55:43 +0100 Subject: [PATCH 15/78] Fix CS --- .../Screens/Settings/Components/Navigation.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js index 239340231..7fdba20d1 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js @@ -4,17 +4,24 @@ import { __ } from '@wordpress/i18n'; import TopNavigation from '../../../ReusableComponents/TopNavigation'; import { useSaveSettings } from '../../../../hooks/useSaveSettings'; -const SettingsNavigation = ( { canSave = true, tabs, activePanel, setActivePanel } ) => { +const SettingsNavigation = ( { + canSave = true, + tabs, + activePanel, + setActivePanel, +} ) => { const { persistAll } = useSaveSettings(); const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' ); return ( - + { canSave && (
- + { tabs.length > 0 && ( + + ) } { showProgressBar && ( From 66752c4a0b246affc3da0a865d8cafce70d31e68 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 28 Jan 2025 15:07:38 +0100 Subject: [PATCH 17/78] Add apms enabled to endpoint response --- .../Settings/Components/Overview/Features.js | 296 ------------------ .../Components/Overview/features-config.js | 13 +- .../ppcp-wc-gateway/src/WCGatewayModule.php | 17 + 3 files changed, 21 insertions(+), 305 deletions(-) delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features.js diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features.js deleted file mode 100644 index 1db97727e..000000000 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/Features.js +++ /dev/null @@ -1,296 +0,0 @@ -import { __ } from '@wordpress/i18n'; -import { TAB_IDS, selectTab } from '../../../../../utils/tabSelector'; -import { payLaterMessagingComponentData } from '../../../../../data/settings/pay-later-messaging-component-data'; - -const Features = { - 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: __( 'Apply', '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' ), - urls: { - sandbox: '#', - live: '#', - }, - 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: __( 'Apply', '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: __( 'Apply', '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( 'google_pay' ); - } ); - }, - showWhen: 'enabled', - class: 'small-button', - }, - { - type: 'secondary', - text: __( 'Apply', '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( 'apple_pay' ); - } ); - }, - 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: __( 'Apply', '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 = - payLaterMessagingComponentData[ storeCountry ] || {}; - - if ( - !! window.ppcpSettings?.isPayLaterConfiguratorAvailable && - countryData - ) { - 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.PAYMENT_METHODS, - 'ppcp-paypal-checkout-card' - ).then( () => { - setActiveModal( 'paypal' ); - } ); - }, - showWhen: 'enabled', - class: 'small-button', - }, - { - type: 'secondary', - text: __( 'Apply', 'woocommerce-paypal-payments' ), - urls: { - sandbox: '#', - live: '#', - }, - showWhen: 'disabled', - class: 'small-button', - }, - { - type: 'tertiary', - text: __( 'Learn more', 'woocommerce-paypal-payments' ), - url: 'https://developer.paypal.com/studio/checkout/pay-later/us', - class: 'small-button', - }, - ], - } ); - } - - return features; - }, -}; - -export default Features; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js index 5dfd30c0c..07b5af356 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js @@ -18,11 +18,9 @@ export const getFeatures = ( setActiveModal ) => { text: __( 'Configure', 'woocommerce-paypal-payments' ), onClick: () => { selectTab( - TAB_IDS.PAYMENT_METHODS, - 'ppcp-paypal-checkout-card' - ).then( () => { - setActiveModal( 'paypal' ); - } ); + TAB_IDS.SETTINGS, + 'ppcp--save-payment-methods' + ); }, showWhen: 'enabled', class: 'small-button', @@ -68,9 +66,7 @@ export const getFeatures = ( setActiveModal ) => { TAB_IDS.PAYMENT_METHODS, 'ppcp-card-payments-card' ).then( () => { - setActiveModal( - 'advanced_credit_and_debit_card_payments' - ); + setActiveModal( 'ppcp-credit-card-gateway' ); } ); }, showWhen: 'enabled', @@ -239,7 +235,6 @@ export const getFeatures = ( setActiveModal ) => { const countryData = payLaterMessaging[ storeCountry ] || {}; - // Add "Pay Later Messaging" to the feature list, if it's available. if ( !! window.ppcpSettings?.isPayLaterConfiguratorAvailable && countryData diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 7539f60ee..c953def06 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -15,6 +15,7 @@ use Throwable; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\Orders; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; @@ -567,6 +568,22 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul 'enabled' => $dcc_enabled, ); + $partners_endpoint = $c->get( 'api.endpoint.partners' ); + assert($partners_endpoint instanceof PartnersEndpoint ); + $seller_status = $partners_endpoint->seller_status(); + + $apms_enabled = false; + foreach ( $seller_status->products() as $product ) { + if ( $product->name() === 'PAYMENT_METHODS' ) { + $apms_enabled = true; + break; + } + } + + $features['alternative_payment_methods'] = array( + 'enabled' => $apms_enabled, + ); + return $features; } ); From 463e158f2154aa2e801e45c829cf4165b1b48bc3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 28 Jan 2025 15:39:59 +0100 Subject: [PATCH 18/78] Add pay later messaging in the response --- .../Settings/Components/Overview/features-config.js | 11 +++-------- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 4 ++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js index 07b5af356..34c3e95ad 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Overview/features-config.js @@ -145,7 +145,7 @@ export const getFeatures = ( setActiveModal ) => { TAB_IDS.PAYMENT_METHODS, 'ppcp-card-payments-card' ).then( () => { - setActiveModal( 'google_pay' ); + setActiveModal( 'ppcp-googlepay' ); } ); }, showWhen: 'enabled', @@ -192,7 +192,7 @@ export const getFeatures = ( setActiveModal ) => { TAB_IDS.PAYMENT_METHODS, 'ppcp-card-payments-card' ).then( () => { - setActiveModal( 'apple_pay' ); + setActiveModal( 'ppcp-applepay' ); } ); }, showWhen: 'enabled', @@ -251,12 +251,7 @@ export const getFeatures = ( setActiveModal ) => { type: 'secondary', text: __( 'Configure', 'woocommerce-paypal-payments' ), onClick: () => { - selectTab( - TAB_IDS.PAYMENT_METHODS, - 'ppcp-paypal-checkout-card' - ).then( () => { - setActiveModal( 'paypal' ); - } ); + selectTab( TAB_IDS.PAY_LATER_MESSAGING ); }, showWhen: 'enabled', class: 'small-button', diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index c953def06..034753afa 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -584,6 +584,10 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul 'enabled' => $apms_enabled, ); + $features['pay_later_messaging'] = array( + 'enabled' => true, + ); + return $features; } ); From 24843bc26d48d34163edbf9cc2f1a8f324c8e4da Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 15:59:54 +0100 Subject: [PATCH 19/78] =?UTF-8?q?=F0=9F=92=84=20Remove=20the=20SEPA=20logo?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Onboarding/Components/AcdcOptionalPaymentMethods.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AcdcOptionalPaymentMethods.js index 6c6641735..dfbe501dc 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AcdcOptionalPaymentMethods.js @@ -145,7 +145,7 @@ const AcdcOptionalPaymentMethods = ( { 'woocommerce-paypal-payments' ) } imageBadge={ [ - 'icon-button-sepa.svg', + // 'icon-button-sepa.svg', 'icon-button-ideal.svg', 'icon-button-blik.svg', 'icon-button-bancontact.svg', @@ -211,7 +211,7 @@ const AcdcOptionalPaymentMethods = ( { 'woocommerce-paypal-payments' ) } imageBadge={ [ - 'icon-button-sepa.svg', + // 'icon-button-sepa.svg', 'icon-button-ideal.svg', 'icon-button-blik.svg', 'icon-button-bancontact.svg', From 09d0b48d16de45e46796383a480e4135f60b140c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 28 Jan 2025 16:01:49 +0100 Subject: [PATCH 20/78] Fix phpcs --- modules/ppcp-settings/src/SettingsModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-settings/src/SettingsModule.php b/modules/ppcp-settings/src/SettingsModule.php index 351e9b53f..b7c334425 100644 --- a/modules/ppcp-settings/src/SettingsModule.php +++ b/modules/ppcp-settings/src/SettingsModule.php @@ -185,7 +185,7 @@ class SettingsModule implements ServiceModule, ExecutableModule { 'wcPaymentsTabUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout' ), 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, 'isPayLaterConfiguratorAvailable' => $is_pay_later_configurator_available, - 'storeCountry' => $container->get( 'wcgateway.store-country' ), + 'storeCountry' => $container->get( 'wcgateway.store-country' ), ); if ( $is_pay_later_configurator_available ) { From 016fe5506f7c9a44a15cba4b38d771b376ed22db Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 28 Jan 2025 16:11:50 +0100 Subject: [PATCH 21/78] Fix phpcs --- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 034753afa..da8bf00cb 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -569,7 +569,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul ); $partners_endpoint = $c->get( 'api.endpoint.partners' ); - assert($partners_endpoint instanceof PartnersEndpoint ); + assert( $partners_endpoint instanceof PartnersEndpoint ); $seller_status = $partners_endpoint->seller_status(); $apms_enabled = false; From 06546e2bc1ee44818da233a1fe400f6a00a4f4ae Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 28 Jan 2025 16:19:50 +0100 Subject: [PATCH 22/78] Fix the Todos in the Overview tab --- .../Components/Screens/Settings/Tabs/TabOverview.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js index 63557516f..7dcd3f7fc 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js @@ -12,10 +12,10 @@ import { 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 } from '../../../../data/common'; import { getFeatures } from '../Components/Overview/features-config'; -import { todosData } from '../todo-items'; import { NOTIFICATION_ERROR, @@ -23,9 +23,14 @@ import { } from '../../../ReusableComponents/Icons'; const TabOverview = () => { + const { todos, isReady: areTodosReady } = useTodos(); + + // Don't render todos section until data is ready + const showTodos = areTodosReady && todos.length > 0; + return (
- { todosData.length > 0 && ( + { showTodos && ( { 'woocommerce-paypal-payments' ) } > - + ) } From 2f9ddc3aae71587b44a264371d04a207bb47d5ac Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 17:49:50 +0100 Subject: [PATCH 23/78] =?UTF-8?q?=F0=9F=92=84=20Remove=20extra=20gap=20abv?= =?UTF-8?q?oe=20the=20top-navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/css/components/screens/_fullscreen.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss b/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss index f29755306..bf00b6a68 100644 --- a/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss +++ b/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss @@ -12,6 +12,7 @@ body:has(.ppcp-r-container--onboarding) { .woocommerce-layout__header, .wrap.woocommerce form > h2, #mainform .subsubsub, + #mainform .subsubsub + br.clear, #screen-meta-links { display: none !important; visibility: hidden; From 1f3049e2eb3ee1534166df0f74f853122a6a9306 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:27:42 +0100 Subject: [PATCH 24/78] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Enabl?= =?UTF-8?q?e=20debug=20tools=20on=20every=20environment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-settings/resources/js/data/debug.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/debug.js b/modules/ppcp-settings/resources/js/data/debug.js index ed3233116..e9bd1295e 100644 --- a/modules/ppcp-settings/resources/js/data/debug.js +++ b/modules/ppcp-settings/resources/js/data/debug.js @@ -6,13 +6,18 @@ import { StylingStoreName, TodosStoreName, } from './index'; -import { setCompleted } from './onboarding/actions'; export const addDebugTools = ( context, modules ) => { - if ( ! context || ! context?.debug ) { + if ( ! context ) { return; } + /* + // TODO - enable this condition for version 3.0.1 + // In version 3.0.0 we want to have the debug tools available on every installation + if ( ! context.debug ) { return } + */ + // Dump the current state of all our Redux stores. context.dumpStore = async () => { /* eslint-disable no-console */ @@ -70,8 +75,12 @@ export const addDebugTools = ( context, modules ) => { // eslint-disable-next-line no-console console.log( `Reset store: ${ storeName }...` ); - store.reset(); - store.persist(); + try { + store.reset(); + store.persist(); + } catch ( error ) { + console.error( ' ... Reset failed, skipping this store' ); + } } ); }; From de71c31bc6a2a9aec8db0b7014cc53b0b5918d02 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:33:50 +0100 Subject: [PATCH 25/78] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Expos?= =?UTF-8?q?e=20debubg=20API=20in=20new=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-settings/resources/js/data/debug.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/debug.js b/modules/ppcp-settings/resources/js/data/debug.js index e9bd1295e..91adebb3c 100644 --- a/modules/ppcp-settings/resources/js/data/debug.js +++ b/modules/ppcp-settings/resources/js/data/debug.js @@ -13,13 +13,15 @@ export const addDebugTools = ( context, modules ) => { } /* - // TODO - enable this condition for version 3.0.1 - // In version 3.0.0 we want to have the debug tools available on every installation - if ( ! context.debug ) { return } - */ + // TODO - enable this condition for version 3.0.1 + // In version 3.0.0 we want to have the debug tools available on every installation + if ( ! context.debug ) { return } + */ + + const debugApi = ( window.ppcpDebugger = window.ppcpDebugger || {} ); // Dump the current state of all our Redux stores. - context.dumpStore = async () => { + debugApi.dumpStore = async () => { /* eslint-disable no-console */ if ( ! console?.groupCollapsed ) { console.error( 'console.groupCollapsed is not supported.' ); @@ -47,7 +49,7 @@ export const addDebugTools = ( context, modules ) => { }; // Reset all Redux stores to their initial state. - context.resetStore = () => { + debugApi.resetStore = () => { const stores = []; const { isConnected } = wp.data.select( CommonStoreName ).merchant(); @@ -85,7 +87,7 @@ export const addDebugTools = ( context, modules ) => { }; // Disconnect the merchant and display the onboarding wizard. - context.disconnect = () => { + debugApi.disconnect = () => { const common = wp.data.dispatch( CommonStoreName ); common.disconnectMerchant(); @@ -97,10 +99,13 @@ export const addDebugTools = ( context, modules ) => { }; // Enters or completes the onboarding wizard without changing anything else. - context.onboardingMode = ( state ) => { + debugApi.onboardingMode = ( state ) => { const onboarding = wp.data.dispatch( OnboardingStoreName ); onboarding.setCompleted( ! state ); onboarding.persist(); }; + + // Expose original debug API. + Object.assign( context, debugApi ); }; From c50a1c30abf1273cc4b59a6f7abd753c62856fd3 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:43:21 +0100 Subject: [PATCH 26/78] =?UTF-8?q?=F0=9F=9A=9A=20Rename=20custom=20icon=20c?= =?UTF-8?q?omponents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/ReusableComponents/Icons/GenericIcon.js | 0 .../Icons/{logo-paypal.js => LogoPayPal.js} | 4 ++-- .../Icons/{open-signup.js => OpenSignup.js} | 4 ++-- .../resources/js/Components/ReusableComponents/Icons/index.js | 4 ++-- .../Screens/Onboarding/Components/ConnectionButton.js | 4 ++-- .../Screens/Onboarding/Components/OnboardingHeader.js | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js rename modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/{logo-paypal.js => LogoPayPal.js} (98%) rename modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/{open-signup.js => OpenSignup.js} (97%) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js new file mode 100644 index 000000000..e69de29bb diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/logo-paypal.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/LogoPayPal.js similarity index 98% rename from modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/logo-paypal.js rename to modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/LogoPayPal.js index fec3f38a3..f1802d549 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/logo-paypal.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/LogoPayPal.js @@ -1,6 +1,6 @@ import { SVG, Path } from '@wordpress/primitives'; -const logoPayPal = ( +const LogoPayPal = ( ); -export default logoPayPal; +export default LogoPayPal; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/open-signup.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/OpenSignup.js similarity index 97% rename from modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/open-signup.js rename to modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/OpenSignup.js index 8e8daa32b..2f5bcc629 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/open-signup.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/OpenSignup.js @@ -1,9 +1,9 @@ import { SVG, Path } from '@wordpress/primitives'; -const openSignup = ( +const OpenSignup = ( ); -export default openSignup; +export default OpenSignup; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js index 005d1bc58..f79e4c987 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js @@ -1,5 +1,5 @@ -export { default as openSignup } from './open-signup'; -export { default as logoPayPal } from './logo-paypal'; +export { default as OpenSignup } from './OpenSignup'; +export { default as LogoPayPal } from './LogoPayPal'; export const NOTIFICATION_SUCCESS = '✔️'; export const NOTIFICATION_ERROR = '❌'; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js index 36ab8e197..29434f344 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -1,7 +1,7 @@ import { Button } from '@wordpress/components'; import { useEffect } from '@wordpress/element'; import classNames from 'classnames'; -import { openSignup } from '../../../ReusableComponents/Icons'; +import { OpenSignup } from '../../../ReusableComponents/Icons'; import { useHandleOnboardingButton } from '../../../../hooks/useHandleConnections'; import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper'; @@ -27,7 +27,7 @@ const ButtonOrPlaceholder = ( { const buttonProps = { className, variant, - icon: showIcon ? openSignup : null, + icon: showIcon ? OpenSignup : null, }; if ( href ) { diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js index 638af4704..47acf2895 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js @@ -1,13 +1,13 @@ import { Icon } from '@wordpress/components'; -import { logoPayPal } from '../../../ReusableComponents/Icons'; +import { LogoPayPal } from '../../../ReusableComponents/Icons'; const OnboardingHeader = ( props ) => { return (
- +
From 235ff308f5a48682ff164f1967469b2d4984da4c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:43:43 +0100 Subject: [PATCH 27/78] =?UTF-8?q?=E2=9C=A8=20New=20generic=20icon=20to=20r?= =?UTF-8?q?eplace=20data().getImage()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/Icons/GenericIcon.js | 15 +++++++++++++++ .../Components/ReusableComponents/Icons/index.js | 1 + 2 files changed, 16 insertions(+) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js index e69de29bb..1b5e0a184 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/GenericIcon.js @@ -0,0 +1,15 @@ +import React from 'react'; + +const GenericIcon = ( { imageName, className = '', alt = '' } ) => { + const pathToImages = global.ppcpSettings.assets.imagesUrl; + + return ( + { + ); +}; + +export default GenericIcon; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js index f79e4c987..5519a99d7 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Icons/index.js @@ -1,3 +1,4 @@ +export { default as PPIcon } from './GenericIcon'; export { default as OpenSignup } from './OpenSignup'; export { default as LogoPayPal } from './LogoPayPal'; From e1c57bd35e2232213dc1d988703a535d38044313 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:51:12 +0100 Subject: [PATCH 28/78] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20several=20React=20wa?= =?UTF-8?q?rnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/ReusableComponents/BadgeBox.js | 10 ++++++++-- .../ReusableComponents/Fields/OptionSelector.js | 4 +++- .../Screens/Onboarding/Components/OnboardingHeader.js | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js index 337e5626c..b044e7087 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js @@ -1,4 +1,4 @@ -import data from '../../utils/data'; +import { PPIcon } from './Icons'; const ImageBadge = ( { images } ) => { if ( ! images || ! images.length ) { @@ -8,7 +8,13 @@ const ImageBadge = ( { images } ) => { return ( - { images.map( ( badge ) => data().getImage( badge ) ) } + { images.map( ( badge, index ) => ( + + ) ) } ); diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields/OptionSelector.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields/OptionSelector.js index e4327273f..7faf68a0c 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields/OptionSelector.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Fields/OptionSelector.js @@ -65,7 +65,9 @@ const OptionItem = ( {
{ itemTitle } -

{ itemDescription }

+
+ { itemDescription } +
{ children && (
{ children }
) } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js index 47acf2895..349eadd80 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/OnboardingHeader.js @@ -7,7 +7,7 @@ const OnboardingHeader = ( props ) => {
- +
From 2380892b652a7a7c5c364c8daed1a4906c87f98e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 18:52:02 +0100 Subject: [PATCH 29/78] =?UTF-8?q?=F0=9F=90=9B=20Hide=20top-navigtation=20a?= =?UTF-8?q?ctions=20on=20welcome=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Onboarding/Components/Navigation.js | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index 52d496b33..9d4bcd0a1 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -21,9 +21,37 @@ const OnboardingNavigation = ( { stepDetails, onNext, onPrev } ) => { showProgressBar={ true } progressBarPercent={ percentage * 0.9 } > - + { showNext && ( ) } - + ); }; - -export default OnboardingNavigation; From 8bd0743183e9f0be9366b8e658a168e5f3312579 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 Jan 2025 19:03:30 +0100 Subject: [PATCH 30/78] =?UTF-8?q?=E2=9C=A8=20Prepare=20a=20sub-navigation?= =?UTF-8?q?=20slot=20for=20the=20top-nav?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reusable-components/_navigation.scss | 10 ++- .../ReusableComponents/TopNavigation.js | 61 +++++++++++-------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index a4695e256..fdcbcaa40 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -1,10 +1,12 @@ +$margin_bottom: 48px; + .ppcp-r-navigation-container { position: sticky; top: var(--wp-admin--admin-bar--height); z-index: 10; padding: 10px 48px; - margin: 0 -20px 48px -20px; + margin: 0 -20px #{$margin_bottom} -20px; box-shadow: 0 -1px 0 0 $color-gray-300 inset; background: var(--ppcp-color-app-bg); @@ -83,6 +85,12 @@ box-shadow: 0 -1px 0 0 $color-gray-300 inset, 0 8px 8px 0 rgba(85, 93, 102, .3); } + + .ppcp--top-sub-navigation { + position: relative; + margin: -#{$margin_bottom } -20px #{$margin_bottom}; + padding: 24px 20px 0; + } + @media screen and (max-width: 782px) { padding: 10px 12px; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js index bbffaaaa5..d456c4e4a 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js @@ -15,6 +15,7 @@ const TopNavigation = ( { onTitleClick = null, showProgressBar = false, progressBarPercent = 0, + subNavigation = null, } ) => { const { goToWooCommercePaymentsTab } = useNavigation(); const { isScrolled } = useIsScrolled(); @@ -40,35 +41,43 @@ const TopNavigation = ( { }, [] ); return ( -
-
- - - + + - - { children } - + + { children } + - { showProgressBar && ( - - ) } -
-
+ { showProgressBar && ( + + ) } +
+ + + { subNavigation && ( +
+ { subNavigation } +
+ ) } + ); }; From 68d4182b1d02c3420630f195f58686b5f4e23b67 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 15:28:48 +0100 Subject: [PATCH 31/78] =?UTF-8?q?=E2=9C=A8=20Create=20new=20data=20sanitat?= =?UTF-8?q?ion=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Service/DataSanitizer.php | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/src/Service/DataSanitizer.php b/modules/ppcp-settings/src/Service/DataSanitizer.php index 3baab9034..bd07baf84 100644 --- a/modules/ppcp-settings/src/Service/DataSanitizer.php +++ b/modules/ppcp-settings/src/Service/DataSanitizer.php @@ -74,17 +74,47 @@ class DataSanitizer { * @param string $default Default value. * @return string Sanitized string. */ - protected function sanitize_text( $value, string $default = '' ) : string { + public function sanitize_text( $value, string $default = '' ) : string { return sanitize_text_field( $value ?? $default ); } + /** + * Helper. Ensures the matches one of the provided enumerations. + * + * The comparison is case-insensitive, if no valid default is given, the + * first $valid_values entry is returned on failure. + * + * @param mixed $value Value to sanitize. + * @param string[] $valid_values List of allowed return values. Must use ASCII-only characters. + * @param string $default Default value. + * @return string Sanitized string. + */ + public function sanitize_enum( $value, array $valid_values, string $default = '' ) : string { + if ( empty( $valid_values ) ) { + return $default; + } + + $value = $this->sanitize_text( $value ); + $match = $this->find_enum_value( $value, $valid_values ); + if ( $match ) { + return $match; + } + + $default_match = $this->find_enum_value( $default, $valid_values ); + if ( $default_match ) { + return $default_match; + } + + return $valid_values[0]; + } + /** * Helper. Ensures the value is a boolean. * * @param mixed $value Value to sanitize. * @return bool Sanitized boolean. */ - protected function sanitize_bool( $value ) : bool { + public function sanitize_bool( $value ) : bool { return filter_var( $value, FILTER_VALIDATE_BOOLEAN ); } @@ -95,11 +125,30 @@ class DataSanitizer { * @param callable $sanitize_callback Callback to sanitize each item in the array. * @return array Array with sanitized items. */ - protected function sanitize_array( ?array $array, callable $sanitize_callback ) : array { + public function sanitize_array( ?array $array, callable $sanitize_callback ) : array { if ( ! is_array( $array ) ) { return array(); } return array_map( $sanitize_callback, $array ); } + + /** + * Helper function to find a case-insensitive match in the valid values array. + * + * @param string $value Value to find. + * @param string[] $valid_values List of allowed values. + * @return string|null Matching value if found, null otherwise. + */ + private function find_enum_value( string $value, array $valid_values ) : ?string { + foreach ( $valid_values as $valid_value ) { + // Compare both strings case-insensitive and binary safe. + // Note: This function is safe for ASCII but can fail for unicode-characters. + if ( 0 === strcasecmp( $value, $valid_value ) ) { + return $valid_value; + } + } + + return null; + } } From cdb2210c9fd4e1ade60a5e443ca734526cf86476 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 15:55:09 +0100 Subject: [PATCH 32/78] =?UTF-8?q?=E2=9C=A8=20Add=20new=20sanitizer=20to=20?= =?UTF-8?q?enforce=20int=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/src/Service/DataSanitizer.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/ppcp-settings/src/Service/DataSanitizer.php b/modules/ppcp-settings/src/Service/DataSanitizer.php index bd07baf84..75b778789 100644 --- a/modules/ppcp-settings/src/Service/DataSanitizer.php +++ b/modules/ppcp-settings/src/Service/DataSanitizer.php @@ -118,6 +118,19 @@ class DataSanitizer { return filter_var( $value, FILTER_VALIDATE_BOOLEAN ); } + /** + * Helper. Ensures the value is an integer. + * + * Attention: When passing a non-integer value (like 12.5 or "12a") the + * function will return 0. + * + * @param mixed $value Value to sanitize. + * @return int Sanitized integer. + */ + public function sanitize_int( $value ) : int { + return (int) filter_var( $value, FILTER_VALIDATE_INT ); + } + /** * Helper. Ensures the value is an array and all items are sanitized. * From fc2d91b769fe6b79f441bd0c028664d5b9f9f000 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 15:59:42 +0100 Subject: [PATCH 33/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Small=20changes=20in?= =?UTF-8?q?=20REST=20base=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Endpoint/RestEndpoint.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-settings/src/Endpoint/RestEndpoint.php b/modules/ppcp-settings/src/Endpoint/RestEndpoint.php index 7fba1529c..e9e9948ab 100644 --- a/modules/ppcp-settings/src/Endpoint/RestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/RestEndpoint.php @@ -99,6 +99,7 @@ abstract class RestEndpoint extends WC_REST_Controller { $source_key = $details['js_name'] ?? ''; $sanitation_cb = $details['sanitize'] ?? null; + // Skip missing values, skip "null" values, skip "read_only" values. if ( ! $source_key || ! isset( $params[ $source_key ] ) @@ -111,8 +112,11 @@ abstract class RestEndpoint extends WC_REST_Controller { if ( null === $sanitation_cb ) { $sanitized[ $key ] = $value; - } elseif ( is_string( $sanitation_cb ) && method_exists( $this, $sanitation_cb ) ) { - $sanitized[ $key ] = $this->{$sanitation_cb}( $value, $key ); + continue; + } + + if ( is_string( $sanitation_cb ) && method_exists( $this, $sanitation_cb ) ) { + $sanitized[ $key ] = $this->{$sanitation_cb}( $value ); } elseif ( is_callable( $sanitation_cb ) ) { $sanitized[ $key ] = $sanitation_cb( $value, $key ); } @@ -155,10 +159,11 @@ abstract class RestEndpoint extends WC_REST_Controller { * * @param mixed $value The value to sanitize. * - * @return bool|null The boolean value, or null if not set. + * @return bool The boolean value. + * @todo Switch to the DataSanitizer class. */ - public function to_boolean( $value ) : ?bool { - return $value !== null ? (bool) $value : null; + public function to_boolean( $value ) : bool { + return (bool) $value; } /** @@ -166,13 +171,10 @@ abstract class RestEndpoint extends WC_REST_Controller { * * @param mixed $value The value to sanitize. * - * @return int|float|null The numeric value, or null if not set. + * @return int The numeric value. + * @todo Switch to the DataSanitizer class. */ - public function to_number( $value ) { - if ( $value !== null ) { - $value = is_numeric( $value ) ? $value + 0 : null; - } - - return $value; + public function to_number( $value ) : int { + return (int) $value; } } From cd9ff71cd08d5f21de6b6d7aa47712243a2e01a3 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 16:05:14 +0100 Subject: [PATCH 34/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20the=20setti?= =?UTF-8?q?ngs=20data=20model=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/services.php | 8 +- .../ppcp-settings/src/Data/SettingsModel.php | 342 +++++++++++++++--- 2 files changed, 295 insertions(+), 55 deletions(-) diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index cf397b8bf..d99107890 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -81,6 +81,11 @@ return array( 'settings.data.payment' => static function ( ContainerInterface $container ) : PaymentSettings { return new PaymentSettings(); }, + 'settings.data.settings' => static function ( ContainerInterface $container ) : SettingsModel { + return new SettingsModel( + $container->get( 'settings.service.sanitizer' ) + ); + }, 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); }, @@ -230,9 +235,6 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ), ); }, - 'settings.data.settings' => static function() : SettingsModel { - return new SettingsModel(); - }, 'settings.rest.todos' => static function ( ContainerInterface $container ) : TodosRestEndpoint { return new TodosRestEndpoint( $container->get( 'settings.data.todos' ), diff --git a/modules/ppcp-settings/src/Data/SettingsModel.php b/modules/ppcp-settings/src/Data/SettingsModel.php index 9bb2b94bb..915980bd7 100644 --- a/modules/ppcp-settings/src/Data/SettingsModel.php +++ b/modules/ppcp-settings/src/Data/SettingsModel.php @@ -9,81 +9,319 @@ declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Settings\Data; -use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use RuntimeException; +use WooCommerce\PayPalCommerce\Settings\Service\DataSanitizer; /** * Class SettingsModel * * Handles the storage and retrieval of PayPal Commerce settings in WordPress options table. - * Provides methods to get and update settings with proper type casting and default values. */ -class SettingsModel { +class SettingsModel extends AbstractDataModel { + /** - * WordPress option name for storing the settings. + * Option key where settings are stored. * * @var string */ - const OPTION_NAME = 'ppcp_settings'; + protected const OPTION_KEY = 'woocommerce-ppcp-data-settings'; /** - * Retrieves the formatted settings from WordPress options. + * Valid options for subtotal adjustment. * - * Loads the raw settings from wp_options table and formats them into a - * standardized array structure with proper type casting. - * - * @return array The formatted settings array. + * @var array */ - public function get() : array { - $settings = get_option( self::OPTION_NAME, array() ); + public const SUBTOTAL_ADJUSTMENT_OPTIONS = array( 'correction', 'no_details', 'skip_details' ); - $formatted = array( - 'invoicePrefix' => $settings['invoice_prefix'] ?? '', - 'authorizeOnly' => (bool) ( $settings['authorize_only'] ?? false ), - 'captureVirtualOnlyOrders' => (bool) ( $settings['capture_virtual_only_orders'] ?? false ), - 'savePaypalAndVenmo' => (bool) ( $settings['save_paypal_and_venmo'] ?? false ), - 'saveCardDetails' => (bool) ( $settings['save_credit_card_and_debit_card'] ?? false ), - 'payNowExperience' => (bool) ( $settings['pay_now_experience'] ?? false ), - 'logging' => (bool) ( $settings['logging'] ?? false ), - 'subtotalAdjustment' => $settings['subtotal_mismatch_fallback'] ?? null, - 'brandName' => $settings['brand_name'] ?? '', - 'softDescriptor' => $settings['soft_descriptor'] ?? '', - 'landingPage' => $settings['paypal_landing_page'] ?? null, - 'buttonLanguage' => $settings['button_language'] ?? '', - ); + /** + * Valid options for landing page. + * + * @var array + */ + public const LANDING_PAGE_OPTIONS = array( 'any', 'login', 'guest_checkout' ); - return $formatted; + /** + * Data sanitizer service. + * + * @var DataSanitizer + */ + protected DataSanitizer $sanitizer; + + /** + * Constructor. + * + * @param DataSanitizer $sanitizer Data sanitizer service. + * @throws RuntimeException If the OPTION_KEY is not defined in the child class. + */ + public function __construct( DataSanitizer $sanitizer ) { + $this->sanitizer = $sanitizer; + parent::__construct(); } /** - * Updates the settings in WordPress options. + * Get default values for the model. * - * Converts the provided data array from camelCase to snake_case format - * and saves it to wp_options table. Throws an exception if update fails. - * - * @param array $data The settings data to update. - * @return void - * @throws RuntimeException When the settings update fails. + * @return array */ - public function update( array $data ) : void { - $settings = array( - 'invoice_prefix' => $data['invoicePrefix'] ?? '', - 'authorize_only' => (bool) ( $data['authorizeOnly'] ?? false ), - 'capture_virtual_only_orders' => (bool) ( $data['captureVirtualOnlyOrders'] ?? false ), - 'save_paypal_and_venmo' => (bool) ( $data['savePaypalAndVenmo'] ?? false ), - 'save_credit_card_and_debit_card' => (bool) ( $data['saveCardDetails'] ?? false ), - 'pay_now_experience' => (bool) ( $data['payNowExperience'] ?? false ), - 'logging' => (bool) ( $data['logging'] ?? false ), - 'subtotal_mismatch_fallback' => $data['subtotalAdjustment'] ?? null, - 'brand_name' => $data['brandName'] ?? '', - 'soft_descriptor' => $data['softDescriptor'] ?? '', - 'paypal_landing_page' => $data['landingPage'] ?? null, - 'button_language' => $data['buttonLanguage'] ?? '', + protected function get_defaults() : array { + return array( + // Free-form string values. + 'invoice_prefix' => '', + 'brand_name' => '', + 'soft_descriptor' => '', + + // Enum-type string values. + 'subtotal_adjustment' => 'skip_details', // Options: [correction|no_details]. + 'landing_page' => 'any', // Options: [any|login|guest_checkout]. + 'button_language' => '', // empty or a 2-letter language code. + + // Boolean flags. + 'authorize_only' => false, + 'capture_virtual_orders' => false, + 'save_paypal_and_venmo' => false, + 'save_card_details' => false, + 'enable_pay_now' => false, + 'enable_logging' => false, + + // Array of string values. + 'disabled_cards' => array(), ); + } - $result = update_option( self::OPTION_NAME, $settings ); + /** + * Gets the invoice prefix. + * + * @return string The invoice prefix. + */ + public function get_invoice_prefix() : string { + return $this->data['invoice_prefix']; + } - if ( ! $result ) { - throw new RuntimeException( 'Failed to update settings' ); - } + /** + * Sets the invoice prefix. + * + * @param string $prefix The invoice prefix to set. + */ + public function set_invoice_prefix( string $prefix ) : void { + $this->data['invoice_prefix'] = $this->sanitizer->sanitize_text( $prefix ); + } + + /** + * Gets the brand name. + * + * @return string The brand name. + */ + public function get_brand_name() : string { + return $this->data['brand_name']; + } + + /** + * Sets the brand name. + * + * @param string $name The brand name to set. + */ + public function set_brand_name( string $name ) : void { + $this->data['brand_name'] = $this->sanitizer->sanitize_text( $name ); + } + + /** + * Gets the soft descriptor. + * + * @return string The soft descriptor. + */ + public function get_soft_descriptor() : string { + return $this->data['soft_descriptor']; + } + + /** + * Sets the soft descriptor. + * + * @param string $descriptor The soft descriptor to set. + */ + public function set_soft_descriptor( string $descriptor ) : void { + $this->data['soft_descriptor'] = $this->sanitizer->sanitize_text( $descriptor ); + } + + /** + * Gets the subtotal adjustment setting. + * + * @return string The subtotal adjustment setting. + */ + public function get_subtotal_adjustment() : string { + return $this->data['subtotal_adjustment']; + } + + /** + * Sets the subtotal adjustment setting. + * + * @param string $adjustment The subtotal adjustment to set. + */ + public function set_subtotal_adjustment( string $adjustment ) : void { + $this->data['subtotal_adjustment'] = $this->sanitizer->sanitize_enum( $adjustment, self::SUBTOTAL_ADJUSTMENT_OPTIONS ); + } + + /** + * Gets the landing page setting. + * + * @return string The landing page setting. + */ + public function get_landing_page() : string { + return $this->data['landing_page']; + } + + /** + * Sets the landing page setting. + * + * @param string $page The landing page to set. + */ + public function set_landing_page( string $page ) : void { + $this->data['landing_page'] = $this->sanitizer->sanitize_enum( $page, self::LANDING_PAGE_OPTIONS ); + } + + /** + * Gets the button language setting. + * + * @return string The button language. + */ + public function get_button_language() : string { + return $this->data['button_language']; + } + + /** + * Sets the button language. + * + * @param string $language The button language to set. + */ + public function set_button_language( string $language ) : void { + $this->data['button_language'] = $this->sanitizer->sanitize_text( $language ); + } + + /** + * Gets the authorize only setting. + * + * @return bool True if authorize only is enabled, false otherwise. + */ + public function get_authorize_only() : bool { + return $this->data['authorize_only']; + } + + /** + * Sets the authorize only setting. + * + * @param bool $authorize Whether to enable authorize only. + */ + public function set_authorize_only( bool $authorize ) : void { + $this->data['authorize_only'] = $this->sanitizer->sanitize_bool( $authorize ); + } + + /** + * Gets the capture virtual orders setting. + * + * @return bool True if capturing virtual orders is enabled, false otherwise. + */ + public function get_capture_virtual_orders() : bool { + return $this->data['capture_virtual_orders']; + } + + /** + * Sets the capture virtual orders setting. + * + * @param bool $capture Whether to capture virtual orders. + */ + public function set_capture_virtual_orders( bool $capture ) : void { + $this->data['capture_virtual_orders'] = $this->sanitizer->sanitize_bool( $capture ); + } + + /** + * Gets the save PayPal and Venmo setting. + * + * @return bool True if saving PayPal and Venmo is enabled, false otherwise. + */ + public function get_save_paypal_and_venmo() : bool { + return $this->data['save_paypal_and_venmo']; + } + + /** + * Sets the save PayPal and Venmo setting. + * + * @param bool $save Whether to save PayPal and Venmo. + */ + public function set_save_paypal_and_venmo( bool $save ) : void { + $this->data['save_paypal_and_venmo'] = $this->sanitizer->sanitize_bool( $save ); + } + + /** + * Gets the save card details setting. + * + * @return bool True if saving card details is enabled, false otherwise. + */ + public function get_save_card_details() : bool { + return $this->data['save_card_details']; + } + + /** + * Sets the save card details setting. + * + * @param bool $save Whether to save card details. + */ + public function set_save_card_details( bool $save ) : void { + $this->data['save_card_details'] = $this->sanitizer->sanitize_bool( $save ); + } + + /** + * Gets the enable Pay Now setting. + * + * @return bool True if Pay Now is enabled, false otherwise. + */ + public function get_enable_pay_now() : bool { + return $this->data['enable_pay_now']; + } + + /** + * Sets the enable Pay Now setting. + * + * @param bool $enable Whether to enable Pay Now. + */ + public function set_enable_pay_now( bool $enable ) : void { + $this->data['enable_pay_now'] = $this->sanitizer->sanitize_bool( $enable ); + } + + /** + * Gets the enable logging setting. + * + * @return bool True if logging is enabled, false otherwise. + */ + public function get_enable_logging() : bool { + return $this->data['enable_logging']; + } + + /** + * Sets the enable logging setting. + * + * @param bool $enable Whether to enable logging. + */ + public function set_enable_logging( bool $enable ) : void { + $this->data['enable_logging'] = $this->sanitizer->sanitize_bool( $enable ); + } + + /** + * Gets the disabled cards. + * + * @return array The array of disabled cards. + */ + public function get_disabled_cards() : array { + return $this->data['disabled_cards']; + } + + /** + * Sets the disabled cards. + * + * @param array $cards The array of cards to disable. + */ + public function set_disabled_cards( array $cards ) : void { + $this->data['disabled_cards'] = array_map( + array( $this->sanitizer, 'sanitize_text' ), + $cards + ); } } From 16d8598ab634d9a0f05350a105070acb3419fd3a Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 16:07:24 +0100 Subject: [PATCH 35/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Adapt=20the=20REST?= =?UTF-8?q?=20endpoint=20to=20the=20new=20data=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/services.php | 11 +- .../src/Endpoint/SettingsRestEndpoint.php | 163 +++++++++--------- 2 files changed, 85 insertions(+), 89 deletions(-) diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index d99107890..1326f4a4a 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -125,6 +125,11 @@ return array( $container->get( 'webhook.status.simulation' ) ); }, + 'settings.rest.settings' => static function ( ContainerInterface $container ) : SettingsRestEndpoint { + return new SettingsRestEndpoint( + $container->get( 'settings.data.settings' ) + ); + }, 'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array { return array( 'AR', @@ -229,12 +234,6 @@ return array( $container->get( 'api.merchant_id' ) !== '' ); }, - 'settings.rest.settings' => static function( ContainerInterface $container ): SettingsRestEndpoint { - return new SettingsRestEndpoint( - $container->get( 'settings.data.settings' ), - $container->get( 'woocommerce.logger.woocommerce' ), - ); - }, 'settings.rest.todos' => static function ( ContainerInterface $container ) : TodosRestEndpoint { return new TodosRestEndpoint( $container->get( 'settings.data.todos' ), diff --git a/modules/ppcp-settings/src/Endpoint/SettingsRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/SettingsRestEndpoint.php index 5b82c57d9..9f200ed99 100644 --- a/modules/ppcp-settings/src/Endpoint/SettingsRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/SettingsRestEndpoint.php @@ -8,14 +8,14 @@ * @package WooCommerce\PayPalCommerce\Settings\Endpoint */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Settings\Endpoint; -use WooCommerce\PayPalCommerce\Settings\Data\SettingsModel; -use Psr\Log\LoggerInterface; use WP_REST_Request; use WP_REST_Response; +use WP_REST_Server; +use WooCommerce\PayPalCommerce\Settings\Data\SettingsModel; /** * Class SettingsRestEndpoint @@ -25,18 +25,11 @@ use WP_REST_Response; class SettingsRestEndpoint extends RestEndpoint { /** - * The REST API endpoint base. + * The base path for this REST controller. * * @var string */ - private const ENDPOINT = 'settings'; - - /** - * The REST API namespace. - * - * @var string - */ - protected $namespace = 'wc/v3/wc_paypal'; + protected $rest_base = 'settings'; /** * The settings model instance. @@ -46,42 +39,87 @@ class SettingsRestEndpoint extends RestEndpoint { private SettingsModel $settings; /** - * The logger instance. + * Field mapping for request to profile transformation. * - * @var LoggerInterface + * @var array */ - private LoggerInterface $logger; + private array $field_map = array( + 'invoice_prefix' => array( + 'js_name' => 'invoicePrefix', + ), + 'brand_name' => array( + 'js_name' => 'brandName', + ), + 'soft_descriptor' => array( + 'js_name' => 'softDescriptor', + ), + 'subtotal_adjustment' => array( + 'js_name' => 'subtotalAdjustment', + ), + 'landing_page' => array( + 'js_name' => 'landingPage', + ), + 'button_language' => array( + 'js_name' => 'buttonLanguage', + ), + 'authorize_only' => array( + 'js_name' => 'authorizeOnly', + 'sanitize' => 'to_boolean', + ), + 'capture_virtual_orders' => array( + 'js_name' => 'captureVirtualOrders', + 'sanitize' => 'to_boolean', + ), + 'save_paypal_and_venmo' => array( + 'js_name' => 'savePaypalAndVenmo', + 'sanitize' => 'to_boolean', + ), + 'save_card_details' => array( + 'js_name' => 'saveCardDetails', + 'sanitize' => 'to_boolean', + ), + 'enable_pay_now' => array( + 'js_name' => 'enablePayNow', + 'sanitize' => 'to_boolean', + ), + 'enable_logging' => array( + 'js_name' => 'enableLogging', + 'sanitize' => 'to_boolean', + ), + 'disabled_cards' => array( + 'js_name' => 'disabledCards', + ), + ); /** * SettingsRestEndpoint constructor. * - * @param SettingsModel $settings The settings model instance. - * @param LoggerInterface $logger The logger instance. + * @param SettingsModel $settings The settings model instance. */ - public function __construct( - SettingsModel $settings, - LoggerInterface $logger - ) { + public function __construct( SettingsModel $settings ) { $this->settings = $settings; - $this->logger = $logger; } /** * Registers the REST API routes for settings management. */ - public function register_routes(): void { + public function register_routes() : void { + /** + * GET wc/v3/wc_paypal/settings + * POST wc/v3/wc_paypal/settings + */ register_rest_route( $this->namespace, - '/' . self::ENDPOINT, + '/' . $this->rest_base, array( array( - 'methods' => 'GET', - 'callback' => array( $this, 'get_settings' ), + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_details' ), 'permission_callback' => array( $this, 'check_permission' ), ), array( - 'methods' => 'POST', - 'callback' => array( $this, 'update_settings' ), + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_details' ), 'permission_callback' => array( $this, 'check_permission' ), ), ) @@ -91,37 +129,15 @@ class SettingsRestEndpoint extends RestEndpoint { /** * Retrieves the current settings. * - * @param WP_REST_Request $request The request instance. * @return WP_REST_Response The response containing settings data or error details. - * @throws \Exception When encoding settings data fails. */ - public function get_settings( WP_REST_Request $request ): WP_REST_Response { - try { - // Get settings data. - $data = $this->settings->get(); + public function get_details() : WP_REST_Response { + $js_data = $this->sanitize_for_javascript( + $this->settings->to_array(), + $this->field_map + ); - // Ensure the data is JSON-encodable. - $encoded = wp_json_encode( $data ); - if ( $encoded === false ) { - throw new \Exception( 'Failed to encode settings data: ' . json_last_error_msg() ); - } - - // Create response with pre-verified JSON data. - $response_data = array( - 'success' => true, - 'data' => json_decode( $encoded, true ), - ); - - return new WP_REST_Response( $response_data, 200 ); - } catch ( \Exception $error ) { - return new WP_REST_Response( - array( - 'success' => false, - 'message' => $error->getMessage(), - ), - 500 - ); - } + return $this->return_success( $js_data ); } /** @@ -129,35 +145,16 @@ class SettingsRestEndpoint extends RestEndpoint { * * @param WP_REST_Request $request The request instance containing new settings. * @return WP_REST_Response The response containing updated settings or error details. - * @throws \Exception When encoding updated settings fails. */ - public function update_settings( WP_REST_Request $request ): WP_REST_Response { - try { - $data = $request->get_json_params(); - $this->settings->update( $data ); - $updated_data = $this->settings->get(); + public function update_details( WP_REST_Request $request ) : WP_REST_Response { + $wp_data = $this->sanitize_for_wordpress( + $request->get_params(), + $this->field_map + ); - // Verify JSON encoding. - $encoded = wp_json_encode( $updated_data ); - if ( $encoded === false ) { - throw new \Exception( 'Failed to encode updated settings: ' . json_last_error_msg() ); - } + $this->settings->from_array( $wp_data ); + $this->settings->save(); - return new WP_REST_Response( - array( - 'success' => true, - 'data' => json_decode( $encoded, true ), - ), - 200 - ); - } catch ( \Exception $error ) { - return new WP_REST_Response( - array( - 'success' => false, - 'message' => $error->getMessage(), - ), - 500 - ); - } + return $this->get_details(); } } From 4a6dee7e3640aba01c18dd24514c3f7913f3c780 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 16:09:25 +0100 Subject: [PATCH 36/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Adjust=20Redux=20sto?= =?UTF-8?q?re=20to=20changes=20in=20PHP=20data=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/settings/hooks.js | 25 +++++++++++-------- .../resources/js/data/settings/reducer.js | 23 +++++++++++------ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/settings/hooks.js b/modules/ppcp-settings/resources/js/data/settings/hooks.js index 37965feee..f161ee48e 100644 --- a/modules/ppcp-settings/resources/js/data/settings/hooks.js +++ b/modules/ppcp-settings/resources/js/data/settings/hooks.js @@ -21,25 +21,28 @@ const useHooks = () => { // Persistent accessors. const [ invoicePrefix, setInvoicePrefix ] = usePersistent( 'invoicePrefix' ); + const [ brandName, setBrandName ] = usePersistent( 'brandName' ); + const [ softDescriptor, setSoftDescriptor ] = + usePersistent( 'softDescriptor' ); + + const [ subtotalAdjustment, setSubtotalAdjustment ] = + usePersistent( 'subtotalAdjustment' ); + const [ landingPage, setLandingPage ] = usePersistent( 'landingPage' ); + const [ buttonLanguage, setButtonLanguage ] = + usePersistent( 'buttonLanguage' ); + const [ authorizeOnly, setAuthorizeOnly ] = usePersistent( 'authorizeOnly' ); const [ captureVirtualOnlyOrders, setCaptureVirtualOnlyOrders ] = - usePersistent( 'captureVirtualOnlyOrders' ); + usePersistent( 'captureVirtualOrders' ); const [ savePaypalAndVenmo, setSavePaypalAndVenmo ] = usePersistent( 'savePaypalAndVenmo' ); const [ saveCardDetails, setSaveCardDetails ] = usePersistent( 'saveCardDetails' ); const [ payNowExperience, setPayNowExperience ] = - usePersistent( 'payNowExperience' ); - const [ logging, setLogging ] = usePersistent( 'logging' ); - const [ subtotalAdjustment, setSubtotalAdjustment ] = - usePersistent( 'subtotalAdjustment' ); - const [ brandName, setBrandName ] = usePersistent( 'brandName' ); - const [ softDescriptor, setSoftDescriptor ] = - usePersistent( 'softDescriptor' ); - const [ landingPage, setLandingPage ] = usePersistent( 'landingPage' ); - const [ buttonLanguage, setButtonLanguage ] = - usePersistent( 'buttonLanguage' ); + usePersistent( 'enablePayNow' ); + const [ logging, setLogging ] = usePersistent( 'enableLogging' ); + const [ disabledCards, setDisabledCards ] = usePersistent( 'disabledCards' ); diff --git a/modules/ppcp-settings/resources/js/data/settings/reducer.js b/modules/ppcp-settings/resources/js/data/settings/reducer.js index f48348f14..875129bd8 100644 --- a/modules/ppcp-settings/resources/js/data/settings/reducer.js +++ b/modules/ppcp-settings/resources/js/data/settings/reducer.js @@ -25,18 +25,25 @@ const defaultTransient = Object.freeze( { * These represent the core PayPal payment settings configuration. */ const defaultPersistent = Object.freeze( { + // String values. invoicePrefix: '', // Prefix for PayPal invoice IDs - authorizeOnly: false, // Whether to only authorize payments initially - captureVirtualOnlyOrders: false, // Auto-capture virtual-only orders - savePaypalAndVenmo: false, // Enable PayPal & Venmo vaulting - saveCardDetails: false, // Enable card vaulting - payNowExperience: false, // Enable Pay Now experience - logging: false, // Enable debug logging - subtotalAdjustment: 'skip_details', // Handling for subtotal mismatches brandName: '', // Merchant brand name for PayPal softDescriptor: '', // Payment descriptor on statements - landingPage: 'any', // PayPal checkout landing page + + // Limited value strings. + subtotalAdjustment: 'no_details', // [correction|no_details] Handling for subtotal mismatches + landingPage: 'any', // [any|login|guest_checkout] PayPal checkout landing page buttonLanguage: '', // Language for PayPal buttons + + // Boolean flags. + authorizeOnly: false, // Whether to only authorize payments initially + captureVirtualOrders: false, // Auto-capture virtual-only orders + savePaypalAndVenmo: false, // Enable PayPal & Venmo vaulting + saveCardDetails: false, // Enable card vaulting + enablePayNow: false, // Enable Pay Now experience + enableLogging: false, // Enable debug logging + + // String arrays. disabledCards: [], // Disabled credit card types } ); From 01e7592deca6607b1df0e2c455c0e1d48be33dda Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 16:13:07 +0100 Subject: [PATCH 37/78] =?UTF-8?q?=F0=9F=90=9B=20Remove=20invalid=20enum=20?= =?UTF-8?q?option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/src/Data/SettingsModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-settings/src/Data/SettingsModel.php b/modules/ppcp-settings/src/Data/SettingsModel.php index 915980bd7..0334d4aeb 100644 --- a/modules/ppcp-settings/src/Data/SettingsModel.php +++ b/modules/ppcp-settings/src/Data/SettingsModel.php @@ -31,7 +31,7 @@ class SettingsModel extends AbstractDataModel { * * @var array */ - public const SUBTOTAL_ADJUSTMENT_OPTIONS = array( 'correction', 'no_details', 'skip_details' ); + public const SUBTOTAL_ADJUSTMENT_OPTIONS = array( 'no_details', 'correction' ); /** * Valid options for landing page. From 83912f2b93e2b0aea8f6657631ff35caadef9c60 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 Jan 2025 16:41:50 +0100 Subject: [PATCH 38/78] Add rest endpoint boilerplate --- modules/ppcp-settings/services.php | 4 + .../Endpoint/PayLaterMessagingEndpoint.php | 88 +++++++++++++++++++ modules/ppcp-settings/src/SettingsModule.php | 1 + 3 files changed, 93 insertions(+) create mode 100644 modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index cf397b8bf..cc993f0d6 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\Settings\Endpoint\AuthenticationRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint; +use WooCommerce\PayPalCommerce\Settings\Endpoint\PayLaterMessagingEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\PaymentRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\RefreshFeatureStatusEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\WebhookSettingsEndpoint; @@ -120,6 +121,9 @@ return array( $container->get( 'webhook.status.simulation' ) ); }, + 'settings.rest.pay_later_messaging' => static function ( ContainerInterface $container ) : PayLaterMessagingEndpoint { + return new PayLaterMessagingEndpoint(); + }, 'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array { return array( 'AR', diff --git a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php new file mode 100644 index 000000000..5e5643261 --- /dev/null +++ b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php @@ -0,0 +1,88 @@ +namespace, + '/' . $this->rest_base, + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_details' ), + 'permission_callback' => array( $this, 'check_permission' ), + ) + ); + + /** + * POST wc/v3/wc_paypal/pay_later_messaging + * { + * [gateway_id]: { + * enabled + * title + * description + * } + * } + */ + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_details' ), + 'permission_callback' => array( $this, 'check_permission' ), + ) + ); + } + + /** + * Returns all payment methods details. + * + * @return WP_REST_Response The current payment methods details. + */ + public function get_details() : WP_REST_Response { + return $this->return_success( array() ); + } + + /** + * Updates payment methods details based on the request. + * + * @param WP_REST_Request $request Full data about the request. + * + * @return WP_REST_Response The updated payment methods details. + */ + public function update_details( WP_REST_Request $request ) : WP_REST_Response { + return $this->get_details(); + } + +} diff --git a/modules/ppcp-settings/src/SettingsModule.php b/modules/ppcp-settings/src/SettingsModule.php index b7c334425..c6fea6621 100644 --- a/modules/ppcp-settings/src/SettingsModule.php +++ b/modules/ppcp-settings/src/SettingsModule.php @@ -238,6 +238,7 @@ class SettingsModule implements ServiceModule, ExecutableModule { 'settings' => $container->get( 'settings.rest.settings' ), 'styling' => $container->get( 'settings.rest.styling' ), 'todos' => $container->get( 'settings.rest.todos' ), + 'pay_later_messaging' => $container->get( 'settings.rest.pay_later_messaging' ), ); foreach ( $endpoints as $endpoint ) { From 40edcdfc3d57db84a72dcb62420746456d91dbac Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 Jan 2025 16:51:12 +0100 Subject: [PATCH 39/78] Add get response details --- modules/ppcp-settings/services.php | 4 ++- .../Endpoint/PayLaterMessagingEndpoint.php | 27 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index 97f561a9e..0016a0096 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -127,7 +127,9 @@ return array( ); }, 'settings.rest.pay_later_messaging' => static function ( ContainerInterface $container ) : PayLaterMessagingEndpoint { - return new PayLaterMessagingEndpoint(); + return new PayLaterMessagingEndpoint( + $container->get( 'wcgateway.settings' ) + ); }, 'settings.rest.settings' => static function ( ContainerInterface $container ) : SettingsRestEndpoint { return new SettingsRestEndpoint( diff --git a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php index 5e5643261..5dfbe7613 100644 --- a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php @@ -9,6 +9,8 @@ declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Settings\Endpoint; +use WooCommerce\PayPalCommerce\PayLaterConfigurator\Factory\ConfigFactory; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WP_REST_Request; use WP_REST_Response; use WP_REST_Server; @@ -27,6 +29,22 @@ class PayLaterMessagingEndpoint extends RestEndpoint { */ protected $rest_base = 'pay_later_messaging'; + /** + * The settings. + * + * @var Settings + */ + protected $settings; + + /** + * PayLaterMessagingEndpoint constructor. + * + * @param Settings $settings The settings. + */ + public function __construct( Settings $settings ) { + $this->settings = $settings; + } + /** * Configure REST API routes. */ @@ -46,13 +64,6 @@ class PayLaterMessagingEndpoint extends RestEndpoint { /** * POST wc/v3/wc_paypal/pay_later_messaging - * { - * [gateway_id]: { - * enabled - * title - * description - * } - * } */ register_rest_route( $this->namespace, @@ -71,7 +82,7 @@ class PayLaterMessagingEndpoint extends RestEndpoint { * @return WP_REST_Response The current payment methods details. */ public function get_details() : WP_REST_Response { - return $this->return_success( array() ); + return $this->return_success( ( new ConfigFactory() )->from_settings( $this->settings ) ); } /** From ffa869fce4d874e3cdd2ffe6c109f3b1b647b878 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 Jan 2025 17:10:27 +0100 Subject: [PATCH 40/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Change=20TabNavigati?= =?UTF-8?q?on=20to=20generic=20=E2=80=9CsubNavigation=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/TopNavigation.js | 72 ++++++++----------- .../Screens/Settings/Components/Navigation.js | 11 ++- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js index 635a2969f..26613f574 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js @@ -6,7 +6,6 @@ import classNames from 'classnames'; import useIsScrolled from '../../hooks/useIsScrolled'; import { useNavigation } from '../../hooks/useNavigation'; import BusyStateWrapper from './BusyStateWrapper'; -import TabNavigation from './TabNavigation'; const TopNavigation = ( { title, @@ -17,9 +16,6 @@ const TopNavigation = ( { showProgressBar = false, progressBarPercent = 0, subNavigation = null, - tabs = [], - activePanel = '', - setActivePanel = () => {}, } ) => { const { goToWooCommercePaymentsTab } = useNavigation(); const { isScrolled } = useIsScrolled(); @@ -48,50 +44,38 @@ const TopNavigation = ( { <> + { showProgressBar && ( + + ) } - { subNavigation && ( -
- { subNavigation } -
- ) } + { subNavigation && ( +
+ { subNavigation } +
+ ) } + ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js index 7fdba20d1..0bcc96288 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js @@ -3,6 +3,7 @@ import { __ } from '@wordpress/i18n'; import TopNavigation from '../../../ReusableComponents/TopNavigation'; import { useSaveSettings } from '../../../../hooks/useSaveSettings'; +import TabNavigation from '../../../ReusableComponents/TabNavigation'; const SettingsNavigation = ( { canSave = true, @@ -18,9 +19,13 @@ const SettingsNavigation = ( { + } > { canSave && (
- { showProgressBar && ( - - ) } { subNavigation && (
{ subNavigation }
) } + + { showProgressBar && ( + + ) } ); From 0e89cde688d0357cf8f5f864bc815e95868942f3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 Jan 2025 17:57:50 +0100 Subject: [PATCH 45/78] Add data store boilerplate --- .../ppcp-settings/resources/js/data/index.js | 13 ++- .../js/data/pay-later-messaging/README.md | 45 +++++++++++ .../data/pay-later-messaging/action-types.js | 18 +++++ .../js/data/pay-later-messaging/actions.js | 80 +++++++++++++++++++ .../js/data/pay-later-messaging/constants.js | 28 +++++++ .../js/data/pay-later-messaging/controls.js | 23 ++++++ .../js/data/pay-later-messaging/hooks.js | 50 ++++++++++++ .../js/data/pay-later-messaging/index.js | 32 ++++++++ .../js/data/pay-later-messaging/reducer.js | 62 ++++++++++++++ .../js/data/pay-later-messaging/resolvers.js | 39 +++++++++ .../js/data/pay-later-messaging/selectors.js | 21 +++++ .../Endpoint/PayLaterMessagingEndpoint.php | 2 +- 12 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/action-types.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/constants.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/controls.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/index.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js create mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/selectors.js diff --git a/modules/ppcp-settings/resources/js/data/index.js b/modules/ppcp-settings/resources/js/data/index.js index 0985aa972..227a62226 100644 --- a/modules/ppcp-settings/resources/js/data/index.js +++ b/modules/ppcp-settings/resources/js/data/index.js @@ -5,8 +5,17 @@ import * as Payment from './payment'; import * as Settings from './settings'; import * as Styling from './styling'; import * as Todos from './todos'; +import * as PayLaterMessaging from './pay-later-messaging'; -const stores = [ Onboarding, Common, Payment, Settings, Styling, Todos ]; +const stores = [ + Onboarding, + Common, + Payment, + Settings, + Styling, + Todos, + PayLaterMessaging, +]; stores.forEach( ( store ) => { try { @@ -30,6 +39,7 @@ export const PaymentHooks = Payment.hooks; export const SettingsHooks = Settings.hooks; export const StylingHooks = Styling.hooks; export const TodosHooks = Todos.hooks; +export const PayLaterMessagingHooks = PayLaterMessaging.hooks; export const OnboardingStoreName = Onboarding.STORE_NAME; export const CommonStoreName = Common.STORE_NAME; @@ -37,6 +47,7 @@ export const PaymentStoreName = Payment.STORE_NAME; export const SettingsStoreName = Settings.STORE_NAME; export const StylingStoreName = Styling.STORE_NAME; export const TodosStoreName = Todos.STORE_NAME; +export const PayLaterMessagingStoreName = PayLaterMessaging.STORE_NAME; export * from './configuration'; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md b/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md new file mode 100644 index 000000000..b97f6ca4c --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md @@ -0,0 +1,45 @@ +# Store template + +This template contains all files for a Redux store. + +## New Store: Redux integration + +1. Copy this folder, give it a correct name. +2. Check each file for `` placeholders and `TODO` remarks. +3. Edit the main store-index file and add the relevant store integration there. +4. Check the debug-module, and add relevant debug code. + - Register the store in the `reset()` method. + +--- + +Main store-index: +`modules/ppcp-settings/resources/js/data/index.js` + +Sample store integration: +```js +import * as YourStore from './yourStore'; +// ... +YourStore.initStore(); +// ... +export const YourStoreHooks = YourStore.hooks; +// ... +export const YourStoreName = YourStore.STORE_NAME; +// ... +addDebugTools( window.ppcpSettings, [ ..., YourStoreName ] ); +``` + +--- + +### New Store: PHP integration + +1. Create the **REST endpoint** for hydrating and persisting data. + - `modules/ppcp-settings/src/Endpoint/YourStoreRestEndpoint.php` + - Extend from base class `RestEndpoint` +2. Create the **data model** class to manage the DB interaction. + - `modules/ppcp-settings/src/Data/YourStoreSettings.php` + - Extend from base class `AbstractDataModel` +3. Create relevant **DI services** for both files. + - `modules/ppcp-settings/services.php` +4. Register the REST endpoint in the **service module**. + - `modules/ppcp-settings/src/SettingsModule.php` + - Find the action `rest_api_init` diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/action-types.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/action-types.js new file mode 100644 index 000000000..70913ddd6 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/action-types.js @@ -0,0 +1,18 @@ +/** + * Action Types: Define unique identifiers for actions across all store modules. + * + * @file + */ + +export default { + // Transient data. + SET_TRANSIENT: 'PAY_LATER_MESSAGING:SET_TRANSIENT', + + // Persistent data. + SET_PERSISTENT: 'PAY_LATER_MESSAGING:SET_PERSISTENT', + RESET: 'PAY_LATER_MESSAGING:RESET', + HYDRATE: 'PAY_LATER_MESSAGING:HYDRATE', + + // Controls - always start with "DO_". + DO_PERSIST_DATA: 'PAY_LATER_MESSAGING:DO_PERSIST_DATA', +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js new file mode 100644 index 000000000..59d68d37c --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js @@ -0,0 +1,80 @@ +/** + * Action Creators: Define functions to create action objects. + * + * These functions update state or trigger side effects (e.g., async operations). + * Actions are categorized as Transient, Persistent, or Side effect. + * + * @file + */ + +import { select } from '@wordpress/data'; + +import ACTION_TYPES from './action-types'; +import { STORE_NAME } from './constants'; + +/** + * @typedef {Object} Action An action object that is handled by a reducer or control. + * @property {string} type - The action type. + * @property {Object?} payload - Optional payload for the action. + */ + +/** + * Special. Resets all values in the store to initial defaults. + * + * @return {Action} The action. + */ +export const reset = () => ( { type: ACTION_TYPES.RESET } ); + +/** + * Persistent. Set the full store details during app initialization. + * + * @param {{data: {}, flags?: {}}} payload + * @return {Action} The action. + */ +export const hydrate = ( payload ) => ( { + type: ACTION_TYPES.HYDRATE, + payload, +} ); + +/** + * Generic transient-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 setTransient = ( prop, value ) => ( { + type: ACTION_TYPES.SET_TRANSIENT, + 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. + * + * @param {boolean} isReady + * @return {Action} The action. + */ +export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady ); + +/** + * Side effect. Triggers the persistence of store data to the server. + * + * @return {Action} The action. + */ +export const persist = function* () { + const data = yield select( STORE_NAME ).persistentData(); + + yield { type: ACTION_TYPES.DO_PERSIST_DATA, data }; +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/constants.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/constants.js new file mode 100644 index 000000000..09f3bab52 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/constants.js @@ -0,0 +1,28 @@ +/** + * Name of the Redux store module. + * + * Used by: Reducer, Selector, Index + * + * @type {string} + */ +export const STORE_NAME = 'wc/paypal/pay_later_messaging'; + +/** + * REST path to hydrate data of this module by loading data from the WP DB. + * + * Used by: Resolvers + * See: PayLaterMessagingEndpoint.php + * + * @type {string} + */ +export const REST_HYDRATE_PATH = '/wc/v3/wc_paypal/pay_later_messaging'; + +/** + * REST path to persist data of this module to the WP DB. + * + * Used by: Controls + * See: PayLaterMessagingEndpoint.php + * + * @type {string} + */ +export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/pay_later_messaging'; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/controls.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/controls.js new file mode 100644 index 000000000..9295b62bc --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/controls.js @@ -0,0 +1,23 @@ +/** + * Controls: Implement side effects, typically asynchronous operations. + * + * Controls use ACTION_TYPES keys as identifiers. + * They are triggered by corresponding actions and handle external interactions. + * + * @file + */ + +import apiFetch from '@wordpress/api-fetch'; + +import { REST_PERSIST_PATH } from './constants'; +import ACTION_TYPES from './action-types'; + +export const controls = { + async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) { + return await apiFetch( { + path: REST_PERSIST_PATH, + method: 'POST', + data, + } ); + }, +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js new file mode 100644 index 000000000..b6878c2a9 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js @@ -0,0 +1,50 @@ +/** + * Hooks: Provide the main API for components to interact with the store. + * + * These encapsulate store interactions, offering a consistent interface. + * Hooks simplify data access and manipulation for components. + * + * @file + */ + +import { useDispatch } from '@wordpress/data'; + +import { createHooksForStore } from '../utils'; +import { STORE_NAME } from './constants'; + +const useHooks = () => { + const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); + const { persist } = useDispatch( STORE_NAME ); + + // Read-only flags and derived state. + // Nothing here yet. + + // Transient accessors. + const [ isReady ] = useTransient( 'isReady' ); + + // Persistent accessors. + // TODO: Replace with real property. + const [ sampleValue, setSampleValue ] = usePersistent( 'sampleValue' ); + + return { + persist, + isReady, + sampleValue, + setSampleValue, + }; +}; + +export const useState = () => { + const { persist, isReady } = useHooks(); + return { persist, isReady }; +}; + +// TODO: Replace with real hook. +export const useSampleValue = () => { + const { sampleValue, setSampleValue } = useHooks(); + + return { + sampleValue, + setSampleValue, + }; +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/index.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/index.js new file mode 100644 index 000000000..3bd6e4459 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/index.js @@ -0,0 +1,32 @@ +import { createReduxStore, register } from '@wordpress/data'; +import { controls as wpControls } from '@wordpress/data-controls'; + +import { STORE_NAME } from './constants'; +import reducer from './reducer'; +import * as selectors from './selectors'; +import * as actions from './actions'; +import * as hooks from './hooks'; +import { resolvers } from './resolvers'; +import { controls } from './controls'; + +/** + * Initializes and registers the settings store with WordPress data layer. + * Combines custom controls with WordPress data controls. + * + * @return {boolean} True if initialization succeeded, false otherwise. + */ +export const initStore = () => { + const store = createReduxStore( STORE_NAME, { + reducer, + controls: { ...wpControls, ...controls }, + actions, + selectors, + resolvers, + } ); + + register( store ); + + return Boolean( wp.data.select( STORE_NAME ) ); +}; + +export { hooks, selectors, STORE_NAME }; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js new file mode 100644 index 000000000..7111d9302 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js @@ -0,0 +1,62 @@ +/** + * Reducer: Defines store structure and state updates for this module. + * + * Manages both transient (temporary) and persistent (saved) state. + * The initial state must define all properties, as dynamic additions are not supported. + * + * @file + */ + +import { createReducer, createReducerSetters } from '../utils'; +import ACTION_TYPES from './action-types'; + +// Store structure. + +// Transient: Values that are _not_ saved to the DB (like app lifecycle-flags). +const defaultTransient = Object.freeze( { + isReady: false, +} ); + +// Persistent: Values that are loaded from the DB. +const defaultPersistent = Object.freeze( { + // TODO: Add real DB properties here. + sampleValue: 'foo', + cart: {}, + checkout: {}, + product: {}, + shop: {}, + home: {}, + custom_placement: [], +} ); + +// Reducer logic. + +const [ changeTransient, changePersistent ] = createReducerSetters( + defaultTransient, + defaultPersistent +); + +const reducer = createReducer( defaultTransient, defaultPersistent, { + [ ACTION_TYPES.SET_TRANSIENT ]: ( state, payload ) => + changeTransient( state, payload ), + + [ ACTION_TYPES.SET_PERSISTENT ]: ( state, payload ) => + changePersistent( state, payload ), + + [ ACTION_TYPES.RESET ]: ( state ) => { + const cleanState = changeTransient( + changePersistent( state, defaultPersistent ), + defaultTransient + ); + + // Keep "read-only" details and initialization flags. + cleanState.isReady = true; + + return cleanState; + }, + + [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => + changePersistent( state, payload.data ), +} ); + +export default reducer; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js new file mode 100644 index 000000000..4b9bc4230 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js @@ -0,0 +1,39 @@ +/** + * Resolvers: Handle asynchronous data fetching for the store. + * + * These functions update store state with data from external sources. + * Each resolver corresponds to a specific selector (selector with same name must exist). + * Resolvers are called automatically when selectors request unavailable data. + * + * @file + */ + +import { dispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { apiFetch } from '@wordpress/data-controls'; + +import { STORE_NAME, REST_HYDRATE_PATH } from './constants'; + +export const resolvers = { + /** + * Retrieve settings from the site's REST API. + */ + *persistentData() { + try { + const result = yield apiFetch( { path: REST_HYDRATE_PATH } ); + + console.log( result ); + + yield dispatch( STORE_NAME ).hydrate( result ); + yield dispatch( STORE_NAME ).setIsReady( true ); + } catch ( e ) { + yield dispatch( 'core/notices' ).createErrorNotice( + // TODO: Add the module name to the error message. + __( + 'Error retrieving Pay Later Messaging config details.', + 'woocommerce-paypal-payments' + ) + ); + } + }, +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/selectors.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/selectors.js new file mode 100644 index 000000000..14334fcf3 --- /dev/null +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/selectors.js @@ -0,0 +1,21 @@ +/** + * Selectors: Extract specific pieces of state from the store. + * + * These functions provide a consistent interface for accessing store data. + * They allow components to retrieve data without knowing the store structure. + * + * @file + */ + +const EMPTY_OBJ = Object.freeze( {} ); + +const getState = ( state ) => state || EMPTY_OBJ; + +export const persistentData = ( state ) => { + return getState( state ).data || EMPTY_OBJ; +}; + +export const transientData = ( state ) => { + const { data, ...transientState } = getState( state ); + return transientState || EMPTY_OBJ; +}; diff --git a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php index c39fa056f..d448e1a9b 100644 --- a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php @@ -100,7 +100,7 @@ class PayLaterMessagingEndpoint extends RestEndpoint { * * @param WP_REST_Request $request Full data about the request. * - * @return WP_REST_Response The updated payment methods details. + * @return WP_REST_Response The updated Pay Later Messaging configuration details. */ public function update_details( WP_REST_Request $request ) : WP_REST_Response { $this->save_config->save_config( $request->get_params() ); From a652f21629a51b556522a559a36ded6bf09c9f01 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 30 Jan 2025 10:50:44 +0100 Subject: [PATCH 46/78] Read config from store --- .../resources/js/paylater-configurator.js | 1 + .../Screens/Overview/TabPayLaterMessaging.js | 5 ++-- .../js/data/pay-later-messaging/hooks.js | 28 +++++++++++++++++++ .../js/data/pay-later-messaging/resolvers.js | 2 -- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js b/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js index f3daad01e..27145de16 100644 --- a/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js +++ b/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js @@ -51,6 +51,7 @@ document.addEventListener( 'DOMContentLoaded', () => { .then( ( data ) => { if ( data.success ) { const config = data.data; + console.log( config ); merchantConfigurators.Messaging( { config, diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js index a3a3083c9..d22318d26 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js @@ -1,7 +1,8 @@ import React, { useEffect } from 'react'; +import { PayLaterMessagingHooks } from '../../../data'; const TabPayLaterMessaging = () => { - const config = {}; // Replace with the appropriate/saved configuration. + const { config } = PayLaterMessagingHooks.usePayLaterMessaging(); const PcpPayLaterConfigurator = window.ppcpSettings?.PcpPayLaterConfigurator; @@ -37,7 +38,7 @@ const TabPayLaterMessaging = () => { }, } ); } - }, [ PcpPayLaterConfigurator ] ); + }, [ PcpPayLaterConfigurator, config ] ); return (
{ // TODO: Replace with real property. const [ sampleValue, setSampleValue ] = usePersistent( 'sampleValue' ); + const [ cart ] = usePersistent( 'cart' ); + const [ checkout ] = usePersistent( 'checkout' ); + const [ product ] = usePersistent( 'product' ); + const [ shop ] = usePersistent( 'shop' ); + const [ home ] = usePersistent( 'home' ); + const [ custom_placement ] = usePersistent( 'custom_placement' ); + return { persist, isReady, sampleValue, setSampleValue, + cart, + checkout, + product, + shop, + home, + custom_placement, }; }; @@ -48,3 +61,18 @@ export const useSampleValue = () => { setSampleValue, }; }; + +export const usePayLaterMessaging = () => { + const { cart, checkout, product, shop, home, customPlacement } = useHooks(); + + return { + config: { + cart, + checkout, + product, + shop, + home, + customPlacement, + }, + }; +}; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js index 4b9bc4230..39ff6c343 100644 --- a/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/resolvers.js @@ -22,8 +22,6 @@ export const resolvers = { try { const result = yield apiFetch( { path: REST_HYDRATE_PATH } ); - console.log( result ); - yield dispatch( STORE_NAME ).hydrate( result ); yield dispatch( STORE_NAME ).setIsReady( true ); } catch ( e ) { From 917a147af8a39a0d285ec7eb85d1feb20acf5316 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 13:36:57 +0100 Subject: [PATCH 47/78] =?UTF-8?q?=F0=9F=A5=85=20Catch=20failed=20eligibili?= =?UTF-8?q?ty=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-wc-gateway/src/WCGatewayModule.php | 86 ++++++++++++------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index da8bf00cb..c10813c86 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -551,42 +551,64 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul add_filter( 'woocommerce_paypal_payments_rest_common_merchant_data', - function( array $features ) use ( $c ): array { - $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); - assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); + static function ( array $features ) use ( $c ) : array { + try { + $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); + assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); - $reference_transactions_enabled = $billing_agreements_endpoint->reference_transaction_enabled(); - $features['save_paypal_and_venmo'] = array( - 'enabled' => $reference_transactions_enabled, - ); - - $dcc_product_status = $c->get( 'wcgateway.helper.dcc-product-status' ); - assert( $dcc_product_status instanceof DCCProductStatus ); - - $dcc_enabled = $dcc_product_status->dcc_is_active(); - $features['advanced_credit_and_debit_cards'] = array( - 'enabled' => $dcc_enabled, - ); - - $partners_endpoint = $c->get( 'api.endpoint.partners' ); - assert( $partners_endpoint instanceof PartnersEndpoint ); - $seller_status = $partners_endpoint->seller_status(); - - $apms_enabled = false; - foreach ( $seller_status->products() as $product ) { - if ( $product->name() === 'PAYMENT_METHODS' ) { - $apms_enabled = true; - break; - } + $reference_transactions_enabled = $billing_agreements_endpoint->reference_transaction_enabled(); + $features['save_paypal_and_venmo'] = array( + 'enabled' => $reference_transactions_enabled, + ); + } catch ( Exception $ex ) { + $features['save_paypal_and_venmo'] = array( + 'enabled' => false, + ); } - $features['alternative_payment_methods'] = array( - 'enabled' => $apms_enabled, - ); + try { + $dcc_product_status = $c->get( 'wcgateway.helper.dcc-product-status' ); + assert( $dcc_product_status instanceof DCCProductStatus ); + $dcc_enabled = $dcc_product_status->dcc_is_active(); + $features['advanced_credit_and_debit_cards'] = array( + 'enabled' => $dcc_enabled, + ); + } catch ( Exception $ex ) { + $features['advanced_credit_and_debit_cards'] = array( + 'enabled' => false, + ); + } - $features['pay_later_messaging'] = array( - 'enabled' => true, - ); + try { + // TODO: The `seller_status()` call throws a PayPalApiException. Why? + $partners_endpoint = $c->get( 'api.endpoint.partners' ); + assert( $partners_endpoint instanceof PartnersEndpoint ); + $seller_status = $partners_endpoint->seller_status(); + + $apms_enabled = false; + foreach ( $seller_status->products() as $product ) { + if ( $product->name() === 'PAYMENT_METHODS' ) { + $apms_enabled = true; + break; + } + } + + $features['alternative_payment_methods'] = array( + 'enabled' => $apms_enabled, + ); + + $features['pay_later_messaging'] = array( + 'enabled' => true, + ); + } catch ( Exception $ex ) { + $features['alternative_payment_methods'] = array( + 'enabled' => false, + ); + + $features['pay_later_messaging'] = array( + 'enabled' => false, + ); + } return $features; } From 66c6fb17b81e2da4e3ae25bff92426d20914b60e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 30 Jan 2025 16:39:26 +0100 Subject: [PATCH 48/78] Save pay later messaging config to database --- .../Screens/Overview/TabPayLaterMessaging.js | 23 +++++--- .../resources/js/data/_example/hooks.js | 2 +- .../js/data/pay-later-messaging/hooks.js | 59 +++++++++++-------- .../js/data/pay-later-messaging/reducer.js | 2 - .../resources/js/hooks/useSaveSettings.js | 19 +++++- .../Endpoint/PayLaterMessagingEndpoint.php | 2 +- 6 files changed, 70 insertions(+), 37 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js index d22318d26..274ef91c2 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js @@ -2,7 +2,15 @@ import React, { useEffect } from 'react'; import { PayLaterMessagingHooks } from '../../../data'; const TabPayLaterMessaging = () => { - const { config } = PayLaterMessagingHooks.usePayLaterMessaging(); + const { + config, + setCart, + setCheckout, + setProduct, + setShop, + setHome, + setCustom_placement, + } = PayLaterMessagingHooks.usePayLaterMessaging(); const PcpPayLaterConfigurator = window.ppcpSettings?.PcpPayLaterConfigurator; @@ -28,13 +36,12 @@ const TabPayLaterMessaging = () => { subheader: 'ppcp-r-paylater-configurator__subheader', }, onSave: ( data ) => { - /* - TODO: - - The saving will be handled in a separate PR. - - One option could be: - - When saving the settings, programmatically click on the configurator's - "Save Changes" button and send the request to PHP. - */ + setCart( data.config.cart ); + setCheckout( data.config.checkout ); + setProduct( data.config.product ); + setShop( data.config.shop ); + setHome( data.config.home ); + setCustom_placement( data.config.custom_placement ); }, } ); } diff --git a/modules/ppcp-settings/resources/js/data/_example/hooks.js b/modules/ppcp-settings/resources/js/data/_example/hooks.js index b6878c2a9..95db3765e 100644 --- a/modules/ppcp-settings/resources/js/data/_example/hooks.js +++ b/modules/ppcp-settings/resources/js/data/_example/hooks.js @@ -34,7 +34,7 @@ const useHooks = () => { }; }; -export const useState = () => { +export const useStore = () => { const { persist, isReady } = useHooks(); return { persist, isReady }; }; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js index 174e618e7..0f51051cd 100644 --- a/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/hooks.js @@ -23,47 +23,52 @@ const useHooks = () => { const [ isReady ] = useTransient( 'isReady' ); // Persistent accessors. - // TODO: Replace with real property. - const [ sampleValue, setSampleValue ] = usePersistent( 'sampleValue' ); - - const [ cart ] = usePersistent( 'cart' ); - const [ checkout ] = usePersistent( 'checkout' ); - const [ product ] = usePersistent( 'product' ); - const [ shop ] = usePersistent( 'shop' ); - const [ home ] = usePersistent( 'home' ); - const [ custom_placement ] = usePersistent( 'custom_placement' ); + const [ cart, setCart ] = usePersistent( 'cart' ); + const [ checkout, setCheckout ] = usePersistent( 'checkout' ); + const [ product, setProduct ] = usePersistent( 'product' ); + const [ shop, setShop ] = usePersistent( 'shop' ); + const [ home, setHome ] = usePersistent( 'home' ); + const [ custom_placement, setCustom_placement ] = + usePersistent( 'custom_placement' ); return { persist, isReady, - sampleValue, - setSampleValue, cart, + setCart, checkout, + setCheckout, product, + setProduct, shop, + setShop, home, + setHome, custom_placement, + setCustom_placement, }; }; -export const useState = () => { +export const useStore = () => { const { persist, isReady } = useHooks(); return { persist, isReady }; }; -// TODO: Replace with real hook. -export const useSampleValue = () => { - const { sampleValue, setSampleValue } = useHooks(); - - return { - sampleValue, - setSampleValue, - }; -}; - export const usePayLaterMessaging = () => { - const { cart, checkout, product, shop, home, customPlacement } = useHooks(); + const { + cart, + setCart, + checkout, + setCheckout, + product, + setProduct, + shop, + setShop, + home, + setHome, + custom_placement, + setCustom_placement, + } = useHooks(); return { config: { @@ -72,7 +77,13 @@ export const usePayLaterMessaging = () => { product, shop, home, - customPlacement, + custom_placement, }, + setCart, + setCheckout, + setProduct, + setShop, + setHome, + setCustom_placement, }; }; diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js index 7111d9302..5843ef400 100644 --- a/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/reducer.js @@ -19,8 +19,6 @@ const defaultTransient = Object.freeze( { // Persistent: Values that are loaded from the DB. const defaultPersistent = Object.freeze( { - // TODO: Add real DB properties here. - sampleValue: 'foo', cart: {}, checkout: {}, product: {}, diff --git a/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js b/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js index f268d3214..ca594e39d 100644 --- a/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js +++ b/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js @@ -2,6 +2,7 @@ import { useCallback } from '@wordpress/element'; import { CommonHooks, + PayLaterMessagingHooks, PaymentHooks, SettingsHooks, StylingHooks, @@ -13,8 +14,13 @@ export const useSaveSettings = () => { const { persist: persistPayment } = PaymentHooks.useStore(); const { persist: persistSettings } = SettingsHooks.useStore(); const { persist: persistStyling } = StylingHooks.useStore(); + const { persist: persistPayLaterMessaging } = + PayLaterMessagingHooks.useStore(); const persistAll = useCallback( () => { + // Executes onSave on TabPayLaterMessaging component. + document.getElementById( 'configurator-publishButton' )?.click(); + withActivity( 'persist-methods', 'Save payment methods', @@ -30,7 +36,18 @@ export const useSaveSettings = () => { 'Save styling details', persistStyling ); - }, [ persistPayment, persistSettings, persistStyling, withActivity ] ); + withActivity( + 'persist-pay-later-messaging', + 'Save pay later messaging details', + persistPayLaterMessaging + ); + }, [ + persistPayment, + persistSettings, + persistStyling, + persistPayLaterMessaging, + withActivity, + ] ); return { persistAll }; }; diff --git a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php index d448e1a9b..5713ce570 100644 --- a/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/PayLaterMessagingEndpoint.php @@ -103,7 +103,7 @@ class PayLaterMessagingEndpoint extends RestEndpoint { * @return WP_REST_Response The updated Pay Later Messaging configuration details. */ public function update_details( WP_REST_Request $request ) : WP_REST_Response { - $this->save_config->save_config( $request->get_params() ); + $this->save_config->save_config( $request->get_json_params() ); return $this->get_details(); } From 84a30d84d9e8b2534c43332fb395c1275d58f229 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 30 Jan 2025 16:48:06 +0100 Subject: [PATCH 49/78] Remove readme boilerplace example --- .../js/data/pay-later-messaging/README.md | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md b/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md deleted file mode 100644 index b97f6ca4c..000000000 --- a/modules/ppcp-settings/resources/js/data/pay-later-messaging/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Store template - -This template contains all files for a Redux store. - -## New Store: Redux integration - -1. Copy this folder, give it a correct name. -2. Check each file for `` placeholders and `TODO` remarks. -3. Edit the main store-index file and add the relevant store integration there. -4. Check the debug-module, and add relevant debug code. - - Register the store in the `reset()` method. - ---- - -Main store-index: -`modules/ppcp-settings/resources/js/data/index.js` - -Sample store integration: -```js -import * as YourStore from './yourStore'; -// ... -YourStore.initStore(); -// ... -export const YourStoreHooks = YourStore.hooks; -// ... -export const YourStoreName = YourStore.STORE_NAME; -// ... -addDebugTools( window.ppcpSettings, [ ..., YourStoreName ] ); -``` - ---- - -### New Store: PHP integration - -1. Create the **REST endpoint** for hydrating and persisting data. - - `modules/ppcp-settings/src/Endpoint/YourStoreRestEndpoint.php` - - Extend from base class `RestEndpoint` -2. Create the **data model** class to manage the DB interaction. - - `modules/ppcp-settings/src/Data/YourStoreSettings.php` - - Extend from base class `AbstractDataModel` -3. Create relevant **DI services** for both files. - - `modules/ppcp-settings/services.php` -4. Register the REST endpoint in the **service module**. - - `modules/ppcp-settings/src/SettingsModule.php` - - Find the action `rest_api_init` From f907558262b18982d48a062e156b2a8683f9490b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 30 Jan 2025 16:50:34 +0100 Subject: [PATCH 50/78] Remove console.log --- .../resources/js/paylater-configurator.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js b/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js index 27145de16..f3daad01e 100644 --- a/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js +++ b/modules/ppcp-paylater-configurator/resources/js/paylater-configurator.js @@ -51,7 +51,6 @@ document.addEventListener( 'DOMContentLoaded', () => { .then( ( data ) => { if ( data.success ) { const config = data.data; - console.log( config ); merchantConfigurators.Messaging( { config, From 70abceeee03eb74c45d6ae987eb4a1da4d63a312 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:12:19 +0100 Subject: [PATCH 51/78] =?UTF-8?q?=E2=9C=A8=20New=20base=20class=20to=20che?= =?UTF-8?q?ck=20feature=20eligibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Helper/ProductStatus.php | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 modules/ppcp-api-client/src/Helper/ProductStatus.php diff --git a/modules/ppcp-api-client/src/Helper/ProductStatus.php b/modules/ppcp-api-client/src/Helper/ProductStatus.php new file mode 100644 index 000000000..a6c575a3e --- /dev/null +++ b/modules/ppcp-api-client/src/Helper/ProductStatus.php @@ -0,0 +1,195 @@ +is_connected = $is_connected; + $this->partners_endpoint = $partners_endpoint; + $this->api_failure_registry = $api_failure_registry; + } + + /** + * Uses local data (DB values, hooks) to determine if the feature is eligible. + * + * Returns true when the feature is available, and false if ineligible. + * On failure, an RuntimeException is thrown. + * + * @return null|bool Boolean to indicate the status; null if the status not locally defined. + * @throws RuntimeException When the check failed. + */ + abstract protected function check_local_state() : ?bool; + + /** + * Inspects the API response of the SellerStatus to determine feature eligibility. + * + * Returns true when the feature is available, and false if ineligible. + * On failure, an RuntimeException is thrown. + * + * @param SellerStatus $seller_status The seller status, returned from the API. + * @return bool + * @throws RuntimeException When the check failed. + */ + abstract protected function check_active_state( SellerStatus $seller_status ) : bool; + + /** + * Clears the eligibility status from the local cache/DB to enforce a new + * API call on the next eligibility check. + * + * @param Settings|null $settings See description in {@see self::clear()}. + * @return void + */ + abstract protected function clear_state( Settings $settings = null ) : void; + + /** + * Whether the merchant has access to the feature. + * + * @return bool + */ + public function is_active() : bool { + if ( null !== $this->is_eligible ) { + return $this->is_eligible; + } + + $this->is_eligible = false; + $this->has_request_failure = false; + + if ( ! $this->is_onboarded() ) { + return $this->is_eligible; + } + + try { + // Try to use filters and DB values to determine the state. + $local_state = $this->check_local_state(); + if ( null !== $local_state ) { + $this->is_eligible = $local_state; + + return $this->is_eligible; + } + + // Check using the merchant-API. + $seller_status = $this->get_seller_status_object(); + $this->is_eligible = $this->check_active_state( $seller_status ); + } catch ( RuntimeException $exception ) { + $this->has_request_failure = true; + } + + return $this->is_eligible; + } + + /** + * Fetches the seller-status object from the PayPal merchant API. + * + * TODO: We might cache the SellerStatus, as it's usually accessed multiple times during one request. + * + * @return SellerStatus + * @throws RuntimeException When the check failed. + */ + protected function get_seller_status_object() : SellerStatus { + // Check API failure registry to prevent multiple failed API requests. + if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { + throw new RuntimeException( 'Timeout for re-check not reached yet' ); + } + + // Request seller status via PayPal API, might throw an Exception. + return $this->partners_endpoint->seller_status(); + } + + /** + * Whether the merchant was fully onboarded, and we have valid API credentials. + * + * @return bool True, if we can use the merchant API endpoints. + */ + public function is_onboarded() : bool { + return $this->is_connected; + } + + /** + * Returns if there was a request failure. + * + * @return bool + */ + public function has_request_failure() : bool { + return $this->has_request_failure; + } + + /** + * Clears the persisted result to force a recheck. + * + * Accepts a Settings object to don't override other sequential settings that are being updated + * elsewhere. + * + * @param Settings|null $settings The settings object. + * @return void + */ + public function clear( Settings $settings = null ) : void { + $this->is_eligible = null; + $this->has_request_failure = false; + + $this->clear_state( $settings ); + } +} From 11f30715792fd85acb89f1ea9c3b2d9a2259c1b8 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:12:50 +0100 Subject: [PATCH 52/78] =?UTF-8?q?=E2=9C=A8=20New=20services=20to=20check?= =?UTF-8?q?=20the=20onboarding=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-onboarding/services.php | 10 ++++++++++ modules/ppcp-settings/services.php | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 2a6de82d8..85f3d4219 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -117,6 +117,16 @@ return array( $settings = $container->get( 'wcgateway.settings' ); return new State( $settings ); }, + /** + * Checks if the onboarding process is completed and the merchant API can be used. + * This service is overwritten by the ppcp-settings module, when it's active. + */ + 'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool { + $state = $container->get( 'onboarding.state' ); + assert( $state instanceof State ); + + return $state->current_state() >= State::STATE_ONBOARDED; + }, 'onboarding.environment' => function( ContainerInterface $container ) : Environment { $settings = $container->get( 'wcgateway.settings' ); return new Environment( $settings ); diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index 1326f4a4a..cbfb9f1ed 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -86,6 +86,15 @@ return array( $container->get( 'settings.service.sanitizer' ) ); }, + /** + * Checks if valid merchant connection details are stored in the DB. + */ + 'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool { + $data = $container->get( 'settings.data.general' ); + assert( $data instanceof GeneralSettings ); + + return $data->is_merchant_connected(); + }, 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); }, From 3bd118de1bfd0741e0df42ee436163d7762f5046 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:13:56 +0100 Subject: [PATCH 53/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20new=20base=20c?= =?UTF-8?q?lass=20for=20ApplePay=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-applepay/services.php | 2 +- .../src/Assets/AppleProductStatus.php | 148 ++++-------------- 2 files changed, 29 insertions(+), 121 deletions(-) diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 1ba23ca08..963891f48 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -79,7 +79,7 @@ return array( return new AppleProductStatus( $container->get( 'wcgateway.settings' ), $container->get( 'api.endpoint.partners' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'api.helper.failure-registry' ) ); } diff --git a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php index d5409ddaf..fb728696f 100644 --- a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php +++ b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php @@ -5,134 +5,71 @@ * @package WooCommerce\PayPalCommerce\Applepay\Assets */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Applepay\Assets; -use Throwable; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusCapability; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\ProductStatus; +use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus; /** * Class AppleProductStatus */ -class AppleProductStatus { - const CAPABILITY_NAME = 'APPLE_PAY'; - const SETTINGS_KEY = 'products_apple_enabled'; +class AppleProductStatus extends ProductStatus { + public const CAPABILITY_NAME = 'APPLE_PAY'; + public const SETTINGS_KEY = 'products_apple_enabled'; - const SETTINGS_VALUE_ENABLED = 'yes'; - const SETTINGS_VALUE_DISABLED = 'no'; - const SETTINGS_VALUE_UNDEFINED = ''; - - /** - * The current status stored in memory. - * - * @var bool|null - */ - private $current_status = null; - - /** - * If there was a request failure. - * - * @var bool - */ - private $has_request_failure = false; + public const SETTINGS_VALUE_ENABLED = 'yes'; + public const SETTINGS_VALUE_DISABLED = 'no'; + public const SETTINGS_VALUE_UNDEFINED = ''; /** * The settings. * * @var Settings */ - private $settings; - - /** - * The partners endpoint. - * - * @var PartnersEndpoint - */ - private $partners_endpoint; - - /** - * The onboarding status - * - * @var State - */ - private $onboarding_state; - - /** - * The API failure registry - * - * @var FailureRegistry - */ - private $api_failure_registry; + private Settings $settings; /** * AppleProductStatus constructor. * - * @param Settings $settings The Settings. - * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. - * @param State $onboarding_state The onboarding state. + * @param Settings $settings The Settings. + * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. + * @param bool $is_connected The onboarding state. * @param FailureRegistry $api_failure_registry The API failure registry. */ public function __construct( Settings $settings, PartnersEndpoint $partners_endpoint, - State $onboarding_state, + bool $is_connected, FailureRegistry $api_failure_registry ) { - $this->settings = $settings; - $this->partners_endpoint = $partners_endpoint; - $this->onboarding_state = $onboarding_state; - $this->api_failure_registry = $api_failure_registry; + parent::__construct( $is_connected, $partners_endpoint, $api_failure_registry ); + + $this->settings = $settings; } - /** - * Whether the active/subscribed products support Applepay. - * - * @return bool - */ - public function is_active() : bool { - - // If not onboarded then makes no sense to check status. - if ( ! $this->is_onboarded() ) { - return false; - } - + /** {@inheritDoc} */ + protected function check_local_state() : ?bool { $status_override = apply_filters( 'woocommerce_paypal_payments_apple_pay_product_status', null ); if ( null !== $status_override ) { return $status_override; } - // If status was already checked on this request return the same result. - if ( null !== $this->current_status ) { - return $this->current_status; - } - // Check if status was checked on previous requests. if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { - $this->current_status = wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); - return $this->current_status; + return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } - // Check API failure registry to prevent multiple failed API requests. - if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { - $this->has_request_failure = true; - $this->current_status = false; - return $this->current_status; - } - - // Request seller status via PayPal API. - try { - $seller_status = $this->partners_endpoint->seller_status(); - } catch ( Throwable $error ) { - $this->has_request_failure = true; - $this->current_status = false; - return $this->current_status; - } + return null; + } + /** {@inheritDoc} */ + protected function check_active_state( SellerStatus $seller_status ) : bool { // Check the seller status for the intended capability. $has_capability = false; foreach ( $seller_status->products() as $product ) { @@ -156,54 +93,25 @@ class AppleProductStatus { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); $this->settings->persist(); - $this->current_status = true; - return $this->current_status; + return true; } // Capability not found, persist status and return false. $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); $this->settings->persist(); - $this->current_status = false; - return $this->current_status; + return false; } - /** - * Returns if the seller is onboarded. - * - * @return bool - */ - public function is_onboarded(): bool { - return $this->onboarding_state->current_state() >= State::STATE_ONBOARDED; - } - - /** - * Returns if there was a request failure. - * - * @return bool - */ - public function has_request_failure(): bool { - return $this->has_request_failure; - } - - /** - * Clears the persisted result to force a recheck. - * - * @param Settings|null $settings The settings object. - * We accept a Settings object to don't override other sequential settings that are being updated elsewhere. - * @return void - */ - public function clear( Settings $settings = null ): void { + /** {@inheritDoc} */ + protected function clear_state( Settings $settings = null ) : void { if ( null === $settings ) { $settings = $this->settings; } - $this->current_status = null; - if ( $settings->has( self::SETTINGS_KEY ) ) { $settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED ); $settings->persist(); } } - } From 00edc1139217ae7996ba03a096a7ea5e26ba403a Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:26:24 +0100 Subject: [PATCH 54/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20new=20base=20c?= =?UTF-8?q?lass=20for=20GooglePay=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-googlepay/services.php | 2 +- .../src/Helper/ApmProductStatus.php | 161 ++++-------------- 2 files changed, 34 insertions(+), 129 deletions(-) diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index b1cccaebd..18c48f3ad 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -75,7 +75,7 @@ return array( return new ApmProductStatus( $container->get( 'wcgateway.settings' ), $container->get( 'api.endpoint.partners' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'api.helper.failure-registry' ) ); } diff --git a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php index 3639a1717..1da8379df 100644 --- a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php +++ b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php @@ -9,130 +9,67 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Googlepay\Helper; -use Throwable; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusCapability; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\ProductStatus; +use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus; /** * Class ApmProductStatus */ -class ApmProductStatus { - const CAPABILITY_NAME = 'GOOGLE_PAY'; - const SETTINGS_KEY = 'products_googlepay_enabled'; +class ApmProductStatus extends ProductStatus { + public const CAPABILITY_NAME = 'GOOGLE_PAY'; + public const SETTINGS_KEY = 'products_googlepay_enabled'; - const SETTINGS_VALUE_ENABLED = 'yes'; - const SETTINGS_VALUE_DISABLED = 'no'; - const SETTINGS_VALUE_UNDEFINED = ''; - - /** - * The current status stored in memory. - * - * @var bool|null - */ - private $current_status = null; - - /** - * If there was a request failure. - * - * @var bool - */ - private $has_request_failure = false; + public const SETTINGS_VALUE_ENABLED = 'yes'; + public const SETTINGS_VALUE_DISABLED = 'no'; + public const SETTINGS_VALUE_UNDEFINED = ''; /** * The settings. * * @var Settings */ - private $settings; - - /** - * The partners endpoint. - * - * @var PartnersEndpoint - */ - private $partners_endpoint; - - /** - * The onboarding status - * - * @var State - */ - private $onboarding_state; - - /** - * The API failure registry - * - * @var FailureRegistry - */ - private $api_failure_registry; + private Settings $settings; /** * ApmProductStatus constructor. * - * @param Settings $settings The Settings. - * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. - * @param State $onboarding_state The onboarding state. + * @param Settings $settings The Settings. + * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. + * @param bool $is_connected The onboarding state. * @param FailureRegistry $api_failure_registry The API failure registry. */ public function __construct( Settings $settings, PartnersEndpoint $partners_endpoint, - State $onboarding_state, + bool $is_connected, FailureRegistry $api_failure_registry ) { - $this->settings = $settings; - $this->partners_endpoint = $partners_endpoint; - $this->onboarding_state = $onboarding_state; - $this->api_failure_registry = $api_failure_registry; + parent::__construct( $is_connected, $partners_endpoint, $api_failure_registry ); + + $this->settings = $settings; } - /** - * Whether the active/subscribed products support Googlepay. - * - * @return bool - */ - public function is_active() : bool { - - // If not onboarded then makes no sense to check status. - if ( ! $this->is_onboarded() ) { - return false; - } - + /** {@inheritDoc} */ + protected function check_local_state() : ?bool { $status_override = apply_filters( 'woocommerce_paypal_payments_google_pay_product_status', null ); if ( null !== $status_override ) { return $status_override; } - // If status was already checked on this request return the same result. - if ( null !== $this->current_status ) { - return $this->current_status; - } - // Check if status was checked on previous requests. if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { - $this->current_status = wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); - return $this->current_status; + return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } - // Check API failure registry to prevent multiple failed API requests. - if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { - $this->has_request_failure = true; - $this->current_status = false; - return $this->current_status; - } - - // Request seller status via PayPal API. - try { - $seller_status = $this->partners_endpoint->seller_status(); - } catch ( Throwable $error ) { - $this->has_request_failure = true; - $this->current_status = false; - return $this->current_status; - } + return null; + } + /** {@inheritDoc} */ + protected function check_active_state( SellerStatus $seller_status ) : bool { // Check the seller status for the intended capability. $has_capability = false; foreach ( $seller_status->products() as $product ) { @@ -145,65 +82,33 @@ class ApmProductStatus { } } - foreach ( $seller_status->capabilities() as $capability ) { - if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) { - $has_capability = true; + if ( ! $has_capability ) { + foreach ( $seller_status->capabilities() as $capability ) { + if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) { + $has_capability = true; + } } } if ( $has_capability ) { - // Capability found, persist status and return true. $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); - $this->settings->persist(); - - $this->current_status = true; - return $this->current_status; + } else { + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); } - - // Capability not found, persist status and return false. - $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); $this->settings->persist(); - $this->current_status = false; - return $this->current_status; + return $has_capability; } - /** - * Returns if the seller is onboarded. - * - * @return bool - */ - public function is_onboarded(): bool { - return $this->onboarding_state->current_state() >= State::STATE_ONBOARDED; - } - - /** - * Returns if there was a request failure. - * - * @return bool - */ - public function has_request_failure(): bool { - return $this->has_request_failure; - } - - /** - * Clears the persisted result to force a recheck. - * - * @param Settings|null $settings The settings object. - * We accept a Settings object to don't override other sequential settings that are being updated elsewhere. - * @return void - */ - public function clear( Settings $settings = null ): void { + /** {@inheritDoc} */ + protected function clear_state( Settings $settings = null ) : void { if ( null === $settings ) { $settings = $this->settings; } - $this->current_status = null; - if ( $settings->has( self::SETTINGS_KEY ) ) { $settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED ); $settings->persist(); } } - } From c985573d1dd5742321c5bf3f488d044f23061072 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:27:37 +0100 Subject: [PATCH 55/78] =?UTF-8?q?=F0=9F=A5=85=20Annotate=20all=20throwable?= =?UTF-8?q?=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/src/Helper/ProductStatus.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Helper/ProductStatus.php b/modules/ppcp-api-client/src/Helper/ProductStatus.php index a6c575a3e..eeda4ad22 100644 --- a/modules/ppcp-api-client/src/Helper/ProductStatus.php +++ b/modules/ppcp-api-client/src/Helper/ProductStatus.php @@ -10,9 +10,11 @@ declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\ApiClient\Helper; use RuntimeException; +use Exception; +use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; -use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; /** * Class ProductStatus @@ -81,6 +83,7 @@ abstract class ProductStatus { * * @return null|bool Boolean to indicate the status; null if the status not locally defined. * @throws RuntimeException When the check failed. + * @throws NotFoundException When a relevant service or setting was not found. */ abstract protected function check_local_state() : ?bool; @@ -134,7 +137,7 @@ abstract class ProductStatus { // Check using the merchant-API. $seller_status = $this->get_seller_status_object(); $this->is_eligible = $this->check_active_state( $seller_status ); - } catch ( RuntimeException $exception ) { + } catch ( Exception $exception ) { $this->has_request_failure = true; } From 4006605964079e66662d07c7b0cb3c9b4131cc40 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:29:23 +0100 Subject: [PATCH 56/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Slightly=20shorten?= =?UTF-8?q?=20ApplePay=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Assets/AppleProductStatus.php | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php index fb728696f..61668c26d 100644 --- a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php +++ b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php @@ -60,7 +60,6 @@ class AppleProductStatus extends ProductStatus { return $status_override; } - // Check if status was checked on previous requests. if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } @@ -82,25 +81,22 @@ class AppleProductStatus extends ProductStatus { } } - foreach ( $seller_status->capabilities() as $capability ) { - if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) { - $has_capability = true; + if ( ! $has_capability ) { + foreach ( $seller_status->capabilities() as $capability ) { + if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) { + $has_capability = true; + } } } if ( $has_capability ) { - // Capability found, persist status and return true. $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); - $this->settings->persist(); - - return true; + } else { + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); } - - // Capability not found, persist status and return false. - $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); $this->settings->persist(); - return false; + return $has_capability; } /** {@inheritDoc} */ From e6a6cbb7b18ba5f910d9f41c4e5e3be8d234455a Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 17:43:15 +0100 Subject: [PATCH 57/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20new=20base=20c?= =?UTF-8?q?lass=20for=20DCC=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/src/SettingsModule.php | 4 +- modules/ppcp-wc-gateway/services.php | 4 +- .../src/Helper/DCCProductStatus.php | 161 +++++------------- .../src/Settings/SectionsRenderer.php | 2 +- .../src/Settings/SettingsRenderer.php | 2 +- .../ppcp-wc-gateway/src/WCGatewayModule.php | 4 +- 6 files changed, 51 insertions(+), 126 deletions(-) diff --git a/modules/ppcp-settings/src/SettingsModule.php b/modules/ppcp-settings/src/SettingsModule.php index b7c334425..63b913e02 100644 --- a/modules/ppcp-settings/src/SettingsModule.php +++ b/modules/ppcp-settings/src/SettingsModule.php @@ -299,7 +299,7 @@ class SettingsModule implements ServiceModule, ExecutableModule { assert( $dcc_applies instanceof DCCApplies ); // Unset BCDC if merchant is eligible for ACDC. - if ( $dcc_product_status->dcc_is_active() && ! $container->get( 'wcgateway.settings.allow_card_button_gateway' ) ) { + if ( $dcc_product_status->is_active() && ! $container->get( 'wcgateway.settings.allow_card_button_gateway' ) ) { unset( $payment_methods[ CardButtonGateway::ID ] ); } @@ -319,7 +319,7 @@ class SettingsModule implements ServiceModule, ExecutableModule { } // Unset Fastlane if store location is not United States or merchant is not eligible for ACDC. - if ( $container->get( 'api.shop.country' ) !== 'US' || ! $dcc_product_status->dcc_is_active() ) { + if ( $container->get( 'api.shop.country' ) !== 'US' || ! $dcc_product_status->is_active() ) { unset( $payment_methods['ppcp-axo-gateway'] ); } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 1bac9b14e..fe868743e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1431,7 +1431,7 @@ return array( $partner_endpoint, $container->get( 'dcc.status-cache' ), $container->get( 'api.helpers.dccapplies' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'api.helper.failure-registry' ) ); }, @@ -1728,7 +1728,7 @@ return array( $environment = $container->get( 'onboarding.environment' ); assert( $environment instanceof Environment ); - $dcc_enabled = $dcc_product_status->dcc_is_active(); + $dcc_enabled = $dcc_product_status->is_active(); $enabled_status_text = esc_html__( 'Status: Available', 'woocommerce-paypal-payments' ); $disabled_status_text = esc_html__( 'Status: Not yet enabled', 'woocommerce-paypal-payments' ); diff --git a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php index 3ca5b9998..d82271b60 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php @@ -9,86 +9,55 @@ declare( strict_types=1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Helper; -use Throwable; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\ProductStatus; +use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus; /** * Class DccProductStatus */ -class DCCProductStatus { +class DCCProductStatus extends ProductStatus { + public const SETTINGS_KEY = 'products_dcc_enabled'; + public const DCC_STATUS_CACHE_KEY = 'dcc_status_cache'; - const DCC_STATUS_CACHE_KEY = 'dcc_status_cache'; + public const SETTINGS_VALUE_ENABLED = 'yes'; + public const SETTINGS_VALUE_DISABLED = 'no'; + public const SETTINGS_VALUE_UNDEFINED = ''; /** * The Cache. * * @var Cache */ - protected $cache; - - /** - * Caches the status for the current load. - * - * @var bool|null - */ - private $current_status_cache; - - /** - * If there was a request failure. - * - * @var bool - */ - private $has_request_failure = false; + protected Cache $cache; /** * The settings. * * @var Settings */ - private $settings; - - /** - * The partners endpoint. - * - * @var PartnersEndpoint - */ - private $partners_endpoint; + private Settings $settings; /** * The dcc applies helper. * * @var DccApplies */ - protected $dcc_applies; - - /** - * The onboarding state. - * - * @var State - */ - private $onboarding_state; - - /** - * The API failure registry - * - * @var FailureRegistry - */ - private $api_failure_registry; + protected DccApplies $dcc_applies; /** * DccProductStatus constructor. * - * @param Settings $settings The Settings. - * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. - * @param Cache $cache The cache. - * @param DccApplies $dcc_applies The dcc applies helper. - * @param State $onboarding_state The onboarding state. + * @param Settings $settings The Settings. + * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. + * @param Cache $cache The cache. + * @param DccApplies $dcc_applies The dcc applies helper. + * @param bool $is_connected The onboarding state. * @param FailureRegistry $api_failure_registry The API failure registry. */ public function __construct( @@ -96,55 +65,31 @@ class DCCProductStatus { PartnersEndpoint $partners_endpoint, Cache $cache, DccApplies $dcc_applies, - State $onboarding_state, + bool $is_connected, FailureRegistry $api_failure_registry ) { - $this->settings = $settings; - $this->partners_endpoint = $partners_endpoint; - $this->cache = $cache; - $this->dcc_applies = $dcc_applies; - $this->onboarding_state = $onboarding_state; - $this->api_failure_registry = $api_failure_registry; + parent::__construct( $is_connected, $partners_endpoint, $api_failure_registry ); + + $this->settings = $settings; + $this->cache = $cache; + $this->dcc_applies = $dcc_applies; } - /** - * Whether the active/subscribed products support DCC. - * - * @return bool - */ - public function dcc_is_active() : bool { - if ( $this->onboarding_state->current_state() < State::STATE_ONBOARDED ) { - return false; - } - + /** {@inheritDoc} */ + protected function check_local_state() : ?bool { if ( $this->cache->has( self::DCC_STATUS_CACHE_KEY ) ) { - return $this->cache->get( self::DCC_STATUS_CACHE_KEY ) === 'true'; + return wc_string_to_bool( $this->cache->get( self::DCC_STATUS_CACHE_KEY ) ); } - if ( $this->current_status_cache === true ) { - return $this->current_status_cache; + if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { + return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } - if ( $this->settings->has( 'products_dcc_enabled' ) && $this->settings->get( 'products_dcc_enabled' ) === true ) { - $this->current_status_cache = true; - return true; - } - - // Check API failure registry to prevent multiple failed API requests. - if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { - $this->has_request_failure = true; - $this->current_status_cache = false; - return $this->current_status_cache; - } - - try { - $seller_status = $this->partners_endpoint->seller_status(); - } catch ( Throwable $error ) { - $this->has_request_failure = true; - $this->current_status_cache = false; - return false; - } + return null; + } + /** {@inheritDoc} */ + protected function check_active_state( SellerStatus $seller_status ) : bool { foreach ( $seller_status->products() as $product ) { if ( ! in_array( $product->vetting_status(), @@ -159,57 +104,37 @@ class DCCProductStatus { } if ( in_array( 'CUSTOM_CARD_PROCESSING', $product->capabilities(), true ) ) { - $this->settings->set( 'products_dcc_enabled', true ); + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); $this->settings->persist(); - $this->current_status_cache = true; - $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); + + $this->cache->set( self::DCC_STATUS_CACHE_KEY, self::SETTINGS_VALUE_ENABLED, MONTH_IN_SECONDS ); + return true; } } - $expiration = MONTH_IN_SECONDS; if ( $this->dcc_applies->for_country_currency() ) { $expiration = 3 * HOUR_IN_SECONDS; + } else { + $expiration = MONTH_IN_SECONDS; } - $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'false', $expiration ); - $this->current_status_cache = false; + $this->cache->set( self::DCC_STATUS_CACHE_KEY, self::SETTINGS_VALUE_DISABLED, $expiration ); + return false; } - /** - * Returns if there was a request failure. - * - * @return bool - */ - public function has_request_failure(): bool { - return $this->has_request_failure; - } - - /** - * Clears the persisted result to force a recheck. - * - * @param Settings|null $settings The settings object. - * We accept a Settings object to don't override other sequential settings that are being updated elsewhere. - * @return void - */ - public function clear( Settings $settings = null ): void { + /** {@inheritDoc} */ + protected function clear_state( Settings $settings = null ): void { if ( null === $settings ) { $settings = $this->settings; } - // Unset check stored in memory. - $this->current_status_cache = null; - - // Unset settings flag. - $settings_key = 'products_dcc_enabled'; - if ( $settings->has( $settings_key ) ) { - $settings->set( $settings_key, false ); + if ( $settings->has( self::SETTINGS_KEY ) ) { + $settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED ); $settings->persist(); } - // Delete cached value. $this->cache->delete( self::DCC_STATUS_CACHE_KEY ); } - } diff --git a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php index bc61c64a9..09f9bd734 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php @@ -168,7 +168,7 @@ class SectionsRenderer { } } - if ( ! $this->dcc_product_status->dcc_is_active() || ! $this->dcc_applies->for_country_currency() ) { + if ( ! $this->dcc_product_status->is_active() || ! $this->dcc_applies->for_country_currency() ) { unset( $sections['ppcp-credit-card-gateway'] ); } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php index ccf91d019..e32cbf0c4 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php @@ -465,7 +465,7 @@ $data_rows_html if ( $this->dcc_applies->for_country_currency() ) { if ( State::STATE_ONBOARDED > $this->state->current_state() ) { $this->render_dcc_onboarding_info(); - } elseif ( ! $this->dcc_product_status->dcc_is_active() ) { + } elseif ( ! $this->dcc_product_status->is_active() ) { $this->render_dcc_not_active_yet(); } } else { diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index c10813c86..95f0eb97d 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -341,7 +341,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul // Update caches. $dcc_status = $c->get( 'wcgateway.helper.dcc-product-status' ); assert( $dcc_status instanceof DCCProductStatus ); - $dcc_status->dcc_is_active(); + $dcc_status->is_active(); $pui_status = $c->get( 'wcgateway.pay-upon-invoice-product-status' ); assert( $pui_status instanceof PayUponInvoiceProductStatus ); @@ -668,7 +668,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul // Performing the full DCCProductStatus check only when on the gateway list page // to avoid sending the API requests all the time. ( $is_our_page || - ( $is_gateways_list_page && $dcc_product_status->dcc_is_active() ) || + ( $is_gateways_list_page && $dcc_product_status->is_active() ) || ( $settings->has( 'products_dcc_enabled' ) && $settings->get( 'products_dcc_enabled' ) ) ) ) { From 5ac70e26c283a0b4ae347921a2b4ae57888ff0b5 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 18:15:40 +0100 Subject: [PATCH 58/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20new=20base=20c?= =?UTF-8?q?lass=20for=20PUI=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-wc-gateway/services.php | 4 +- .../Gateway/PayUponInvoice/PayUponInvoice.php | 4 +- .../Helper/PayUponInvoiceProductStatus.php | 149 +++++------------- .../src/Settings/SectionsRenderer.php | 2 +- .../ppcp-wc-gateway/src/WCGatewayModule.php | 4 +- 5 files changed, 43 insertions(+), 120 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index fe868743e..459dc8b1b 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1515,7 +1515,7 @@ return array( $container->get( 'wcgateway.settings' ), $container->get( 'api.endpoint.partners' ), $container->get( 'pui.status-cache' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'api.helper.failure-registry' ) ); }, @@ -1799,7 +1799,7 @@ return array( $environment = $container->get( 'onboarding.environment' ); assert( $environment instanceof Environment ); - $pui_enabled = $pui_product_status->pui_is_active(); + $pui_enabled = $pui_product_status->is_active(); $enabled_status_text = esc_html__( 'Status: Available', 'woocommerce-paypal-payments' ); $disabled_status_text = esc_html__( 'Status: Not yet enabled', 'woocommerce-paypal-payments' ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php index 94ed7240a..5b1fc0d36 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php @@ -445,7 +445,7 @@ class PayUponInvoice { } if ( - ! $this->pui_product_status->pui_is_active() + ! $this->pui_product_status->is_active() || ! $this->pui_helper->is_checkout_ready_for_pui() ) { unset( $methods[ PayUponInvoiceGateway::ID ] ); @@ -478,7 +478,7 @@ class PayUponInvoice { function() { if ( PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id - && $this->pui_product_status->pui_is_active() + && $this->pui_product_status->is_active() ) { $error_messages = array(); $pui_gateway = WC()->payment_gateways->payment_gateways()[ PayUponInvoiceGateway::ID ]; diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php index dfbcb0380..250138089 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php @@ -9,130 +9,76 @@ declare( strict_types=1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Helper; -use Throwable; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\ProductStatus; +use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus; /** * Class PayUponInvoiceProductStatus */ -class PayUponInvoiceProductStatus { +class PayUponInvoiceProductStatus extends ProductStatus { + public const SETTINGS_KEY = 'products_pui_enabled'; + public const PUI_STATUS_CACHE_KEY = 'pui_status_cache'; - const PUI_STATUS_CACHE_KEY = 'pui_status_cache'; + public const SETTINGS_VALUE_ENABLED = 'yes'; + public const SETTINGS_VALUE_DISABLED = 'no'; + public const SETTINGS_VALUE_UNDEFINED = ''; /** * The Cache. * * @var Cache */ - protected $cache; - - /** - * Caches the status for the current load. - * - * @var bool|null - */ - private $current_status_cache; - - /** - * If there was a request failure. - * - * @var bool - */ - private $has_request_failure = false; + protected Cache $cache; /** * The settings. * * @var Settings */ - private $settings; - - /** - * The partners endpoint. - * - * @var PartnersEndpoint - */ - private $partners_endpoint; - - /** - * The onboarding status - * - * @var State - */ - private $onboarding_state; - - /** - * The API failure registry - * - * @var FailureRegistry - */ - private $api_failure_registry; + private Settings $settings; /** * PayUponInvoiceProductStatus constructor. * - * @param Settings $settings The Settings. - * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. - * @param Cache $cache The cache. - * @param State $onboarding_state The onboarding state. + * @param Settings $settings The Settings. + * @param PartnersEndpoint $partners_endpoint The Partner Endpoint. + * @param Cache $cache The cache. + * @param bool $is_connected The onboarding state. * @param FailureRegistry $api_failure_registry The API failure registry. */ public function __construct( Settings $settings, PartnersEndpoint $partners_endpoint, Cache $cache, - State $onboarding_state, + bool $is_connected, FailureRegistry $api_failure_registry ) { - $this->settings = $settings; - $this->partners_endpoint = $partners_endpoint; - $this->cache = $cache; - $this->onboarding_state = $onboarding_state; - $this->api_failure_registry = $api_failure_registry; + parent::__construct( $is_connected, $partners_endpoint, $api_failure_registry ); + + $this->settings = $settings; + $this->cache = $cache; } - /** - * Whether the active/subscribed products support PUI. - * - * @return bool - */ - public function pui_is_active() : bool { - if ( $this->onboarding_state->current_state() < State::STATE_ONBOARDED ) { - return false; - } - + /** {@inheritDoc} */ + protected function check_local_state() : ?bool { if ( $this->cache->has( self::PUI_STATUS_CACHE_KEY ) ) { - return $this->cache->get( self::PUI_STATUS_CACHE_KEY ) === 'true'; + return wc_string_to_bool( $this->cache->get( self::PUI_STATUS_CACHE_KEY ) ); } - if ( $this->current_status_cache === true ) { - return $this->current_status_cache; - } - if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) === true ) { - $this->current_status_cache = true; - return true; + if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { + return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } - // Check API failure registry to prevent multiple failed API requests. - if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { - $this->has_request_failure = true; - $this->current_status_cache = false; - return $this->current_status_cache; - } - - try { - $seller_status = $this->partners_endpoint->seller_status(); - } catch ( Throwable $error ) { - $this->has_request_failure = true; - $this->current_status_cache = false; - return false; - } + return null; + } + /** {@inheritDoc} */ + protected function check_active_state( SellerStatus $seller_status ) : bool { foreach ( $seller_status->products() as $product ) { if ( $product->name() !== 'PAYMENT_METHODS' ) { continue; @@ -151,52 +97,29 @@ class PayUponInvoiceProductStatus { } if ( in_array( 'PAY_UPON_INVOICE', $product->capabilities(), true ) ) { - $this->settings->set( 'products_pui_enabled', true ); + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); $this->settings->persist(); - $this->current_status_cache = true; - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); + $this->cache->set( self::PUI_STATUS_CACHE_KEY, self::SETTINGS_VALUE_ENABLED, MONTH_IN_SECONDS ); return true; } } - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'false', MONTH_IN_SECONDS ); - $this->current_status_cache = false; + $this->cache->set( self::PUI_STATUS_CACHE_KEY, self::SETTINGS_VALUE_DISABLED, MONTH_IN_SECONDS ); + return false; } - /** - * Returns if there was a request failure. - * - * @return bool - */ - public function has_request_failure(): bool { - return $this->has_request_failure; - } - - /** - * Clears the persisted result to force a recheck. - * - * @param Settings|null $settings The settings object. - * We accept a Settings object to don't override other sequential settings that are being updated elsewhere. - * @return void - */ - public function clear( Settings $settings = null ): void { + /** {@inheritDoc} */ + protected function clear_state( Settings $settings = null ) : void { if ( null === $settings ) { $settings = $this->settings; } - // Unset check stored in memory. - $this->current_status_cache = null; - - // Unset settings flag. - $settings_key = 'products_pui_enabled'; - if ( $settings->has( $settings_key ) ) { - $settings->set( $settings_key, false ); + if ( $settings->has( self::SETTINGS_KEY ) ) { + $settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED ); $settings->persist(); } - // Delete cached value. $this->cache->delete( self::PUI_STATUS_CACHE_KEY ); } - } diff --git a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php index 09f9bd734..f8f05f4ff 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php @@ -176,7 +176,7 @@ class SectionsRenderer { unset( $sections[ Settings::PAY_LATER_TAB_ID ] ); } - if ( ! $this->pui_product_status->pui_is_active() ) { + if ( ! $this->pui_product_status->is_active() ) { unset( $sections[ PayUponInvoiceGateway::ID ] ); } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 95f0eb97d..4885bdbf6 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -345,7 +345,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $pui_status = $c->get( 'wcgateway.pay-upon-invoice-product-status' ); assert( $pui_status instanceof PayUponInvoiceProductStatus ); - $pui_status->pui_is_active(); + $pui_status->is_active(); } ); @@ -686,7 +686,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul if ( 'DE' === $shop_country && ( $is_our_page || - ( $is_gateways_list_page && $pui_product_status->pui_is_active() ) || + ( $is_gateways_list_page && $pui_product_status->is_active() ) || ( $settings->has( 'products_pui_enabled' ) && $settings->get( 'products_pui_enabled' ) ) ) ) { From 5f517620259099adc6eb267055def1c5ad00f9a7 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 18:22:12 +0100 Subject: [PATCH 59/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20More=20appropriate?= =?UTF-8?q?=20hook=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-applepay/src/ApplepayModule.php | 2 +- modules/ppcp-googlepay/src/GooglepayModule.php | 2 +- modules/ppcp-googlepay/src/Helper/ApmProductStatus.php | 1 - modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php | 2 +- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index dc7b3cf11..ea58b4d89 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -183,7 +183,7 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule ); add_filter( - 'woocommerce_paypal_payments_rest_common_merchant_data', + 'woocommerce_paypal_payments_rest_common_merchant_features', function( array $features ) use ( $c ): array { $product_status = $c->get( 'applepay.apple-product-status' ); assert( $product_status instanceof AppleProductStatus ); diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index dd7320011..a50408bd3 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -233,7 +233,7 @@ class GooglepayModule implements ServiceModule, ExtendingModule, ExecutableModul ); add_filter( - 'woocommerce_paypal_payments_rest_common_merchant_data', + 'woocommerce_paypal_payments_rest_common_merchant_features', function ( array $features ) use ( $c ): array { $product_status = $c->get( 'googlepay.helpers.apm-product-status' ); assert( $product_status instanceof ApmProductStatus ); diff --git a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php index 1da8379df..dbe812837 100644 --- a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php +++ b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php @@ -60,7 +60,6 @@ class ApmProductStatus extends ProductStatus { return $status_override; } - // Check if status was checked on previous requests. if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); } diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 35af6c093..e453a1a87 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -218,7 +218,7 @@ class CommonRestEndpoint extends RestEndpoint { if ( $this->settings->is_merchant_connected() ) { $extra_data['features'] = apply_filters( - 'woocommerce_paypal_payments_rest_common_merchant_data', + 'woocommerce_paypal_payments_rest_common_merchant_features', array(), ); } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 4885bdbf6..fa2c76cc8 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -550,7 +550,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul ); add_filter( - 'woocommerce_paypal_payments_rest_common_merchant_data', + 'woocommerce_paypal_payments_rest_common_merchant_features', static function ( array $features ) use ( $c ) : array { try { $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); From 49665f680f41bfab10fb67d03b1c1fa378416fce Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 18:37:01 +0100 Subject: [PATCH 60/78] =?UTF-8?q?=E2=9C=A8=20New=20Local=20APM=20product?= =?UTF-8?q?=20status=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services.php | 9 ++ .../src/LocalApmProductStatus.php | 95 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php diff --git a/modules/ppcp-local-alternative-payment-methods/services.php b/modules/ppcp-local-alternative-payment-methods/services.php index c0a66c11e..5408288c0 100644 --- a/modules/ppcp-local-alternative-payment-methods/services.php +++ b/modules/ppcp-local-alternative-payment-methods/services.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\LocalApmProductStatus; return array( 'ppcp-local-apms.url' => static function ( ContainerInterface $container ): string { @@ -67,6 +68,14 @@ return array( ), ); }, + 'ppcp-local-apms.product-status' => static function ( ContainerInterface $container ): LocalApmProductStatus { + return new LocalApmProductStatus( + $container->get( 'wcgateway.settings' ), + $container->get( 'api.endpoint.partners' ), + $container->get( 'settings.flag.is-connected' ), + $container->get( 'api.helper.failure-registry' ) + ); + }, 'ppcp-local-apms.bancontact.wc-gateway' => static function ( ContainerInterface $container ): BancontactGateway { return new BancontactGateway( $container->get( 'api.endpoint.orders' ), diff --git a/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php b/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php new file mode 100644 index 000000000..2a89146be --- /dev/null +++ b/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php @@ -0,0 +1,95 @@ +settings = $settings; + } + + /** {@inheritDoc} */ + protected function check_local_state() : ?bool { + if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) { + return wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) ); + } + + return null; + } + + /** {@inheritDoc} */ + protected function check_active_state( SellerStatus $seller_status ) : bool { + $has_capability = false; + + foreach ( $seller_status->products() as $product ) { + if ( $product->name() === 'PAYMENT_METHODS' ) { + $has_capability = true; + break; + } + } + + if ( $has_capability ) { + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); + } else { + $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED ); + } + $this->settings->persist(); + + return $has_capability; + } + + /** {@inheritDoc} */ + protected function clear_state( Settings $settings = null ) : void { + if ( null === $settings ) { + $settings = $this->settings; + } + + if ( $settings->has( self::SETTINGS_KEY ) ) { + $settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED ); + $settings->persist(); + } + } +} From 44cb17f1cc0281bdf26a490621ccf18eeb1be0e1 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 30 Jan 2025 18:40:43 +0100 Subject: [PATCH 61/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20More=20robust=20feat?= =?UTF-8?q?ure=20checks=20in=20new=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-wc-gateway/src/WCGatewayModule.php | 71 ++++++------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index fa2c76cc8..f8b54f534 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -63,6 +63,7 @@ use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; +use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\LocalApmProductStatus; /** * Class WcGatewayModule @@ -552,63 +553,35 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul add_filter( 'woocommerce_paypal_payments_rest_common_merchant_features', static function ( array $features ) use ( $c ) : array { - try { - $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); - assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); + $is_connected = $c->get( 'settings.flag.is-connected' ); - $reference_transactions_enabled = $billing_agreements_endpoint->reference_transaction_enabled(); - $features['save_paypal_and_venmo'] = array( - 'enabled' => $reference_transactions_enabled, - ); - } catch ( Exception $ex ) { - $features['save_paypal_and_venmo'] = array( - 'enabled' => false, - ); + if ( ! $is_connected ) { + return $features; } - try { - $dcc_product_status = $c->get( 'wcgateway.helper.dcc-product-status' ); - assert( $dcc_product_status instanceof DCCProductStatus ); - $dcc_enabled = $dcc_product_status->dcc_is_active(); - $features['advanced_credit_and_debit_cards'] = array( - 'enabled' => $dcc_enabled, - ); - } catch ( Exception $ex ) { - $features['advanced_credit_and_debit_cards'] = array( - 'enabled' => false, - ); - } + $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); + assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); - try { - // TODO: The `seller_status()` call throws a PayPalApiException. Why? - $partners_endpoint = $c->get( 'api.endpoint.partners' ); - assert( $partners_endpoint instanceof PartnersEndpoint ); - $seller_status = $partners_endpoint->seller_status(); + $dcc_product_status = $c->get( 'wcgateway.helper.dcc-product-status' ); + assert( $dcc_product_status instanceof DCCProductStatus ); - $apms_enabled = false; - foreach ( $seller_status->products() as $product ) { - if ( $product->name() === 'PAYMENT_METHODS' ) { - $apms_enabled = true; - break; - } - } + $apms_product_status = $c->get( 'ppcp-local-apms.product-status' ); + assert( $apms_product_status instanceof LocalApmProductStatus ); - $features['alternative_payment_methods'] = array( - 'enabled' => $apms_enabled, - ); + $features['save_paypal_and_venmo'] = array( + 'enabled' => $billing_agreements_endpoint->reference_transaction_enabled(), + ); - $features['pay_later_messaging'] = array( - 'enabled' => true, - ); - } catch ( Exception $ex ) { - $features['alternative_payment_methods'] = array( - 'enabled' => false, - ); + $features['advanced_credit_and_debit_cards'] = array( + 'enabled' => $dcc_product_status->is_active(), + ); - $features['pay_later_messaging'] = array( - 'enabled' => false, - ); - } + $features['alternative_payment_methods'] = array( + 'enabled' => $apms_product_status->is_active(), + ); + + // When local APMs are available, then PayLater messaging is also available. + $features['pay_later_messaging'] = $features['alternative_payment_methods']; return $features; } From 2387902a18a9e6bc42834585d01ad941836b661c Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Fri, 31 Jan 2025 10:46:16 +0100 Subject: [PATCH 62/78] Change UK to GB --- .../resources/js/utils/countryPriceInfo.js | 126 +++++++++--------- tests/PHPUnit/ApiClient/Entity/PayerTest.php | 10 +- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js index c5cf52a3e..e32d8e6ce 100644 --- a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js +++ b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js @@ -2,139 +2,139 @@ export const countryPriceInfo = { US: { fixedFee: { USD: 0.49, - GBP: 0.39, - CAD: 0.59, - AUD: 0.59, - EUR: 0.39, + GBP: 0.39, + CAD: 0.59, + AUD: 0.59, + EUR: 0.39, }, checkout: 3.49, - plater: 4.99, - ccf: { - percentage: 2.59, - fixedFee: 0.29, - }, + plater: 4.99, + ccf: { + percentage: 2.59, + fixedFee: 0.29, + }, dw: { - percentage: 2.59, - fixedFee: 0.29, - }, + percentage: 2.59, + fixedFee: 0.29, + }, apm: { - percentage: 2.89, - fixedFee: 0.29, - }, - fast: { - percentage: 2.59, - fixedFee: 0.29, - }, + percentage: 2.89, + fixedFee: 0.29, + }, + fast: { + percentage: 2.59, + fixedFee: 0.29, + }, standardCardFields: 2.99, }, - UK: { + GB: { fixedFee: { GPB: 0.3, - USD: 0.3, - CAD: 0.3, - AUD: 0.3, - EUR: 0.35, + USD: 0.3, + CAD: 0.3, + AUD: 0.3, + EUR: 0.35, }, checkout: 2.9, - plater: 2.9, + plater: 2.9, ccf: 1.2, dw: 1.2, - fast: 1.2, + fast: 1.2, apm: 1.2, standardCardFields: 1.2, }, CA: { fixedFee: { CAD: 0.3, - USD: 0.3, - GBP: 0.2, - AUD: 0.3, - EUR: 0.35, + USD: 0.3, + GBP: 0.2, + AUD: 0.3, + EUR: 0.35, }, checkout: 2.9, ccf: 2.7, dw: 2.7, - fast: 2.7, + fast: 2.7, apm: 2.9, standardCardFields: 2.9, }, AU: { fixedFee: { AUD: 0.3, - USD: 0.3, - GBP: 0.2, - CAD: 0.3, - EUR: 0.35, + USD: 0.3, + GBP: 0.2, + CAD: 0.3, + EUR: 0.35, }, checkout: 2.6, - plater: 2.6, + plater: 2.6, ccf: 1.75, dw: 1.75, - fast: 1.75, + fast: 1.75, apm: 2.6, standardCardFields: 2.6, }, FR: { fixedFee: { EUR: 0.35, - USD: 0.3, - GBP: 0.3, - CAD: 0.3, - AUD: 0.3, + USD: 0.3, + GBP: 0.3, + CAD: 0.3, + AUD: 0.3, }, checkout: 2.9, - plater: 2.9, + plater: 2.9, ccf: 1.2, dw: 1.2, - fast: 1.2, + fast: 1.2, apm: 1.2, standardCardFields: 1.2, }, IT: { fixedFee: { - EUR: 0.35, - USD: 0.3, - GBP: 0.3, - CAD: 0.3, - AUD: 0.3, + EUR: 0.35, + USD: 0.3, + GBP: 0.3, + CAD: 0.3, + AUD: 0.3, }, checkout: 3.4, - plater: 3.4, + plater: 3.4, ccf: 1.2, dw: 1.2, - fast: 1.2, + fast: 1.2, apm: 1.2, standardCardFields: 1.2, }, DE: { fixedFee: { EUR: 0.39, - USD: 0.49, - GBP: 0.29, - CAD: 0.59, - AUD: 0.59, + USD: 0.49, + GBP: 0.29, + CAD: 0.59, + AUD: 0.59, }, checkout: 2.99, - plater: 2.99, + plater: 2.99, ccf: 2.99, dw: 2.99, - fast: 2.99, + fast: 2.99, apm: 2.99, standardCardFields: 2.99, }, ES: { fixedFee: { - EUR: 0.35, - USD: 0.3, - GBP: 0.3, - CAD: 0.3, - AUD: 0.3, + EUR: 0.35, + USD: 0.3, + GBP: 0.3, + CAD: 0.3, + AUD: 0.3, }, checkout: 2.9, - plater: 2.9, + plater: 2.9, ccf: 1.2, dw: 1.2, - fast: 1.2, + fast: 1.2, apm: 1.2, standardCardFields: 1.2, }, diff --git a/tests/PHPUnit/ApiClient/Entity/PayerTest.php b/tests/PHPUnit/ApiClient/Entity/PayerTest.php index affdc206d..9efc603d5 100644 --- a/tests/PHPUnit/ApiClient/Entity/PayerTest.php +++ b/tests/PHPUnit/ApiClient/Entity/PayerTest.php @@ -18,7 +18,7 @@ class PayerTest extends TestCase ->andReturn(['address']); $address ->expects('country_code') - ->andReturn('UK'); + ->andReturn('GB'); $phone = Mockery::mock(PhoneWithType::class); $phone ->expects('to_array') @@ -70,7 +70,7 @@ class PayerTest extends TestCase ->andReturn(['address']); $address ->expects('country_code') - ->andReturn('UK'); + ->andReturn('GB'); $phone = Mockery::mock(PhoneWithType::class); $phone ->expects('to_array') @@ -110,7 +110,7 @@ class PayerTest extends TestCase ->andReturn(['address']); $address ->expects('country_code') - ->andReturn('UK'); + ->andReturn('GB'); $phone = null; $taxInfo = Mockery::mock(PayerTaxInfo::class); $taxInfo @@ -147,7 +147,7 @@ class PayerTest extends TestCase ->andReturn(['address']); $address ->expects('country_code') - ->andReturn('UK'); + ->andReturn('GB'); $phone = Mockery::mock(PhoneWithType::class); $phone ->expects('to_array') @@ -184,7 +184,7 @@ class PayerTest extends TestCase ->andReturn(['address']); $address ->expects('country_code') - ->andReturn('UK'); + ->andReturn('GB'); $phone = Mockery::mock(PhoneWithType::class); $phone ->expects('to_array') From 401e2556567522c79a91e04d40d8fd154c0f2aab Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 12:02:30 +0100 Subject: [PATCH 63/78] =?UTF-8?q?=F0=9F=94=A7=20Add=20settings-mapping=20f?= =?UTF-8?q?or=20=E2=80=9Cmerchant=5Fid=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index 7982366e0..e5f6bcd74 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -145,6 +145,7 @@ return array( * the credentials are used for. */ array( + 'merchant_id' => 'merchant_id', 'is_sandbox' => 'sandbox_merchant', 'live_client_id' => 'client_id', 'live_client_secret' => 'client_secret', From 3d185090e26bdd9aac3779077f89aee867486bde Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 12:06:12 +0100 Subject: [PATCH 64/78] =?UTF-8?q?=F0=9F=90=9B=20Cache=20the=20SellerStatus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Helper/ProductStatus.php | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-api-client/src/Helper/ProductStatus.php b/modules/ppcp-api-client/src/Helper/ProductStatus.php index eeda4ad22..bab84ed24 100644 --- a/modules/ppcp-api-client/src/Helper/ProductStatus.php +++ b/modules/ppcp-api-client/src/Helper/ProductStatus.php @@ -22,6 +22,14 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; * Base class to check the eligibility of a product for the current merchant. */ abstract class ProductStatus { + /** + * Caches the SellerStatus API response to avoid duplicate API calls + * during the same request. + * + * @var ?SellerStatus + */ + private static ?SellerStatus $seller_status = null; + /** * The current status stored in memory. * @@ -147,19 +155,21 @@ abstract class ProductStatus { /** * Fetches the seller-status object from the PayPal merchant API. * - * TODO: We might cache the SellerStatus, as it's usually accessed multiple times during one request. - * * @return SellerStatus * @throws RuntimeException When the check failed. */ protected function get_seller_status_object() : SellerStatus { - // Check API failure registry to prevent multiple failed API requests. - if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) { - throw new RuntimeException( 'Timeout for re-check not reached yet' ); + if ( null === self::$seller_status ) { + // Check API failure registry to prevent multiple failed API requests. + if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, MINUTE_IN_SECONDS ) ) { + throw new RuntimeException( 'Timeout for re-check not reached yet' ); + } + + // Request seller status via PayPal API, might throw an Exception. + self::$seller_status = $this->partners_endpoint->seller_status(); } - // Request seller status via PayPal API, might throw an Exception. - return $this->partners_endpoint->seller_status(); + return self::$seller_status; } /** From d186677a581ea53adf37cc56b89b7090f724d14c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 12:37:34 +0100 Subject: [PATCH 65/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20settings-?= =?UTF-8?q?map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/services.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index e5f6bcd74..91370dff0 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -128,13 +128,6 @@ return array( } return array( - new SettingsMap( - $container->get( 'settings.data.general' ), - array( - 'client_id' => 'client_id', - 'client_secret' => 'client_secret', - ) - ), new SettingsMap( $container->get( 'settings.data.general' ), /** @@ -146,6 +139,8 @@ return array( */ array( 'merchant_id' => 'merchant_id', + 'client_id' => 'client_id', + 'client_secret' => 'client_secret', 'is_sandbox' => 'sandbox_merchant', 'live_client_id' => 'client_id', 'live_client_secret' => 'client_secret', From 90d2c77a5a37fdc7f86900718f70c25091b49894 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:34:30 +0100 Subject: [PATCH 66/78] =?UTF-8?q?=F0=9F=9A=9A=20Extract=20Environment=20cl?= =?UTF-8?q?ass=20to=20gateway=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-applepay/extensions.php | 2 +- modules/ppcp-applepay/services.php | 2 +- modules/ppcp-applepay/src/ApplepayModule.php | 2 +- modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php | 2 +- modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 2 +- modules/ppcp-button/services.php | 2 +- modules/ppcp-button/src/Assets/SmartButton.php | 2 +- modules/ppcp-googlepay/services.php | 2 +- modules/ppcp-googlepay/src/Assets/Button.php | 2 +- .../ppcp-onboarding/src/Assets/OnboardingAssets.php | 2 +- .../src/PayPalSubscriptionsModule.php | 2 +- .../ppcp-vaulting/src/VaultedCreditCardHandler.php | 2 +- modules/ppcp-wc-gateway/extensions.php | 2 +- modules/ppcp-wc-gateway/services.php | 2 +- modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php | 2 +- .../ppcp-wc-gateway/src/Assets/SettingsPageAssets.php | 2 +- .../ppcp-wc-gateway/src/Gateway/CardButtonGateway.php | 3 +-- .../ppcp-wc-gateway/src/Gateway/CreditCardGateway.php | 2 +- .../ppcp-wc-gateway/src/Gateway/OXXO/OXXOGateway.php | 2 +- modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 2 +- .../Gateway/PayUponInvoice/PayUponInvoiceGateway.php | 2 +- .../src/Helper}/Environment.php | 11 +++++------ .../ppcp-wc-gateway/src/Processor/OrderMetaTrait.php | 2 +- .../ppcp-wc-gateway/src/Processor/OrderProcessor.php | 2 +- .../src/Settings/Fields/connection-tab-fields.php | 2 +- modules/ppcp-wc-subscriptions/src/RenewalHandler.php | 2 +- .../src/Status/Assets/WebhooksStatusPageAssets.php | 2 +- 28 files changed, 32 insertions(+), 34 deletions(-) rename modules/{ppcp-onboarding/src => ppcp-wc-gateway/src/Helper}/Environment.php (79%) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 5eb6e47d6..53068c506 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -10,7 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Applepay; use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 963891f48..feb087731 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -19,7 +19,7 @@ use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary; use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice; use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index ea58b4d89..8015e0a4d 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -17,7 +17,7 @@ use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary; use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index 78bafdae3..db4d5438e 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -13,7 +13,7 @@ use WC_Payment_Gateway; use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType; use WooCommerce\PayPalCommerce\Axo\FrontendLoggerEndpoint; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index d02e27355..0263adb3c 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -12,7 +12,7 @@ namespace WooCommerce\PayPalCommerce\Axo\Assets; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter; use WooCommerce\PayPalCommerce\Axo\FrontendLoggerEndpoint; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index cd0ec8579..3f699a44a 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -19,7 +19,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewaySettingsRendererTrait; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 6844a08d0..5a489d330 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -35,7 +35,7 @@ use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index fd1b93404..81ced3a68 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -36,7 +36,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Button\Helper\DisabledFundingSources; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\PayLaterWCBlocks\PayLaterWCBlocksModule; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index 18c48f3ad..f92aa0bd8 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint; use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus; use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; diff --git a/modules/ppcp-googlepay/src/Assets/Button.php b/modules/ppcp-googlepay/src/Assets/Button.php index 1d85b982f..f3ff90611 100644 --- a/modules/ppcp-googlepay/src/Assets/Button.php +++ b/modules/ppcp-googlepay/src/Assets/Button.php @@ -16,7 +16,7 @@ use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint; use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; diff --git a/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php b/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php index 663bdfee1..959aad775 100644 --- a/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php +++ b/modules/ppcp-onboarding/src/Assets/OnboardingAssets.php @@ -11,7 +11,7 @@ namespace WooCommerce\PayPalCommerce\Onboarding\Assets; use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 48c3a71af..0d55965f7 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -21,7 +21,7 @@ use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; diff --git a/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php b/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php index 3261e4b8e..134d9f4e7 100644 --- a/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php +++ b/modules/ppcp-vaulting/src/VaultedCreditCardHandler.php @@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; diff --git a/modules/ppcp-wc-gateway/extensions.php b/modules/ppcp-wc-gateway/extensions.php index fe2de006b..77fea0abd 100644 --- a/modules/ppcp-wc-gateway/extensions.php +++ b/modules/ppcp-wc-gateway/extensions.php @@ -9,7 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\WooCommerce\Logging\Logger\NullLogger; use WooCommerce\WooCommerce\Logging\Logger\WooCommerceLogger; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 459dc8b1b..1bff438dd 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -21,7 +21,7 @@ use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers; use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Settings\SettingsModule; diff --git a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php index 074a57c2e..3ee217887 100644 --- a/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/FraudNetAssets.php @@ -10,7 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Assets; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet; diff --git a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php index bfda515ac..4ab12b13b 100644 --- a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php @@ -11,7 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Assets; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; diff --git a/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php index ffb0fe55c..846f8e7e3 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php @@ -13,8 +13,7 @@ use Exception; use Psr\Log\LoggerInterface; use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; -use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index a5e897548..7a41a40ef 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; diff --git a/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXOGateway.php b/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXOGateway.php index 009018558..dfc2d6a24 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXOGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/OXXO/OXXOGateway.php @@ -17,7 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait; diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 5ffbeec9d..192955d80 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php index dbd6629bd..ecd6c1df1 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php @@ -16,7 +16,7 @@ use WC_Payment_Gateway; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; diff --git a/modules/ppcp-onboarding/src/Environment.php b/modules/ppcp-wc-gateway/src/Helper/Environment.php similarity index 79% rename from modules/ppcp-onboarding/src/Environment.php rename to modules/ppcp-wc-gateway/src/Helper/Environment.php index 9bd136ed0..9c4599333 100644 --- a/modules/ppcp-onboarding/src/Environment.php +++ b/modules/ppcp-wc-gateway/src/Helper/Environment.php @@ -1,13 +1,13 @@ Date: Mon, 3 Feb 2025 13:36:45 +0100 Subject: [PATCH 67/78] =?UTF-8?q?=E2=9C=A8=20Create=20new=20environment=20?= =?UTF-8?q?detection=20helper=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Helper/Environment.php | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/Environment.php b/modules/ppcp-wc-gateway/src/Helper/Environment.php index 9c4599333..a603e9179 100644 --- a/modules/ppcp-wc-gateway/src/Helper/Environment.php +++ b/modules/ppcp-wc-gateway/src/Helper/Environment.php @@ -5,7 +5,7 @@ * @package WooCommerce\PayPalCommerce\WcGateway\Helper */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Helper; @@ -16,20 +16,27 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; */ class Environment { + /** + * Name of the production environment. + */ public const PRODUCTION = 'production'; - public const SANDBOX = 'sandbox'; + + /** + * Name of the sandbox environment. + */ + public const SANDBOX = 'sandbox'; /** * The Settings. * * @var ContainerInterface */ - private $settings; + private ContainerInterface $settings; /** * Environment constructor. * - * @param ContainerInterface $settings The Settings. + * @param ContainerInterface $settings The settings. */ public function __construct( ContainerInterface $settings ) { $this->settings = $settings; @@ -40,7 +47,7 @@ class Environment { * * @return string */ - public function current_environment(): string { + public function current_environment() : string { return ( $this->settings->has( 'sandbox_on' ) && $this->settings->get( 'sandbox_on' ) ) ? self::SANDBOX : self::PRODUCTION; @@ -53,7 +60,25 @@ class Environment { * * @return bool */ - public function current_environment_is( string $environment ): bool { + public function current_environment_is( string $environment ) : bool { return $this->current_environment() === $environment; } + + /** + * Returns whether the current environment is sandbox. + * + * @return bool + */ + public function is_sandbox() : bool { + return $this->current_environment_is( self::SANDBOX ); + } + + /** + * Returns whether the current environment is production. + * + * @return bool + */ + public function is_production() : bool { + return $this->current_environment_is( self::PRODUCTION ); + } } From d6310f30827f3b0b7761c43bae8ce6da584b291b Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:38:12 +0100 Subject: [PATCH 68/78] =?UTF-8?q?=E2=9C=A8=20Support=20Environment=20objec?= =?UTF-8?q?t=20in=20EnvironmentConfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-wc-gateway/src/Helper/EnvironmentConfig.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/EnvironmentConfig.php b/modules/ppcp-wc-gateway/src/Helper/EnvironmentConfig.php index 1542de783..c05776603 100644 --- a/modules/ppcp-wc-gateway/src/Helper/EnvironmentConfig.php +++ b/modules/ppcp-wc-gateway/src/Helper/EnvironmentConfig.php @@ -70,10 +70,14 @@ class EnvironmentConfig { /** * Get the value for the specified environment. * - * @param bool $for_sandbox Whether to get the sandbox value. + * @param bool|Environment $for_sandbox Whether to get the sandbox value. * @return T The value for the specified environment. */ - public function get_value( bool $for_sandbox = false ) { + public function get_value( $for_sandbox = false ) { + if ( $for_sandbox instanceof Environment ) { + return $for_sandbox->is_sandbox() ? $this->sandbox_value : $this->production_value; + } + return $for_sandbox ? $this->sandbox_value : $this->production_value; } } From 5c7fc56d127a7b0610818967311a4d1c06220275 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:39:16 +0100 Subject: [PATCH 69/78] =?UTF-8?q?=F0=9F=90=9B=20Add=20missing=20`use`=20st?= =?UTF-8?q?atement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-onboarding/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 85f3d4219..c1bccbba1 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -20,6 +20,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingSendOnlyNoticeRenderer; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( 'api.sandbox-host' => static function ( ContainerInterface $container ): string { From 0b77ff9e55739abf79dff6da33c0fe4aec00c0cf Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:40:12 +0100 Subject: [PATCH 70/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20bearer-log?= =?UTF-8?q?ic=20from=20onboarding=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/services.php | 6 ++++++ modules/ppcp-onboarding/services.php | 31 ---------------------------- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index b2f489da5..244690926 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -116,6 +116,12 @@ return array( return 'WC-'; }, 'api.bearer' => static function ( ContainerInterface $container ): Bearer { + $is_connected = $container->get( 'settings.flag.is-connected' ); + + if ( ! $is_connected ) { + return new ConnectBearer(); + } + return new PayPalBearer( $container->get( 'api.paypal-bearer-cache' ), $container->get( 'api.host' ), diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index c1bccbba1..8198f3a7c 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -11,9 +11,6 @@ namespace WooCommerce\PayPalCommerce\Onboarding; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\Onboarding\Assets\OnboardingAssets; use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint; @@ -86,34 +83,6 @@ return array( return $container->get( 'api.paypal-website-url-production' ); }, - - 'api.bearer' => static function ( ContainerInterface $container ): Bearer { - - $state = $container->get( 'onboarding.state' ); - - /** - * The State. - * - * @var State $state - */ - if ( $state->current_state() < State::STATE_ONBOARDED ) { - return new ConnectBearer(); - } - $cache = new Cache( 'ppcp-paypal-bearer' ); - $key = $container->get( 'api.key' ); - $secret = $container->get( 'api.secret' ); - $host = $container->get( 'api.host' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); - $settings = $container->get( 'wcgateway.settings' ); - return new PayPalBearer( - $cache, - $host, - $key, - $secret, - $logger, - $settings - ); - }, 'onboarding.state' => function( ContainerInterface $container ) : State { $settings = $container->get( 'wcgateway.settings' ); return new State( $settings ); From 81ca5dd00e646b89d601331e3853ad32b6258abd Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:42:26 +0100 Subject: [PATCH 71/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20cache=20servic?= =?UTF-8?q?e=20instead=20of=20direct=20creation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/services.php | 2 +- modules/ppcp-onboarding/services.php | 4 ++-- modules/ppcp-wc-gateway/services.php | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 244690926..bee10d72f 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -817,7 +817,7 @@ return array( return new OrderHelper(); }, 'api.helper.order-transient' => static function( ContainerInterface $container ): OrderTransient { - $cache = new Cache( 'ppcp-paypal-bearer' ); + $cache = $container->get( 'api.paypal-bearer-cache' ); $purchase_unit_sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' ); return new OrderTransient( $cache, $purchase_unit_sanitizer ); }, diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 8198f3a7c..7756b69db 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -129,8 +129,8 @@ return array( $login_seller_sandbox = $container->get( 'api.endpoint.login-seller-sandbox' ); $partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' ); $settings = $container->get( 'wcgateway.settings' ); - $cache = new Cache( 'ppcp-paypal-bearer' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); + $cache = $container->get( 'api.paypal-bearer-cache' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new LoginSellerEndpoint( $request_data, $login_seller_production, diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 1bff438dd..77c6438e3 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -570,10 +570,10 @@ return array( $settings = $container->get( 'wcgateway.settings' ); $fields = $container->get( 'wcgateway.settings.fields' ); $webhook_registrar = $container->get( 'webhook.registrar' ); - $state = $container->get( 'onboarding.state' ); - $cache = new Cache( 'ppcp-paypal-bearer' ); - $bearer = $container->get( 'api.bearer' ); - $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' ); + $state = $container->get( 'onboarding.state' ); + $cache = $container->get( 'api.paypal-bearer-cache' ); + $bearer = $container->get( 'api.bearer' ); + $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' ); $signup_link_cache = $container->get( 'onboarding.signup-link-cache' ); $signup_link_ids = $container->get( 'onboarding.signup-link-ids' ); $pui_status_cache = $container->get( 'pui.status-cache' ); From aa0a8d41828c11f782891a45676f85633864b85f Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 13:49:16 +0100 Subject: [PATCH 72/78] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20Deprecate=20`curr?= =?UTF-8?q?ent=5Fenvironment=5Fis()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-wc-gateway/src/Helper/Environment.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/Environment.php b/modules/ppcp-wc-gateway/src/Helper/Environment.php index a603e9179..143ebd843 100644 --- a/modules/ppcp-wc-gateway/src/Helper/Environment.php +++ b/modules/ppcp-wc-gateway/src/Helper/Environment.php @@ -56,6 +56,9 @@ class Environment { /** * Detect whether the current environment equals $environment * + * @deprecated Use the is_sandbox() and is_production() methods instead. + * These methods provide better encapsulation, are less error-prone, + * and improve code readability by removing the need to pass environment constants. * @param string $environment The value to check against. * * @return bool @@ -70,7 +73,7 @@ class Environment { * @return bool */ public function is_sandbox() : bool { - return $this->current_environment_is( self::SANDBOX ); + return $this->current_environment() === self::SANDBOX; } /** @@ -79,6 +82,6 @@ class Environment { * @return bool */ public function is_production() : bool { - return $this->current_environment_is( self::PRODUCTION ); + return $this->current_environment() === self::PRODUCTION; } } From 1c70334721159a53c6455a92636443544efc6070 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:21:03 +0100 Subject: [PATCH 73/78] =?UTF-8?q?=F0=9F=90=9B=20Add=20missing=20`use`=20st?= =?UTF-8?q?atement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-onboarding/src/State.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-onboarding/src/State.php b/modules/ppcp-onboarding/src/State.php index 492d180b6..59963dcdf 100644 --- a/modules/ppcp-onboarding/src/State.php +++ b/modules/ppcp-onboarding/src/State.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Onboarding; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; /** * Class State From abb80f99575e2e935d7e89c16a8b6602901c8472 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:22:44 +0100 Subject: [PATCH 74/78] =?UTF-8?q?=F0=9F=90=9B=20Rename=20incorrect=20setti?= =?UTF-8?q?ngs=20mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index 91370dff0..65ee1e3d8 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -141,7 +141,7 @@ return array( 'merchant_id' => 'merchant_id', 'client_id' => 'client_id', 'client_secret' => 'client_secret', - 'is_sandbox' => 'sandbox_merchant', + 'sandbox_on' => 'sandbox_merchant', 'live_client_id' => 'client_id', 'live_client_secret' => 'client_secret', 'live_merchant_id' => 'merchant_id', From 1286d0866e5e3069a3a65e1151838728bdd62a92 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:34:25 +0100 Subject: [PATCH 75/78] =?UTF-8?q?=F0=9F=9A=9A=20Extract=20services=20from?= =?UTF-8?q?=20onboarding=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/services.php | 31 ++++++++++++++++++++++++++++ modules/ppcp-onboarding/services.php | 29 -------------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index bee10d72f..a0d04ae3f 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -80,6 +80,8 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer; use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig; +use WooCommerce\PayPalCommerce\Onboarding\State; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( 'api.host' => function( ContainerInterface $container ) : string { @@ -933,4 +935,33 @@ return array( $container->get( 'api.endpoint.partner-referrals-sandbox' ) ); }, + 'api.sandbox-host' => static function ( ContainerInterface $container ): string { + + $state = $container->get( 'onboarding.state' ); + + /** + * The State object. + * + * @var State $state + */ + if ( $state->current_state() >= State::STATE_ONBOARDED ) { + return PAYPAL_SANDBOX_API_URL; + } + return CONNECT_WOO_SANDBOX_URL; + }, + 'api.production-host' => static function ( ContainerInterface $container ): string { + + $state = $container->get( 'onboarding.state' ); + + /** + * The Environment and State variables. + * + * @var Environment $environment + * @var State $state + */ + if ( $state->current_state() >= State::STATE_ONBOARDED ) { + return PAYPAL_API_URL; + } + return CONNECT_WOO_URL; + }, ); diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 7756b69db..17c9ba8c0 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -20,35 +20,6 @@ use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( - 'api.sandbox-host' => static function ( ContainerInterface $container ): string { - - $state = $container->get( 'onboarding.state' ); - - /** - * The State object. - * - * @var State $state - */ - if ( $state->current_state() >= State::STATE_ONBOARDED ) { - return PAYPAL_SANDBOX_API_URL; - } - return CONNECT_WOO_SANDBOX_URL; - }, - 'api.production-host' => static function ( ContainerInterface $container ): string { - - $state = $container->get( 'onboarding.state' ); - - /** - * The Environment and State variables. - * - * @var Environment $environment - * @var State $state - */ - if ( $state->current_state() >= State::STATE_ONBOARDED ) { - return PAYPAL_API_URL; - } - return CONNECT_WOO_URL; - }, 'api.host' => static function ( ContainerInterface $container ): string { $environment = $container->get( 'onboarding.environment' ); From af6cc7e56b0511199ff603e65eff73ac4bf27cc9 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:39:12 +0100 Subject: [PATCH 76/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20new=20connecti?= =?UTF-8?q?on-check=20for=20API=20host=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/services.php | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index a0d04ae3f..e7cc9e878 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -936,32 +936,21 @@ return array( ); }, 'api.sandbox-host' => static function ( ContainerInterface $container ): string { + $is_connected = $container->get( 'settings.flag.is-connected' ); - $state = $container->get( 'onboarding.state' ); - - /** - * The State object. - * - * @var State $state - */ - if ( $state->current_state() >= State::STATE_ONBOARDED ) { + if ( $is_connected ) { return PAYPAL_SANDBOX_API_URL; } + return CONNECT_WOO_SANDBOX_URL; }, 'api.production-host' => static function ( ContainerInterface $container ): string { + $is_connected = $container->get( 'settings.flag.is-connected' ); - $state = $container->get( 'onboarding.state' ); - - /** - * The Environment and State variables. - * - * @var Environment $environment - * @var State $state - */ - if ( $state->current_state() >= State::STATE_ONBOARDED ) { + if ( $is_connected ) { return PAYPAL_API_URL; } + return CONNECT_WOO_URL; }, ); From 04aeb6acf29a1e532809c0014802cf3c138aded2 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:47:52 +0100 Subject: [PATCH 77/78] =?UTF-8?q?=E2=9C=85=20Update=20`use`=20statements?= =?UTF-8?q?=20in=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php | 2 +- tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php | 2 +- tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php | 2 +- .../Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php | 2 +- tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php | 2 +- tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php index c7d176c8e..c084559a4 100644 --- a/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php +++ b/tests/PHPUnit/Vaulting/VaultedCreditCardHandlerTest.php @@ -19,7 +19,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; diff --git a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php index 1489da4ff..72b90e030 100644 --- a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php @@ -9,7 +9,7 @@ use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\TestCase; diff --git a/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php index 0f70f801c..30e106152 100644 --- a/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/OXXO/OXXOGatewayTest.php @@ -12,7 +12,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use function Brain\Monkey\Functions\when; diff --git a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php index 82c88449f..d806037ee 100644 --- a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php @@ -10,7 +10,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index 8c8a238ae..a9e64ebb0 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -9,7 +9,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; diff --git a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php index 207d31967..951c3d9af 100644 --- a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php @@ -20,7 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper; use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure; -use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\ReadOnlyContainer; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; From 70c44b1f18e9f5e4c7c9313d2be3724eb149b7d1 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 3 Feb 2025 14:50:41 +0100 Subject: [PATCH 78/78] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20?= =?UTF-8?q?=E2=80=98api.host=E2=80=99=20service=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-api-client/services.php | 11 +++++++++-- modules/ppcp-onboarding/services.php | 12 ------------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index e7cc9e878..272bfdaf7 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -84,8 +84,15 @@ use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( - 'api.host' => function( ContainerInterface $container ) : string { - return PAYPAL_API_URL; + 'api.host' => static function( ContainerInterface $container ) : string { + $environment = $container->get( 'onboarding.environment' ); + assert( $environment instanceof Environment ); + + if ( $environment->is_sandbox() ) { + return (string) $container->get( 'api.sandbox-host' ); + } + + return (string) $container->get( 'api.production-host' ); }, 'api.paypal-host' => function( ContainerInterface $container ) : string { return PAYPAL_API_URL; diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 17c9ba8c0..cbbd02da5 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -20,18 +20,6 @@ use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( - 'api.host' => static function ( ContainerInterface $container ): string { - $environment = $container->get( 'onboarding.environment' ); - - /** - * The Environment and State variables. - * - * @var Environment $environment - */ - return $environment->current_environment_is( Environment::SANDBOX ) - ? (string) $container->get( 'api.sandbox-host' ) : (string) $container->get( 'api.production-host' ); - - }, 'api.paypal-host' => function( ContainerInterface $container ) : string { $environment = $container->get( 'onboarding.environment' ); /**