Skip to content

WPS Migration: Fix capture failure #60136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 4, 2025

Conversation

Mayisha
Copy link
Contributor

@Mayisha Mayisha commented Jul 31, 2025

Submission Review Guidelines:

Changes proposed in this Pull Request:

This PR fixes the issue reported in #60044 (review)

If you change the order status to processing, there are two API requests to capture the payment. We have a status check before sending the request. As we were setting the status during webhook processing, the status was not set by the time the 2nd request was made. In this PR, I have set the status when the order capture request is successful.

How to test the changes in this Pull Request:

  1. You will need a Business sandbox account (merchant) and a Personal sandbox account (shopper).
  2. Set the deprecated PayPal gateway feature flag, and set the Orders v2 migration flag:
wp option patch update woocommerce_paypal_settings _should_load 'yes'
wp option patch update woocommerce_paypal_settings use_orders_v2 'yes'
  1. Go to WooCommerce > Settings > Payments > and enable PayPal Standard.
  2. Configure PayPal Standard's settings:
  • For PayPal email, enter your merchant sandbox account email.
  • Check Enable PayPal sandbox
  • Check Enable logging (optional, for logging)
  • For Payment action, select Authorize
  1. To enable end-to-end testing, add the PayPal API client and secret (used by the feature branch's test proxy):
wp option update wc_paypal_api_client_id <YOUR-PAYPAL-CLIENT>
wp option update wc_paypal_api_client_secret <YOUR-PAYPAL-CLIENT-SECRET>
  1. As a shopper, add a product to cart and go to checkout.
  2. You should see PayPal as a payment option.
  3. Click "Proceed to PayPal".
  4. Use your Personal sandbox account credentials to log in and approve the purchase.
  5. You should get redirected to the "Order Confirmation" page.
  6. Wait a few seconds, and go to the wp-admin order page.
  7. There should be an order note about payment authorization and the order status should be on-hold.
  8. In the order details section, the transaction ID should be linked.
  9. Click the transaction ID. It should take you to the PayPal transaction page. Confirm that the transaction status is Authorized.
  10. Go back to the order edit page and change the order status to processing.
  11. Wait a few seconds and refresh the page.
  12. There should be an order note about payment capture.
  13. Go to the PayPal transaction page. Confirm that the transaction status is Authorized.
  14. Confirm that there is no Authorization has already been captured. error in the logs.

Changelog entry

  • Automatically create a changelog entry from the details below.
  • This Pull Request does not require a changelog entry. (Comment required below)
Changelog Entry Details

Significance

  • Patch
  • Minor
  • Major

Type

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Message

Changelog Entry Comment

Comment

@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Jul 31, 2025
@Mayisha Mayisha changed the base branch from trunk to feature/paypal-wps-migration July 31, 2025 14:43
@Mayisha Mayisha requested a review from Copilot July 31, 2025 14:45
Copy link
Contributor

github-actions bot commented Jul 31, 2025

Testing Guidelines

Hi @annemirasol ,

Apart from reviewing the code changes, please make sure to review the testing instructions (Guide) and verify that relevant tests (E2E, Unit, Integration, etc.) have been added or updated as needed.

Reminder: PR reviewers are required to document testing performed. This includes:

  • 🖼️ Screenshots or screen recordings.
  • 📝 List of functionality tested / steps followed.
  • 🌐 Site details (environment attributes such as hosting type, plugins, theme, store size, store age, and relevant settings).
  • 🔍 Any analysis performed, such as assessing potential impacts on environment attributes and other plugins, conducting performance profiling, or using LLM/AI-based analysis.

⚠️ Within the testing details you provide, please ensure that no sensitive information (such as API keys, passwords, user data, etc.) is included in this public issue.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a race condition in the PayPal gateway where duplicate capture requests were being made when changing order status to "processing". The issue occurred because the PayPal status was not immediately available for the second request due to asynchronous webhook processing.

  • Updates PayPal status handling to use lowercase values consistently
  • Sets the PayPal status immediately after successful capture requests
  • Adds explicit order save operations to ensure status is persisted

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
class-wc-gateway-paypal-webhook-handler.php Normalizes PayPal status to lowercase and adds order save after approval
class-wc-gateway-paypal-request.php Updates status check to lowercase, sets status after capture response

Copy link
Contributor

github-actions bot commented Jul 31, 2025

Test using WordPress Playground

The changes in this pull request can be previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Test this pull request with WordPress Playground.

Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit.

Copy link
Contributor

coderabbitai bot commented Jul 31, 2025

Caution

Review failed

Failed to post review comments.

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 912a591 and 58a3240.

📒 Files selected for processing (10)
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php (11 hunks)
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php (1 hunks)
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php (5 hunks)
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php (1 hunks)
  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php (1 hunks)
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php (1 hunks)
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php (1 hunks)
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php (1 hunks)
  • plugins/woocommerce/includes/rest-api/Server.php (1 hunks)
  • plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/rest-api/Server.php
  • plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/rest-api/Server.php
  • plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/generate-pr-description.mdc:0-0
Timestamp: 2025-07-21T05:22:46.426Z
Learning: Provide clear, step-by-step instructions for how to test the changes in the PR description.
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:431-439
Timestamp: 2025-06-17T14:19:30.933Z
Learning: In plugins/woocommerce/src/Internal/Admin/Settings/Payments.php, the process_payment_provider_states() method intentionally filters out payment providers that don't have a _suggestion_id. This is by design to only track state changes for gateways from partner extensions, not core WooCommerce gateways or other installed gateways.
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:484-488
Timestamp: 2025-06-17T11:30:23.806Z
Learning: In the WooCommerce Payments settings provider state tracking system (plugins/woocommerce/src/Internal/Admin/Settings/Payments.php), when an extension is deactivated and its snapshot key disappears, only the 'extension_active' flag should be set to false while keeping other state flags like 'account_connected', 'needs_setup', 'test_mode', etc. unchanged. This is intentional behavior to preserve historical state information.
Learnt from: vladolaru
PR: woocommerce/woocommerce#59160
File: plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/index.tsx:0-0
Timestamp: 2025-06-26T14:56:54.917Z
Learning: In WooCommerce payments onboarding components (like plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/index.tsx), when updating local React state based on API calls, the local state should only be updated after the API call succeeds to prevent inconsistent state if the save operation fails. The pattern is to move setPaymentMethodsState calls inside the .then() callback of the API promise.
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:471-475
Timestamp: 2025-06-17T10:59:56.461Z
Learning: In WooCommerce payments settings, when processing payment provider states, if a gateway appears in the payment providers list (from get_payment_gateways()), its underlying extension must be active by definition. Inactive or uninstalled extensions don't have their gateways available, so setting 'extension_active' to true for all processed gateways is correct.
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Monei.php:27-33
Timestamp: 2025-06-18T10:01:09.421Z
Learning: In WooCommerce payment gateway provider classes, prefer using `is_callable()` over `method_exists()` when checking for method availability, as it properly accounts for method visibility (public/private/protected) rather than just existence.
Learnt from: vladolaru
PR: woocommerce/woocommerce#59486
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WooPayments/WooPaymentsService.php:1544-1544
Timestamp: 2025-07-08T11:18:07.871Z
Learning: WooCommerce has polyfills for newer PHP functions (like str_starts_with() from PHP 8.0+), so these functions can be safely used even though WooCommerce supports PHP 7.4+. No need to suggest PHP 7.4 compatible alternatives when polyfills are available.
📚 Learning: in the woocommerce woopayments rest controller, it's acceptable to let apiexception bubble up from o...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WooPayments/WooPaymentsRestController.php:619-625
Timestamp: 2025-06-17T10:33:54.736Z
Learning: In the WooCommerce WooPayments REST controller, it's acceptable to let ApiException bubble up from onboarding-related method calls like get_onboarding_step_status() and mark_onboarding_step_completed() rather than wrapping them in local try/catch blocks.

Applied to files:

  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
📚 Learning: in woocommerce payment gateway provider classes, prefer using `is_callable()` over `method_exists()`...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Monei.php:27-33
Timestamp: 2025-06-18T10:01:09.421Z
Learning: In WooCommerce payment gateway provider classes, prefer using `is_callable()` over `method_exists()` when checking for method availability, as it properly accounts for method visibility (public/private/protected) rather than just existence.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
📚 Learning: in plugins/woocommerce/src/internal/admin/settings/payments.php, the process_payment_provider_states...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:431-439
Timestamp: 2025-06-17T14:19:30.933Z
Learning: In plugins/woocommerce/src/Internal/Admin/Settings/Payments.php, the process_payment_provider_states() method intentionally filters out payment providers that don't have a _suggestion_id. This is by design to only track state changes for gateways from partner extensions, not core WooCommerce gateways or other installed gateways.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/rest-api/Server.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
  • plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
📚 Learning: in php payment gateway provider classes, when using constants as option keys in $payment_gateway->ge...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/HelioPay.php:31-33
Timestamp: 2025-06-18T10:14:24.984Z
Learning: In PHP payment gateway provider classes, when using constants as option keys in $payment_gateway->get_option(), the backslash prefix (\CONSTANT_NAME) is correct usage to access the global constant's value, not an error that needs fixing.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
📚 Learning: in woocommerce codebase, prefer using `wc_string_to_bool()` over `filter_var($value, filter_validate...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Paytrail.php:29-35
Timestamp: 2025-06-18T09:58:10.616Z
Learning: In WooCommerce codebase, prefer using `wc_string_to_bool()` over `filter_var($value, FILTER_VALIDATE_BOOLEAN)` when converting option values (especially 'yes'/'no' style flags) to boolean. The WooCommerce helper function is more idiomatic and handles the conversion consistently with core WooCommerce patterns.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
📚 Learning: in woocommerce payments settings, when processing payment provider states, if a gateway appears in t...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:471-475
Timestamp: 2025-06-17T10:59:56.461Z
Learning: In WooCommerce payments settings, when processing payment provider states, if a gateway appears in the payment providers list (from get_payment_gateways()), its underlying extension must be active by definition. Inactive or uninstalled extensions don't have their gateways available, so setting 'extension_active' to true for all processed gateways is correct.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
  • plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
  • plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
📚 Learning: in the woocommerce payments settings provider state tracking system (plugins/woocommerce/src/interna...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/Payments.php:484-488
Timestamp: 2025-06-17T11:30:23.806Z
Learning: In the WooCommerce Payments settings provider state tracking system (plugins/woocommerce/src/Internal/Admin/Settings/Payments.php), when an extension is deactivated and its snapshot key disappears, only the 'extension_active' flag should be set to false while keeping other state flags like 'account_connected', 'needs_setup', 'test_mode', etc. unchanged. This is intentional behavior to preserve historical state information.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
📚 Learning: in plugins/woocommerce/client/admin/client/settings-payments/components/other-payment-gateways/other...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/client/admin/client/settings-payments/components/other-payment-gateways/other-payment-gateways.tsx:43-50
Timestamp: 2025-06-18T07:56:06.961Z
Learning: In plugins/woocommerce/client/admin/client/settings-payments/components/other-payment-gateways/other-payment-gateways.tsx, the user vladolaru prefers to keep the current setUpPlugin function signature with optional positional context parameter rather than refactoring to an options object or making context required.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
📚 Learning: the `kp_settings_page::get_setting_status('credentials')` method from the klarna plugin returns a bo...
Learnt from: vladolaru
PR: woocommerce/woocommerce#58784
File: plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Klarna.php:26-33
Timestamp: 2025-06-17T10:34:20.195Z
Learning: The `KP_Settings_Page::get_setting_status('credentials')` method from the Klarna plugin returns a boolean value, not string values like 'complete' or 'incomplete'. The usage of `FILTER_VALIDATE_BOOLEAN` with this method is appropriate.

Applied to files:

  • plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
🧬 Code Graph Analysis (2)
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php (1)
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php (2)
  • process_webhook (28-49)
  • WC_Gateway_Paypal_Webhook_Handler (21-200)
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php (2)
plugins/woocommerce/includes/wc-core-functions.php (1)
  • wc_print_r (2179-2214)
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php (1)
  • process_webhook (28-49)
🪛 PHPMD (2.15.0)
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php

81-81: Avoid unused local variables such as '$data'. (Unused Code Rules)

(UnusedLocalVariable)

plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php

70-70: Avoid unused local variables such as '$result'. (Unused Code Rules)

(UnusedLocalVariable)


124-124: Avoid unused parameters such as '$value'. (Unused Code Rules)

(UnusedFormalParameter)


124-124: Avoid unused parameters such as '$parsed_url'. (Unused Code Rules)

(UnusedFormalParameter)


152-152: Avoid unused parameters such as '$value'. (Unused Code Rules)

(UnusedFormalParameter)


152-152: Avoid unused parameters such as '$parsed_url'. (Unused Code Rules)

(UnusedFormalParameter)

plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php

460-460: Avoid unused local variables such as '$include_tax'. (Unused Code Rules)

(UnusedLocalVariable)


460-460: Avoid unused local variables such as '$rounding_enabled'. (Unused Code Rules)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
  • GitHub Check: Blocks e2e tests 2/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 8/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 1/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 5/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 3/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
  • GitHub Check: Blocks e2e tests 7/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 6/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 10/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 9/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Blocks e2e tests 4/10 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Lint - @woocommerce/plugin-woocommerce
  • GitHub Check: build
🔇 Additional comments (19)
plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php (3)

8-19: LGTM! Good use of strict types and safe class inclusion.

The strict types declaration improves type safety, and the conditional class inclusion with class_exists() check prevents redefinition errors. The helper method call to determine Orders v2 usage is appropriate for the conditional settings logic.


21-108: LGTM! Improved settings structure and user experience.

The settings reorganization enhances the user experience with clearer payment action options ('Capture' vs 'Authorize') and better structure. The consolidated base settings array improves maintainability.


110-199: LGTM! Smart conditional loading of legacy settings.

The conditional inclusion of legacy settings based on Orders v2 usage is excellent. This approach:

  • Keeps the interface clean for users migrating to Orders v2
  • Maintains backward compatibility for legacy API users
  • Reduces configuration complexity for new integrations

The implementation is clear and maintainable.

plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php (3)

44-57: Remove test webhook endpoint before production.

The test webhook endpoint is marked for removal with TODO comments. Ensure this development endpoint is removed before the final merge to avoid exposing unnecessary endpoints in production.


54-66: Verify webhook security considerations.

Both webhook endpoints use 'permission_callback' => '__return_true', which allows unrestricted access. While this may be appropriate for PayPal webhooks (external service), consider whether additional security measures are needed such as:

  • IP allowlisting for PayPal webhook IPs
  • Signature verification within the webhook handler
  • Rate limiting

91-101: LGTM! Solid webhook processing implementation.

The webhook processing method demonstrates good practices:

  • Safe class loading with include_once
  • Proper delegation to dedicated handler class
  • Appropriate error handling with try/catch
  • Correct HTTP status codes in responses
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php (3)

1-19: LGTM! Clean class structure following WooCommerce standards.

The class follows good practices with strict types declaration, proper ABSPATH security check, and clean structure. The Jetpack import is appropriate for the connection functionality.


26-51: LGTM! Well-designed decision logic with performance optimization.

The method demonstrates excellent design:

  • Filter hook provides extensibility for external overrides
  • Early exit strategy avoids expensive Jetpack connection checks when other conditions aren't met
  • Clear documentation of the optimization reasoning
  • Logical flow from basic eligibility to hard requirements

58-78: LGTM! Robust helper methods with safe array handling.

Both helper methods are well-implemented:

  • Migration eligibility logic correctly identifies merchants who can be safely migrated (no legacy API credentials)
  • Safe handling of potentially missing array keys using null coalescing operator
  • Clear business logic that prevents migration when legacy features might be in use
  • Simple and reliable ToS acceptance check
plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php (5)

1-15: LGTM! Proper test class structure.

The test class follows WooCommerce testing standards with appropriate base class extension, strict types declaration, and correct class loading.


20-32: LGTM! Comprehensive error scenario testing.

The test properly verifies error handling by mocking an API error response and asserting that the method returns null gracefully. Good use of filter hooks for mocking HTTP requests.


37-50: LGTM! Effective success scenario validation.

The test properly validates the success path by checking for expected response structure (id and redirect_url keys). Consistent test pattern with proper setup and cleanup.


83-114: LGTM! Thorough parameter validation.

The parameter validation logic is comprehensive, testing all crucial aspects:

  • Request headers and method
  • Payment intent and amounts
  • Currency codes and breakdowns
  • Item details and quantities
  • Application context URLs
  • Custom ID structure with order metadata

This ensures the PayPal API integration sends correctly formatted requests.


124-155: Helper method parameters are required by filter signature.

The static analysis flags unused parameters in the helper methods, but these parameters are required to match the pre_http_request filter signature. This is not an issue - the methods correctly implement the expected interface.

plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php (1)

152-170: Well-implemented order validation.

The method properly validates both the order ID and order key, preventing unauthorized access to orders. Good security practice.

plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php (2)

463-478: Well-implemented Orders v2 payment processing.

The conditional logic properly handles both Orders v2 and legacy flows, with appropriate error handling and user-friendly error messages.


188-208: Good implementation of conditional site registration.

The method properly checks prerequisites and handles errors gracefully without disrupting the gateway initialization.

plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php (2)

111-156: Well-implemented PayPal order creation with proper error handling.

The method properly validates responses, handles errors gracefully, and returns structured data. Good use of exception handling for error cases.


427-442: Good validation of custom_id length constraint.

The method properly enforces PayPal's 255 character limit for custom_id and throws a descriptive exception if exceeded.

📝 Walkthrough

Walkthrough

This change introduces comprehensive support for the PayPal Orders v2 API in WooCommerce, including new helper and webhook handler classes, REST API proxy and webhook controllers, and conditional logic to switch between legacy and Orders v2 flows. It updates the PayPal gateway, request handling, and settings, and adds unit tests for the new API integration.

Changes

Cohort / File(s) Change Summary
PayPal Gateway Core Integration
plugins/woocommerce/includes/gateways/paypal/class-wc-gateway-paypal.php
Enhanced the PayPal gateway with Jetpack connection management, conditional Orders v2 API support, new methods for connection status and blog token retrieval, dynamic settings form filtering, and updated payment processing/capture logic to support both legacy and Orders v2 flows.
PayPal Helper Utilities
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-helper.php
Introduced a strict-typed helper class to determine eligibility and conditions for using Orders v2, including ToS acceptance and Jetpack connection checks, with filter support for external overrides.
PayPal Request Handling
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php
Added strict typing, a new invoice ID length constant, and extensive new methods for creating/capturing/authorizing PayPal Orders v2 payments, with robust parameter building, response validation, and error handling. Existing methods updated for consistency and type safety.
PayPal Webhook Handling
plugins/woocommerce/includes/gateways/paypal/includes/class-wc-gateway-paypal-webhook-handler.php
Added a new class to process PayPal webhook events, route them based on event type, update WooCommerce orders accordingly, and handle payment capture/authorization, with logging and order validation.
PayPal Settings Refactor
plugins/woocommerce/includes/gateways/paypal/includes/settings-paypal.php
Refactored settings with strict types, conditional inclusion of legacy settings based on Orders v2 usage, improved array structure, and updated input options for payment action and debug settings.
REST API Proxy Controllers
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php,
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-proxy-controller.php
Added new REST API controllers to proxy PayPal API and webhook requests, including endpoints for order creation, payment capture, authorization, and webhook forwarding, with logging, error handling, and credential management.
REST API Webhook Controller
plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-webhooks-controller.php
Introduced a REST API controller to process PayPal webhook events, invoking the new webhook handler and providing a test endpoint.
REST API Server Registration
plugins/woocommerce/includes/rest-api/Server.php
Registered new PayPal proxy and webhook controllers in the REST API server for the wc/v3 namespace, marked as temporary with TODO comments.
Unit Tests for PayPal Request
plugins/woocommerce/tests/php/includes/gateways/paypal/class-wc-gateway-paypal-request-test.php
Added unit tests for PayPal Orders v2 order creation, covering success and error scenarios, and verifying request parameter correctness via mocks and assertions.

Sequence Diagram(s)

PayPal Orders v2 Payment Flow (High-Level)

sequenceDiagram
    participant Customer
    participant WooCommerce (WC_Gateway_Paypal)
    participant PayPal Proxy API (WC_REST_Paypal_Proxy_Controller)
    participant PayPal API
    participant Webhook Handler

    Customer->>WooCommerce (WC_Gateway_Paypal): Place Order
    WooCommerce (WC_Gateway_Paypal)->>PayPal Proxy API: Create PayPal Order
    PayPal Proxy API->>PayPal API: POST /v2/checkout/orders
    PayPal API-->>PayPal Proxy API: Order ID + Approval URL
    PayPal Proxy API-->>WooCommerce (WC_Gateway_Paypal): Order ID + Approval URL
    WooCommerce (WC_Gateway_Paypal)-->>Customer: Redirect to Approval URL

    Customer->>PayPal API: Approve Payment
    PayPal API->>Webhook Handler: Send Webhook (e.g., ORDER.APPROVED)
    Webhook Handler->>WooCommerce (WC_Gateway_Paypal): Update Order, Capture/Authorize Payment
    WooCommerce (WC_Gateway_Paypal)->>PayPal Proxy API: Capture/Authorize Payment
    PayPal Proxy API->>PayPal API: POST /capture or /authorize
    PayPal API-->>PayPal Proxy API: Payment Status
    PayPal Proxy API-->>WooCommerce (WC_Gateway_Paypal): Payment Status
    WooCommerce (WC_Gateway_Paypal)->>Order: Update Status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • woocommerce/woocommerce#60017: Also modifies the WC_Gateway_Paypal class to support PayPal Orders v2 API, including the should_use_orders_v2() method and updates to process_payment(), indicating a direct code-level relationship.

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/save-pp-status

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Mayisha Mayisha requested a review from annemirasol July 31, 2025 15:04
Copy link

@annemirasol annemirasol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as expected. I don't see the duplicate request anymore.

@Mayisha Mayisha merged commit 7a16cc9 into feature/paypal-wps-migration Aug 4, 2025
37 checks passed
@Mayisha Mayisha deleted the fix/save-pp-status branch August 4, 2025 04:47
Copy link
Contributor

github-actions bot commented Aug 4, 2025

⚠️ API Documentation Reminder

Hi @Mayisha! Your PR contains REST API changes. Please consider updating the REST API documentation if your changes affect the public API.

Changed API files:

plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-paypal-proxy-controller.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: woocommerce Issues related to the WooCommerce Core plugin.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants