From 9c688efbeb2e1180bb00496988f67deabfd994ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Pellicer?= <5908855+puntope@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:24:12 +0200 Subject: [PATCH 01/10] Update README.md Dev section --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 64814bde1..68372bdad 100644 --- a/README.md +++ b/README.md @@ -52,21 +52,51 @@ Visit our [official documentation](https://woocommerce.com/document/woocommerce- ## Development -### Install dependencies & build +### Setup using DDEV (recommended) + +You can install WooCommerce PayPal Payments locally using the dev environment of your preference, or you can use the DDEV setup provided in this repository which includes WP, WC and all developments tools. + +To set up the DDEV environment, follow these steps: + +0. Install Docker and [DDEV](https://ddev.readthedocs.io/en/stable/). +1. Edit the [configuration](https://docs.ddev.com/en/stable/users/configuration/config/#managing-configuration) in the [`.ddev/config.yml`](.ddev/config.yaml) file if needed. +2. `$ ddev setup` to setup and orchestrate the plugin, WooCommerce and WordPress +3. Open https://woocommerce-paypal-payments.ddev.site + +Use `$ ddev reset` for reinstallation (will destroy all site data). +You may also need `$ ddev restart` to apply the config changes. + +#### Running tests and other tasks in the DDEV environment + +Tests and code style: +- `$ yarn ddev:unit-tests` +- `$ yarn ddev:lint` +- `$ yarn ddev:fix-lint` +- `$ yarn ddev:lint-js` + +See [package.json](/package.json) for other useful commands. + +For debugging, see [the DDEV docs](https://ddev.readthedocs.io/en/stable/users/step-debugging/). +Enable xdebug via `$ ddev xdebug enable`, and press `Start Listening for PHP Debug Connections` in PHPStorm. +After creating the server in the PHPStorm dialog, you need to set the local project path for the server plugin path. +Check [this article](https://docs.ddev.com/en/stable/users/debugging-profiling/step-debugging/#phpstorm-debugging-setup) for a detailed guide. + +## Setup in other environments + +#### Install dependencies & build - `$ composer install` - `$ yarn install` Optionally, change the `PAYPAL_INTEGRATION_DATE` constant to `gmdate( 'Y-m-d' )` to run the latest PayPal JavaScript SDK -### Unit tests and code style +#### Unit tests and code style -1. `$ composer install` -2. `$ ./vendor/bin/phpunit` -3. `$ ./vendor/bin/phpcs` -4. `$ ./vendor/bin/psalm` -5. `$ wp-scripts lint-js` -6. `$ yarn run test:unit-js` - Ensure node version is `18` or above +1. `$ ./vendor/bin/phpunit` +2. `$ ./vendor/bin/phpcs` +3. `$ ./vendor/bin/psalm` +4. `$ yarn run lint-js` +5. `$ yarn run test:unit-js` - Ensure node version is `18` or above ### Building a release package @@ -88,38 +118,6 @@ or if using the DDEV setup: $ yarn run ddev:build-package ``` -## Setup - -You can install WooCommerce PayPal Payments locally using the dev environment of your preference, or you can use the DDEV setup provided in this repository which includes WP, WC and all developments tools. - -To set up the DDEV environment, follow these steps: - -0. Install Docker and [DDEV](https://ddev.readthedocs.io/en/stable/). -1. Edit the configuration in the [`.ddev/config.yml`](.ddev/config.yaml) file if needed. -2. `$ ddev start` -3. `$ ddev orchestrate` to install WP/WC. -4. Open https://wc-pp.ddev.site - -Use `$ ddev orchestrate -f` for reinstallation (will destroy all site data). -You may also need `$ ddev restart` to apply the config changes. - -### Running tests and other tasks in the DDEV environment - -Tests and code style: -- `$ yarn ddev:test` -- `$ yarn ddev:lint` -- `$ yarn ddev:fix-lint` -- `$ yarn ddev:lint-js` - -See [package.json](/package.json) for other useful commands. - -For debugging, see [the DDEV docs](https://ddev.readthedocs.io/en/stable/users/step-debugging/). -Enable xdebug via `$ ddev xdebug`, and press `Start Listening for PHP Debug Connections` in PHPStorm. -After creating the server in the PHPStorm dialog, you need to set the local project path for the server plugin path. -It should look [like this](https://i.imgur.com/ofsF1Mc.png). - -See [tests/playwright](tests/playwright) for e2e (browser-based) tests. - ## Test account setup You will need a PayPal sandbox merchant and customer accounts to configure the plugin and make test purchases with it. From 9d53b4ecf79766c6fbb0bab64af674facf7e5871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Pellicer?= <5908855+puntope@users.noreply.github.com> Date: Mon, 25 Aug 2025 18:12:38 +0200 Subject: [PATCH 02/10] Tweak WordPress and WooCommerce wording --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68372bdad..84e44bc6a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Visit our [official documentation](https://woocommerce.com/document/woocommerce- ### Setup using DDEV (recommended) -You can install WooCommerce PayPal Payments locally using the dev environment of your preference, or you can use the DDEV setup provided in this repository which includes WP, WC and all developments tools. +You can install WooCommerce PayPal Payments locally using the dev environment of your preference, or you can use the DDEV setup provided in this repository. Which includes WordPress, WooCommerce and all development tools. To set up the DDEV environment, follow these steps: @@ -101,7 +101,7 @@ Optionally, change the `PAYPAL_INTEGRATION_DATE` constant to `gmdate( 'Y-m-d' )` ### Building a release package If you want to build a release package -(that can be used for deploying a new version on wordpress.org or manual installation on a WP website via ZIP uploading), +(that can be used for deploying a new version on wordpress.org or manual installation on a WordPress website via ZIP uploading), follow these steps: 1. Clone the repository and `cd` into it. @@ -131,7 +131,7 @@ For testing webhooks locally, follow these steps to set up ngrok: 0. Install [ngrok](https://ngrok.com/). 1. - - If using DDEV, run our wrapper Bash script which will start `ddev share` and replace the URLs in the WP database: + - If using DDEV, run our wrapper Bash script which will start `ddev share` and replace the URLs in the WordPress database: ``` $ .ddev/bin/share ``` From 32afcda616721ba49bfe8e58f67b7ff952f6d83f Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 27 Aug 2025 14:47:16 +0400 Subject: [PATCH 03/10] docs: Add WooCommerce inbox notifications development guide. Documents the complete workflow from note definition to registration, including character limits, HTML restrictions, and cleanup processes --- docs/inbox-notifications-guide.md | 115 ++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 docs/inbox-notifications-guide.md diff --git a/docs/inbox-notifications-guide.md b/docs/inbox-notifications-guide.md new file mode 100644 index 000000000..eebb3c8d6 --- /dev/null +++ b/docs/inbox-notifications-guide.md @@ -0,0 +1,115 @@ +# WooCommerce Home Inbox Notifications Development Guide + +This guide explains how to create and manage inbox notifications that appear in the WooCommerce Admin dashboard. + +## Overview + +The WooCommerce PayPal Payments plugin uses a structured system to create inbox notes through three main components: note definitions, note objects, and registration handling. + +## Architecture + +### 1. Define Inbox Notes + +Inbox notes are defined as service configurations in `modules/ppcp-wc-gateway/services.php` (Service name: `wcgateway.settings.inbox-notes`). The system creates an array of `InboxNote` objects with the following properties: + +- `title`: The note headline +- `content`: The note body text +- `type`: Note type (e.g., `Note::E_WC_ADMIN_NOTE_INFORMATIONAL`) +- `name`: Unique identifier for the note +- `status`: Note status (e.g., `Note::E_WC_ADMIN_NOTE_UNACTIONED`) +- `is_enabled`: Boolean function to control visibility +- `action`: An `InboxNoteAction` object for user interactions + +### 2. Create Inbox Note Objects + +Each inbox note is created using the `InboxNoteFactory` and `InboxNote` class. The constructor requires all the properties listed above. + +### 3. Register Inbox Notes + +The `InboxNoteRegistrar` handles the registration process by: +- Creating WooCommerce `Note` objects from `InboxNote` definitions +- Saving notes to display in the admin inbox +- Managing note lifecycle (creation/deletion based on conditions) + +### 4. Registration Hook + +Inbox notes are registered via the `register_woo_inbox_notes` method in `WCGatewayModule`, which hooks into the `admin_init` action. + +## Implementation Example + +```php +// In services.php +'inbox-note.example' => static function ( ContainerInterface $container ): InboxNote { + return $container->get( 'inbox-note.factory' )->create_note( + __( 'Example Note Title', 'woocommerce-paypal-payments' ), + __( 'This is the note content that appears in the inbox.', 'woocommerce-paypal-payments' ), + Note::E_WC_ADMIN_NOTE_INFORMATIONAL, + 'example-note-unique-name', + Note::E_WC_ADMIN_NOTE_UNACTIONED, + static function () use ( $container ): bool { + // Conditional logic to determine when note should be shown + return true; // or your condition + }, + new InboxNoteAction( + 'apply_now', + __( 'Apply now', 'woocommerce-paypal-payments' ), + 'http://example.com/', + Note::E_WC_ADMIN_NOTE_UNACTIONED, + true + ) + ); +}, + +``` + +## Content Limitations + +WooCommerce inbox notes have several restrictions: + +### Character Limit +- Content is automatically truncated at **320 characters** with "..." +- No expansion option available in the UI +- Reference: [WooCommerce Developer Blog](https://developer.woocommerce.com/2021/11/10/introducing-a-320-character-limit-to-inbox-notes/) + +### HTML Restrictions +Only basic HTML tags are allowed: +- ``, `` for emphasis +- `` for links (with `href`, `rel`, `name`, `target`, `download` attributes) +- `
`, `

` for formatting +- Tags like ``, ``, `` are stripped + +### Workarounds +- Use asterisks (*) for emphasis when HTML tags aren't supported +- Keep messages concise and prioritize essential information +- Place most important content within the first 320 characters + +## Automatic Cleanup + +The system includes automatic cleanup functionality: +- Notes are deleted when their `is_enabled` condition becomes `false` (`InboxNoteRegistrar.php`) +- This prevents stale notifications from persisting in the admin + +## Actions + +Notes can include user actions defined through the `InboxNoteAction` class. Actions appear as buttons in the inbox note and can: +- Navigate to specific admin pages +- Trigger custom functionality +- Dismiss or acknowledge the notification + +## Best Practices + +1. **Use descriptive, unique names** for note identification +2. **Implement proper conditional logic** in the `is_enabled` function +3. **Keep content concise** due to the 320-character limit +4. **Test note visibility conditions** thoroughly +5. **Provide clear, actionable next steps** through note actions +6. **Consider cleanup scenarios** when notes should be removed + +## Existing Examples + +The codebase includes several inbox note implementations: +- PayPal Working Capital note +- Settings migration notices +- Feature announcements + +These examples demonstrate conditional logic based on feature flags, user settings, and other criteria. From f5c9c4298afa8ed8341d05085fe518fa4b575061 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 27 Aug 2025 15:12:26 +0400 Subject: [PATCH 04/10] docs: Add WooCommerce tasks and todos development guide --- docs/woocommerce-tasks-guide.md | 296 ++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 docs/woocommerce-tasks-guide.md diff --git a/docs/woocommerce-tasks-guide.md b/docs/woocommerce-tasks-guide.md new file mode 100644 index 000000000..678b33583 --- /dev/null +++ b/docs/woocommerce-tasks-guide.md @@ -0,0 +1,296 @@ +# WooCommerce Tasks and Todos Development Guide + +This guide explains how to create and manage task items that appear in WooCommerce's "Things to do next" section, as well as in the "Things to do next" section of the plugin’s overview tab. The PayPal Payments plugin uses two distinct systems to cover different use cases. + +## Overview + +The plugin uses two separate task systems: + +1. **WooCommerce Native Tasks System** - Integrates with WooCommerce's built-in task list +2. **Plugin's React-Based Todos System** - Custom implementation for the PayPal settings interface + +## WooCommerce Native Tasks System + +### Architecture + +Native WooCommerce tasks are managed through the `TaskRegistrar` system and appear in WooCommerce's main admin "Things to do next" section. + +### Implementation + +Tasks are registered via the `register_wc_tasks` method in `WCGatewayModule.php`, which: + +1. Hooks into the `init` action for proper timing +2. Retrieves simple redirect tasks from the container +3. Uses the `TaskRegistrar` to register tasks with the 'extended' list +4. Includes error handling and logging for registration failures + +```php +protected function register_wc_tasks( ContainerInterface $container ): void { + add_action( + 'init', + static function () use ( $container ): void { + $logger = $container->get( 'woocommerce.logger.woocommerce' ); + try { + $simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' ); + if ( empty( $simple_redirect_tasks ) ) { + return; + } + + $task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' ); + $task_registrar->register( 'extended', $simple_redirect_tasks ); + } catch ( Exception $exception ) { + $logger->error( "Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage() ); + } + }, + ); +} +``` + +### Task Configuration + +Tasks are defined in `services.php` as service definitions. Each task configuration includes: + +- `id`: Unique identifier for the task +- `title`: Display title in the task list +- `description`: Explanatory text for what the task accomplishes +- `redirect_url`: URL where users are taken when they click the task + +```php +// Example: Pay Later messaging configuration task +'wcgateway.settings.wc-tasks.pay-later-task-config' => static function( ContainerInterface $container ): array { + $section_id = Settings::CONNECTION_TAB_ID; + $pay_later_tab_id = Settings::PAY_LATER_TAB_ID; + + if ( $container->has( 'paylater-configurator.is-available' ) && $container->get( 'paylater-configurator.is-available' ) ) { + return array( + array( + 'id' => 'pay-later-messaging-task', + 'title' => __( 'Configure PayPal Pay Later messaging', 'woocommerce-paypal-payments' ), + 'description' => __( 'Decide where you want dynamic Pay Later messaging to show up and how you want it to look on your site.', 'woocommerce-paypal-payments' ), + 'redirect_url' => admin_url( "admin.php?page=wc-settings&tab=checkout§ion={$section_id}&ppcp-tab={$pay_later_tab_id}" ), + ), + ); + } + return array(); +}, +``` + +### Registration Process + +The `TaskRegistrar` class handles task registration through the `register()` method: + +```php +public function register( string $list_id, array $tasks ): void { + $task_lists = TaskLists::get_lists(); + if ( ! isset( $task_lists[ $list_id ] ) ) { + return; + } + + foreach ( $tasks as $task ) { + $added_task = TaskLists::add_task( $list_id, $task ); + if ( $added_task instanceof WP_Error ) { + throw new RuntimeException( $added_task->get_error_message() ); + } + } +} +``` + +The registration process: +- Validates the target task list exists +- Iterates through task definitions +- Uses WooCommerce's `TaskLists::add_task()` API +- Handles registration errors with exceptions + +## Plugin's React-Based Todos System + +### Architecture + +The custom todos system provides more advanced functionality and appears specifically in the PayPal Payments settings Overview tab. + +### Components + +1. **Backend Definition** - `TodosDefinition.php` contains todo configurations +2. **REST API Endpoint** - `TodosRestEndpoint.php` handles CRUD operations +3. **React Frontend** - `Todos.js` renders the user interface + +### Todo Configuration + +Each todo item in `TodosDefinition.php` requires the following properties: + +```php +public function get(): array { + $eligibility_checks = $this->eligibilities->get_eligibility_checks(); + + $todo_items = array( + 'enable_fastlane' => array( + 'title' => __( 'Enable Fastlane', 'woocommerce-paypal-payments' ), + 'description' => __( 'Accelerate your guest checkout with Fastlane by PayPal', 'woocommerce-paypal-payments' ), + 'isEligible' => $eligibility_checks['enable_fastlane'], + 'action' => array( + 'type' => 'tab', + 'tab' => 'payment_methods', + 'section' => 'ppcp-axo-gateway', + 'highlight' => 'ppcp-axo-gateway', + ), + 'priority' => 1, + ), + 'enable_pay_later_messaging' => array( + 'title' => __( 'Enable Pay Later messaging', 'woocommerce-paypal-payments' ), + 'description' => __( 'Show Pay Later options to increase conversion.', 'woocommerce-paypal-payments' ), + 'isEligible' => $eligibility_checks['enable_pay_later_messaging'], + 'action' => array( + 'type' => 'tab', + 'tab' => 'pay_later', + 'section' => 'pay-later-messaging', + ), + 'priority' => 2, + ), + // Additional todo items... + ); + + return $todo_items; +} +``` + +### Advanced Features + +The React-based system supports: + +- **Eligibility Checks**: Dynamic visibility based on conditions +- **Dismissal**: Users can dismiss individual todos +- **Completion Tracking**: Automatic removal when tasks are completed +- **Priority Ordering**: Control display order with priority values +- **REST API**: Full CRUD operations via dedicated endpoints + +### API Integration + +The REST API provides endpoints for managing todos: + +```php +public function register_routes(): void { + // GET/POST /todos - Get todos list and update dismissed todos + register_rest_route( + static::NAMESPACE, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_todos' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_todos' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + ) + ); + + // POST /todos/reset - Reset dismissed todos + register_rest_route( + static::NAMESPACE, + '/' . $this->rest_base . '/reset', + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'reset_todos' ), + 'permission_callback' => array( $this, 'check_permission' ), + ) + ); +} +``` + +The endpoints handle: +- **GET /todos**: Fetching current todo list with eligibility filtering +- **POST /todos**: Updating dismissed todo status +- **POST /todos/reset**: Restoring all dismissed todos + +### Frontend Rendering + +The React component provides a complete todo management interface: + +```jsx +const Todos = ( { todos, resetTodos, dismissTodo } ) => { + const [ isResetting, setIsResetting ] = useState( false ); + const [ activeModal, setActiveModal ] = useState( null ); + + // Reset handler for restoring dismissed todos + const resetHandler = useCallback( async () => { + setIsResetting( true ); + try { + await resetTodos(); + } finally { + setIsResetting( false ); + } + }, [ resetTodos ] ); + + if ( ! todos?.length ) { + return null; + } + + return ( + +

+ { __( + 'Complete these tasks to keep your store updated with the latest products and services.', + 'woocommerce-paypal-payments' + ) } +

+ + + } + > + + + ); +}; +``` + +Key features: +- **Restore Functionality**: Users can restore dismissed todos +- **Modal Integration**: Support for detailed todo actions +- **Dismissal Handling**: Individual todo dismissal with state management +- **Loading States**: Visual feedback during operations + +## Best Practices + +### For WooCommerce Native Tasks + +1. **Keep task definitions simple** - Use basic configuration only +2. **Provide clear redirect URLs** - Direct users to relevant settings +3. **Use descriptive IDs** - Include plugin prefix for uniqueness +4. **Test with WooCommerce updates** - Ensure compatibility with core changes + +### For Plugin React Todos + +1. **Implement robust eligibility checks** - Prevent showing irrelevant todos +2. **Use appropriate priority values** - Ensure logical ordering +3. **Provide actionable descriptions** - Help users understand next steps +4. **Handle edge cases** - Account for various plugin states +5. **Test dismissal functionality** - Ensure proper state management + +## Existing Examples + +Both systems have multiple implementations in the codebase: +- Onboarding completion tasks +- Feature enablement todos +- Configuration reminder items +- Migration assistance tasks + +These examples demonstrate various conditional logic patterns and user experience flows. From 8825a887649f5019ca3b978eb82f9137c14cb0bc Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 27 Aug 2025 15:17:04 +0400 Subject: [PATCH 05/10] docs: Open the link in new tab --- docs/inbox-notifications-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/inbox-notifications-guide.md b/docs/inbox-notifications-guide.md index eebb3c8d6..b7e55972f 100644 --- a/docs/inbox-notifications-guide.md +++ b/docs/inbox-notifications-guide.md @@ -69,7 +69,7 @@ WooCommerce inbox notes have several restrictions: ### Character Limit - Content is automatically truncated at **320 characters** with "..." - No expansion option available in the UI -- Reference: [WooCommerce Developer Blog](https://developer.woocommerce.com/2021/11/10/introducing-a-320-character-limit-to-inbox-notes/) +- Reference: [WooCommerce Developer Blog ↗](https://developer.woocommerce.com/2021/11/10/introducing-a-320-character-limit-to-inbox-notes/) ### HTML Restrictions Only basic HTML tags are allowed: From 564eec1ea4705789733f33c9064dc5cbb297f7c4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 27 Aug 2025 16:16:21 +0200 Subject: [PATCH 06/10] Fix fastlane not displayed in legacy ui for eligible countries --- modules/ppcp-wc-gateway/services.php | 3 +- .../src/Settings/SettingsRenderer.php | 31 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index fd5a357b4..105ae5266 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -535,7 +535,8 @@ return array( $container->get( 'wcgateway.helper.dcc-product-status' ), $container->get( 'wcgateway.settings.status' ), $container->get( 'wcgateway.current-ppcp-settings-page-id' ), - $container->get( 'api.shop.country' ) + $container->get( 'api.shop.country' ), + $container->get( 'axo.eligibility.check' ) ); }, 'wcgateway.settings.listener' => static function ( ContainerInterface $container ): SettingsListener { diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php index cd3c1ffcb..50081f601 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php @@ -89,6 +89,13 @@ class SettingsRenderer { */ protected $page_id; + /** + * A callable property used to check the eligibility for Fastlane. + * + * @var callable + */ + private $axo_eligibility_check; + /** * SettingsRenderer constructor. * @@ -111,20 +118,22 @@ class SettingsRenderer { DCCProductStatus $dcc_product_status, SettingsStatus $settings_status, string $page_id, - string $api_shop_country + string $api_shop_country, + callable $axo_eligibility_check ) { // This is a legacy settings class, it's correctly relying on the `Status` class. - $this->settings = $settings; - $this->state = $state; - $this->fields = $fields; - $this->dcc_applies = $dcc_applies; - $this->messages_apply = $messages_apply; - $this->dcc_product_status = $dcc_product_status; - $this->settings_status = $settings_status; - $this->page_id = $page_id; - $this->api_shop_country = $api_shop_country; + $this->settings = $settings; + $this->state = $state; + $this->fields = $fields; + $this->dcc_applies = $dcc_applies; + $this->messages_apply = $messages_apply; + $this->dcc_product_status = $dcc_product_status; + $this->settings_status = $settings_status; + $this->page_id = $page_id; + $this->api_shop_country = $api_shop_country; + $this->axo_eligibility_check = $axo_eligibility_check; } /** @@ -398,7 +407,7 @@ $data_rows_html } if ( in_array( 'axo', $config['requirements'], true ) - && $this->api_shop_country !== 'US' + && ! ( $this->axo_eligibility_check )() ) { continue; } From cae8828e70b03b33df8013157a2f4b3b62835c09 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 28 Aug 2025 13:41:41 +0400 Subject: [PATCH 07/10] Update the wording --- modules/ppcp-wc-gateway/services.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index fd5a357b4..fb3a9adec 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -2241,22 +2241,22 @@ return array( ) ), $inbox_note_factory->create_note( - __( 'Pay Later messaging now optimizing conversions', 'woocommerce-paypal-payments' ), + __( 'PayPal Pay Later Messaging now enabled', 'woocommerce-paypal-payments' ), sprintf( // translators: %1$s is the URL for Pay Later messaging documentation. __( - 'PayPal Pay Later messages are now displaying on your store through your
Stay Updated preference, helping customers see flexible payment options. Merchants typically see 20-40% higher conversion rates with these purchase incentives. The messages appear contextually near prices, not as ads. You control this completely—disable anytime if your conversion metrics don\'t improve.', + 'PayPal Pay Later messaging was included in the 3.1 version release and has now been enabled on your store based on your STAY UPDATED preference.
This feature displays the payment option earlier in the shopping experience to drive customer engagement and can be fully customized or disabled through the PayPal admin panel.', 'woocommerce-paypal-payments' ), $stay_updated_field_link ), Note::E_WC_ADMIN_NOTE_INFORMATIONAL, - 'ppcp-settings-paylater-messaging-force-enabled-inbox-note', + 'ppcp-settings-paylater-messaging-force-enabled-indbox-note', Note::E_WC_ADMIN_NOTE_UNACTIONED, $is_paylater_messaging_force_enabled_feature_flag_enabled && $messages_apply->for_country() && $stay_updated, new InboxNoteAction( 'review_pay_later_settings', - __( 'Review Pay Later settings', 'woocommerce-paypal-payments' ), + __( 'REVIEW PAY LATER SETTINGS', 'woocommerce-paypal-payments' ), $paylater_messaging_tab_link, Note::E_WC_ADMIN_NOTE_UNACTIONED, true From d71c395fbf4cc6c744093beb41a244912615fdae Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 28 Aug 2025 13:52:15 +0400 Subject: [PATCH 08/10] Fix the name --- modules/ppcp-wc-gateway/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index fb3a9adec..83a60aca0 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -2251,7 +2251,7 @@ return array( $stay_updated_field_link ), Note::E_WC_ADMIN_NOTE_INFORMATIONAL, - 'ppcp-settings-paylater-messaging-force-enabled-indbox-note', + 'ppcp-settings-paylater-messaging-force-enabled-inbox-note', Note::E_WC_ADMIN_NOTE_UNACTIONED, $is_paylater_messaging_force_enabled_feature_flag_enabled && $messages_apply->for_country() && $stay_updated, new InboxNoteAction( From f599f75a8a934ff55f63678e1daafbfbb74e1d7c Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 28 Aug 2025 16:10:12 +0400 Subject: [PATCH 09/10] Remove capitalized styling --- modules/ppcp-wc-gateway/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 83a60aca0..d7cf10ccd 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -2256,7 +2256,7 @@ return array( $is_paylater_messaging_force_enabled_feature_flag_enabled && $messages_apply->for_country() && $stay_updated, new InboxNoteAction( 'review_pay_later_settings', - __( 'REVIEW PAY LATER SETTINGS', 'woocommerce-paypal-payments' ), + __( 'Review Pay Later settings', 'woocommerce-paypal-payments' ), $paylater_messaging_tab_link, Note::E_WC_ADMIN_NOTE_UNACTIONED, true From c6a2855d3d71616b5dd620da19fbac11bebbfaf4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 28 Aug 2025 14:48:30 +0200 Subject: [PATCH 10/10] Revert deleted data client token for vault v2 --- .../js/modules/Helper/PayPalScriptLoading.js | 29 +++++++++++++++++++ .../js/modules/Helper/ScriptLoading.js | 16 ++++++++++ 2 files changed, 45 insertions(+) diff --git a/modules/ppcp-button/resources/js/modules/Helper/PayPalScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/PayPalScriptLoading.js index b90bad117..48134d2bc 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/PayPalScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/PayPalScriptLoading.js @@ -1,10 +1,31 @@ import { loadScript } from '@paypal/paypal-js'; +import dataClientIdAttributeHandler from '../DataClientIdAttributeHandler'; import widgetBuilder from '../Renderer/WidgetBuilder'; import { processConfig } from './ConfigProcessor'; const loadedScripts = new Map(); const scriptPromises = new Map(); +const handleDataClientIdAttribute = async ( scriptOptions, config ) => { + if ( + config.data_client_id?.set_attribute && + config.vault_v3_enabled !== true + ) { + return new Promise( ( resolve, reject ) => { + dataClientIdAttributeHandler( + scriptOptions, + config.data_client_id, + ( paypal ) => { + widgetBuilder.setPaypal( paypal ); + resolve( paypal ); + }, + reject + ); + } ); + } + return null; +}; + export const loadPayPalScript = async ( namespace, config ) => { if ( ! namespace ) { throw new Error( 'Namespace is required' ); @@ -27,6 +48,14 @@ export const loadPayPalScript = async ( namespace, config ) => { 'data-namespace': namespace, }; + const dataClientIdResult = await handleDataClientIdAttribute( + scriptOptions, + config + ); + if ( dataClientIdResult ) { + return dataClientIdResult; + } + const scriptPromise = new Promise( ( resolve, reject ) => { loadScript( scriptOptions ) .then( ( script ) => { diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index 9d143ad16..86e16c7de 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -1,7 +1,9 @@ +import dataClientIdAttributeHandler from '../DataClientIdAttributeHandler'; import { loadScript } from '@paypal/paypal-js'; import widgetBuilder from '../Renderer/WidgetBuilder'; import merge from 'deepmerge'; import { keysToCamelCase } from './Utils'; +import { getCurrentPaymentMethod } from './CheckoutMethodState'; // This component may be used by multiple modules. This assures that options are shared between all instances. const scriptOptionsMap = {}; @@ -69,6 +71,20 @@ export const loadPaypalScript = ( config, onLoaded, onError = null ) => { scriptOptions = merge( scriptOptions, config.script_attributes ); } + // Load PayPal script for special case with data-client-token + if ( + config.data_client_id?.set_attribute && + config.vault_v3_enabled !== '1' + ) { + dataClientIdAttributeHandler( + scriptOptions, + config.data_client_id, + callback, + errorCallback + ); + return; + } + // Adds data-user-id-token to script options. const userIdToken = config?.save_payment_methods?.id_token; if ( userIdToken && config?.user?.is_logged === true ) {