From 7639bc4ab6b99914894f8f69d99bba0fd463fa51 Mon Sep 17 00:00:00 2001 From: Daniel Guerra <15204776+danielmx-dev@users.noreply.github.com> Date: Tue, 17 Dec 2024 00:13:56 -0600 Subject: [PATCH 01/38] Checkout: Fix error when wc_address_i18n_params does not have data for a given country (#9974) --- ...low-addresses-from-woo-supported-countries | 4 + client/checkout/utils/test/upe.test.js | 138 +++++++++++++++++- client/checkout/utils/upe.js | 4 +- 3 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 changelog/fix-allow-addresses-from-woo-supported-countries diff --git a/changelog/fix-allow-addresses-from-woo-supported-countries b/changelog/fix-allow-addresses-from-woo-supported-countries new file mode 100644 index 00000000000..626fd1ce34f --- /dev/null +++ b/changelog/fix-allow-addresses-from-woo-supported-countries @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Checkout: Fix error when wc_address_i18n_params does not have data for a given country diff --git a/client/checkout/utils/test/upe.test.js b/client/checkout/utils/test/upe.test.js index 7357840b51a..91ad592d5cf 100644 --- a/client/checkout/utils/test/upe.test.js +++ b/client/checkout/utils/test/upe.test.js @@ -12,6 +12,7 @@ import { isUsingSavedPaymentMethod, dispatchChangeEventFor, togglePaymentMethodForCountry, + isBillingInformationMissing, } from '../upe'; import { getPaymentMethodsConstants } from '../../constants'; @@ -22,11 +23,134 @@ jest.mock( 'wcpay/utils/checkout' ); jest.mock( '../../constants', () => { return { + ...jest.requireActual( '../../constants' ), getPaymentMethodsConstants: jest.fn(), }; } ); +function buildForm( fields ) { + const form = document.createElement( 'form' ); + fields.forEach( ( field ) => { + const input = document.createElement( 'input' ); + input.id = field.id; + input.value = field.value; + form.appendChild( input ); + } ); + return form; +} + describe( 'UPE checkout utils', () => { + describe( 'isBillingInformationMissing', () => { + beforeAll( () => { + window.wc_address_i18n_params = { + locale: { + US: {}, + HK: { + postcode: { required: false }, + }, + default: { + address_1: { required: true }, + postcode: { required: true }, + }, + }, + }; + } ); + + beforeEach( () => { + getUPEConfig.mockImplementation( ( argument ) => { + if ( argument === 'enabledBillingFields' ) { + return { + billing_first_name: { + required: true, + }, + billing_last_name: { + required: true, + }, + billing_company: { + required: false, + }, + billing_country: { + required: true, + }, + billing_address_1: { + required: true, + }, + billing_address_2: { + required: false, + }, + billing_city: { + required: true, + }, + billing_state: { + required: true, + }, + billing_postcode: { + required: true, + }, + billing_phone: { + required: true, + }, + billing_email: { + required: true, + }, + }; + } + } ); + } ); + + it( 'should return false when the billing information is not missing', () => { + const form = buildForm( [ + { id: 'billing_first_name', value: 'Test' }, + { id: 'billing_last_name', value: 'User' }, + { id: 'billing_email', value: 'test@example.com' }, + { id: 'billing_country', value: 'US' }, + { id: 'billing_address_1', value: '123 Main St' }, + { id: 'billing_city', value: 'Anytown' }, + { id: 'billing_postcode', value: '12345' }, + ] ); + expect( isBillingInformationMissing( form ) ).toBe( false ); + } ); + + it( 'should return true when the billing information is missing', () => { + const form = buildForm( [ + { id: 'billing_first_name', value: 'Test' }, + { id: 'billing_last_name', value: 'User' }, + { id: 'billing_email', value: 'test@example.com' }, + { id: 'billing_country', value: 'US' }, + { id: 'billing_address_1', value: '123 Main St' }, + { id: 'billing_city', value: 'Anytown' }, + { id: 'billing_postcode', value: '' }, + ] ); + expect( isBillingInformationMissing( form ) ).toBe( true ); + } ); + + it( 'should use the defaults when there is no specific locale data for a country', () => { + const form = buildForm( [ + { id: 'billing_first_name', value: 'Test' }, + { id: 'billing_last_name', value: 'User' }, + { id: 'billing_email', value: 'test@example.com' }, + { id: 'billing_country', value: 'MX' }, + { id: 'billing_address_1', value: '123 Main St' }, + { id: 'billing_city', value: 'Anytown' }, + { id: 'billing_postcode', value: '' }, + ] ); + expect( isBillingInformationMissing( form ) ).toBe( true ); + } ); + + it( 'should return false when the locale data for a country has no required fields', () => { + const form = buildForm( [ + { id: 'billing_first_name', value: 'Test' }, + { id: 'billing_last_name', value: 'User' }, + { id: 'billing_email', value: 'test@example.com' }, + { id: 'billing_country', value: 'HK' }, + { id: 'billing_address_1', value: '123 Main St' }, + { id: 'billing_city', value: 'Anytown' }, + { id: 'billing_postcode', value: '' }, + ] ); + expect( isBillingInformationMissing( form ) ).toBe( true ); + } ); + } ); + describe( 'getSelectedUPEGatewayPaymentMethod', () => { let container; @@ -54,7 +178,7 @@ describe( 'UPE checkout utils', () => { } ); test( 'Selected UPE Payment Method is card', () => { - container.innerHTML = ` { test( 'Selected UPE Payment Method is bancontact', () => { container.innerHTML = ` - `; diff --git a/client/checkout/utils/upe.js b/client/checkout/utils/upe.js index af8e0427c04..c8201ff1ba1 100644 --- a/client/checkout/utils/upe.js +++ b/client/checkout/utils/upe.js @@ -449,8 +449,8 @@ export const isBillingInformationMissing = ( form ) => { if ( country && locale && fieldName !== 'billing_email' ) { const key = fieldName.replace( 'billing_', '' ); isRequired = - locale[ country ][ key ]?.required ?? - locale.default[ key ]?.required; + locale[ country ]?.[ key ]?.required ?? + locale.default?.[ key ]?.required; } const hasValue = field?.value; From 0a312f6a1ab723a9e4e3c0069f6ea18859f1c5f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 06:31:46 +0000 Subject: [PATCH 02/38] Update version and add changelog entries for release 8.6.1 --- changelog.txt | 2 ++ changelog/update-server-container-name | 5 ----- package-lock.json | 4 ++-- package.json | 2 +- readme.txt | 5 ++++- woocommerce-payments.php | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 changelog/update-server-container-name diff --git a/changelog.txt b/changelog.txt index 969e9401b6c..0e404535b60 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,7 @@ *** WooPayments Changelog *** += 8.6.1 - 2024-12-17 = + = 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. * Add - Display credit card brand icons on order received page. diff --git a/changelog/update-server-container-name b/changelog/update-server-container-name deleted file mode 100644 index cb9580f8a22..00000000000 --- a/changelog/update-server-container-name +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Updates server container name used by E2E tests - - diff --git a/package-lock.json b/package-lock.json index 4d8d73b8e87..083ed1adf12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "woocommerce-payments", - "version": "8.6.0", + "version": "8.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "woocommerce-payments", - "version": "8.6.0", + "version": "8.6.1", "hasInstallScript": true, "license": "GPL-3.0-or-later", "dependencies": { diff --git a/package.json b/package.json index 4fa803a245c..bbcb906489d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "woocommerce-payments", - "version": "8.6.0", + "version": "8.6.1", "main": "webpack.config.js", "author": "Automattic", "license": "GPL-3.0-or-later", diff --git a/readme.txt b/readme.txt index d67f01c3951..03917a00033 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: woocommerce payments, apple pay, credit card, google pay, payment, payment Requires at least: 6.0 Tested up to: 6.7 Requires PHP: 7.3 -Stable tag: 8.6.0 +Stable tag: 8.6.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -94,6 +94,9 @@ Please note that our support for the checkout block is still experimental and th == Changelog == += 8.6.1 - 2024-12-17 = + + = 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. * Add - Display credit card brand icons on order received page. diff --git a/woocommerce-payments.php b/woocommerce-payments.php index 19012d26053..9578b077742 100644 --- a/woocommerce-payments.php +++ b/woocommerce-payments.php @@ -11,7 +11,7 @@ * WC tested up to: 9.4.0 * Requires at least: 6.0 * Requires PHP: 7.3 - * Version: 8.6.0 + * Version: 8.6.1 * Requires Plugins: woocommerce * * @package WooCommerce\Payments From 940ea23b7e330258f64e12f1186390dc7820acc2 Mon Sep 17 00:00:00 2001 From: Daniel Guerra <15204776+danielmx-dev@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:12:36 -0600 Subject: [PATCH 03/38] Skip mysqlcheck SSL Requirement during E2E environment setup (#9941) --- bin/docker-setup.sh | 4 ++-- changelog/fix-skip-ssl-requirement-env-setup | 4 ++++ tests/e2e/env/setup.sh | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelog/fix-skip-ssl-requirement-env-setup diff --git a/bin/docker-setup.sh b/bin/docker-setup.sh index 66c9fa1ee8b..6db41510908 100755 --- a/bin/docker-setup.sh +++ b/bin/docker-setup.sh @@ -27,11 +27,11 @@ cli() set +e # Wait for containers to be started up before the setup. # The db being accessible means that the db container started and the WP has been downloaded and the plugin linked -cli wp db check --path=/var/www/html --quiet > /dev/null +cli wp db check --skip_ssl --path=/var/www/html --quiet > /dev/null while [[ $? -ne 0 ]]; do echo "Waiting until the service is ready..." sleep 5 - cli wp db check --path=/var/www/html --quiet > /dev/null + cli wp db check --skip_ssl --path=/var/www/html --quiet > /dev/null done # If the plugin is already active then return early diff --git a/changelog/fix-skip-ssl-requirement-env-setup b/changelog/fix-skip-ssl-requirement-env-setup new file mode 100644 index 00000000000..691f98adbfa --- /dev/null +++ b/changelog/fix-skip-ssl-requirement-env-setup @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Skip mysqlcheck SSL Requirement during E2E environment setup diff --git a/tests/e2e/env/setup.sh b/tests/e2e/env/setup.sh index d2aa3a50e89..5ab08183bac 100755 --- a/tests/e2e/env/setup.sh +++ b/tests/e2e/env/setup.sh @@ -123,11 +123,11 @@ step "Setting up CLIENT site" # Wait for containers to be started up before the setup. # The db being accessible means that the db container started and the WP has been downloaded and the plugin linked set +e -cli wp db check --path=/var/www/html --quiet > /dev/null +cli wp db check --skip_ssl --path=/var/www/html --quiet > /dev/null while [[ $? -ne 0 ]]; do echo "Waiting until the service is ready..." sleep 5 - cli wp db check --path=/var/www/html --quiet > /dev/null + cli wp db check --skip_ssl --path=/var/www/html --quiet > /dev/null done echo "Client DB is up and running..." set -e From ef9bd6cdf8d4d40555142d5e84b5a0f67b0c2e8f Mon Sep 17 00:00:00 2001 From: Daniel Guerra <15204776+danielmx-dev@users.noreply.github.com> Date: Tue, 17 Dec 2024 00:13:56 -0600 Subject: [PATCH 04/38] Checkout: Fix error when wc_address_i18n_params does not have data for a given country (#9974) --- changelog/fix-allow-addresses-from-woo-supported-countries | 4 ++++ client/checkout/classic/event-handlers.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelog/fix-allow-addresses-from-woo-supported-countries diff --git a/changelog/fix-allow-addresses-from-woo-supported-countries b/changelog/fix-allow-addresses-from-woo-supported-countries new file mode 100644 index 00000000000..626fd1ce34f --- /dev/null +++ b/changelog/fix-allow-addresses-from-woo-supported-countries @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Checkout: Fix error when wc_address_i18n_params does not have data for a given country diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js index fe53b9b2a88..95856896226 100644 --- a/client/checkout/classic/event-handlers.js +++ b/client/checkout/classic/event-handlers.js @@ -308,8 +308,8 @@ jQuery( function ( $ ) { if ( country && locale && fieldName !== 'billing_email' ) { const key = fieldName.replace( 'billing_', '' ); isRequired = - locale[ country ][ key ]?.required ?? - locale.default[ key ]?.required; + locale[ country ]?.[ key ]?.required ?? + locale.default?.[ key ]?.required; } const hasValue = $field?.value; From 16603e308255e0fdf7f82f8bc64172d0ef583ead Mon Sep 17 00:00:00 2001 From: botwoo Date: Tue, 17 Dec 2024 08:19:29 +0000 Subject: [PATCH 05/38] Amend changelog entries for release 8.6.1 --- changelog.txt | 41 ++++++++++- changelog/8969-fallback-to-card-payment-type | 5 -- .../add-2253-clickwrap-terms-and-conditions | 4 -- .../add-6924-migrate-test-drive-capabilities | 4 -- changelog/add-9690-recommended-pm | 4 -- changelog/add-jetpack-config-callback | 4 -- ...imit-woopay-themeing-to-shortcode-checkout | 5 -- .../add-pass-footer-header-styles-to-woopay | 5 -- changelog/as-fix-ece-variable-subs | 4 -- changelog/as-fix-ece-variable-subs-free-trial | 4 -- changelog/as-hk-address | 4 -- .../chore-prb-references-in-ece-docs-and-logs | 4 -- ...e-remove-ece-error-assignment-on-loaderror | 5 -- ...emove-tokenized-payment-request-references | 5 -- .../compat-9727-avoid-early-translations | 4 -- changelog/dev-qit-auth-fix-take-2 | 4 -- ...nized-ece-product-page-base-implementation | 5 -- ...ndle-error-on-refund-during-manual-capture | 4 -- .../fix-7230-payments-details-mobile-view | 4 -- ...ix-9421-auto-enable-woopay-in-sandbox-mode | 4 -- changelog/fix-9612-inquiry-order-note | 4 -- ...ry-payment-activity-transaction-search-css | 4 -- .../fix-9787-woopay-enable-state-settings | 4 -- ...-9806-ECE-subscription-checkout-signed-out | 4 -- ...x-9830-browser-error-on-dispute-submission | 4 -- changelog/fix-9889-log-level | 4 -- changelog/fix-add-payment-method-check | 5 -- ...low-addresses-from-woo-supported-countries | 4 -- .../fix-change-woopay-theming-settings-copy | 4 -- .../fix-php-8-compatibility-errors-warnings | 5 -- .../fix-rounding-error-with-deposit-products | 4 -- changelog/fix-skip-ssl-requirement-env-setup | 4 -- changelog/fix-stripe-link-button | 4 -- .../fix-tokenized-cart-error-notice-json | 5 -- changelog/fix-upe-country-selection | 4 -- changelog/fix-upe-theme-block | 4 -- changelog/fix-use-effect-console-warning | 5 -- changelog/fix-use-type-is-in-filter | 4 -- changelog/frosso-patch-1 | 4 -- changelog/mobile-tpv-tracking-channel | 4 -- changelog/replace-from-url-query | 4 -- changelog/scope-payment-elements-selectors | 4 -- changelog/test-instructions-item-color | 4 -- .../update-1-5316-rename-bank-reference-id | 4 -- changelog/update-7900-payout-notice | 4 -- changelog/update-9910-transaction-id-label | 5 -- .../update-9916-go-live-modal-and-notice | 4 -- changelog/update-confirmation-modal-nox | 4 -- changelog/update-jetpack-onboarding-flow | 4 -- ...s-capabilities-to-onboarding-as-get-params | 4 -- changelog/update-server-container-name | 5 -- changelog/update-to-standalone-jt | 4 -- readme.txt | 72 +++++++++++++++++++ 53 files changed, 112 insertions(+), 217 deletions(-) delete mode 100644 changelog/8969-fallback-to-card-payment-type delete mode 100644 changelog/add-2253-clickwrap-terms-and-conditions delete mode 100644 changelog/add-6924-migrate-test-drive-capabilities delete mode 100644 changelog/add-9690-recommended-pm delete mode 100644 changelog/add-jetpack-config-callback delete mode 100644 changelog/add-limit-woopay-themeing-to-shortcode-checkout delete mode 100644 changelog/add-pass-footer-header-styles-to-woopay delete mode 100644 changelog/as-fix-ece-variable-subs delete mode 100644 changelog/as-fix-ece-variable-subs-free-trial delete mode 100644 changelog/as-hk-address delete mode 100644 changelog/chore-prb-references-in-ece-docs-and-logs delete mode 100644 changelog/chore-remove-ece-error-assignment-on-loaderror delete mode 100644 changelog/chore-remove-tokenized-payment-request-references delete mode 100644 changelog/compat-9727-avoid-early-translations delete mode 100644 changelog/dev-qit-auth-fix-take-2 delete mode 100644 changelog/feat-tokenized-ece-product-page-base-implementation delete mode 100644 changelog/fix-5671-handle-error-on-refund-during-manual-capture delete mode 100644 changelog/fix-7230-payments-details-mobile-view delete mode 100644 changelog/fix-9421-auto-enable-woopay-in-sandbox-mode delete mode 100644 changelog/fix-9612-inquiry-order-note delete mode 100644 changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css delete mode 100644 changelog/fix-9787-woopay-enable-state-settings delete mode 100644 changelog/fix-9806-ECE-subscription-checkout-signed-out delete mode 100644 changelog/fix-9830-browser-error-on-dispute-submission delete mode 100644 changelog/fix-9889-log-level delete mode 100644 changelog/fix-add-payment-method-check delete mode 100644 changelog/fix-allow-addresses-from-woo-supported-countries delete mode 100644 changelog/fix-change-woopay-theming-settings-copy delete mode 100644 changelog/fix-php-8-compatibility-errors-warnings delete mode 100644 changelog/fix-rounding-error-with-deposit-products delete mode 100644 changelog/fix-skip-ssl-requirement-env-setup delete mode 100644 changelog/fix-stripe-link-button delete mode 100644 changelog/fix-tokenized-cart-error-notice-json delete mode 100644 changelog/fix-upe-country-selection delete mode 100644 changelog/fix-upe-theme-block delete mode 100644 changelog/fix-use-effect-console-warning delete mode 100644 changelog/fix-use-type-is-in-filter delete mode 100644 changelog/frosso-patch-1 delete mode 100644 changelog/mobile-tpv-tracking-channel delete mode 100644 changelog/replace-from-url-query delete mode 100644 changelog/scope-payment-elements-selectors delete mode 100644 changelog/test-instructions-item-color delete mode 100644 changelog/update-1-5316-rename-bank-reference-id delete mode 100644 changelog/update-7900-payout-notice delete mode 100644 changelog/update-9910-transaction-id-label delete mode 100644 changelog/update-9916-go-live-modal-and-notice delete mode 100644 changelog/update-confirmation-modal-nox delete mode 100644 changelog/update-jetpack-onboarding-flow delete mode 100644 changelog/update-pass-capabilities-to-onboarding-as-get-params delete mode 100644 changelog/update-server-container-name delete mode 100644 changelog/update-to-standalone-jt diff --git a/changelog.txt b/changelog.txt index 969e9401b6c..7661027758a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,37 +1,76 @@ *** WooPayments Changelog *** -= 8.6.0 - 2024-12-04 = += 8.6.1 - 2024-12-17 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. +* Add - Clickwrap terms and conditions support on WooPay * Add - Display credit card brand icons on order received page. +* Add - Implement gateway method to retrieve recommended payment method. +* Add - Migrate active capabilities from test-drive account when switching to live account. +* Fix - Added conditional use of Jetpack Config callback to avoid i18n notices. * Fix - Add support to load stripe js asynchronously when it is not immediately available in the global scope. * Fix - Add the missing "Download" column heading label and toggle menu option to the Payments → Documents list view table. +* Fix - Browser error no longer shows after dispute evidence submission +* Fix - Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. +* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country +* Fix - Consider WooPay eligibility when retrieving WooPay enable state in the settings. +* Fix - Enable ECE for Virtual Variable Subscriptions with Free Trials. * Fix - Ensure ECE button load events are triggered for multiple buttons on the same page. * Fix - Ensure ECE is displayed correctly taking into account the tax settings. +* Fix - Ensure ECE login confirmation dialog is shown on Blocks. +* Fix - Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. +* Fix - Errors were incorrectly marked as info in logs. * Fix - Evidence submission is no longer available for Klarna inquiries as this is not supported by Stripe / Klarna. * Fix - fix: express checkout to use its own css files. * Fix - fix: missing ece is_product_page checks +* Fix - fix: undefined $cart_contains_subscription * Fix - Fix ECE Tracks events not triggering when WooPay is disabled. +* Fix - Fixed Affirm using black logo on dark themes +* Fix - Fixed an issue where order metadata was not updated when capturing an order in the processing state. +* Fix - Fixed UPE country detection in Checkout for non-logged in users +* Fix - Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. +* Fix - Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" +* Fix - Fix styling of transaction details page in mobile view. * Fix - Fix WooPay component spacing. * Fix - Fix WooPay trial subscriptions purchases. * Fix - Make sure the status of manual capture enablement is fetched from the right place. +* Fix - Normalize HK addresses for ECE +* Fix - Order notes for inquiries have clearer content. * Fix - Prevent express checkout from being used if cart total becomes zero after coupon usage. +* Fix - Remove translations during initialization, preventing unnecessary warnings. * Fix - Resolved issue with terminal payments in the payment intent failed webhook processing. +* Fix - Restrict Stripe Link to credit card payment method and improve cleanup. * Fix - Set the support phone field as mandatory in the settings page. +* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup +* Fix - Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. * Fix - Update Link logo alignment issue when WooPay is enabled and a specific version of Gutenberg is enabled. * Fix - Use paragraph selector instead of label for pmme appearance * Fix - Validate required billing fields using data from objects instead of checking the labels. +* Update - Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. +* Update - Adjust the go-live modal to match the latest design. * Update - Avoid getting the appearance for pay for order page with the wrong appearance key. +* Update - Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. +* Update - chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. * Update - chore: rename wrapper from payment-request to express-checkout +* Update - Ensure more robust selectors scoping to improve theme compatibility. * Update - feat: add `wcpay_checkout_use_plain_method_label` filter to allow themes or merchants to force the "plain" WooPayments label on shortcode checkout. +* Update - Make test instructions copy icon use the same color as the text next to it * Update - refactor: express checkout initialization page location checks * Update - refactor: express checkout utility for button UI interactions +* Update - Remove payout timing notice and update the help tooltil on Payments Overview page. +* Update - Update confirmation modal after onbarding +* Update - Update Jetpack onboarding flow +* Update - WooPay theming copy in the settings page +* Dev - Add support for utilizing NOX capabilities as URL parameters during account creation. * Dev - Allow redirect to the settings page from WCPay connect * Dev - chore: removed old PRB implementation for ApplePay/GooglePay in favor of the ECE implementation; cleaned up ECE feature flag; * Dev - Disable visual regression testing from Playwright until a more reliable approach is defined. * Dev - Ensure proper return types in the webhook processing service. * Dev - fix: E_DEPRECATED on BNPL empty PMME +* Dev - Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. * Dev - Fix return types +* Dev - Refine verification for disabling ECE on subscriptions that require shipping. * Dev - Update snapshots for E2E Playwright screenshots +* Dev - Update the tunelling setup. = 8.5.1 - 2024-11-25 = * Fix - fix: remove "test mode" badge from shortcode checkout. diff --git a/changelog/8969-fallback-to-card-payment-type b/changelog/8969-fallback-to-card-payment-type deleted file mode 100644 index ee66dbfa7e7..00000000000 --- a/changelog/8969-fallback-to-card-payment-type +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: Small change to payment method types fallback scenario. - - diff --git a/changelog/add-2253-clickwrap-terms-and-conditions b/changelog/add-2253-clickwrap-terms-and-conditions deleted file mode 100644 index ac0a4ece4b7..00000000000 --- a/changelog/add-2253-clickwrap-terms-and-conditions +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Clickwrap terms and conditions support on WooPay diff --git a/changelog/add-6924-migrate-test-drive-capabilities b/changelog/add-6924-migrate-test-drive-capabilities deleted file mode 100644 index 7b280af4d92..00000000000 --- a/changelog/add-6924-migrate-test-drive-capabilities +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Migrate active capabilities from test-drive account when switching to live account. diff --git a/changelog/add-9690-recommended-pm b/changelog/add-9690-recommended-pm deleted file mode 100644 index 2d615350daa..00000000000 --- a/changelog/add-9690-recommended-pm +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Implement gateway method to retrieve recommended payment method. diff --git a/changelog/add-jetpack-config-callback b/changelog/add-jetpack-config-callback deleted file mode 100644 index 64b1a2abb1b..00000000000 --- a/changelog/add-jetpack-config-callback +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Added conditional use of Jetpack Config callback to avoid i18n notices. diff --git a/changelog/add-limit-woopay-themeing-to-shortcode-checkout b/changelog/add-limit-woopay-themeing-to-shortcode-checkout deleted file mode 100644 index 4c089593b1f..00000000000 --- a/changelog/add-limit-woopay-themeing-to-shortcode-checkout +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: add -Comment: Updates the availability criteria of WooPay Global theme-ing feature. This feature is unreleased and behind a feature flag. - - diff --git a/changelog/add-pass-footer-header-styles-to-woopay b/changelog/add-pass-footer-header-styles-to-woopay deleted file mode 100644 index ab6375db250..00000000000 --- a/changelog/add-pass-footer-header-styles-to-woopay +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: add -Comment: Impovements to WooPay themeing, which is not yet released to the public. - - diff --git a/changelog/as-fix-ece-variable-subs b/changelog/as-fix-ece-variable-subs deleted file mode 100644 index 236497bcab9..00000000000 --- a/changelog/as-fix-ece-variable-subs +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Refine verification for disabling ECE on subscriptions that require shipping. diff --git a/changelog/as-fix-ece-variable-subs-free-trial b/changelog/as-fix-ece-variable-subs-free-trial deleted file mode 100644 index 64d67393c06..00000000000 --- a/changelog/as-fix-ece-variable-subs-free-trial +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Enable ECE for Virtual Variable Subscriptions with Free Trials. diff --git a/changelog/as-hk-address b/changelog/as-hk-address deleted file mode 100644 index d58ddb9ffd9..00000000000 --- a/changelog/as-hk-address +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Normalize HK addresses for ECE diff --git a/changelog/chore-prb-references-in-ece-docs-and-logs b/changelog/chore-prb-references-in-ece-docs-and-logs deleted file mode 100644 index 887525ff7bc..00000000000 --- a/changelog/chore-prb-references-in-ece-docs-and-logs +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. diff --git a/changelog/chore-remove-ece-error-assignment-on-loaderror b/changelog/chore-remove-ece-error-assignment-on-loaderror deleted file mode 100644 index cce991d09ba..00000000000 --- a/changelog/chore-remove-ece-error-assignment-on-loaderror +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: chore: remove ECE error assignment on loaderror - - diff --git a/changelog/chore-remove-tokenized-payment-request-references b/changelog/chore-remove-tokenized-payment-request-references deleted file mode 100644 index 56dc3b0a0cc..00000000000 --- a/changelog/chore-remove-tokenized-payment-request-references +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: chore: remove tokeinzed payment request code - - diff --git a/changelog/compat-9727-avoid-early-translations b/changelog/compat-9727-avoid-early-translations deleted file mode 100644 index 51432b8cd10..00000000000 --- a/changelog/compat-9727-avoid-early-translations +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Remove translations during initialization, preventing unnecessary warnings. diff --git a/changelog/dev-qit-auth-fix-take-2 b/changelog/dev-qit-auth-fix-take-2 deleted file mode 100644 index 67ec99abd79..00000000000 --- a/changelog/dev-qit-auth-fix-take-2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. diff --git a/changelog/feat-tokenized-ece-product-page-base-implementation b/changelog/feat-tokenized-ece-product-page-base-implementation deleted file mode 100644 index e0f342c1623..00000000000 --- a/changelog/feat-tokenized-ece-product-page-base-implementation +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: feat: tokenized ECE product page base implementation - - diff --git a/changelog/fix-5671-handle-error-on-refund-during-manual-capture b/changelog/fix-5671-handle-error-on-refund-during-manual-capture deleted file mode 100644 index 016c68f13aa..00000000000 --- a/changelog/fix-5671-handle-error-on-refund-during-manual-capture +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fixed an issue where order metadata was not updated when capturing an order in the processing state. diff --git a/changelog/fix-7230-payments-details-mobile-view b/changelog/fix-7230-payments-details-mobile-view deleted file mode 100644 index 93e179a44ca..00000000000 --- a/changelog/fix-7230-payments-details-mobile-view +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix styling of transaction details page in mobile view. diff --git a/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode b/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode deleted file mode 100644 index 30ec0c7fed5..00000000000 --- a/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. diff --git a/changelog/fix-9612-inquiry-order-note b/changelog/fix-9612-inquiry-order-note deleted file mode 100644 index 3fce0a23430..00000000000 --- a/changelog/fix-9612-inquiry-order-note +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Order notes for inquiries have clearer content. diff --git a/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css b/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css deleted file mode 100644 index 3841ea6164e..00000000000 --- a/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. diff --git a/changelog/fix-9787-woopay-enable-state-settings b/changelog/fix-9787-woopay-enable-state-settings deleted file mode 100644 index cee183680df..00000000000 --- a/changelog/fix-9787-woopay-enable-state-settings +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Consider WooPay eligibility when retrieving WooPay enable state in the settings. diff --git a/changelog/fix-9806-ECE-subscription-checkout-signed-out b/changelog/fix-9806-ECE-subscription-checkout-signed-out deleted file mode 100644 index fa25afd1f10..00000000000 --- a/changelog/fix-9806-ECE-subscription-checkout-signed-out +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Ensure ECE login confirmation dialog is shown on Blocks. diff --git a/changelog/fix-9830-browser-error-on-dispute-submission b/changelog/fix-9830-browser-error-on-dispute-submission deleted file mode 100644 index 918ad744351..00000000000 --- a/changelog/fix-9830-browser-error-on-dispute-submission +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Browser error no longer shows after dispute evidence submission diff --git a/changelog/fix-9889-log-level b/changelog/fix-9889-log-level deleted file mode 100644 index d2f54e24c1a..00000000000 --- a/changelog/fix-9889-log-level +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Errors were incorrectly marked as info in logs. diff --git a/changelog/fix-add-payment-method-check b/changelog/fix-add-payment-method-check deleted file mode 100644 index 4ffc9e6342f..00000000000 --- a/changelog/fix-add-payment-method-check +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Added a check for the gateway id before comparing it - - diff --git a/changelog/fix-allow-addresses-from-woo-supported-countries b/changelog/fix-allow-addresses-from-woo-supported-countries deleted file mode 100644 index 626fd1ce34f..00000000000 --- a/changelog/fix-allow-addresses-from-woo-supported-countries +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Checkout: Fix error when wc_address_i18n_params does not have data for a given country diff --git a/changelog/fix-change-woopay-theming-settings-copy b/changelog/fix-change-woopay-theming-settings-copy deleted file mode 100644 index fa73b3672f8..00000000000 --- a/changelog/fix-change-woopay-theming-settings-copy +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -WooPay theming copy in the settings page diff --git a/changelog/fix-php-8-compatibility-errors-warnings b/changelog/fix-php-8-compatibility-errors-warnings deleted file mode 100644 index 9c393f71654..00000000000 --- a/changelog/fix-php-8-compatibility-errors-warnings +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: These changes fix some PHP compatibility errors that don't impact WooPayments behaviour. - - diff --git a/changelog/fix-rounding-error-with-deposit-products b/changelog/fix-rounding-error-with-deposit-products deleted file mode 100644 index d42215e3919..00000000000 --- a/changelog/fix-rounding-error-with-deposit-products +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. diff --git a/changelog/fix-skip-ssl-requirement-env-setup b/changelog/fix-skip-ssl-requirement-env-setup deleted file mode 100644 index 691f98adbfa..00000000000 --- a/changelog/fix-skip-ssl-requirement-env-setup +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Skip mysqlcheck SSL Requirement during E2E environment setup diff --git a/changelog/fix-stripe-link-button b/changelog/fix-stripe-link-button deleted file mode 100644 index d8acf0626f1..00000000000 --- a/changelog/fix-stripe-link-button +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Restrict Stripe Link to credit card payment method and improve cleanup. diff --git a/changelog/fix-tokenized-cart-error-notice-json b/changelog/fix-tokenized-cart-error-notice-json deleted file mode 100644 index c132e0f7eeb..00000000000 --- a/changelog/fix-tokenized-cart-error-notice-json +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fix -Comment: fix: tokenized cart error notice json - - diff --git a/changelog/fix-upe-country-selection b/changelog/fix-upe-country-selection deleted file mode 100644 index 478ffa1cfcd..00000000000 --- a/changelog/fix-upe-country-selection +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fixed UPE country detection in Checkout for non-logged in users diff --git a/changelog/fix-upe-theme-block b/changelog/fix-upe-theme-block deleted file mode 100644 index 6afa59f04d3..00000000000 --- a/changelog/fix-upe-theme-block +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fixed Affirm using black logo on dark themes diff --git a/changelog/fix-use-effect-console-warning b/changelog/fix-use-effect-console-warning deleted file mode 100644 index 45219e7b39a..00000000000 --- a/changelog/fix-use-effect-console-warning +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: fix: console warning on plugins page - - diff --git a/changelog/fix-use-type-is-in-filter b/changelog/fix-use-type-is-in-filter deleted file mode 100644 index 3639b203c36..00000000000 --- a/changelog/fix-use-type-is-in-filter +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. diff --git a/changelog/frosso-patch-1 b/changelog/frosso-patch-1 deleted file mode 100644 index e3812625698..00000000000 --- a/changelog/frosso-patch-1 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -fix: undefined $cart_contains_subscription diff --git a/changelog/mobile-tpv-tracking-channel b/changelog/mobile-tpv-tracking-channel deleted file mode 100644 index a7b990214df..00000000000 --- a/changelog/mobile-tpv-tracking-channel +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. diff --git a/changelog/replace-from-url-query b/changelog/replace-from-url-query deleted file mode 100644 index 58688e1c42f..00000000000 --- a/changelog/replace-from-url-query +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" diff --git a/changelog/scope-payment-elements-selectors b/changelog/scope-payment-elements-selectors deleted file mode 100644 index 515bb60dc2e..00000000000 --- a/changelog/scope-payment-elements-selectors +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Ensure more robust selectors scoping to improve theme compatibility. diff --git a/changelog/test-instructions-item-color b/changelog/test-instructions-item-color deleted file mode 100644 index 4bf5983e8e6..00000000000 --- a/changelog/test-instructions-item-color +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Make test instructions copy icon use the same color as the text next to it diff --git a/changelog/update-1-5316-rename-bank-reference-id b/changelog/update-1-5316-rename-bank-reference-id deleted file mode 100644 index 0a2841c0ad9..00000000000 --- a/changelog/update-1-5316-rename-bank-reference-id +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. diff --git a/changelog/update-7900-payout-notice b/changelog/update-7900-payout-notice deleted file mode 100644 index 4a49df73e41..00000000000 --- a/changelog/update-7900-payout-notice +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Remove payout timing notice and update the help tooltil on Payments Overview page. diff --git a/changelog/update-9910-transaction-id-label b/changelog/update-9910-transaction-id-label deleted file mode 100644 index 0e43652d02b..00000000000 --- a/changelog/update-9910-transaction-id-label +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fix -Comment: Change ID to uppercase in the 'Transaction ID' column label for consistency with similar unique IDs in the UI. - - diff --git a/changelog/update-9916-go-live-modal-and-notice b/changelog/update-9916-go-live-modal-and-notice deleted file mode 100644 index 789b36753a9..00000000000 --- a/changelog/update-9916-go-live-modal-and-notice +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Adjust the go-live modal to match the latest design. diff --git a/changelog/update-confirmation-modal-nox b/changelog/update-confirmation-modal-nox deleted file mode 100644 index 0ffd1af6127..00000000000 --- a/changelog/update-confirmation-modal-nox +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Update confirmation modal after onbarding diff --git a/changelog/update-jetpack-onboarding-flow b/changelog/update-jetpack-onboarding-flow deleted file mode 100644 index a28c6ac383c..00000000000 --- a/changelog/update-jetpack-onboarding-flow +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Update Jetpack onboarding flow diff --git a/changelog/update-pass-capabilities-to-onboarding-as-get-params b/changelog/update-pass-capabilities-to-onboarding-as-get-params deleted file mode 100644 index 9104e7a8f99..00000000000 --- a/changelog/update-pass-capabilities-to-onboarding-as-get-params +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Add support for utilizing NOX capabilities as URL parameters during account creation. diff --git a/changelog/update-server-container-name b/changelog/update-server-container-name deleted file mode 100644 index cb9580f8a22..00000000000 --- a/changelog/update-server-container-name +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Updates server container name used by E2E tests - - diff --git a/changelog/update-to-standalone-jt b/changelog/update-to-standalone-jt deleted file mode 100644 index 4df87f235ec..00000000000 --- a/changelog/update-to-standalone-jt +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Update the tunelling setup. diff --git a/readme.txt b/readme.txt index d67f01c3951..762cea4c9ef 100644 --- a/readme.txt +++ b/readme.txt @@ -94,6 +94,78 @@ Please note that our support for the checkout block is still experimental and th == Changelog == += 8.6.1 - 2024-12-17 = +* Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. +* Add - Clickwrap terms and conditions support on WooPay +* Add - Display credit card brand icons on order received page. +* Add - Implement gateway method to retrieve recommended payment method. +* Add - Migrate active capabilities from test-drive account when switching to live account. +* Fix - Added conditional use of Jetpack Config callback to avoid i18n notices. +* Fix - Add support to load stripe js asynchronously when it is not immediately available in the global scope. +* Fix - Add the missing "Download" column heading label and toggle menu option to the Payments → Documents list view table. +* Fix - Browser error no longer shows after dispute evidence submission +* Fix - Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. +* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country +* Fix - Consider WooPay eligibility when retrieving WooPay enable state in the settings. +* Fix - Enable ECE for Virtual Variable Subscriptions with Free Trials. +* Fix - Ensure ECE button load events are triggered for multiple buttons on the same page. +* Fix - Ensure ECE is displayed correctly taking into account the tax settings. +* Fix - Ensure ECE login confirmation dialog is shown on Blocks. +* Fix - Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. +* Fix - Errors were incorrectly marked as info in logs. +* Fix - Evidence submission is no longer available for Klarna inquiries as this is not supported by Stripe / Klarna. +* Fix - fix: express checkout to use its own css files. +* Fix - fix: missing ece is_product_page checks +* Fix - fix: undefined $cart_contains_subscription +* Fix - Fix ECE Tracks events not triggering when WooPay is disabled. +* Fix - Fixed Affirm using black logo on dark themes +* Fix - Fixed an issue where order metadata was not updated when capturing an order in the processing state. +* Fix - Fixed UPE country detection in Checkout for non-logged in users +* Fix - Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. +* Fix - Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" +* Fix - Fix styling of transaction details page in mobile view. +* Fix - Fix WooPay component spacing. +* Fix - Fix WooPay trial subscriptions purchases. +* Fix - Make sure the status of manual capture enablement is fetched from the right place. +* Fix - Normalize HK addresses for ECE +* Fix - Order notes for inquiries have clearer content. +* Fix - Prevent express checkout from being used if cart total becomes zero after coupon usage. +* Fix - Remove translations during initialization, preventing unnecessary warnings. +* Fix - Resolved issue with terminal payments in the payment intent failed webhook processing. +* Fix - Restrict Stripe Link to credit card payment method and improve cleanup. +* Fix - Set the support phone field as mandatory in the settings page. +* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup +* Fix - Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. +* Fix - Update Link logo alignment issue when WooPay is enabled and a specific version of Gutenberg is enabled. +* Fix - Use paragraph selector instead of label for pmme appearance +* Fix - Validate required billing fields using data from objects instead of checking the labels. +* Update - Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. +* Update - Adjust the go-live modal to match the latest design. +* Update - Avoid getting the appearance for pay for order page with the wrong appearance key. +* Update - Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. +* Update - chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. +* Update - chore: rename wrapper from payment-request to express-checkout +* Update - Ensure more robust selectors scoping to improve theme compatibility. +* Update - feat: add `wcpay_checkout_use_plain_method_label` filter to allow themes or merchants to force the "plain" WooPayments label on shortcode checkout. +* Update - Make test instructions copy icon use the same color as the text next to it +* Update - refactor: express checkout initialization page location checks +* Update - refactor: express checkout utility for button UI interactions +* Update - Remove payout timing notice and update the help tooltil on Payments Overview page. +* Update - Update confirmation modal after onbarding +* Update - Update Jetpack onboarding flow +* Update - WooPay theming copy in the settings page +* Dev - Add support for utilizing NOX capabilities as URL parameters during account creation. +* Dev - Allow redirect to the settings page from WCPay connect +* Dev - chore: removed old PRB implementation for ApplePay/GooglePay in favor of the ECE implementation; cleaned up ECE feature flag; +* Dev - Disable visual regression testing from Playwright until a more reliable approach is defined. +* Dev - Ensure proper return types in the webhook processing service. +* Dev - fix: E_DEPRECATED on BNPL empty PMME +* Dev - Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. +* Dev - Fix return types +* Dev - Refine verification for disabling ECE on subscriptions that require shipping. +* Dev - Update snapshots for E2E Playwright screenshots +* Dev - Update the tunelling setup. + = 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. * Add - Display credit card brand icons on order received page. From 96cec560b7c5c2ab941cbeb8b3d7c34cb987fb83 Mon Sep 17 00:00:00 2001 From: botwoo Date: Tue, 17 Dec 2024 08:20:37 +0000 Subject: [PATCH 06/38] Amend changelog entries for release 8.6.1 --- changelog.txt | 2 ++ changelog/fix-allow-addresses-from-woo-supported-countries | 4 ---- changelog/fix-skip-ssl-requirement-env-setup | 4 ---- readme.txt | 2 ++ 4 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 changelog/fix-allow-addresses-from-woo-supported-countries delete mode 100644 changelog/fix-skip-ssl-requirement-env-setup diff --git a/changelog.txt b/changelog.txt index 0e404535b60..cc6402cb77c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,8 @@ *** WooPayments Changelog *** = 8.6.1 - 2024-12-17 = +* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country +* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup = 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. diff --git a/changelog/fix-allow-addresses-from-woo-supported-countries b/changelog/fix-allow-addresses-from-woo-supported-countries deleted file mode 100644 index 626fd1ce34f..00000000000 --- a/changelog/fix-allow-addresses-from-woo-supported-countries +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Checkout: Fix error when wc_address_i18n_params does not have data for a given country diff --git a/changelog/fix-skip-ssl-requirement-env-setup b/changelog/fix-skip-ssl-requirement-env-setup deleted file mode 100644 index 691f98adbfa..00000000000 --- a/changelog/fix-skip-ssl-requirement-env-setup +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Skip mysqlcheck SSL Requirement during E2E environment setup diff --git a/readme.txt b/readme.txt index 03917a00033..0122e5021ae 100644 --- a/readme.txt +++ b/readme.txt @@ -95,6 +95,8 @@ Please note that our support for the checkout block is still experimental and th == Changelog == = 8.6.1 - 2024-12-17 = +* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country +* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup = 8.6.0 - 2024-12-04 = From d40911012f355a49e59bd43215ac16066b2aa2ee Mon Sep 17 00:00:00 2001 From: Taha Paksu <3295+tpaksu@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:31:20 +0100 Subject: [PATCH 07/38] Revert "Amend changelog entries for release 8.6.1" (#9978) --- changelog.txt | 41 +---------- changelog/8969-fallback-to-card-payment-type | 5 ++ .../add-2253-clickwrap-terms-and-conditions | 4 ++ .../add-6924-migrate-test-drive-capabilities | 4 ++ changelog/add-9690-recommended-pm | 4 ++ changelog/add-jetpack-config-callback | 4 ++ ...imit-woopay-themeing-to-shortcode-checkout | 5 ++ .../add-pass-footer-header-styles-to-woopay | 5 ++ changelog/as-fix-ece-variable-subs | 4 ++ changelog/as-fix-ece-variable-subs-free-trial | 4 ++ changelog/as-hk-address | 4 ++ .../chore-prb-references-in-ece-docs-and-logs | 4 ++ ...e-remove-ece-error-assignment-on-loaderror | 5 ++ ...emove-tokenized-payment-request-references | 5 ++ .../compat-9727-avoid-early-translations | 4 ++ changelog/dev-qit-auth-fix-take-2 | 4 ++ ...nized-ece-product-page-base-implementation | 5 ++ ...ndle-error-on-refund-during-manual-capture | 4 ++ .../fix-7230-payments-details-mobile-view | 4 ++ ...ix-9421-auto-enable-woopay-in-sandbox-mode | 4 ++ changelog/fix-9612-inquiry-order-note | 4 ++ ...ry-payment-activity-transaction-search-css | 4 ++ .../fix-9787-woopay-enable-state-settings | 4 ++ ...-9806-ECE-subscription-checkout-signed-out | 4 ++ ...x-9830-browser-error-on-dispute-submission | 4 ++ changelog/fix-9889-log-level | 4 ++ changelog/fix-add-payment-method-check | 5 ++ ...low-addresses-from-woo-supported-countries | 4 ++ .../fix-change-woopay-theming-settings-copy | 4 ++ .../fix-php-8-compatibility-errors-warnings | 5 ++ .../fix-rounding-error-with-deposit-products | 4 ++ changelog/fix-skip-ssl-requirement-env-setup | 4 ++ changelog/fix-stripe-link-button | 4 ++ .../fix-tokenized-cart-error-notice-json | 5 ++ changelog/fix-upe-country-selection | 4 ++ changelog/fix-upe-theme-block | 4 ++ changelog/fix-use-effect-console-warning | 5 ++ changelog/fix-use-type-is-in-filter | 4 ++ changelog/frosso-patch-1 | 4 ++ changelog/mobile-tpv-tracking-channel | 4 ++ changelog/replace-from-url-query | 4 ++ changelog/scope-payment-elements-selectors | 4 ++ changelog/test-instructions-item-color | 4 ++ .../update-1-5316-rename-bank-reference-id | 4 ++ changelog/update-7900-payout-notice | 4 ++ changelog/update-9910-transaction-id-label | 5 ++ .../update-9916-go-live-modal-and-notice | 4 ++ changelog/update-confirmation-modal-nox | 4 ++ changelog/update-jetpack-onboarding-flow | 4 ++ ...s-capabilities-to-onboarding-as-get-params | 4 ++ changelog/update-server-container-name | 5 ++ changelog/update-to-standalone-jt | 4 ++ readme.txt | 72 ------------------- 53 files changed, 217 insertions(+), 112 deletions(-) create mode 100644 changelog/8969-fallback-to-card-payment-type create mode 100644 changelog/add-2253-clickwrap-terms-and-conditions create mode 100644 changelog/add-6924-migrate-test-drive-capabilities create mode 100644 changelog/add-9690-recommended-pm create mode 100644 changelog/add-jetpack-config-callback create mode 100644 changelog/add-limit-woopay-themeing-to-shortcode-checkout create mode 100644 changelog/add-pass-footer-header-styles-to-woopay create mode 100644 changelog/as-fix-ece-variable-subs create mode 100644 changelog/as-fix-ece-variable-subs-free-trial create mode 100644 changelog/as-hk-address create mode 100644 changelog/chore-prb-references-in-ece-docs-and-logs create mode 100644 changelog/chore-remove-ece-error-assignment-on-loaderror create mode 100644 changelog/chore-remove-tokenized-payment-request-references create mode 100644 changelog/compat-9727-avoid-early-translations create mode 100644 changelog/dev-qit-auth-fix-take-2 create mode 100644 changelog/feat-tokenized-ece-product-page-base-implementation create mode 100644 changelog/fix-5671-handle-error-on-refund-during-manual-capture create mode 100644 changelog/fix-7230-payments-details-mobile-view create mode 100644 changelog/fix-9421-auto-enable-woopay-in-sandbox-mode create mode 100644 changelog/fix-9612-inquiry-order-note create mode 100644 changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css create mode 100644 changelog/fix-9787-woopay-enable-state-settings create mode 100644 changelog/fix-9806-ECE-subscription-checkout-signed-out create mode 100644 changelog/fix-9830-browser-error-on-dispute-submission create mode 100644 changelog/fix-9889-log-level create mode 100644 changelog/fix-add-payment-method-check create mode 100644 changelog/fix-allow-addresses-from-woo-supported-countries create mode 100644 changelog/fix-change-woopay-theming-settings-copy create mode 100644 changelog/fix-php-8-compatibility-errors-warnings create mode 100644 changelog/fix-rounding-error-with-deposit-products create mode 100644 changelog/fix-skip-ssl-requirement-env-setup create mode 100644 changelog/fix-stripe-link-button create mode 100644 changelog/fix-tokenized-cart-error-notice-json create mode 100644 changelog/fix-upe-country-selection create mode 100644 changelog/fix-upe-theme-block create mode 100644 changelog/fix-use-effect-console-warning create mode 100644 changelog/fix-use-type-is-in-filter create mode 100644 changelog/frosso-patch-1 create mode 100644 changelog/mobile-tpv-tracking-channel create mode 100644 changelog/replace-from-url-query create mode 100644 changelog/scope-payment-elements-selectors create mode 100644 changelog/test-instructions-item-color create mode 100644 changelog/update-1-5316-rename-bank-reference-id create mode 100644 changelog/update-7900-payout-notice create mode 100644 changelog/update-9910-transaction-id-label create mode 100644 changelog/update-9916-go-live-modal-and-notice create mode 100644 changelog/update-confirmation-modal-nox create mode 100644 changelog/update-jetpack-onboarding-flow create mode 100644 changelog/update-pass-capabilities-to-onboarding-as-get-params create mode 100644 changelog/update-server-container-name create mode 100644 changelog/update-to-standalone-jt diff --git a/changelog.txt b/changelog.txt index 7661027758a..969e9401b6c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,76 +1,37 @@ *** WooPayments Changelog *** -= 8.6.1 - 2024-12-17 = += 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. -* Add - Clickwrap terms and conditions support on WooPay * Add - Display credit card brand icons on order received page. -* Add - Implement gateway method to retrieve recommended payment method. -* Add - Migrate active capabilities from test-drive account when switching to live account. -* Fix - Added conditional use of Jetpack Config callback to avoid i18n notices. * Fix - Add support to load stripe js asynchronously when it is not immediately available in the global scope. * Fix - Add the missing "Download" column heading label and toggle menu option to the Payments → Documents list view table. -* Fix - Browser error no longer shows after dispute evidence submission -* Fix - Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. -* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country -* Fix - Consider WooPay eligibility when retrieving WooPay enable state in the settings. -* Fix - Enable ECE for Virtual Variable Subscriptions with Free Trials. * Fix - Ensure ECE button load events are triggered for multiple buttons on the same page. * Fix - Ensure ECE is displayed correctly taking into account the tax settings. -* Fix - Ensure ECE login confirmation dialog is shown on Blocks. -* Fix - Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. -* Fix - Errors were incorrectly marked as info in logs. * Fix - Evidence submission is no longer available for Klarna inquiries as this is not supported by Stripe / Klarna. * Fix - fix: express checkout to use its own css files. * Fix - fix: missing ece is_product_page checks -* Fix - fix: undefined $cart_contains_subscription * Fix - Fix ECE Tracks events not triggering when WooPay is disabled. -* Fix - Fixed Affirm using black logo on dark themes -* Fix - Fixed an issue where order metadata was not updated when capturing an order in the processing state. -* Fix - Fixed UPE country detection in Checkout for non-logged in users -* Fix - Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. -* Fix - Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" -* Fix - Fix styling of transaction details page in mobile view. * Fix - Fix WooPay component spacing. * Fix - Fix WooPay trial subscriptions purchases. * Fix - Make sure the status of manual capture enablement is fetched from the right place. -* Fix - Normalize HK addresses for ECE -* Fix - Order notes for inquiries have clearer content. * Fix - Prevent express checkout from being used if cart total becomes zero after coupon usage. -* Fix - Remove translations during initialization, preventing unnecessary warnings. * Fix - Resolved issue with terminal payments in the payment intent failed webhook processing. -* Fix - Restrict Stripe Link to credit card payment method and improve cleanup. * Fix - Set the support phone field as mandatory in the settings page. -* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup -* Fix - Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. * Fix - Update Link logo alignment issue when WooPay is enabled and a specific version of Gutenberg is enabled. * Fix - Use paragraph selector instead of label for pmme appearance * Fix - Validate required billing fields using data from objects instead of checking the labels. -* Update - Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. -* Update - Adjust the go-live modal to match the latest design. * Update - Avoid getting the appearance for pay for order page with the wrong appearance key. -* Update - Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. -* Update - chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. * Update - chore: rename wrapper from payment-request to express-checkout -* Update - Ensure more robust selectors scoping to improve theme compatibility. * Update - feat: add `wcpay_checkout_use_plain_method_label` filter to allow themes or merchants to force the "plain" WooPayments label on shortcode checkout. -* Update - Make test instructions copy icon use the same color as the text next to it * Update - refactor: express checkout initialization page location checks * Update - refactor: express checkout utility for button UI interactions -* Update - Remove payout timing notice and update the help tooltil on Payments Overview page. -* Update - Update confirmation modal after onbarding -* Update - Update Jetpack onboarding flow -* Update - WooPay theming copy in the settings page -* Dev - Add support for utilizing NOX capabilities as URL parameters during account creation. * Dev - Allow redirect to the settings page from WCPay connect * Dev - chore: removed old PRB implementation for ApplePay/GooglePay in favor of the ECE implementation; cleaned up ECE feature flag; * Dev - Disable visual regression testing from Playwright until a more reliable approach is defined. * Dev - Ensure proper return types in the webhook processing service. * Dev - fix: E_DEPRECATED on BNPL empty PMME -* Dev - Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. * Dev - Fix return types -* Dev - Refine verification for disabling ECE on subscriptions that require shipping. * Dev - Update snapshots for E2E Playwright screenshots -* Dev - Update the tunelling setup. = 8.5.1 - 2024-11-25 = * Fix - fix: remove "test mode" badge from shortcode checkout. diff --git a/changelog/8969-fallback-to-card-payment-type b/changelog/8969-fallback-to-card-payment-type new file mode 100644 index 00000000000..ee66dbfa7e7 --- /dev/null +++ b/changelog/8969-fallback-to-card-payment-type @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Small change to payment method types fallback scenario. + + diff --git a/changelog/add-2253-clickwrap-terms-and-conditions b/changelog/add-2253-clickwrap-terms-and-conditions new file mode 100644 index 00000000000..ac0a4ece4b7 --- /dev/null +++ b/changelog/add-2253-clickwrap-terms-and-conditions @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Clickwrap terms and conditions support on WooPay diff --git a/changelog/add-6924-migrate-test-drive-capabilities b/changelog/add-6924-migrate-test-drive-capabilities new file mode 100644 index 00000000000..7b280af4d92 --- /dev/null +++ b/changelog/add-6924-migrate-test-drive-capabilities @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Migrate active capabilities from test-drive account when switching to live account. diff --git a/changelog/add-9690-recommended-pm b/changelog/add-9690-recommended-pm new file mode 100644 index 00000000000..2d615350daa --- /dev/null +++ b/changelog/add-9690-recommended-pm @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Implement gateway method to retrieve recommended payment method. diff --git a/changelog/add-jetpack-config-callback b/changelog/add-jetpack-config-callback new file mode 100644 index 00000000000..64b1a2abb1b --- /dev/null +++ b/changelog/add-jetpack-config-callback @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Added conditional use of Jetpack Config callback to avoid i18n notices. diff --git a/changelog/add-limit-woopay-themeing-to-shortcode-checkout b/changelog/add-limit-woopay-themeing-to-shortcode-checkout new file mode 100644 index 00000000000..4c089593b1f --- /dev/null +++ b/changelog/add-limit-woopay-themeing-to-shortcode-checkout @@ -0,0 +1,5 @@ +Significance: patch +Type: add +Comment: Updates the availability criteria of WooPay Global theme-ing feature. This feature is unreleased and behind a feature flag. + + diff --git a/changelog/add-pass-footer-header-styles-to-woopay b/changelog/add-pass-footer-header-styles-to-woopay new file mode 100644 index 00000000000..ab6375db250 --- /dev/null +++ b/changelog/add-pass-footer-header-styles-to-woopay @@ -0,0 +1,5 @@ +Significance: patch +Type: add +Comment: Impovements to WooPay themeing, which is not yet released to the public. + + diff --git a/changelog/as-fix-ece-variable-subs b/changelog/as-fix-ece-variable-subs new file mode 100644 index 00000000000..236497bcab9 --- /dev/null +++ b/changelog/as-fix-ece-variable-subs @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Refine verification for disabling ECE on subscriptions that require shipping. diff --git a/changelog/as-fix-ece-variable-subs-free-trial b/changelog/as-fix-ece-variable-subs-free-trial new file mode 100644 index 00000000000..64d67393c06 --- /dev/null +++ b/changelog/as-fix-ece-variable-subs-free-trial @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Enable ECE for Virtual Variable Subscriptions with Free Trials. diff --git a/changelog/as-hk-address b/changelog/as-hk-address new file mode 100644 index 00000000000..d58ddb9ffd9 --- /dev/null +++ b/changelog/as-hk-address @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Normalize HK addresses for ECE diff --git a/changelog/chore-prb-references-in-ece-docs-and-logs b/changelog/chore-prb-references-in-ece-docs-and-logs new file mode 100644 index 00000000000..887525ff7bc --- /dev/null +++ b/changelog/chore-prb-references-in-ece-docs-and-logs @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. diff --git a/changelog/chore-remove-ece-error-assignment-on-loaderror b/changelog/chore-remove-ece-error-assignment-on-loaderror new file mode 100644 index 00000000000..cce991d09ba --- /dev/null +++ b/changelog/chore-remove-ece-error-assignment-on-loaderror @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: chore: remove ECE error assignment on loaderror + + diff --git a/changelog/chore-remove-tokenized-payment-request-references b/changelog/chore-remove-tokenized-payment-request-references new file mode 100644 index 00000000000..56dc3b0a0cc --- /dev/null +++ b/changelog/chore-remove-tokenized-payment-request-references @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: chore: remove tokeinzed payment request code + + diff --git a/changelog/compat-9727-avoid-early-translations b/changelog/compat-9727-avoid-early-translations new file mode 100644 index 00000000000..51432b8cd10 --- /dev/null +++ b/changelog/compat-9727-avoid-early-translations @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Remove translations during initialization, preventing unnecessary warnings. diff --git a/changelog/dev-qit-auth-fix-take-2 b/changelog/dev-qit-auth-fix-take-2 new file mode 100644 index 00000000000..67ec99abd79 --- /dev/null +++ b/changelog/dev-qit-auth-fix-take-2 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. diff --git a/changelog/feat-tokenized-ece-product-page-base-implementation b/changelog/feat-tokenized-ece-product-page-base-implementation new file mode 100644 index 00000000000..e0f342c1623 --- /dev/null +++ b/changelog/feat-tokenized-ece-product-page-base-implementation @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: feat: tokenized ECE product page base implementation + + diff --git a/changelog/fix-5671-handle-error-on-refund-during-manual-capture b/changelog/fix-5671-handle-error-on-refund-during-manual-capture new file mode 100644 index 00000000000..016c68f13aa --- /dev/null +++ b/changelog/fix-5671-handle-error-on-refund-during-manual-capture @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixed an issue where order metadata was not updated when capturing an order in the processing state. diff --git a/changelog/fix-7230-payments-details-mobile-view b/changelog/fix-7230-payments-details-mobile-view new file mode 100644 index 00000000000..93e179a44ca --- /dev/null +++ b/changelog/fix-7230-payments-details-mobile-view @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix styling of transaction details page in mobile view. diff --git a/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode b/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode new file mode 100644 index 00000000000..30ec0c7fed5 --- /dev/null +++ b/changelog/fix-9421-auto-enable-woopay-in-sandbox-mode @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. diff --git a/changelog/fix-9612-inquiry-order-note b/changelog/fix-9612-inquiry-order-note new file mode 100644 index 00000000000..3fce0a23430 --- /dev/null +++ b/changelog/fix-9612-inquiry-order-note @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Order notes for inquiries have clearer content. diff --git a/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css b/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css new file mode 100644 index 00000000000..3841ea6164e --- /dev/null +++ b/changelog/fix-9736-remove-temporary-payment-activity-transaction-search-css @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. diff --git a/changelog/fix-9787-woopay-enable-state-settings b/changelog/fix-9787-woopay-enable-state-settings new file mode 100644 index 00000000000..cee183680df --- /dev/null +++ b/changelog/fix-9787-woopay-enable-state-settings @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Consider WooPay eligibility when retrieving WooPay enable state in the settings. diff --git a/changelog/fix-9806-ECE-subscription-checkout-signed-out b/changelog/fix-9806-ECE-subscription-checkout-signed-out new file mode 100644 index 00000000000..fa25afd1f10 --- /dev/null +++ b/changelog/fix-9806-ECE-subscription-checkout-signed-out @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Ensure ECE login confirmation dialog is shown on Blocks. diff --git a/changelog/fix-9830-browser-error-on-dispute-submission b/changelog/fix-9830-browser-error-on-dispute-submission new file mode 100644 index 00000000000..918ad744351 --- /dev/null +++ b/changelog/fix-9830-browser-error-on-dispute-submission @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Browser error no longer shows after dispute evidence submission diff --git a/changelog/fix-9889-log-level b/changelog/fix-9889-log-level new file mode 100644 index 00000000000..d2f54e24c1a --- /dev/null +++ b/changelog/fix-9889-log-level @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Errors were incorrectly marked as info in logs. diff --git a/changelog/fix-add-payment-method-check b/changelog/fix-add-payment-method-check new file mode 100644 index 00000000000..4ffc9e6342f --- /dev/null +++ b/changelog/fix-add-payment-method-check @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Added a check for the gateway id before comparing it + + diff --git a/changelog/fix-allow-addresses-from-woo-supported-countries b/changelog/fix-allow-addresses-from-woo-supported-countries new file mode 100644 index 00000000000..626fd1ce34f --- /dev/null +++ b/changelog/fix-allow-addresses-from-woo-supported-countries @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Checkout: Fix error when wc_address_i18n_params does not have data for a given country diff --git a/changelog/fix-change-woopay-theming-settings-copy b/changelog/fix-change-woopay-theming-settings-copy new file mode 100644 index 00000000000..fa73b3672f8 --- /dev/null +++ b/changelog/fix-change-woopay-theming-settings-copy @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +WooPay theming copy in the settings page diff --git a/changelog/fix-php-8-compatibility-errors-warnings b/changelog/fix-php-8-compatibility-errors-warnings new file mode 100644 index 00000000000..9c393f71654 --- /dev/null +++ b/changelog/fix-php-8-compatibility-errors-warnings @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: These changes fix some PHP compatibility errors that don't impact WooPayments behaviour. + + diff --git a/changelog/fix-rounding-error-with-deposit-products b/changelog/fix-rounding-error-with-deposit-products new file mode 100644 index 00000000000..d42215e3919 --- /dev/null +++ b/changelog/fix-rounding-error-with-deposit-products @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. diff --git a/changelog/fix-skip-ssl-requirement-env-setup b/changelog/fix-skip-ssl-requirement-env-setup new file mode 100644 index 00000000000..691f98adbfa --- /dev/null +++ b/changelog/fix-skip-ssl-requirement-env-setup @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Skip mysqlcheck SSL Requirement during E2E environment setup diff --git a/changelog/fix-stripe-link-button b/changelog/fix-stripe-link-button new file mode 100644 index 00000000000..d8acf0626f1 --- /dev/null +++ b/changelog/fix-stripe-link-button @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Restrict Stripe Link to credit card payment method and improve cleanup. diff --git a/changelog/fix-tokenized-cart-error-notice-json b/changelog/fix-tokenized-cart-error-notice-json new file mode 100644 index 00000000000..c132e0f7eeb --- /dev/null +++ b/changelog/fix-tokenized-cart-error-notice-json @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized cart error notice json + + diff --git a/changelog/fix-upe-country-selection b/changelog/fix-upe-country-selection new file mode 100644 index 00000000000..478ffa1cfcd --- /dev/null +++ b/changelog/fix-upe-country-selection @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixed UPE country detection in Checkout for non-logged in users diff --git a/changelog/fix-upe-theme-block b/changelog/fix-upe-theme-block new file mode 100644 index 00000000000..6afa59f04d3 --- /dev/null +++ b/changelog/fix-upe-theme-block @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixed Affirm using black logo on dark themes diff --git a/changelog/fix-use-effect-console-warning b/changelog/fix-use-effect-console-warning new file mode 100644 index 00000000000..45219e7b39a --- /dev/null +++ b/changelog/fix-use-effect-console-warning @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: fix: console warning on plugins page + + diff --git a/changelog/fix-use-type-is-in-filter b/changelog/fix-use-type-is-in-filter new file mode 100644 index 00000000000..3639b203c36 --- /dev/null +++ b/changelog/fix-use-type-is-in-filter @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. diff --git a/changelog/frosso-patch-1 b/changelog/frosso-patch-1 new file mode 100644 index 00000000000..e3812625698 --- /dev/null +++ b/changelog/frosso-patch-1 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +fix: undefined $cart_contains_subscription diff --git a/changelog/mobile-tpv-tracking-channel b/changelog/mobile-tpv-tracking-channel new file mode 100644 index 00000000000..a7b990214df --- /dev/null +++ b/changelog/mobile-tpv-tracking-channel @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. diff --git a/changelog/replace-from-url-query b/changelog/replace-from-url-query new file mode 100644 index 00000000000..58688e1c42f --- /dev/null +++ b/changelog/replace-from-url-query @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" diff --git a/changelog/scope-payment-elements-selectors b/changelog/scope-payment-elements-selectors new file mode 100644 index 00000000000..515bb60dc2e --- /dev/null +++ b/changelog/scope-payment-elements-selectors @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Ensure more robust selectors scoping to improve theme compatibility. diff --git a/changelog/test-instructions-item-color b/changelog/test-instructions-item-color new file mode 100644 index 00000000000..4bf5983e8e6 --- /dev/null +++ b/changelog/test-instructions-item-color @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Make test instructions copy icon use the same color as the text next to it diff --git a/changelog/update-1-5316-rename-bank-reference-id b/changelog/update-1-5316-rename-bank-reference-id new file mode 100644 index 00000000000..0a2841c0ad9 --- /dev/null +++ b/changelog/update-1-5316-rename-bank-reference-id @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. diff --git a/changelog/update-7900-payout-notice b/changelog/update-7900-payout-notice new file mode 100644 index 00000000000..4a49df73e41 --- /dev/null +++ b/changelog/update-7900-payout-notice @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Remove payout timing notice and update the help tooltil on Payments Overview page. diff --git a/changelog/update-9910-transaction-id-label b/changelog/update-9910-transaction-id-label new file mode 100644 index 00000000000..0e43652d02b --- /dev/null +++ b/changelog/update-9910-transaction-id-label @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Change ID to uppercase in the 'Transaction ID' column label for consistency with similar unique IDs in the UI. + + diff --git a/changelog/update-9916-go-live-modal-and-notice b/changelog/update-9916-go-live-modal-and-notice new file mode 100644 index 00000000000..789b36753a9 --- /dev/null +++ b/changelog/update-9916-go-live-modal-and-notice @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Adjust the go-live modal to match the latest design. diff --git a/changelog/update-confirmation-modal-nox b/changelog/update-confirmation-modal-nox new file mode 100644 index 00000000000..0ffd1af6127 --- /dev/null +++ b/changelog/update-confirmation-modal-nox @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update confirmation modal after onbarding diff --git a/changelog/update-jetpack-onboarding-flow b/changelog/update-jetpack-onboarding-flow new file mode 100644 index 00000000000..a28c6ac383c --- /dev/null +++ b/changelog/update-jetpack-onboarding-flow @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update Jetpack onboarding flow diff --git a/changelog/update-pass-capabilities-to-onboarding-as-get-params b/changelog/update-pass-capabilities-to-onboarding-as-get-params new file mode 100644 index 00000000000..9104e7a8f99 --- /dev/null +++ b/changelog/update-pass-capabilities-to-onboarding-as-get-params @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add support for utilizing NOX capabilities as URL parameters during account creation. diff --git a/changelog/update-server-container-name b/changelog/update-server-container-name new file mode 100644 index 00000000000..cb9580f8a22 --- /dev/null +++ b/changelog/update-server-container-name @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Updates server container name used by E2E tests + + diff --git a/changelog/update-to-standalone-jt b/changelog/update-to-standalone-jt new file mode 100644 index 00000000000..4df87f235ec --- /dev/null +++ b/changelog/update-to-standalone-jt @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Update the tunelling setup. diff --git a/readme.txt b/readme.txt index 762cea4c9ef..d67f01c3951 100644 --- a/readme.txt +++ b/readme.txt @@ -94,78 +94,6 @@ Please note that our support for the checkout block is still experimental and th == Changelog == -= 8.6.1 - 2024-12-17 = -* Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. -* Add - Clickwrap terms and conditions support on WooPay -* Add - Display credit card brand icons on order received page. -* Add - Implement gateway method to retrieve recommended payment method. -* Add - Migrate active capabilities from test-drive account when switching to live account. -* Fix - Added conditional use of Jetpack Config callback to avoid i18n notices. -* Fix - Add support to load stripe js asynchronously when it is not immediately available in the global scope. -* Fix - Add the missing "Download" column heading label and toggle menu option to the Payments → Documents list view table. -* Fix - Browser error no longer shows after dispute evidence submission -* Fix - Ceil product prices after applying currency conversion, but before charm pricing and price rounding from settings is applied. -* Fix - Checkout: Fix error when wc_address_i18n_params does not have data for a given country -* Fix - Consider WooPay eligibility when retrieving WooPay enable state in the settings. -* Fix - Enable ECE for Virtual Variable Subscriptions with Free Trials. -* Fix - Ensure ECE button load events are triggered for multiple buttons on the same page. -* Fix - Ensure ECE is displayed correctly taking into account the tax settings. -* Fix - Ensure ECE login confirmation dialog is shown on Blocks. -* Fix - Ensure WooPay 'enabled by default' value is correctly set in sandbox mode. -* Fix - Errors were incorrectly marked as info in logs. -* Fix - Evidence submission is no longer available for Klarna inquiries as this is not supported by Stripe / Klarna. -* Fix - fix: express checkout to use its own css files. -* Fix - fix: missing ece is_product_page checks -* Fix - fix: undefined $cart_contains_subscription -* Fix - Fix ECE Tracks events not triggering when WooPay is disabled. -* Fix - Fixed Affirm using black logo on dark themes -* Fix - Fixed an issue where order metadata was not updated when capturing an order in the processing state. -* Fix - Fixed UPE country detection in Checkout for non-logged in users -* Fix - Fix inconsistent alignment of the download button across transactions, payouts, and disputes reporting views for a more cohesive user interface. -* Fix - Fix Jetpack onboarding URL query from "woocommerce-payments" to "woocommerce-core-profiler" -* Fix - Fix styling of transaction details page in mobile view. -* Fix - Fix WooPay component spacing. -* Fix - Fix WooPay trial subscriptions purchases. -* Fix - Make sure the status of manual capture enablement is fetched from the right place. -* Fix - Normalize HK addresses for ECE -* Fix - Order notes for inquiries have clearer content. -* Fix - Prevent express checkout from being used if cart total becomes zero after coupon usage. -* Fix - Remove translations during initialization, preventing unnecessary warnings. -* Fix - Resolved issue with terminal payments in the payment intent failed webhook processing. -* Fix - Restrict Stripe Link to credit card payment method and improve cleanup. -* Fix - Set the support phone field as mandatory in the settings page. -* Fix - Skip mysqlcheck SSL Requirement during E2E environment setup -* Fix - Support 'type_is_in' filter for Transactions list report, to allow easy filtering by multiple types. -* Fix - Update Link logo alignment issue when WooPay is enabled and a specific version of Gutenberg is enabled. -* Fix - Use paragraph selector instead of label for pmme appearance -* Fix - Validate required billing fields using data from objects instead of checking the labels. -* Update - Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. -* Update - Adjust the go-live modal to match the latest design. -* Update - Avoid getting the appearance for pay for order page with the wrong appearance key. -* Update - Change 'Bank reference key' label to 'Bank reference ID' in Payouts list column for consistency. -* Update - chore: renamed PRB references in GooglePay/ApplePay implementation docs and logs files to ECE. -* Update - chore: rename wrapper from payment-request to express-checkout -* Update - Ensure more robust selectors scoping to improve theme compatibility. -* Update - feat: add `wcpay_checkout_use_plain_method_label` filter to allow themes or merchants to force the "plain" WooPayments label on shortcode checkout. -* Update - Make test instructions copy icon use the same color as the text next to it -* Update - refactor: express checkout initialization page location checks -* Update - refactor: express checkout utility for button UI interactions -* Update - Remove payout timing notice and update the help tooltil on Payments Overview page. -* Update - Update confirmation modal after onbarding -* Update - Update Jetpack onboarding flow -* Update - WooPay theming copy in the settings page -* Dev - Add support for utilizing NOX capabilities as URL parameters during account creation. -* Dev - Allow redirect to the settings page from WCPay connect -* Dev - chore: removed old PRB implementation for ApplePay/GooglePay in favor of the ECE implementation; cleaned up ECE feature flag; -* Dev - Disable visual regression testing from Playwright until a more reliable approach is defined. -* Dev - Ensure proper return types in the webhook processing service. -* Dev - fix: E_DEPRECATED on BNPL empty PMME -* Dev - Fixing issue with parsing QIT authentication.Fixing issue with parsing QIT authentication. -* Dev - Fix return types -* Dev - Refine verification for disabling ECE on subscriptions that require shipping. -* Dev - Update snapshots for E2E Playwright screenshots -* Dev - Update the tunelling setup. - = 8.6.0 - 2024-12-04 = * Add - Add Bank reference key column in Payout reports. This will help reconcile WooPayments Payouts with bank statements. * Add - Display credit card brand icons on order received page. From 0c7422e78b84f23247d56db031aa0fde5b49a0e9 Mon Sep 17 00:00:00 2001 From: Timur Karimov Date: Tue, 17 Dec 2024 17:19:13 +0100 Subject: [PATCH 08/38] Move Promise instantiation into memoized function to ensure caching consistency (#9927) Co-authored-by: Timur Karimov --- changelog/fix-unhandled-promises | 4 + client/express-checkout/blocks/index.js | 8 +- .../utils/checkPaymentMethodIsAvailable.js | 122 +++++++++--------- 3 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 changelog/fix-unhandled-promises diff --git a/changelog/fix-unhandled-promises b/changelog/fix-unhandled-promises new file mode 100644 index 00000000000..a4d1a679405 --- /dev/null +++ b/changelog/fix-unhandled-promises @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix payment method filtering when billing country changes in Blocks checkout. diff --git a/client/express-checkout/blocks/index.js b/client/express-checkout/blocks/index.js index 764dc2292cd..a46ef99e82b 100644 --- a/client/express-checkout/blocks/index.js +++ b/client/express-checkout/blocks/index.js @@ -39,9 +39,7 @@ const expressCheckoutElementApplePay = ( api ) => ( { return false; } - return new Promise( ( resolve ) => { - checkPaymentMethodIsAvailable( 'applePay', cart, resolve ); - } ); + return checkPaymentMethodIsAvailable( 'applePay', cart ); }, } ); @@ -77,9 +75,7 @@ const expressCheckoutElementGooglePay = ( api ) => { return false; } - return new Promise( ( resolve ) => { - checkPaymentMethodIsAvailable( 'googlePay', cart, resolve ); - } ); + return checkPaymentMethodIsAvailable( 'googlePay', cart ); }, }; }; diff --git a/client/express-checkout/utils/checkPaymentMethodIsAvailable.js b/client/express-checkout/utils/checkPaymentMethodIsAvailable.js index b592169da22..5beb7e32942 100644 --- a/client/express-checkout/utils/checkPaymentMethodIsAvailable.js +++ b/client/express-checkout/utils/checkPaymentMethodIsAvailable.js @@ -14,71 +14,75 @@ import WCPayAPI from 'wcpay/checkout/api'; import { getUPEConfig } from 'wcpay/utils/checkout'; export const checkPaymentMethodIsAvailable = memoize( - ( paymentMethod, cart, resolve ) => { - // Create the DIV container on the fly - const containerEl = document.createElement( 'div' ); + ( paymentMethod, cart ) => { + return new Promise( ( resolve ) => { + // Create the DIV container on the fly + const containerEl = document.createElement( 'div' ); - // Ensure the element is hidden and doesn’t interfere with the page layout. - containerEl.style.display = 'none'; + // Ensure the element is hidden and doesn’t interfere with the page layout. + containerEl.style.display = 'none'; - document.querySelector( 'body' ).appendChild( containerEl ); + document.querySelector( 'body' ).appendChild( containerEl ); - const root = ReactDOM.createRoot( containerEl ); + const root = ReactDOM.createRoot( containerEl ); - const api = new WCPayAPI( - { - publishableKey: getUPEConfig( 'publishableKey' ), - accountId: getUPEConfig( 'accountId' ), - forceNetworkSavedCards: getUPEConfig( - 'forceNetworkSavedCards' - ), - locale: getUPEConfig( 'locale' ), - isStripeLinkEnabled: isLinkEnabled( - getUPEConfig( 'paymentMethodsConfig' ) - ), - }, - request - ); + const api = new WCPayAPI( + { + publishableKey: getUPEConfig( 'publishableKey' ), + accountId: getUPEConfig( 'accountId' ), + forceNetworkSavedCards: getUPEConfig( + 'forceNetworkSavedCards' + ), + locale: getUPEConfig( 'locale' ), + isStripeLinkEnabled: isLinkEnabled( + getUPEConfig( 'paymentMethodsConfig' ) + ), + }, + request + ); - root.render( - - resolve( false ) } + root.render( + { - let canMakePayment = false; - if ( event.availablePaymentMethods ) { - canMakePayment = - event.availablePaymentMethods[ paymentMethod ]; - } - resolve( canMakePayment ); - root.unmount(); - containerEl.remove(); - } } - /> - - ); + > + resolve( false ) } + options={ { + paymentMethods: { + amazonPay: 'never', + applePay: + paymentMethod === 'applePay' + ? 'always' + : 'never', + googlePay: + paymentMethod === 'googlePay' + ? 'always' + : 'never', + link: 'never', + paypal: 'never', + }, + } } + onReady={ ( event ) => { + let canMakePayment = false; + if ( event.availablePaymentMethods ) { + canMakePayment = + event.availablePaymentMethods[ + paymentMethod + ]; + } + resolve( canMakePayment ); + root.unmount(); + containerEl.remove(); + } } + /> + + ); + } ); } ); From 129fe05703a44bb4085b746e540865e1e6a18995 Mon Sep 17 00:00:00 2001 From: Rafael Zaleski Date: Tue, 17 Dec 2024 15:45:40 -0300 Subject: [PATCH 09/38] Refresh page when ECE is dismissed (#9888) --- .../fix-9794-refresh-page-when-ece-dismissed | 4 + client/express-checkout/event-handlers.js | 10 ++ client/express-checkout/utils/index.ts | 1 + .../express-checkout/utils/shipping-fields.js | 131 ++++++++++++++++++ .../event-handlers.js | 14 +- .../tokenized-express-checkout/utils/index.ts | 1 + .../utils/shipping-fields.js | 131 ++++++++++++++++++ 7 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 changelog/fix-9794-refresh-page-when-ece-dismissed create mode 100644 client/express-checkout/utils/shipping-fields.js create mode 100644 client/tokenized-express-checkout/utils/shipping-fields.js diff --git a/changelog/fix-9794-refresh-page-when-ece-dismissed b/changelog/fix-9794-refresh-page-when-ece-dismissed new file mode 100644 index 00000000000..7ec81b4760e --- /dev/null +++ b/changelog/fix-9794-refresh-page-when-ece-dismissed @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Refresh the cart and checkout pages when ECE is dismissed and the shipping options were modified in the payment sheet. diff --git a/client/express-checkout/event-handlers.js b/client/express-checkout/event-handlers.js index 2d1345ff752..3c59d456251 100644 --- a/client/express-checkout/event-handlers.js +++ b/client/express-checkout/event-handlers.js @@ -13,12 +13,15 @@ import { normalizeShippingAddress, normalizeLineItems, getExpressCheckoutData, + updateShippingAddressUI, } from './utils'; import { trackExpressCheckoutButtonClick, trackExpressCheckoutButtonLoad, } from './tracking'; +let lastSelectedAddress = null; + export const shippingAddressChangeHandler = async ( api, event, elements ) => { try { const response = await api.expressCheckoutECECalculateShippingOptions( @@ -29,6 +32,9 @@ export const shippingAddressChangeHandler = async ( api, event, elements ) => { elements.update( { amount: response.total.amount, } ); + + lastSelectedAddress = event.address; + event.resolve( { shippingRates: response.shipping_options, lineItems: normalizeLineItems( response.displayItems ), @@ -171,5 +177,9 @@ export const onCompletePaymentHandler = () => { }; export const onCancelHandler = () => { + if ( lastSelectedAddress ) { + updateShippingAddressUI( lastSelectedAddress ); + } + lastSelectedAddress = null; unblockUI(); }; diff --git a/client/express-checkout/utils/index.ts b/client/express-checkout/utils/index.ts index 7e6c4bf2d09..3fcb6286071 100644 --- a/client/express-checkout/utils/index.ts +++ b/client/express-checkout/utils/index.ts @@ -2,6 +2,7 @@ * Internal dependencies */ export * from './normalize'; +export * from './shipping-fields'; import { getDefaultBorderRadius } from 'wcpay/utils/express-checkout'; interface MyWindow extends Window { diff --git a/client/express-checkout/utils/shipping-fields.js b/client/express-checkout/utils/shipping-fields.js new file mode 100644 index 00000000000..f097b1eca59 --- /dev/null +++ b/client/express-checkout/utils/shipping-fields.js @@ -0,0 +1,131 @@ +/* global jQuery */ +/** + * Internal dependencies + */ +import { normalizeShippingAddress, getExpressCheckoutData } from '.'; + +/** + * Checks if the intermediate address is redacted for the given country. + * CA and GB addresses are redacted and are causing errors until WooCommerce is able to + * handle redacted addresses. + * https://developers.google.com/pay/api/web/reference/response-objects#IntermediateAddress + * + * @param {string} country - The country code. + * + * @return {boolean} True if the postcode is redacted for the country, false otherwise. + */ +const isPostcodeRedactedForCountry = ( country ) => { + return [ 'CA', 'GB' ].includes( country ); +}; + +/* + * Updates a field in a form with a new value. + * + * @param {String} formSelector - The selector for the form containing the field. + * @param {Object} fieldName - The name of the field to update. + * @param {Object} value - The new value for the field. + */ +const updateShortcodeField = ( formSelector, fieldName, value ) => { + const field = document.querySelector( + `${ formSelector } [name="${ fieldName }"]` + ); + + if ( ! field ) return; + + // Check if the field is a dropdown (country/state). + if ( field.tagName === 'SELECT' && /country|state/.test( fieldName ) ) { + const options = Array.from( field.options ); + const match = options.find( + ( opt ) => + opt.value === value || + opt.textContent.trim().toLowerCase() === value.toLowerCase() + ); + + if ( match ) { + field.value = match.value; + jQuery( field ).trigger( 'change' ).trigger( 'close' ); + } + } else { + // Default behavior for text inputs. + field.value = value; + jQuery( field ).trigger( 'change' ); + } +}; + +/** + * Updates the WooCommerce Blocks shipping UI to reflect a new shipping address. + * + * @param {Object} eventAddress - The shipping address returned by the payment event. + */ +const updateBlocksShippingUI = ( eventAddress ) => { + wp?.data + ?.dispatch( 'wc/store/cart' ) + ?.setShippingAddress( normalizeShippingAddress( eventAddress ) ); +}; + +/** + * Updates the WooCommerce shortcode cart/checkout shipping UI to reflect a new shipping address. + * + * @param {Object} eventAddress - The shipping address returned by the payment event. + */ +const updateShortcodeShippingUI = ( eventAddress ) => { + const context = getExpressCheckoutData( 'button_context' ); + const address = normalizeShippingAddress( eventAddress ); + + const keys = [ 'country', 'state', 'city', 'postcode' ]; + + if ( context === 'cart' ) { + keys.forEach( ( key ) => { + if ( address[ key ] ) { + updateShortcodeField( + 'form.woocommerce-shipping-calculator', + `calc_shipping_${ key }`, + address[ key ] + ); + } + } ); + document + .querySelector( + 'form.woocommerce-shipping-calculator [name="calc_shipping"]' + ) + ?.click(); + } else if ( context === 'checkout' ) { + keys.forEach( ( key ) => { + if ( address[ key ] ) { + updateShortcodeField( + 'form.woocommerce-checkout', + `billing_${ key }`, + address[ key ] + ); + } + } ); + } +}; + +/** + * Updates the WooCommerce shipping UI to reflect a new shipping address. + * + * Determines the current context (cart or checkout) and updates either + * WooCommerce Blocks or shortcode-based shipping forms, if applicable. + * + * @param {Object} newAddress - The new shipping address object returned by the payment event. + * @param {string} newAddress.country - The country code of the shipping address. + * @param {string} [newAddress.state] - The state/province of the shipping address. + * @param {string} [newAddress.city] - The city of the shipping address. + * @param {string} [newAddress.postcode] - The postal/ZIP code of the shipping address. + */ +export const updateShippingAddressUI = ( newAddress ) => { + const context = getExpressCheckoutData( 'button_context' ); + const isBlocks = getExpressCheckoutData( 'has_block' ); + + if ( + [ 'cart', 'checkout' ].includes( context ) && + ! isPostcodeRedactedForCountry( newAddress.country ) + ) { + if ( isBlocks ) { + updateBlocksShippingUI( newAddress ); + } else { + updateShortcodeShippingUI( newAddress ); + } + } +}; diff --git a/client/tokenized-express-checkout/event-handlers.js b/client/tokenized-express-checkout/event-handlers.js index 12ec2513cc7..db2bc4c2c3e 100644 --- a/client/tokenized-express-checkout/event-handlers.js +++ b/client/tokenized-express-checkout/event-handlers.js @@ -8,7 +8,11 @@ import { applyFilters } from '@wordpress/hooks'; /** * Internal dependencies */ -import { getErrorMessageFromNotice, getExpressCheckoutData } from './utils'; +import { + getErrorMessageFromNotice, + getExpressCheckoutData, + updateShippingAddressUI, +} from './utils'; import { trackExpressCheckoutButtonClick, trackExpressCheckoutButtonLoad, @@ -24,6 +28,7 @@ import { transformPrice, } from './transformers/wc-to-stripe'; +let lastSelectedAddress = null; let cartApi = new ExpressCheckoutCartApi(); export const setCartApiHandler = ( handler ) => ( cartApi = handler ); export const getCartApiHandler = () => cartApi; @@ -56,6 +61,9 @@ export const shippingAddressChangeHandler = async ( event, elements ) => { cartData.totals ), } ); + + lastSelectedAddress = event.address; + event.resolve( { shippingRates: transformCartDataForShippingRates( cartData ), lineItems: transformCartDataForDisplayItems( cartData ), @@ -216,5 +224,9 @@ export const onCompletePaymentHandler = () => { }; export const onCancelHandler = () => { + if ( lastSelectedAddress ) { + updateShippingAddressUI( lastSelectedAddress ); + } + lastSelectedAddress = null; unblockUI(); }; diff --git a/client/tokenized-express-checkout/utils/index.ts b/client/tokenized-express-checkout/utils/index.ts index 98ee8b90091..bae962363b2 100644 --- a/client/tokenized-express-checkout/utils/index.ts +++ b/client/tokenized-express-checkout/utils/index.ts @@ -3,6 +3,7 @@ */ import { WCPayExpressCheckoutParams } from 'wcpay/express-checkout/utils'; export * from './normalize'; +export * from './shipping-fields'; import { getDefaultBorderRadius } from 'wcpay/utils/express-checkout'; export const getExpressCheckoutData = < diff --git a/client/tokenized-express-checkout/utils/shipping-fields.js b/client/tokenized-express-checkout/utils/shipping-fields.js new file mode 100644 index 00000000000..f097b1eca59 --- /dev/null +++ b/client/tokenized-express-checkout/utils/shipping-fields.js @@ -0,0 +1,131 @@ +/* global jQuery */ +/** + * Internal dependencies + */ +import { normalizeShippingAddress, getExpressCheckoutData } from '.'; + +/** + * Checks if the intermediate address is redacted for the given country. + * CA and GB addresses are redacted and are causing errors until WooCommerce is able to + * handle redacted addresses. + * https://developers.google.com/pay/api/web/reference/response-objects#IntermediateAddress + * + * @param {string} country - The country code. + * + * @return {boolean} True if the postcode is redacted for the country, false otherwise. + */ +const isPostcodeRedactedForCountry = ( country ) => { + return [ 'CA', 'GB' ].includes( country ); +}; + +/* + * Updates a field in a form with a new value. + * + * @param {String} formSelector - The selector for the form containing the field. + * @param {Object} fieldName - The name of the field to update. + * @param {Object} value - The new value for the field. + */ +const updateShortcodeField = ( formSelector, fieldName, value ) => { + const field = document.querySelector( + `${ formSelector } [name="${ fieldName }"]` + ); + + if ( ! field ) return; + + // Check if the field is a dropdown (country/state). + if ( field.tagName === 'SELECT' && /country|state/.test( fieldName ) ) { + const options = Array.from( field.options ); + const match = options.find( + ( opt ) => + opt.value === value || + opt.textContent.trim().toLowerCase() === value.toLowerCase() + ); + + if ( match ) { + field.value = match.value; + jQuery( field ).trigger( 'change' ).trigger( 'close' ); + } + } else { + // Default behavior for text inputs. + field.value = value; + jQuery( field ).trigger( 'change' ); + } +}; + +/** + * Updates the WooCommerce Blocks shipping UI to reflect a new shipping address. + * + * @param {Object} eventAddress - The shipping address returned by the payment event. + */ +const updateBlocksShippingUI = ( eventAddress ) => { + wp?.data + ?.dispatch( 'wc/store/cart' ) + ?.setShippingAddress( normalizeShippingAddress( eventAddress ) ); +}; + +/** + * Updates the WooCommerce shortcode cart/checkout shipping UI to reflect a new shipping address. + * + * @param {Object} eventAddress - The shipping address returned by the payment event. + */ +const updateShortcodeShippingUI = ( eventAddress ) => { + const context = getExpressCheckoutData( 'button_context' ); + const address = normalizeShippingAddress( eventAddress ); + + const keys = [ 'country', 'state', 'city', 'postcode' ]; + + if ( context === 'cart' ) { + keys.forEach( ( key ) => { + if ( address[ key ] ) { + updateShortcodeField( + 'form.woocommerce-shipping-calculator', + `calc_shipping_${ key }`, + address[ key ] + ); + } + } ); + document + .querySelector( + 'form.woocommerce-shipping-calculator [name="calc_shipping"]' + ) + ?.click(); + } else if ( context === 'checkout' ) { + keys.forEach( ( key ) => { + if ( address[ key ] ) { + updateShortcodeField( + 'form.woocommerce-checkout', + `billing_${ key }`, + address[ key ] + ); + } + } ); + } +}; + +/** + * Updates the WooCommerce shipping UI to reflect a new shipping address. + * + * Determines the current context (cart or checkout) and updates either + * WooCommerce Blocks or shortcode-based shipping forms, if applicable. + * + * @param {Object} newAddress - The new shipping address object returned by the payment event. + * @param {string} newAddress.country - The country code of the shipping address. + * @param {string} [newAddress.state] - The state/province of the shipping address. + * @param {string} [newAddress.city] - The city of the shipping address. + * @param {string} [newAddress.postcode] - The postal/ZIP code of the shipping address. + */ +export const updateShippingAddressUI = ( newAddress ) => { + const context = getExpressCheckoutData( 'button_context' ); + const isBlocks = getExpressCheckoutData( 'has_block' ); + + if ( + [ 'cart', 'checkout' ].includes( context ) && + ! isPostcodeRedactedForCountry( newAddress.country ) + ) { + if ( isBlocks ) { + updateBlocksShippingUI( newAddress ); + } else { + updateShortcodeShippingUI( newAddress ); + } + } +}; From cf6d2c1f5de1df9d60c9f040757fd35a79525b35 Mon Sep 17 00:00:00 2001 From: bruce aldridge Date: Wed, 18 Dec 2024 11:09:21 +1300 Subject: [PATCH 10/38] Performance improvements for disputes reminder task list (#9906) --- changelog/fix-9716-disputes-api-requests | 4 ++ includes/admin/class-wc-payments-admin.php | 1 + .../tasks/class-wc-payments-task-disputes.php | 42 ++++++++++++++----- includes/class-wc-payments-tasks.php | 10 ++++- .../test-class-wc-payments-task-disputes.php | 1 + 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 changelog/fix-9716-disputes-api-requests diff --git a/changelog/fix-9716-disputes-api-requests b/changelog/fix-9716-disputes-api-requests new file mode 100644 index 00000000000..10f5387c9b4 --- /dev/null +++ b/changelog/fix-9716-disputes-api-requests @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Performance improvements for Disputes Needing Response task shown in WooCommerce admin. diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index 6509cc90ecb..e7ad01fe210 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -1336,6 +1336,7 @@ public function add_transactions_notification_badge() { /** * Gets the number of disputes which need a response. ie have a 'needs_response' or 'warning_needs_response' status. + * Used to display a notification badge on the Payments > Disputes menu item. * * @return int The number of disputes which need a response. */ diff --git a/includes/admin/tasks/class-wc-payments-task-disputes.php b/includes/admin/tasks/class-wc-payments-task-disputes.php index b7212ec7623..7d5ac82faf4 100644 --- a/includes/admin/tasks/class-wc-payments-task-disputes.php +++ b/includes/admin/tasks/class-wc-payments-task-disputes.php @@ -49,6 +49,13 @@ class WC_Payments_Task_Disputes extends Task { */ private $disputes_due_within_1d; + /** + * A memory cache of all disputes needing response. + * + * @var array|null + */ + private $disputes_needing_response = null; + /** * WC_Payments_Task_Disputes constructor. */ @@ -57,13 +64,12 @@ public function __construct() { $this->api_client = \WC_Payments::get_payments_api_client(); $this->database_cache = \WC_Payments::get_database_cache(); parent::__construct(); - $this->init(); } /** * Initialize the task. */ - private function init() { + private function fetch_relevant_disputes() { $this->disputes_due_within_7d = $this->get_disputes_needing_response_within_days( 7 ); $this->disputes_due_within_1d = $this->get_disputes_needing_response_within_days( 1 ); } @@ -83,6 +89,9 @@ public function get_id() { * @return string */ public function get_title() { + if ( null === $this->disputes_needing_response ) { + $this->fetch_relevant_disputes(); + } if ( count( (array) $this->disputes_due_within_7d ) === 1 ) { $dispute = $this->disputes_due_within_7d[0]; $amount = WC_Payments_Utils::interpret_stripe_amount( $dispute['amount'], $dispute['currency'] ); @@ -275,6 +284,9 @@ public function is_complete() { * @return bool */ public function can_view() { + if ( null === $this->disputes_needing_response ) { + $this->fetch_relevant_disputes(); + } return count( (array) $this->disputes_due_within_7d ) > 0; } @@ -322,15 +334,24 @@ private function get_disputes_needing_response_within_days( $num_days ) { * @return array|null Array of disputes awaiting a response. Null on failure. */ private function get_disputes_needing_response() { - return $this->database_cache->get_or_add( + if ( null !== $this->disputes_needing_response ) { + return $this->disputes_needing_response; + } + + $this->disputes_needing_response = $this->database_cache->get_or_add( Database_Cache::ACTIVE_DISPUTES_KEY, function () { - $response = $this->api_client->get_disputes( - [ - 'pagesize' => 50, - 'search' => [ 'warning_needs_response', 'needs_response' ], - ] - ); + try { + $response = $this->api_client->get_disputes( + [ + 'pagesize' => 50, + 'search' => [ 'warning_needs_response', 'needs_response' ], + ] + ); + } catch ( \Exception $e ) { + // Ensure an array is always returned, even if the API call fails. + return []; + } $active_disputes = $response['data'] ?? []; @@ -347,8 +368,9 @@ function ( $a, $b ) { return $active_disputes; }, - // We'll consider all array values to be valid as the cache is only invalidated when it is deleted or it expires. 'is_array' ); + + return $this->disputes_needing_response; } } diff --git a/includes/class-wc-payments-tasks.php b/includes/class-wc-payments-tasks.php index ee3feacff48..b0b01e22896 100644 --- a/includes/class-wc-payments-tasks.php +++ b/includes/class-wc-payments-tasks.php @@ -21,7 +21,11 @@ class WC_Payments_Tasks { * WC_Payments_Admin_Tasks constructor. */ public static function init() { - include_once WCPAY_ABSPATH . 'includes/admin/tasks/class-wc-payments-task-disputes.php'; + // As WooCommerce Onboarding tasks need to hook into 'init' and requires an API call. + // We only add this task for users who can manage_woocommerce / view the task. + if ( ! current_user_can( 'manage_woocommerce' ) ) { + return; + } add_action( 'init', [ __CLASS__, 'add_task_disputes_need_response' ] ); } @@ -31,9 +35,11 @@ public static function init() { */ public static function add_task_disputes_need_response() { $account_service = WC_Payments::get_account_service(); - if ( ! $account_service || ! $account_service->is_stripe_account_valid() ) { + // The task is not required if the account is not connected, under review, or rejected. + if ( ! $account_service || ! $account_service->is_stripe_account_valid() || $account_service->is_account_under_review() || $account_service->is_account_rejected() ) { return; } + include_once WCPAY_ABSPATH . 'includes/admin/tasks/class-wc-payments-task-disputes.php'; // 'extended' = 'Things to do next' task list on WooCommerce > Home. TaskLists::add_task( 'extended', new WC_Payments_Task_Disputes() ); diff --git a/tests/unit/admin/tasks/test-class-wc-payments-task-disputes.php b/tests/unit/admin/tasks/test-class-wc-payments-task-disputes.php index 43771de278d..729be9743f0 100644 --- a/tests/unit/admin/tasks/test-class-wc-payments-task-disputes.php +++ b/tests/unit/admin/tasks/test-class-wc-payments-task-disputes.php @@ -8,6 +8,7 @@ use WCPay\Constants\Country_Code; use WooCommerce\Payments\Tasks\WC_Payments_Task_Disputes; +require_once WCPAY_ABSPATH . 'includes/admin/tasks/class-wc-payments-task-disputes.php'; /** * WC_Payments_Task_Disputes unit tests. */ From 99db1fb899286f547481e0ef9969827b896eb606 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 18 Dec 2024 10:05:16 +0100 Subject: [PATCH 11/38] fix: tokenized cart w/ multiple variations (#9979) --- .../fix-tokenized-cart-multiple-variations | 5 +++++ client/tokenized-express-checkout/index.js | 17 ++++++++++++++++- .../transformers/wc-to-stripe.js | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 changelog/fix-tokenized-cart-multiple-variations diff --git a/changelog/fix-tokenized-cart-multiple-variations b/changelog/fix-tokenized-cart-multiple-variations new file mode 100644 index 00000000000..5d155cd5513 --- /dev/null +++ b/changelog/fix-tokenized-cart-multiple-variations @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized cart & multiple variations. + + diff --git a/client/tokenized-express-checkout/index.js b/client/tokenized-express-checkout/index.js index eca8a2f8a36..3b2eb1a8b46 100644 --- a/client/tokenized-express-checkout/index.js +++ b/client/tokenized-express-checkout/index.js @@ -326,6 +326,21 @@ jQuery( ( $ ) => { 'wcpay.express-checkout.update-button-data', 'automattic/wcpay/express-checkout', async () => { + // if the product cannot be added to cart (because of missing variation selection, etc), + // don't try to add it to the cart to get new data - the call will likely fail. + if ( + getExpressCheckoutData( 'button_context' ) === 'product' + ) { + const addToCartButton = $( + '.single_add_to_cart_button' + ); + + // First check if product can be added to cart. + if ( addToCartButton.is( '.disabled' ) ) { + return; + } + } + try { expressCheckoutButtonUi.blockButton(); @@ -375,7 +390,7 @@ jQuery( ( $ ) => { expressCheckoutButtonUi.unblockButton(); } catch ( e ) { - expressCheckoutButtonUi.hide(); + expressCheckoutButtonUi.hideContainer(); } } ); diff --git a/client/tokenized-express-checkout/transformers/wc-to-stripe.js b/client/tokenized-express-checkout/transformers/wc-to-stripe.js index 867a389006b..7c4c9c14822 100644 --- a/client/tokenized-express-checkout/transformers/wc-to-stripe.js +++ b/client/tokenized-express-checkout/transformers/wc-to-stripe.js @@ -96,7 +96,7 @@ export const transformCartDataForDisplayItems = ( cartData ) => { * @return {{id: string, label: string, amount: integer, deliveryEstimate: string}} `shippingRates` for Stripe. */ export const transformCartDataForShippingRates = ( cartData ) => - cartData.shipping_rates?.[ 0 ].shipping_rates + cartData.shipping_rates?.[ 0 ]?.shipping_rates .sort( ( rateA, rateB ) => { if ( rateA.selected === rateB.selected ) { return 0; // Keep relative order if both have the same value for 'selected' From bf363d636c639525a0fe5a1039e5bc93e4a6c08e Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Wed, 18 Dec 2024 13:29:53 +0100 Subject: [PATCH 12/38] Improve message shown to merchants when there is an error capturing/cancelling pending authorizations (#9937) --- ...there-is-an-error-capturing-authorizations | 4 + client/data/authorizations/actions.ts | 102 +++++++++-- .../data/authorizations/test/actions.test.ts | 159 +++++++++++++++++- 3 files changed, 247 insertions(+), 18 deletions(-) create mode 100644 changelog/update-5002-authorizations-improve-message-shown-to-merchants-when-there-is-an-error-capturing-authorizations diff --git a/changelog/update-5002-authorizations-improve-message-shown-to-merchants-when-there-is-an-error-capturing-authorizations b/changelog/update-5002-authorizations-improve-message-shown-to-merchants-when-there-is-an-error-capturing-authorizations new file mode 100644 index 00000000000..b76d70e1cf9 --- /dev/null +++ b/changelog/update-5002-authorizations-improve-message-shown-to-merchants-when-there-is-an-error-capturing-authorizations @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update error messages for payment authorization actions to provide more specific and user-friendly feedback. diff --git a/client/data/authorizations/actions.ts b/client/data/authorizations/actions.ts index 0eaf50f105d..0885da1cbfe 100644 --- a/client/data/authorizations/actions.ts +++ b/client/data/authorizations/actions.ts @@ -19,6 +19,51 @@ import { import { STORE_NAME } from '../constants'; import { ApiError } from 'wcpay/types/errors'; +const getErrorMessage = ( apiError: { + code?: string; + message?: string; +} ): string => { + // Map specific error codes to user-friendly messages + const errorMessages: Record< string, string > = { + wcpay_missing_order: __( + 'The order could not be found.', + 'woocommerce-payments' + ), + wcpay_refunded_order_uncapturable: __( + 'Payment cannot be processed for partially or fully refunded orders.', + 'woocommerce-payments' + ), + wcpay_intent_order_mismatch: __( + 'The payment cannot be processed due to a mismatch with order details.', + 'woocommerce-payments' + ), + wcpay_payment_uncapturable: __( + 'This payment cannot be processed in its current state.', + 'woocommerce-payments' + ), + wcpay_capture_error: __( + 'The payment capture failed to complete.', + 'woocommerce-payments' + ), + wcpay_cancel_error: __( + 'The payment cancellation failed to complete.', + 'woocommerce-payments' + ), + wcpay_server_error: __( + 'An unexpected error occurred. Please try again later.', + 'woocommerce-payments' + ), + }; + + return ( + errorMessages[ apiError.code ?? '' ] ?? + __( + 'Unable to process the payment. Please try again later.', + 'woocommerce-payments' + ) + ); +}; + export function updateAuthorizations( query: Query, data: Authorization[] @@ -165,17 +210,29 @@ export function* submitCaptureAuthorization( ) ); } catch ( error ) { + const baseErrorMessage = sprintf( + // translators: %s Order id + __( + 'There has been an error capturing the payment for order #%s.', + 'woocommerce-payments' + ), + orderId + ); + + const apiError = error as { + code?: string; + message?: string; + data?: { + status?: number; + }; + }; + + const errorDetails = getErrorMessage( apiError ); + yield controls.dispatch( 'core/notices', 'createErrorNotice', - sprintf( - // translators: %s Order id - __( - 'There has been an error capturing the payment for order #%s. Please try again later.', - 'woocommerce-payments' - ), - orderId - ) + `${ baseErrorMessage } ${ errorDetails }` ); } finally { yield controls.dispatch( @@ -184,6 +241,7 @@ export function* submitCaptureAuthorization( 'getAuthorization', [ paymentIntentId ] ); + yield controls.dispatch( STORE_NAME, 'setIsRequestingAuthorization', @@ -278,17 +336,29 @@ export function* submitCancelAuthorization( ) ); } catch ( error ) { + const baseErrorMessage = sprintf( + // translators: %s Order id + __( + 'There has been an error canceling the payment for order #%s.', + 'woocommerce-payments' + ), + orderId + ); + + const apiError = error as { + code?: string; + message?: string; + data?: { + status?: number; + }; + }; + + const errorDetails = getErrorMessage( apiError ); + yield controls.dispatch( 'core/notices', 'createErrorNotice', - sprintf( - // translators: %s Order id - __( - 'There has been an error canceling the payment for order #%s. Please try again later.', - 'woocommerce-payments' - ), - orderId - ) + `${ baseErrorMessage } ${ errorDetails }` ); } finally { yield controls.dispatch( diff --git a/client/data/authorizations/test/actions.test.ts b/client/data/authorizations/test/actions.test.ts index 1c73ab5d7a2..171ef6dd5ad 100644 --- a/client/data/authorizations/test/actions.test.ts +++ b/client/data/authorizations/test/actions.test.ts @@ -16,6 +16,7 @@ import { updateAuthorization, } from '../actions'; import authorizationsFixture from './authorizations.fixture.json'; +import { STORE_NAME } from 'wcpay/data/constants'; describe( 'Authorizations actions', () => { describe( 'submitCaptureAuthorization', () => { @@ -153,10 +154,117 @@ describe( 'Authorizations actions', () => { controls.dispatch( 'core/notices', 'createErrorNotice', - 'There has been an error capturing the payment for order #42. Please try again later.' + 'There has been an error capturing the payment for order #42. Unable to process the payment. Please try again later.' ) ); } ); + + describe( 'error handling', () => { + it( 'should create error notice with API error message', () => { + const generator = submitCaptureAuthorization( 'pi_123', 123 ); + + // Mock the start of the capture process + expect( generator.next().value ).toEqual( + controls.dispatch( + STORE_NAME, + 'startResolution', + 'getAuthorization', + [ 'pi_123' ] + ) + ); + + expect( generator.next().value ).toEqual( + controls.dispatch( + STORE_NAME, + 'setIsRequestingAuthorization', + true + ) + ); + + // Mock API error response + const apiError = { + code: 'wcpay_refunded_order_uncapturable', + message: + 'Payment cannot be captured for partially or fully refunded orders.', + data: { status: 400 }, + }; + + // Simulate API error + expect( generator.throw( apiError ).value ).toEqual( + controls.dispatch( + 'core/notices', + 'createErrorNotice', + 'There has been an error capturing the payment for order #123. Payment cannot be processed for partially or fully refunded orders.' + ) + ); + + // Verify cleanup in finally block + expect( generator.next().value ).toEqual( + controls.dispatch( + STORE_NAME, + 'finishResolution', + 'getAuthorization', + [ 'pi_123' ] + ) + ); + + expect( generator.next().value ).toEqual( + controls.dispatch( + STORE_NAME, + 'setIsRequestingAuthorization', + false + ) + ); + } ); + + it( 'should create error notice with fallback message when API error has no message', () => { + const generator = submitCaptureAuthorization( 'pi_123', 123 ); + + // Skip initial dispatch calls + generator.next(); + generator.next(); + + // Mock API error without message + const apiError = { + code: 'unknown_error', + data: { status: 500 }, + }; + + expect( generator.throw( apiError ).value ).toEqual( + controls.dispatch( + 'core/notices', + 'createErrorNotice', + 'There has been an error capturing the payment for order #123. Unable to process the payment. Please try again later.' + ) + ); + } ); + + it( 'should show default error notice for unknown error code', () => { + const generator = submitCaptureAuthorization( + 'pi_unknown', + 999 + ); + + // Start the generator to the point where it would throw an error + generator.next(); + generator.next(); + + // Mock an API error with an unknown error code + const apiError = { + code: 'unknown_error_code', + data: { status: 500 }, + }; + + // Expect the default error message to be dispatched + expect( generator.throw( apiError ).value ).toEqual( + controls.dispatch( + 'core/notices', + 'createErrorNotice', + 'There has been an error capturing the payment for order #999. Unable to process the payment. Please try again later.' + ) + ); + } ); + } ); } ); describe( 'submitCancelAuthorization', () => { @@ -294,9 +402,56 @@ describe( 'Authorizations actions', () => { controls.dispatch( 'core/notices', 'createErrorNotice', - 'There has been an error canceling the payment for order #42. Please try again later.' + 'There has been an error canceling the payment for order #42. Unable to process the payment. Please try again later.' ) ); } ); + + describe( 'error handling', () => { + it( 'should create error notice with API error message', () => { + const generator = submitCancelAuthorization( 'pi_123', 123 ); + + // Skip initial dispatch calls + generator.next(); + generator.next(); + + // Mock API error response + const apiError = { + code: 'wcpay_payment_uncapturable', + message: 'The payment cannot be canceled at this time.', + data: { status: 400 }, + }; + + expect( generator.throw( apiError ).value ).toEqual( + controls.dispatch( + 'core/notices', + 'createErrorNotice', + 'There has been an error canceling the payment for order #123. This payment cannot be processed in its current state.' + ) + ); + } ); + + it( 'should create error notice with fallback message when API error has no message', () => { + const generator = submitCancelAuthorization( 'pi_123', 123 ); + + // Skip initial dispatch calls + generator.next(); + generator.next(); + + // Mock API error without message + const apiError = { + code: 'unknown_error', + data: { status: 500 }, + }; + + expect( generator.throw( apiError ).value ).toEqual( + controls.dispatch( + 'core/notices', + 'createErrorNotice', + 'There has been an error canceling the payment for order #123. Unable to process the payment. Please try again later.' + ) + ); + } ); + } ); } ); } ); From 6639fe9fb7887e828ebc0386e6f4c536602d0e7d Mon Sep 17 00:00:00 2001 From: Timur Karimov Date: Wed, 18 Dec 2024 13:53:34 +0100 Subject: [PATCH 13/38] Set gateway method title when the title is defined (#9982) Co-authored-by: Timur Karimov --- changelog/fix-method-title-availability | 4 ++++ includes/class-wc-payment-gateway-wcpay.php | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelog/fix-method-title-availability diff --git a/changelog/fix-method-title-availability b/changelog/fix-method-title-availability new file mode 100644 index 00000000000..d9d2a0c0217 --- /dev/null +++ b/changelog/fix-method-title-availability @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Set payment method title once title is known. diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 3e02aa6acf5..4f579caf2b1 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -321,8 +321,7 @@ public function __construct( ]; if ( 'card' !== $this->stripe_id ) { - $this->id = self::GATEWAY_ID . '_' . $this->stripe_id; - $this->method_title = "WooPayments ($this->title)"; + $this->id = self::GATEWAY_ID . '_' . $this->stripe_id; } // Capabilities have different keys than the payment method ID's, @@ -366,7 +365,8 @@ public function __construct( * @return string */ public function get_title() { - $this->title = $this->payment_method->get_title(); + $this->title = $this->payment_method->get_title(); + $this->method_title = "WooPayments ($this->title)"; return parent::get_title(); } From efc8b586011b2f9c055c0ff121c9b2c8ba9900d3 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 18 Dec 2024 15:03:46 +0100 Subject: [PATCH 14/38] fix: tokenized cart subscription signup fee price (#9951) --- changelog/fix-tokenized-cart-subscription-signup-fee | 5 +++++ .../tokenized-express-checkout/transformers/wc-to-stripe.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelog/fix-tokenized-cart-subscription-signup-fee diff --git a/changelog/fix-tokenized-cart-subscription-signup-fee b/changelog/fix-tokenized-cart-subscription-signup-fee new file mode 100644 index 00000000000..5abe9f0226b --- /dev/null +++ b/changelog/fix-tokenized-cart-subscription-signup-fee @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized cart subscription signup fee price + + diff --git a/client/tokenized-express-checkout/transformers/wc-to-stripe.js b/client/tokenized-express-checkout/transformers/wc-to-stripe.js index 7c4c9c14822..6d9b39035ce 100644 --- a/client/tokenized-express-checkout/transformers/wc-to-stripe.js +++ b/client/tokenized-express-checkout/transformers/wc-to-stripe.js @@ -40,8 +40,8 @@ export const transformPrice = ( price, priceObject ) => { export const transformCartDataForDisplayItems = ( cartData ) => { const displayItems = cartData.items.map( ( item ) => ( { amount: transformPrice( - parseInt( item.prices.price, 10 ), - item.prices + parseInt( item.totals?.line_subtotal || item.prices.price, 10 ), + item.totals || item.prices ), name: [ item.name, From 8a7027fff46f3bc2944bdd044f9098bd807d0357 Mon Sep 17 00:00:00 2001 From: Zvonimir Maglica Date: Wed, 18 Dec 2024 16:11:32 +0100 Subject: [PATCH 15/38] Load checkout scripts when they are not previously loaded on checkout page (#9984) --- ...checkout-scripts-on-checkout-if-not-loaded | 4 +++ includes/class-wc-payments-checkout.php | 31 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 changelog/load-checkout-scripts-on-checkout-if-not-loaded diff --git a/changelog/load-checkout-scripts-on-checkout-if-not-loaded b/changelog/load-checkout-scripts-on-checkout-if-not-loaded new file mode 100644 index 00000000000..4a684203a2e --- /dev/null +++ b/changelog/load-checkout-scripts-on-checkout-if-not-loaded @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Load checkout scripts when they are not previously loaded on checkout page. diff --git a/includes/class-wc-payments-checkout.php b/includes/class-wc-payments-checkout.php index ee7a161f3b1..44d92d10b23 100644 --- a/includes/class-wc-payments-checkout.php +++ b/includes/class-wc-payments-checkout.php @@ -103,6 +103,7 @@ public function init_hooks() { add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts_for_zero_order_total' ], 11 ); + add_action( 'woocommerce_after_checkout_form', [ $this, 'maybe_load_checkout_scripts' ] ); } /** @@ -151,11 +152,18 @@ public function register_scripts_for_zero_order_total() { ! has_block( 'woocommerce/checkout' ) && ! wp_script_is( 'wcpay-upe-checkout', 'enqueued' ) ) { - WC_Payments::get_gateway()->tokenization_script(); - $script_handle = 'wcpay-upe-checkout'; - $js_object = 'wcpay_upe_config'; - wp_localize_script( $script_handle, $js_object, WC_Payments::get_wc_payments_checkout()->get_payment_fields_js_config() ); - wp_enqueue_script( $script_handle ); + $this->load_checkout_scripts(); + } + } + + /** + * Sometimes the filters can remove the payment gateway from the checkout page which results in the payment fields not being displayed. + * This could prevent loading of the payment fields (checkout) scripts. + * This function ensures that these scripts are loaded. + */ + public function maybe_load_checkout_scripts() { + if ( is_checkout() && ! wp_script_is( 'wcpay-upe-checkout', 'enqueued' ) ) { + $this->load_checkout_scripts(); } } @@ -416,7 +424,7 @@ function () use ( $prepared_customer_data ) { } ?> -
gateway = $this->gateway->wc_payments_get_payment_gateway_by_id( $payment_method_id ); } } + + /** + * Load the checkout scripts. + */ + private function load_checkout_scripts() { + WC_Payments::get_gateway()->tokenization_script(); + $script_handle = 'wcpay-upe-checkout'; + $js_object = 'wcpay_upe_config'; + wp_localize_script( $script_handle, $js_object, WC_Payments::get_wc_payments_checkout()->get_payment_fields_js_config() ); + wp_enqueue_script( $script_handle ); + } } From 5eaa046fa289b83943f5b595074cd1fc0da417a6 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Wed, 18 Dec 2024 16:31:39 +0100 Subject: [PATCH 16/38] Add failure reason to failed timeline events (#9980) --- ...lude-more-info-on-transaction-details-page | 4 ++ client/payment-details/timeline/map-events.js | 19 +++++-- client/payment-details/timeline/mappings.ts | 43 +++++++++++++++ .../timeline/test/__snapshots__/index.js.snap | 2 +- .../test/__snapshots__/map-events.js.snap | 2 +- .../timeline/test/map-events.js | 55 +++++++++++++++++++ 6 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 changelog/update-5713-failed-orders-should-include-more-info-on-transaction-details-page diff --git a/changelog/update-5713-failed-orders-should-include-more-info-on-transaction-details-page b/changelog/update-5713-failed-orders-should-include-more-info-on-transaction-details-page new file mode 100644 index 00000000000..daf90a1cd39 --- /dev/null +++ b/changelog/update-5713-failed-orders-should-include-more-info-on-transaction-details-page @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Add failure reason to failed payments in the timeline. diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index da4e275105c..b76f659ac63 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -30,7 +30,7 @@ import { import { formatFee } from 'utils/fees'; import { getAdminUrl } from 'wcpay/utils'; import { ShieldIcon } from 'wcpay/icons'; -import { fraudOutcomeRulesetMapping } from './mappings'; +import { fraudOutcomeRulesetMapping, paymentFailureMapping } from './mappings'; /** * Creates a timeline item about a payment status change @@ -772,6 +772,10 @@ const mapEventToTimelineItems = ( event ) => { ), ]; case 'failed': + const paymentFailureMessage = + paymentFailureMapping[ event.reason ] || + paymentFailureMapping.default; + return [ getStatusChangeTimelineItem( event, @@ -779,11 +783,14 @@ const mapEventToTimelineItems = ( event ) => { ), getMainTimelineItem( event, - stringWithAmount( - /* translators: %s is a monetary amount */ - __( 'A payment of %s failed.', 'woocommerce-payments' ), - event.amount, - true + sprintf( + /* translators: %1$s is the payment amount, %2$s is the failure reason message */ + __( + 'A payment of %1$s failed: %2$s.', + 'woocommerce-payments' + ), + formatExplicitCurrency( event.amount, event.currency ), + paymentFailureMessage ), ), diff --git a/client/payment-details/timeline/mappings.ts b/client/payment-details/timeline/mappings.ts index 80d2aceaa98..affcb62063a 100644 --- a/client/payment-details/timeline/mappings.ts +++ b/client/payment-details/timeline/mappings.ts @@ -65,3 +65,46 @@ export const fraudOutcomeRulesetMapping = { ), }, }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const paymentFailureMapping = { + card_declined: __( + 'The card was declined by the bank', + 'woocommerce-payments' + ), + expired_card: __( 'The card has expired', 'woocommerce-payments' ), + incorrect_cvc: __( + 'The security code is incorrect', + 'woocommerce-payments' + ), + incorrect_number: __( + 'The card number is incorrect', + 'woocommerce-payments' + ), + incorrect_zip: __( 'The postal code is incorrect', 'woocommerce-payments' ), + invalid_cvc: __( 'The security code is invalid', 'woocommerce-payments' ), + invalid_expiry_month: __( + 'The expiration month is invalid', + 'woocommerce-payments' + ), + invalid_expiry_year: __( + 'The expiration year is invalid', + 'woocommerce-payments' + ), + invalid_number: __( 'The card number is invalid', 'woocommerce-payments' ), + processing_error: __( + 'An error occurred while processing the card', + 'woocommerce-payments' + ), + authentication_required: __( + 'The payment requires authentication', + 'woocommerce-payments' + ), + insufficient_funds: __( + 'The card has insufficient funds to complete the purchase', + 'woocommerce-payments' + ), + + // Default fallback + default: __( 'The payment was declined', 'woocommerce-payments' ), +}; diff --git a/client/payment-details/timeline/test/__snapshots__/index.js.snap b/client/payment-details/timeline/test/__snapshots__/index.js.snap index 95f1e8a5c9f..c7915e6aee9 100644 --- a/client/payment-details/timeline/test/__snapshots__/index.js.snap +++ b/client/payment-details/timeline/test/__snapshots__/index.js.snap @@ -933,7 +933,7 @@ exports[`PaymentDetailsTimeline renders correctly (with a mocked Timeline compon - A payment of $77.00 failed. + A payment of $77.00 failed: The card was declined by the bank.
, diff --git a/client/payment-details/timeline/test/map-events.js b/client/payment-details/timeline/test/map-events.js index c3e42ceae8b..f1c0588d659 100644 --- a/client/payment-details/timeline/test/map-events.js +++ b/client/payment-details/timeline/test/map-events.js @@ -662,4 +662,59 @@ describe( 'mapTimelineEvents', () => { ).toMatchSnapshot(); } ); } ); + + test( 'formats payment failure events with different error codes', () => { + const testCases = [ + { + reason: 'insufficient_funds', + expectedMessage: + 'A payment of $77.00 USD failed: The card has insufficient funds to complete the purchase.', + }, + { + reason: 'expired_card', + expectedMessage: + 'A payment of $77.00 USD failed: The card has expired.', + }, + { + reason: 'invalid_cvc', + expectedMessage: + 'A payment of $77.00 USD failed: The security code is invalid.', + }, + { + reason: 'unknown_reason', + expectedMessage: + 'A payment of $77.00 USD failed: The payment was declined.', + }, + ]; + + testCases.forEach( ( { reason, expectedMessage } ) => { + const events = mapTimelineEvents( [ + { + amount: 7700, + currency: 'USD', + datetime: 1585712113, + reason, + type: 'failed', + }, + ] ); + + expect( events[ 1 ].headline ).toBe( expectedMessage ); + } ); + } ); + + test( 'formats payment failure events with different currencies', () => { + const events = mapTimelineEvents( [ + { + amount: 7700, + currency: 'EUR', + datetime: 1585712113, + reason: 'card_declined', + type: 'failed', + }, + ] ); + + expect( events[ 1 ].headline ).toBe( + 'A payment of €77.00 EUR failed: The card was declined by the bank.' + ); + } ); } ); From f1a21c81e18ae6a3ef8ba6439b74ace7d6261df4 Mon Sep 17 00:00:00 2001 From: Radoslav Georgiev Date: Thu, 19 Dec 2024 09:06:59 +0200 Subject: [PATCH 17/38] Fix rounding problems with Level 3 data (#9991) --- changelog/fix-9114-level3-rounding | 4 ++ src/Internal/Service/Level3Service.php | 37 +++++++++++++++---- .../Internal/Service/Level3ServiceTest.php | 23 ++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 changelog/fix-9114-level3-rounding diff --git a/changelog/fix-9114-level3-rounding b/changelog/fix-9114-level3-rounding new file mode 100644 index 00000000000..713c8d684cc --- /dev/null +++ b/changelog/fix-9114-level3-rounding @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Add a rounding entry to Level 3 data for rare cases where rounding errors break calculations. diff --git a/src/Internal/Service/Level3Service.php b/src/Internal/Service/Level3Service.php index 67db748debe..b75f3dd1271 100644 --- a/src/Internal/Service/Level3Service.php +++ b/src/Internal/Service/Level3Service.php @@ -80,10 +80,10 @@ public function get_data_from_order( int $order_id ): array { $order_items = array_values( $order->get_items( [ 'line_item', 'fee' ] ) ); $currency = $order->get_currency(); - $process_item = function ( $item ) use ( $currency ) { - return $this->process_item( $item, $currency ); - }; - $items_to_send = array_map( $process_item, $order_items ); + $items_to_send = []; + foreach ( $order_items as $item ) { + $items_to_send = array_merge( $items_to_send, $this->process_item( $item, $currency ) ); + } $level3_data = [ 'merchant_reference' => (string) $order->get_id(), // An alphanumeric string of up to characters in length. This unique value is assigned by the merchant to identify the order. Also known as an “Order ID”. @@ -137,9 +137,9 @@ public function get_data_from_order( int $order_id ): array { * * @param WC_Order_Item_Product|WC_Order_Item_Fee $item Item to process. * @param string $currency Currency to use. - * @return \stdClass + * @return \stdClass[] */ - private function process_item( WC_Order_Item $item, string $currency ): stdClass { + private function process_item( WC_Order_Item $item, string $currency ): array { // Check to see if it is a WC_Order_Item_Product or a WC_Order_Item_Fee. if ( $item instanceof WC_Order_Item_Product ) { $subtotal = $item->get_subtotal(); @@ -164,7 +164,7 @@ private function process_item( WC_Order_Item $item, string $currency ): stdClass $unit_cost = 0; } - return (object) [ + $line_item = (object) [ 'product_code' => (string) $product_code, // Up to 12 characters that uniquely identify the product. 'product_description' => $description, // Up to 26 characters long describing the product. 'unit_cost' => $unit_cost, // Cost of the product, in cents, as a non-negative integer. @@ -172,6 +172,29 @@ private function process_item( WC_Order_Item $item, string $currency ): stdClass 'tax_amount' => $tax_amount, // The amount of tax this item had added to it, in cents, as a non-negative integer. 'discount_amount' => $discount_amount, // The amount an item was discounted—if there was a sale,for example, as a non-negative integer. ]; + $line_items = [ $line_item ]; + + /** + * In edge cases, rounding after division might lead to a slight inconsistency. + * + * For example: 10/3 with 2 decimal places = 3.33, but 3.33*3 = 9.99. + */ + if ( $subtotal > 0 ) { + $prepared_subtotal = $this->prepare_amount( $subtotal, $currency ); + $difference = $prepared_subtotal - ( $unit_cost * $quantity ); + if ( $difference > 0 ) { + $line_items[] = (object) [ + 'product_code' => 'rounding-fix', + 'product_description' => __( 'Rounding fix', 'woocommerce-payments' ), + 'unit_cost' => $difference, + 'quantity' => 1, + 'tax_amount' => 0, + 'discount_amount' => 0, + ]; + } + } + + return $line_items; } /** diff --git a/tests/unit/src/Internal/Service/Level3ServiceTest.php b/tests/unit/src/Internal/Service/Level3ServiceTest.php index fe3eee573c3..dba9766386d 100644 --- a/tests/unit/src/Internal/Service/Level3ServiceTest.php +++ b/tests/unit/src/Internal/Service/Level3ServiceTest.php @@ -167,6 +167,10 @@ protected function mock_level_3_order( $mock_items = array_merge( $mock_items, array_fill( 0, $basket_size - count( $mock_items ), $mock_items[0] ) ); } + $this->mock_order( $mock_items, $shipping_postcode ); + } + + protected function mock_order( array $mock_items, string $shipping_postcode ) { // Setup the order. $mock_order = $this ->getMockBuilder( WC_Order::class ) @@ -434,6 +438,25 @@ public function test_full_level3_data_with_float_quantity() { $this->assertEquals( $expected_data, $level_3_data ); } + public function test_rounding_in_edge_cases() { + $this->mock_account->method( 'get_account_country' )->willReturn( Country_Code::UNITED_STATES ); + + $mock_items = []; + $mock_items[] = $this->create_mock_item( 'Beanie with Addon', 3, 73, 0, 30 ); + $this->mock_order( $mock_items, '98012' ); + + $level_3_data = $this->sut->get_data_from_order( $this->order_id ); + + $this->assertCount( 2, $level_3_data['line_items'] ); + $this->assertEquals( 2433, $level_3_data['line_items'][0]->unit_cost ); + $this->assertEquals( 'rounding-fix', $level_3_data['line_items'][1]->product_code ); + $this->assertEquals( 'Rounding fix', $level_3_data['line_items'][1]->product_description ); + $this->assertEquals( 1, $level_3_data['line_items'][1]->unit_cost ); + $this->assertEquals( 1, $level_3_data['line_items'][1]->quantity ); + $this->assertEquals( 0, $level_3_data['line_items'][1]->tax_amount ); + $this->assertEquals( 0, $level_3_data['line_items'][1]->discount_amount ); + } + public function test_full_level3_data_with_float_quantity_zero() { $expected_data = [ 'merchant_reference' => '210', From 7b6e7efa8aac9cde89a8399a158561173497560e Mon Sep 17 00:00:00 2001 From: Zvonimir Maglica Date: Thu, 19 Dec 2024 09:21:00 +0100 Subject: [PATCH 18/38] Remove hooks from customer service constructor (#9992) --- .../dev-7264-remove-hooks-from-customer-service-constructor | 4 ++++ dev/phpcs/WCPay/ruleset.xml | 4 ---- includes/class-wc-payments-customer-service.php | 5 +++++ includes/class-wc-payments-token-service.php | 5 +++++ includes/class-wc-payments.php | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 changelog/dev-7264-remove-hooks-from-customer-service-constructor diff --git a/changelog/dev-7264-remove-hooks-from-customer-service-constructor b/changelog/dev-7264-remove-hooks-from-customer-service-constructor new file mode 100644 index 00000000000..d912717fc31 --- /dev/null +++ b/changelog/dev-7264-remove-hooks-from-customer-service-constructor @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Remove hooks from customer and token services to dedicated methods diff --git a/dev/phpcs/WCPay/ruleset.xml b/dev/phpcs/WCPay/ruleset.xml index 9806ccfe9e7..7c8cefbd0e3 100644 --- a/dev/phpcs/WCPay/ruleset.xml +++ b/dev/phpcs/WCPay/ruleset.xml @@ -17,10 +17,6 @@ */includes/class-wc-payments-order-success-page.php - - */includes/class-wc-payments-customer-service.php - */includes/class-wc-payments-token-service.php - */includes/class-wc-payments-webhook-reliability-service.php diff --git a/includes/class-wc-payments-customer-service.php b/includes/class-wc-payments-customer-service.php index 05f95c32d31..d0f97e061c0 100644 --- a/includes/class-wc-payments-customer-service.php +++ b/includes/class-wc-payments-customer-service.php @@ -99,7 +99,12 @@ public function __construct( $this->database_cache = $database_cache; $this->session_service = $session_service; $this->order_service = $order_service; + } + /** + * Initialize hooks + */ + public function init_hooks() { /* * Adds the WooCommerce Payments customer ID found in the user session * to the WordPress user as metadata. diff --git a/includes/class-wc-payments-token-service.php b/includes/class-wc-payments-token-service.php index 7bfdc482e18..283a0d7851a 100644 --- a/includes/class-wc-payments-token-service.php +++ b/includes/class-wc-payments-token-service.php @@ -47,7 +47,12 @@ class WC_Payments_Token_Service { public function __construct( WC_Payments_API_Client $payments_api_client, WC_Payments_Customer_Service $customer_service ) { $this->payments_api_client = $payments_api_client; $this->customer_service = $customer_service; + } + /** + * Initializes hooks. + */ + public function init_hooks() { add_action( 'woocommerce_payment_token_deleted', [ $this, 'woocommerce_payment_token_deleted' ], 10, 2 ); add_action( 'woocommerce_payment_token_set_default', [ $this, 'woocommerce_payment_token_set_default' ], 10, 2 ); add_filter( 'woocommerce_get_customer_payment_tokens', [ $this, 'woocommerce_get_customer_payment_tokens' ], 10, 3 ); diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 17300478794..32c75df5fc7 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -554,6 +554,8 @@ public static function init() { self::$onboarding_service->init_hooks(); self::$incentives_service->init_hooks(); self::$compatibility_service->init_hooks(); + self::$customer_service->init_hooks(); + self::$token_service->init_hooks(); $payment_method_classes = [ CC_Payment_Method::class, From 5005b6bcc01664f1df277dac599c111227b424ad Mon Sep 17 00:00:00 2001 From: Dat Hoang Date: Thu, 19 Dec 2024 15:58:18 +0700 Subject: [PATCH 19/38] Add seller_message to failed order notes (#9934) --- changelog/feat-9810-add-seller-message | 4 ++ includes/class-wc-payment-gateway-wcpay.php | 17 ++++++- includes/class-wc-payments.php | 1 + ...wc-payment-gateway-wcpay-subscriptions.php | 8 ++- .../class-api-merchant-exception.php | 49 +++++++++++++++++++ .../class-wc-payments-api-client.php | 8 +++ .../test-class-wc-payments-api-client.php | 20 ++++++++ 7 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 changelog/feat-9810-add-seller-message create mode 100644 includes/exceptions/class-api-merchant-exception.php diff --git a/changelog/feat-9810-add-seller-message b/changelog/feat-9810-add-seller-message new file mode 100644 index 00000000000..2669c24015b --- /dev/null +++ b/changelog/feat-9810-add-seller-message @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add seller_message to failed order notes diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 4f579caf2b1..d1be21241b9 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -20,7 +20,19 @@ use WCPay\Constants\Intent_Status; use WCPay\Constants\Payment_Type; use WCPay\Constants\Payment_Method; -use WCPay\Exceptions\{ Add_Payment_Method_Exception, Amount_Too_Small_Exception, Process_Payment_Exception, Intent_Authentication_Exception, API_Exception, Invalid_Address_Exception, Fraud_Prevention_Enabled_Exception, Invalid_Phone_Number_Exception, Rate_Limiter_Enabled_Exception, Order_ID_Mismatch_Exception, Order_Not_Found_Exception, New_Process_Payment_Exception }; +use WCPay\Exceptions\{Add_Payment_Method_Exception, + Amount_Too_Small_Exception, + API_Merchant_Exception, + Process_Payment_Exception, + Intent_Authentication_Exception, + API_Exception, + Invalid_Address_Exception, + Fraud_Prevention_Enabled_Exception, + Invalid_Phone_Number_Exception, + Rate_Limiter_Enabled_Exception, + Order_ID_Mismatch_Exception, + Order_Not_Found_Exception, + New_Process_Payment_Exception}; use WCPay\Core\Server\Request\Cancel_Intention; use WCPay\Core\Server\Request\Capture_Intention; use WCPay\Core\Server\Request\Create_And_Confirm_Intention; @@ -1270,6 +1282,9 @@ public function process_payment( $order_id ) { ); $error_details = esc_html( rtrim( $e->getMessage(), '.' ) ); + if ( $e instanceof API_Merchant_Exception ) { + $error_details = $error_details . '. ' . esc_html( rtrim( $e->get_merchant_message(), '.' ) ); + } if ( $e instanceof API_Exception && 'card_error' === $e->get_error_type() ) { // If the payment failed with a 'card_error' API exception, initialize the fraud meta box diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 32c75df5fc7..4ad2d32625e 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -354,6 +354,7 @@ public static function init() { include_once __DIR__ . '/exceptions/class-base-exception.php'; include_once __DIR__ . '/exceptions/class-api-exception.php'; + include_once __DIR__ . '/exceptions/class-api-merchant-exception.php'; include_once __DIR__ . '/exceptions/class-connection-exception.php'; include_once __DIR__ . '/core/class-mode.php'; diff --git a/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php b/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php index 31ec70bedf8..d2584f9b824 100644 --- a/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php +++ b/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php @@ -11,6 +11,7 @@ use WCPay\Core\Server\Request\Get_Intention; use WCPay\Exceptions\API_Exception; +use WCPay\Exceptions\API_Merchant_Exception; use WCPay\Exceptions\Invalid_Payment_Method_Exception; use WCPay\Exceptions\Add_Payment_Method_Exception; use WCPay\Exceptions\Order_Not_Found_Exception; @@ -342,6 +343,11 @@ public function scheduled_subscription_payment( $amount, $renewal_order ) { $renewal_order->update_status( 'failed' ); if ( ! empty( $payment_information ) ) { + $error_details = esc_html( rtrim( $e->getMessage(), '.' ) ); + if ( $e instanceof API_Merchant_Exception ) { + $error_details = $error_details . '. ' . esc_html( rtrim( $e->get_merchant_message(), '.' ) ); + } + $note = sprintf( WC_Payments_Utils::esc_interpolated_html( /* translators: %1: the failed payment amount, %2: error message */ @@ -358,7 +364,7 @@ public function scheduled_subscription_payment( $amount, $renewal_order ) { wc_price( $amount, [ 'currency' => WC_Payments_Utils::get_order_intent_currency( $renewal_order ) ] ), $renewal_order ), - esc_html( rtrim( $e->getMessage(), '.' ) ) + $error_details ); $renewal_order->add_order_note( $note ); } diff --git a/includes/exceptions/class-api-merchant-exception.php b/includes/exceptions/class-api-merchant-exception.php new file mode 100644 index 00000000000..ac10bd271bc --- /dev/null +++ b/includes/exceptions/class-api-merchant-exception.php @@ -0,0 +1,49 @@ +merchant_message = $merchant_message; + + parent::__construct( $message, $error_code, $http_code, $error_type, $decline_code, $code, $previous ); + } + + /** + * Returns the merchant message. + * + * @return string Merchant message. + */ + public function get_merchant_message(): string { + return $this->merchant_message; + } +} diff --git a/includes/wc-payment-api/class-wc-payments-api-client.php b/includes/wc-payment-api/class-wc-payments-api-client.php index b3adf5bf7eb..e90094d57de 100644 --- a/includes/wc-payment-api/class-wc-payments-api-client.php +++ b/includes/wc-payment-api/class-wc-payments-api-client.php @@ -9,6 +9,7 @@ use WCPay\Constants\Intent_Status; use WCPay\Exceptions\API_Exception; +use WCPay\Exceptions\API_Merchant_Exception; use WCPay\Exceptions\Amount_Too_Small_Exception; use WCPay\Exceptions\Amount_Too_Large_Exception; use WCPay\Exceptions\Connection_Exception; @@ -2419,6 +2420,13 @@ protected function check_response_for_errors( $response ) { ); Logger::error( "$error_message ($error_code)" ); + + if ( 'card_declined' === $error_code && isset( $response_body['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message'] ) ) { + $merchant_message = $response_body['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message']; + + throw new API_Merchant_Exception( $message, $error_code, $response_code, $merchant_message, $error_type, $decline_code ); + } + throw new API_Exception( $message, $error_code, $response_code, $error_type, $decline_code ); } } diff --git a/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php b/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php index 3fc4a56c8f6..fb95bcf1591 100644 --- a/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php +++ b/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php @@ -7,7 +7,9 @@ use WCPay\Constants\Country_Code; use WCPay\Constants\Intent_Status; +use WCPay\Core\Server\Request\Create_And_Confirm_Intention; use WCPay\Exceptions\API_Exception; +use WCPay\Exceptions\API_Merchant_Exception; use WCPay\Internal\Logger; use WCPay\Exceptions\Connection_Exception; use WCPay\Fraud_Prevention\Fraud_Prevention_Service; @@ -1195,6 +1197,24 @@ public function test_get_tracking_info() { $this->assertEquals( $expect, $result ); } + public function test_throws_api_merchant_exception() { + $mock_response = []; + $mock_response['error']['code'] = 'card_declined'; + $mock_response['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message'] = 'Bank declined'; + $this->set_http_mock_response( + 401, + $mock_response + ); + + try { + // This is a dummy call to trigger the response so that our test can validate the exception. + $this->payments_api_client->create_subscription(); + } catch ( API_Merchant_Exception $e ) { + $this->assertSame( 'card_declined', $e->get_error_code() ); + $this->assertSame( 'Bank declined', $e->get_merchant_message() ); + } + } + /** * Set up http mock response. * From a64815337185fff3d801562c43401715e34de8f6 Mon Sep 17 00:00:00 2001 From: Jessy Pappachan <32092402+jessy-p@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:10:20 +0530 Subject: [PATCH 20/38] Use the filter param in server-side disputes CSV export (#9988) Co-authored-by: Jessy Co-authored-by: Nagesh Pai --- changelog/fix-9987-filter-csv-disputes | 4 ++++ client/data/disputes/hooks.ts | 15 ++------------- client/data/disputes/resolvers.js | 7 +++++-- client/disputes/index.tsx | 2 ++ 4 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 changelog/fix-9987-filter-csv-disputes diff --git a/changelog/fix-9987-filter-csv-disputes b/changelog/fix-9987-filter-csv-disputes new file mode 100644 index 00000000000..e4a87b24b1b --- /dev/null +++ b/changelog/fix-9987-filter-csv-disputes @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix filtering in async Disputes CSV export diff --git a/client/data/disputes/hooks.ts b/client/data/disputes/hooks.ts index b8a95b1e5e6..5db1c2c3c59 100644 --- a/client/data/disputes/hooks.ts +++ b/client/data/disputes/hooks.ts @@ -17,7 +17,6 @@ import type { } from 'wcpay/types/disputes'; import type { ApiError } from 'wcpay/types/errors'; import { STORE_NAME } from '../constants'; -import { disputeAwaitingResponseStatuses } from 'wcpay/disputes/filters/config'; /** * Returns the dispute object, error object, and loading state. @@ -98,11 +97,6 @@ export const useDisputes = ( { ( select ) => { const { getDisputes, isResolving } = select( STORE_NAME ); - const search = - filter === 'awaiting_response' - ? disputeAwaitingResponseStatuses - : undefined; - const query = { paged: Number.isNaN( parseInt( paged ?? '', 10 ) ) ? '1' @@ -119,7 +113,7 @@ export const useDisputes = ( { dateBetween.sort( ( a, b ) => moment( a ).diff( moment( b ) ) ), - search, + filter, statusIs, statusIsNot, orderBy: orderBy || 'created', @@ -163,11 +157,6 @@ export const useDisputesSummary = ( { ( select ) => { const { getDisputesSummary, isResolving } = select( STORE_NAME ); - const search = - filter === 'awaiting_response' - ? disputeAwaitingResponseStatuses - : undefined; - const query = { paged: Number.isNaN( parseInt( paged ?? '', 10 ) ) ? '1' @@ -180,7 +169,7 @@ export const useDisputesSummary = ( { dateBefore, dateAfter, dateBetween, - search, + filter, statusIs, statusIsNot, }; diff --git a/client/data/disputes/resolvers.js b/client/data/disputes/resolvers.js index bf45770537c..ce748a46562 100644 --- a/client/data/disputes/resolvers.js +++ b/client/data/disputes/resolvers.js @@ -20,6 +20,7 @@ import { updateDisputesSummary, updateErrorForDispute, } from './actions'; +import { disputeAwaitingResponseStatuses } from 'wcpay/disputes/filters/config'; const formatQueryFilters = ( query ) => ( { user_email: query.userEmail, @@ -31,7 +32,10 @@ const formatQueryFilters = ( query ) => ( { formatDateValue( query.dateBetween[ 0 ] ), formatDateValue( query.dateBetween[ 1 ], true ), ], - search: query.search, + search: + query.filter === 'awaiting_response' + ? disputeAwaitingResponseStatuses + : query.search, status_is: query.statusIs, status_is_not: query.statusIsNot, locale: query.locale, @@ -42,7 +46,6 @@ export function getDisputesCSV( query ) { `${ NAMESPACE }/disputes/download`, formatQueryFilters( query ) ); - return path; } diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 060afccce35..cdb85131f5d 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -372,6 +372,7 @@ export const DisputesList = (): JSX.Element => { date_after: dateAfter, date_between: dateBetween, match, + filter, status_is: statusIs, status_is_not: statusIsNot, } = getQuery(); @@ -407,6 +408,7 @@ export const DisputesList = (): JSX.Element => { dateBefore, dateBetween, match, + filter, statusIs, statusIsNot, } ), From 771e3abe2a3145d32c493a43eb78d83df1d4831c Mon Sep 17 00:00:00 2001 From: Cvetan Cvetanov Date: Thu, 19 Dec 2024 12:45:15 +0200 Subject: [PATCH 21/38] Enhance Embedded Components and MOX to support custom width and paddings (#9995) Co-authored-by: Dan Paun <82826872+dpaun1985@users.noreply.github.com> --- changelog/update-9919-embedded-components-width | 4 ++++ client/onboarding/style.scss | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 changelog/update-9919-embedded-components-width diff --git a/changelog/update-9919-embedded-components-width b/changelog/update-9919-embedded-components-width new file mode 100644 index 00000000000..ca8fe89ebb7 --- /dev/null +++ b/changelog/update-9919-embedded-components-width @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update Embedded Components and MOX to support custom width and paddings. diff --git a/client/onboarding/style.scss b/client/onboarding/style.scss index 67a6351aab6..415849c1d06 100644 --- a/client/onboarding/style.scss +++ b/client/onboarding/style.scss @@ -85,10 +85,12 @@ body.wcpay-onboarding__body { } &__content { - max-width: 400px; + max-width: 615px; + width: 100%; - @media screen and ( min-width: $break-mobile ) { - width: 400px; + @media screen and ( max-width: $break-mobile ) { + width: 100%; + padding: 0 $gap; } } From e61393136fb409c73802b0ee68ce49ccdf6968e7 Mon Sep 17 00:00:00 2001 From: Zvonimir Maglica Date: Thu, 19 Dec 2024 15:46:50 +0100 Subject: [PATCH 22/38] Hide transaction fee on admin order view screen when transaction is not captured (#9997) --- ...418-hide-transaction-fees-when-transaction-is-not-captured | 4 ++++ includes/admin/class-wc-payments-admin.php | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelog/fix-9418-hide-transaction-fees-when-transaction-is-not-captured diff --git a/changelog/fix-9418-hide-transaction-fees-when-transaction-is-not-captured b/changelog/fix-9418-hide-transaction-fees-when-transaction-is-not-captured new file mode 100644 index 00000000000..f524fd812f1 --- /dev/null +++ b/changelog/fix-9418-hide-transaction-fees-when-transaction-is-not-captured @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Hide transaction fee on admin view order screen when transaction is not captured. diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index e7ad01fe210..d78671d1298 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -6,6 +6,7 @@ */ use Automattic\Jetpack\Identity_Crisis as Jetpack_Identity_Crisis; +use WCPay\Constants\Intent_Status; use WCPay\Core\Server\Request; use WCPay\Database_Cache; use WCPay\Logger; @@ -1253,7 +1254,7 @@ public function show_woopay_payment_method_name_admin( $order_id ) { */ public function display_wcpay_transaction_fee( $order_id ) { $order = wc_get_order( $order_id ); - if ( ! $order || ! $order->get_meta( '_wcpay_transaction_fee' ) ) { + if ( ! $order || ! $order->get_meta( '_wcpay_transaction_fee' ) || Intent_Status::REQUIRES_CAPTURE === $order->get_meta( WC_Payments_Order_Service::INTENTION_STATUS_META_KEY ) ) { return; } ?> From 7b223259e070bf7ac54d0e9e0715567b013b42f9 Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 19 Dec 2024 15:55:08 +0100 Subject: [PATCH 23/38] fix: tokenized ECE item compatibility w/ product bundles (#9994) --- .../fix-tokenized-ece-product-bundles-totals | 5 + .../__tests__/wc-product-bundles.test.js | 253 ++++++++++++++++++ .../compatibility/wc-product-bundles.js | 19 ++ client/tokenized-express-checkout/index.js | 1 + .../transformers/wc-to-stripe.js | 11 +- 5 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 changelog/fix-tokenized-ece-product-bundles-totals create mode 100644 client/tokenized-express-checkout/compatibility/__tests__/wc-product-bundles.test.js create mode 100644 client/tokenized-express-checkout/compatibility/wc-product-bundles.js diff --git a/changelog/fix-tokenized-ece-product-bundles-totals b/changelog/fix-tokenized-ece-product-bundles-totals new file mode 100644 index 00000000000..c003feec46a --- /dev/null +++ b/changelog/fix-tokenized-ece-product-bundles-totals @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized ECE item compatibility w/ product bundles + + diff --git a/client/tokenized-express-checkout/compatibility/__tests__/wc-product-bundles.test.js b/client/tokenized-express-checkout/compatibility/__tests__/wc-product-bundles.test.js new file mode 100644 index 00000000000..91009ab1a25 --- /dev/null +++ b/client/tokenized-express-checkout/compatibility/__tests__/wc-product-bundles.test.js @@ -0,0 +1,253 @@ +/** + * External dependencies + */ +import { applyFilters } from '@wordpress/hooks'; + +/** + * Internal dependencies + */ +import '../wc-product-bundles'; + +describe( 'ECE product bundles compatibility', () => { + it( 'filters out cart items that are bundled by something else', () => { + const cartData = applyFilters( + 'wcpay.express-checkout.map-line-items', + { + items: [ + { + key: 'd179a6924eafc82d7864f1e0caedbe95', + id: 261, + type: 'bundle', + quantity: 1, + item_data: [ + { + key: 'Includes', + value: 'T-Shirt × 1', + }, + { + key: 'Includes', + value: 'T-Shirt with Logo × 2', + }, + { + key: 'Includes', + value: 'V-Neck T-Shirt - Medium × 1', + }, + ], + extensions: { + bundles: { + bundled_items: [ + 'abda15f782e68dc63bd615d6a05fa3d2', + '4d16fa6ebc10a1d66013b0f85640eb2b', + 'ff279cc5574ef1cf45aa76bde0d66baa', + ], + bundle_data: { + configuration: { + '1': { + product_id: 13, + quantity: 1, + discount: 20, + optional_selected: 'yes', + }, + '2': { + product_id: 30, + quantity: 2, + discount: '', + }, + '3': { + product_id: 10, + quantity: 1, + discount: '', + attributes: { + attribute_size: 'Medium', + }, + variation_id: '25', + }, + '4': { + product_id: 10, + quantity: 0, + discount: '', + optional_selected: 'no', + attributes: [], + }, + }, + is_editable: false, + is_price_hidden: false, + is_subtotal_hidden: false, + is_hidden: false, + is_meta_hidden_in_cart: true, + is_meta_hidden_in_summary: false, + }, + }, + }, + }, + { + key: 'abda15f782e68dc63bd615d6a05fa3d2', + id: 13, + type: 'simple', + quantity: 1, + extensions: { + bundles: { + bundled_by: 'd179a6924eafc82d7864f1e0caedbe95', + bundled_item_data: { + bundle_id: 261, + bundled_item_id: 1, + is_removable: true, + is_indented: true, + is_subtotal_aggregated: true, + is_parent_visible: true, + is_last: false, + is_price_hidden: false, + is_subtotal_hidden: false, + is_thumbnail_hidden: false, + is_hidden_in_cart: false, + is_hidden_in_summary: true, + }, + }, + }, + }, + { + key: '4d16fa6ebc10a1d66013b0f85640eb2b', + id: 30, + type: 'simple', + quantity: 2, + extensions: { + bundles: { + bundled_by: 'd179a6924eafc82d7864f1e0caedbe95', + bundled_item_data: { + bundle_id: 261, + bundled_item_id: 2, + is_removable: false, + is_indented: true, + is_subtotal_aggregated: true, + is_parent_visible: true, + is_last: false, + is_price_hidden: true, + is_subtotal_hidden: true, + is_thumbnail_hidden: false, + is_hidden_in_cart: false, + is_hidden_in_summary: true, + }, + }, + }, + }, + { + key: 'ff279cc5574ef1cf45aa76bde0d66baa', + id: 25, + type: 'variation', + quantity: 1, + extensions: { + bundles: { + bundled_by: 'd179a6924eafc82d7864f1e0caedbe95', + bundled_item_data: { + bundle_id: 261, + bundled_item_id: 3, + is_removable: false, + is_indented: true, + is_subtotal_aggregated: true, + is_parent_visible: true, + is_last: true, + is_price_hidden: true, + is_subtotal_hidden: true, + is_thumbnail_hidden: false, + is_hidden_in_cart: false, + is_hidden_in_summary: true, + }, + }, + }, + }, + { + key: 'c51ce410c124a10e0db5e4b97fc2af39', + id: 13, + type: 'simple', + quantity: 1, + extensions: { + bundles: [], + }, + }, + ], + items_count: 2, + } + ); + + expect( cartData ).toStrictEqual( { + items: [ + { + extensions: { + bundles: { + bundle_data: { + configuration: { + '1': { + discount: 20, + optional_selected: 'yes', + product_id: 13, + quantity: 1, + }, + '2': { + discount: '', + product_id: 30, + quantity: 2, + }, + '3': { + attributes: { + attribute_size: 'Medium', + }, + discount: '', + product_id: 10, + quantity: 1, + variation_id: '25', + }, + '4': { + attributes: [], + discount: '', + optional_selected: 'no', + product_id: 10, + quantity: 0, + }, + }, + is_editable: false, + is_hidden: false, + is_meta_hidden_in_cart: true, + is_meta_hidden_in_summary: false, + is_price_hidden: false, + is_subtotal_hidden: false, + }, + bundled_items: [ + 'abda15f782e68dc63bd615d6a05fa3d2', + '4d16fa6ebc10a1d66013b0f85640eb2b', + 'ff279cc5574ef1cf45aa76bde0d66baa', + ], + }, + }, + id: 261, + item_data: [ + { + key: 'Includes', + value: 'T-Shirt × 1', + }, + { + key: 'Includes', + value: 'T-Shirt with Logo × 2', + }, + { + key: 'Includes', + value: 'V-Neck T-Shirt - Medium × 1', + }, + ], + key: 'd179a6924eafc82d7864f1e0caedbe95', + quantity: 1, + type: 'bundle', + }, + { + extensions: { + bundles: [], + }, + id: 13, + key: 'c51ce410c124a10e0db5e4b97fc2af39', + quantity: 1, + type: 'simple', + }, + ], + items_count: 2, + } ); + } ); +} ); diff --git a/client/tokenized-express-checkout/compatibility/wc-product-bundles.js b/client/tokenized-express-checkout/compatibility/wc-product-bundles.js new file mode 100644 index 00000000000..7a3d2a4dc3c --- /dev/null +++ b/client/tokenized-express-checkout/compatibility/wc-product-bundles.js @@ -0,0 +1,19 @@ +/** + * External dependencies + */ +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'wcpay.express-checkout.map-line-items', + 'automattic/wcpay/express-checkout', + ( cartData ) => { + return { + ...cartData, + // ensuring that the items that are bundled by another don't appear in the summary. + // otherwise they might be contributing to the wrong order total, creating errors. + items: cartData.items.filter( + ( item ) => ! item.extensions?.bundles?.bundled_by + ), + }; + } +); diff --git a/client/tokenized-express-checkout/index.js b/client/tokenized-express-checkout/index.js index 3b2eb1a8b46..e6c24d02e91 100644 --- a/client/tokenized-express-checkout/index.js +++ b/client/tokenized-express-checkout/index.js @@ -13,6 +13,7 @@ import '../checkout/express-checkout-buttons.scss'; import './compatibility/wc-deposits'; import './compatibility/wc-order-attribution'; import './compatibility/wc-product-page'; +import './compatibility/wc-product-bundles'; import { getExpressCheckoutButtonAppearance, getExpressCheckoutButtonStyleSettings, diff --git a/client/tokenized-express-checkout/transformers/wc-to-stripe.js b/client/tokenized-express-checkout/transformers/wc-to-stripe.js index 6d9b39035ce..794fc5309b1 100644 --- a/client/tokenized-express-checkout/transformers/wc-to-stripe.js +++ b/client/tokenized-express-checkout/transformers/wc-to-stripe.js @@ -8,6 +8,7 @@ import { decodeEntities } from '@wordpress/html-entities'; * Internal dependencies */ import { getExpressCheckoutData } from '../utils'; +import { applyFilters } from '@wordpress/hooks'; /** * GooglePay/ApplePay expect the prices to be formatted in cents. @@ -34,10 +35,16 @@ export const transformPrice = ( price, priceObject ) => { * - https://docs.stripe.com/js/elements_object/express_checkout_element_shippingaddresschange_event * - https://docs.stripe.com/js/elements_object/express_checkout_element_shippingratechange_event * - * @param {Object} cartData Store API Cart response object. + * @param {Object} rawCartData Store API Cart response object. * @return {{pending: boolean, name: string, amount: integer}} `displayItems` for Stripe. */ -export const transformCartDataForDisplayItems = ( cartData ) => { +export const transformCartDataForDisplayItems = ( rawCartData ) => { + // allowing extensions to manipulate the individual items returned by the backend. + const cartData = applyFilters( + 'wcpay.express-checkout.map-line-items', + rawCartData + ); + const displayItems = cartData.items.map( ( item ) => ( { amount: transformPrice( parseInt( item.totals?.line_subtotal || item.prices.price, 10 ), From 863519dde8a28374fcf6d231f2d21161bd34edd9 Mon Sep 17 00:00:00 2001 From: Adam Heckler <5512652+aheckler@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:49:37 -0500 Subject: [PATCH 24/38] Use "currency conversion fee" (#10002) Co-authored-by: Nagesh Pai --- changelog/fix-9996-currency-conversion-fee-phrasing | 4 ++++ client/payment-details/timeline/map-events.js | 4 ++-- .../timeline/test/__snapshots__/map-events.js.snap | 2 +- client/utils/account-fees.tsx | 2 +- client/utils/test/__snapshots__/account-fees.tsx.snap | 4 ++-- includes/class-wc-payments-captured-event-note.php | 4 ++-- tests/fixtures/captured-payments/discount.json | 2 +- tests/fixtures/captured-payments/foreign-card.json | 2 +- tests/fixtures/captured-payments/fx-decimal.json | 2 +- tests/fixtures/captured-payments/fx-partial-capture.json | 2 +- tests/fixtures/captured-payments/fx-with-capped-fee.json | 2 +- tests/fixtures/captured-payments/fx.json | 2 +- tests/fixtures/captured-payments/jpy-payment.json | 2 +- tests/fixtures/captured-payments/subscription.json | 2 +- 14 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 changelog/fix-9996-currency-conversion-fee-phrasing diff --git a/changelog/fix-9996-currency-conversion-fee-phrasing b/changelog/fix-9996-currency-conversion-fee-phrasing new file mode 100644 index 00000000000..bdee2cbc00f --- /dev/null +++ b/changelog/fix-9996-currency-conversion-fee-phrasing @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Use "currency conversion fee" instead "foreign exchange fee" in payment timeline and various other places. diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index b76f659ac63..7e5570ce8ce 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -410,12 +410,12 @@ export const feeBreakdown = ( event ) => { fixedRate !== 0 ? __( /* translators: %1$s% is the fee percentage and %2$s is the fixed rate */ - 'Foreign exchange fee: %1$s%% + %2$s', + 'Currency conversion fee: %1$s%% + %2$s', 'woocommerce-payments' ) : __( /* translators: %1$s% is the fee percentage */ - 'Foreign exchange fee: %1$s%%', + 'Currency conversion fee: %1$s%%', 'woocommerce-payments' ), 'additional-wcpay-subscription': diff --git a/client/payment-details/timeline/test/__snapshots__/map-events.js.snap b/client/payment-details/timeline/test/__snapshots__/map-events.js.snap index 4126cb1ff38..b229ba674be 100644 --- a/client/payment-details/timeline/test/__snapshots__/map-events.js.snap +++ b/client/payment-details/timeline/test/__snapshots__/map-events.js.snap @@ -397,7 +397,7 @@ exports[`mapTimelineEvents single currency events formats captured events with f International card fee: 1.5%
  • - Foreign exchange fee: 2% + Currency conversion fee: 2%
  • Discount diff --git a/client/utils/account-fees.tsx b/client/utils/account-fees.tsx index 711d3d337ed..a1c8056f78d 100644 --- a/client/utils/account-fees.tsx +++ b/client/utils/account-fees.tsx @@ -166,7 +166,7 @@ export const formatMethodFeesTooltip = ( ) } { hasFees( accountFees.fx ) ? (
    -
    Foreign exchange fee
    +
    Currency conversion fee
    { getFeeDescriptionString( accountFees.fx ) }
    ) : ( diff --git a/client/utils/test/__snapshots__/account-fees.tsx.snap b/client/utils/test/__snapshots__/account-fees.tsx.snap index 89321bc7582..d92aa6ae54e 100644 --- a/client/utils/test/__snapshots__/account-fees.tsx.snap +++ b/client/utils/test/__snapshots__/account-fees.tsx.snap @@ -23,7 +23,7 @@ exports[`Account fees utility functions formatMethodFeesTooltip() displays base
    - Foreign exchange fee + Currency conversion fee
    1% @@ -102,7 +102,7 @@ exports[`Account fees utility functions formatMethodFeesTooltip() displays base
    - Foreign exchange fee + Currency conversion fee
    1% diff --git a/includes/class-wc-payments-captured-event-note.php b/includes/class-wc-payments-captured-event-note.php index 10c48567952..07e902d8632 100644 --- a/includes/class-wc-payments-captured-event-note.php +++ b/includes/class-wc-payments-captured-event-note.php @@ -327,9 +327,9 @@ private function fee_label_mapping( int $fixed_rate, bool $is_capped ) { $res['additional-fx'] = 0 !== $fixed_rate /* translators: %1$s% is the fee percentage and %2$s is the fixed rate */ - ? __( 'Foreign exchange fee: %1$s%% + %2$s', 'woocommerce-payments' ) + ? __( 'Currency conversion fee: %1$s%% + %2$s', 'woocommerce-payments' ) /* translators: %1$s% is the fee percentage */ - : __( 'Foreign exchange fee: %1$s%%', 'woocommerce-payments' ); + : __( 'Currency conversion fee: %1$s%%', 'woocommerce-payments' ); $res['additional-wcpay-subscription'] = 0 !== $fixed_rate /* translators: %1$s% is the fee percentage and %2$s is the fixed rate */ diff --git a/tests/fixtures/captured-payments/discount.json b/tests/fixtures/captured-payments/discount.json index 2fa6a911d74..5bf6f936c45 100644 --- a/tests/fixtures/captured-payments/discount.json +++ b/tests/fixtures/captured-payments/discount.json @@ -60,7 +60,7 @@ "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", "additional-international": "International card fee: 1%", - "additional-fx": "Foreign exchange fee: 1%", + "additional-fx": "Currency conversion fee: 1%", "discount": { "label": "Discount", "variable": "Variable fee: -4.9%", diff --git a/tests/fixtures/captured-payments/foreign-card.json b/tests/fixtures/captured-payments/foreign-card.json index 234878b2372..df45c326d62 100644 --- a/tests/fixtures/captured-payments/foreign-card.json +++ b/tests/fixtures/captured-payments/foreign-card.json @@ -53,7 +53,7 @@ "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", "additional-international": "International card fee: 1%", - "additional-fx": "Foreign exchange fee: 1%" + "additional-fx": "Currency conversion fee: 1%" }, "netString": "Net payout: $95.47 USD" } diff --git a/tests/fixtures/captured-payments/fx-decimal.json b/tests/fixtures/captured-payments/fx-decimal.json index b95e9318c84..2f065036122 100644 --- a/tests/fixtures/captured-payments/fx-decimal.json +++ b/tests/fixtures/captured-payments/fx-decimal.json @@ -45,7 +45,7 @@ "feeString": "Fee (3.9% + $0.30): -$4.39", "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", - "additional-fx": "Foreign exchange fee: 1%" + "additional-fx": "Currency conversion fee: 1%" }, "netString": "Net payout: $100.65 USD" } diff --git a/tests/fixtures/captured-payments/fx-partial-capture.json b/tests/fixtures/captured-payments/fx-partial-capture.json index f10ff7aa9e9..691390d4852 100644 --- a/tests/fixtures/captured-payments/fx-partial-capture.json +++ b/tests/fixtures/captured-payments/fx-partial-capture.json @@ -57,7 +57,7 @@ "feeString": "Fee (3.51% + £0.21): -$0.88", "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", - "additional-fx": "Foreign exchange fee: 1%", + "additional-fx": "Currency conversion fee: 1%", "discount": { "label": "Discount", "variable": "Variable fee: -0.39%", diff --git a/tests/fixtures/captured-payments/fx-with-capped-fee.json b/tests/fixtures/captured-payments/fx-with-capped-fee.json index 8c1b602a3eb..4c31a8435d7 100644 --- a/tests/fixtures/captured-payments/fx-with-capped-fee.json +++ b/tests/fixtures/captured-payments/fx-with-capped-fee.json @@ -55,7 +55,7 @@ "feeBreakdown": { "base": "Base fee: capped at $6.00", "additional-international": "International card fee: 1.5%", - "additional-fx": "Foreign exchange fee: 1%" + "additional-fx": "Currency conversion fee: 1%" }, "netString": "Net payout: $971.04 USD" } diff --git a/tests/fixtures/captured-payments/fx.json b/tests/fixtures/captured-payments/fx.json index 8ceee7b7438..f18ca9297ab 100644 --- a/tests/fixtures/captured-payments/fx.json +++ b/tests/fixtures/captured-payments/fx.json @@ -46,7 +46,7 @@ "feeString": "Fee (3.9% + $0.30): -$4.20", "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", - "additional-fx": "Foreign exchange fee: 1%" + "additional-fx": "Currency conversion fee: 1%" }, "netString": "Net payout: $95.84 USD" } diff --git a/tests/fixtures/captured-payments/jpy-payment.json b/tests/fixtures/captured-payments/jpy-payment.json index 6c7a6b3ee05..4b4c6c152c9 100644 --- a/tests/fixtures/captured-payments/jpy-payment.json +++ b/tests/fixtures/captured-payments/jpy-payment.json @@ -57,7 +57,7 @@ "feeBreakdown": { "base": "Base fee: 3.6%", "additional-international": "International card fee: 2%", - "additional-fx": "Foreign exchange fee: 2%" + "additional-fx": "Currency conversion fee: 2%" }, "netString": "Net payout: ¥4,507 JPY" } diff --git a/tests/fixtures/captured-payments/subscription.json b/tests/fixtures/captured-payments/subscription.json index b7312ea0c02..d0e1fe705e4 100644 --- a/tests/fixtures/captured-payments/subscription.json +++ b/tests/fixtures/captured-payments/subscription.json @@ -53,7 +53,7 @@ "feeString": "Fee (4.9% + $0.30): -$3.04", "feeBreakdown": { "base": "Base fee: 2.9% + $0.30", - "additional-fx": "Foreign exchange fee: 1%", + "additional-fx": "Currency conversion fee: 1%", "additional-wcpay-subscription": "Subscription transaction fee: 1%" }, "netString": "Net payout: $52.87 USD" From 2176d63f1329cd058a49dd8c63315f617b386547 Mon Sep 17 00:00:00 2001 From: Timur Karimov Date: Thu, 19 Dec 2024 17:46:54 +0100 Subject: [PATCH 25/38] Show ECE buttons on products w/o shipping tax included in price, and improve tests (#9954) Co-authored-by: Timur Karimov Co-authored-by: Mike Moore --- .../fix-ece-button-for-price-including-tax | 4 + ...ayments-express-checkout-button-helper.php | 8 +- ...yments-express-checkout-button-handler.php | 136 ++++++++++++++++++ ...ayments-express-checkout-button-helper.php | 112 ++++++--------- 4 files changed, 185 insertions(+), 75 deletions(-) create mode 100644 changelog/fix-ece-button-for-price-including-tax create mode 100644 tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php diff --git a/changelog/fix-ece-button-for-price-including-tax b/changelog/fix-ece-button-for-price-including-tax new file mode 100644 index 00000000000..521ceb2af68 --- /dev/null +++ b/changelog/fix-ece-button-for-price-including-tax @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Show express checkout for products w/o shipping but where tax is included into price. diff --git a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php index 672f2584c67..86d1a82c54d 100644 --- a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php +++ b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php @@ -415,7 +415,7 @@ public function should_show_express_checkout_button() { return true; } - // Non-shipping product and billing is calculated based on shopper billing addres. Excludes Pay for Order page. + // Non-shipping product and tax is calculated based on shopper billing address. Excludes Pay for Order page. if ( // If the product doesn't needs shipping. ( @@ -426,8 +426,10 @@ public function should_show_express_checkout_button() { ( ( $this->is_cart() || $this->is_checkout() ) && ! WC()->cart->needs_shipping() ) ) - // ...and billing is calculated based on billing address. - && wc_tax_enabled() && 'billing' === get_option( 'woocommerce_tax_based_on' ) + // ...and tax is calculated based on billing address. + && wc_tax_enabled() + && 'billing' === get_option( 'woocommerce_tax_based_on' ) + && 'yes' !== get_option( 'woocommerce_prices_include_tax' ) ) { return false; } diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php new file mode 100644 index 00000000000..0b10752c0f5 --- /dev/null +++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php @@ -0,0 +1,136 @@ +shipping()->unregister_shipping_methods(); + + $this->mock_wcpay_account = $this->createMock( WC_Payments_Account::class ); + $this->mock_wcpay_gateway = $this->createMock( WC_Payment_Gateway_WCPay::class ); + $this->mock_ece_button_helper = $this->createMock( WC_Payments_Express_Checkout_Button_Helper::class ); + $this->mock_express_checkout_ajax_handler = $this->createMock( WC_Payments_Express_Checkout_Ajax_Handler::class ); + + $this->system_under_test = new WC_Payments_Express_Checkout_Button_Handler( + $this->mock_wcpay_account, + $this->mock_wcpay_gateway, + $this->mock_ece_button_helper, + $this->mock_express_checkout_ajax_handler + ); + + // Set up shipping zones and methods. + $this->zone = new WC_Shipping_Zone(); + $this->zone->set_zone_name( 'Worldwide' ); + $this->zone->set_zone_order( 1 ); + $this->zone->save(); + + $flat_rate = $this->zone->add_shipping_method( 'flat_rate' ); + $this->flat_rate_id = $flat_rate; + + $local_pickup = $this->zone->add_shipping_method( 'local_pickup' ); + $this->local_pickup_id = $local_pickup; + } + + public function tear_down() { + parent::tear_down(); + + // Clean up shipping zones and methods. + $this->zone->delete(); + } + + public function test_filter_cart_needs_shipping_address_regular_products() { + $this->assertEquals( + true, + $this->system_under_test->filter_cart_needs_shipping_address( true ), + 'Should not modify shipping address requirement for regular products' + ); + } + + + public function test_filter_cart_needs_shipping_address_subscription_products() { + WC_Subscriptions_Cart::set_cart_contains_subscription( true ); + $this->mock_ece_button_helper->method( 'is_checkout' )->willReturn( true ); + + $this->zone->delete_shipping_method( $this->flat_rate_id ); + $this->zone->delete_shipping_method( $this->local_pickup_id ); + + $this->assertFalse( + $this->system_under_test->filter_cart_needs_shipping_address( true ), + 'Should not require shipping address for subscription without shipping methods' + ); + + remove_filter( 'woocommerce_shipping_method_count', '__return_zero' ); + WC_Subscriptions_Cart::set_cart_contains_subscription( false ); + } +} diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php index 2432c61172c..8006faac78f 100644 --- a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php +++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php @@ -28,13 +28,6 @@ class WC_Payments_Express_Checkout_Button_Helper_Test extends WCPAY_UnitTestCase */ private $mock_wcpay_account; - /** - * Express Checkout Helper instance. - * - * @var WC_Payments_Express_Checkout_Button_Helper - */ - private $express_checkout_helper; - /** * Test shipping zone. * @@ -61,21 +54,7 @@ class WC_Payments_Express_Checkout_Button_Helper_Test extends WCPAY_UnitTestCase * * @var WC_Payments_Express_Checkout_Button_Helper */ - private $mock_express_checkout_helper; - - /** - * Express Checkout Ajax Handler instance. - * - * @var WC_Payments_Express_Checkout_Ajax_Handler - */ - private $mock_express_checkout_ajax_handler; - - /** - * Express Checkout ECE Button Handler instance. - * - * @var WC_Payments_Express_Checkout_Button_Handler - */ - private $mock_express_checkout_ece_button_handler; + private $system_under_test; /** * Test product to add to the cart @@ -92,23 +71,7 @@ public function set_up() { $this->mock_wcpay_account = $this->createMock( WC_Payments_Account::class ); $this->mock_wcpay_gateway = $this->make_wcpay_gateway(); - $this->mock_express_checkout_helper = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account ); - $this->mock_express_checkout_ajax_handler = $this->getMockBuilder( WC_Payments_Express_Checkout_Ajax_Handler::class ) - ->setConstructorArgs( - [ - $this->mock_express_checkout_helper, - ] - ) - ->getMock(); - - $this->mock_ece_button_helper = $this->getMockBuilder( WC_Payments_Express_Checkout_Button_Helper::class ) - ->setConstructorArgs( - [ - $this->mock_wcpay_gateway, - $this->mock_wcpay_account, - ] - ) - ->getMock(); + $this->system_under_test = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account ); WC_Helper_Shipping::delete_simple_flat_rate(); $zone = new WC_Shipping_Zone(); @@ -128,7 +91,7 @@ public function set_up() { WC()->session->init(); WC()->cart->add_to_cart( $this->simple_product->get_id(), 1 ); - $this->mock_express_checkout_helper->update_shipping_method( [ self::get_shipping_option_rate_id( $this->flat_rate_id ) ] ); + $this->system_under_test->update_shipping_method( [ self::get_shipping_option_rate_id( $this->flat_rate_id ) ] ); WC()->cart->calculate_totals(); } @@ -195,34 +158,34 @@ public function test_common_get_button_settings() { 'height' => '48', 'radius' => '', ], - $this->mock_express_checkout_helper->get_common_button_settings() + $this->system_under_test->get_common_button_settings() ); } public function test_cart_prices_include_tax_with_tax_disabled() { add_filter( 'wc_tax_enabled', '__return_false' ); - $this->assertTrue( $this->mock_express_checkout_helper->cart_prices_include_tax() ); + $this->assertTrue( $this->system_under_test->cart_prices_include_tax() ); } public function test_cart_prices_include_tax_with_tax_enabled_and_display_incl() { add_filter( 'wc_tax_enabled', '__return_true' ); // reset in tear_down. add_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); // reset in tear_down. - $this->assertTrue( $this->mock_express_checkout_helper->cart_prices_include_tax() ); + $this->assertTrue( $this->system_under_test->cart_prices_include_tax() ); } public function test_cart_prices_include_tax_with_tax_enabled_and_display_excl() { add_filter( 'wc_tax_enabled', '__return_true' ); // reset in tear_down. add_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_excl' ] ); // reset in tear_down. - $this->assertFalse( $this->mock_express_checkout_helper->cart_prices_include_tax() ); + $this->assertFalse( $this->system_under_test->cart_prices_include_tax() ); } public function test_get_total_label() { $this->mock_wcpay_account->method( 'get_statement_descriptor' ) ->willReturn( 'Google Pay' ); - $result = $this->mock_express_checkout_helper->get_total_label(); + $result = $this->system_under_test->get_total_label(); $this->assertEquals( 'Google Pay (via WooCommerce)', $result ); } @@ -238,49 +201,54 @@ function () { } ); - $result = $this->mock_express_checkout_helper->get_total_label(); + $result = $this->system_under_test->get_total_label(); $this->assertEquals( 'Google Pay (via WooPayments)', $result ); remove_all_filters( 'wcpay_payment_request_total_label_suffix' ); } - public function test_filter_cart_needs_shipping_address_returns_false() { - sleep( 1 ); - $this->zone->delete_shipping_method( $this->flat_rate_id ); - $this->zone->delete_shipping_method( $this->local_pickup_id ); + public function test_should_show_express_checkout_button_for_non_shipping_but_price_includes_tax() { + $this->mock_wcpay_account + ->method( 'is_stripe_connected' ) + ->willReturn( true ); - WC_Subscriptions_Cart::set_cart_contains_subscription( true ); + WC_Payments::mode()->dev(); - $this->mock_ece_button_helper - ->method( 'is_product' ) - ->willReturn( true ); + add_filter( 'woocommerce_is_checkout', '__return_true' ); + add_filter( 'wc_shipping_enabled', '__return_false' ); + add_filter( 'wc_tax_enabled', '__return_true' ); - $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler( - $this->mock_wcpay_account, - $this->mock_wcpay_gateway, - $this->mock_ece_button_helper, - $this->mock_express_checkout_ajax_handler - ); + update_option( 'woocommerce_tax_based_on', 'billing' ); + update_option( 'woocommerce_prices_include_tax', 'yes' ); - $this->assertFalse( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) ); + $this->assertTrue( $this->system_under_test->should_show_express_checkout_button() ); + + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + remove_filter( 'wc_tax_enabled', '__return_true' ); + remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); } - public function test_filter_cart_needs_shipping_address_returns_true() { - WC_Subscriptions_Cart::set_cart_contains_subscription( true ); - $this->mock_ece_button_helper - ->method( 'is_product' ) + public function test_should_not_show_express_checkout_button_for_non_shipping_but_price_does_not_include_tax() { + $this->mock_wcpay_account + ->method( 'is_stripe_connected' ) ->willReturn( true ); - $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler( - $this->mock_wcpay_account, - $this->mock_wcpay_gateway, - $this->mock_ece_button_helper, - $this->mock_express_checkout_ajax_handler - ); + WC_Payments::mode()->dev(); + + add_filter( 'woocommerce_is_checkout', '__return_true' ); + add_filter( 'wc_shipping_enabled', '__return_false' ); + add_filter( 'wc_tax_enabled', '__return_true' ); + + update_option( 'woocommerce_tax_based_on', 'billing' ); + update_option( 'woocommerce_prices_include_tax', 'no' ); - $this->assertTrue( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) ); + $this->assertFalse( $this->system_under_test->should_show_express_checkout_button() ); + + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + remove_filter( 'wc_tax_enabled', '__return_true' ); + remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); } /** From 732ab9286b19d0210933cd80c1909e8c5f33f912 Mon Sep 17 00:00:00 2001 From: Alefe Souza Date: Thu, 19 Dec 2024 14:17:26 -0300 Subject: [PATCH 26/38] Add WooPay Klaviyo newsletter integration (#9908) --- changelog/add-woopay-klaviyo-newsletter-support | 4 ++++ includes/compat/blocks/class-blocks-data-extractor.php | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 changelog/add-woopay-klaviyo-newsletter-support diff --git a/changelog/add-woopay-klaviyo-newsletter-support b/changelog/add-woopay-klaviyo-newsletter-support new file mode 100644 index 00000000000..64e94c6638e --- /dev/null +++ b/changelog/add-woopay-klaviyo-newsletter-support @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Add WooPay Klaviyo newsletter integration. diff --git a/includes/compat/blocks/class-blocks-data-extractor.php b/includes/compat/blocks/class-blocks-data-extractor.php index 673cae7f352..becc393a5da 100644 --- a/includes/compat/blocks/class-blocks-data-extractor.php +++ b/includes/compat/blocks/class-blocks-data-extractor.php @@ -59,6 +59,15 @@ private function get_available_blocks() { $blocks[] = new \Mailchimp_Woocommerce_Newsletter_Blocks_Integration(); } + if ( class_exists( '\WCK\Blocks\CheckoutIntegration' ) ) { + // phpcs:ignore + /** + * @psalm-suppress UndefinedClass + * @phpstan-ignore-next-line + */ + $blocks[] = new \WCK\Blocks\CheckoutIntegration(); + } + return $blocks; } From 915fcc89ac17370b91a825a46b203eff54e73369 Mon Sep 17 00:00:00 2001 From: Ricardo Metring Date: Thu, 19 Dec 2024 20:11:44 -0300 Subject: [PATCH 27/38] Fix FedEx shipping multi-currency rates (#9985) Co-authored-by: Rafael Zaleski --- changelog/fix-198-mccy-fedex-conversion | 5 ++ .../Compatibility/WooCommerceFedEx.php | 45 ++++++++++---- .../test-class-woocommerce-fedex.php | 60 +++++++++++++------ 3 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 changelog/fix-198-mccy-fedex-conversion diff --git a/changelog/fix-198-mccy-fedex-conversion b/changelog/fix-198-mccy-fedex-conversion new file mode 100644 index 00000000000..7fecbc49b87 --- /dev/null +++ b/changelog/fix-198-mccy-fedex-conversion @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Fix FedEx insurance rates with different currencies. + + diff --git a/includes/multi-currency/Compatibility/WooCommerceFedEx.php b/includes/multi-currency/Compatibility/WooCommerceFedEx.php index 8a38d058e40..15c25b4ba27 100644 --- a/includes/multi-currency/Compatibility/WooCommerceFedEx.php +++ b/includes/multi-currency/Compatibility/WooCommerceFedEx.php @@ -8,13 +8,25 @@ namespace WCPay\MultiCurrency\Compatibility; use WCPay\MultiCurrency\MultiCurrency; -use WCPay\MultiCurrency\Utils; /** * Class that controls Multi Currency Compatibility with WooCommerce FedEx Plugin. */ class WooCommerceFedEx extends BaseCompatibility { + /** + * Calls to look for in the backtrace when determining whether + * to return store currency or skip converting product prices. + */ + private const WC_SHIPPING_FEDEX_CALLS = [ + 'WC_Shipping_Fedex->set_settings', + 'WC_Shipping_Fedex->per_item_shipping', + 'WC_Shipping_Fedex->box_shipping', + 'WC_Shipping_Fedex->get_fedex_api_request', + 'WC_Shipping_Fedex->get_fedex_requests', + 'WC_Shipping_Fedex->process_result', + ]; + /** * Init the class. * @@ -23,10 +35,31 @@ class WooCommerceFedEx extends BaseCompatibility { public function init() { // Add needed actions and filters if FedEx is active. if ( class_exists( 'WC_Shipping_Fedex_Init' ) ) { + add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ] ); add_filter( MultiCurrency::FILTER_PREFIX . 'should_return_store_currency', [ $this, 'should_return_store_currency' ] ); } } + /** + * Checks to see if the product's price should be converted. + * + * @param bool $return Whether to convert the product's price or not. Default is true. + * + * @return bool True if it should be converted. + */ + public function should_convert_product_price( bool $return ): bool { + // If it's already false, return it. + if ( ! $return ) { + return $return; + } + + if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) { + return false; + } + + return $return; + } + /** * Determine whether to return the store currency or not. * @@ -40,15 +73,7 @@ public function should_return_store_currency( bool $return ): bool { return $return; } - $calls = [ - 'WC_Shipping_Fedex->set_settings', - 'WC_Shipping_Fedex->per_item_shipping', - 'WC_Shipping_Fedex->box_shipping', - 'WC_Shipping_Fedex->get_fedex_api_request', - 'WC_Shipping_Fedex->get_fedex_requests', - 'WC_Shipping_Fedex->process_result', - ]; - if ( $this->utils->is_call_in_backtrace( $calls ) ) { + if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) { return true; } diff --git a/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php b/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php index 60e130390fd..e52927230ca 100644 --- a/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php +++ b/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php @@ -35,6 +35,20 @@ class WCPay_Multi_Currency_WooCommerceFedEx_Tests extends WCPAY_UnitTestCase { */ private $woocommerce_fedex; + /** + * Calls to check in the backtrace. + * + * @var array + */ + private $woocommerce_fedex_calls = [ + 'WC_Shipping_Fedex->set_settings', + 'WC_Shipping_Fedex->per_item_shipping', + 'WC_Shipping_Fedex->box_shipping', + 'WC_Shipping_Fedex->get_fedex_api_request', + 'WC_Shipping_Fedex->get_fedex_requests', + 'WC_Shipping_Fedex->process_result', + ]; + /** * Pre-test setup */ @@ -54,37 +68,45 @@ public function test_should_return_store_currency_returns_true_if_true_passed() // If the calls are found, it should return true. public function test_should_return_store_currency_returns_true_if_calls_found() { - $calls = [ - 'WC_Shipping_Fedex->set_settings', - 'WC_Shipping_Fedex->per_item_shipping', - 'WC_Shipping_Fedex->box_shipping', - 'WC_Shipping_Fedex->get_fedex_api_request', - 'WC_Shipping_Fedex->get_fedex_requests', - 'WC_Shipping_Fedex->process_result', - ]; $this->mock_utils ->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) - ->with( $calls ) + ->with( $this->woocommerce_fedex_calls ) ->willReturn( true ); + $this->assertTrue( $this->woocommerce_fedex->should_return_store_currency( false ) ); } - // If the calls are found, it should return true. + // If the calls are not found, it should return false. public function test_should_return_store_currency_returns_false_if_no_calls_found() { - $calls = [ - 'WC_Shipping_Fedex->set_settings', - 'WC_Shipping_Fedex->per_item_shipping', - 'WC_Shipping_Fedex->box_shipping', - 'WC_Shipping_Fedex->get_fedex_api_request', - 'WC_Shipping_Fedex->get_fedex_requests', - 'WC_Shipping_Fedex->process_result', - ]; $this->mock_utils ->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) - ->with( $calls ) + ->with( $this->woocommerce_fedex_calls ) ->willReturn( false ); + $this->assertFalse( $this->woocommerce_fedex->should_return_store_currency( false ) ); } + + // If true is passed to should_convert_product_price and no calls are found, it should return true. + public function test_should_convert_product_price_returns_true_if_true_passed_and_no_calls_found() { + $this->mock_utils + ->expects( $this->once() ) + ->method( 'is_call_in_backtrace' ) + ->with( $this->woocommerce_fedex_calls ) + ->willReturn( false ); + + $this->assertTrue( $this->woocommerce_fedex->should_convert_product_price( true ) ); + } + + // If calls are found, should_convert_product_price should return false even if true was passed. + public function test_should_convert_product_price_returns_false_if_calls_found() { + $this->mock_utils + ->expects( $this->once() ) + ->method( 'is_call_in_backtrace' ) + ->with( $this->woocommerce_fedex_calls ) + ->willReturn( true ); + + $this->assertFalse( $this->woocommerce_fedex->should_convert_product_price( true ) ); + } } From 779ef47509e3907aeab9e2a0a7f693e1a9d3d5d7 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 20 Dec 2024 10:21:49 +0100 Subject: [PATCH 28/38] Translate hard coded strings in the fees tooltip breakdown (#10004) --- ...rakedown-tooltip-are-not-internationalised | 4 ++++ client/utils/account-fees.tsx | 20 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 changelog/fix-8620-fee-types-in-fees-brakedown-tooltip-are-not-internationalised diff --git a/changelog/fix-8620-fee-types-in-fees-brakedown-tooltip-are-not-internationalised b/changelog/fix-8620-fee-types-in-fees-brakedown-tooltip-are-not-internationalised new file mode 100644 index 00000000000..57528510958 --- /dev/null +++ b/changelog/fix-8620-fee-types-in-fees-brakedown-tooltip-are-not-internationalised @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Use translatable strings on the fee breakdown tooltip of the payment settings screen. diff --git a/client/utils/account-fees.tsx b/client/utils/account-fees.tsx index a1c8056f78d..791b727b164 100644 --- a/client/utils/account-fees.tsx +++ b/client/utils/account-fees.tsx @@ -143,7 +143,7 @@ export const formatMethodFeesTooltip = ( return (
    -
    Base fee
    +
    { __( 'Base fee', 'woocommerce-payments' ) }
    { getFeeDescriptionString( accountFees.base, @@ -153,7 +153,12 @@ export const formatMethodFeesTooltip = (
    { hasFees( accountFees.additional ) ? (
    -
    International payment method fee
    +
    + { __( + 'International payment method fee', + 'woocommerce-payments' + ) } +
    { getFeeDescriptionString( accountFees.additional, @@ -166,14 +171,21 @@ export const formatMethodFeesTooltip = ( ) } { hasFees( accountFees.fx ) ? (
    -
    Currency conversion fee
    +
    + { __( + 'Currency conversion fee', + 'woocommerce-payments' + ) } +
    { getFeeDescriptionString( accountFees.fx ) }
    ) : ( '' ) }
    -
    Total per transaction
    +
    + { __( 'Total per transaction', 'woocommerce-payments' ) } +
    { getFeeDescriptionString( total ) }
    From 3c7b1155c4e3db7e8dbe5cb70cccf19a1fc40ae9 Mon Sep 17 00:00:00 2001 From: Zvonimir Maglica Date: Fri, 20 Dec 2024 11:00:47 +0100 Subject: [PATCH 29/38] Invalidate transactions store when transaction is captured (#10015) --- .../fix-9735-render-transactions-correctly-on-capture | 4 ++++ client/data/authorizations/actions.ts | 7 +++++++ client/data/authorizations/test/actions.test.ts | 8 ++++++++ 3 files changed, 19 insertions(+) create mode 100644 changelog/fix-9735-render-transactions-correctly-on-capture diff --git a/changelog/fix-9735-render-transactions-correctly-on-capture b/changelog/fix-9735-render-transactions-correctly-on-capture new file mode 100644 index 00000000000..9ed7f628e4c --- /dev/null +++ b/changelog/fix-9735-render-transactions-correctly-on-capture @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Ensure captured transactions appear in the Transactions tab without requiring a page refresh. diff --git a/client/data/authorizations/actions.ts b/client/data/authorizations/actions.ts index 0885da1cbfe..ace7f3c6fed 100644 --- a/client/data/authorizations/actions.ts +++ b/client/data/authorizations/actions.ts @@ -196,6 +196,13 @@ export function* submitCaptureAuthorization( 'getPaymentIntent' ); + // Need to invalidate transactions tab to update newly captured transaction if needed. + yield controls.dispatch( + STORE_NAME, + 'invalidateResolutionForStoreSelector', + 'getTransactions' + ); + // Create success notice. yield controls.dispatch( 'core/notices', diff --git a/client/data/authorizations/test/actions.test.ts b/client/data/authorizations/test/actions.test.ts index 171ef6dd5ad..36527d1836a 100644 --- a/client/data/authorizations/test/actions.test.ts +++ b/client/data/authorizations/test/actions.test.ts @@ -118,6 +118,14 @@ describe( 'Authorizations actions', () => { ) ); + expect( generator.next().value ).toEqual( + controls.dispatch( + 'wc/payments', + 'invalidateResolutionForStoreSelector', + 'getTransactions' + ) + ); + expect( generator.next().value ).toEqual( controls.dispatch( 'core/notices', From f0ebc14465fa5f66268b22b358ab6dcde4085a58 Mon Sep 17 00:00:00 2001 From: Vlad Olaru Date: Fri, 20 Dec 2024 12:48:43 +0200 Subject: [PATCH 30/38] Reduce API impact from test drive setup (#10014) Co-authored-by: Dan Paun <82826872+dpaun1985@users.noreply.github.com> --- ...837-reduce-api-impact-for-test-drive-setup | 5 +++ client/connect-account-page/index.tsx | 32 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 changelog/update-s6837-reduce-api-impact-for-test-drive-setup diff --git a/changelog/update-s6837-reduce-api-impact-for-test-drive-setup b/changelog/update-s6837-reduce-api-impact-for-test-drive-setup new file mode 100644 index 00000000000..eaa0360fe7a --- /dev/null +++ b/changelog/update-s6837-reduce-api-impact-for-test-drive-setup @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Reduce the maximum number of API calls during the test-drive process and add maximum duration cut off logic. + + diff --git a/client/connect-account-page/index.tsx b/client/connect-account-page/index.tsx index 2b3f402abcb..42cb5f25f88 100644 --- a/client/connect-account-page/index.tsx +++ b/client/connect-account-page/index.tsx @@ -95,6 +95,16 @@ const ConnectAccountPage: React.FC = () => { const loaderProgressRef = useRef( testDriveLoaderProgress ); loaderProgressRef.current = testDriveLoaderProgress; + // Use a timer to track the elapsed time for the test drive mode setup. + let testDriveSetupStartTime: number; + // The test drive setup will be forced finished after 40 seconds + // (10 seconds for the initial calls plus 30 for checking the account status in a loop). + const testDriveSetupMaxDuration = 40; + + // Helper function to calculate the elapsed time in seconds. + const elapsed = ( time: number ) => + Math.round( ( Date.now() - time ) / 1000 ); + const { connectUrl, connect: { availableCountries, country }, @@ -173,19 +183,20 @@ const ConnectAccountPage: React.FC = () => { method: 'GET', } ).then( ( account ) => { // Simulate the update of the loader progress bar by 4% per check. - // Limit to a maximum of 15 checks or 30 seconds. - updateLoaderProgress( 100, 4 ); + // Limit to a maximum of 10 checks (6% progress per each request starting from 40% = max 10 checks). + updateLoaderProgress( 100, 6 ); - // If the account status is not a pending one or progress percentage is above 95, - // consider our work done and redirect the merchant. - // Otherwise, schedule another check after 2 seconds. + // If the account status is not a pending one, the progress percentage is above 95, + // or we've exceeded the timeout, consider our work done and redirect the merchant. + // Otherwise, schedule another check after a 2.5 seconds wait. if ( ( account && ( account as AccountData ).status && ! ( account as AccountData ).status.includes( 'pending' ) ) || - loaderProgressRef.current > 95 + loaderProgressRef.current > 95 || + elapsed( testDriveSetupStartTime ) > testDriveSetupMaxDuration ) { setTestDriveLoaderProgress( 100 ); const queryArgs = { @@ -203,12 +214,18 @@ const ConnectAccountPage: React.FC = () => { ...extraQueryArgs, } ); } else { - setTimeout( () => checkAccountStatus( extraQueryArgs ), 2000 ); + // Schedule another check after 2.5 seconds. + // 2.5 seconds plus 0.5 seconds for the fetch request is 3 seconds. + // With a maximum of 10 checks, we will wait for 30 seconds before ending the process normally. + setTimeout( () => checkAccountStatus( extraQueryArgs ), 2500 ); } } ); }; const handleSetupTestDriveMode = async () => { + // Record the start time of the test drive setup. + testDriveSetupStartTime = Date.now(); + // Initialize the progress bar. setTestDriveLoaderProgress( 5 ); setTestDriveModeSubmitted( true ); trackConnectAccountClicked( true ); @@ -256,6 +273,7 @@ const ConnectAccountPage: React.FC = () => { } clearInterval( updateProgress ); + // Update the progress bar to 40% since we've finished the initial account setup. setTestDriveLoaderProgress( 40 ); // Check the url for the `wcpay-connection-success` parameter, indicating a successful connection. From 3794508c29cdc11e37eae148ee12d58252311782 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 20 Dec 2024 12:14:15 +0100 Subject: [PATCH 31/38] Apply User-Defined Date Formatting Settings to WP Admin React Components (#9635) Co-authored-by: deepakpathania <68396823+deepakpathania@users.noreply.github.com> --- ...atting-arent-respected-in-react-components | 4 + client/capital/index.tsx | 12 +- .../test/__snapshots__/index.test.tsx.snap | 48 +++++ client/capital/test/index.test.tsx | 2 + .../account-fees/expiration-description.js | 7 +- .../account-status/account-fees/test/index.js | 1 + .../components/active-loan-summary/index.tsx | 20 +- .../test/__snapshots__/index.js.snap | 2 +- .../active-loan-summary/test/index.js | 10 + .../components/date-format-notice/index.tsx | 67 +++++++ .../components/date-format-notice/style.scss | 5 + .../deposits-overview/test/index.tsx | 2 + .../components/disputed-order-notice/index.js | 12 +- .../disputed-order-notice/test/index.test.js | 1 + client/deposits/details/index.tsx | 11 +- client/deposits/details/test/index.tsx | 2 + client/deposits/index.tsx | 2 + client/deposits/list/index.tsx | 15 +- .../list/test/__snapshots__/index.tsx.snap | 12 +- client/deposits/list/test/index.tsx | 6 +- client/deposits/utils/index.ts | 12 +- client/deposits/utils/test/index.ts | 22 +++ client/disputes/evidence/test/index.js | 3 + client/disputes/index.tsx | 32 ++-- client/disputes/info/index.tsx | 16 +- client/disputes/info/test/index.tsx | 4 + .../test/__snapshots__/index.tsx.snap | 48 +++++ client/disputes/test/index.tsx | 13 +- client/documents/index.tsx | 3 +- client/documents/list/index.tsx | 20 +- client/documents/list/test/index.tsx | 9 + client/globals.d.ts | 3 + client/overview/index.js | 2 + .../modal/update-business-details/index.tsx | 13 +- .../overview/task-list/tasks/dispute-task.tsx | 16 +- .../tasks/update-business-details-task.tsx | 10 +- client/overview/task-list/test/tasks.js | 1 + .../dispute-details/dispute-due-by-date.tsx | 12 +- .../dispute-resolution-footer.tsx | 50 ++--- .../dispute-details/dispute-steps.tsx | 27 +-- .../dispute-details/dispute-summary-row.tsx | 11 +- .../test/__snapshots__/index.test.tsx.snap | 48 +++++ .../order-details/test/index.test.tsx | 4 + .../payment-details/payment-details/index.tsx | 3 +- client/payment-details/readers/index.js | 3 +- client/payment-details/summary/index.tsx | 22 ++- .../test/__snapshots__/index.test.tsx.snap | 4 +- .../summary/test/index.test.tsx | 17 +- .../test/__snapshots__/index.test.tsx.snap | 96 ++++++++++ client/payment-details/test/index.test.tsx | 4 + client/payment-details/timeline/map-events.js | 13 +- client/payment-details/timeline/test/index.js | 1 + .../timeline/test/map-events.js | 1 + client/transactions/blocked/columns.tsx | 10 +- .../blocked/test/columns.test.tsx | 4 + client/transactions/index.tsx | 2 + client/transactions/list/deposit.tsx | 10 +- client/transactions/list/index.tsx | 10 +- .../list/test/__snapshots__/index.tsx.snap | 32 ++-- client/transactions/list/test/deposit.tsx | 20 ++ client/transactions/list/test/index.tsx | 11 ++ client/transactions/risk-review/columns.tsx | 10 +- .../risk-review/test/columns.test.tsx | 4 + client/transactions/uncaptured/index.tsx | 36 ++-- .../uncaptured/test/index.test.tsx | 4 + client/utils/date-time.ts | 82 ++++++++ client/utils/test/date-time.test.ts | 181 ++++++++++++++++++ includes/admin/class-wc-payments-admin.php | 3 + includes/class-wc-payments.php | 1 + 69 files changed, 915 insertions(+), 289 deletions(-) create mode 100644 changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components create mode 100644 client/components/date-format-notice/index.tsx create mode 100644 client/components/date-format-notice/style.scss create mode 100644 client/utils/date-time.ts create mode 100644 client/utils/test/date-time.test.ts diff --git a/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components b/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components new file mode 100644 index 00000000000..5c69920cf26 --- /dev/null +++ b/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Apply User-Defined Date Formatting Settings to WP Admin React Components diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 81e76ad91b4..469b2d283a8 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -6,7 +6,6 @@ import * as React from 'react'; import { __, _n } from '@wordpress/i18n'; import { TableCard } from '@woocommerce/components'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies. @@ -25,6 +24,8 @@ import Chip from 'components/chip'; import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; +import DateFormatNotice from 'wcpay/components/date-format-notice'; const columns = [ { @@ -80,7 +81,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - dateI18n( 'M j, Y', loan.fully_paid_at ) + formatDateTimeFromString( loan.fully_paid_at ) : __( 'Active', 'woocommerce-payments' ); }; @@ -112,7 +113,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => const data = { paid_out_at: { value: loan.paid_out_at, - display: clickable( dateI18n( 'M j, Y', loan.paid_out_at ) ), + display: clickable( + formatDateTimeFromString( loan.paid_out_at ) + ), }, status: { value: getLoanStatusText( loan ), @@ -150,7 +153,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? dateI18n( 'M j, Y', loan.first_paydown_at ) + ? formatDateTimeFromString( loan.first_paydown_at ) : '-' ), }, @@ -207,6 +210,7 @@ const CapitalPage = (): JSX.Element => { return ( + { wcpaySettings.accountLoans.has_active_loan && ( diff --git a/client/capital/test/__snapshots__/index.test.tsx.snap b/client/capital/test/__snapshots__/index.test.tsx.snap index 2b146dd5714..9f0e93091cf 100644 --- a/client/capital/test/__snapshots__/index.test.tsx.snap +++ b/client/capital/test/__snapshots__/index.test.tsx.snap @@ -5,6 +5,54 @@ exports[`CapitalPage renders the TableCard component with loan data 1`] = `
    +
    + +
    + The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
    + +
    diff --git a/client/capital/test/index.test.tsx b/client/capital/test/index.test.tsx index f9eb43b8a49..41ea917902a 100644 --- a/client/capital/test/index.test.tsx +++ b/client/capital/test/index.test.tsx @@ -25,6 +25,7 @@ declare const global: { accountLoans: { has_active_loan: boolean; }; + dateFormat: string; }; }; @@ -37,6 +38,7 @@ describe( 'CapitalPage', () => { }, accountLoans: { has_active_loan: true }, testMode: true, + dateFormat: 'M j, Y', }; } ); diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index 6be5b58681c..497f0207ef3 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -4,13 +4,12 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; -import moment from 'moment'; /** * Internal dependencies */ import { formatCurrency } from 'multi-currency/interface/functions'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const ExpirationDescription = ( { feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, @@ -26,7 +25,7 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - dateI18n( 'F j, Y', moment( endTime ).toISOString() ) + formatDateTimeFromString( endTime ) ); } else if ( volumeAllowance ) { description = sprintf( @@ -44,7 +43,7 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - dateI18n( 'F j, Y', moment( endTime ).toISOString() ) + formatDateTimeFromString( endTime ) ); } else { return null; diff --git a/client/components/account-status/account-fees/test/index.js b/client/components/account-status/account-fees/test/index.js index 5258af4ffdc..7405b33e371 100644 --- a/client/components/account-status/account-fees/test/index.js +++ b/client/components/account-status/account-fees/test/index.js @@ -46,6 +46,7 @@ describe( 'AccountFees', () => { precision: 2, }, }, + dateFormat: 'F j, Y', }; } ); diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index 0c5059ef87c..7ae902c590e 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -13,7 +13,6 @@ import { } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies. @@ -24,6 +23,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const Block = ( { title, @@ -210,12 +210,8 @@ const ActiveLoanSummary = (): JSX.Element => { 'Repaid this period (until %s)', 'woocommerce-payments' ), - dateI18n( - 'M j, Y', - new Date( - details.current_repayment_interval.due_at * - 1000 - ) + formatDateTimeFromTimestamp( + details.current_repayment_interval.due_at ) ) } > @@ -251,9 +247,8 @@ const ActiveLoanSummary = (): JSX.Element => { - { dateI18n( - 'M j, Y', - new Date( details.advance_paid_out_at * 1000 ) + { formatDateTimeFromTimestamp( + details.advance_paid_out_at ) } { - { dateI18n( - 'M j, Y', - new Date( details.repayments_begin_at * 1000 ) + { formatDateTimeFromTimestamp( + details.repayments_begin_at ) } diff --git a/client/components/active-loan-summary/test/__snapshots__/index.js.snap b/client/components/active-loan-summary/test/__snapshots__/index.js.snap index 4424415245c..4e9dd15ec13 100644 --- a/client/components/active-loan-summary/test/__snapshots__/index.js.snap +++ b/client/components/active-loan-summary/test/__snapshots__/index.js.snap @@ -74,7 +74,7 @@ exports[`Active loan summary renders correctly 1`] = `
    - Repaid this period (until Feb 14, 2022) + Repaid this period (until Feb 15, 2022)
    ( { useActiveLoanSummary: jest.fn(), } ) ); +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Active loan summary', () => { beforeEach( () => { global.wcpaySettings = { @@ -34,6 +43,7 @@ describe( 'Active loan summary', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); afterEach( () => { diff --git a/client/components/date-format-notice/index.tsx b/client/components/date-format-notice/index.tsx new file mode 100644 index 00000000000..cdaba000939 --- /dev/null +++ b/client/components/date-format-notice/index.tsx @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import React, { useState } from 'react'; +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import BannerNotice from 'components/banner-notice'; +import interpolateComponents from '@automattic/interpolate-components'; +import { Link } from '@woocommerce/components'; +import './style.scss'; + +const optionName = 'wcpay_date_format_notice_dismissed'; + +const DateFormatNotice: React.FC = () => { + const { updateOptions } = useDispatch( 'wc/admin/options' ); + const [ isBannerVisible, setIsBannerVisible ] = useState( + ! wcpaySettings.isDateFormatNoticeDismissed + ); + + const handleDismiss = () => { + setIsBannerVisible( false ); + wcpaySettings.isDateFormatNoticeDismissed = true; + updateOptions( { + [ optionName ]: true, + } ); + }; + + const handleSettingsClick = () => { + handleDismiss(); + }; + + if ( ! isBannerVisible ) { + return null; + } + + return ( + + { interpolateComponents( { + components: { + settingsLink: ( + + ), + }, + mixedString: __( + 'The date and time formats now match your preferences. You can update them anytime in the {{settingsLink}}settings{{/settingsLink}}.', + 'woocommerce-payments' + ), + } ) } + + ); +}; + +export default DateFormatNotice; diff --git a/client/components/date-format-notice/style.scss b/client/components/date-format-notice/style.scss new file mode 100644 index 00000000000..86ee0987728 --- /dev/null +++ b/client/components/date-format-notice/style.scss @@ -0,0 +1,5 @@ +.date-format-notice { + .wcpay-banner-notice__content { + align-self: center; // Align the content to the center of the notice and the icon. + } +} diff --git a/client/components/deposits-overview/test/index.tsx b/client/components/deposits-overview/test/index.tsx index edaa068a100..920adf540c7 100644 --- a/client/components/deposits-overview/test/index.tsx +++ b/client/components/deposits-overview/test/index.tsx @@ -68,6 +68,7 @@ declare const global: { connect: { country: string; }; + dateFormat: string; }; }; @@ -240,6 +241,7 @@ describe( 'Deposits Overview information', () => { precision: 2, }, }, + dateFormat: 'F j, Y', }; mockUseDepositIncludesLoan.mockReturnValue( { includesFinancingPayout: false, diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index ab51a52d16e..c7d1db9e7d1 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -1,7 +1,6 @@ import moment from 'moment'; import React, { useEffect } from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { createInterpolateElement } from '@wordpress/element'; /** @@ -20,6 +19,7 @@ import { import { useCharge } from 'wcpay/data'; import { recordEvent } from 'tracks'; import './style.scss'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { const { data: charge } = useCharge( chargeId ); @@ -84,8 +84,10 @@ const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { return null; } - const now = moment(); - const dueBy = moment.unix( dispute.evidence_details?.due_by ); + // Get current time in UTC for consistent timezone-independent comparison + const now = moment().utc(); + // Parse the Unix timestamp as UTC since it's stored that way in the API + const dueBy = moment.unix( dispute.evidence_details?.due_by ).utc(); // If the dispute is due in the past, don't show notice. if ( ! now.isBefore( dueBy ) ) { @@ -131,7 +133,7 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - dateI18n( 'M j, Y', dueBy.local().toISOString() ) + formatDateTimeFromString( dueBy.toISOString() ) ); let suffix = sprintf( @@ -182,7 +184,7 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - dateI18n( 'M j, Y', dueBy.local().toISOString() ) + formatDateTimeFromString( dueBy.toISOString() ) ); return ( diff --git a/client/components/disputed-order-notice/test/index.test.js b/client/components/disputed-order-notice/test/index.test.js index 7e44da132e0..784092295f3 100644 --- a/client/components/disputed-order-notice/test/index.test.js +++ b/client/components/disputed-order-notice/test/index.test.js @@ -36,6 +36,7 @@ describe( 'DisputedOrderNoticeHandler', () => { connect: { country: 'US', }, + dateFormat: 'M j, Y', }; useCharge.mockReturnValue( { data: mockCharge } ); } ); diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 2176b079377..4e15a8653a8 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -4,9 +4,7 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { Card, CardBody, @@ -41,6 +39,8 @@ import { import { depositStatusLabels } from '../strings'; import './style.scss'; import { PayoutsRenameNotice } from '../rename-notice'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; +import DateFormatNotice from 'wcpay/components/date-format-notice'; /** * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. @@ -135,11 +135,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - dateI18n( - 'M j, Y', - moment.utc( deposit.date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. - ) + formatDateTimeFromString( deposit.date ) } value={ } detail={ deposit.bankAccount } @@ -248,6 +244,7 @@ export const DepositDetails: React.FC< DepositDetailsProps > = ( { return ( + { isLoading ? ( diff --git a/client/deposits/details/test/index.tsx b/client/deposits/details/test/index.tsx index 5f350daf4d0..fa9a4dbf042 100644 --- a/client/deposits/details/test/index.tsx +++ b/client/deposits/details/test/index.tsx @@ -45,6 +45,7 @@ declare const global: { connect: { country: string; }; + dateFormat: string; }; wcSettings: { countries: Record< string, string > }; }; @@ -67,6 +68,7 @@ describe( 'Deposit overview', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); diff --git a/client/deposits/index.tsx b/client/deposits/index.tsx index d799ff3d385..229acdbbb09 100644 --- a/client/deposits/index.tsx +++ b/client/deposits/index.tsx @@ -23,6 +23,7 @@ import { useSettings } from 'wcpay/data'; import DepositsList from './list'; import { hasAutomaticScheduledDeposits } from 'wcpay/deposits/utils'; import { recordEvent } from 'wcpay/tracks'; +import DateFormatNotice from 'wcpay/components/date-format-notice'; const useNextDepositNoticeState = () => { const { updateOptions } = useDispatch( 'wc/admin/options' ); @@ -149,6 +150,7 @@ const DepositsPage: React.FC = () => { return ( + diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 1ac643d31dc..dc3e20ac02a 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -6,9 +6,7 @@ import React, { useState } from 'react'; import { recordEvent } from 'tracks'; import { useMemo } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, Link } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; import { @@ -48,6 +46,7 @@ import CSVExportModal from 'components/csv-export-modal'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import './style.scss'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ { @@ -140,11 +139,7 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'payouts' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { dateI18n( - 'M j, Y', - moment.utc( deposit.date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. - ) } + { formatDateTimeFromString( deposit.date ) } ); @@ -335,11 +330,7 @@ export const DepositsList = (): JSX.Element => { row[ 0 ], { ...row[ 1 ], - value: dateI18n( - 'Y-m-d', - moment.utc( row[ 1 ].value ).toISOString(), - true - ), + value: formatDateTimeFromString( row[ 1 ].value as string ), }, ...row.slice( 2 ), ] ); diff --git a/client/deposits/list/test/__snapshots__/index.tsx.snap b/client/deposits/list/test/__snapshots__/index.tsx.snap index 07945cd0c64..c26a364fd04 100644 --- a/client/deposits/list/test/__snapshots__/index.tsx.snap +++ b/client/deposits/list/test/__snapshots__/index.tsx.snap @@ -361,7 +361,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` data-link-type="wc-admin" href="admin.php?page=wc-admin&path=%2Fpayments%2Fpayouts%2Fdetails&id=po_mock1" > - Jan 2, 2020 + Jan 2 2020 - Jan 3, 2020 + Jan 3 2020 - Jan 4, 2020 + Jan 4 2020 - Jan 2, 2020 + Jan 2 2020 - Jan 3, 2020 + Jan 3 2020 - Jan 4, 2020 + Jan 4 2020 { reporting: { exportModalDismissed: true, }, + dateFormat: 'M j Y', }; } ); @@ -321,7 +323,7 @@ describe( 'Deposits list', () => { // 2. The indexOf check in amount's expect is because the amount in CSV may not contain // trailing zeros as in the display amount. // - expect( formatDate( csvFirstDeposit[ 1 ], 'M j, Y' ) ).toBe( + expect( csvFirstDeposit[ 1 ].replace( /^"|"$/g, '' ) ).toBe( displayFirstDeposit[ 0 ] ); // date expect( csvFirstDeposit[ 2 ] ).toBe( displayFirstDeposit[ 1 ] ); // type diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 3d8fd6276e1..05f65c46bc3 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -2,21 +2,15 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; - -const formatDate = ( format: string, date: number | string ) => - dateI18n( - format, - moment.utc( date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. - ); +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface DepositObject { date: number | string; } + export const getDepositDate = ( deposit?: DepositObject | null ): string => - deposit ? formatDate( 'F j, Y', deposit?.date ) : '—'; + deposit ? formatDateTimeFromString( deposit?.date as string ) : '—'; interface GetDepositMonthlyAnchorLabelProps { monthlyAnchor: number; diff --git a/client/deposits/utils/test/index.ts b/client/deposits/utils/test/index.ts index d0361137104..e1957ce4564 100644 --- a/client/deposits/utils/test/index.ts +++ b/client/deposits/utils/test/index.ts @@ -8,7 +8,29 @@ import momentLib from 'moment'; */ import { getDepositDate, getDepositMonthlyAnchorLabel } from '../'; +declare const global: { + wcpaySettings: { + dateFormat: string; + }; +}; + +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Deposits Overview Utils / getDepositDate', () => { + beforeEach( () => { + jest.clearAllMocks(); + global.wcpaySettings = { + dateFormat: 'F j, Y', + }; + } ); + test( 'returns a display value without a deposit', () => { expect( getDepositDate() ).toEqual( '—' ); } ); diff --git a/client/disputes/evidence/test/index.js b/client/disputes/evidence/test/index.js index e9ccdb826cb..142ee738ab8 100644 --- a/client/disputes/evidence/test/index.js +++ b/client/disputes/evidence/test/index.js @@ -96,6 +96,7 @@ describe( 'Dispute evidence form', () => { global.wcpaySettings = { restUrl: 'http://example.com/wp-json/', + dateFormat: 'M j, Y', }; } ); afterEach( () => { @@ -190,6 +191,8 @@ describe( 'Dispute evidence page', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index cdb85131f5d..6e8c4d2d61b 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -5,7 +5,6 @@ */ import React, { useState } from 'react'; import { recordEvent } from 'tracks'; -import { dateI18n } from '@wordpress/date'; import { _n, __, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { Button } from '@wordpress/components'; @@ -56,8 +55,9 @@ import { useSettings } from 'wcpay/data'; import { isAwaitingResponse } from 'wcpay/disputes/utils'; import CSVExportModal from 'components/csv-export-modal'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; - +import DateFormatNotice from 'wcpay/components/date-format-notice'; import './style.scss'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ { @@ -201,10 +201,9 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return dateI18n( - 'M j, Y / g:iA', - moment.utc( dispute.due_by ).local().toISOString() - ); + return formatDateTimeFromString( dispute.due_by, { + includeTime: true, + } ); }; export const DisputesList = (): JSX.Element => { @@ -301,10 +300,9 @@ export const DisputesList = (): JSX.Element => { created: { value: dispute.created, display: clickable( - dateI18n( - 'M j, Y', - moment( dispute.created ).toISOString() - ) + formatDateTimeFromString( dispute.created, { + includeTime: true, + } ) ), }, dueBy: { @@ -485,17 +483,18 @@ export const DisputesList = (): JSX.Element => { { // Disputed On. ...row[ 10 ], - value: dateI18n( - 'Y-m-d', - moment( row[ 10 ].value ).toISOString() + value: formatDateTimeFromString( + row[ 10 ].value as string ), }, { // Respond by. ...row[ 11 ], - value: dateI18n( - 'Y-m-d / g:iA', - moment( row[ 11 ].value ).toISOString() + value: formatDateTimeFromString( + row[ 11 ].value as string, + { + includeTime: true, + } ), }, ]; @@ -553,6 +552,7 @@ export const DisputesList = (): JSX.Element => { return ( + { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); diff --git a/client/disputes/test/__snapshots__/index.tsx.snap b/client/disputes/test/__snapshots__/index.tsx.snap index 06459d236e4..b3fae6b5d47 100644 --- a/client/disputes/test/__snapshots__/index.tsx.snap +++ b/client/disputes/test/__snapshots__/index.tsx.snap @@ -5,6 +5,54 @@ exports[`Disputes list renders correctly 1`] = `
    +
    + +
    + The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
    + +
    diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 1409bfc852d..37bbd2e93af 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -17,13 +17,14 @@ import { useReportingExportLanguage, useSettings, } from 'data/index'; -import { formatDate, getUnformattedAmount } from 'wcpay/utils/test-utils'; +import { getUnformattedAmount } from 'wcpay/utils/test-utils'; import React from 'react'; import { CachedDispute, DisputeReason, DisputeStatus, } from 'wcpay/types/disputes'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; jest.mock( '@woocommerce/csv-export', () => { const actualModule = jest.requireActual( '@woocommerce/csv-export' ); @@ -100,6 +101,8 @@ declare const global: { reporting?: { exportModalDismissed: boolean; }; + dateFormat?: string; + timeFormat?: string; }; }; @@ -198,6 +201,8 @@ describe( 'Disputes list', () => { reporting: { exportModalDismissed: true, }, + dateFormat: 'Y-m-d', + timeFormat: 'g:iA', }; } ); @@ -363,8 +368,10 @@ describe( 'Disputes list', () => { `"${ displayFirstDispute[ 5 ] }"` ); // customer - expect( formatDate( csvFirstDispute[ 11 ], 'Y-m-d / g:iA' ) ).toBe( - formatDate( displayFirstDispute[ 6 ], 'Y-m-d / g:iA' ) + expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( + formatDateTimeFromString( mockDisputes[ 0 ].due_by, { + includeTime: true, + } ) ); // date respond by } ); } ); diff --git a/client/documents/index.tsx b/client/documents/index.tsx index 07f75e99ddf..c95c9d3a6ba 100644 --- a/client/documents/index.tsx +++ b/client/documents/index.tsx @@ -9,10 +9,11 @@ import React from 'react'; import Page from 'components/page'; import DocumentsList from './list'; import { TestModeNotice } from 'components/test-mode-notice'; - +import DateFormatNotice from 'wcpay/components/date-format-notice'; export const DocumentsPage = (): JSX.Element => { return ( + diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index b69e2df54af..3223d45dcec 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -4,9 +4,7 @@ * External dependencies */ import React, { useCallback, useEffect, useState } from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, TableCardColumn } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; import { Button } from '@wordpress/components'; @@ -21,6 +19,7 @@ import DocumentsFilters from '../filters'; import Page from '../../components/page'; import { getDocumentUrl } from 'wcpay/utils'; import VatFormModal from 'wcpay/vat/form-modal'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'date' | 'type' | 'description' | 'download'; @@ -68,16 +67,8 @@ const getDocumentDescription = ( document: Document ) => { if ( document.period_from && document.period_to ) { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), - dateI18n( - 'M j, Y', - moment.utc( document.period_from ).toISOString(), - 'utc' - ), - dateI18n( - 'M j, Y', - moment.utc( document.period_to ).toISOString(), - 'utc' - ) + formatDateTimeFromString( document.period_from ), + formatDateTimeFromString( document.period_to ) ); } return __( @@ -180,10 +171,7 @@ export const DocumentsList = (): JSX.Element => { const data = { date: { value: document.date, - display: dateI18n( - 'M j, Y', - moment.utc( document.date ).local().toISOString() - ), + display: formatDateTimeFromString( document.date ), }, type: { value: documentType, diff --git a/client/documents/list/test/index.tsx b/client/documents/list/test/index.tsx index c54cf7a02c8..0eac7e0bf61 100644 --- a/client/documents/list/test/index.tsx +++ b/client/documents/list/test/index.tsx @@ -36,6 +36,7 @@ declare const global: { accountStatus: { hasSubmittedVatData: boolean; }; + dateFormat: string; }; }; @@ -60,6 +61,11 @@ describe( 'Documents list', () => { let container: Element; let rerender: ( ui: React.ReactElement ) => void; beforeEach( () => { + global.wcpaySettings = { + accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', + }; + mockUseDocuments.mockReturnValue( { documents: getMockDocuments(), isLoading: false, @@ -200,6 +206,7 @@ describe( 'Document download button', () => { beforeEach( () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', }; render( ); @@ -223,6 +230,7 @@ describe( 'Document download button', () => { beforeEach( () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: false }, + dateFormat: 'M j, Y', }; render( ); @@ -293,6 +301,7 @@ describe( 'Direct document download', () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', }; } ); diff --git a/client/globals.d.ts b/client/globals.d.ts index 0d10d7de86b..8b91ee4b05f 100644 --- a/client/globals.d.ts +++ b/client/globals.d.ts @@ -123,6 +123,7 @@ declare global { storeName: string; isNextDepositNoticeDismissed: boolean; isInstantDepositNoticeDismissed: boolean; + isDateFormatNoticeDismissed: boolean; reporting: { exportModalDismissed?: boolean; }; @@ -137,6 +138,8 @@ declare global { isOverviewSurveySubmitted: boolean; lifetimeTPV: number; defaultExpressCheckoutBorderRadius: string; + dateFormat: string; + timeFormat: string; }; const wc: { diff --git a/client/overview/index.js b/client/overview/index.js index edb215993c7..5d6d06a52c2 100644 --- a/client/overview/index.js +++ b/client/overview/index.js @@ -33,6 +33,7 @@ import SandboxModeSwitchToLiveNotice from 'wcpay/components/sandbox-mode-switch- import './style.scss'; import BannerNotice from 'wcpay/components/banner-notice'; import { PayoutsRenameNotice } from 'wcpay/deposits/rename-notice'; +import DateFormatNotice from 'wcpay/components/date-format-notice'; const OverviewPageError = () => { const queryParams = getQuery(); @@ -152,6 +153,7 @@ const OverviewPage = () => { + { showLoanOfferError && ( { __( diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index 2c654a57f9b..29a649ad561 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -3,9 +3,7 @@ */ import React, { useState } from 'react'; import { Button, Modal, Notice } from '@wordpress/components'; -import { dateI18n } from '@wordpress/date'; import { sprintf } from '@wordpress/i18n'; -import moment from 'moment'; /** * Internal dependencies @@ -13,6 +11,7 @@ import moment from 'moment'; import strings from './strings'; import './index.scss'; import { recordEvent } from 'wcpay/tracks'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { errorMessages: Array< string >; @@ -57,11 +56,11 @@ const UpdateBusinessDetailsModal = ( { currentDeadline ? sprintf( strings.restrictedSoonDescription, - dateI18n( - 'ga M j, Y', - moment( - currentDeadline * 1000 - ).toISOString() + formatDateTimeFromTimestamp( + currentDeadline, + { + customFormat: 'ga M j, Y', + } ) ) : strings.restrictedDescription } diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index 235b92696b9..333c15d6709 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -2,7 +2,6 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import { getHistory } from '@woocommerce/navigation'; @@ -15,6 +14,7 @@ import { formatCurrency } from 'multi-currency/interface/functions'; import { getAdminUrl } from 'wcpay/utils'; import { recordEvent } from 'tracks'; import { isDueWithin } from 'wcpay/disputes/utils'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; /** * Returns an array of disputes that are due within the specified number of days. @@ -142,10 +142,9 @@ export const getDisputeResolutionTask = ( ? sprintf( __( 'Respond today by %s', 'woocommerce-payments' ), // Show due_by time in local timezone: e.g. "11:59 PM". - dateI18n( - 'g:i A', - moment.utc( dispute.due_by ).local().toISOString() - ) + formatDateTimeFromString( dispute.due_by, { + customFormat: 'g:i A', + } ) ) : sprintf( __( @@ -153,11 +152,8 @@ export const getDisputeResolutionTask = ( 'woocommerce-payments' ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". - dateI18n( - 'M j, Y', - moment.utc( dispute.due_by ).local().toISOString() - ), - moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". + formatDateTimeFromString( dispute.due_by ), + moment.utc( dispute.due_by ).fromNow( true ) // E.g. "2 days". ); return disputeTask; diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx index a18e06e9c09..61255a0e413 100644 --- a/client/overview/task-list/tasks/update-business-details-task.tsx +++ b/client/overview/task-list/tasks/update-business-details-task.tsx @@ -11,9 +11,8 @@ import { addQueryArgs } from '@wordpress/url'; */ import type { TaskItemProps } from '../types'; import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; -import { dateI18n } from '@wordpress/date'; -import moment from 'moment'; import { recordEvent } from 'wcpay/tracks'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; export const getUpdateBusinessDetailsTask = ( errorMessages: string[], @@ -46,10 +45,9 @@ export const getUpdateBusinessDetailsTask = ( 'Update by %s to avoid a disruption in payouts.', 'woocommerce-payments' ), - dateI18n( - 'ga M j, Y', - moment( currentDeadline * 1000 ).toISOString() - ) + formatDateTimeFromTimestamp( currentDeadline, { + customFormat: 'ga M j, Y', + } ) ); if ( hasSingleError ) { diff --git a/client/overview/task-list/test/tasks.js b/client/overview/task-list/test/tasks.js index 733d2208b08..9134a1c6842 100644 --- a/client/overview/task-list/test/tasks.js +++ b/client/overview/task-list/test/tasks.js @@ -139,6 +139,7 @@ describe( 'getTasks()', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); afterEach( () => { diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 18993ef2387..91255a4d786 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -2,22 +2,22 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import classNames from 'classnames'; import moment from 'moment'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const DisputeDueByDate: React.FC< { dueBy: number; showRemainingDays?: boolean; } > = ( { dueBy, showRemainingDays = true } ) => { const daysRemaining = Math.floor( - moment.unix( dueBy ).diff( moment(), 'days', true ) - ); - const respondByDate = dateI18n( - 'M j, Y, g:ia', - moment( dueBy * 1000 ).toISOString() + moment.unix( dueBy ).utc().diff( moment().utc(), 'days', true ) ); + const respondByDate = formatDateTimeFromTimestamp( dueBy, { + separator: ', ', + includeTime: true, + } ); return ( { respondByDate } diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index 15fec759244..bc4b1e94dbd 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -2,8 +2,6 @@ * External dependencies */ import React from 'react'; -import moment from 'moment'; -import { dateI18n } from '@wordpress/date'; import { __, sprintf } from '@wordpress/i18n'; import { Link } from '@woocommerce/components'; import { createInterpolateElement } from '@wordpress/element'; @@ -17,18 +15,15 @@ import { recordEvent } from 'tracks'; import { getAdminUrl } from 'wcpay/utils'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import './style.scss'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const DisputeUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? dateI18n( - 'M j, Y', - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) - ) - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ), + { includeTime: true } ) : '-'; @@ -93,13 +88,9 @@ const DisputeWonFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ), + { includeTime: true } ) : '-'; @@ -171,13 +162,8 @@ const DisputeLostFooter: React.FC< { const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) : '-'; @@ -274,13 +260,8 @@ const InquiryUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? dateI18n( - 'M j, Y', - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) - ) - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) : '-'; @@ -346,13 +327,8 @@ const InquiryClosedFooter: React.FC< { } > = ( { dispute } ) => { const isSubmitted = !! dispute.metadata.__evidence_submitted_at; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) : '-'; diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index 0f90723a8f1..28707dfc6c4 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -7,8 +7,6 @@ import React from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; import { ExternalLink } from '@wordpress/components'; -import { dateI18n } from '@wordpress/date'; -import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; /** @@ -20,6 +18,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { ClickTooltip } from 'wcpay/components/tooltip'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import DisputeDueByDate from './dispute-due-by-date'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -35,14 +34,8 @@ export const DisputeSteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = dateI18n( - 'Y-m-d', - moment( chargeCreated * 1000 ).toISOString() - ); - const disputeDate = dateI18n( - 'Y-m-d', - moment( dispute.created * 1000 ).toISOString() - ); + const chargeDate = formatDateTimeFromTimestamp( chargeCreated ); + const disputeDate = formatDateTimeFromTimestamp( dispute.created ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. __( @@ -175,14 +168,12 @@ export const InquirySteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = dateI18n( - 'Y-m-d', - moment( chargeCreated * 1000 ).toISOString() - ); - const disputeDate = dateI18n( - 'Y-m-d', - moment( dispute.created * 1000 ).toISOString() - ); + const chargeDate = formatDateTimeFromTimestamp( chargeCreated, { + includeTime: true, + } ); + const disputeDate = formatDateTimeFromTimestamp( dispute.created, { + includeTime: true, + } ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. __( diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index 0a43cb223e0..95119d01f82 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -4,10 +4,8 @@ * External dependencies */ import React from 'react'; -import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies @@ -20,6 +18,7 @@ import { formatStringValue } from 'wcpay/utils'; import { ClickTooltip } from 'wcpay/components/tooltip'; import Paragraphs from 'wcpay/components/paragraphs'; import DisputeDueByDate from './dispute-due-by-date'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -39,10 +38,10 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { { title: __( 'Disputed On', 'woocommerce-payments' ), content: dispute.created - ? dateI18n( - 'M j, Y, g:ia', - moment( dispute.created * 1000 ).toISOString() - ) + ? formatDateTimeFromTimestamp( dispute.created, { + separator: ', ', + includeTime: true, + } ) : '–', }, { diff --git a/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap b/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap index 7b4a7e3650f..a1af47f03c4 100644 --- a/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap @@ -6,6 +6,54 @@ exports[`Order details page should match the snapshot - Charge without payment i class="wcpay-payment-details woocommerce-payments-page" style="max-width: 1032px;" > +
    + +
    + The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
    + +
    { featureFlags: { paymentTimeline: true }, zeroDecimalCurrencies: [], connect: { country: 'US' }, + timeFormat: 'g:ia', + dateFormat: 'M j, Y', }; const selectMock = jest.fn( ( storeName ) => diff --git a/client/payment-details/payment-details/index.tsx b/client/payment-details/payment-details/index.tsx index cdf568520f2..a32d0c94193 100644 --- a/client/payment-details/payment-details/index.tsx +++ b/client/payment-details/payment-details/index.tsx @@ -17,7 +17,7 @@ import PaymentDetailsPaymentMethod from '../payment-method'; import { ApiError } from '../../types/errors'; import { Charge } from '../../types/charges'; import { PaymentIntent } from '../../types/payment-intents'; - +import DateFormatNotice from 'wcpay/components/date-format-notice'; interface PaymentDetailsProps { id: string; isLoading: boolean; @@ -56,6 +56,7 @@ const PaymentDetails: React.FC< PaymentDetailsProps > = ( { return ( + { const { readers, chargeError, isLoading } = useCardReaderStats( props.chargeId, @@ -34,6 +34,7 @@ const PaymentCardReaderChargeDetails = ( props ) => { if ( ! isLoading && chargeError instanceof Error ) { return ( + diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 1e5b6bfcac9..59ae556c785 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -4,7 +4,6 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { Card, CardBody, @@ -64,6 +63,10 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote import ErrorBoundary from 'components/error-boundary'; import RefundModal from 'wcpay/payment-details/summary/refund-modal'; import CardNotice from 'wcpay/components/card-notice'; +import { + formatDateTimeFromString, + formatDateTimeFromTimestamp, +} from 'wcpay/utils/date-time'; declare const window: any; @@ -110,10 +113,10 @@ const composePaymentSummaryItems = ( { { title: __( 'Date', 'woocommerce-payments' ), content: charge.created - ? dateI18n( - 'M j, Y, g:ia', - moment( charge.created * 1000 ).toISOString() - ) + ? formatDateTimeFromTimestamp( charge.created, { + separator: ', ', + includeTime: true, + } ) : '–', }, { @@ -714,12 +717,13 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { } ) }{ ' ' } diff --git a/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap b/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap index 083da902f05..6dacc01df87 100644 --- a/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap @@ -304,7 +304,7 @@ exports[`PaymentDetailsSummary capture notification and fraud buttons renders ca this charge within the next 7 days @@ -660,7 +660,7 @@ exports[`PaymentDetailsSummary capture notification and fraud buttons renders th this charge within the next 7 days diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx index 9055d481dda..8c47e9b9b6a 100755 --- a/client/payment-details/summary/test/index.test.tsx +++ b/client/payment-details/summary/test/index.test.tsx @@ -17,6 +17,15 @@ import PaymentDetailsSummary from '../'; import { useAuthorization } from 'wcpay/data'; import { paymentIntentMock } from 'wcpay/data/payment-intents/test/hooks'; +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + declare const global: { wcSettings: { locale: { @@ -34,6 +43,8 @@ declare const global: { featureFlags: { isAuthAndCaptureEnabled: boolean; }; + dateFormat: string; + timeFormat: string; }; }; @@ -74,7 +85,7 @@ const getBaseCharge = (): Charge => id: 'ch_38jdHA39KKA', payment_intent: 'pi_abc', /* Stripe data comes in seconds, instead of the default Date milliseconds */ - created: Date.parse( 'Sep 19, 2019, 5:24 pm' ) / 1000, + created: 1568913840, amount: 2000, amount_refunded: 0, application_fee_amount: 70, @@ -203,6 +214,8 @@ describe( 'PaymentDetailsSummary', () => { precision: 0, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:ia', }; // mock Date.now that moment library uses to get current date for testing purposes @@ -408,7 +421,7 @@ describe( 'PaymentDetailsSummary', () => { ).toHaveTextContent( /\$20.00/ ); expect( screen.getByText( /Disputed On/i ).nextSibling - ).toHaveTextContent( /Aug 30, 2023/ ); + ).toHaveTextContent( /Aug 31, 2023/ ); expect( screen.getByText( /Reason/i ).nextSibling ).toHaveTextContent( /Transaction unauthorized/ ); diff --git a/client/payment-details/test/__snapshots__/index.test.tsx.snap b/client/payment-details/test/__snapshots__/index.test.tsx.snap index aa8a34effd0..19af9ff7235 100644 --- a/client/payment-details/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/test/__snapshots__/index.test.tsx.snap @@ -6,6 +6,54 @@ exports[`Payment details page should match the snapshot - Charge query param 1`] class="wcpay-payment-details woocommerce-payments-page" style="max-width: 1032px;" > +
    + +
    + The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
    + +
    +
    + +
    + The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
    + +
    { diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index 7e5570ce8ce..6e4f593e1f0 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -5,9 +5,7 @@ */ import { flatMap } from 'lodash'; import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { addQueryArgs } from '@wordpress/url'; -import moment from 'moment'; import { createInterpolateElement } from '@wordpress/element'; import { Link } from '@woocommerce/components'; import SyncIcon from 'gridicons/dist/sync'; @@ -31,6 +29,7 @@ import { formatFee } from 'utils/fees'; import { getAdminUrl } from 'wcpay/utils'; import { ShieldIcon } from 'wcpay/icons'; import { fraudOutcomeRulesetMapping, paymentFailureMapping } from './mappings'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; /** * Creates a timeline item about a payment status change @@ -84,10 +83,7 @@ const getDepositTimelineItem = ( 'woocommerce-payments' ), formattedAmount, - dateI18n( - 'M j, Y', - moment( event.deposit.arrival_date * 1000 ).toISOString() - ) + formatDateTimeFromTimestamp( event.deposit.arrival_date ) ); const depositUrl = getAdminUrl( { page: 'wc-admin', @@ -143,10 +139,7 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { 'woocommerce-payments' ), formattedAmount, - dateI18n( - 'M j, Y', - moment( event.deposit.arrival_date * 1000 ).toISOString() - ) + formatDateTimeFromTimestamp( event.deposit.arrival_date ) ); const depositUrl = getAdminUrl( { diff --git a/client/payment-details/timeline/test/index.js b/client/payment-details/timeline/test/index.js index 2529f3d673e..616c780dd69 100644 --- a/client/payment-details/timeline/test/index.js +++ b/client/payment-details/timeline/test/index.js @@ -34,6 +34,7 @@ describe( 'PaymentDetailsTimeline', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); diff --git a/client/payment-details/timeline/test/map-events.js b/client/payment-details/timeline/test/map-events.js index f1c0588d659..beee5dc959b 100644 --- a/client/payment-details/timeline/test/map-events.js +++ b/client/payment-details/timeline/test/map-events.js @@ -47,6 +47,7 @@ describe( 'mapTimelineEvents', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 1d75e407cea..8e4f410ff32 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -2,9 +2,7 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; /** @@ -15,6 +13,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; import { getDetailsURL } from '../../components/details-link'; import ClickableCell from '../../components/clickable-cell'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -70,10 +69,9 @@ export const getBlockedListRowContent = ( data.payment_intent.id || data.order_id.toString(), 'transactions' ); - const formattedCreatedDate = dateI18n( - 'M j, Y / g:iA', - moment.utc( data.created ).local().toISOString() - ); + const formattedCreatedDate = formatDateTimeFromString( data.created, { + includeTime: true, + } ); const clickable = ( children: JSX.Element | string ) => ( { children } diff --git a/client/transactions/blocked/test/columns.test.tsx b/client/transactions/blocked/test/columns.test.tsx index 7ca2c3d4895..b65e2d10c12 100644 --- a/client/transactions/blocked/test/columns.test.tsx +++ b/client/transactions/blocked/test/columns.test.tsx @@ -15,6 +15,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; const mockWcPaySettings = { @@ -23,6 +25,8 @@ const mockWcPaySettings = { connect: { country: 'US', }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; describe( 'Blocked fraud outcome transactions columns', () => { diff --git a/client/transactions/index.tsx b/client/transactions/index.tsx index 507a972cd5a..f8139f16797 100644 --- a/client/transactions/index.tsx +++ b/client/transactions/index.tsx @@ -23,6 +23,7 @@ import { } from 'wcpay/data'; import WCPaySettingsContext from '../settings/wcpay-settings-context'; import BlockedList from './blocked'; +import DateFormatNotice from 'components/date-format-notice'; declare const window: any; @@ -106,6 +107,7 @@ export const TransactionsPage: React.FC = () => { return ( + = ( { depositId, dateAvailable } ) => { id: depositId, } ); - const formattedDateAvailable = dateI18n( - 'M j, Y', - moment.utc( dateAvailable ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. + const formattedDateAvailable = formatDateTimeFromString( + dateAvailable ); - return { formattedDateAvailable }; } diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index a5206ae0e5e..a4d9abc852a 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -7,9 +7,7 @@ import React, { Fragment, useState } from 'react'; import { uniq } from 'lodash'; import { useDispatch } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, Search, @@ -70,6 +68,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface TransactionsListProps { depositId?: string; @@ -466,10 +465,9 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - dateI18n( - 'M j, Y / g:iA', - moment.utc( txn.date ).local().toISOString() - ) + formatDateTimeFromString( txn.date, { + includeTime: true, + } ) ), }, channel: { diff --git a/client/transactions/list/test/__snapshots__/index.tsx.snap b/client/transactions/list/test/__snapshots__/index.tsx.snap index 7a4b1eb3392..c432444b3ea 100644 --- a/client/transactions/list/test/__snapshots__/index.tsx.snap +++ b/client/transactions/list/test/__snapshots__/index.tsx.snap @@ -493,7 +493,7 @@ exports[`Transactions list renders correctly when can filter by several currenci href="admin.php?page=wc-admin&path=%2Fpayments%2Ftransactions%2Fdetails&id=pi_mock&transaction_id=txn_j23jda9JJa&transaction_type=refund" tabindex="-1" > - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Deposit', () => { + beforeEach( () => { + // Mock the window.wcpaySettings property + window.wcpaySettings.dateFormat = 'M j, Y'; + window.wcpaySettings.timeFormat = 'g:i a'; + } ); + + afterEach( () => { + // Reset the mock + jest.clearAllMocks(); + } ); + test( 'renders with date and payout available', () => { const { container: link } = render( diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx index b233b4d5477..66db0268a34 100644 --- a/client/transactions/list/test/index.tsx +++ b/client/transactions/list/test/index.tsx @@ -58,6 +58,15 @@ jest.mock( 'data/index', () => ( { useReportingExportLanguage: jest.fn( () => [ 'en', jest.fn() ] ), } ) ); +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + const mockDownloadCSVFile = downloadCSVFile as jest.MockedFunction< typeof downloadCSVFile >; @@ -244,6 +253,8 @@ describe( 'Transactions list', () => { exportModalDismissed: true, }, }; + window.wcpaySettings.dateFormat = 'M j, Y'; + window.wcpaySettings.timeFormat = 'g:iA'; } ); test( 'renders correctly when filtered by payout', () => { diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index d7f5de95111..c1e24c4d428 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -2,9 +2,7 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; import { Button } from '@wordpress/components'; @@ -17,6 +15,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -76,10 +75,9 @@ export const getRiskReviewListRowContent = ( data: FraudOutcomeTransaction ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); - const formattedCreatedDate = dateI18n( - 'M j, Y / g:iA', - moment.utc( data.created ).local().toISOString() - ); + const formattedCreatedDate = formatDateTimeFromString( data.created, { + includeTime: true, + } ); const clickable = ( children: JSX.Element | string ) => ( { children } diff --git a/client/transactions/risk-review/test/columns.test.tsx b/client/transactions/risk-review/test/columns.test.tsx index 033f9eb7aa2..54de107e0e4 100644 --- a/client/transactions/risk-review/test/columns.test.tsx +++ b/client/transactions/risk-review/test/columns.test.tsx @@ -15,6 +15,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; const mockWcPaySettings = { @@ -23,6 +25,8 @@ const mockWcPaySettings = { connect: { country: 'US', }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; describe( 'Review fraud outcome transactions columns', () => { diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 17058760c19..42d6d69542b 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -7,7 +7,6 @@ import React, { useEffect } from 'react'; import { __ } from '@wordpress/i18n'; import { TableCard, TableCardColumn } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; /** @@ -21,6 +20,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; import { recordEvent } from 'tracks'; import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: @@ -130,35 +130,25 @@ export const AuthorizationsList = (): JSX.Element => { display: auth.payment_intent_id, }, created: { - value: dateI18n( - 'M j, Y / g:iA', - moment.utc( auth.created ).local().toISOString() - ), + value: formatDateTimeFromString( auth.created, { + includeTime: true, + } ), display: clickable( - dateI18n( - 'M j, Y / g:iA', - moment.utc( auth.created ).local().toISOString() - ) + formatDateTimeFromString( auth.created, { + includeTime: true, + } ) ), }, // Payments are authorized for a maximum of 7 days capture_by: { - value: dateI18n( - 'M j, Y / g:iA', - moment - .utc( auth.created ) - .add( 7, 'd' ) - .local() - .toISOString() + value: formatDateTimeFromString( + moment.utc( auth.created ).add( 7, 'd' ).toISOString(), + { includeTime: true } ), display: clickable( - dateI18n( - 'M j, Y / g:iA', - moment - .utc( auth.created ) - .add( 7, 'd' ) - .local() - .toISOString() + formatDateTimeFromString( + moment.utc( auth.created ).add( 7, 'd' ).toISOString(), + { includeTime: true } ) ), }, diff --git a/client/transactions/uncaptured/test/index.test.tsx b/client/transactions/uncaptured/test/index.test.tsx index 52f76dcc882..fc21a532b67 100644 --- a/client/transactions/uncaptured/test/index.test.tsx +++ b/client/transactions/uncaptured/test/index.test.tsx @@ -67,6 +67,8 @@ declare const global: { precision: number; }; }; + dateFormat: string; + timeFormat: string; }; }; @@ -126,6 +128,8 @@ describe( 'Authorizations list', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts new file mode 100644 index 00000000000..83e4c2c2257 --- /dev/null +++ b/client/utils/date-time.ts @@ -0,0 +1,82 @@ +/** + * External dependencies + */ +import { dateI18n } from '@wordpress/date'; +import moment from 'moment'; + +type DateTimeFormat = string | null; + +interface FormatDateTimeOptions { + /** Whether to include time in the formatted string (defaults to true) */ + includeTime?: boolean; + /** Separator between date and time (defaults to ' / ') */ + separator?: string; + /** Custom format to use instead of WordPress settings */ + customFormat?: DateTimeFormat; + /** Timezone string (e.g., 'UTC', 'America/New_York'). If undefined, uses site default */ + timezone?: string; +} + +/** + * Formats a date/time string in YYYY-MM-DD HH:MM:SS format according to WordPress settings. + * The input date string is converted to UTC for consistent handling across timezones. + * + * @param dateTimeStr - Date time string in YYYY-MM-DD HH:MM:SS format + * @param options - Formatting options + */ +export function formatDateTimeFromString( + dateTimeStr: string, + options: FormatDateTimeOptions = {} +): string { + const { + customFormat = null, + includeTime = false, + separator = ' / ', + timezone = undefined, + } = options; + + // Convert to UTC ISO string for consistent handling + const utcDateTime = moment.utc( dateTimeStr ).toISOString(); + + const format = + customFormat || + `${ window.wcpaySettings.dateFormat }${ + includeTime + ? `${ separator }${ window.wcpaySettings.timeFormat }` + : '' + }`; + + return dateI18n( format, utcDateTime, timezone ); +} + +/** + * Formats a Unix timestamp according to WordPress settings. + * The input timestamp is converted to UTC for consistent handling across timezones. + * + * @param timestamp - Unix timestamp (seconds since epoch) + * @param options - Formatting options + */ +export function formatDateTimeFromTimestamp( + timestamp: number, + options: FormatDateTimeOptions = {} +): string { + const { + customFormat = null, + includeTime = false, + separator = ' / ', + timezone = undefined, + } = options; + + // Convert to UTC ISO string for consistent handling + const utcDateTime = moment.unix( timestamp ).utc().toISOString(); + + const format = + customFormat || + `${ window.wcpaySettings.dateFormat }${ + includeTime + ? `${ separator }${ window.wcpaySettings.timeFormat }` + : '' + }`; + + return dateI18n( format, utcDateTime, timezone ); +} diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts new file mode 100644 index 00000000000..798c95d7755 --- /dev/null +++ b/client/utils/test/date-time.test.ts @@ -0,0 +1,181 @@ +/** + * Internal dependencies + */ +import { + formatDateTimeFromString, + formatDateTimeFromTimestamp, +} from 'wcpay/utils/date-time'; + +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date, timezone ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, timezone || 'UTC' ); // Use provided timezone or fallback to UTC + } ), +} ) ); + +describe( 'Date/Time Formatting', () => { + const originalWcpaySettings = window.wcpaySettings; + const mockWcpaySettings = { + dateFormat: 'Y-m-d', + timeFormat: 'H:i', + }; + + beforeEach( () => { + jest.clearAllMocks(); + window.wcpaySettings = mockWcpaySettings as typeof wcpaySettings; + } ); + + afterEach( () => { + window.wcpaySettings = originalWcpaySettings; + } ); + + describe( 'formatDateTimeFromString', () => { + it( 'should format using default WordPress settings', () => { + const dateTime = '2024-10-23 15:28:26'; + const formatted = formatDateTimeFromString( dateTime, { + includeTime: true, + } ); + + expect( formatted ).toBe( '2024-10-23 / 15:28' ); + } ); + + it( 'should use custom format if provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { customFormat: 'd-m-Y H:i:s' }; + const formatted = formatDateTimeFromString( dateTime, options ); + + expect( formatted ).toBe( '23-10-2024 15:28:26' ); + } ); + + it( 'should exclude time if includeTime is set to false', () => { + const dateTime = '2024-10-23 15:28:26'; + const formatted = formatDateTimeFromString( dateTime ); + + expect( formatted ).toBe( '2024-10-23' ); + } ); + + it( 'should use custom separator when provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { separator: ' - ', includeTime: true }; + const formatted = formatDateTimeFromString( dateTime, options ); + + expect( formatted ).toBe( '2024-10-23 - 15:28' ); + } ); + + it( 'should handle different timezones correctly', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const dateI18n = require( '@wordpress/date' ).dateI18n; + // Temporarily modify the mock to use a different timezone: America/New_York + dateI18n.mockImplementationOnce( + ( format: string, date: string | number ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'America/New_York' ); + } + ); + + const dateTime = '2024-10-23 15:28:26'; + const formatted = formatDateTimeFromString( dateTime, { + includeTime: true, + } ); + + expect( formatted ).toBe( '2024-10-23 / 11:28' ); + } ); + + it( 'should respect explicitly provided timezone', () => { + const dateTime = '2024-10-23 15:28:26'; + + // Test with UTC timezone + const formattedUTC = formatDateTimeFromString( dateTime, { + includeTime: true, + timezone: 'UTC', + } ); + expect( formattedUTC ).toBe( '2024-10-23 / 15:28' ); + + // Test with New York timezone + const formattedNY = formatDateTimeFromString( dateTime, { + includeTime: true, + timezone: 'America/New_York', + } ); + expect( formattedNY ).toBe( '2024-10-23 / 11:28' ); + } ); + } ); + + describe( 'formatDateTimeFromTimestamp', () => { + it( 'should format using default WordPress settings', () => { + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC + const formatted = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + } ); + + expect( formatted ).toBe( '2024-10-24 / 10:48' ); + } ); + + it( 'should use custom format if provided', () => { + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC + const options = { customFormat: 'd-m-Y H:i:s' }; + const formatted = formatDateTimeFromTimestamp( timestamp, options ); + + expect( formatted ).toBe( '24-10-2024 10:48:26' ); + } ); + + it( 'should exclude time if includeTime is set to false', () => { + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC + const formatted = formatDateTimeFromTimestamp( timestamp ); + + expect( formatted ).toBe( '2024-10-24' ); + } ); + + it( 'should use custom separator when provided', () => { + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC + const options = { + separator: ' - ', + includeTime: true, + }; + const formatted = formatDateTimeFromTimestamp( timestamp, options ); + + expect( formatted ).toBe( '2024-10-24 - 10:48' ); + } ); + + it( 'should handle different timezones correctly', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const dateI18n = require( '@wordpress/date' ).dateI18n; + // Temporarily modify the mock to use a different timezone: America/New_York + dateI18n.mockImplementationOnce( + ( format: string, date: string | number ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'America/New_York' ); + } + ); + + const timestamp = 1729766906; // 2024-10-24 10:48:26 UTC + const formatted = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + } ); + + // In New York (EDT), this should be 4 hours behind UTC + expect( formatted ).toBe( '2024-10-24 / 06:48' ); + } ); + + it( 'should respect explicitly provided timezone', () => { + const timestamp = 1729766906; // 2024-10-24 10:48:26 UTC + + // Test with UTC timezone + const formattedUTC = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + timezone: 'UTC', + } ); + expect( formattedUTC ).toBe( '2024-10-24 / 10:48' ); + + // Test with New York timezone + const formattedNY = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + timezone: 'America/New_York', + } ); + expect( formattedNY ).toBe( '2024-10-24 / 06:48' ); + } ); + } ); +} ); diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index d78671d1298..392dec5c611 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -973,6 +973,7 @@ private function get_js_settings(): array { 'storeName' => get_bloginfo( 'name' ), 'isNextDepositNoticeDismissed' => WC_Payments_Features::is_next_deposit_notice_dismissed(), 'isInstantDepositNoticeDismissed' => get_option( 'wcpay_instant_deposit_notice_dismissed', false ), + 'isDateFormatNoticeDismissed' => get_option( 'wcpay_date_format_notice_dismissed', false ), 'reporting' => [ 'exportModalDismissed' => get_option( 'wcpay_reporting_export_modal_dismissed', false ), ], @@ -983,6 +984,8 @@ private function get_js_settings(): array { 'lifetimeTPV' => $this->account->get_lifetime_total_payment_volume(), 'defaultExpressCheckoutBorderRadius' => WC_Payments_Express_Checkout_Button_Handler::DEFAULT_BORDER_RADIUS_IN_PX, 'isWooPayGlobalThemeSupportEligible' => WC_Payments_Features::is_woopay_global_theme_support_eligible(), + 'dateFormat' => wc_date_format(), + 'timeFormat' => get_option( 'time_format' ), ]; return apply_filters( 'wcpay_js_settings', $this->wcpay_js_settings ); diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 4ad2d32625e..7b4ae0750c2 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -1930,6 +1930,7 @@ public static function add_wcpay_options_to_woocommerce_permissions_list( $permi 'wcpay_duplicate_payment_method_notices_dismissed', 'wcpay_exit_survey_dismissed', 'wcpay_instant_deposit_notice_dismissed', + 'wcpay_date_format_notice_dismissed', ], true ); From f6010a425ef2d3619c83441552a4fff58c6fcf6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Costa?= <10233985+cesarcosta99@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:38:33 -0300 Subject: [PATCH 32/38] Show "postal code check" label based on store country (#9952) --- changelog/fix-9896-postal-code-label | 4 ++++ .../order-details/test/__snapshots__/index.test.tsx.snap | 2 +- client/payment-details/payment-method/card/index.js | 5 ++++- .../payment-details/test/__snapshots__/index.test.tsx.snap | 4 ++-- client/style.scss | 4 ++++ 5 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 changelog/fix-9896-postal-code-label diff --git a/changelog/fix-9896-postal-code-label b/changelog/fix-9896-postal-code-label new file mode 100644 index 00000000000..dad971912a8 --- /dev/null +++ b/changelog/fix-9896-postal-code-label @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Localize postal code check label based on country. diff --git a/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap b/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap index a1af47f03c4..ed6c9656178 100644 --- a/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/order-details/test/__snapshots__/index.test.tsx.snap @@ -613,7 +613,7 @@ exports[`Order details page should match the snapshot - Charge without payment i

    - Zip check + Postal code check

    { diff --git a/client/payment-details/test/__snapshots__/index.test.tsx.snap b/client/payment-details/test/__snapshots__/index.test.tsx.snap index 19af9ff7235..feea948e950 100644 --- a/client/payment-details/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/test/__snapshots__/index.test.tsx.snap @@ -485,7 +485,7 @@ exports[`Payment details page should match the snapshot - Charge query param 1`] aria-busy="true" class="is-loadable-placeholder is-block" > - Zip check + Postal code check

    - Zip check + Postal code check

    Date: Fri, 20 Dec 2024 16:35:13 +0200 Subject: [PATCH 33/38] Fix blank Overview page when WC onboarding is disabled (#10020) Co-authored-by: Cvetan Cvetanov --- changelog/fix-9742-blank-overview-page-without-wc-features | 4 ++++ client/onboarding/utils.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog/fix-9742-blank-overview-page-without-wc-features diff --git a/changelog/fix-9742-blank-overview-page-without-wc-features b/changelog/fix-9742-blank-overview-page-without-wc-features new file mode 100644 index 00000000000..8d473bd8ab4 --- /dev/null +++ b/changelog/fix-9742-blank-overview-page-without-wc-features @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix blank Payments > Overview page when WC onboarding is disabled. diff --git a/client/onboarding/utils.ts b/client/onboarding/utils.ts index 306328f64f7..809c87a9927 100644 --- a/client/onboarding/utils.ts +++ b/client/onboarding/utils.ts @@ -140,7 +140,7 @@ export const isPoEligible = async ( * @return {string | undefined} The MCC code for the selected industry. Will return undefined if no industry is selected. */ export const getMccFromIndustry = (): string | undefined => { - const industry = wcSettings.admin.onboarding.profile.industry?.[ 0 ]; + const industry = wcSettings.admin?.onboarding?.profile?.industry?.[ 0 ]; if ( ! industry ) { return undefined; } From cd650bd4444cfb6487f05aa4238ae8b7d8044757 Mon Sep 17 00:00:00 2001 From: Malith Senaweera <6216000+malithsen@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:01:43 -0600 Subject: [PATCH 34/38] Show a beta badge next to WooPay theme checkbox (#10000) --- changelog/add-beta-badge-woopay-theme | 5 +++++ .../settings/express-checkout-settings/index.scss | 12 ++++++++++++ .../express-checkout-settings/woopay-settings.js | 15 +++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 changelog/add-beta-badge-woopay-theme diff --git a/changelog/add-beta-badge-woopay-theme b/changelog/add-beta-badge-woopay-theme new file mode 100644 index 00000000000..8d379077613 --- /dev/null +++ b/changelog/add-beta-badge-woopay-theme @@ -0,0 +1,5 @@ +Significance: patch +Type: add +Comment: Adds a label to a gated feature. + + diff --git a/client/settings/express-checkout-settings/index.scss b/client/settings/express-checkout-settings/index.scss index f57e1e973e7..7b6bd1ca1fe 100644 --- a/client/settings/express-checkout-settings/index.scss +++ b/client/settings/express-checkout-settings/index.scss @@ -58,6 +58,18 @@ } .woopay-settings { + &__badge { + background-color: $studio-gray-5; + padding: 0 8px; + border-radius: 2px; + font-size: 12px; + line-height: 20px; + margin-left: 4px; + } + &__global-theme-label { + display: inline-flex; + align-items: center; + } &__custom-message-wrapper { position: relative; diff --git a/client/settings/express-checkout-settings/woopay-settings.js b/client/settings/express-checkout-settings/woopay-settings.js index f2a4931f9f6..6f5b16baece 100644 --- a/client/settings/express-checkout-settings/woopay-settings.js +++ b/client/settings/express-checkout-settings/woopay-settings.js @@ -226,10 +226,17 @@ const WooPaySettings = ( { section } ) => { onChange={ updateIsWooPayGlobalThemeSupportEnabled } - label={ __( - 'Enable global theme support', - 'woocommerce-payments' - ) } + label={ +

    + { __( + 'Enable global theme support', + 'woocommerce-payments' + ) } + + Beta + +
    + } help={ interpolateComponents( { mixedString: __( 'When enabled, WooPay checkout will be themed with your store’s brand colors and fonts. ' + From 34e3731ed0424de26dc0cab68751c4c163d10247 Mon Sep 17 00:00:00 2001 From: Cvetan Cvetanov Date: Fri, 20 Dec 2024 18:03:58 +0200 Subject: [PATCH 35/38] Enable Payment Methods preselected by NOX after onboarding accounts (#10018) --- .../add-9973-enable-pms-from-capabilities | 4 + includes/class-wc-payments-account.php | 30 ++++++ .../class-wc-payments-onboarding-service.php | 92 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 changelog/add-9973-enable-pms-from-capabilities diff --git a/changelog/add-9973-enable-pms-from-capabilities b/changelog/add-9973-enable-pms-from-capabilities new file mode 100644 index 00000000000..5ecfd2ac5cd --- /dev/null +++ b/changelog/add-9973-enable-pms-from-capabilities @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Enable Payment Methods preselected by NOX after onboarding accounts diff --git a/includes/class-wc-payments-account.php b/includes/class-wc-payments-account.php index dc5430d6f0f..ebe3888afc5 100644 --- a/includes/class-wc-payments-account.php +++ b/includes/class-wc-payments-account.php @@ -2049,6 +2049,21 @@ private function init_stripe_onboarding( string $setup_mode, string $wcpay_conne $gateway->update_option( 'enabled', 'yes' ); $gateway->update_option( 'test_mode', empty( $onboarding_data['is_live'] ) ? 'yes' : 'no' ); + /** + * ================== + * Enforces the update of payment methods to 'enabled' based on the capabilities + * provided during the NOX onboarding process. + * + * @see WC_Payments_Onboarding_Service::update_enabled_payment_methods_ids + * ================== + */ + $capabilities = $this->onboarding_service->get_capabilities_from_request(); + + // Activate enabled Payment Methods IDs. + if ( ! empty( $capabilities ) ) { + $this->onboarding_service->update_enabled_payment_methods_ids( $gateway, $capabilities ); + } + // Store a state after completing KYC for tracks. This is stored temporarily in option because // user might not have agreed to TOS yet. update_option( '_wcpay_onboarding_stripe_connected', [ 'is_existing_stripe_account' => true ] ); @@ -2161,6 +2176,20 @@ private function finalize_connection( string $state, string $mode, array $additi $gateway->update_option( 'enabled', 'yes' ); $gateway->update_option( 'test_mode', 'live' !== $mode ? 'yes' : 'no' ); + /** + * ================== + * Enforces the update of payment methods to 'enabled' based on the capabilities + * provided during the NOX onboarding process. + * + * @see WC_Payments_Onboarding_Service::update_enabled_payment_methods_ids + * ================== + */ + $capabilities = $this->onboarding_service->get_capabilities_from_request(); + // Activate enabled Payment Methods IDs. + if ( ! empty( $capabilities ) ) { + $this->onboarding_service->update_enabled_payment_methods_ids( $gateway, $capabilities ); + } + // Store a state after completing KYC for tracks. This is stored temporarily in option because // user might not have agreed to TOS yet. update_option( '_wcpay_onboarding_stripe_connected', [ 'is_existing_stripe_account' => false ] ); @@ -2570,6 +2599,7 @@ function (): array { ); } + /** * Send a Tracks event. * diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 504ac3bd5e4..cd5fe83d348 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -277,6 +277,22 @@ public function create_embedded_kyc_session( array $self_assessment_data, bool $ ); $actioned_notes = self::get_actioned_notes(); + /** + * ================== + * Enforces the update of payment methods to 'enabled' based on the capabilities + * provided during the NOX onboarding process. + * + * @see self::update_enabled_payment_methods_ids + * ================== + */ + $capabilities = $this->get_capabilities_from_request(); + $gateway = WC_Payments::get_gateway(); + + // Activate enabled Payment Methods IDs. + if ( ! empty( $capabilities ) ) { + $this->update_enabled_payment_methods_ids( $gateway, $capabilities ); + } + try { $account_session = $this->payments_api_client->initialize_onboarding_embedded_kyc( 'live' === $setup_mode, @@ -1024,4 +1040,80 @@ public function maybe_add_test_drive_settings_to_new_account_request( array $arg return $args; } + + /** + * Update payment methods to 'enabled' based on the capabilities + * provided during the NOX onboarding process. Merchants can preselect their preferred + * payment methods as part of this flow. + * + * The capabilities are provided in the following format: + * + * [ + * 'card' => true, + * 'affirm' => true, + * ... + * ] + * + * @param WC_Payment_Gateway_WCPay $gateway Payment gateway instance. + * @param array $capabilities Provided capabilities. + */ + public function update_enabled_payment_methods_ids( $gateway, $capabilities = [] ): void { + $enabled_gateways = $gateway->get_upe_enabled_payment_method_ids(); + + $enabled_payment_methods = array_unique( + array_merge( + $enabled_gateways, + $this->exclude_placeholder_payment_methods( $capabilities ) + ) + ); + + // Update the gateway option. + $gateway->update_option( 'upe_enabled_payment_method_ids', $enabled_payment_methods ); + + /** + * Keeps the list of enabled payment method IDs synchronized between the default + * `woocommerce_woocommerce_payments_settings` and duplicates in individual gateway settings. + */ + foreach ( $enabled_payment_methods as $payment_method_id ) { + $payment_gateway = WC_Payments::get_payment_gateway_by_id( $payment_method_id ); + if ( $payment_gateway ) { + $payment_gateway->enable(); + $payment_gateway->update_option( 'upe_enabled_payment_method_ids', $enabled_payment_methods ); + } + } + + // If WooPay is enabled, update the gateway option. + if ( ! empty( $capabilities['woopay'] ) ) { + $gateway->update_is_woopay_enabled( true ); + } + + // If Apple Pay and Google Pay are disabled update the gateway option, + // otherwise they are enabled by default. + if ( empty( $capabilities['apple_google'] ) ) { + $gateway->update_option( 'payment_request', 'no' ); + } + } + + /** + * Excludes placeholder payment methods and removes duplicates. + * + * WooPay and Apple Pay & Google Pay are considered placeholder payment methods and are excluded. + * + * @param array $payment_methods Array of payment methods to process. + * + * @return array Filtered array of unique payment methods. + */ + private function exclude_placeholder_payment_methods( array $payment_methods ): array { + // Placeholder payment methods. + $excluded_methods = [ 'woopay', 'apple_google' ]; + + return array_filter( + array_unique( + array_keys( array_filter( $payment_methods ) ) + ), + function ( $payment_method ) use ( $excluded_methods ) { + return ! in_array( $payment_method, $excluded_methods, true ); + } + ); + } } From 23516e938e9e7e0ddc879fa0543de28992c69160 Mon Sep 17 00:00:00 2001 From: Francesco Date: Sun, 22 Dec 2024 08:00:15 +0100 Subject: [PATCH 36/38] fix: tokenized ECE do not show button when missing billing email (#10019) --- ...ed-ece-pay-for-order-without-billing-email | 5 +++ ...ayments-express-checkout-button-helper.php | 34 ++++++++++++++++++- ...ayments-express-checkout-button-helper.php | 26 +++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 changelog/fix-tokenized-ece-pay-for-order-without-billing-email diff --git a/changelog/fix-tokenized-ece-pay-for-order-without-billing-email b/changelog/fix-tokenized-ece-pay-for-order-without-billing-email new file mode 100644 index 00000000000..6e34989b976 --- /dev/null +++ b/changelog/fix-tokenized-ece-pay-for-order-without-billing-email @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized ECE do not show button when missing billing email + + diff --git a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php index 86d1a82c54d..0613e3a4557 100644 --- a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php +++ b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php @@ -412,7 +412,7 @@ public function should_show_express_checkout_button() { // Order total doesn't matter for Pay for Order page. Thus, this page should always display payment buttons. if ( $this->is_pay_for_order_page() ) { - return true; + return $this->is_pay_for_order_supported(); } // Non-shipping product and tax is calculated based on shopper billing address. Excludes Pay for Order page. @@ -749,6 +749,38 @@ public function get_product_data() { return apply_filters( 'wcpay_payment_request_product_data', $data, $product ); } + /** + * The Store API doesn't allow checkout without the billing email address present on the order data. + * https://github.com/woocommerce/woocommerce/issues/48540 + * + * @return bool + */ + private function is_pay_for_order_supported() { + if ( ! WC_Payments_Features::is_tokenized_cart_ece_enabled() ) { + return true; + } + + $order_id = absint( get_query_var( 'order-pay' ) ); + if ( 0 === $order_id ) { + return false; + } + + $order = wc_get_order( $order_id ); + if ( ! is_a( $order, 'WC_Order' ) ) { + return false; + } + + // we don't need to check its validity or value, we just need to ensure a billing email is present. + $billing_email = $order->get_billing_email(); + if ( ! empty( $billing_email ) ) { + return true; + } + + Logger::log( 'Billing email not present ( Express Checkout Element button disabled )' ); + + return false; + } + /** * Whether product page has a supported product. * diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php index 8006faac78f..9c7ebbef971 100644 --- a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php +++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php @@ -104,6 +104,7 @@ public function tear_down() { remove_filter( 'wc_tax_enabled', '__return_false' ); remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_excl' ] ); remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); + delete_option( '_wcpay_feature_tokenized_cart_ece' ); parent::tear_down(); } @@ -208,6 +209,30 @@ function () { remove_all_filters( 'wcpay_payment_request_total_label_suffix' ); } + public function test_should_show_express_checkout_button_for_tokenized_ece_with_billing_email() { + global $wp; + global $wp_query; + + $this->mock_wcpay_account + ->method( 'is_stripe_connected' ) + ->willReturn( true ); + WC_Payments::mode()->dev(); + $_GET['pay_for_order'] = true; + + // Total is 100 USD, which is above both payment methods (Affirm and AfterPay) minimums. + $order = WC_Helper_Order::create_order( 1, 100 ); + $order_id = $order->get_id(); + $wp->query_vars = [ 'order-pay' => strval( $order_id ) ]; + $wp_query->query_vars = [ 'order-pay' => strval( $order_id ) ]; + + update_option( '_wcpay_feature_tokenized_cart_ece', '1' ); + add_filter( 'woocommerce_is_checkout', '__return_true' ); + + $this->assertTrue( $this->system_under_test->should_show_express_checkout_button() ); + + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + } + public function test_should_show_express_checkout_button_for_non_shipping_but_price_includes_tax() { $this->mock_wcpay_account ->method( 'is_stripe_connected' ) @@ -229,7 +254,6 @@ public function test_should_show_express_checkout_button_for_non_shipping_but_pr remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); } - public function test_should_not_show_express_checkout_button_for_non_shipping_but_price_does_not_include_tax() { $this->mock_wcpay_account ->method( 'is_stripe_connected' ) From 49b6961fd123f3156115dbbbdff51f6307d5c2db Mon Sep 17 00:00:00 2001 From: Eric Jinks <3147296+Jinksi@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:10:32 +1000 Subject: [PATCH 37/38] Add TS type assertion for disputes CSV export API response to fix type safety (#10008) Co-authored-by: Shendy <73803630+shendy-a8c@users.noreply.github.com> --- ...isputes-csv-export-response-type-assertion | 4 +++ client/disputes/index.tsx | 35 ++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 changelog/fix-10007-disputes-csv-export-response-type-assertion diff --git a/changelog/fix-10007-disputes-csv-export-response-type-assertion b/changelog/fix-10007-disputes-csv-export-response-type-assertion new file mode 100644 index 00000000000..b578456ece2 --- /dev/null +++ b/changelog/fix-10007-disputes-csv-export-response-type-assertion @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Add type assertion for disputes CSV export response to ensure type safety and fix TypeScript error diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 6e8c4d2d61b..ad5e8258d7f 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -397,22 +397,25 @@ export const DisputesList = (): JSX.Element => { window.confirm( confirmMessage ) ) { try { - const { exported_disputes: exportedDisputes } = await apiFetch( - { - path: getDisputesCSV( { - userEmail, - locale, - dateAfter, - dateBefore, - dateBetween, - match, - filter, - statusIs, - statusIsNot, - } ), - method: 'POST', - } - ); + const { + exported_disputes: exportedDisputes, + } = await apiFetch< { + /** The total number of disputes that will be exported in the CSV. */ + exported_disputes: number; + } >( { + path: getDisputesCSV( { + userEmail, + locale, + dateAfter, + dateBefore, + dateBetween, + match, + filter, + statusIs, + statusIsNot, + } ), + method: 'POST', + } ); createNotice( 'success', From 96372477c893eeb6892c268b3893e944ec0aac9e Mon Sep 17 00:00:00 2001 From: Eric Jinks <3147296+Jinksi@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:15:00 +1000 Subject: [PATCH 38/38] =?UTF-8?q?Upgrade=20`@woocommerce/csv-export`=20to?= =?UTF-8?q?=20v1.10.0=20=E2=80=93=20fixes=20unnecessary=20escaping=20of=20?= =?UTF-8?q?negative=20values=20in=20CSV=20exports=20(#10028)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...303-upgrade-woocommerce-csv-export-package | 4 + package-lock.json | 83 ++++++++++--------- package.json | 4 +- 3 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 changelog/fix-9303-upgrade-woocommerce-csv-export-package diff --git a/changelog/fix-9303-upgrade-woocommerce-csv-export-package b/changelog/fix-9303-upgrade-woocommerce-csv-export-package new file mode 100644 index 00000000000..db23bab80b2 --- /dev/null +++ b/changelog/fix-9303-upgrade-woocommerce-csv-export-package @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Upgrade `@woocommerce/csv-export` package to v1.10.0 – fixes unnecessary escaping of negative values in CSV exports that was preventing numerical analysis in spreadsheet applications diff --git a/package-lock.json b/package-lock.json index 083ed1adf12..1ddaf7a4ded 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "@typescript-eslint/parser": "4.15.2", "@woocommerce/api": "0.2.0", "@woocommerce/components": "12.3.0", - "@woocommerce/csv-export": "1.9.0", + "@woocommerce/csv-export": "1.10.0", "@woocommerce/currency": "4.3.0", "@woocommerce/date": "4.2.0", "@woocommerce/dependency-extraction-webpack-plugin": "2.2.0", @@ -10969,6 +10969,11 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@woocommerce/components/node_modules/@types/node": { + "version": "16.18.122", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.122.tgz", + "integrity": "sha512-rF6rUBS80n4oK16EW8nE75U+9fw0SSUgoPtWSvHhPXdT7itbvmS7UjB/jyM8i3AkvI6yeSM5qCwo+xN0npGDHg==" + }, "node_modules/@woocommerce/components/node_modules/@types/react": { "version": "17.0.80", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz", @@ -10989,6 +10994,19 @@ "@types/wordpress__rich-text": "*" } }, + "node_modules/@woocommerce/components/node_modules/@woocommerce/csv-export": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@woocommerce/csv-export/-/csv-export-1.9.0.tgz", + "integrity": "sha512-fo1byPYTljic8ml5HxvxafdhrYc8pMYXqjlGZtZ9xNxWL2yffXJvskbcb7Kj6JSQhHT3BCh/5PlczJci9vQuDQ==", + "dependencies": { + "@types/node": "^16.18.68", + "browser-filesaver": "^1.1.1" + }, + "engines": { + "node": "^20.11.1", + "pnpm": "^9.1.0" + } + }, "node_modules/@woocommerce/components/node_modules/@wordpress/api-fetch": { "version": "6.55.0", "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.55.0.tgz", @@ -11652,23 +11670,19 @@ } }, "node_modules/@woocommerce/csv-export": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@woocommerce/csv-export/-/csv-export-1.9.0.tgz", - "integrity": "sha512-fo1byPYTljic8ml5HxvxafdhrYc8pMYXqjlGZtZ9xNxWL2yffXJvskbcb7Kj6JSQhHT3BCh/5PlczJci9vQuDQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@woocommerce/csv-export/-/csv-export-1.10.0.tgz", + "integrity": "sha512-OckWCp52fGFcyK2lwBJyiheBbvUFl0dO3Ai6tSfX+jApJa8SUlx9txhwKVSz4Xnpo+nH0B0SPHjoPtQeQeXa2w==", + "dev": true, "dependencies": { - "@types/node": "^16.18.68", + "@types/node": "20.x.x", "browser-filesaver": "^1.1.1" }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" } }, - "node_modules/@woocommerce/csv-export/node_modules/@types/node": { - "version": "16.18.102", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.102.tgz", - "integrity": "sha512-eSe2YwGCcRjqPidxfm20IAq02krERWcIIJW4FNPkU0zQLbc4L9pvhsmB0p6UJecjEf0j/E2ERHsKq7madvthKw==" - }, "node_modules/@woocommerce/currency": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@woocommerce/currency/-/currency-4.3.0.tgz", @@ -13775,6 +13789,24 @@ "pnpm": "^8.12.1" } }, + "node_modules/@woocommerce/onboarding/node_modules/@types/node": { + "version": "16.18.122", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.122.tgz", + "integrity": "sha512-rF6rUBS80n4oK16EW8nE75U+9fw0SSUgoPtWSvHhPXdT7itbvmS7UjB/jyM8i3AkvI6yeSM5qCwo+xN0npGDHg==" + }, + "node_modules/@woocommerce/onboarding/node_modules/@woocommerce/csv-export": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@woocommerce/csv-export/-/csv-export-1.9.0.tgz", + "integrity": "sha512-fo1byPYTljic8ml5HxvxafdhrYc8pMYXqjlGZtZ9xNxWL2yffXJvskbcb7Kj6JSQhHT3BCh/5PlczJci9vQuDQ==", + "dependencies": { + "@types/node": "^16.18.68", + "browser-filesaver": "^1.1.1" + }, + "engines": { + "node": "^20.11.1", + "pnpm": "^9.1.0" + } + }, "node_modules/@woocommerce/onboarding/node_modules/@woocommerce/experimental": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@woocommerce/experimental/-/experimental-3.2.0.tgz", @@ -20443,23 +20475,6 @@ "node": ">= 10.13.0" } }, - "node_modules/@wordpress/scripts/node_modules/rechoir/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/@wordpress/scripts/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -20655,18 +20670,6 @@ "node": ">=8" } }, - "node_modules/@wordpress/scripts/node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@wordpress/scripts/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", diff --git a/package.json b/package.json index f634378e064..cf6c7f7b843 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "@typescript-eslint/parser": "4.15.2", "@woocommerce/api": "0.2.0", "@woocommerce/components": "12.3.0", - "@woocommerce/csv-export": "1.9.0", + "@woocommerce/csv-export": "1.10.0", "@woocommerce/currency": "4.3.0", "@woocommerce/date": "4.2.0", "@woocommerce/dependency-extraction-webpack-plugin": "2.2.0", @@ -215,7 +215,7 @@ "overrides": { "@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#update/babel-deps", "@woocommerce/components": { - "@woocommerce/csv-export": "1.9.0", + "@woocommerce/csv-export": "1.10.0", "@woocommerce/currency": "4.3.0" }, "@wordpress/scripts": {