From f76a0173da78565deef86ccf58f6ced29ff41927 Mon Sep 17 00:00:00 2001 From: Daniel Mallory Date: Mon, 21 Oct 2024 12:51:42 +0100 Subject: [PATCH 01/45] Add new tracking events and update Stripe connect library (#9607) --- changelog/dev-readd-tracking-event | 4 ++++ client/onboarding/steps/embedded-kyc.tsx | 12 ++++++++++++ client/onboarding/tracking.ts | 23 ++++++++++++++++++++++- client/tracks/event.d.ts | 1 + package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 6 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 changelog/dev-readd-tracking-event diff --git a/changelog/dev-readd-tracking-event b/changelog/dev-readd-tracking-event new file mode 100644 index 00000000000..475904c11b2 --- /dev/null +++ b/changelog/dev-readd-tracking-event @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Improvements to events during onboarding flow. diff --git a/client/onboarding/steps/embedded-kyc.tsx b/client/onboarding/steps/embedded-kyc.tsx index e01ed82ca90..4acf74f6d94 100644 --- a/client/onboarding/steps/embedded-kyc.tsx +++ b/client/onboarding/steps/embedded-kyc.tsx @@ -25,6 +25,10 @@ import { isPoEligible, } from 'wcpay/onboarding/utils'; import { getConnectUrl, getOverviewUrl } from 'wcpay/utils'; +import { + trackEmbeddedStepChange, + trackRedirected, +} from 'wcpay/onboarding/tracking'; interface Props { continueKyc?: boolean; @@ -57,6 +61,7 @@ const EmbeddedKyc: React.FC< Props > = ( { isEligible ); if ( accountSession && accountSession.clientSecret ) { + trackRedirected( isEligible, true ); return accountSession; // Return the full account session object } @@ -139,6 +144,10 @@ const EmbeddedKyc: React.FC< Props > = ( { locale, ] ); + const handleStepChange = ( step: string ) => { + trackEmbeddedStepChange( step ); + }; + const handleOnExit = async () => { const urlParams = new URLSearchParams( window.location.search ); const urlSource = @@ -192,6 +201,9 @@ const EmbeddedKyc: React.FC< Props > = ( { ) } onExit={ handleOnExit } + onStepChange={ ( stepChange ) => + handleStepChange( stepChange.step ) + } collectionOptions={ { fields: collectPayoutRequirements ? 'eventually_due' diff --git a/client/onboarding/tracking.ts b/client/onboarding/tracking.ts index 1250e4c5a16..a370a1449b2 100644 --- a/client/onboarding/tracking.ts +++ b/client/onboarding/tracking.ts @@ -46,11 +46,32 @@ export const trackStepCompleted = ( step: string ): void => { trackedSteps.add( step ); }; -export const trackRedirected = ( isPoEligible: boolean ): void => { +export const trackRedirected = ( + isPoEligible: boolean, + isEmbedded = false +): void => { const urlParams = new URLSearchParams( window.location.search ); recordEvent( 'wcpay_onboarding_flow_redirected', { is_po_eligible: isPoEligible, + is_embedded_onboarding: isEmbedded, + elapsed: elapsed( startTime ), + source: + urlParams.get( 'source' )?.replace( /[^\w-]+/g, '' ) || 'unknown', + } ); +}; + +/** + * Track a change in the embedded onboarding step. + * + * @param step The current step in the embedded onboarding flow. See: + * https://docs.stripe.com/connect/supported-embedded-components/account-onboarding#step-values + */ +export const trackEmbeddedStepChange = ( step: string ): void => { + const urlParams = new URLSearchParams( window.location.search ); + + recordEvent( 'wcpay_onboarding_flow_embedded_step_change', { + step: step, elapsed: elapsed( startTime ), source: urlParams.get( 'source' )?.replace( /[^\w-]+/g, '' ) || 'unknown', diff --git a/client/tracks/event.d.ts b/client/tracks/event.d.ts index 170038e4bcc..b445a55e65a 100644 --- a/client/tracks/event.d.ts +++ b/client/tracks/event.d.ts @@ -60,6 +60,7 @@ export type Event = | 'wcpay_onboarding_flow_reset' | 'wcpay_onboarding_flow_eligibility_modal_closed' | 'wcpay_onboarding_flow_setup_live_payments' + | 'wcpay_onboarding_flow_embedded_step_change' | 'wcpay_overview_balances_currency_tab_click' | 'wcpay_overview_deposits_view_history_click' | 'wcpay_overview_deposits_change_schedule_click' diff --git a/package-lock.json b/package-lock.json index 56cfed2cfa8..0fa3c1bd240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "dependencies": { "@automattic/interpolate-components": "1.2.1", "@fingerprintjs/fingerprintjs": "3.4.1", - "@stripe/connect-js": "3.3.12", - "@stripe/react-connect-js": "3.3.13", + "@stripe/connect-js": "3.3.16", + "@stripe/react-connect-js": "3.3.17", "@stripe/react-stripe-js": "2.5.1", "@stripe/stripe-js": "1.15.1", "@woocommerce/explat": "2.3.0", @@ -9158,16 +9158,16 @@ } }, "node_modules/@stripe/connect-js": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/@stripe/connect-js/-/connect-js-3.3.12.tgz", - "integrity": "sha512-hXbgvGq9Lb6BYgsb8lcbjL76Yqsxr0yAj6T9ZFTfUK0O4otI5GSEWum9do9rf/E5OfYy6fR1FG/77Jve2w1o6Q==" + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/@stripe/connect-js/-/connect-js-3.3.16.tgz", + "integrity": "sha512-lMUKJJaDl6qzjp+czNn+N6wMwFXwLawmB2jNNgds8SeR+bXCVCXevzJ8dfF92KfmexKg++hBYagF9e99sEMBJQ==" }, "node_modules/@stripe/react-connect-js": { - "version": "3.3.13", - "resolved": "https://registry.npmjs.org/@stripe/react-connect-js/-/react-connect-js-3.3.13.tgz", - "integrity": "sha512-kMxYjeQUcl/ixu/mSeX5QGIr/MuP+YxFSEBdb8j6w+tbK82tmcjyFDgoQTQwVXNqUV6jI66Kks3XcfpPRfeiJA==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/@stripe/react-connect-js/-/react-connect-js-3.3.17.tgz", + "integrity": "sha512-uxCh6ACcSWS/t0kBeqvvRieBI9pRxh2rPxt6NpjrTg3Ft1ZDleUfg9OAjkfoOT3Ta+FTomouA19l2ju7If2h5A==", "peerDependencies": { - "@stripe/connect-js": ">=3.3.11", + "@stripe/connect-js": ">=3.3.16", "react": ">=16.8.0", "react-dom": ">=16.8.0" } diff --git a/package.json b/package.json index 629b9ceee87..70bb5da54ae 100644 --- a/package.json +++ b/package.json @@ -77,8 +77,8 @@ "dependencies": { "@automattic/interpolate-components": "1.2.1", "@fingerprintjs/fingerprintjs": "3.4.1", - "@stripe/connect-js": "3.3.12", - "@stripe/react-connect-js": "3.3.13", + "@stripe/connect-js": "3.3.16", + "@stripe/react-connect-js": "3.3.17", "@stripe/react-stripe-js": "2.5.1", "@stripe/stripe-js": "1.15.1", "@woocommerce/explat": "2.3.0", From 51b94bee5900b8e2a71a221a290262583d2649ef Mon Sep 17 00:00:00 2001 From: Eric Jinks <3147296+Jinksi@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:06:46 +1000 Subject: [PATCH 02/45] Remove unnecessary WC table css overrides to fix WC Analytics styling and a11y issues (#9603) --- changelog/fix-9572-woococommerce-table-specificity | 4 ++++ client/style.scss | 13 ++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 changelog/fix-9572-woococommerce-table-specificity diff --git a/changelog/fix-9572-woococommerce-table-specificity b/changelog/fix-9572-woococommerce-table-specificity new file mode 100644 index 00000000000..de47974865b --- /dev/null +++ b/changelog/fix-9572-woococommerce-table-specificity @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Remove unnecessary '.woocommerce-table' css overrides to fix WC Analytics styling and a11y issues diff --git a/client/style.scss b/client/style.scss index 7fe3269109f..2e4b8312271 100644 --- a/client/style.scss +++ b/client/style.scss @@ -20,15 +20,10 @@ } } -.woocommerce-table { - font-weight: 300; - letter-spacing: 0.012rem; - color: $studio-gray-60; - - .woocommerce-table__summary { - border-radius: 0 0 3px 3px; - } - +/** +* This styling is for all `` components found in WooPayments screens. +*/ +.woocommerce-payments-page .woocommerce-table { .woocommerce-table__item { white-space: nowrap; } From 1652cc9092b3fa7fe77111b28c994cc9d96ef29d Mon Sep 17 00:00:00 2001 From: Shendy <73803630+shendy-a8c@users.noreply.github.com> Date: Tue, 22 Oct 2024 17:58:24 +0700 Subject: [PATCH 03/45] npm script to update e2e-pw screenshots / snapshots (#9544) --- changelog/update-npm-readme-update-e2e-pw-snapshots | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) create mode 100644 changelog/update-npm-readme-update-e2e-pw-snapshots diff --git a/changelog/update-npm-readme-update-e2e-pw-snapshots b/changelog/update-npm-readme-update-e2e-pw-snapshots new file mode 100644 index 00000000000..3927f45ec0d --- /dev/null +++ b/changelog/update-npm-readme-update-e2e-pw-snapshots @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Not user-facing. + + diff --git a/package.json b/package.json index 70bb5da54ae..9d207be5e40 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "test:e2e-dev": "NODE_CONFIG_DIR='tests/e2e/config' JEST_PUPPETEER_CONFIG='tests/e2e/config/jest-puppeteer.config.js' wp-scripts test-e2e --config tests/e2e/config/jest.config.js --puppeteer-interactive", "test:e2e-performance": "NODE_CONFIG_DIR='tests/e2e/config' wp-scripts test-e2e --config tests/e2e/config/jest.performance.config.js", "test:e2e-pw": "./tests/e2e-pw/test-e2e-pw.sh", + "test:e2e-pw-update-snapshots": "npm run test:e2e-pw -- --update-snapshots", "test:e2e-pw-ui": "./tests/e2e-pw/test-e2e-pw-ui.sh", "test:e2e-pw-ci": "npx playwright test --config=tests/e2e-pw/playwright.config.ts --grep-invert @todo", "test:update-snapshots": "npm run test:js -- --updateSnapshot", From 439c151c278d2638f780953c4e88a3c5edae84e8 Mon Sep 17 00:00:00 2001 From: Alefe Souza Date: Tue, 22 Oct 2024 08:33:25 -0500 Subject: [PATCH 04/45] Fix WooPay duplicated Save my info section (#9531) Co-authored-by: Danae Millan --- changelog/fix-woopay-duplicated-component | 4 +++ client/checkout/woopay/index.js | 43 +++++++++++++---------- 2 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 changelog/fix-woopay-duplicated-component diff --git a/changelog/fix-woopay-duplicated-component b/changelog/fix-woopay-duplicated-component new file mode 100644 index 00000000000..a5735f338c3 --- /dev/null +++ b/changelog/fix-woopay-duplicated-component @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix WooPay duplicated Save my info section. diff --git a/client/checkout/woopay/index.js b/client/checkout/woopay/index.js index cdc2ca864f3..f6c8902a514 100644 --- a/client/checkout/woopay/index.js +++ b/client/checkout/woopay/index.js @@ -23,31 +23,38 @@ const renderSaveUserSection = () => { ); if ( blocksCheckout.length ) { - const checkoutPageSaveUserContainer = document.createElement( - 'fieldset' + let checkoutPageSaveUserContainer = document.querySelector( + '#remember-me' ); - const paymentOptions = document.getElementsByClassName( - 'wp-block-woocommerce-checkout-payment-block' - )?.[ 0 ]; - checkoutPageSaveUserContainer.className = - 'wc-block-checkout__payment-method wp-block-woocommerce-checkout-remember-block wc-block-components-checkout-step '; - checkoutPageSaveUserContainer.id = 'remember-me'; + if ( ! checkoutPageSaveUserContainer ) { + const paymentOptions = document.getElementsByClassName( + 'wp-block-woocommerce-checkout-payment-block' + )?.[ 0 ]; - if ( paymentOptions ) { - // Render right after the payment options block, as a sibling element. - paymentOptions.parentNode.insertBefore( - checkoutPageSaveUserContainer, - paymentOptions.nextSibling + checkoutPageSaveUserContainer = document.createElement( + 'fieldset' ); - paymentOptions.classList.add( 'is-woopay' ); + checkoutPageSaveUserContainer.className = + 'wc-block-checkout__payment-method wp-block-woocommerce-checkout-remember-block wc-block-components-checkout-step '; + checkoutPageSaveUserContainer.id = 'remember-me'; - ReactDOM.render( - , - checkoutPageSaveUserContainer - ); + if ( paymentOptions ) { + // Render right after the payment options block, as a sibling element. + paymentOptions.parentNode.insertBefore( + checkoutPageSaveUserContainer, + paymentOptions.nextSibling + ); + + paymentOptions.classList.add( 'is-woopay' ); + } } + + ReactDOM.render( + , + checkoutPageSaveUserContainer + ); } else { const checkoutPageSaveUserContainer = document.createElement( 'div' ); checkoutPageSaveUserContainer.className = From 6611bb1ffae5f4e78b8843861c0b240a7d7caa4e Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Tue, 22 Oct 2024 11:24:07 -0300 Subject: [PATCH 05/45] Add risk level information to the Fraud & Risk meta box (#9515) --- assets/css/admin.css | 84 +++++++- .../add-1225-flag-elevated-risk-transactions | 4 + .../class-wc-payments-admin-settings.php | 6 +- includes/class-wc-payments-order-service.php | 46 +++- .../class-order-fraud-and-risk-meta-box.php | 67 +++++- ...st-class-order-fraud-and-risk-meta-box.php | 204 ++++++++++++++++-- 6 files changed, 384 insertions(+), 27 deletions(-) create mode 100644 changelog/add-1225-flag-elevated-risk-transactions diff --git a/assets/css/admin.css b/assets/css/admin.css index baf66dcee88..7b6d75945de 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -155,11 +155,93 @@ padding-top: 2px; } +/* WCPay Fraud Risk Level meta box */ +.wcpay-fraud-risk-level { + border-bottom: 1px solid #ddd; + padding: 8px 12px; +} + +.wcpay-fraud-risk-level > p { + margin: 0; +} + +.wcpay-fraud-risk-level__title { + font-weight: 600; +} + +.wcpay-fraud-risk-level__bar { + display: grid; + gap: 4px; + grid-template-columns: 50% auto; + margin: 6px 0 8px; +} + +.wcpay-fraud-risk-level__bar::after, +.wcpay-fraud-risk-level__bar::before { + background-color: #bbb; + content: ''; + border-radius: 4px; + height: 4px; +} + +.wcpay-fraud-risk-level--normal .wcpay-fraud-risk-level__title { + color: #008a20; +} + +.wcpay-fraud-risk-level--normal .wcpay-fraud-risk-level__bar { + grid-template-columns: 15% auto; +} + +.wcpay-fraud-risk-level--normal .wcpay-fraud-risk-level__bar::before { + background-color: #008a20; +} + +.wcpay-fraud-risk-level--elevated .wcpay-fraud-risk-level__title { + color: #b16202; +} + +.wcpay-fraud-risk-level--elevated .wcpay-fraud-risk-level__bar { + grid-template-columns: 60% auto; +} + +.wcpay-fraud-risk-level--elevated .wcpay-fraud-risk-level__bar::before { + background-color: #b16202; +} + +.wcpay-fraud-risk-level--highest .wcpay-fraud-risk-level__bar::before { + background-color: #b32d2e; +} + +.wcpay-fraud-risk-level--highest .wcpay-fraud-risk-level__bar { + grid-template-columns: 100% auto; +} + +.wcpay-fraud-risk-level--highest .wcpay-fraud-risk-level__bar::before { + background-color: #b32d2e; +} + +/* WCPay Fraud Risk Action meta box */ +#wcpay-order-fraud-and-risk-meta-box div.inside { + margin-top: 0; + padding: 0; +} + +.wcpay-fraud-risk-action { + padding: 8px 12px 12px; +} + +.wcpay-fraud-risk-action > p { + margin: 0 0 6px; +} + +.wcpay-fraud-risk-action > p:last-child { + margin-bottom: 0; +} + .wcpay-fraud-risk-meta-allow, .wcpay-fraud-risk-meta-review, .wcpay-fraud-risk-meta-blocked { font-weight: 600; - font-size: 14px; } .wcpay-fraud-risk-meta-allow img { diff --git a/changelog/add-1225-flag-elevated-risk-transactions b/changelog/add-1225-flag-elevated-risk-transactions new file mode 100644 index 00000000000..aaf4e54a8b6 --- /dev/null +++ b/changelog/add-1225-flag-elevated-risk-transactions @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add risk level information to the fraud and risk box on the order details page. diff --git a/includes/admin/class-wc-payments-admin-settings.php b/includes/admin/class-wc-payments-admin-settings.php index cec7ca48b17..ea4105406f0 100644 --- a/includes/admin/class-wc-payments-admin-settings.php +++ b/includes/admin/class-wc-payments-admin-settings.php @@ -96,9 +96,11 @@ public static function is_current_page_settings() { /** * Returns the URL of the configuration screen for this gateway, for use in internal links. * + * @param array $query_args Optional additional query args to append to the URL. + * * @return string URL of the configuration screen for this gateway */ - public static function get_settings_url() { - return admin_url( add_query_arg( self::$settings_url_params, 'admin.php' ) ); // nosemgrep: audit.php.wp.security.xss.query-arg -- constant string is passed in. + public static function get_settings_url( $query_args = [] ) { + return admin_url( add_query_arg( array_merge( self::$settings_url_params, $query_args ), 'admin.php' ) ); // nosemgrep: audit.php.wp.security.xss.query-arg -- constant string is passed in. } } diff --git a/includes/class-wc-payments-order-service.php b/includes/class-wc-payments-order-service.php index ce6fa128987..246dbb13e34 100644 --- a/includes/class-wc-payments-order-service.php +++ b/includes/class-wc-payments-order-service.php @@ -51,6 +51,13 @@ class WC_Payments_Order_Service { */ const INTENTION_STATUS_META_KEY = '_intention_status'; + /** + * Meta key used to store the charge risk level. + * + * @const string + */ + const CHARGE_RISK_LEVEL_META_KEY = '_charge_risk_level'; + /** * Meta key used to store customer Id. * @@ -566,6 +573,37 @@ public function set_payment_transaction_id_for_order( $order, $payment_transacti $order->save_meta_data(); } + /** + * Set the payment metadata for risk level. + * + * @param mixed $order The order. + * @param string $risk_level The value to be set. + * + * @throws Order_Not_Found_Exception + */ + public function set_charge_risk_level_for_order( $order, $risk_level ) { + if ( ! isset( $risk_level ) || null === $risk_level ) { + return; + } + $order = $this->get_order( $order ); + $order->update_meta_data( self::CHARGE_RISK_LEVEL_META_KEY, $risk_level ); + $order->save_meta_data(); + } + + /** + * Get the risk level for an order. + * + * @param mixed $order The order Id or order object. + * + * @return string + * + * @throws Order_Not_Found_Exception + */ + public function get_charge_risk_level_for_order( $order ): string { + $order = $this->get_order( $order ); + return $order->get_meta( self::CHARGE_RISK_LEVEL_META_KEY, true ); + } + /** * Get the payment metadata for charge id. * @@ -828,8 +866,10 @@ public function attach_intent_info_to_order( WC_Order $order, $intent ) { $charge_id = $charge ? $charge->get_id() : null; $payment_transaction = $charge ? $charge->get_balance_transaction() : null; $payment_transaction_id = $payment_transaction['id'] ?? ''; + $outcome = $charge ? $charge->get_outcome() : null; + $risk_level = $outcome ? $outcome['risk_level'] : null; // next, save it in order meta. - $this->attach_intent_info_to_order__legacy( $order, $intent_id, $intent_status, $payment_method, $customer_id, $charge_id, $currency, $payment_transaction_id ); + $this->attach_intent_info_to_order__legacy( $order, $intent_id, $intent_status, $payment_method, $customer_id, $charge_id, $currency, $payment_transaction_id, $risk_level ); } /** @@ -845,10 +885,11 @@ public function attach_intent_info_to_order( WC_Order $order, $intent ) { * @param string $charge_id Charge ID. * @param string $currency Currency code. * @param string $payment_transaction_id The transaction ID of the linked charge. + * @param string $risk_level The risk level of the payment. * * @throws Order_Not_Found_Exception */ - public function attach_intent_info_to_order__legacy( $order, $intent_id, $intent_status, $payment_method, $customer_id, $charge_id, $currency, $payment_transaction_id = null ) { + public function attach_intent_info_to_order__legacy( $order, $intent_id, $intent_status, $payment_method, $customer_id, $charge_id, $currency, $payment_transaction_id = null, $risk_level = null ) { // first, let's save all the metadata that needed for refunds, required for status change etc. $order->set_transaction_id( $intent_id ); $this->set_intent_id_for_order( $order, $intent_id ); @@ -858,6 +899,7 @@ public function attach_intent_info_to_order__legacy( $order, $intent_id, $intent $this->set_customer_id_for_order( $order, $customer_id ); $this->set_wcpay_intent_currency_for_order( $order, $currency ); $this->set_payment_transaction_id_for_order( $order, $payment_transaction_id ); + $this->set_charge_risk_level_for_order( $order, $risk_level ); $order->save(); } diff --git a/includes/fraud-prevention/class-order-fraud-and-risk-meta-box.php b/includes/fraud-prevention/class-order-fraud-and-risk-meta-box.php index 5b498ec90c8..70202beb213 100644 --- a/includes/fraud-prevention/class-order-fraud-and-risk-meta-box.php +++ b/includes/fraud-prevention/class-order-fraud-and-risk-meta-box.php @@ -7,7 +7,7 @@ namespace WCPay\Fraud_Prevention; -use WC_Payments_Features; +use WC_Payments_Admin_Settings; use WC_Payments_Order_Service; use WC_Payments_Utils; use WCPay\Constants\Fraud_Meta_Box_Type; @@ -54,7 +54,7 @@ public function maybe_add_meta_box() { // Get the order edit screen to be able to add the meta box to. $wc_screen_id = \wc_get_page_screen_id( 'shop-order' ); - add_meta_box( 'wcpay_order_fraud_and_risk_meta_box', __( 'Fraud & Risk', 'woocommerce-payments' ), [ $this, 'display_order_fraud_and_risk_meta_box_message' ], $wc_screen_id, 'side', 'default' ); + add_meta_box( 'wcpay-order-fraud-and-risk-meta-box', __( 'Fraud & Risk', 'woocommerce-payments' ), [ $this, 'display_order_fraud_and_risk_meta_box_message' ], $wc_screen_id, 'side', 'default' ); } /** @@ -74,6 +74,7 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { $intent_id = $this->order_service->get_intent_id_for_order( $order ); $charge_id = $this->order_service->get_charge_id_for_order( $order ); $meta_box_type = $this->order_service->get_fraud_meta_box_type_for_order( $order ); + $risk_level = $this->order_service->get_charge_risk_level_for_order( $order ); $payment_method = $order->get_payment_method(); if ( strstr( $payment_method, 'woocommerce_payments_' ) ) { @@ -104,6 +105,14 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { 'no_action_taken' => __( 'No action taken', 'woocommerce-payments' ), ]; + $risk_filters_callout = __( 'Adjust risk filters', 'woocommerce-payments' ); + $risk_filters_url = WC_Payments_Admin_Settings::get_settings_url( [ 'anchor' => '%23fp-settings' ] ); + $show_adjust_risk_filters_link = true; + + $this->maybe_print_risk_level_block( $risk_level ); + + echo '
'; + switch ( $meta_box_type ) { case Fraud_Meta_Box_Type::ALLOW: $description = __( 'The payment for this order passed your risk filtering.', 'woocommerce-payments' ); @@ -114,12 +123,13 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { $description = __( 'The payment for this order was blocked by your risk filtering. There is no pending authorization, and the order can be cancelled to reduce any held stock.', 'woocommerce-payments' ); $callout = __( 'View more details', 'woocommerce-payments' ); $transaction_url = $this->compose_transaction_url_with_tracking( $order->get_id(), '', Rule::FRAUD_OUTCOME_BLOCK ); - echo '

' . esc_html( $icons['red_shield']['alt'] ) . ' ' . esc_html( $statuses['blocked'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $icons['red_shield']['alt'] ) . ' ' . esc_html( $statuses['blocked'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::NOT_CARD: case Fraud_Meta_Box_Type::NOT_WCPAY: - $payment_method_title = $order->get_payment_method_title(); + $payment_method_title = $order->get_payment_method_title(); + $show_adjust_risk_filters_link = false; if ( ! empty( $payment_method_title ) && 'Popular payment methods' !== $payment_method_title ) { $description = sprintf( @@ -139,7 +149,7 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { $callout = __( 'Learn more', 'woocommerce-payments' ); $callout_url = 'https://woocommerce.com/document/woopayments/fraud-and-disputes/fraud-protection/'; $callout_url = add_query_arg( 'status_is', 'fraud-meta-box-not-wcpay-learn-more', $callout_url ); - echo '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::PAYMENT_STARTED: @@ -151,7 +161,7 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { $description = __( 'The payment for this order was held for review by your risk filtering. You can review the details and determine whether to approve or block the payment.', 'woocommerce-payments' ); $callout = __( 'Review payment', 'woocommerce-payments' ); $transaction_url = $this->compose_transaction_url_with_tracking( $intent_id, $charge_id, Rule::FRAUD_OUTCOME_REVIEW ); - echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::REVIEW_ALLOWED: @@ -163,21 +173,21 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { $description = __( 'This transaction was held for review by your risk filters, and the charge was manually blocked after review.', 'woocommerce-payments' ); $callout = __( 'Review payment', 'woocommerce-payments' ); $transaction_url = WC_Payments_Utils::compose_transaction_url( $intent_id, $charge_id ); - echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::REVIEW_EXPIRED: $description = __( 'The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have expired.', 'woocommerce-payments' ); $callout = __( 'Review payment', 'woocommerce-payments' ); $transaction_url = WC_Payments_Utils::compose_transaction_url( $intent_id, $charge_id ); - echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::REVIEW_FAILED: $description = __( 'The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have failed.', 'woocommerce-payments' ); $callout = __( 'Review payment', 'woocommerce-payments' ); $transaction_url = WC_Payments_Utils::compose_transaction_url( $intent_id, $charge_id ); - echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . ''; + echo '

' . esc_html( $icons['orange_shield']['alt'] ) . ' ' . esc_html( $statuses['held_for_review'] ) . '

' . esc_html( $description ) . '

' . esc_html( $callout ) . '

'; break; case Fraud_Meta_Box_Type::TERMINAL_PAYMENT: @@ -194,6 +204,45 @@ public function display_order_fraud_and_risk_meta_box_message( $order ) { echo '

' . esc_html( $description ) . '

'; break; } + + if ( $show_adjust_risk_filters_link ) { + echo '

' . esc_html( $risk_filters_callout ) . '

'; + } + + echo '
'; + } + + /** + * Prints the risk level block. + * + * @param string $risk_level The risk level to display. + * + * @return void + */ + private function maybe_print_risk_level_block( $risk_level ) { + $valid_risk_levels = [ 'normal', 'elevated', 'highest' ]; + + if ( ! in_array( $risk_level, $valid_risk_levels, true ) ) { + return; + } + + $titles = [ + 'normal' => __( 'Normal', 'woocommerce-payments' ), + 'elevated' => __( 'Elevated', 'woocommerce-payments' ), + 'highest' => __( 'High', 'woocommerce-payments' ), + ]; + + $descriptions = [ + 'normal' => __( 'This payment shows a lower than normal risk of fraudulent activity.', 'woocommerce-payments' ), + 'elevated' => __( 'This order has a moderate risk of being fraudulent. We suggest contacting the customer to confirm their details before fulfilling it.', 'woocommerce-payments' ), + 'highest' => __( 'This order has a high risk of being fraudulent. We suggest contacting the customer to confirm their details before fulfilling it.', 'woocommerce-payments' ), + ]; + + echo '
'; + echo '

' . esc_html( $titles[ $risk_level ] ) . '

'; + echo '
'; + echo '

' . esc_html( $descriptions[ $risk_level ] ) . '

'; + echo '
'; } /** diff --git a/tests/unit/fraud-prevention/test-class-order-fraud-and-risk-meta-box.php b/tests/unit/fraud-prevention/test-class-order-fraud-and-risk-meta-box.php index dbe604c82e2..e8ea5223e13 100644 --- a/tests/unit/fraud-prevention/test-class-order-fraud-and-risk-meta-box.php +++ b/tests/unit/fraud-prevention/test-class-order-fraud-and-risk-meta-box.php @@ -12,7 +12,6 @@ * Fraud_Prevention_Service_Test unit tests. */ class Order_Fraud_And_Risk_Meta_Box_Test extends WCPAY_UnitTestCase { - /** * Test WC_Order object. * @@ -72,11 +71,16 @@ public function test_display_order_fraud_and_risk_meta_box_message_with_provider ->method( 'get_fraud_meta_box_type_for_order' ) ->willReturn( $meta_box_type ); + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( '' ); + // Act: Call the method to display the meta box. $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); // Assert: Check to make sure the expected string has been output. - $this->expectOutputString( $expected_output ); + $this->expectOutputString( $this->compose_fraud_and_risk_actions_block( $expected_output ) ); } public function display_order_fraud_and_risk_meta_box_message_provider() { @@ -91,7 +95,7 @@ public function display_order_fraud_and_risk_meta_box_message_provider() { ], 'Fraud_Meta_Box_Type_REVIEW' => [ 'meta_box_type' => Fraud_Meta_Box_Type::REVIEW, - 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. You can review the details and determine whether to approve or block the payment.

Review payment', + 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. You can review the details and determine whether to approve or block the payment.

Review payment

', ], 'Fraud_Meta_Box_Type_REVIEW_ALLOWED' => [ 'meta_box_type' => Fraud_Meta_Box_Type::REVIEW_ALLOWED, @@ -99,15 +103,15 @@ public function display_order_fraud_and_risk_meta_box_message_provider() { ], 'Fraud_Meta_Box_Type_REVIEW_BLOCKED' => [ 'meta_box_type' => Fraud_Meta_Box_Type::REVIEW_BLOCKED, - 'expected_output' => '

Orange shield outline Held for review

This transaction was held for review by your risk filters, and the charge was manually blocked after review.

Review payment', + 'expected_output' => '

Orange shield outline Held for review

This transaction was held for review by your risk filters, and the charge was manually blocked after review.

Review payment

', ], 'Fraud_Meta_Box_Type_REVIEW_EXPIRED' => [ 'meta_box_type' => Fraud_Meta_Box_Type::REVIEW_EXPIRED, - 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have expired.

Review payment', + 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have expired.

Review payment

', ], 'Fraud_Meta_Box_Type_REVIEW_FAILED' => [ 'meta_box_type' => Fraud_Meta_Box_Type::REVIEW_FAILED, - 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have failed.

Review payment', + 'expected_output' => '

Orange shield outline Held for review

The payment for this order was held for review by your risk filtering. The authorization for the charge appears to have failed.

Review payment

', ], 'Fraud_Meta_Box_Type_TERMINAL_PAYMENT' => [ 'meta_box_type' => Fraud_Meta_Box_Type::TERMINAL_PAYMENT, @@ -130,6 +134,10 @@ public function test_display_order_fraud_and_risk_meta_box_message_exits_if_no_o ->expects( $this->never() ) ->method( 'get_fraud_meta_box_type_for_order' ); + $this->mock_order_service + ->expects( $this->never() ) + ->method( 'get_charge_risk_level_for_order' ); + // Act: Call the method to display the meta box. $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( 'fake_order' ); @@ -154,11 +162,16 @@ public function test_display_order_fraud_and_risk_meta_box_message_block() { ->method( 'get_fraud_meta_box_type_for_order' ) ->willReturn( Fraud_Meta_Box_Type::BLOCK ); + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( '' ); + // Act: Call the method to display the meta box. $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); // Assert: Check to make sure the expected string has been output. - $this->expectOutputString( '

Red shield outline Blocked

The payment for this order was blocked by your risk filtering. There is no pending authorization, and the order can be cancelled to reduce any held stock.

View more details' ); + $this->expectOutputString( $this->compose_fraud_and_risk_actions_block( '

Red shield outline Blocked

The payment for this order was blocked by your risk filtering. There is no pending authorization, and the order can be cancelled to reduce any held stock.

View more details

' ) ); } /** @@ -183,6 +196,11 @@ public function test_display_order_fraud_and_risk_meta_box_message_not_card_with ->method( 'get_fraud_meta_box_type_for_order' ) ->willReturn( Fraud_Meta_Box_Type::NOT_CARD ); + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( '' ); + // Arrange: Update the order's payment method. $this->order->set_payment_method( $payment_method_id ); $this->order->set_payment_method_title( $payment_method_title ); @@ -192,7 +210,7 @@ public function test_display_order_fraud_and_risk_meta_box_message_not_card_with $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); // Assert: Check to make sure the expected string has been output. - $this->expectOutputString( $expected_output ); + $this->expectOutputString( $this->compose_fraud_and_risk_actions_block( $expected_output, false ) ); } public function display_order_fraud_and_risk_meta_box_message_not_card_provider() { @@ -200,17 +218,17 @@ public function display_order_fraud_and_risk_meta_box_message_not_card_provider( 'simulate legacy UPE Popular payment methods' => [ 'payment_method_id' => 'woocommerce_payments', 'payment_method_title' => 'Popular payment methods', - 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments.

Learn more', + 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments.

Learn more

', ], 'simulate legacy UPE Bancontact' => [ 'payment_method_id' => 'woocommerce_payments', 'payment_method_title' => 'Bancontact', - 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Bancontact.

Learn more', + 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Bancontact.

Learn more

', ], 'simulate split UPE Bancontact' => [ 'payment_method_id' => 'woocommerce_payments_bancontact', 'payment_method_title' => 'Bancontact', - 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Bancontact.

Learn more', + 'expected_output' => '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Bancontact.

Learn more

', ], ]; } @@ -232,6 +250,11 @@ public function test_display_order_fraud_and_risk_meta_box_message_not_wcpay() { ->method( 'get_fraud_meta_box_type_for_order' ) ->willReturn( '' ); + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( '' ); + // Arrange: Update the order's payment method. $this->order->set_payment_method( 'bacs' ); $this->order->save(); @@ -240,7 +263,7 @@ public function test_display_order_fraud_and_risk_meta_box_message_not_wcpay() { $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); // Assert: Check to make sure the expected string has been output. - $this->expectOutputString( '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Direct bank transfer.

Learn more' ); + $this->expectOutputString( $this->compose_fraud_and_risk_actions_block( '

Risk filtering is only available for orders processed using credit cards with WooPayments. This order was processed with Direct bank transfer.

Learn more

', false ) ); } public function test_display_order_fraud_and_risk_meta_box_message_default() { @@ -260,10 +283,165 @@ public function test_display_order_fraud_and_risk_meta_box_message_default() { ->method( 'get_fraud_meta_box_type_for_order' ) ->willReturn( '' ); + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( '' ); + + // Act: Call the method to display the meta box. + $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); + + // Assert: Check to make sure the expected string has been output. + $this->expectOutputString( $this->compose_fraud_and_risk_actions_block( '

Risk filtering through WooPayments was not found on this order, it may have been created while filtering was not enabled.

' ) ); + } + + public function test_display_risk_level_normal_in_order_fraud_and_risk_meta_box() { + // Arrange: Set the return results for the order service methods. + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_intent_id_for_order' ) + ->willReturn( 'pi_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_id_for_order' ) + ->willReturn( 'ch_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_fraud_meta_box_type_for_order' ) + ->willReturn( Fraud_Meta_Box_Type::ALLOW ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( 'normal' ); + + // Act: Call the method to display the meta box. + $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); + + // Assert: Check to make sure the expected string has been output. + $risk_level_block = $this->compose_fraud_and_risk_level_block( 'normal', 'Normal', 'This payment shows a lower than normal risk of fraudulent activity.' ); + $risk_actions_block = $this->compose_fraud_and_risk_actions_block( '

Green check mark No action taken

The payment for this order passed your risk filtering.

' ); + + $this->expectOutputString( $risk_level_block . $risk_actions_block ); + } + + public function test_display_risk_level_elevated_in_order_fraud_and_risk_meta_box() { + // Arrange: Set the return results for the order service methods. + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_intent_id_for_order' ) + ->willReturn( 'pi_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_id_for_order' ) + ->willReturn( 'ch_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_fraud_meta_box_type_for_order' ) + ->willReturn( Fraud_Meta_Box_Type::ALLOW ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( 'elevated' ); + + // Act: Call the method to display the meta box. + $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); + + // Assert: Check to make sure the expected string has been output. + $risk_level_block = $this->compose_fraud_and_risk_level_block( 'elevated', 'Elevated', 'This order has a moderate risk of being fraudulent. We suggest contacting the customer to confirm their details before fulfilling it.' ); + $risk_actions_block = $this->compose_fraud_and_risk_actions_block( '

Green check mark No action taken

The payment for this order passed your risk filtering.

' ); + + $this->expectOutputString( $risk_level_block . $risk_actions_block ); + } + + public function test_display_risk_level_highest_in_order_fraud_and_risk_meta_box() { + // Arrange: Set the return results for the order service methods. + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_intent_id_for_order' ) + ->willReturn( 'pi_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_id_for_order' ) + ->willReturn( 'ch_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_fraud_meta_box_type_for_order' ) + ->willReturn( Fraud_Meta_Box_Type::ALLOW ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( 'highest' ); + + // Act: Call the method to display the meta box. + $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); + + // Assert: Check to make sure the expected string has been output. + $risk_level_block = $this->compose_fraud_and_risk_level_block( 'highest', 'High', 'This order has a high risk of being fraudulent. We suggest contacting the customer to confirm their details before fulfilling it.' ); + $risk_actions_block = $this->compose_fraud_and_risk_actions_block( '

Green check mark No action taken

The payment for this order passed your risk filtering.

' ); + + $this->expectOutputString( $risk_level_block . $risk_actions_block ); + } + + public function test_do_not_display_risk_level_in_order_fraud_and_risk_meta_box_with_invalid_risk_level() { + // Arrange: Set the return results for the order service methods. + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_intent_id_for_order' ) + ->willReturn( 'pi_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_id_for_order' ) + ->willReturn( 'ch_mock' ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_fraud_meta_box_type_for_order' ) + ->willReturn( Fraud_Meta_Box_Type::ALLOW ); + + $this->mock_order_service + ->expects( $this->once() ) + ->method( 'get_charge_risk_level_for_order' ) + ->willReturn( 'unknown' ); + // Act: Call the method to display the meta box. $this->order_fraud_and_risk_meta_box->display_order_fraud_and_risk_meta_box_message( $this->order ); // Assert: Check to make sure the expected string has been output. - $this->expectOutputString( '

Risk filtering through WooPayments was not found on this order, it may have been created while filtering was not enabled.

' ); + $risk_actions_block = $this->compose_fraud_and_risk_actions_block( '

Green check mark No action taken

The payment for this order passed your risk filtering.

' ); + + $this->expectOutputString( $risk_actions_block ); + } + + private function compose_fraud_and_risk_level_block( $risk_level, $title, $description ) { + $output = '
'; + $output .= '

' . $title . '

'; + $output .= '
'; + $output .= '

' . $description . '

'; + $output .= '
'; + + return $output; + } + + private function compose_fraud_and_risk_actions_block( $content, $show_adjust_risk_filters_link = true ) { + $output = '
'; + $output .= $content; + + if ( $show_adjust_risk_filters_link ) { + $output .= '

Adjust risk filters

'; + } + + $output .= '
'; + + return $output; } } From 278acb7ea7ec3ee95168ae8bd53ab6114534966a Mon Sep 17 00:00:00 2001 From: Daniel Guerra <15204776+danielmx-dev@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:54:03 +0300 Subject: [PATCH 06/45] Omit Test Mode badge in the Change payment method form (#9615) --- ...mit-test-mode-badge-in-change-payment-form | 4 + client/checkout/classic/style.scss | 83 ++++++++++--------- includes/class-wc-payment-gateway-wcpay.php | 6 +- 3 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 changelog/fix-omit-test-mode-badge-in-change-payment-form diff --git a/changelog/fix-omit-test-mode-badge-in-change-payment-form b/changelog/fix-omit-test-mode-badge-in-change-payment-form new file mode 100644 index 00000000000..eff6afc6766 --- /dev/null +++ b/changelog/fix-omit-test-mode-badge-in-change-payment-form @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Omit the test mode badge in the change payment method form for subscriptions. diff --git a/client/checkout/classic/style.scss b/client/checkout/classic/style.scss index aa8df8b1676..5b452accc83 100644 --- a/client/checkout/classic/style.scss +++ b/client/checkout/classic/style.scss @@ -39,53 +39,56 @@ max-height: 24px !important; } - li.payment_method_woocommerce_payments { - display: grid; - grid-template-columns: 0fr 0fr 1fr; - grid-template-rows: max-content; - - > input[name='payment_method'] { - align-self: center; - } - > label { - grid-column: 3; + &.wc_payment_methods, + &.woocommerce-PaymentMethods { + li.payment_method_woocommerce_payments { display: grid; - grid-template-columns: 0fr auto; + grid-template-columns: 0fr 0fr 1fr; grid-template-rows: max-content; - grid-gap: 0; - margin-bottom: 0; - - > .label-title-container { - grid-area: 1 / 2 / 2 / 3; - } - .payment-method-title { - margin-right: 8px; + > input[name='payment_method'] { + align-self: center; } - - .test-mode.badge { - display: inline-block; - background-color: #fff2d7; - border-radius: 4px; - padding: 4px 6px; - font-size: 12px; - font-weight: 400; - line-height: 16px; - color: #4d3716; - vertical-align: middle; + > label { + grid-column: 3; + display: grid; + grid-template-columns: 0fr auto; + grid-template-rows: max-content; + grid-gap: 0; + margin-bottom: 0; + + > .label-title-container { + grid-area: 1 / 2 / 2 / 3; + } + + .payment-method-title { + margin-right: 8px; + } + + .test-mode.badge { + display: inline-block; + background-color: #fff2d7; + border-radius: 4px; + padding: 4px 6px; + font-size: 12px; + font-weight: 400; + line-height: 16px; + color: #4d3716; + vertical-align: middle; + } + + img { + float: none; + grid-area: 1 / 4 / 2 / 5; + align-self: baseline; + justify-self: end; + margin-left: 1em; + } } - - img { - float: none; - grid-area: 1 / 4 / 2 / 5; - align-self: baseline; - justify-self: end; - margin-left: 1em; + > div.payment_box { + grid-area: 2 / 1 / 3 / 4; } } - > div.payment_box { - grid-area: 2 / 1 / 3 / 4; - } } } diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index f795a7514b7..d1fe2b1cca6 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -580,7 +580,11 @@ public function init_hooks() { public function get_title() { $title = parent::get_title(); - if ( Payment_Method::CARD === $this->stripe_id && ( is_checkout() || is_add_payment_method_page() ) ) { + if ( + Payment_Method::CARD === $this->stripe_id && + ( is_checkout() || is_add_payment_method_page() ) && + ! isset( $_GET['change_payment_method'] ) // phpcs:ignore WordPress.Security.NonceVerification + ) { if ( WC_Payments::mode()->is_test() ) { $test_mode_badge = '' . __( 'Test Mode', 'woocommerce-payments' ) . ''; } else { From 27c562b200ecb8059eec36af4d99f0de05617c19 Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Tue, 22 Oct 2024 18:03:34 -0300 Subject: [PATCH 07/45] Fix settings rendering on the Advanced Fraud Protection page (#9521) --- ...ix-9520-advanced-fraud-protection-settings | 4 + .../allow-countries-notice.tsx | 4 +- .../cards/order-items-threshold.tsx | 61 +++++---------- .../cards/purchase-price-threshold.tsx | 52 ++++--------- .../address-mismatch.test.tsx.snap | 2 + .../international-ip-address.test.tsx.snap | 8 +- .../ip-address-mismatch.test.tsx.snap | 2 + .../order-items-threshold.test.tsx.snap | 2 + .../purchase-price-threshold.test.tsx.snap | 2 + .../cards/test/address-mismatch.test.tsx | 2 - .../cards/test/avs-mismatch.test.tsx | 4 - .../cards/test/cvc-verification.test.tsx | 4 - .../test/international-ip-address.test.tsx | 2 - .../cards/test/ip-address-mismatch.test.tsx | 2 - .../cards/test/order-items-threshold.test.tsx | 2 - .../test/purchase-price-threshold.test.tsx | 2 - .../advanced-settings/context.ts | 2 - .../advanced-settings/index.tsx | 8 +- .../advanced-settings/rule-toggle.tsx | 76 ++++++++----------- .../test/__snapshots__/index.test.tsx.snap | 22 +++--- .../__snapshots__/rule-toggle.test.tsx.snap | 4 + .../test/allow-countries-notice.test.tsx | 2 - .../test/rule-toggle.test.tsx | 26 ++----- .../settings/fraud-protection/interfaces.ts | 4 +- 24 files changed, 111 insertions(+), 188 deletions(-) create mode 100644 changelog/fix-9520-advanced-fraud-protection-settings diff --git a/changelog/fix-9520-advanced-fraud-protection-settings b/changelog/fix-9520-advanced-fraud-protection-settings new file mode 100644 index 00000000000..b08dee60572 --- /dev/null +++ b/changelog/fix-9520-advanced-fraud-protection-settings @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix settings display on the advanced fraud protection page. diff --git a/client/settings/fraud-protection/advanced-settings/allow-countries-notice.tsx b/client/settings/fraud-protection/advanced-settings/allow-countries-notice.tsx index bd03bea132c..e35671b83f2 100644 --- a/client/settings/fraud-protection/advanced-settings/allow-countries-notice.tsx +++ b/client/settings/fraud-protection/advanced-settings/allow-countries-notice.tsx @@ -45,7 +45,7 @@ interface AllowedCountriesNoticeProps { const AllowedCountriesNotice: React.FC< AllowedCountriesNoticeProps > = ( { setting, } ) => { - const { protectionSettingsUI, protectionSettingsChanged } = useContext( + const { protectionSettingsUI } = useContext( FraudPreventionSettingsContext ); const [ isBlocking, setIsBlocking ] = useState( @@ -53,7 +53,7 @@ const AllowedCountriesNotice: React.FC< AllowedCountriesNoticeProps > = ( { ); useEffect( () => { setIsBlocking( protectionSettingsUI[ setting ]?.block ?? false ); - }, [ protectionSettingsUI, setting, protectionSettingsChanged ] ); + }, [ protectionSettingsUI, setting ] ); const supportedCountriesType = getSupportedCountriesType(); const settingCountries = getSettingCountries(); diff --git a/client/settings/fraud-protection/advanced-settings/cards/order-items-threshold.tsx b/client/settings/fraud-protection/advanced-settings/cards/order-items-threshold.tsx index 31b4c7be8b6..7b730a06057 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/order-items-threshold.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/order-items-threshold.tsx @@ -1,14 +1,7 @@ /** * External dependencies */ -import React, { - useContext, - useEffect, - useState, - useMemo, - Dispatch, - SetStateAction, -} from 'react'; +import React, { useContext, useMemo, Dispatch, SetStateAction } from 'react'; import { __ } from '@wordpress/i18n'; import { TextControl } from '@wordpress/components'; @@ -36,7 +29,6 @@ const OrderItemsThresholdCustomForm: React.FC< OrderItemsThresholdCustomFormProp const { protectionSettingsUI, setProtectionSettingsUI, - setProtectionSettingsChanged, setIsDirty, } = useContext( FraudPreventionSettingsContext ); @@ -48,33 +40,11 @@ const OrderItemsThresholdCustomForm: React.FC< OrderItemsThresholdCustomFormProp [ protectionSettingsUI, setting ] ); - const minItemsTemp = parseInt( settingUI.min_items + '', 10 ); - const maxItemsTemp = parseInt( settingUI.max_items + '', 10 ); + const minItems = parseInt( settingUI?.min_items + '', 10 ); + const maxItems = parseInt( settingUI?.max_items + '', 10 ); - const [ minItemsCount, setMinItemsCount ] = useState( - isNaN( minItemsTemp ) ? '' : minItemsTemp - ); - const [ maxItemsCount, setMaxItemsCount ] = useState( - isNaN( maxItemsTemp ) ? '' : maxItemsTemp - ); - - useEffect( () => { - settingUI.min_items = minItemsCount - ? parseInt( minItemsCount + '', 10 ) - : minItemsCount; - settingUI.max_items = maxItemsCount - ? parseInt( maxItemsCount + '', 10 ) - : maxItemsCount; - setProtectionSettingsUI( protectionSettingsUI ); - setProtectionSettingsChanged( ( prev ) => ! prev ); - }, [ - settingUI, - minItemsCount, - maxItemsCount, - protectionSettingsUI, - setProtectionSettingsUI, - setProtectionSettingsChanged, - ] ); + const minItemsCount = isNaN( minItems ) ? '' : minItems; + const maxItemsCount = isNaN( maxItems ) ? '' : maxItems; const isItemRangeEmpty = ! parseInt( minItemsCount + '', 10 ) && @@ -82,6 +52,17 @@ const OrderItemsThresholdCustomForm: React.FC< OrderItemsThresholdCustomFormProp const isMinGreaterThanMax = parseInt( minItemsCount + '', 10 ) > parseInt( maxItemsCount + '', 10 ); + const handleInputChange = ( name: string ) => ( val: string ) => { + setProtectionSettingsUI( ( settings ) => ( { + ...settings, + [ setting ]: { + ...settings[ setting ], + [ name ]: val ? parseInt( val + '', 10 ) : val, + }, + } ) ); + setIsDirty( true ); + }; + return (
Limits @@ -98,10 +79,7 @@ const OrderItemsThresholdCustomForm: React.FC< OrderItemsThresholdCustomFormProp placeholder={ '0' } value={ minItemsCount } type="number" - onChange={ ( value ) => { - setMinItemsCount( value ); - setIsDirty( true ); - } } + onChange={ handleInputChange( 'min_items' ) } onKeyDown={ ( e ) => /^[+-.,e]$/m.test( e.key ) && e.preventDefault() } @@ -125,10 +103,7 @@ const OrderItemsThresholdCustomForm: React.FC< OrderItemsThresholdCustomFormProp placeholder={ '0' } type="number" value={ maxItemsCount } - onChange={ ( value ) => { - setMaxItemsCount( value ); - setIsDirty( true ); - } } + onChange={ handleInputChange( 'max_items' ) } onKeyDown={ ( e ) => /^[+-.,e]$/m.test( e.key ) && e.preventDefault() } diff --git a/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx b/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx index 5c47153e0fb..e32b2222d2e 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx @@ -1,14 +1,7 @@ /** * External dependencies */ -import React, { - useContext, - useEffect, - useState, - useMemo, - SetStateAction, - Dispatch, -} from 'react'; +import React, { useContext, useMemo, SetStateAction, Dispatch } from 'react'; import { __ } from '@wordpress/i18n'; import AmountInput from 'wcpay/components/amount-input'; @@ -55,7 +48,6 @@ const PurchasePriceThresholdCustomForm: React.FC< PurchasePriceThresholdCustomFo const { protectionSettingsUI, setProtectionSettingsUI, - setProtectionSettingsChanged, setIsDirty, } = useContext( FraudPreventionSettingsContext ); @@ -67,25 +59,8 @@ const PurchasePriceThresholdCustomForm: React.FC< PurchasePriceThresholdCustomFo [ protectionSettingsUI, setting ] ); - const minAmountTemp = parseFloat( settingUI.min_amount + '' ); - const maxAmountTemp = parseFloat( settingUI.max_amount + '' ); - - const [ minAmount, setMinAmount ] = useState( minAmountTemp ?? '' ); - const [ maxAmount, setMaxAmount ] = useState( maxAmountTemp ?? '' ); - - useEffect( () => { - settingUI.min_amount = minAmount ? parseFloat( minAmount + '' ) : null; - settingUI.max_amount = maxAmount ? parseFloat( maxAmount + '' ) : null; - setProtectionSettingsUI( protectionSettingsUI ); - setProtectionSettingsChanged( ( prev ) => ! prev ); - }, [ - minAmount, - maxAmount, - protectionSettingsUI, - setProtectionSettingsUI, - setProtectionSettingsChanged, - settingUI, - ] ); + const minAmount = parseFloat( settingUI.min_amount + '' ); + const maxAmount = parseFloat( settingUI.max_amount + '' ); const areInputsEmpty = ! getFloatValue( minAmount + '' ) && ! getFloatValue( maxAmount + '' ); @@ -96,6 +71,17 @@ const PurchasePriceThresholdCustomForm: React.FC< PurchasePriceThresholdCustomFo const currencySymbol = getCurrencySymbol(); + const handleAmountInputChange = ( name: string ) => ( val: string ) => { + setProtectionSettingsUI( ( settings ) => ( { + ...settings, + [ setting ]: { + ...settings[ setting ], + [ name ]: val ? parseFloat( val + '' ) : null, + }, + } ) ); + setIsDirty( true ); + }; + return (
Limits @@ -112,10 +98,7 @@ const PurchasePriceThresholdCustomForm: React.FC< PurchasePriceThresholdCustomFo prefix={ currencySymbol } placeholder={ '0.00' } value={ minAmount.toString() } - onChange={ ( val ) => { - setMinAmount( Number( val ) ); - setIsDirty( true ); - } } + onChange={ handleAmountInputChange( 'min_amount' ) } help={ __( 'Leave blank for no limit', 'woocommerce-payments' @@ -134,10 +117,7 @@ const PurchasePriceThresholdCustomForm: React.FC< PurchasePriceThresholdCustomFo prefix={ currencySymbol } placeholder={ '0.00' } value={ maxAmount.toString() } - onChange={ ( val ) => { - setMaxAmount( Number( val ) ); - setIsDirty( true ); - } } + onChange={ handleAmountInputChange( 'max_amount' ) } help={ __( 'Leave blank for no limit', 'woocommerce-payments' diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/address-mismatch.test.tsx.snap b/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/address-mismatch.test.tsx.snap index ce9560f5875..566772c05f0 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/address-mismatch.test.tsx.snap +++ b/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/address-mismatch.test.tsx.snap @@ -158,6 +158,7 @@ exports[`Address mismatch card renders correctly when enabled 1`] = ` > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -232,6 +233,7 @@ exports[`International IP address card renders correctly when enabled and checke > - Orders from the following countries will be blocked by the filter: + Orders from the following countries will be screened by the filter: Canada, United States @@ -753,7 +755,7 @@ exports[`International IP address card renders correctly when woocommerce_allowe data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/ip-address-mismatch.test.tsx.snap b/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/ip-address-mismatch.test.tsx.snap index 30b97d21b1a..f5f48430b26 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/ip-address-mismatch.test.tsx.snap +++ b/client/settings/fraud-protection/advanced-settings/cards/test/__snapshots__/ip-address-mismatch.test.tsx.snap @@ -174,6 +174,7 @@ exports[`International billing address card renders correctly when enabled 1`] = > { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; test( 'renders correctly', () => { diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/avs-mismatch.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/avs-mismatch.test.tsx index b2ae2de8e27..c178672128d 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/avs-mismatch.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/avs-mismatch.test.tsx @@ -40,8 +40,6 @@ describe( 'AVS mismatch card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; const { container } = render( @@ -69,8 +67,6 @@ describe( 'AVS mismatch card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; const { container } = render( diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/cvc-verification.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/cvc-verification.test.tsx index dfbbbc47ad5..9be801f735b 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/cvc-verification.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/cvc-verification.test.tsx @@ -40,8 +40,6 @@ describe( 'CVC verification card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; const { container } = render( @@ -72,8 +70,6 @@ describe( 'CVC verification card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; const { container } = render( diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/international-ip-address.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/international-ip-address.test.tsx index 6a36f94b4ee..a8a2a699190 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/international-ip-address.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/international-ip-address.test.tsx @@ -41,8 +41,6 @@ describe( 'International IP address card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), isDirty: false, setIsDirty: jest.fn(), }; diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/ip-address-mismatch.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/ip-address-mismatch.test.tsx index b102364ad75..f2eaa32ba8c 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/ip-address-mismatch.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/ip-address-mismatch.test.tsx @@ -38,8 +38,6 @@ describe( 'International billing address card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; global.wcSettings = { diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/order-items-threshold.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/order-items-threshold.test.tsx index 268845ba776..b2832aea03c 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/order-items-threshold.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/order-items-threshold.test.tsx @@ -37,8 +37,6 @@ describe( 'Order items threshold card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; test( 'renders correctly', () => { diff --git a/client/settings/fraud-protection/advanced-settings/cards/test/purchase-price-threshold.test.tsx b/client/settings/fraud-protection/advanced-settings/cards/test/purchase-price-threshold.test.tsx index 23f12a04d51..163cb3d06b8 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/test/purchase-price-threshold.test.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/test/purchase-price-threshold.test.tsx @@ -65,8 +65,6 @@ describe( 'Purchase price threshold card', () => { const contextValue = { protectionSettingsUI: settings, setProtectionSettingsUI: setSettings, - protectionSettingsChanged: false, - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; test( 'renders correctly', () => { diff --git a/client/settings/fraud-protection/advanced-settings/context.ts b/client/settings/fraud-protection/advanced-settings/context.ts index aa708fa36d0..c46b6bad440 100644 --- a/client/settings/fraud-protection/advanced-settings/context.ts +++ b/client/settings/fraud-protection/advanced-settings/context.ts @@ -7,8 +7,6 @@ import { FraudPreventionSettingsContextType } from '../interfaces'; const FraudPreventionSettingsContext = createContext( { protectionSettingsUI: {}, setProtectionSettingsUI: () => null, - protectionSettingsChanged: false, - setProtectionSettingsChanged: () => false, setIsDirty: () => null, } as FraudPreventionSettingsContextType ); diff --git a/client/settings/fraud-protection/advanced-settings/index.tsx b/client/settings/fraud-protection/advanced-settings/index.tsx index 881a09fdb90..74a37e9734a 100644 --- a/client/settings/fraud-protection/advanced-settings/index.tsx +++ b/client/settings/fraud-protection/advanced-settings/index.tsx @@ -122,10 +122,6 @@ const FraudProtectionAdvancedSettingsPage: React.FC = () => { const [ protectionSettingsUI, setProtectionSettingsUI ] = useState< ProtectionSettingsUI >( {} ); - const [ - protectionSettingsChanged, - setProtectionSettingsChanged, - ] = useState( false ); useEffect( () => { setProtectionSettingsUI( @@ -317,7 +313,7 @@ const FraudProtectionAdvancedSettingsPage: React.FC = () => { useEffect( confirmLeaveCallback, [ confirmLeaveCallback, - protectionSettingsChanged, + protectionSettingsUI, advancedFraudProtectionSettings, ] ); @@ -342,8 +338,6 @@ const FraudProtectionAdvancedSettingsPage: React.FC = () => { value={ { protectionSettingsUI, setProtectionSettingsUI, - protectionSettingsChanged, - setProtectionSettingsChanged, setIsDirty, } } > diff --git a/client/settings/fraud-protection/advanced-settings/rule-toggle.tsx b/client/settings/fraud-protection/advanced-settings/rule-toggle.tsx index 85e32790595..c2c49da2f5f 100644 --- a/client/settings/fraud-protection/advanced-settings/rule-toggle.tsx +++ b/client/settings/fraud-protection/advanced-settings/rule-toggle.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext } from 'react'; import { __ } from '@wordpress/i18n'; import { ToggleControl, RadioControl } from '@wordpress/components'; @@ -10,6 +10,7 @@ import { ToggleControl, RadioControl } from '@wordpress/components'; */ import './../style.scss'; import FraudPreventionSettingsContext from './context'; +import { FraudPreventionSettings } from '../interfaces'; interface FraudProtectionRuleToggleProps { setting: string; @@ -49,6 +50,15 @@ export const getHelpText = ( return helpTextMapping[ filterAction ]; }; +const getFilterAction = ( + settingUI: FraudPreventionSettings, + isFRTReviewFeatureActive: boolean +) => { + if ( ! isFRTReviewFeatureActive ) return filterActions.BLOCK; + + return settingUI.block ? filterActions.BLOCK : filterActions.REVIEW; +}; + const FraudProtectionRuleToggle: React.FC< FraudProtectionRuleToggleProps > = ( { setting, label, @@ -57,51 +67,31 @@ const FraudProtectionRuleToggle: React.FC< FraudProtectionRuleToggleProps > = ( const { protectionSettingsUI, setProtectionSettingsUI, - setProtectionSettingsChanged, setIsDirty, } = useContext( FraudPreventionSettingsContext ); const { isFRTReviewFeatureActive } = wcpaySettings; - const [ toggleState, setToggleState ] = useState( false ); - const [ filterAction, setFilterAction ] = useState( - isFRTReviewFeatureActive ? filterActions.REVIEW : filterActions.BLOCK - ); - const settingUI = protectionSettingsUI?.[ setting ]; + const filterAction = getFilterAction( settingUI, isFRTReviewFeatureActive ); + + const handleToggleChange = ( field: string, value: boolean ) => { + setProtectionSettingsUI( ( settings ) => ( { + ...settings, + [ setting ]: { + ...settings[ setting ], + [ field ]: value, + }, + } ) ); + setIsDirty( true ); + }; - // Set initial states from saved settings. - useEffect( () => { - if ( ! settingUI ) return; - - setToggleState( settingUI.enabled ); - setFilterAction( () => { - if ( ! isFRTReviewFeatureActive ) return filterActions.BLOCK; - - return settingUI.block ? filterActions.BLOCK : filterActions.REVIEW; - } ); - }, [ settingUI, isFRTReviewFeatureActive ] ); - - // Set global object values from input changes. - useEffect( () => { - if ( ! settingUI ) return; - - settingUI.enabled = toggleState; - settingUI.block = filterActions.BLOCK === filterAction; - setProtectionSettingsUI( protectionSettingsUI ); - setProtectionSettingsChanged( ( prev ) => ! prev ); - }, [ - settingUI, - toggleState, - filterAction, - setProtectionSettingsChanged, - protectionSettingsUI, - setProtectionSettingsUI, - ] ); + const handleEnableToggleChange = ( value: boolean ) => { + handleToggleChange( 'enabled', value ); + }; - const handleToggleChange = () => { - setToggleState( ( value ) => ! value ); - setIsDirty( true ); + const handleBlockToggleChange = ( value: string ) => { + handleToggleChange( 'block', filterActions.BLOCK === value ); }; if ( ! protectionSettingsUI ) { @@ -117,13 +107,13 @@ const FraudProtectionRuleToggle: React.FC< FraudProtectionRuleToggleProps > = ( - { toggleState && ( + { settingUI?.enabled && (
{ children } @@ -139,7 +129,7 @@ const FraudProtectionRuleToggle: React.FC< FraudProtectionRuleToggleProps > = (
) } diff --git a/client/settings/fraud-protection/advanced-settings/test/__snapshots__/index.test.tsx.snap b/client/settings/fraud-protection/advanced-settings/test/__snapshots__/index.test.tsx.snap index dddbf129bdb..c550c6f6480 100644 --- a/client/settings/fraud-protection/advanced-settings/test/__snapshots__/index.test.tsx.snap +++ b/client/settings/fraud-protection/advanced-settings/test/__snapshots__/index.test.tsx.snap @@ -340,7 +340,7 @@ exports[`Advanced fraud protection settings doesn't save when there's a validati data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -1345,7 +1345,7 @@ exports[`Advanced fraud protection settings doesn't save when there's a validati data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -2119,7 +2119,7 @@ exports[`Advanced fraud protection settings renders an error message when settin id="a11y-speak-polite" style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;" > - Orders from outside of the following countries will be blocked by the filter: Canada, United States + For security, this filter is enabled and cannot be modified. Payments failing CVC verification will be blocked. Learn more
@@ -2415,7 +2415,7 @@ exports[`Advanced fraud protection settings renders an error message when settin data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -3277,7 +3277,7 @@ exports[`Advanced fraud protection settings renders an error message when settin data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -3925,7 +3925,7 @@ exports[`Advanced fraud protection settings renders correctly 1`] = ` id="a11y-speak-polite" style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;" > - Orders from outside of the following countries will be blocked by the filter: Canada, United States + For security, this filter is enabled and cannot be modified. Payments failing CVC verification will be blocked. Learn more
- Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -5012,7 +5012,7 @@ exports[`Advanced fraud protection settings renders correctly 1`] = ` data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -5659,7 +5659,7 @@ exports[`Advanced fraud protection settings saves settings when there are no val id="a11y-speak-polite" style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;" > - Orders from outside of the following countries will be blocked by the filter: Canada, United States + For security, this filter is enabled and cannot be modified. Payments failing CVC verification will be blocked. Learn more
@@ -5938,7 +5938,7 @@ exports[`Advanced fraud protection settings saves settings when there are no val data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States @@ -6863,7 +6863,7 @@ exports[`Advanced fraud protection settings saves settings when there are no val data-wp-c16t="true" data-wp-component="FlexItem" > - Orders from outside of the following countries will be blocked by the filter: + Orders from outside of the following countries will be screened by the filter: Canada, United States diff --git a/client/settings/fraud-protection/advanced-settings/test/__snapshots__/rule-toggle.test.tsx.snap b/client/settings/fraud-protection/advanced-settings/test/__snapshots__/rule-toggle.test.tsx.snap index daa0f6c642e..daa15a8d239 100644 --- a/client/settings/fraud-protection/advanced-settings/test/__snapshots__/rule-toggle.test.tsx.snap +++ b/client/settings/fraud-protection/advanced-settings/test/__snapshots__/rule-toggle.test.tsx.snap @@ -219,6 +219,7 @@ exports[`Fraud protection rule toggle tests renders correctly when enabled 1`] = > { block: false, }, }, - protectionSettingsChanged: false, setProtectionSettingsUI: jest.fn(), - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; @@ -55,9 +52,7 @@ describe( 'Fraud protection rule toggle tests', () => { block: false, }, }, - protectionSettingsChanged: false, setProtectionSettingsUI: jest.fn(), - setProtectionSettingsChanged: jest.fn(), setIsDirty: jest.fn(), }; } ); @@ -134,7 +129,9 @@ describe( 'Fraud protection rule toggle tests', () => { expect( container.getByLabelText( 'Test rule toggle' ) ).toBeChecked(); expect( container.queryByText( 'test content' ) ).toBeInTheDocument(); } ); - test( 'sets the value correctly when enabled', () => { + test( 'calls the toggle enable function when clicking in the label', () => { + mockContext.protectionSettingsUI.test_rule.enabled = false; + const container = render( { ); + const activationToggle = container.getByLabelText( 'Test rule toggle' ); - expect( - mockContext.protectionSettingsUI.test_rule.enabled - ).toBeFalsy(); - activationToggle.click(); - expect( - mockContext.protectionSettingsUI.test_rule.enabled - ).toBeTruthy(); - activationToggle.click(); - expect( - mockContext.protectionSettingsUI.test_rule.enabled - ).toBeFalsy(); + userEvent.click( activationToggle ); + + expect( mockContext.setProtectionSettingsUI ).toHaveBeenCalled(); } ); } ); diff --git a/client/settings/fraud-protection/interfaces.ts b/client/settings/fraud-protection/interfaces.ts index 7ad040bad87..a034fb5d6a5 100644 --- a/client/settings/fraud-protection/interfaces.ts +++ b/client/settings/fraud-protection/interfaces.ts @@ -33,9 +33,7 @@ export type ProtectionSettingsUI = Record< string, FraudPreventionSettings >; export interface FraudPreventionSettingsContextType { protectionSettingsUI: ProtectionSettingsUI; - setProtectionSettingsUI: ( settings: ProtectionSettingsUI ) => void; - protectionSettingsChanged: boolean; - setProtectionSettingsChanged: Dispatch< SetStateAction< boolean > >; + setProtectionSettingsUI: Dispatch< SetStateAction< ProtectionSettingsUI > >; setIsDirty: Dispatch< SetStateAction< boolean > >; } From 98f346755bfc51b698afbee084a7c930908f7dc3 Mon Sep 17 00:00:00 2001 From: Guilherme Pressutto Date: Fri, 25 Oct 2024 10:38:54 -0300 Subject: [PATCH 08/45] Fix PMME appearance in checkout (#9621) Co-authored-by: Timur Karimov --- changelog/fix-pmme-appearance-checkout | 4 +++ client/checkout/blocks/index.js | 3 -- client/checkout/blocks/payment-elements.js | 2 +- .../checkout/blocks/payment-method-label.js | 34 ++++++++++++++----- client/checkout/classic/event-handlers.js | 3 +- client/checkout/classic/payment-processing.js | 5 +-- 6 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 changelog/fix-pmme-appearance-checkout diff --git a/changelog/fix-pmme-appearance-checkout b/changelog/fix-pmme-appearance-checkout new file mode 100644 index 00000000000..4fc63e58c52 --- /dev/null +++ b/changelog/fix-pmme-appearance-checkout @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix PMME appearance in checkout diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 1648598a24b..12017a03129 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -77,8 +77,6 @@ const api = new WCPayAPI( request ); -const stripeAppearance = getUPEConfig( 'wcBlocksUPEAppearance' ); - Object.entries( enabledPaymentMethodsConfig ) .filter( ( [ upeName ] ) => upeName !== 'link' ) .forEach( ( [ upeName, upeConfig ] ) => { @@ -114,7 +112,6 @@ Object.entries( enabledPaymentMethodsConfig ) api={ api } upeConfig={ upeConfig } upeName={ upeName } - stripeAppearance={ stripeAppearance } upeAppearanceTheme={ upeAppearanceTheme } /> ), diff --git a/client/checkout/blocks/payment-elements.js b/client/checkout/blocks/payment-elements.js index 7359b89a162..bedbf4777cc 100644 --- a/client/checkout/blocks/payment-elements.js +++ b/client/checkout/blocks/payment-elements.js @@ -36,7 +36,7 @@ const PaymentElements = ( { api, ...props } ) => { useEffect( () => { async function generateUPEAppearance() { // Generate UPE input styles. - let upeAppearance = getAppearance( 'blocks_checkout', false, true ); + let upeAppearance = getAppearance( 'blocks_checkout', false ); upeAppearance = await api.saveUPEAppearance( upeAppearance, 'blocks_checkout' diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js index e63009d691d..417007816aa 100644 --- a/client/checkout/blocks/payment-method-label.js +++ b/client/checkout/blocks/payment-method-label.js @@ -9,17 +9,16 @@ import { normalizeCurrencyToMinorUnit } from '../utils'; import { getUPEConfig } from 'wcpay/utils/checkout'; import { __ } from '@wordpress/i18n'; import './style.scss'; +import { useEffect, useState } from '@wordpress/element'; +import { getAppearance } from 'wcpay/checkout/upe-styles'; -export default ( { - api, - upeConfig, - upeName, - stripeAppearance, - upeAppearanceTheme, -} ) => { +export default ( { api, upeConfig, upeName, upeAppearanceTheme } ) => { const cartData = wp.data.select( 'wc/store/cart' ).getCartData(); const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ]; const isTestMode = getUPEConfig( 'testMode' ); + const [ appearance, setAppearance ] = useState( + getUPEConfig( 'wcBlocksUPEAppearance' ) + ); // Stripe expects the amount to be sent as the minor unit of 2 digits. const amount = parseInt( @@ -38,6 +37,22 @@ export default ( { const isCreditCard = upeName === 'card'; + useEffect( () => { + async function generateUPEAppearance() { + // Generate UPE input styles. + let upeAppearance = getAppearance( 'blocks_checkout', false ); + upeAppearance = await api.saveUPEAppearance( + upeAppearance, + 'blocks_checkout' + ); + setAppearance( upeAppearance ); + } + + if ( ! appearance ) { + generateUPEAppearance(); + } + }, [ api, appearance ] ); + return ( <>
@@ -63,12 +78,13 @@ export default ( { ( upeConfig.countries.length === 0 || upeConfig.countries.includes( currentCountry ) ) && amount > 0 && - currentCountry && ( + currentCountry && + appearance && (
Date: Fri, 25 Oct 2024 22:23:09 -0300 Subject: [PATCH 09/45] Decoupled Multi-currency module (#9617) Co-authored-by: Hector Lovo Co-authored-by: Rafael Zaleski Co-authored-by: Hector Lovo --- changelog/fix-backwards-compat-mccy-instance | 4 + changelog/multi-currency-v2 | 4 + .../add-payment-methods-task.js | 2 +- .../currency-information-for-methods.js | 4 +- .../test/add-payment-methods-task.test.js | 14 +- .../currency-information-for-methods.test.js | 11 +- .../wizard/task-item.scss | 16 + .../wizard/task-item.tsx | 12 + client/capital/index.tsx | 5 +- client/checkout/woopay/email-input-iframe.js | 4 +- .../account-balances/balance-block.tsx | 2 +- client/components/account-balances/index.tsx | 2 +- .../account-fees/expiration-bar.js | 2 +- .../account-fees/expiration-description.js | 2 +- .../account-status/account-fees/index.js | 5 +- .../components/active-loan-summary/index.tsx | 2 +- client/components/deposits-overview/index.tsx | 2 +- .../recent-deposits-list.tsx | 2 +- .../components/disputed-order-notice/index.js | 2 +- .../payment-activity/payment-data-tile.tsx | 2 +- client/components/welcome/currency-select.tsx | 2 +- client/data/deposits/actions.js | 2 +- client/data/index.ts | 1 - client/data/multi-currency/index.js | 12 - client/data/store.js | 5 - client/deposits/details/index.tsx | 5 +- client/deposits/filters/index.js | 2 +- client/deposits/instant-deposits/index.tsx | 2 +- client/deposits/instant-deposits/modal.tsx | 5 +- client/deposits/list/index.tsx | 5 +- client/disputes/filters/index.tsx | 2 +- client/disputes/filters/test/index.tsx | 2 +- client/disputes/index.tsx | 5 +- client/disputes/info/index.tsx | 2 +- client/disputes/utils.ts | 5 +- client/index.js | 2 +- .../wizard/task-item.scss | 17 - .../multi-currency-setup/wizard/task-item.tsx | 71 -- .../overview/task-list/tasks/dispute-task.tsx | 2 +- .../dispute-details/dispute-steps.tsx | 2 +- .../dispute-details/dispute-summary-row.tsx | 2 +- client/payment-details/readers/index.js | 5 +- client/payment-details/summary/index.tsx | 5 +- client/payment-details/timeline/map-events.js | 2 +- .../cards/purchase-price-threshold.tsx | 2 +- .../fraud-protection/test/index.test.tsx | 16 - client/settings/payment-methods-list/index.js | 2 +- .../__tests__/payment-methods-section.test.js | 12 +- client/transactions/blocked/columns.tsx | 2 +- client/transactions/blocked/index.tsx | 2 +- client/transactions/filters/index.tsx | 2 +- client/transactions/list/converted-amount.tsx | 2 +- client/transactions/list/index.tsx | 2 +- client/transactions/risk-review/columns.tsx | 2 +- client/transactions/risk-review/index.tsx | 2 +- client/transactions/uncaptured/index.tsx | 2 +- client/utils/account-fees.tsx | 4 +- client/utils/test/account-fees.tsx | 4 +- client/vat/form/tasks/company-data-task.tsx | 6 +- client/vat/form/tasks/vat-number-task.tsx | 6 +- composer.json | 2 +- includes/class-database-cache.php | 5 +- includes/class-wc-payments-account.php | 58 +- ...s-wc-payments-explicit-price-formatter.php | 1 - ...class-wc-payments-localization-service.php | 4 +- .../class-wc-payments-settings-service.php | 42 + includes/class-wc-payments-utils.php | 18 - includes/class-wc-payments.php | 32 +- .../class-wc-payments-currency-manager.php} | 77 +- .../wc-payments-multi-currency.php | 17 +- includes/multi-currency/CountryFlags.php | 304 ----- .../multi-currency/Helpers/OrderMetaHelper.md | 20 - .../Helpers/OrderMetaHelper.php | 438 ------- .../class-wc-payments-api-client.php | 7 +- .../client/analytics}/index.js | 0 .../client}/blocks/currency-switcher.js | 5 +- .../currency-delete-illustration/index.js | 0 .../currency-delete-illustration/styles.scss | 0 .../client/components}/preview-modal/index.js | 2 +- .../components}/preview-modal/index.scss | 0 .../client}/components/search/index.js | 0 .../client}/components/search/style.scss | 0 .../search/test/__snapshots__/index.js.snap | 0 .../client}/components/search/test/index.js | 0 .../client}/context.js | 0 .../client/data}/action-types.js | 0 .../client/data}/actions.js | 4 +- multi-currency/client/data/constants.js | 4 + .../client/data}/hooks.js | 6 +- multi-currency/client/data/index.ts | 20 + .../client/data}/reducer.js | 0 .../client/data}/resolvers.js | 4 +- .../client/data}/selectors.js | 2 +- multi-currency/client/data/store.js | 28 + .../client}/index.js | 4 +- multi-currency/client/interface/assets.js | 6 + multi-currency/client/interface/components.js | 23 + multi-currency/client/interface/data.js | 10 + multi-currency/client/interface/functions.js | 25 + .../enabled-currencies-list/delete-button.js | 10 +- .../enabled-currencies-list/index.js | 4 +- .../list-item-placeholder.js | 2 +- .../enabled-currencies-list/list-item.js | 2 +- .../enabled-currencies-list/list.js | 0 .../modal-checkbox-list.js | 0 .../enabled-currencies-list/modal-checkbox.js | 0 .../enabled-currencies-list/modal.js | 6 +- .../enabled-currencies-list/style.scss | 0 .../test/__snapshots__/index.js.snap | 0 .../enabled-currencies-list/test/index.js | 8 +- .../client/settings/multi-currency}/index.js | 2 +- .../multi-currency}/store-settings/index.js | 10 +- .../multi-currency}/store-settings/style.scss | 0 .../test/__snapshots__/index.test.js.snap | 0 .../store-settings/test/index.test.js | 4 +- .../settings/multi-currency}/style.scss | 0 .../test/__snapshots__/index.test.js.snap | 0 .../multi-currency}/test/index.test.js | 2 +- .../settings/single-currency}/constants.js | 0 .../single-currency}/currency-preview.js | 5 +- .../client/settings/single-currency}/index.js | 12 +- .../settings/single-currency}/style.scss | 0 .../test/__snapshots__/index.test.js.snap | 0 .../single-currency}/test/index.test.js | 6 +- .../client/setup}/index.js | 4 +- .../tasks/add-currencies-task/constants.js | 0 .../setup}/tasks/add-currencies-task/index.js | 19 +- .../tasks/add-currencies-task/index.scss | 0 .../test/__snapshots__/index.test.js.snap | 0 .../add-currencies-task/test/index.test.js | 10 +- .../add-currencies-task/test/utils.test.js | 0 .../setup}/tasks/add-currencies-task/utils.js | 0 .../setup}/tasks/multi-currency-setup.js | 8 +- .../setup}/tasks/multi-currency-setup.scss | 0 .../setup}/tasks/setup-complete-task/index.js | 10 +- .../tasks/setup-complete-task/index.scss | 0 .../setup-complete-task/test/index.test.js | 5 +- .../setup}/tasks/store-settings-task/index.js | 13 +- .../tasks/store-settings-task/index.scss | 0 .../test/__snapshots__/index.test.js.snap | 0 .../store-settings-task/test/index.test.js | 16 +- .../multi-currency-setup.test.js.snap | 0 .../tasks/test/multi-currency-setup.test.js | 0 .../client/setup}/test/index.js | 0 .../client}/utils/currency/index.js | 0 .../client}/utils/currency/test/index.js | 2 +- .../__tests__/index.test.js | 2 +- .../utils/missing-currencies-message/index.ts | 2 +- .../src}/AdminNotices.php | 0 .../src}/Analytics.php | 21 +- .../src}/BackendCurrencies.php | 12 +- .../src}/Compatibility.php | 4 +- .../src}/Compatibility/BaseCompatibility.php | 2 +- .../Compatibility/WooCommerceBookings.php | 2 +- .../Compatibility/WooCommerceDeposits.php | 2 +- .../src}/Compatibility/WooCommerceFedEx.php | 2 +- .../WooCommerceNameYourPrice.php | 2 +- .../WooCommercePointsAndRewards.php | 2 +- .../Compatibility/WooCommercePreOrders.php | 2 +- .../WooCommerceProductAddOns.php | 2 +- .../WooCommerceSubscriptions.php | 22 +- .../src}/Compatibility/WooCommerceUPS.php | 2 +- multi-currency/src/CountryFlags.php | 301 +++++ .../src}/Currency.php | 17 +- .../src}/CurrencySwitcherBlock.php | 4 +- .../src}/CurrencySwitcherWidget.php | 0 .../Exceptions/InvalidCurrencyException.php | 4 +- .../InvalidCurrencyRateException.php | 4 +- .../src}/FrontendCurrencies.php | 16 +- .../src}/FrontendPrices.php | 0 .../src}/Geolocation.php | 10 +- .../MultiCurrencyAccountInterface.php | 61 + .../MultiCurrencyApiClientInterface.php | 30 + .../MultiCurrencyCacheInterface.php | 46 + .../MultiCurrencyLocalizationInterface.php | 31 + .../MultiCurrencySettingsInterface.php | 34 + multi-currency/src/Logger.php | 68 ++ .../src}/MultiCurrency.php | 1029 +++++++++-------- .../src}/Notes/NoteMultiCurrencyAvailable.php | 10 +- .../src}/RestController.php | 62 +- .../src}/Settings.php | 2 +- .../src}/SettingsOnboardCta.php | 23 +- .../src}/StorefrontIntegration.php | 0 .../src}/Tracking.php | 0 .../src}/UserSettings.php | 0 .../src}/Utils.php | 37 +- phpcs.xml.dist | 2 +- psalm-baseline.xml | 4 +- tasks/release.js | 8 +- tests/js/jest.config.js | 7 +- .../test-class-woocommerce-subscriptions.php | 23 +- ...ass-note-multi-currency-available-test.php | 9 +- .../multi-currency/test-class-analytics.php | 51 +- .../test-class-backend-currencies.php | 7 +- .../test-class-compatibility.php | 28 +- .../test-class-country-flags.php | 3 +- .../test-class-currency-switcher-block.php | 33 +- .../test-class-frontend-currencies.php | 1 + .../test-class-frontend-prices.php | 6 +- .../multi-currency/test-class-geolocation.php | 18 +- .../test-class-multi-currency.php | 103 +- .../test-class-rest-controller.php | 107 +- .../multi-currency/test-class-settings.php | 2 - .../unit/multi-currency/test-class-utils.php | 3 +- ...st-class-wc-payments-currency-manager.php} | 37 +- ...s-wc-payments-explicit-price-formatter.php | 12 +- tsconfig.json | 3 +- webpack/shared.js | 17 +- 208 files changed, 2149 insertions(+), 1865 deletions(-) create mode 100644 changelog/fix-backwards-compat-mccy-instance create mode 100644 changelog/multi-currency-v2 delete mode 100644 client/data/multi-currency/index.js delete mode 100644 client/multi-currency-setup/wizard/task-item.scss delete mode 100644 client/multi-currency-setup/wizard/task-item.tsx create mode 100644 includes/class-wc-payments-settings-service.php rename includes/{multi-currency/PaymentMethodsCompatibility.php => compat/multi-currency/class-wc-payments-currency-manager.php} (69%) rename includes/{ => compat}/multi-currency/wc-payments-multi-currency.php (80%) delete mode 100644 includes/multi-currency/CountryFlags.php delete mode 100644 includes/multi-currency/Helpers/OrderMetaHelper.md delete mode 100644 includes/multi-currency/Helpers/OrderMetaHelper.php rename {client/multi-currency-analytics => multi-currency/client/analytics}/index.js (100%) rename {client/multi-currency => multi-currency/client}/blocks/currency-switcher.js (99%) rename {client => multi-currency/client}/components/currency-delete-illustration/index.js (100%) rename {client => multi-currency/client}/components/currency-delete-illustration/styles.scss (100%) rename {client/multi-currency => multi-currency/client/components}/preview-modal/index.js (95%) rename {client/multi-currency => multi-currency/client/components}/preview-modal/index.scss (100%) rename {client => multi-currency/client}/components/search/index.js (100%) rename {client => multi-currency/client}/components/search/style.scss (100%) rename {client => multi-currency/client}/components/search/test/__snapshots__/index.js.snap (100%) rename {client => multi-currency/client}/components/search/test/index.js (100%) rename {client/multi-currency => multi-currency/client}/context.js (100%) rename {client/data/multi-currency => multi-currency/client/data}/action-types.js (100%) rename {client/data/multi-currency => multi-currency/client/data}/actions.js (96%) create mode 100644 multi-currency/client/data/constants.js rename {client/data/multi-currency => multi-currency/client/data}/hooks.js (96%) create mode 100644 multi-currency/client/data/index.ts rename {client/data/multi-currency => multi-currency/client/data}/reducer.js (100%) rename {client/data/multi-currency => multi-currency/client/data}/resolvers.js (93%) rename {client/data/multi-currency => multi-currency/client/data}/selectors.js (96%) create mode 100644 multi-currency/client/data/store.js rename {client/multi-currency => multi-currency/client}/index.js (91%) create mode 100644 multi-currency/client/interface/assets.js create mode 100644 multi-currency/client/interface/components.js create mode 100644 multi-currency/client/interface/data.js create mode 100644 multi-currency/client/interface/functions.js rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/delete-button.js (92%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/index.js (97%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/list-item-placeholder.js (94%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/list-item.js (97%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/list.js (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/modal-checkbox-list.js (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/modal-checkbox.js (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/modal.js (97%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/style.scss (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/test/__snapshots__/index.js.snap (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/enabled-currencies-list/test/index.js (97%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/index.js (87%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/store-settings/index.js (95%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/store-settings/style.scss (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/store-settings/test/__snapshots__/index.test.js.snap (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/store-settings/test/index.test.js (95%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/style.scss (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/test/__snapshots__/index.test.js.snap (100%) rename {client/multi-currency/multi-currency-settings => multi-currency/client/settings/multi-currency}/test/index.test.js (92%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/constants.js (100%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/currency-preview.js (96%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/index.js (98%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/style.scss (100%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/test/__snapshots__/index.test.js.snap (100%) rename {client/multi-currency/single-currency-settings => multi-currency/client/settings/single-currency}/test/index.test.js (97%) rename {client/multi-currency-setup => multi-currency/client/setup}/index.js (77%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/constants.js (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/index.js (93%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/index.scss (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/test/__snapshots__/index.test.js.snap (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/test/index.test.js (97%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/test/utils.test.js (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/add-currencies-task/utils.js (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/multi-currency-setup.js (84%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/multi-currency-setup.scss (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/setup-complete-task/index.js (89%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/setup-complete-task/index.scss (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/setup-complete-task/test/index.test.js (87%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/store-settings-task/index.js (92%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/store-settings-task/index.scss (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/store-settings-task/test/__snapshots__/index.test.js.snap (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/store-settings-task/test/index.test.js (92%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/test/__snapshots__/multi-currency-setup.test.js.snap (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/tasks/test/multi-currency-setup.test.js (100%) rename {client/multi-currency-setup => multi-currency/client/setup}/test/index.js (100%) rename {client => multi-currency/client}/utils/currency/index.js (100%) rename {client => multi-currency/client}/utils/currency/test/index.js (98%) rename client/multi-currency/__tests__/missing-currencies-message.test.js => multi-currency/client/utils/missing-currencies-message/__tests__/index.test.js (89%) rename client/multi-currency/missing-currencies-message.ts => multi-currency/client/utils/missing-currencies-message/index.ts (91%) rename {includes/multi-currency => multi-currency/src}/AdminNotices.php (100%) rename {includes/multi-currency => multi-currency/src}/Analytics.php (96%) rename {includes/multi-currency => multi-currency/src}/BackendCurrencies.php (91%) rename {includes/multi-currency => multi-currency/src}/Compatibility.php (99%) rename {includes/multi-currency => multi-currency/src}/Compatibility/BaseCompatibility.php (95%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceBookings.php (99%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceDeposits.php (99%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceFedEx.php (97%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceNameYourPrice.php (99%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommercePointsAndRewards.php (98%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommercePreOrders.php (96%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceProductAddOns.php (99%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceSubscriptions.php (97%) rename {includes/multi-currency => multi-currency/src}/Compatibility/WooCommerceUPS.php (97%) create mode 100644 multi-currency/src/CountryFlags.php rename {includes/multi-currency => multi-currency/src}/Currency.php (89%) rename {includes/multi-currency => multi-currency/src}/CurrencySwitcherBlock.php (97%) rename {includes/multi-currency => multi-currency/src}/CurrencySwitcherWidget.php (100%) rename {includes/multi-currency => multi-currency/src}/Exceptions/InvalidCurrencyException.php (71%) rename {includes/multi-currency => multi-currency/src}/Exceptions/InvalidCurrencyRateException.php (71%) rename {includes/multi-currency => multi-currency/src}/FrontendCurrencies.php (94%) rename {includes/multi-currency => multi-currency/src}/FrontendPrices.php (100%) rename {includes/multi-currency => multi-currency/src}/Geolocation.php (84%) create mode 100644 multi-currency/src/Interfaces/MultiCurrencyAccountInterface.php create mode 100644 multi-currency/src/Interfaces/MultiCurrencyApiClientInterface.php create mode 100644 multi-currency/src/Interfaces/MultiCurrencyCacheInterface.php create mode 100644 multi-currency/src/Interfaces/MultiCurrencyLocalizationInterface.php create mode 100644 multi-currency/src/Interfaces/MultiCurrencySettingsInterface.php create mode 100644 multi-currency/src/Logger.php rename {includes/multi-currency => multi-currency/src}/MultiCurrency.php (89%) rename {includes/multi-currency => multi-currency/src}/Notes/NoteMultiCurrencyAvailable.php (84%) rename {includes/multi-currency => multi-currency/src}/RestController.php (78%) rename {includes/multi-currency => multi-currency/src}/Settings.php (96%) rename {includes/multi-currency => multi-currency/src}/SettingsOnboardCta.php (74%) rename {includes/multi-currency => multi-currency/src}/StorefrontIntegration.php (100%) rename {includes/multi-currency => multi-currency/src}/Tracking.php (100%) rename {includes/multi-currency => multi-currency/src}/UserSettings.php (100%) rename {includes/multi-currency => multi-currency/src}/Utils.php (51%) rename tests/unit/{multi-currency/test-class-payment-methods-compatibility.php => test-class-wc-payments-currency-manager.php} (84%) diff --git a/changelog/fix-backwards-compat-mccy-instance b/changelog/fix-backwards-compat-mccy-instance new file mode 100644 index 00000000000..95a0701ac1c --- /dev/null +++ b/changelog/fix-backwards-compat-mccy-instance @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Provide backwards-compatible method for retrieving the multi-currency instance. diff --git a/changelog/multi-currency-v2 b/changelog/multi-currency-v2 new file mode 100644 index 00000000000..463f61c882e --- /dev/null +++ b/changelog/multi-currency-v2 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Decoupled Multi-currency module from gateway dependencies. diff --git a/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js b/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js index 76a2f3baa71..fbc23b63b67 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js @@ -36,7 +36,7 @@ import PaymentMethodCheckbox from '../../components/payment-methods-checkboxes/p import { LoadableBlock } from '../../components/loadable'; import LoadableSettingsSection from '../../settings/loadable-settings-section'; import CurrencyInformationForMethods from './currency-information-for-methods'; -import { getMissingCurrenciesTooltipMessage } from 'wcpay/multi-currency/missing-currencies-message'; +import { getMissingCurrenciesTooltipMessage } from 'multi-currency/interface/functions'; import { upeCapabilityStatuses, upeMethods } from '../constants'; import paymentMethodsMap from '../../payment-methods-map'; import ConfirmPaymentMethodActivationModal from 'wcpay/settings/payment-methods-list/activation-modal'; diff --git a/client/additional-methods-setup/upe-preview-methods-selector/currency-information-for-methods.js b/client/additional-methods-setup/upe-preview-methods-selector/currency-information-for-methods.js index f4e1fc02cbc..9639a729c8b 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/currency-information-for-methods.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/currency-information-for-methods.js @@ -9,11 +9,11 @@ import interpolateComponents from '@automattic/interpolate-components'; /** * Internal dependencies */ +import { useAccountDomesticCurrency } from '../../data'; import { - useAccountDomesticCurrency, useCurrencies, useEnabledCurrencies, -} from '../../data'; +} from 'multi-currency/interface/data'; import WCPaySettingsContext from '../../settings/wcpay-settings-context'; import InlineNotice from 'components/inline-notice'; import PaymentMethodsMap from '../../payment-methods-map'; diff --git a/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js b/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js index 3339e812975..3a12347327f 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js @@ -22,11 +22,14 @@ import { useEnabledPaymentMethodIds, useGetPaymentMethodStatuses, useSettings, - useCurrencies, - useEnabledCurrencies, useManualCapture, useAccountDomesticCurrency, } from '../../../data'; +import { + useCurrencies, + useEnabledCurrencies, +} from 'multi-currency/interface/data'; + import WCPaySettingsContext from '../../../settings/wcpay-settings-context'; import { upeCapabilityStatuses } from 'wcpay/additional-methods-setup/constants'; @@ -34,13 +37,16 @@ jest.mock( '../../../data', () => ( { useGetAvailablePaymentMethodIds: jest.fn(), useEnabledPaymentMethodIds: jest.fn(), useSettings: jest.fn(), - useCurrencies: jest.fn(), - useEnabledCurrencies: jest.fn(), useGetPaymentMethodStatuses: jest.fn(), useManualCapture: jest.fn(), useAccountDomesticCurrency: jest.fn(), } ) ); +jest.mock( 'multi-currency/interface/data', () => ( { + useCurrencies: jest.fn(), + useEnabledCurrencies: jest.fn(), +} ) ); + jest.mock( '@wordpress/a11y', () => ( { ...jest.requireActual( '@wordpress/a11y' ), speak: jest.fn(), diff --git a/client/additional-methods-setup/upe-preview-methods-selector/test/currency-information-for-methods.test.js b/client/additional-methods-setup/upe-preview-methods-selector/test/currency-information-for-methods.test.js index fd473249eb7..cc2abefe96d 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/test/currency-information-for-methods.test.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/test/currency-information-for-methods.test.js @@ -7,18 +7,21 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { useAccountDomesticCurrency } from '../../../data'; import { useCurrencies, useEnabledCurrencies, - useAccountDomesticCurrency, -} from 'wcpay/data'; +} from 'multi-currency/interface/data'; import CurrencyInformationForMethods from '../currency-information-for-methods'; import WCPaySettingsContext from '../../../settings/wcpay-settings-context'; -jest.mock( 'wcpay/data', () => ( { +jest.mock( '../../../data', () => ( { + useAccountDomesticCurrency: jest.fn(), +} ) ); + +jest.mock( 'multi-currency/interface/data', () => ( { useCurrencies: jest.fn(), useEnabledCurrencies: jest.fn(), - useAccountDomesticCurrency: jest.fn(), } ) ); jest.mock( '@wordpress/a11y', () => ( { diff --git a/client/additional-methods-setup/wizard/task-item.scss b/client/additional-methods-setup/wizard/task-item.scss index c2b7dbb5ec6..ae24c06ac08 100644 --- a/client/additional-methods-setup/wizard/task-item.scss +++ b/client/additional-methods-setup/wizard/task-item.scss @@ -117,6 +117,22 @@ } } + &__visible-description-element { + position: absolute; + margin-left: 40px; + margin-top: 0; + margin-bottom: 1em; + + &.is-muted-color { + color: $gray-700; + } + + .components-external-link svg { + width: 1em; + height: 1em; + } + } + .add-payment-methods-task { &__payment-selector { &-wrapper { diff --git a/client/additional-methods-setup/wizard/task-item.tsx b/client/additional-methods-setup/wizard/task-item.tsx index f1c7ba4177e..a0ec6169bae 100644 --- a/client/additional-methods-setup/wizard/task-item.tsx +++ b/client/additional-methods-setup/wizard/task-item.tsx @@ -16,6 +16,7 @@ interface WizardTaskItemProps { title: string; index: number; className?: string; + visibleDescription?: string; } const WizardTaskItem: React.FC< WizardTaskItemProps > = ( { @@ -23,6 +24,7 @@ const WizardTaskItem: React.FC< WizardTaskItemProps > = ( { title, index, className, + visibleDescription, } ) => { const { isCompleted, isActive } = useContext( WizardTaskContext ); @@ -51,6 +53,16 @@ const WizardTaskItem: React.FC< WizardTaskItemProps > = ( {
{ title }
+ { visibleDescription && ! isActive && ( + + { visibleDescription } + + ) }
{ children }
); diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 8d1ab3f4741..81e76ad91b4 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -15,7 +15,10 @@ import Page from 'components/page'; import { TestModeNotice } from 'components/test-mode-notice'; import ErrorBoundary from 'components/error-boundary'; import ActiveLoanSummary from 'components/active-loan-summary'; -import { formatExplicitCurrency, isZeroDecimalCurrency } from 'utils/currency'; +import { + formatExplicitCurrency, + isZeroDecimalCurrency, +} from 'multi-currency/interface/functions'; import { CapitalLoan } from 'data/capital/types'; import ClickableCell from 'components/clickable-cell'; import Chip from 'components/chip'; diff --git a/client/checkout/woopay/email-input-iframe.js b/client/checkout/woopay/email-input-iframe.js index 83a4f196924..c5ccaceed96 100644 --- a/client/checkout/woopay/email-input-iframe.js +++ b/client/checkout/woopay/email-input-iframe.js @@ -13,8 +13,8 @@ import { appendRedirectionParams, shouldSkipWooPay, deleteSkipWooPayCookie, -} from 'wcpay/checkout/woopay/utils'; -import { getAppearanceType } from 'wcpay/checkout/utils'; +} from './utils'; +import { getAppearanceType } from '../utils'; export const handleWooPayEmailInput = async ( field, diff --git a/client/components/account-balances/balance-block.tsx b/client/components/account-balances/balance-block.tsx index 4b71f4ef273..c9c9535c9ee 100644 --- a/client/components/account-balances/balance-block.tsx +++ b/client/components/account-balances/balance-block.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; /** * Internal dependencies */ -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import Loadable from 'components/loadable'; /** diff --git a/client/components/account-balances/index.tsx b/client/components/account-balances/index.tsx index 2a1ace68487..fa3be8b8422 100644 --- a/client/components/account-balances/index.tsx +++ b/client/components/account-balances/index.tsx @@ -23,7 +23,7 @@ import { } from './balance-tooltip'; import { fundLabelStrings } from './strings'; import { ClickTooltip } from '../tooltip'; -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import { useAllDepositsOverviews } from 'wcpay/data'; import { useSelectedCurrency } from 'wcpay/overview/hooks'; import './style.scss'; diff --git a/client/components/account-status/account-fees/expiration-bar.js b/client/components/account-status/account-fees/expiration-bar.js index 80003bba2a4..2d293e948c9 100644 --- a/client/components/account-status/account-fees/expiration-bar.js +++ b/client/components/account-status/account-fees/expiration-bar.js @@ -4,7 +4,7 @@ * Internal dependencies */ import ProgressBar from 'components/progress-bar'; -import { formatCurrency } from 'utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; const ExpirationBar = ( { feeData: { diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index a07067b6495..6be5b58681c 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -10,7 +10,7 @@ import moment from 'moment'; /** * Internal dependencies */ -import { formatCurrency } from 'utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; const ExpirationDescription = ( { feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, diff --git a/client/components/account-status/account-fees/index.js b/client/components/account-status/account-fees/index.js index 93a93293c11..4b77b39e5f3 100644 --- a/client/components/account-status/account-fees/index.js +++ b/client/components/account-status/account-fees/index.js @@ -10,7 +10,10 @@ import { __ } from '@wordpress/i18n'; */ import ExpirationBar from './expiration-bar'; import ExpirationDescription from './expiration-description'; -import { formatCurrencyName, getCurrency } from 'utils/currency'; +import { + formatCurrencyName, + getCurrency, +} from 'multi-currency/interface/functions'; import { formatAccountFeesDescription, getCurrentBaseFee, diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index 97aac9ac082..0c5059ef87c 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -18,7 +18,7 @@ import { dateI18n } from '@wordpress/date'; /** * Internal dependencies. */ -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import Loadable from 'components/loadable'; import { useActiveLoanSummary } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; diff --git a/client/components/deposits-overview/index.tsx b/client/components/deposits-overview/index.tsx index 23ce733bf50..5b89c21a95a 100644 --- a/client/components/deposits-overview/index.tsx +++ b/client/components/deposits-overview/index.tsx @@ -16,7 +16,7 @@ import { getHistory } from '@woocommerce/navigation'; * Internal dependencies. */ import { getAdminUrl } from 'wcpay/utils'; -import { formatExplicitCurrency } from 'wcpay/utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; import Loadable from 'components/loadable'; import { useSelectedCurrencyOverview } from 'wcpay/overview/hooks'; diff --git a/client/components/deposits-overview/recent-deposits-list.tsx b/client/components/deposits-overview/recent-deposits-list.tsx index 1fed2448758..88555793d0d 100644 --- a/client/components/deposits-overview/recent-deposits-list.tsx +++ b/client/components/deposits-overview/recent-deposits-list.tsx @@ -21,7 +21,7 @@ import './style.scss'; import DepositStatusChip from 'components/deposit-status-chip'; import { getDepositDate } from 'deposits/utils'; import { CachedDeposit } from 'wcpay/types/deposits'; -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import { getDetailsURL } from 'wcpay/components/details-link'; interface RecentDepositsProps { diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index de5b720715a..ab51a52d16e 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -8,7 +8,7 @@ import { createInterpolateElement } from '@wordpress/element'; * Internal dependencies */ import InlineNotice from 'wcpay/components/inline-notice'; -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { reasons } from 'wcpay/disputes/strings'; import { getDetailsURL } from 'wcpay/components/details-link'; import { diff --git a/client/components/payment-activity/payment-data-tile.tsx b/client/components/payment-activity/payment-data-tile.tsx index f9bbfbf7318..7c0d190d79f 100644 --- a/client/components/payment-activity/payment-data-tile.tsx +++ b/client/components/payment-activity/payment-data-tile.tsx @@ -9,7 +9,7 @@ import { recordEvent } from 'wcpay/tracks'; /** * Internal dependencies */ -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import Loadable from '../loadable'; import './style.scss'; diff --git a/client/components/welcome/currency-select.tsx b/client/components/welcome/currency-select.tsx index 7f82c412313..32c1e5a3cd9 100644 --- a/client/components/welcome/currency-select.tsx +++ b/client/components/welcome/currency-select.tsx @@ -8,7 +8,7 @@ import { decodeEntities } from '@wordpress/html-entities'; * Internal dependencies */ import { useSelectedCurrency } from 'overview/hooks'; -import { getCurrency } from 'utils/currency'; +import { getCurrency } from 'multi-currency/interface/functions'; import InlineLabelSelect from '../inline-label-select'; import { recordEvent } from 'tracks'; diff --git a/client/data/deposits/actions.js b/client/data/deposits/actions.js index 392b890657c..60b4479ab24 100644 --- a/client/data/deposits/actions.js +++ b/client/data/deposits/actions.js @@ -6,7 +6,7 @@ import { apiFetch } from '@wordpress/data-controls'; import { dispatch } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; -import { formatCurrency } from 'utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; /** * Internal Dependencies diff --git a/client/data/index.ts b/client/data/index.ts index d20938feb56..878ecdc11f6 100644 --- a/client/data/index.ts +++ b/client/data/index.ts @@ -18,7 +18,6 @@ export * from './charges/hooks'; export * from './timeline/hooks'; export * from './disputes/hooks'; export * from './settings/hooks'; -export * from './multi-currency'; export * from './card-readers/hooks'; export * from './capital/hooks'; export * from './documents/hooks'; diff --git a/client/data/multi-currency/index.js b/client/data/multi-currency/index.js deleted file mode 100644 index c524cca8b05..00000000000 --- a/client/data/multi-currency/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @format */ - -/** - * Internal dependencies - */ -import reducer from './reducer'; -import * as selectors from './selectors'; -import * as actions from './actions'; -import * as resolvers from './resolvers'; - -export { reducer, selectors, actions, resolvers }; -export * from './hooks'; diff --git a/client/data/store.js b/client/data/store.js index 974efdb1e07..c4387901c37 100644 --- a/client/data/store.js +++ b/client/data/store.js @@ -14,7 +14,6 @@ import * as charges from './charges'; import * as timeline from './timeline'; import * as disputes from './disputes'; import * as settings from './settings'; -import * as multiCurrency from './multi-currency'; import * as readers from './card-readers'; import * as capital from './capital'; import * as documents from './documents'; @@ -33,7 +32,6 @@ export const initStore = () => timeline: timeline.reducer, disputes: disputes.reducer, settings: settings.reducer, - multiCurrency: multiCurrency.reducer, readers: readers.reducer, capital: capital.reducer, documents: documents.reducer, @@ -49,7 +47,6 @@ export const initStore = () => ...timeline.actions, ...disputes.actions, ...settings.actions, - ...multiCurrency.actions, ...readers.actions, ...capital.actions, ...documents.actions, @@ -66,7 +63,6 @@ export const initStore = () => ...timeline.selectors, ...disputes.selectors, ...settings.selectors, - ...multiCurrency.selectors, ...readers.selectors, ...capital.selectors, ...documents.selectors, @@ -82,7 +78,6 @@ export const initStore = () => ...timeline.resolvers, ...disputes.resolvers, ...settings.resolvers, - ...multiCurrency.resolvers, ...readers.resolvers, ...capital.resolvers, ...documents.resolvers, diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 4d47ae51d6a..74aec3f2a7d 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -34,7 +34,10 @@ import Page from 'components/page'; import ErrorBoundary from 'components/error-boundary'; import { TestModeNotice } from 'components/test-mode-notice'; import InlineNotice from 'components/inline-notice'; -import { formatCurrency, formatExplicitCurrency } from 'utils/currency'; +import { + formatCurrency, + formatExplicitCurrency, +} from 'multi-currency/interface/functions'; import { displayStatus } from '../strings'; import './style.scss'; diff --git a/client/deposits/filters/index.js b/client/deposits/filters/index.js index 34cc4bbdf52..0d6d57afecc 100644 --- a/client/deposits/filters/index.js +++ b/client/deposits/filters/index.js @@ -8,7 +8,7 @@ import { getQuery } from '@woocommerce/navigation'; * Internal dependencies */ import { filters, advancedFilters } from './config'; -import { formatCurrencyName } from '../../utils/currency'; +import { formatCurrencyName } from 'multi-currency/interface/functions'; export const DepositsFilters = ( props ) => { const populateDepositCurrencies = ( filtersConfiguration ) => { diff --git a/client/deposits/instant-deposits/index.tsx b/client/deposits/instant-deposits/index.tsx index 683aecb03a3..64c49bc069f 100644 --- a/client/deposits/instant-deposits/index.tsx +++ b/client/deposits/instant-deposits/index.tsx @@ -12,7 +12,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import './style.scss'; -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import InstantDepositModal from './modal'; import { useInstantDeposit } from 'wcpay/data'; import type * as AccountOverview from 'wcpay/types/account-overview'; diff --git a/client/deposits/instant-deposits/modal.tsx b/client/deposits/instant-deposits/modal.tsx index 25b2783a348..e9d0f530aeb 100644 --- a/client/deposits/instant-deposits/modal.tsx +++ b/client/deposits/instant-deposits/modal.tsx @@ -11,7 +11,10 @@ import { createInterpolateElement } from '@wordpress/element'; /** * Internal dependencies */ -import { formatCurrency, formatExplicitCurrency } from 'utils/currency'; +import { + formatCurrency, + formatExplicitCurrency, +} from 'multi-currency/interface/functions'; import type * as AccountOverview from 'wcpay/types/account-overview'; import './style.scss'; diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 21c48d2f631..03789c466e1 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -26,7 +26,10 @@ import { useDispatch } from '@wordpress/data'; import { useDeposits, useDepositsSummary } from 'wcpay/data'; import { useReportingExportLanguage } from 'data/index'; import { displayType, displayStatus } from '../strings'; -import { formatExplicitCurrency, formatExportAmount } from 'utils/currency'; +import { + formatExplicitCurrency, + formatExportAmount, +} from 'multi-currency/interface/functions'; import DetailsLink, { getDetailsURL } from 'components/details-link'; import ClickableCell from 'components/clickable-cell'; import Page from '../../components/page'; diff --git a/client/disputes/filters/index.tsx b/client/disputes/filters/index.tsx index 6e8288f2611..0f6fc09069f 100644 --- a/client/disputes/filters/index.tsx +++ b/client/disputes/filters/index.tsx @@ -9,7 +9,7 @@ import { getQuery } from '@woocommerce/navigation'; * Internal dependencies */ import { filters, advancedFilters, DisputesFilterType } from './config'; -import { formatCurrencyName } from '../../utils/currency'; +import { formatCurrencyName } from 'multi-currency/interface/functions'; interface DisputesFiltersProps { storeCurrencies?: string[]; diff --git a/client/disputes/filters/test/index.tsx b/client/disputes/filters/test/index.tsx index c122ba7eb3a..015f28aec19 100644 --- a/client/disputes/filters/test/index.tsx +++ b/client/disputes/filters/test/index.tsx @@ -12,7 +12,7 @@ import { getQuery, updateQueryString } from '@woocommerce/navigation'; * Internal dependencies */ import { DisputesFilters } from '../'; -import { formatCurrencyName } from '../../../utils/currency'; +import { formatCurrencyName } from 'multi-currency/interface/functions'; // TODO: this is a bit of a hack as we're mocking an old version of WC, we should relook at this. jest.mock( '@woocommerce/settings', () => ( { diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 0832ee5515b..060afccce35 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -37,7 +37,10 @@ import Page from 'components/page'; import { TestModeNotice } from 'components/test-mode-notice'; import { reasons } from './strings'; import { formatStringValue } from 'utils'; -import { formatExplicitCurrency, formatExportAmount } from 'utils/currency'; +import { + formatExplicitCurrency, + formatExportAmount, +} from 'multi-currency/interface/functions'; import DisputesFilters from './filters'; import DownloadButton from 'components/download-button'; import disputeStatusMapping from 'components/dispute-status-chip/mappings'; diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index 7aa20ca6174..12f7ba0e64a 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -16,7 +16,7 @@ import OrderLink from 'components/order-link'; import { getDetailsURL } from 'components/details-link'; import { reasons } from '../strings'; import { formatStringValue } from 'utils'; -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import './style.scss'; import Loadable from 'components/loadable'; import { Dispute } from 'wcpay/types/disputes'; diff --git a/client/disputes/utils.ts b/client/disputes/utils.ts index e96251c7863..96a1ca40129 100644 --- a/client/disputes/utils.ts +++ b/client/disputes/utils.ts @@ -18,7 +18,10 @@ import { disputeAwaitingResponseStatuses, disputeUnderReviewStatuses, } from 'wcpay/disputes/filters/config'; -import { formatCurrency, formatExplicitCurrency } from 'wcpay/utils/currency'; +import { + formatCurrency, + formatExplicitCurrency, +} from 'multi-currency/interface/functions'; interface IsDueWithinProps { dueBy: CachedDispute[ 'due_by' ] | EvidenceDetails[ 'due_by' ]; diff --git a/client/index.js b/client/index.js index 12d96efc666..11083acffac 100644 --- a/client/index.js +++ b/client/index.js @@ -23,7 +23,7 @@ import DisputesPage from 'disputes'; import RedirectToTransactionDetails from 'disputes/redirect-to-transaction-details'; import DisputeEvidencePage from 'disputes/evidence'; import AdditionalMethodsPage from 'wcpay/additional-methods-setup'; -import MultiCurrencySetupPage from 'wcpay/multi-currency-setup'; +import { MultiCurrencySetupPage } from 'multi-currency/interface/components'; import CardReadersPage from 'card-readers'; import CapitalPage from 'capital'; import OverviewPage from 'overview'; diff --git a/client/multi-currency-setup/wizard/task-item.scss b/client/multi-currency-setup/wizard/task-item.scss deleted file mode 100644 index 9751a378ab6..00000000000 --- a/client/multi-currency-setup/wizard/task-item.scss +++ /dev/null @@ -1,17 +0,0 @@ -.wcpay-wizard-task { - &__visible-description-element { - position: absolute; - margin-left: 40px; - margin-top: 0; - margin-bottom: 1em; - - &.is-muted-color { - color: $gray-700; - } - - .components-external-link svg { - width: 1em; - height: 1em; - } - } -} diff --git a/client/multi-currency-setup/wizard/task-item.tsx b/client/multi-currency-setup/wizard/task-item.tsx deleted file mode 100644 index 93711e4c80f..00000000000 --- a/client/multi-currency-setup/wizard/task-item.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/** - * External dependencies - */ -import React, { useContext } from 'react'; -import classNames from 'classnames'; -import { Icon, check } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import WizardTaskContext from '../../additional-methods-setup/wizard/task/context'; -import './task-item.scss'; - -interface WizardTaskItemProps { - children: React.ReactNode; - title: string; - index: number; - visibleDescription: string; - className?: string; -} - -const WizardTaskItem: React.FC< WizardTaskItemProps > = ( { - children, - title, - index, - visibleDescription, - className, -} ) => { - const { isCompleted, isActive } = useContext( WizardTaskContext ); - - return ( -
  • -
    -
    -
    -
    - { index } -
    - -
    - { title } -
    - { visibleDescription && ! isActive && ( - - { visibleDescription } - - ) } -
    { children }
    -
  • - ); -}; - -export default WizardTaskItem; diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index fdf48c03d78..235b92696b9 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -11,7 +11,7 @@ import { getHistory } from '@woocommerce/navigation'; */ import type { TaskItemProps } from '../types'; import type { CachedDispute } from 'wcpay/types/disputes'; -import { formatCurrency } from 'wcpay/utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import { getAdminUrl } from 'wcpay/utils'; import { recordEvent } from 'tracks'; import { isDueWithin } from 'wcpay/disputes/utils'; diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index ccc0764f38b..01f87431274 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -16,7 +16,7 @@ import HelpOutlineIcon from 'gridicons/dist/help-outline'; */ import type { Dispute } from 'wcpay/types/disputes'; import { ChargeBillingDetails } from 'wcpay/types/charges'; -import { formatExplicitCurrency } from 'utils/currency'; +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'; diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index ac6dada265e..0a43cb223e0 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -14,7 +14,7 @@ import { dateI18n } from '@wordpress/date'; */ import type { Dispute } from 'wcpay/types/disputes'; import { HorizontalList } from 'wcpay/components/horizontal-list'; -import { formatExplicitCurrency } from 'wcpay/utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { reasons } from 'wcpay/disputes/strings'; import { formatStringValue } from 'wcpay/utils'; import { ClickTooltip } from 'wcpay/components/tooltip'; diff --git a/client/payment-details/readers/index.js b/client/payment-details/readers/index.js index 9ff428c94fe..193ee236288 100644 --- a/client/payment-details/readers/index.js +++ b/client/payment-details/readers/index.js @@ -19,7 +19,10 @@ import { useCardReaderStats } from 'wcpay/data'; import { TestModeNotice } from 'components/test-mode-notice'; import Page from 'components/page'; import DownloadButton from 'components/download-button'; -import { formatExplicitCurrency, formatExportAmount } from 'utils/currency'; +import { + formatExplicitCurrency, + formatExportAmount, +} from 'multi-currency/interface/functions'; const PaymentCardReaderChargeDetails = ( props ) => { const { readers, chargeError, isLoading } = useCardReaderStats( diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 4350d609cd7..4e9d9559a4b 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -37,7 +37,10 @@ import { HorizontalList, HorizontalListItem } from 'components/horizontal-list'; import Loadable, { LoadableBlock } from 'components/loadable'; import riskMappings from 'components/risk-level/strings'; import OrderLink from 'components/order-link'; -import { formatCurrency, formatExplicitCurrency } from 'utils/currency'; +import { + formatCurrency, + formatExplicitCurrency, +} from 'multi-currency/interface/functions'; import CustomerLink from 'components/customer-link'; import { ClickTooltip } from 'components/tooltip'; import DisputeStatusChip from 'components/dispute-status-chip'; diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index 1cb14d6aa7a..64bc74d91d2 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -26,7 +26,7 @@ import { formatCurrency, formatFX, formatExplicitCurrency, -} from 'utils/currency'; +} from 'multi-currency/interface/functions'; import { formatFee } from 'utils/fees'; import { getAdminUrl } from 'wcpay/utils'; import { ShieldIcon } from 'wcpay/icons'; diff --git a/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx b/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx index e32b2222d2e..828d1bbf5c8 100644 --- a/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx +++ b/client/settings/fraud-protection/advanced-settings/cards/purchase-price-threshold.tsx @@ -8,7 +8,7 @@ import AmountInput from 'wcpay/components/amount-input'; /** * Internal dependencies */ -import { getCurrency } from 'utils/currency'; +import { getCurrency } from 'multi-currency/interface/functions'; import FraudProtectionRuleCard from '../rule-card'; import FraudProtectionRuleToggle from '../rule-toggle'; import FraudProtectionRuleDescription from '../rule-description'; diff --git a/client/settings/fraud-protection/test/index.test.tsx b/client/settings/fraud-protection/test/index.test.tsx index 0719a904c78..1b78011aad4 100644 --- a/client/settings/fraud-protection/test/index.test.tsx +++ b/client/settings/fraud-protection/test/index.test.tsx @@ -11,7 +11,6 @@ import { useDispatch } from '@wordpress/data'; import FraudProtection from '..'; import { useCurrentProtectionLevel, - useCurrencies, useAdvancedFraudProtectionSettings, useSettings, } from 'wcpay/data'; @@ -29,7 +28,6 @@ jest.mock( 'wcpay/data', () => ( { useAdvancedFraudProtectionSettings: jest.fn(), useCurrentProtectionLevel: jest.fn(), useSettings: jest.fn(), - useCurrencies: jest.fn(), } ) ); jest.mock( '@wordpress/data', () => ( { @@ -44,10 +42,6 @@ const mockUseCurrentProtectionLevel = useCurrentProtectionLevel as jest.MockedFu () => [ string, ( level: string ) => void ] >; -const mockUseCurrencies = useCurrencies as jest.MockedFunction< - () => { currencies: Record< string, any >; isLoading: boolean } ->; - const mockUseAdvancedFraudProtectionSettings = useAdvancedFraudProtectionSettings as jest.MockedFunction< () => [ any[] | string, ( settings: string ) => void ] >; @@ -70,16 +64,6 @@ describe( 'FraudProtection', () => { 'standard', jest.fn(), ] ); - mockUseCurrencies.mockReturnValue( { - isLoading: false, - currencies: { - available: { - EUR: { name: 'Euro', symbol: '€' }, - USD: { name: 'US Dollar', symbol: '$' }, - PLN: { name: 'Polish zÅ‚oty', symbol: 'zÅ‚' }, - }, - }, - } ); mockUseAdvancedFraudProtectionSettings.mockReturnValue( [ [], diff --git a/client/settings/payment-methods-list/index.js b/client/settings/payment-methods-list/index.js index 99a34b40daf..997c2bb9874 100644 --- a/client/settings/payment-methods-list/index.js +++ b/client/settings/payment-methods-list/index.js @@ -22,7 +22,7 @@ import { upeCapabilityStatuses } from 'wcpay/additional-methods-setup/constants' import ConfirmPaymentMethodActivationModal from './activation-modal'; import ConfirmPaymentMethodDeleteModal from './delete-modal'; import CapabilityRequestNotice from './capability-request'; -import { getMissingCurrenciesTooltipMessage } from 'wcpay/multi-currency/missing-currencies-message'; +import { getMissingCurrenciesTooltipMessage } from 'multi-currency/interface/functions'; const PaymentMethodsList = ( { methodIds } ) => { const [ enabledMethodIds ] = useEnabledPaymentMethodIds(); diff --git a/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js b/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js index 2e1f1da1538..d64f2ded639 100644 --- a/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js +++ b/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js @@ -35,8 +35,6 @@ jest.mock( '@woocommerce/components', () => { jest.mock( 'wcpay/data', () => ( { useEnabledPaymentMethodIds: jest.fn(), useGetAvailablePaymentMethodIds: jest.fn(), - useCurrencies: jest.fn().mockReturnValue( { isLoading: true } ), - useEnabledCurrencies: jest.fn().mockReturnValue( {} ), useGetPaymentMethodStatuses: jest.fn().mockReturnValue( {} ), useManualCapture: jest.fn(), useSelectedPaymentMethod: jest.fn(), @@ -45,6 +43,16 @@ jest.mock( 'wcpay/data', () => ( { useSettings: jest.fn().mockReturnValue( { isLoading: false } ), } ) ); +jest.mock( 'multi-currency/interface/data', () => ( { + useCurrencies: jest.fn().mockReturnValue( { isLoading: true } ), + useEnabledCurrencies: jest.fn().mockReturnValue( {} ), +} ) ); + +jest.mock( 'multi-currency/interface/data', () => ( { + useCurrencies: jest.fn().mockReturnValue( { isLoading: true } ), + useEnabledCurrencies: jest.fn().mockReturnValue( {} ), +} ) ); + jest.mock( '@wordpress/data', () => ( { useDispatch: jest .fn() diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 3ecf1554d06..1d75e407cea 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -10,7 +10,7 @@ import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; /** * Internal dependencies */ -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; import { getDetailsURL } from '../../components/details-link'; diff --git a/client/transactions/blocked/index.tsx b/client/transactions/blocked/index.tsx index e1bdce63664..f6153231f69 100644 --- a/client/transactions/blocked/index.tsx +++ b/client/transactions/blocked/index.tsx @@ -34,7 +34,7 @@ import { getBlockedListColumns, getBlockedListColumnsStructure, } from './columns'; -import { formatExplicitCurrency } from '../../utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import autocompleter from '../fraud-protection/autocompleter'; import DownloadButton from '../../components/download-button'; import { getFraudOutcomeTransactionsExport } from '../../data/transactions/resolvers'; diff --git a/client/transactions/filters/index.tsx b/client/transactions/filters/index.tsx index cbb729ce5d9..ef8c11d4f15 100644 --- a/client/transactions/filters/index.tsx +++ b/client/transactions/filters/index.tsx @@ -9,7 +9,7 @@ import { getQuery } from '@woocommerce/navigation'; * Internal dependencies */ import { getFilters, getAdvancedFilters } from './config'; -import { formatCurrencyName } from '../../utils/currency'; +import { formatCurrencyName } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; interface TransactionsFiltersProps { diff --git a/client/transactions/list/converted-amount.tsx b/client/transactions/list/converted-amount.tsx index 6a74b01a25a..fac4d9acd25 100644 --- a/client/transactions/list/converted-amount.tsx +++ b/client/transactions/list/converted-amount.tsx @@ -12,7 +12,7 @@ import classNames from 'classnames'; /** * Internal dependencies */ -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; declare const window: any; diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 3893df6658a..e123d0319f5 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -54,7 +54,7 @@ import { formatCurrency, formatExplicitCurrency, formatExportAmount, -} from 'utils/currency'; +} from 'multi-currency/interface/functions'; import { getChargeChannel } from 'utils/charge'; import Deposit from './deposit'; import ConvertedAmount from './converted-amount'; diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index 59db13c52c8..d7f5de95111 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -13,7 +13,7 @@ import { Button } from '@wordpress/components'; */ import { getDetailsURL } from 'components/details-link'; import ClickableCell from 'components/clickable-cell'; -import { formatExplicitCurrency } from 'utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; diff --git a/client/transactions/risk-review/index.tsx b/client/transactions/risk-review/index.tsx index 0681110d678..f83010dc1e8 100644 --- a/client/transactions/risk-review/index.tsx +++ b/client/transactions/risk-review/index.tsx @@ -35,7 +35,7 @@ import { getRiskReviewListColumnsStructure, } from './columns'; import './style.scss'; -import { formatExplicitCurrency } from '../../utils/currency'; +import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import autocompleter from '../fraud-protection/autocompleter'; import DownloadButton from '../../components/download-button'; import { getFraudOutcomeTransactionsExport } from '../../data/transactions/resolvers'; diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 3cf14c956f7..17058760c19 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -17,7 +17,7 @@ import { useAuthorizations, useAuthorizationsSummary } from 'data/index'; import Page from '../../components/page'; import { getDetailsURL } from 'components/details-link'; import ClickableCell from 'components/clickable-cell'; -import { formatExplicitCurrency } from 'utils/currency'; +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'; diff --git a/client/utils/account-fees.tsx b/client/utils/account-fees.tsx index b3d0f5b9f2c..711d3d337ed 100644 --- a/client/utils/account-fees.tsx +++ b/client/utils/account-fees.tsx @@ -1,7 +1,7 @@ /** @format */ /** - * External depencencies + * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; import interpolateComponents from '@automattic/interpolate-components'; @@ -10,7 +10,7 @@ import './account-fees.scss'; /** * Internal dependencies */ -import { formatCurrency } from 'utils/currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import { formatFee } from 'utils/fees'; import React from 'react'; import { BaseFee, DiscountFee, FeeStructure } from 'wcpay/types/fees'; diff --git a/client/utils/test/account-fees.tsx b/client/utils/test/account-fees.tsx index c1b181ac160..3184e42d644 100644 --- a/client/utils/test/account-fees.tsx +++ b/client/utils/test/account-fees.tsx @@ -14,10 +14,10 @@ import { formatMethodFeesTooltip, getCurrentBaseFee, } from '../account-fees'; -import { formatCurrency } from '../currency'; +import { formatCurrency } from 'multi-currency/interface/functions'; import { BaseFee, DiscountFee, FeeStructure } from 'wcpay/types/fees'; -jest.mock( '../currency', () => ( { +jest.mock( 'multi-currency/interface/functions', () => ( { formatCurrency: jest.fn( ( amount: number ): string => { return sprintf( '$%.2f', amount / 100 ); } ), diff --git a/client/vat/form/tasks/company-data-task.tsx b/client/vat/form/tasks/company-data-task.tsx index d2cac07cc67..76c91b7e9ed 100644 --- a/client/vat/form/tasks/company-data-task.tsx +++ b/client/vat/form/tasks/company-data-task.tsx @@ -11,14 +11,14 @@ import { } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import React, { useContext, useEffect, useState } from 'react'; -import CollapsibleBody from 'wcpay/additional-methods-setup/wizard/collapsible-body'; -import WizardTaskItem from 'wcpay/additional-methods-setup/wizard/task-item'; -import WizardTaskContext from 'wcpay/additional-methods-setup/wizard/task/context'; import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies */ +import CollapsibleBody from 'wcpay/additional-methods-setup/wizard/collapsible-body'; +import WizardTaskItem from 'wcpay/additional-methods-setup/wizard/task-item'; +import WizardTaskContext from 'wcpay/additional-methods-setup/wizard/task/context'; import { VatError, VatFormOnCompleted, diff --git a/client/vat/form/tasks/vat-number-task.tsx b/client/vat/form/tasks/vat-number-task.tsx index 611e214318d..7fad346de00 100644 --- a/client/vat/form/tasks/vat-number-task.tsx +++ b/client/vat/form/tasks/vat-number-task.tsx @@ -11,14 +11,14 @@ import { } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import React, { useContext, useState } from 'react'; -import CollapsibleBody from 'wcpay/additional-methods-setup/wizard/collapsible-body'; -import WizardTaskItem from 'wcpay/additional-methods-setup/wizard/task-item'; -import WizardTaskContext from 'wcpay/additional-methods-setup/wizard/task/context'; import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies */ +import CollapsibleBody from 'wcpay/additional-methods-setup/wizard/collapsible-body'; +import WizardTaskItem from 'wcpay/additional-methods-setup/wizard/task-item'; +import WizardTaskContext from 'wcpay/additional-methods-setup/wizard/task/context'; import { VatError, VatFormOnCompleted, VatValidationResult } from '../../types'; import '../style.scss'; diff --git a/composer.json b/composer.json index cdb679b4afb..497530e36e1 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ }, "autoload": { "psr-4": { - "WCPay\\MultiCurrency\\": "includes/multi-currency", + "WCPay\\MultiCurrency\\": "multi-currency/src", "WCPay\\Vendor\\": "lib/packages", "WCPay\\": "src" }, diff --git a/includes/class-database-cache.php b/includes/class-database-cache.php index 68b4c7d4d89..042d99f7f9e 100644 --- a/includes/class-database-cache.php +++ b/includes/class-database-cache.php @@ -7,16 +7,17 @@ namespace WCPay; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyCacheInterface; + defined( 'ABSPATH' ) || exit; // block direct access. /** * A class for caching data as an option in the database. */ -class Database_Cache { +class Database_Cache implements MultiCurrencyCacheInterface { const ACCOUNT_KEY = 'wcpay_account_data'; const ONBOARDING_FIELDS_DATA_KEY = 'wcpay_onboarding_fields_data'; const BUSINESS_TYPES_KEY = 'wcpay_business_types_data'; - const CURRENCIES_KEY = 'wcpay_multi_currency_cached_currencies'; const PAYMENT_PROCESS_FACTORS_KEY = 'wcpay_payment_process_factors'; const FRAUD_SERVICES_KEY = 'wcpay_fraud_services_data'; diff --git a/includes/class-wc-payments-account.php b/includes/class-wc-payments-account.php index f50acfc5b35..93fab3929d7 100644 --- a/includes/class-wc-payments-account.php +++ b/includes/class-wc-payments-account.php @@ -17,11 +17,12 @@ use WCPay\Exceptions\API_Exception; use WCPay\Logger; use WCPay\Database_Cache; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; /** * Class handling any account connection functionality */ -class WC_Payments_Account { +class WC_Payments_Account implements MultiCurrencyAccountInterface { // ACCOUNT_OPTION is only used in the supporting dev tools plugin, it can be removed once everyone has upgraded. const ACCOUNT_OPTION = 'wcpay_account_data'; @@ -171,6 +172,18 @@ public function get_publishable_key( $is_test ) { return $account['live_publishable_key']; } + /** + * Checks if the account is connected to the payment provider. + * Note: This method is a proxy for `is_stripe_connected` for the MultiCurrencyAccountInterface. + * + * @param bool $on_error Value to return on server error, defaults to false. + * + * @return bool True if the account is connected, false otherwise, $on_error on error. + */ + public function is_provider_connected( bool $on_error = false ): bool { + return $this->is_stripe_connected( $on_error ); + } + /** * Determine if the store has a working Jetpack connection. * @@ -636,11 +649,22 @@ public function get_account_email(): string { * * @return array Currencies. */ - public function get_account_customer_supported_currencies() { + public function get_account_customer_supported_currencies(): array { $account = $this->get_cached_account_data(); return ! empty( $account ) && isset( $account['customer_currencies']['supported'] ) ? $account['customer_currencies']['supported'] : []; } + /** + * List of countries enabled for Stripe platform account. See also this URL: + * https://woocommerce.com/document/woopayments/compatibility/countries/#supported-countries + * + * @return array + */ + public function get_supported_countries(): array { + // This is a wrapper function because of the MultiCurrencyAccountInterface. + return WC_Payments_Utils::supported_countries(); + } + /** * Gets the account live mode value. * @@ -1628,6 +1652,21 @@ private function get_login_url() { ); } + /** + * Get provider onboarding page url. + * + * @return string + */ + public function get_provider_onboarding_page_url(): string { + return add_query_arg( + [ + 'page' => 'wc-admin', + 'path' => '/payments/connect', + ], + admin_url( 'admin.php' ) + ); + } + /** * Get connect url. * @@ -1669,21 +1708,6 @@ public static function get_payments_task_page_url() { ); } - /** - * Get Connect page url. - * - * @return string - */ - public static function get_connect_page_url(): string { - return add_query_arg( - [ - 'page' => 'wc-admin', - 'path' => '/payments/connect', - ], - admin_url( 'admin.php' ) - ); - } - /** * Get overview page url * diff --git a/includes/class-wc-payments-explicit-price-formatter.php b/includes/class-wc-payments-explicit-price-formatter.php index d96bfa4f25f..31c5364cfbe 100644 --- a/includes/class-wc-payments-explicit-price-formatter.php +++ b/includes/class-wc-payments-explicit-price-formatter.php @@ -6,7 +6,6 @@ */ use WCPay\MultiCurrency\MultiCurrency; -use WCPay\Logger; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. diff --git a/includes/class-wc-payments-localization-service.php b/includes/class-wc-payments-localization-service.php index 5ea9375429d..c0c0d7d84f8 100644 --- a/includes/class-wc-payments-localization-service.php +++ b/includes/class-wc-payments-localization-service.php @@ -5,12 +5,14 @@ * @package WooCommerce\Payments */ +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; + defined( 'ABSPATH' ) || exit; /** * WC_Payments_Localization_Service. */ -class WC_Payments_Localization_Service { +class WC_Payments_Localization_Service implements MultiCurrencyLocalizationInterface { const WCPAY_CURRENCY_FORMAT_TRANSIENT = 'wcpay_currency_format'; const WCPAY_LOCALE_INFO_TRANSIENT = 'wcpay_locale_info'; diff --git a/includes/class-wc-payments-settings-service.php b/includes/class-wc-payments-settings-service.php new file mode 100644 index 00000000000..5da12e8a897 --- /dev/null +++ b/includes/class-wc-payments-settings-service.php @@ -0,0 +1,42 @@ +is_dev(); + } + + /** + * Gets the plugin file path. + * + * @return string + */ + public function get_plugin_file_path(): string { + return WCPAY_PLUGIN_FILE; + } + + /** + * Gets the plugin version. + * + * @return string + */ + public function get_plugin_version(): string { + return WCPAY_VERSION_NUMBER; + } +} diff --git a/includes/class-wc-payments-utils.php b/includes/class-wc-payments-utils.php index 755901e15ca..0b2506d73b6 100644 --- a/includes/class-wc-payments-utils.php +++ b/includes/class-wc-payments-utils.php @@ -1270,24 +1270,6 @@ public static function is_store_api_request(): bool { return false; } - /** - * Returns true if the request that's currently being processed is a Store API batch request, false - * otherwise. - * - * @return bool True if the request is a Store API batch request, false otherwise. - */ - public static function is_store_batch_request(): bool { - if ( isset( $_REQUEST['rest_route'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification - $rest_route = sanitize_text_field( $_REQUEST['rest_route'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.NonceVerification - } else { - $url_parts = wp_parse_url( esc_url_raw( $_SERVER['REQUEST_URI'] ?? '' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash - $request_path = $url_parts ? rtrim( $url_parts['path'], '/' ) : ''; - $rest_route = str_replace( trailingslashit( rest_get_url_prefix() ), '', $request_path ); - } - - return 1 === preg_match( '@^\/wc\/store(\/v[\d]+)?\/batch@', $rest_route ); - } - /** * Gets the current active theme transient for a given location * Falls back to 'stripe' if no transients are set. diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 42923e906d5..7897a3c7752 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -42,6 +42,7 @@ use WCPay\WooPay\WooPay_Session; use WCPay\Compatibility_Service; use WCPay\Duplicates_Detection_Service; +use WCPay\WC_Payments_Currency_Manager; /** * Main class for the WooPayments extension. Its responsibility is to initialize the extension. @@ -124,6 +125,13 @@ class WC_Payments { */ private static $localization_service; + /** + * Instance of WC_Payments_Settings_Service, created in init function + * + * @var WC_Payments_Settings_Service + */ + private static $settings_service; + /** * Instance of WC_Payments_Dependency_Service, created in init function * @@ -299,6 +307,13 @@ class WC_Payments { */ private static $duplicates_detection_service; + /** + * Instance of WC_Payments_Currency_Manager, created in init function + * + * @var WC_Payments_Currency_Manager + */ + private static $currency_manager; + /** * Entry point to the initialization logic. */ @@ -468,6 +483,7 @@ public static function init() { include_once __DIR__ . '/class-wc-payments-onboarding-service.php'; include_once __DIR__ . '/class-experimental-abtest.php'; include_once __DIR__ . '/class-wc-payments-localization-service.php'; + include_once __DIR__ . '/class-wc-payments-settings-service.php'; include_once __DIR__ . '/in-person-payments/class-wc-payments-in-person-payments-receipts-service.php'; include_once __DIR__ . '/class-wc-payments-order-service.php'; include_once __DIR__ . '/class-wc-payments-order-success-page.php'; @@ -489,7 +505,8 @@ public static function init() { include_once __DIR__ . '/class-duplicate-payment-prevention-service.php'; include_once __DIR__ . '/class-wc-payments-incentives-service.php'; include_once __DIR__ . '/class-compatibility-service.php'; - include_once __DIR__ . '/multi-currency/wc-payments-multi-currency.php'; + include_once __DIR__ . '/compat/multi-currency/wc-payments-multi-currency.php'; + include_once __DIR__ . '/compat/multi-currency/class-wc-payments-currency-manager.php'; include_once __DIR__ . '/class-duplicates-detection-service.php'; self::$woopay_checkout_service = new Checkout_Service(); @@ -520,6 +537,7 @@ public static function init() { self::$fraud_service = new WC_Payments_Fraud_Service( self::$api_client, self::$customer_service, self::$account, self::$session_service, self::$database_cache ); self::$in_person_payments_receipts_service = new WC_Payments_In_Person_Payments_Receipts_Service(); self::$localization_service = new WC_Payments_Localization_Service(); + self::$settings_service = new WC_Payments_Settings_Service(); self::$failed_transaction_rate_limiter = new Session_Rate_Limiter( Session_Rate_Limiter::SESSION_KEY_DECLINED_CARD_REGISTRY, 5, 10 * MINUTE_IN_SECONDS ); self::$order_success_page = new WC_Payments_Order_Success_Page(); self::$woopay_util = new WooPay_Utilities(); @@ -582,6 +600,9 @@ public static function init() { self::$customer_service_api = new WC_Payments_Customer_Service_API( self::$customer_service ); + self::$currency_manager = new WC_Payments_Currency_Manager( self::get_gateway() ); + self::$currency_manager->init_hooks(); + // Only register hooks of the new `src` service with the same feature of Duplicate_Payment_Prevention_Service. // To avoid register the same hooks twice. wcpay_get_container()->get( \WCPay\Internal\Service\DuplicatePaymentPreventionService::class )->init_hooks(); @@ -1323,6 +1344,15 @@ public static function get_localization_service() { return self::$localization_service; } + /** + * Returns the WC_Payments_Settings_Service + * + * @return WC_Payments_Settings_Service Localization Service instance + */ + public static function get_settings_service() { + return self::$settings_service; + } + /** * Returns the WC_Payments_Action_Scheduler_Service * diff --git a/includes/multi-currency/PaymentMethodsCompatibility.php b/includes/compat/multi-currency/class-wc-payments-currency-manager.php similarity index 69% rename from includes/multi-currency/PaymentMethodsCompatibility.php rename to includes/compat/multi-currency/class-wc-payments-currency-manager.php index 0c5f7d4b736..c00da0d1a69 100644 --- a/includes/multi-currency/PaymentMethodsCompatibility.php +++ b/includes/compat/multi-currency/class-wc-payments-currency-manager.php @@ -1,29 +1,21 @@ multi_currency = $multi_currency; - $this->gateway = $gateway; + public function __construct( WC_Payment_Gateway_WCPay $gateway ) { + $this->gateway = $gateway; } /** @@ -48,17 +38,32 @@ public function __construct( MultiCurrency $multi_currency, WC_Payment_Gateway_W * @return void */ public function init_hooks() { - add_action( - 'update_option_woocommerce_woocommerce_payments_settings', - [ $this, 'add_missing_currencies' ] - ); + add_action( 'update_option_woocommerce_woocommerce_payments_settings', [ $this, 'maybe_add_missing_currencies' ] ); add_action( 'admin_head', [ $this, 'add_payment_method_currency_dependencies_script' ] ); } + /** + * Gets the multi-currency instance or returns null if it's not available. + * This method allows for easier testing by allowing the multi-currency instance to be mocked. + * + * @return \WCPay\MultiCurrency\MultiCurrency|null + */ + public function get_multi_currency_instance() { + if ( ! function_exists( 'WC_Payments_Multi_Currency' ) ) { + return null; + } + + if ( ! WC_Payments_Multi_Currency()->is_initialized() ) { + return null; + } + + return WC_Payments_Multi_Currency(); + } + /** * Returns the currencies needed per enabled payment method * - * @return array The currencies keyed with the related payment method + * @return array The currencies keyed with the related payment method */ public function get_enabled_payment_method_currencies() { $enabled_payment_method_ids = $this->gateway->get_upe_enabled_payment_method_ids(); @@ -97,14 +102,19 @@ function ( $result, $method ) use ( $account_currency ) { /** * Ensures that when a payment method is added from the settings, the needed currency is also added. */ - public function add_missing_currencies() { + public function maybe_add_missing_currencies() { + $multi_currency = $this->get_multi_currency_instance(); + if ( is_null( $multi_currency ) ) { + return; + } + $payment_methods_needing_currency = $this->get_enabled_payment_method_currencies(); if ( empty( $payment_methods_needing_currency ) ) { return; } - $enabled_currencies = $this->multi_currency->get_enabled_currencies(); - $available_currencies = $this->multi_currency->get_available_currencies(); + $enabled_currencies = $multi_currency->get_enabled_currencies(); + $available_currencies = $multi_currency->get_available_currencies(); $missing_currency_codes = []; @@ -137,15 +147,21 @@ public function add_missing_currencies() { * The set_enabled_currencies method throws an exception if any currencies passed are not found in the current available currencies. * Any currencies not found are filtered out above, so we shouldn't need a try/catch here. */ - $this->multi_currency->set_enabled_currencies( array_merge( array_keys( $enabled_currencies ), $missing_currency_codes ) ); + $multi_currency->set_enabled_currencies( array_merge( array_keys( $enabled_currencies ), $missing_currency_codes ) ); } /** - * Adds the notices for currencies that are bound to an UPE payment method. + * Adds the `multiCurrencyPaymentMethodsMap` JS object to the multi-currency settings page. * - * @return void + * This object maps currencies to payment methods that require them, so the multi-currency settings page displays a notice in case of dependencies. */ public function add_payment_method_currency_dependencies_script() { + $multi_currency = $this->get_multi_currency_instance(); + + if ( is_null( $multi_currency ) || ! $multi_currency->is_multi_currency_settings_page() ) { + return; + } + $payment_methods_needing_currency = $this->get_enabled_payment_method_currencies(); if ( empty( $payment_methods_needing_currency ) ) { return; @@ -161,11 +177,10 @@ public function add_payment_method_currency_dependencies_script() { } } - if ( WC_Payments_Multi_Currency()->is_multi_currency_settings_page() ) : ?> + ?> - init_hooks(); + } + + return $instance; } add_action( 'plugins_loaded', 'WC_Payments_Multi_Currency', 12 ); diff --git a/includes/multi-currency/CountryFlags.php b/includes/multi-currency/CountryFlags.php deleted file mode 100644 index e841095255e..00000000000 --- a/includes/multi-currency/CountryFlags.php +++ /dev/null @@ -1,304 +0,0 @@ - '🇦🇩', - Country_Code::UNITED_ARAB_EMIRATES => '🇦🇪', - Country_Code::AFGHANISTAN => '🇦🇫', - Country_Code::ANTIGUA_AND_BARBUDA => '🇦🇬', - Country_Code::ANGUILLA => '🇦🇮', - Country_Code::ALBANIA => '🇦🇱', - Country_Code::ARMENIA => '🇦🇲', - Country_Code::ANGOLA => '🇦🇴', - Country_Code::ANTARCTICA => '🇦🇶', - Country_Code::ARGENTINA => '🇦🇷', - Country_Code::AMERICAN_SAMOA => '🇦🇸', - Country_Code::AUSTRIA => '🇦🇹', - Country_Code::AUSTRALIA => '🇦🇺', - Country_Code::ARUBA => '🇦🇼', - Country_Code::ALAND_ISLANDS => '🇦🇽', - Country_Code::AZERBAIJAN => '🇦🇿', - Country_Code::BOSNIA_AND_HERZEGOVINA => '🇧🇦', - Country_Code::BARBADOS => '🇧🇧', - Country_Code::BANGLADESH => '🇧🇩', - Country_Code::BELGIUM => '🇧🇪', - Country_Code::BURKINA_FASO => '🇧🇫', - Country_Code::BULGARIA => '🇧🇬', - Country_Code::BAHRAIN => '🇧🇭', - Country_Code::BURUNDI => '🇧🇮', - Country_Code::BENIN => '🇧🇯', - Country_Code::SAINT_BARTHELEMY => '🇧🇱', - Country_Code::BERMUDA => '🇧🇲', - Country_Code::BRUNEI => '🇧🇳', - Country_Code::BOLIVIA => '🇧🇴', - Country_Code::CARIBBEAN_NETHERLANDS => '🇧🇶', - Country_Code::BRAZIL => '🇧🇷', - Country_Code::BAHAMAS => '🇧🇸', - Country_Code::BHUTAN => '🇧🇹', - Country_Code::BOUVET_ISLAND => '🇧🇻', - Country_Code::BOTSWANA => '🇧🇼', - Country_Code::BELARUS => '🇧🇾', - Country_Code::BELIZE => '🇧🇿', - Country_Code::CANADA => '🇨🇦', - Country_Code::COCOS_KEELING_ISLANDS => '🇨🇨', - Country_Code::DEMOCRATIC_REPUBLIC_OF_THE_CONGO => '🇨🇩', - Country_Code::CENTRAL_AFRICAN_REPUBLIC => '🇨🇫', - Country_Code::CONGO => '🇨🇬', - Country_Code::SWITZERLAND => '🇨🇭', - Country_Code::IVORY_COAST => '🇨🇮', - Country_Code::COOK_ISLANDS => '🇨🇰', - Country_Code::CHILE => '🇨🇱', - Country_Code::CAMEROON => '🇨🇲', - Country_Code::CHINA => '🇨🇳', - Country_Code::COLOMBIA => '🇨🇴', - Country_Code::COSTA_RICA => '🇨🇷', - Country_Code::CUBA => '🇨🇺', - Country_Code::CABO_VERDE => '🇨🇻', - 'CW' => '🇨🇼', - 'CX' => '🇨🇽', - Country_Code::CYPRUS => '🇨🇾', - Country_Code::CZECHIA => '🇨🇿', - Country_Code::GERMANY => '🇩🇪', - Country_Code::DJIBOUTI => '🇩🇯', - Country_Code::DENMARK => '🇩🇰', - Country_Code::DOMINICA => '🇩🇲', - Country_Code::DOMINICAN_REPUBLIC => '🇩🇴', - Country_Code::ALGERIA => '🇩🇿', - Country_Code::ECUADOR => '🇪🇨', - Country_Code::ESTONIA => '🇪🇪', - Country_Code::EGYPT => '🇪🇬', - 'EH' => '🇪🇭', - Country_Code::ERITREA => '🇪🇷', - Country_Code::SPAIN => '🇪🇸', - Country_Code::ETHIOPIA => '🇪🇹', - 'EU' => '🇪🇺', - Country_Code::FINLAND => '🇫🇮', - Country_Code::FIJI => '🇫🇯', - 'FK' => '🇫🇰', - Country_Code::MICRONESIA => '🇫🇲', - 'FO' => '🇫🇴', - Country_Code::FRANCE => '🇫🇷', - Country_Code::GABON => '🇬🇦', - Country_Code::UNITED_KINGDOM => '🇬🇧', - Country_Code::GRENADA => '🇬🇩', - Country_Code::GEORGIA => '🇬🇪', - 'GF' => '🇬🇫', - 'GG' => '🇬🇬', - Country_Code::GHANA => '🇬🇭', - Country_Code::GIBRALTAR => '🇬🇮', - 'GL' => '🇬🇱', - Country_Code::GAMBIA => '🇬🇲', - Country_Code::GUINEA => '🇬🇳', - 'GP' => '🇬🇵', - Country_Code::EQUATORIAL_GUINEA => '🇬🇶', - Country_Code::GREECE => '🇬🇷', - 'GS' => '🇬🇸', - Country_Code::GUATEMALA => '🇬🇹', - 'GU' => '🇬🇺', - Country_Code::GUINEA_BISSAU => '🇬🇼', - Country_Code::GUYANA => '🇬🇾', - Country_Code::HONG_KONG => '🇭🇰', - 'HM' => '🇭🇲', - Country_Code::HONDURAS => '🇭🇳', - Country_Code::CROATIA => '🇭🇷', - Country_Code::HAITI => '🇭🇹', - Country_Code::HUNGARY => '🇭🇺', - Country_Code::INDONESIA => '🇮🇩', - Country_Code::IRELAND => '🇮🇪', - Country_Code::ISRAEL => '🇮🇱', - 'IM' => '🇮🇲', - Country_Code::INDIA => '🇮🇳', - Country_Code::BRITISH_INDIAN_OCEAN_TERRITORY => '🇮🇴', - Country_Code::IRAQ => '🇮🇶', - Country_Code::IRAN => '🇮🇷', - Country_Code::ICELAND => '🇮🇸', - Country_Code::ITALY => '🇮🇹', - 'JE' => '🇯🇪', - Country_Code::JAMAICA => '🇯🇲', - Country_Code::JORDAN => '🇯🇴', - Country_Code::JAPAN => '🇯🇵', - Country_Code::KENYA => '🇰🇪', - Country_Code::KYRGYZSTAN => '🇰🇬', - Country_Code::CAMBODIA => '🇰🇭', - Country_Code::KIRIBATI => '🇰🇮', - Country_Code::COMOROS => '🇰🇲', - Country_Code::SAINT_KITTS_AND_NEVIS => '🇰🇳', - Country_Code::NORTH_KOREA => '🇰🇵', - Country_Code::SOUTH_KOREA => '🇰🇷', - Country_Code::KUWAIT => '🇰🇼', - 'KY' => '🇰🇾', - Country_Code::KAZAKHSTAN => '🇰🇿', - Country_Code::LAOS => '🇱🇦', - Country_Code::LEBANON => '🇱🇧', - Country_Code::SAINT_LUCIA => '🇱🇨', - Country_Code::LIECHTENSTEIN => '🇱🇮', - Country_Code::SRI_LANKA => '🇱🇰', - Country_Code::LIBERIA => '🇱🇷', - Country_Code::LESOTHO => '🇱🇸', - Country_Code::LITHUANIA => '🇱🇹', - Country_Code::LUXEMBOURG => '🇱🇺', - Country_Code::LATVIA => '🇱🇻', - Country_Code::LIBYA => '🇱🇾', - Country_Code::MOROCCO => '🇲🇦', - Country_Code::MONACO => '🇲🇨', - Country_Code::MOLDOVA => '🇲🇩', - Country_Code::MONTENEGRO => '🇲🇪', - 'MF' => '🇲🇫', - Country_Code::MADAGASCAR => '🇲🇬', - Country_Code::MARSHALL_ISLANDS => '🇲🇭', - Country_Code::NORTH_MACEDONIA => '🇲🇰', - Country_Code::MALI => '🇲🇱', - Country_Code::MYANMAR => '🇲🇲', - Country_Code::MONGOLIA => '🇲🇳', - 'MO' => '🇲🇴', - 'MP' => '🇲🇵', - 'MQ' => '🇲🇶', - Country_Code::MAURITANIA => '🇲🇷', - 'MS' => '🇲🇸', - Country_Code::MALTA => '🇲🇹', - Country_Code::MAURITIUS => '🇲🇺', - Country_Code::MALDIVES => '🇲🇻', - Country_Code::MALAWI => '🇲🇼', - Country_Code::MEXICO => '🇲🇽', - Country_Code::MALAYSIA => '🇲🇾', - Country_Code::MOZAMBIQUE => '🇲🇿', - Country_Code::NAMIBIA => '🇳🇦', - 'NC' => '🇳🇨', - Country_Code::NIGER => '🇳🇪', - 'NF' => '🇳🇫', - Country_Code::NIGERIA => '🇳🇬', - Country_Code::NICARAGUA => '🇳🇮', - Country_Code::NETHERLANDS => '🇳🇱', - Country_Code::NORWAY => '🇳🇴', - Country_Code::NEPAL => '🇳🇵', - Country_Code::NAURU => '🇳🇷', - 'NU' => '🇳🇺', - Country_Code::NEW_ZEALAND => '🇳🇿', - Country_Code::OMAN => '🇴🇲', - Country_Code::PANAMA => '🇵🇦', - Country_Code::PERU => '🇵🇪', - 'PF' => '🇵🇫', - Country_Code::PAPUA_NEW_GUINEA => '🇵🇬', - Country_Code::PHILIPPINES => '🇵🇭', - Country_Code::PAKISTAN => '🇵🇰', - Country_Code::POLAND => '🇵🇱', - 'PM' => '🇵🇲', - 'PN' => '🇵🇳', - 'PR' => '🇵🇷', - Country_Code::PALESTINE => '🇵🇸', - Country_Code::PORTUGAL => '🇵🇹', - Country_Code::PALAU => '🇵🇼', - Country_Code::PARAGUAY => '🇵🇾', - Country_Code::QATAR => '🇶🇦', - 'RE' => '🇷🇪', - Country_Code::ROMANIA => '🇷🇴', - Country_Code::SERBIA => '🇷🇸', - Country_Code::RUSSIA => '🇷🇺', - Country_Code::RWANDA => '🇷🇼', - Country_Code::SAUDI_ARABIA => '🇸🇦', - Country_Code::SOLOMON_ISLANDS => '🇸🇧', - Country_Code::SEYCHELLES => '🇸🇨', - Country_Code::SUDAN => '🇸🇩', - Country_Code::SWEDEN => '🇸🇪', - Country_Code::SINGAPORE => '🇸🇬', - 'SH' => '🇸🇭', - Country_Code::SLOVENIA => '🇸🇮', - 'SJ' => '🇸🇯', - Country_Code::SLOVAKIA => '🇸🇰', - Country_Code::SIERRA_LEONE => '🇸🇱', - Country_Code::SAN_MARINO => '🇸🇲', - Country_Code::SENEGAL => '🇸🇳', - Country_Code::SOMALIA => '🇸🇴', - Country_Code::SURINAME => '🇸🇷', - Country_Code::SOUTH_SUDAN => '🇸🇸', - Country_Code::SAO_TOME_AND_PRINCIPE => '🇸🇹', - Country_Code::EL_SALVADOR => '🇸🇻', - 'SX' => '🇸🇽', - Country_Code::SYRIA => '🇸🇾', - Country_Code::ESWATINI => '🇸🇿', - 'TC' => '🇹🇨', - Country_Code::CHAD => '🇹🇩', - 'TF' => '🇹🇫', - Country_Code::TOGO => '🇹🇬', - Country_Code::THAILAND => '🇹🇭', - Country_Code::TAJIKISTAN => '🇹🇯', - 'TK' => '🇹🇰', - Country_Code::EAST_TIMOR => '🇹🇱', - Country_Code::TURKMENISTAN => '🇹🇲', - Country_Code::TUNISIA => '🇹🇳', - Country_Code::TONGA => '🇹🇴', - Country_Code::TURKEY => '🇹🇷', - Country_Code::TRINIDAD_AND_TOBAGO => '🇹🇹', - Country_Code::TUVALU => '🇹🇻', - Country_Code::TAIWAN => '🇹🇼', - Country_Code::TANZANIA => '🇹🇿', - Country_Code::UKRAINE => '🇺🇦', - Country_Code::UGANDA => '🇺🇬', - 'UM' => '🇺🇲', - Country_Code::UNITED_STATES => '🇺🇸', - Country_Code::URUGUAY => '🇺🇾', - Country_Code::UZBEKISTAN => '🇺🇿', - Country_Code::VATICAN_CITY => '🇻🇦', - Country_Code::SAINT_VINCENT_AND_THE_GRENADINES => '🇻🇨', - Country_Code::VENEZUELA => '🇻🇪', - 'VG' => '🇻🇬', - 'VI' => '🇻🇮', - Country_Code::VIETNAM => '🇻🇳', - Country_Code::VANUATU => '🇻🇺', - 'WF' => '🇼🇫', - Country_Code::SAMOA => '🇼🇸', - Country_Code::KOSOVO => '🇽🇰', - Country_Code::YEMEN => '🇾🇪', - 'YT' => '🇾🇹', - Country_Code::SOUTH_AFRICA => '🇿🇦', - Country_Code::ZAMBIA => '🇿🇲', - Country_Code::ZIMBABWE => '🇿🇼', - ]; - - /** - * Retrieves a flag by country code. - * - * @param string $country country alpha-2 code (ISO 3166) like US. - * @return string - */ - public static function get_by_country( string $country ): string { - return self::EMOJI_COUNTRIES_FLAGS[ $country ] ?? ''; - } - - /** - * Retrieves a flag by currency code. - * - * @param string $currency currency code (ISO 4217) like USD. - * @return string - */ - public static function get_by_currency( string $currency ): string { - $exceptions = [ - Currency_Code::NETHERLANDS_ANTILLEAN_GUILDER => '', - Currency_Code::BITCOIN => '', - Currency_Code::CENTRAL_AFRICAN_CFA_FRANC => '', - Currency_Code::EAST_CARIBBEAN_DOLLAR => '', - Currency_Code::WEST_AFRICAN_CFA_FRANC => '', - Currency_Code::CFP_FRANC => '', - ]; - - $flag = $exceptions[ $currency ] ?? self::get_by_country( substr( $currency, 0, -1 ) ); - - return $flag; - } -} diff --git a/includes/multi-currency/Helpers/OrderMetaHelper.md b/includes/multi-currency/Helpers/OrderMetaHelper.md deleted file mode 100644 index 84e7a0ff5c7..00000000000 --- a/includes/multi-currency/Helpers/OrderMetaHelper.md +++ /dev/null @@ -1,20 +0,0 @@ -## Order Meta Helper - -The purpose of this helper is to allow merchants to fix or update the Multi-Currency exchange rates on orders if/when they are incorrect. - -### Usage - -* Go to WooCommerce > Status > Tools -* Find the _Enable/Disable Multi-Currency Meta Helper_ option and click the button. You should then receive a notice that the tool was enabled. -* Go to the order that has an issue with the Multi-Currency exchange rate. -* A new meta box named _Multi-Currency Meta Helper_ will appear in the main column near the bottom. - * This will only show for orders where the currency does not match the store currency. An example would be if the store currency is USD and the order currency is EUR. -* There will be information displayed about the order in the meta box. - * If either the Multi-Currency or Stripe exchange rate are not found, they will display _Not Found_. - * If the order is through WooPayments, and if a charge is found, then there will be a _Charge Exchange Rate_ shown. If the _Charge Exchange Rate_ is found, it will also display as a _Suggested Exchange Rate_. -* Enter the new exchange rate to be used into the field, and then click the _Update_ button for the order. -* A new order note will appear stating that the Multi-Currency exchange rate has been updated. - -### Additional Note - -This tool is for edge cases where this data may be missing, which can cause issues with Analytics reporting. Once the exchange rate is updated, the _Import historical data_ tool under Analytics > Settings will need to be run. This will correct data related to any orders that have been adjusted. diff --git a/includes/multi-currency/Helpers/OrderMetaHelper.php b/includes/multi-currency/Helpers/OrderMetaHelper.php deleted file mode 100644 index 208fefc2c26..00000000000 --- a/includes/multi-currency/Helpers/OrderMetaHelper.php +++ /dev/null @@ -1,438 +0,0 @@ -payments_api_client = $payments_api_client; - } - - /** - * Initializes this class' WP hooks. - * - * @return void - */ - public function init_hooks() { - add_action( 'add_meta_boxes', [ $this, 'maybe_add_meta_box' ], 10, 2 ); - add_action( 'save_post', [ $this, 'maybe_update_exchange_rate' ] ); - add_action( 'admin_notices', [ $this, 'maybe_output_errors' ] ); - add_filter( 'get_edit_post_link', [ $this, 'maybe_update_edit_post_link' ] ); - } - - /** - * Outputs and then clears our errors if there are any. - * - * @return void - */ - public function maybe_output_errors() { - if ( $this->has_errors() ) { - foreach ( $this->errors as $error ) { - - ?> -
    -

    Error:

    -
    - clear_errors(); - } - } - - /** - * Updates the exchange rate meta data. - * - * @param int $order_id The order we are working with. - * - * @return void - */ - public function maybe_update_exchange_rate( $order_id ) { - // Verify nonce. - $nonce_value = ! empty( $_POST['wcpay_multi_currency_exchange_rate_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['wcpay_multi_currency_exchange_rate_nonce'] ) ) : false; - if ( false === $nonce_value || ! wp_verify_nonce( $nonce_value, 'wcpay_multi_currency_exchange_rate_nonce_' . $order_id ) || ! current_user_can( 'manage_woocommerce' ) ) { - return; - } - - // Confirm our order is valid, should be worked on, and that a new exchange rate has been passed. - $order = $this->confirm_actions( $order_id ); - if ( ! $order || empty( $_POST['wcpay_multi_currency_exchange_rate'] ) ) { - return; - } - - // Clean the data, confirm the rate is a number, and if it isn't, add error and exit. - $new_exchange_rate = sanitize_text_field( wp_unslash( $_POST['wcpay_multi_currency_exchange_rate'] ) ); - if ( ! is_numeric( $new_exchange_rate ) ) { - $this->add_error( 'Exchange rate value not numeric: ' . $new_exchange_rate ); - return; - } - - // Add the exchange rates to the log. - $old_exchange_rate = $order->get_meta( '_wcpay_multi_currency_order_exchange_rate' ); - $old_exchange_rate = ! empty( $old_exchange_rate ) ? $old_exchange_rate : __( 'Not found', 'woocommerce-payments' ); - $exchange_rate_log = $order->get_meta( '_wcpay_multi_currency_order_exchange_rate_log' ); - $exchange_rate_log = ( is_array( $exchange_rate_log ) ) ? $exchange_rate_log : []; - $current_time = time(); - - $exchange_rate_log[ $current_time ] = [ - 'user_id' => get_current_user_id(), - 'old_rate' => $old_exchange_rate, - 'new_rate' => $new_exchange_rate, - ]; - - // Add an order note stating what was updated. - $note = sprintf( - /* translators: %1 Old exchange rate, or 'Not found' string, %2 new exchange rate */ - __( 'The exchange rate has been updated:
    From: %1$s
    To: %2$s', 'woocommerce-payments' ), - $old_exchange_rate, - $new_exchange_rate - ); - $order->add_order_note( $note, 0, true ); - - // Update the exchange rate and the log on the order. - $order->update_meta_data( '_wcpay_multi_currency_order_exchange_rate', $new_exchange_rate ); - $order->update_meta_data( '_wcpay_multi_currency_order_exchange_rate_log', $exchange_rate_log ); - $order->save_meta_data(); - } - - /** - * Maybe add the meta box. - * - * @param string $post_type Unused. The type of post being viewed. - * @param object $post The post object for the post being viewed. - * - * @return void - */ - public function maybe_add_meta_box( $post_type, $post ) { - // Confirm we should be working on the order, if not, exit. - $order = $this->confirm_actions( $post ); - if ( ! $order || ! function_exists( '\wc_get_page_screen_id' ) ) { - return; - } - - // Get the order edit screen to be able to add the meta box to. - $wc_screen_id = \wc_get_page_screen_id( 'shop-order' ); - - add_meta_box( 'wcpay_mc_order_meta_helper_meta_box', __( 'Multi-Currency Meta Helper', 'woocommerce-payments' ), [ $this, 'display_meta_box_content' ], $wc_screen_id, 'advanced', 'high' ); - } - - /** - * Displays the content of the meta box. - * - * @param \WC_Order $order The order we are working with. - * - * @return void - */ - public function display_meta_box_content( $order ) { - // Again, make sure we are actually working with an order. - $order = wc_get_order( $order ); - if ( ! $order ) { - return; - } - - // Start getting all of the items we need. - $order_currency = strtoupper( $order->get_currency() ); - $intent_id = $order->get_meta( '_intent_id' ); - $payment_method = $order->get_payment_method(); - - // Define the store items. - $store_items = [ - 'store_currency' => [ - 'label' => __( 'Store Currency', 'woocommerce-payments' ), - 'value' => strtoupper( get_woocommerce_currency() ), - ], - ]; - - // Define the order meta items. - $order_meta_items = [ - 'order_currency' => [ - 'label' => __( 'Order Currency', 'woocommerce-payments' ), - 'value' => $order_currency, - ], - 'order_default_currency' => [ - 'label' => __( 'Order Default Currency', 'woocommerce-payments' ), - 'value' => strtoupper( $order->get_meta( '_wcpay_multi_currency_order_default_currency' ) ), - ], - 'payment_method' => [ - 'label' => __( 'Payment Method ID', 'woocommerce-payments' ), - 'value' => $payment_method, - ], - 'payment_method_title' => [ - 'label' => __( 'Payment Method Title', 'woocommerce-payments' ), - 'value' => $order->get_payment_method_title(), - ], - 'intent_id' => [ - 'label' => __( 'Intent ID', 'woocommerce-payments' ), - 'value' => $intent_id, - ], - 'intent_currency' => [ - 'label' => __( 'Intent Currency', 'woocommerce-payments' ), - 'value' => strtoupper( $order->get_meta( '_wcpay_intent_currency' ) ), - ], - 'mc_exchange_rate' => [ - 'label' => __( 'Multi-Currency Exchange Rate', 'woocommerce-payments' ), - 'value' => $order->get_meta( '_wcpay_multi_currency_order_exchange_rate' ), - ], - 'stripe_exchange_rate' => [ - 'label' => __( 'Stripe Exchange Rate', 'woocommerce-payments' ), - 'value' => $order->get_meta( '_wcpay_multi_currency_stripe_exchange_rate' ), - ], - ]; - - // Define the charge (intent) items. - $charge_items = [ - 'charge_exchange_rate' => [ - 'label' => __( 'Charge Exchange Rate', 'woocommerce-payments' ), - 'value' => '', - ], - 'charge_currency' => [ - 'label' => __( 'Charge Currency', 'woocommerce-payments' ), - 'value' => '', - ], - ]; - - if ( ! empty( $intent_id ) ) { - // Attempt to get the intent. - try { - $intent_object = $this->payments_api_client->get_intent( $intent_id ); - } catch ( API_Exception $e ) { - // Log the error returned. - Logger::error( "Error when attempting to get intent ($intent_id):\n" . $e->getMessage() ); - $intent_object = null; - } - - // If we have an intent, then get the charge and the exchange rate from it. - if ( is_a( $intent_object, 'WC_Payments_API_Payment_Intention' ) ) { - $charge_object = $intent_object->get_charge(); - $balance_transaction = $charge_object->get_balance_transaction(); - - // Set the Charge Exchange Rate value from the intent itself. - $charge_items['charge_exchange_rate']['value'] = $balance_transaction['exchange_rate']; - $charge_items['charge_currency']['value'] = strtoupper( $charge_object->get_currency() ); - } - } - - /** - * Zero decimal currencies have a different conversion rate value. - */ - if ( in_array( strtolower( $order_currency ), WC_Payments_Utils::zero_decimal_currencies(), true ) ) { - if ( '' !== $charge_items['charge_exchange_rate']['value'] ) { - $charge_items['charge_exchange_rate']['value'] = $charge_items['charge_exchange_rate']['value'] / 100; - } - } - - // Convert the Stripe exchange amounts to what we use for Multi-Currency. - if ( '' !== $charge_items['charge_exchange_rate']['value'] ) { - $charge_items['charge_exchange_rate']['value'] = 1 / $charge_items['charge_exchange_rate']['value']; - } - if ( '' !== $order_meta_items['stripe_exchange_rate']['value'] ) { - $order_meta_items['stripe_exchange_rate']['value'] = 1 / $order_meta_items['stripe_exchange_rate']['value']; - } - - // Let's see if we can get a suggested rate. - $suggested = false; - if ( empty( $order_meta_items['mc_exchange_rate']['value'] ) ) { - if ( ! empty( $charge_items['charge_exchange_rate']['value'] ) ) { - $suggested = $charge_items['charge_exchange_rate']['value']; - } - if ( ! empty( $order_meta_items['stripe_exchange_rate']['value'] ) ) { - $suggested = $order_meta_items['stripe_exchange_rate']['value']; - } - } - - // Define our labels. - $form_description = __( 'If the exchange rate meta data is missing, update the order with the suggested exchange rate. Once the rate is updated, you can then use the Import historical data tool under Analytics > Settings to correct analytical data.', 'woocommerce-payments' ); - $suggested_label = __( 'Suggested exchange rate: ', 'woocommerce-payments' ); - $new_exchange_rate_label = __( 'New exchange rate:', 'woocommerce-payments' ); - $nonce_value = wp_create_nonce( 'wcpay_multi_currency_exchange_rate_nonce_' . $order->get_id() ); - - // Display the form itself. - ?> -

    - -

    - -

    - -

    - -

    -

    -
    - $store_items, - __( 'Order meta items', 'woocommerce-payments' ) => $order_meta_items, - __( 'Charge items', 'woocommerce-payments' ) => $charge_items, - ]; - - $not_found = __( 'Not found', 'woocommerce-payments' ); - - // Iterate through our display items to display all available information in a table. - echo "\n"; - foreach ( $display_items as $label => $items ) { - echo '\n"; - foreach ( $items as $item ) { - echo '\n"; - } - } - echo "
    ' . esc_html( $label ) . "
    ' . esc_html( $item['label'] ) . ': '; - if ( ! empty( $item['value'] ) ) { - echo esc_html( $item['value'] ); - } else { - echo '' . esc_html( $not_found ) . ''; - } - echo "
    \n"; - } - - /** - * Appends our parameter to the edit post link if needed. - * - * @param string|null $url The current edit post link. - * - * @return string|null - */ - public function maybe_update_edit_post_link( $url ): ?string { - if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { - return null; - } - - if ( $this->is_feature_enabled() ) { - $url .= '&wcpay_mc_meta_helper=1'; - } - - return $url; - } - - /** - * Checks to see if the feature is enabled by the request parameter. - * - * @return bool - */ - private function is_feature_enabled(): bool { - // Nonce verification ignored due to this is just checking for a set specific value. - return isset( $_REQUEST['wcpay_mc_meta_helper'] ) && 1 === intval( $_REQUEST['wcpay_mc_meta_helper'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - } - - /** - * Confirms that we should be working on this order. - * - * @param int|\WC_Order $order The order we're working with. - * - * @return bool|\WC_Order|\WC_Order_Refund Returns false or the order we're working with. - */ - private function confirm_actions( $order ) { - // If the feature is not enabled, exit. - if ( ! $this->is_feature_enabled() ) { - return false; - } - - // If it's actually not an order, or if it's not in a status that accepted a payment, exit. - $order = wc_get_order( $order ); - $paid_statuses = array_merge( wc_get_is_paid_statuses(), [ 'refunded' ] ); - if ( ! $order || ! in_array( $order->get_status(), $paid_statuses, true ) ) { - return false; - } - - // If the store currency and the order currency match, do not display the box. - $store_currency = get_woocommerce_currency(); - $order_currency = $order->get_currency(); - if ( $store_currency === $order_currency ) { - return false; - } - - return $order; - } - - /** - * Adds an error to the stack for us. - * - * @param string $error The error to add to the stack. - * - * @return void - */ - private function add_error( $error ) { - // Refresh the errors, then add the new one. - $this->get_errors(); - $this->errors[] = $error; - - // Update the errors option. - update_option( '_wcpay_multi_currency_order_meta_helper_errors', $this->errors ); - } - - /** - * Gets the errors from the options table. - * - * @return array The array of errors. - */ - private function get_errors(): array { - // Get any errors from the database. - $errors = array_filter( (array) get_option( '_wcpay_multi_currency_order_meta_helper_errors' ) ); - - // If we have any errors currently in our object, add those. - if ( 0 < count( $this->errors ) ) { - $errors = array_merge( $errors, $this->errors ); - } - - // Add all the errors to the object, and return them. - $this->errors = array_unique( $errors ); - - return $this->errors; - } - - /** - * Checks if we have any errors. - * - * @return bool - */ - private function has_errors(): bool { - return 0 < count( $this->get_errors() ); - } - - /** - * Removes the errors from the object and deletes the database option. - * - * @return void - */ - private function clear_errors() { - $this->errors = []; - delete_option( '_wcpay_multi_currency_order_meta_helper_errors' ); - } -} 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 50aabec994c..13b25e07dd6 100644 --- a/includes/wc-payment-api/class-wc-payments-api-client.php +++ b/includes/wc-payment-api/class-wc-payments-api-client.php @@ -21,11 +21,12 @@ use WCPay\Core\Server\Request; use WCPay\Core\Server\Request\List_Fraud_Outcome_Transactions; use WCPay\Exceptions\Cannot_Combine_Currencies_Exception; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyApiClientInterface; /** * Communicates with WooCommerce Payments API. */ -class WC_Payments_API_Client { +class WC_Payments_API_Client implements MultiCurrencyApiClientInterface { const ENDPOINT_BASE = 'https://public-api.wordpress.com/wpcom/v2'; const ENDPOINT_SITE_FRAGMENT = 'sites/%s'; @@ -191,7 +192,7 @@ public function __construct( $user_agent, $http_client, $wcpay_db ) { * * @return bool */ - public function is_server_connected() { + public function is_server_connected(): bool { return $this->http_client->is_connected(); } @@ -857,7 +858,7 @@ function ( $a, $b ) { * * @throws API_Exception - Error contacting the API. */ - public function get_currency_rates( string $currency_from, $currencies_to = null ) { + public function get_currency_rates( string $currency_from, $currencies_to = null ): array { if ( empty( $currency_from ) ) { throw new API_Exception( __( 'Currency From parameter is required', 'woocommerce-payments' ), diff --git a/client/multi-currency-analytics/index.js b/multi-currency/client/analytics/index.js similarity index 100% rename from client/multi-currency-analytics/index.js rename to multi-currency/client/analytics/index.js diff --git a/client/multi-currency/blocks/currency-switcher.js b/multi-currency/client/blocks/currency-switcher.js similarity index 99% rename from client/multi-currency/blocks/currency-switcher.js rename to multi-currency/client/blocks/currency-switcher.js index 5dee2d130be..75111f42768 100644 --- a/client/multi-currency/blocks/currency-switcher.js +++ b/multi-currency/client/blocks/currency-switcher.js @@ -2,7 +2,10 @@ /** * Internal dependencies */ -import { useEnabledCurrencies, useCurrencies } from 'wcpay/data'; +import { + useEnabledCurrencies, + useCurrencies, +} from 'multi-currency/interface/data'; /** * External dependencies diff --git a/client/components/currency-delete-illustration/index.js b/multi-currency/client/components/currency-delete-illustration/index.js similarity index 100% rename from client/components/currency-delete-illustration/index.js rename to multi-currency/client/components/currency-delete-illustration/index.js diff --git a/client/components/currency-delete-illustration/styles.scss b/multi-currency/client/components/currency-delete-illustration/styles.scss similarity index 100% rename from client/components/currency-delete-illustration/styles.scss rename to multi-currency/client/components/currency-delete-illustration/styles.scss diff --git a/client/multi-currency/preview-modal/index.js b/multi-currency/client/components/preview-modal/index.js similarity index 95% rename from client/multi-currency/preview-modal/index.js rename to multi-currency/client/components/preview-modal/index.js index 9857ddd71e3..60d4fac0a13 100644 --- a/client/multi-currency/preview-modal/index.js +++ b/multi-currency/client/components/preview-modal/index.js @@ -3,7 +3,7 @@ */ import { Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useStoreSettings } from 'wcpay/data'; +import { useStoreSettings } from 'multi-currency/data'; /** * Internal dependencies diff --git a/client/multi-currency/preview-modal/index.scss b/multi-currency/client/components/preview-modal/index.scss similarity index 100% rename from client/multi-currency/preview-modal/index.scss rename to multi-currency/client/components/preview-modal/index.scss diff --git a/client/components/search/index.js b/multi-currency/client/components/search/index.js similarity index 100% rename from client/components/search/index.js rename to multi-currency/client/components/search/index.js diff --git a/client/components/search/style.scss b/multi-currency/client/components/search/style.scss similarity index 100% rename from client/components/search/style.scss rename to multi-currency/client/components/search/style.scss diff --git a/client/components/search/test/__snapshots__/index.js.snap b/multi-currency/client/components/search/test/__snapshots__/index.js.snap similarity index 100% rename from client/components/search/test/__snapshots__/index.js.snap rename to multi-currency/client/components/search/test/__snapshots__/index.js.snap diff --git a/client/components/search/test/index.js b/multi-currency/client/components/search/test/index.js similarity index 100% rename from client/components/search/test/index.js rename to multi-currency/client/components/search/test/index.js diff --git a/client/multi-currency/context.js b/multi-currency/client/context.js similarity index 100% rename from client/multi-currency/context.js rename to multi-currency/client/context.js diff --git a/client/data/multi-currency/action-types.js b/multi-currency/client/data/action-types.js similarity index 100% rename from client/data/multi-currency/action-types.js rename to multi-currency/client/data/action-types.js diff --git a/client/data/multi-currency/actions.js b/multi-currency/client/data/actions.js similarity index 96% rename from client/data/multi-currency/actions.js rename to multi-currency/client/data/actions.js index 7c28cf73781..0b822dde4cb 100644 --- a/client/data/multi-currency/actions.js +++ b/multi-currency/client/data/actions.js @@ -6,13 +6,13 @@ import { apiFetch } from '@wordpress/data-controls'; import { dispatch, select } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { recordEvent } from 'tracks'; /** * Internal Dependencies */ +import { recordEvent } from 'multi-currency/interface/functions'; import TYPES from './action-types'; -import { NAMESPACE, STORE_NAME } from '../constants'; +import { NAMESPACE, STORE_NAME } from './constants'; export function updateCurrencies( data ) { return { diff --git a/multi-currency/client/data/constants.js b/multi-currency/client/data/constants.js new file mode 100644 index 00000000000..eaee38c6ee6 --- /dev/null +++ b/multi-currency/client/data/constants.js @@ -0,0 +1,4 @@ +/** @format */ + +export const NAMESPACE = '/wc/v3/payments'; +export const STORE_NAME = 'wc/payments/multi-currency'; diff --git a/client/data/multi-currency/hooks.js b/multi-currency/client/data/hooks.js similarity index 96% rename from client/data/multi-currency/hooks.js rename to multi-currency/client/data/hooks.js index aa39e24ee6f..ea5c23e5e75 100644 --- a/client/data/multi-currency/hooks.js +++ b/multi-currency/client/data/hooks.js @@ -4,7 +4,11 @@ * External dependencies */ import { useSelect, useDispatch, dispatch } from '@wordpress/data'; -import { STORE_NAME } from '../constants'; + +/** + * Internal dependencies + */ +import { STORE_NAME } from './constants'; export const useCurrencies = () => useSelect( ( select ) => { diff --git a/multi-currency/client/data/index.ts b/multi-currency/client/data/index.ts new file mode 100644 index 00000000000..f1d8d84a456 --- /dev/null +++ b/multi-currency/client/data/index.ts @@ -0,0 +1,20 @@ +/** @format */ + +/** + * Internal dependencies + */ +import { STORE_NAME } from './constants'; +import { initStore } from './store'; + +initStore(); + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const WCPAY_STORE_NAME = STORE_NAME; + +// We only ask for hooks when importing directly from 'multi-currency/data'. +import * as selectors from './selectors'; +import * as actions from './actions'; +import * as resolvers from './resolvers'; + +export { selectors, actions, resolvers }; +export * from './hooks'; diff --git a/client/data/multi-currency/reducer.js b/multi-currency/client/data/reducer.js similarity index 100% rename from client/data/multi-currency/reducer.js rename to multi-currency/client/data/reducer.js diff --git a/client/data/multi-currency/resolvers.js b/multi-currency/client/data/resolvers.js similarity index 93% rename from client/data/multi-currency/resolvers.js rename to multi-currency/client/data/resolvers.js index 4eada7da994..78168ad708a 100644 --- a/client/data/multi-currency/resolvers.js +++ b/multi-currency/client/data/resolvers.js @@ -10,7 +10,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { NAMESPACE } from '../constants'; +import { NAMESPACE } from './constants'; import { updateCurrencies, updateCurrencySettings, @@ -34,7 +34,7 @@ export function* getCurrencies() { } /** - * Retrieve single currency sttings from the site's REST API. + * Retrieve single currency settings from the site's REST API. * * @param {string} currencyCode The currency code to fetch settings for. */ diff --git a/client/data/multi-currency/selectors.js b/multi-currency/client/data/selectors.js similarity index 96% rename from client/data/multi-currency/selectors.js rename to multi-currency/client/data/selectors.js index 6c2a2b20f97..825a9271db0 100644 --- a/client/data/multi-currency/selectors.js +++ b/multi-currency/client/data/selectors.js @@ -13,7 +13,7 @@ const getMultiCurrencyState = ( state ) => { return {}; } - return state.multiCurrency || {}; + return state || {}; }; export const getCurrencies = ( state ) => { diff --git a/multi-currency/client/data/store.js b/multi-currency/client/data/store.js new file mode 100644 index 00000000000..2e142e66f9c --- /dev/null +++ b/multi-currency/client/data/store.js @@ -0,0 +1,28 @@ +/* + * External dependencies + */ +import { registerStore } from '@wordpress/data'; +import { controls } from '@wordpress/data-controls'; + +/** + * Internal dependencies + */ +import { STORE_NAME } from './constants'; +import * as multiCurrency from './'; +import reducer from './reducer'; + +// Extracted into wrapper function to facilitate testing. +export const initStore = () => + registerStore( STORE_NAME, { + reducer, + actions: { + ...multiCurrency.actions, + }, + controls, + selectors: { + ...multiCurrency.selectors, + }, + resolvers: { + ...multiCurrency.resolvers, + }, + } ); diff --git a/client/multi-currency/index.js b/multi-currency/client/index.js similarity index 91% rename from client/multi-currency/index.js rename to multi-currency/client/index.js index a479c326914..f4daf3b9a3a 100644 --- a/client/multi-currency/index.js +++ b/multi-currency/client/index.js @@ -7,8 +7,8 @@ import ReactDOM from 'react-dom'; /** * Internal dependencies */ -import MultiCurrencySettings from './multi-currency-settings'; -import SingleCurrencySettings from './single-currency-settings'; +import MultiCurrencySettings from './settings/multi-currency'; +import SingleCurrencySettings from './settings/single-currency'; import MultiCurrencySettingsContext from './context'; const MultiCurrencySettingsPage = () => { diff --git a/multi-currency/client/interface/assets.js b/multi-currency/client/interface/assets.js new file mode 100644 index 00000000000..3a3a095891a --- /dev/null +++ b/multi-currency/client/interface/assets.js @@ -0,0 +1,6 @@ +/** + * External Dependencies + */ +import paymentMethodsMap from 'wcpay/payment-methods-map'; + +export { paymentMethodsMap }; diff --git a/multi-currency/client/interface/components.js b/multi-currency/client/interface/components.js new file mode 100644 index 00000000000..eddab5ae84e --- /dev/null +++ b/multi-currency/client/interface/components.js @@ -0,0 +1,23 @@ +/** + * Dependencies from WooPayments to MCCY. + */ +// wcpay/additional-methods-setup/* +export { default as CollapsibleBody } from 'wcpay/additional-methods-setup/wizard/collapsible-body'; +export { default as Wizard } from 'wcpay/additional-methods-setup/wizard/wrapper'; +export { default as WizardTask } from 'wcpay/additional-methods-setup/wizard/task'; +export { default as WizardTaskItem } from 'wcpay/additional-methods-setup/wizard/task-item'; +export { default as WizardTaskList } from 'wcpay/additional-methods-setup/wizard/task-list'; +// wcpay/components/* +export { default as ConfirmationModal } from 'wcpay/components/confirmation-modal'; +export { default as Page } from 'wcpay/components/page'; +export { LoadableBlock } from 'wcpay/components/loadable'; +// wcpay/settings/* +export { default as PaymentMethodIcon } from 'wcpay/settings/payment-method-icon'; +export { default as SettingsLayout } from 'wcpay/settings/settings-layout'; +export { default as SettingsSection } from 'wcpay/settings/settings-section'; + +/** + * Dependencies from MCCY to WooPayments. + */ +// multi-currency/setup +export { default as MultiCurrencySetupPage } from 'multi-currency/setup'; diff --git a/multi-currency/client/interface/data.js b/multi-currency/client/interface/data.js new file mode 100644 index 00000000000..af162ddd6ed --- /dev/null +++ b/multi-currency/client/interface/data.js @@ -0,0 +1,10 @@ +/** + * Dependencies from WooPayments to MCCY. + */ +// wcpay/data +export { useSettings, useMultiCurrency } from 'wcpay/data'; + +/** + * Dependencies from MCCY to WooPayments. + */ +export { useCurrencies, useEnabledCurrencies } from 'multi-currency/data'; diff --git a/multi-currency/client/interface/functions.js b/multi-currency/client/interface/functions.js new file mode 100644 index 00000000000..5ffed26e00d --- /dev/null +++ b/multi-currency/client/interface/functions.js @@ -0,0 +1,25 @@ +/** + * Dependencies from WooPayments to MCCY. + */ +// wcpay/tracks +export { recordEvent } from 'wcpay/tracks'; +// wcpay/settings +export { default as WCPaySettingsContext } from 'wcpay/settings/wcpay-settings-context'; +// wcpay/additional-methods-setup/* +export { default as WizardTaskContext } from 'wcpay/additional-methods-setup/wizard/task/context'; +// wcpay/utils/* +export { formatListOfItems } from 'wcpay/utils/format-list-of-items'; + +/** + * Dependencies from MCCY to WooPayments. + */ +export { getMissingCurrenciesTooltipMessage } from 'multi-currency/utils/missing-currencies-message'; +export { + formatCurrency, + formatCurrencyName, + formatFX, + formatExplicitCurrency, + formatExportAmount, + getCurrency, + isZeroDecimalCurrency, +} from 'multi-currency/utils/currency'; diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/delete-button.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/delete-button.js similarity index 92% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/delete-button.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/delete-button.js index cc58435ed22..ad99c04d327 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/delete-button.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/delete-button.js @@ -6,10 +6,12 @@ import { __, sprintf } from '@wordpress/i18n'; import { Button, Icon } from '@wordpress/components'; import interpolateComponents from '@automattic/interpolate-components'; import { useCallback, useState } from '@wordpress/element'; -import ConfirmationModal from 'wcpay/components/confirmation-modal'; -import CurrencyDeleteIllustration from 'wcpay/components/currency-delete-illustration'; -import PaymentMethodIcon from 'wcpay/settings/payment-method-icon'; -import paymentMethodsMap from 'wcpay/payment-methods-map'; +import { + ConfirmationModal, + PaymentMethodIcon, +} from 'multi-currency/interface/components'; +import CurrencyDeleteIllustration from 'multi-currency/components/currency-delete-illustration'; +import { paymentMethodsMap } from 'multi-currency/interface/assets'; const DeleteButton = ( { code, label, symbol, onClick, className } ) => { const [ isConfirmationModalOpen, setIsConfirmationModalOpen ] = useState( diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/index.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/index.js similarity index 97% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/index.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/index.js index 8d08b90ef11..00ac75e0c6e 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/index.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/index.js @@ -15,13 +15,13 @@ import { useCurrencies, useDefaultCurrency, useEnabledCurrencies, -} from 'wcpay/data'; +} from 'multi-currency/data'; import EnabledCurrenciesList from './list'; import EnabledCurrenciesListItem from './list-item'; import EnabledCurrenciesListItemPlaceholder from './list-item-placeholder'; import EnabledCurrenciesModal from './modal'; -import SettingsSection from 'wcpay/settings/settings-section'; +import { SettingsSection } from 'multi-currency/interface/components'; const EnabledCurrenciesSettingsDescription = () => { const LEARN_MORE_URL = diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item-placeholder.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item-placeholder.js similarity index 94% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item-placeholder.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item-placeholder.js index 6a44378e72a..c29d2902a8b 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item-placeholder.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item-placeholder.js @@ -3,7 +3,7 @@ * External dependencies */ import classNames from 'classnames'; -import { LoadableBlock } from 'wcpay/components/loadable'; +import { LoadableBlock } from 'multi-currency/interface/components'; const EnabledCurrenciesListItemPlaceholder = ( { isLoading } ) => { return ( diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item.js similarity index 97% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item.js index 135e95a0a94..0b8d0b14689 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/list-item.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/list-item.js @@ -10,7 +10,7 @@ import { Button } from '@wordpress/components'; * Internal dependencies */ import DeleteButton from './delete-button'; -import MultiCurrencySettingsContext from '../../context'; +import MultiCurrencySettingsContext from 'multi-currency/context'; import { useContext } from 'react'; const EnabledCurrenciesListItem = ( { diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/list.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/list.js similarity index 100% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/list.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/list.js diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox-list.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/modal-checkbox-list.js similarity index 100% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox-list.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/modal-checkbox-list.js diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/modal-checkbox.js similarity index 100% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/modal-checkbox.js diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/modal.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/modal.js similarity index 97% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/modal.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/modal.js index 619e51a01c5..13610e78c17 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/modal.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/modal.js @@ -13,11 +13,11 @@ import { useAvailableCurrencies, useEnabledCurrencies, useDefaultCurrency, -} from 'wcpay/data'; +} from 'multi-currency/data'; import EnabledCurrenciesModalCheckboxList from './modal-checkbox-list'; import EnabledCurrenciesModalCheckbox from './modal-checkbox'; -import ConfirmationModal from 'wcpay/components/confirmation-modal'; -import Search from 'components/search'; +import { ConfirmationModal } from 'multi-currency/interface/components'; +import Search from 'multi-currency/components/search'; import './style.scss'; // TODO: This works when saving, but list does not refresh. diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/style.scss b/multi-currency/client/settings/multi-currency/enabled-currencies-list/style.scss similarity index 100% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/style.scss rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/style.scss diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/test/__snapshots__/index.js.snap b/multi-currency/client/settings/multi-currency/enabled-currencies-list/test/__snapshots__/index.js.snap similarity index 100% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/test/__snapshots__/index.js.snap rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/test/__snapshots__/index.js.snap diff --git a/client/multi-currency/multi-currency-settings/enabled-currencies-list/test/index.js b/multi-currency/client/settings/multi-currency/enabled-currencies-list/test/index.js similarity index 97% rename from client/multi-currency/multi-currency-settings/enabled-currencies-list/test/index.js rename to multi-currency/client/settings/multi-currency/enabled-currencies-list/test/index.js index 38e15b1b04b..fb4e89bd92c 100644 --- a/client/multi-currency/multi-currency-settings/enabled-currencies-list/test/index.js +++ b/multi-currency/client/settings/multi-currency/enabled-currencies-list/test/index.js @@ -14,13 +14,13 @@ import { useCurrencies, useDefaultCurrency, useEnabledCurrencies, -} from 'wcpay/data'; +} from 'multi-currency/data'; -import MultiCurrencySettingsContext from '../../../context'; +import MultiCurrencySettingsContext from 'multi-currency/context'; -jest.mock( 'wcpay/data', () => ( { - useCurrencies: jest.fn(), +jest.mock( 'multi-currency/data', () => ( { useAvailableCurrencies: jest.fn(), + useCurrencies: jest.fn(), useDefaultCurrency: jest.fn(), useEnabledCurrencies: jest.fn(), } ) ); diff --git a/client/multi-currency/multi-currency-settings/index.js b/multi-currency/client/settings/multi-currency/index.js similarity index 87% rename from client/multi-currency/multi-currency-settings/index.js rename to multi-currency/client/settings/multi-currency/index.js index 89ee0fd6361..a2caf57151e 100644 --- a/client/multi-currency/multi-currency-settings/index.js +++ b/multi-currency/client/settings/multi-currency/index.js @@ -6,7 +6,7 @@ import React from 'react'; /** * Internal dependencies */ -import SettingsLayout from '../../settings/settings-layout'; +import { SettingsLayout } from 'multi-currency/interface/components'; import EnabledCurrenciesList from './enabled-currencies-list'; import StoreSettings from './store-settings'; import './style.scss'; diff --git a/client/multi-currency/multi-currency-settings/store-settings/index.js b/multi-currency/client/settings/multi-currency/store-settings/index.js similarity index 95% rename from client/multi-currency/multi-currency-settings/store-settings/index.js rename to multi-currency/client/settings/multi-currency/store-settings/index.js index 9e5be4eb9df..18972164698 100644 --- a/client/multi-currency/multi-currency-settings/store-settings/index.js +++ b/multi-currency/client/settings/multi-currency/store-settings/index.js @@ -11,10 +11,12 @@ import { createInterpolateElement } from '@wordpress/element'; */ import './style.scss'; -import { useStoreSettings } from 'wcpay/data'; -import SettingsSection from 'wcpay/settings/settings-section'; -import { LoadableBlock } from 'wcpay/components/loadable'; -import PreviewModal from 'wcpay/multi-currency/preview-modal'; +import { useStoreSettings } from 'multi-currency/data'; +import { + LoadableBlock, + SettingsSection, +} from 'multi-currency/interface/components'; +import PreviewModal from 'multi-currency/components/preview-modal'; const StoreSettingsDescription = () => { const LEARN_MORE_URL = diff --git a/client/multi-currency/multi-currency-settings/store-settings/style.scss b/multi-currency/client/settings/multi-currency/store-settings/style.scss similarity index 100% rename from client/multi-currency/multi-currency-settings/store-settings/style.scss rename to multi-currency/client/settings/multi-currency/store-settings/style.scss diff --git a/client/multi-currency/multi-currency-settings/store-settings/test/__snapshots__/index.test.js.snap b/multi-currency/client/settings/multi-currency/store-settings/test/__snapshots__/index.test.js.snap similarity index 100% rename from client/multi-currency/multi-currency-settings/store-settings/test/__snapshots__/index.test.js.snap rename to multi-currency/client/settings/multi-currency/store-settings/test/__snapshots__/index.test.js.snap diff --git a/client/multi-currency/multi-currency-settings/store-settings/test/index.test.js b/multi-currency/client/settings/multi-currency/store-settings/test/index.test.js similarity index 95% rename from client/multi-currency/multi-currency-settings/store-settings/test/index.test.js rename to multi-currency/client/settings/multi-currency/store-settings/test/index.test.js index d65d4af5f6b..0aadcd7720d 100644 --- a/client/multi-currency/multi-currency-settings/store-settings/test/index.test.js +++ b/multi-currency/client/settings/multi-currency/store-settings/test/index.test.js @@ -7,10 +7,10 @@ import { render, screen, fireEvent } from '@testing-library/react'; /** * Internal dependencies */ -import { useStoreSettings } from 'wcpay/data'; +import { useStoreSettings } from 'multi-currency/data'; import StoreSettings from '..'; -jest.mock( 'wcpay/data', () => ( { +jest.mock( 'multi-currency/data', () => ( { useStoreSettings: jest.fn(), } ) ); diff --git a/client/multi-currency/multi-currency-settings/style.scss b/multi-currency/client/settings/multi-currency/style.scss similarity index 100% rename from client/multi-currency/multi-currency-settings/style.scss rename to multi-currency/client/settings/multi-currency/style.scss diff --git a/client/multi-currency/multi-currency-settings/test/__snapshots__/index.test.js.snap b/multi-currency/client/settings/multi-currency/test/__snapshots__/index.test.js.snap similarity index 100% rename from client/multi-currency/multi-currency-settings/test/__snapshots__/index.test.js.snap rename to multi-currency/client/settings/multi-currency/test/__snapshots__/index.test.js.snap diff --git a/client/multi-currency/multi-currency-settings/test/index.test.js b/multi-currency/client/settings/multi-currency/test/index.test.js similarity index 92% rename from client/multi-currency/multi-currency-settings/test/index.test.js rename to multi-currency/client/settings/multi-currency/test/index.test.js index 5940e763cc8..3acbfb022c0 100644 --- a/client/multi-currency/multi-currency-settings/test/index.test.js +++ b/multi-currency/client/settings/multi-currency/test/index.test.js @@ -7,7 +7,7 @@ import { render } from '@testing-library/react'; /** * Internal dependencies */ -import SettingsLayout from '../../../settings/settings-layout'; +import { SettingsLayout } from 'multi-currency/interface/components'; import EnabledCurrenciesList from '../enabled-currencies-list'; import StoreSettings from '../store-settings'; diff --git a/client/multi-currency/single-currency-settings/constants.js b/multi-currency/client/settings/single-currency/constants.js similarity index 100% rename from client/multi-currency/single-currency-settings/constants.js rename to multi-currency/client/settings/single-currency/constants.js diff --git a/client/multi-currency/single-currency-settings/currency-preview.js b/multi-currency/client/settings/single-currency/currency-preview.js similarity index 96% rename from client/multi-currency/single-currency-settings/currency-preview.js rename to multi-currency/client/settings/single-currency/currency-preview.js index 952bc537363..8f16f4c5596 100644 --- a/client/multi-currency/single-currency-settings/currency-preview.js +++ b/multi-currency/client/settings/single-currency/currency-preview.js @@ -6,7 +6,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { __ } from '@wordpress/i18n'; import { Card, CardBody } from '@wordpress/components'; import { TextControlWithAffixes } from '@woocommerce/components'; -import { formatCurrency, isZeroDecimalCurrency } from 'wcpay/utils/currency'; +import { + formatCurrency, + isZeroDecimalCurrency, +} from 'multi-currency/utils/currency'; const CurrencyPreview = ( { storeCurrency, diff --git a/client/multi-currency/single-currency-settings/index.js b/multi-currency/client/settings/single-currency/index.js similarity index 98% rename from client/multi-currency/single-currency-settings/index.js rename to multi-currency/client/settings/single-currency/index.js index 213409c9f47..14a3560f14d 100644 --- a/client/multi-currency/single-currency-settings/index.js +++ b/multi-currency/client/settings/single-currency/index.js @@ -5,8 +5,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { dateI18n } from '@wordpress/date'; import { sprintf, __ } from '@wordpress/i18n'; -import SettingsLayout from 'wcpay/settings/settings-layout'; -import SettingsSection from 'wcpay/settings/settings-section'; import moment from 'moment'; /** @@ -27,9 +25,13 @@ import { useCurrencySettings, useEnabledCurrencies, useStoreSettings, -} from 'wcpay/data'; -import MultiCurrencySettingsContext from '../context'; -import { LoadableBlock } from 'wcpay/components/loadable'; +} from 'multi-currency/data'; +import MultiCurrencySettingsContext from 'multi-currency/context'; +import { + LoadableBlock, + SettingsLayout, + SettingsSection, +} from 'multi-currency/interface/components'; const SingleCurrencySettings = () => { const { diff --git a/client/multi-currency/single-currency-settings/style.scss b/multi-currency/client/settings/single-currency/style.scss similarity index 100% rename from client/multi-currency/single-currency-settings/style.scss rename to multi-currency/client/settings/single-currency/style.scss diff --git a/client/multi-currency/single-currency-settings/test/__snapshots__/index.test.js.snap b/multi-currency/client/settings/single-currency/test/__snapshots__/index.test.js.snap similarity index 100% rename from client/multi-currency/single-currency-settings/test/__snapshots__/index.test.js.snap rename to multi-currency/client/settings/single-currency/test/__snapshots__/index.test.js.snap diff --git a/client/multi-currency/single-currency-settings/test/index.test.js b/multi-currency/client/settings/single-currency/test/index.test.js similarity index 97% rename from client/multi-currency/single-currency-settings/test/index.test.js rename to multi-currency/client/settings/single-currency/test/index.test.js index 5f2597dff2c..0559953e54b 100644 --- a/client/multi-currency/single-currency-settings/test/index.test.js +++ b/multi-currency/client/settings/single-currency/test/index.test.js @@ -15,11 +15,11 @@ import { useEnabledCurrencies, useCurrencySettings, useStoreSettings, -} from 'wcpay/data'; +} from 'multi-currency/data'; -import MultiCurrencySettingsContext from '../../context'; +import MultiCurrencySettingsContext from 'multi-currency/context'; -jest.mock( 'wcpay/data', () => ( { +jest.mock( 'multi-currency/data', () => ( { useCurrencies: jest.fn(), useAvailableCurrencies: jest.fn(), useDefaultCurrency: jest.fn(), diff --git a/client/multi-currency-setup/index.js b/multi-currency/client/setup/index.js similarity index 77% rename from client/multi-currency-setup/index.js rename to multi-currency/client/setup/index.js index 512ab078182..05e76eb67ce 100644 --- a/client/multi-currency-setup/index.js +++ b/multi-currency/client/setup/index.js @@ -3,9 +3,9 @@ /** * Internal dependencies */ -import Page from 'components/page'; import MultiCurrencySetup from './tasks/multi-currency-setup'; -import WCPaySettingsContext from '../settings/wcpay-settings-context'; +import { Page } from 'multi-currency/interface/components'; +import { WCPaySettingsContext } from 'multi-currency/interface/functions'; const MultiCurrencySetupPage = () => { const { isSetupCompleted } = window.wcpaySettings.multiCurrencySetup; diff --git a/client/multi-currency-setup/tasks/add-currencies-task/constants.js b/multi-currency/client/setup/tasks/add-currencies-task/constants.js similarity index 100% rename from client/multi-currency-setup/tasks/add-currencies-task/constants.js rename to multi-currency/client/setup/tasks/add-currencies-task/constants.js diff --git a/client/multi-currency-setup/tasks/add-currencies-task/index.js b/multi-currency/client/setup/tasks/add-currencies-task/index.js similarity index 93% rename from client/multi-currency-setup/tasks/add-currencies-task/index.js rename to multi-currency/client/setup/tasks/add-currencies-task/index.js index 1795edaa6a0..f33fb5e95a3 100644 --- a/client/multi-currency-setup/tasks/add-currencies-task/index.js +++ b/multi-currency/client/setup/tasks/add-currencies-task/index.js @@ -10,23 +10,24 @@ import _ from 'lodash'; /** * Internal dependencies */ -import WizardTaskContext from '../../../additional-methods-setup/wizard/task/context'; -import CollapsibleBody from '../../../additional-methods-setup/wizard/collapsible-body'; -import WizardTaskItem from '../../wizard/task-item'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; +import Search from 'multi-currency/components/search'; +import { + CollapsibleBody, + LoadableBlock, + WizardTaskItem, +} from 'multi-currency/interface/components'; import { useCurrencies, useAvailableCurrencies, useEnabledCurrencies, useDefaultCurrency, -} from 'wcpay/data'; +} from 'multi-currency/data'; // eslint-disable-next-line max-len -import EnabledCurrenciesModalCheckboxList from '../../../multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox-list'; -import EnabledCurrenciesModalCheckbox from '../../../multi-currency/multi-currency-settings/enabled-currencies-list/modal-checkbox'; -import Search from 'components/search'; - -import { LoadableBlock } from '../../../components/loadable'; +import EnabledCurrenciesModalCheckboxList from 'multi-currency/settings/multi-currency/enabled-currencies-list/modal-checkbox-list'; +import EnabledCurrenciesModalCheckbox from 'multi-currency/settings/multi-currency/enabled-currencies-list/modal-checkbox'; import { recommendedCurrencyCodes, numberWords } from './constants'; import { diff --git a/client/multi-currency-setup/tasks/add-currencies-task/index.scss b/multi-currency/client/setup/tasks/add-currencies-task/index.scss similarity index 100% rename from client/multi-currency-setup/tasks/add-currencies-task/index.scss rename to multi-currency/client/setup/tasks/add-currencies-task/index.scss diff --git a/client/multi-currency-setup/tasks/add-currencies-task/test/__snapshots__/index.test.js.snap b/multi-currency/client/setup/tasks/add-currencies-task/test/__snapshots__/index.test.js.snap similarity index 100% rename from client/multi-currency-setup/tasks/add-currencies-task/test/__snapshots__/index.test.js.snap rename to multi-currency/client/setup/tasks/add-currencies-task/test/__snapshots__/index.test.js.snap diff --git a/client/multi-currency-setup/tasks/add-currencies-task/test/index.test.js b/multi-currency/client/setup/tasks/add-currencies-task/test/index.test.js similarity index 97% rename from client/multi-currency-setup/tasks/add-currencies-task/test/index.test.js rename to multi-currency/client/setup/tasks/add-currencies-task/test/index.test.js index f58beb09ced..7041543dadc 100644 --- a/client/multi-currency-setup/tasks/add-currencies-task/test/index.test.js +++ b/multi-currency/client/setup/tasks/add-currencies-task/test/index.test.js @@ -9,23 +9,25 @@ import { useSelect } from '@wordpress/data'; * Internal dependencies */ import AddCurrenciesTask from '..'; +import { useSettings } from 'multi-currency/interface/data'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; import { useCurrencies, useAvailableCurrencies, useDefaultCurrency, useEnabledCurrencies, - useSettings, -} from 'wcpay/data'; +} from 'multi-currency/data'; -import WizardTaskContext from '../../../../additional-methods-setup/wizard/task/context'; import { recommendedCurrencyCodes } from '../constants'; import { __ } from '@wordpress/i18n'; -jest.mock( 'wcpay/data', () => ( { +jest.mock( 'multi-currency/data', () => ( { useCurrencies: jest.fn(), useAvailableCurrencies: jest.fn(), useDefaultCurrency: jest.fn(), useEnabledCurrencies: jest.fn(), +} ) ); +jest.mock( 'multi-currency/interface/data', () => ( { useSettings: jest.fn(), } ) ); diff --git a/client/multi-currency-setup/tasks/add-currencies-task/test/utils.test.js b/multi-currency/client/setup/tasks/add-currencies-task/test/utils.test.js similarity index 100% rename from client/multi-currency-setup/tasks/add-currencies-task/test/utils.test.js rename to multi-currency/client/setup/tasks/add-currencies-task/test/utils.test.js diff --git a/client/multi-currency-setup/tasks/add-currencies-task/utils.js b/multi-currency/client/setup/tasks/add-currencies-task/utils.js similarity index 100% rename from client/multi-currency-setup/tasks/add-currencies-task/utils.js rename to multi-currency/client/setup/tasks/add-currencies-task/utils.js diff --git a/client/multi-currency-setup/tasks/multi-currency-setup.js b/multi-currency/client/setup/tasks/multi-currency-setup.js similarity index 84% rename from client/multi-currency-setup/tasks/multi-currency-setup.js rename to multi-currency/client/setup/tasks/multi-currency-setup.js index 77263d9bf33..cbeb6387bda 100644 --- a/client/multi-currency-setup/tasks/multi-currency-setup.js +++ b/multi-currency/client/setup/tasks/multi-currency-setup.js @@ -7,9 +7,11 @@ import { Card, CardBody } from '@wordpress/components'; /** * Internal dependencies */ -import Wizard from '../../additional-methods-setup/wizard/wrapper'; -import WizardTask from '../../additional-methods-setup/wizard/task'; -import WizardTaskList from '../../additional-methods-setup/wizard/task-list'; +import { + Wizard, + WizardTask, + WizardTaskList, +} from 'multi-currency/interface/components'; import StoreSettingsTask from './store-settings-task'; import SetupCompleteTask from './setup-complete-task'; import AddCurrenciesTask from './add-currencies-task'; diff --git a/client/multi-currency-setup/tasks/multi-currency-setup.scss b/multi-currency/client/setup/tasks/multi-currency-setup.scss similarity index 100% rename from client/multi-currency-setup/tasks/multi-currency-setup.scss rename to multi-currency/client/setup/tasks/multi-currency-setup.scss diff --git a/client/multi-currency-setup/tasks/setup-complete-task/index.js b/multi-currency/client/setup/tasks/setup-complete-task/index.js similarity index 89% rename from client/multi-currency-setup/tasks/setup-complete-task/index.js rename to multi-currency/client/setup/tasks/setup-complete-task/index.js index e846498df0e..6c467ec3a08 100644 --- a/client/multi-currency-setup/tasks/setup-complete-task/index.js +++ b/multi-currency/client/setup/tasks/setup-complete-task/index.js @@ -10,13 +10,15 @@ import { useDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import CollapsibleBody from '../../../additional-methods-setup/wizard/collapsible-body'; -import WizardTaskItem from '../../wizard/task-item'; -import WizardTaskContext from '../../../additional-methods-setup/wizard/task/context'; +import { + CollapsibleBody, + WizardTaskItem, +} from 'multi-currency/interface/components'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; import './index.scss'; -import { useDefaultCurrency } from 'wcpay/data'; +import { useDefaultCurrency } from 'multi-currency/data'; const SetupComplete = () => { const { isActive } = useContext( WizardTaskContext ); diff --git a/client/multi-currency-setup/tasks/setup-complete-task/index.scss b/multi-currency/client/setup/tasks/setup-complete-task/index.scss similarity index 100% rename from client/multi-currency-setup/tasks/setup-complete-task/index.scss rename to multi-currency/client/setup/tasks/setup-complete-task/index.scss diff --git a/client/multi-currency-setup/tasks/setup-complete-task/test/index.test.js b/multi-currency/client/setup/tasks/setup-complete-task/test/index.test.js similarity index 87% rename from client/multi-currency-setup/tasks/setup-complete-task/test/index.test.js rename to multi-currency/client/setup/tasks/setup-complete-task/test/index.test.js index 48c4ac16d7a..a7a9db81872 100644 --- a/client/multi-currency-setup/tasks/setup-complete-task/test/index.test.js +++ b/multi-currency/client/setup/tasks/setup-complete-task/test/index.test.js @@ -6,14 +6,15 @@ import { render } from '@testing-library/react'; /** * Internal dependencies */ -import WizardTaskContext from '../../../../additional-methods-setup/wizard/task/context'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; import SetupCompleteTask from '../../setup-complete-task'; jest.mock( '@wordpress/data', () => ( { useDispatch: jest.fn().mockReturnValue( { updateOptions: jest.fn() } ), } ) ); -jest.mock( 'wcpay/data', () => ( { +jest.mock( 'multi-currency/interface/data', () => ( {} ) ); +jest.mock( 'multi-currency/data', () => ( { useDefaultCurrency: jest.fn().mockReturnValue( { code: 'USD', rate: 1, diff --git a/client/multi-currency-setup/tasks/store-settings-task/index.js b/multi-currency/client/setup/tasks/store-settings-task/index.js similarity index 92% rename from client/multi-currency-setup/tasks/store-settings-task/index.js rename to multi-currency/client/setup/tasks/store-settings-task/index.js index 7e20fc0f3e4..9a3ca8f0666 100644 --- a/client/multi-currency-setup/tasks/store-settings-task/index.js +++ b/multi-currency/client/setup/tasks/store-settings-task/index.js @@ -9,13 +9,16 @@ import interpolateComponents from '@automattic/interpolate-components'; /** * Internal dependencies */ -import WizardTaskContext from '../../../additional-methods-setup/wizard/task/context'; -import CollapsibleBody from '../../../additional-methods-setup/wizard/collapsible-body'; -import WizardTaskItem from '../../wizard/task-item'; -import PreviewModal from '../../../multi-currency/preview-modal'; +import { + CollapsibleBody, + WizardTaskItem, +} from 'multi-currency/interface/components'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; +import { useSettings, useMultiCurrency } from 'multi-currency/interface/data'; +import PreviewModal from 'multi-currency/components/preview-modal'; import './index.scss'; -import { useStoreSettings, useSettings, useMultiCurrency } from 'wcpay/data'; +import { useStoreSettings } from 'multi-currency/data'; const StoreSettingsTask = () => { const { storeSettings, submitStoreSettingsUpdate } = useStoreSettings(); diff --git a/client/multi-currency-setup/tasks/store-settings-task/index.scss b/multi-currency/client/setup/tasks/store-settings-task/index.scss similarity index 100% rename from client/multi-currency-setup/tasks/store-settings-task/index.scss rename to multi-currency/client/setup/tasks/store-settings-task/index.scss diff --git a/client/multi-currency-setup/tasks/store-settings-task/test/__snapshots__/index.test.js.snap b/multi-currency/client/setup/tasks/store-settings-task/test/__snapshots__/index.test.js.snap similarity index 100% rename from client/multi-currency-setup/tasks/store-settings-task/test/__snapshots__/index.test.js.snap rename to multi-currency/client/setup/tasks/store-settings-task/test/__snapshots__/index.test.js.snap diff --git a/client/multi-currency-setup/tasks/store-settings-task/test/index.test.js b/multi-currency/client/setup/tasks/store-settings-task/test/index.test.js similarity index 92% rename from client/multi-currency-setup/tasks/store-settings-task/test/index.test.js rename to multi-currency/client/setup/tasks/store-settings-task/test/index.test.js index e81b7b08f99..d80e8f8d12a 100644 --- a/client/multi-currency-setup/tasks/store-settings-task/test/index.test.js +++ b/multi-currency/client/setup/tasks/store-settings-task/test/index.test.js @@ -7,21 +7,21 @@ import { render, screen, fireEvent } from '@testing-library/react'; /** * Internal dependencies */ -import WizardTaskContext from '../../../../additional-methods-setup/wizard/task/context'; -import { - useCurrencies, - useStoreSettings, - useSettings, - useMultiCurrency, -} from 'wcpay/data'; +import { useCurrencies, useStoreSettings } from 'multi-currency/data'; +import { useSettings, useMultiCurrency } from 'multi-currency/interface/data'; +import { WizardTaskContext } from 'multi-currency/interface/functions'; import StoreSettingsTask from '..'; -jest.mock( 'wcpay/data', () => ( { +jest.mock( 'multi-currency/data', () => ( { useStoreSettings: jest.fn(), useCurrencies: jest.fn(), useSettings: jest.fn(), useMultiCurrency: jest.fn(), } ) ); +jest.mock( 'multi-currency/interface/data', () => ( { + useSettings: jest.fn(), + useMultiCurrency: jest.fn(), +} ) ); const changeableSettings = [ 'enable_storefront_switcher', diff --git a/client/multi-currency-setup/tasks/test/__snapshots__/multi-currency-setup.test.js.snap b/multi-currency/client/setup/tasks/test/__snapshots__/multi-currency-setup.test.js.snap similarity index 100% rename from client/multi-currency-setup/tasks/test/__snapshots__/multi-currency-setup.test.js.snap rename to multi-currency/client/setup/tasks/test/__snapshots__/multi-currency-setup.test.js.snap diff --git a/client/multi-currency-setup/tasks/test/multi-currency-setup.test.js b/multi-currency/client/setup/tasks/test/multi-currency-setup.test.js similarity index 100% rename from client/multi-currency-setup/tasks/test/multi-currency-setup.test.js rename to multi-currency/client/setup/tasks/test/multi-currency-setup.test.js diff --git a/client/multi-currency-setup/test/index.js b/multi-currency/client/setup/test/index.js similarity index 100% rename from client/multi-currency-setup/test/index.js rename to multi-currency/client/setup/test/index.js diff --git a/client/utils/currency/index.js b/multi-currency/client/utils/currency/index.js similarity index 100% rename from client/utils/currency/index.js rename to multi-currency/client/utils/currency/index.js diff --git a/client/utils/currency/test/index.js b/multi-currency/client/utils/currency/test/index.js similarity index 98% rename from client/utils/currency/test/index.js rename to multi-currency/client/utils/currency/test/index.js index 25e67b62c3c..0a08bbdd7db 100644 --- a/client/utils/currency/test/index.js +++ b/multi-currency/client/utils/currency/test/index.js @@ -6,7 +6,7 @@ /** * Internal dependencies */ -import * as utils from 'utils/currency'; +import * as utils from 'multi-currency/utils/currency'; describe( 'Currency utilities', () => { beforeEach( () => { diff --git a/client/multi-currency/__tests__/missing-currencies-message.test.js b/multi-currency/client/utils/missing-currencies-message/__tests__/index.test.js similarity index 89% rename from client/multi-currency/__tests__/missing-currencies-message.test.js rename to multi-currency/client/utils/missing-currencies-message/__tests__/index.test.js index ac74c4ee517..bd7fcff7840 100644 --- a/client/multi-currency/__tests__/missing-currencies-message.test.js +++ b/multi-currency/client/utils/missing-currencies-message/__tests__/index.test.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getMissingCurrenciesTooltipMessage } from '../missing-currencies-message'; +import { getMissingCurrenciesTooltipMessage } from 'multi-currency/utils/missing-currencies-message'; describe( 'getMissingCurrenciesTooltipMessage', () => { it( 'returns correct string with the given LPM label and currency list', () => { diff --git a/client/multi-currency/missing-currencies-message.ts b/multi-currency/client/utils/missing-currencies-message/index.ts similarity index 91% rename from client/multi-currency/missing-currencies-message.ts rename to multi-currency/client/utils/missing-currencies-message/index.ts index 514ccff973d..b6e68116479 100644 --- a/client/multi-currency/missing-currencies-message.ts +++ b/multi-currency/client/utils/missing-currencies-message/index.ts @@ -6,7 +6,7 @@ import { sprintf, _n } from '@wordpress/i18n'; /** * Internal dependencies */ -import { formatListOfItems } from 'wcpay/utils/format-list-of-items'; +import { formatListOfItems } from 'multi-currency/interface/functions'; export const getMissingCurrenciesTooltipMessage = ( paymentMethodLabel: string, diff --git a/includes/multi-currency/AdminNotices.php b/multi-currency/src/AdminNotices.php similarity index 100% rename from includes/multi-currency/AdminNotices.php rename to multi-currency/src/AdminNotices.php diff --git a/includes/multi-currency/Analytics.php b/multi-currency/src/Analytics.php similarity index 96% rename from includes/multi-currency/Analytics.php rename to multi-currency/src/Analytics.php index ef3f5b17a5a..822ba88efc5 100644 --- a/includes/multi-currency/Analytics.php +++ b/multi-currency/src/Analytics.php @@ -12,7 +12,7 @@ use Automattic\WooCommerce\Utilities\OrderUtil; use WC_Order; use WC_Order_Refund; -use WC_Payments; +use WCPay\MultiCurrency\Interfaces\MultiCurrencySettingsInterface; defined( 'ABSPATH' ) || exit; @@ -42,13 +42,22 @@ class Analytics { */ private $multi_currency; + /** + * Instance of MultiCurrencySettingsInterface. + * + * @var MultiCurrencySettingsInterface $settings_service + */ + private $settings_service; + /** * Constructor * - * @param MultiCurrency $multi_currency Instance of MultiCurrency. + * @param MultiCurrency $multi_currency Instance of MultiCurrency. + * @param MultiCurrencySettingsInterface $settings_service Instance of MultiCurrencySettingsInterface. */ - public function __construct( MultiCurrency $multi_currency ) { - $this->multi_currency = $multi_currency; + public function __construct( MultiCurrency $multi_currency, MultiCurrencySettingsInterface $settings_service ) { + $this->multi_currency = $multi_currency; + $this->settings_service = $settings_service; $this->init(); } @@ -63,7 +72,7 @@ public function init() { $this->register_customer_currencies(); } - if ( WC_Payments::mode()->is_dev() ) { + if ( $this->settings_service->is_dev_mode() ) { add_filter( 'woocommerce_analytics_report_should_use_cache', [ $this, 'disable_report_caching' ] ); } @@ -105,7 +114,7 @@ public function init() { * @return void */ public function register_admin_scripts() { - WC_Payments::register_script_with_dependencies( self::SCRIPT_NAME, 'dist/multi-currency-analytics' ); + $this->multi_currency->register_script_with_dependencies( self::SCRIPT_NAME, 'dist/multi-currency-analytics' ); } /** diff --git a/includes/multi-currency/BackendCurrencies.php b/multi-currency/src/BackendCurrencies.php similarity index 91% rename from includes/multi-currency/BackendCurrencies.php rename to multi-currency/src/BackendCurrencies.php index 9ea009ac4c4..bdb2da92b72 100644 --- a/includes/multi-currency/BackendCurrencies.php +++ b/multi-currency/src/BackendCurrencies.php @@ -7,7 +7,7 @@ namespace WCPay\MultiCurrency; -use WC_Payments_Localization_Service; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; defined( 'ABSPATH' ) || exit; @@ -23,9 +23,9 @@ class BackendCurrencies { protected $multi_currency; /** - * WC_Payments_Localization_Service instance. + * MultiCurrencyLocalizationInterface instance. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ protected $localization_service; @@ -39,10 +39,10 @@ class BackendCurrencies { /** * Constructor. * - * @param MultiCurrency $multi_currency The MultiCurrency instance. - * @param WC_Payments_Localization_Service $localization_service The Localization Service instance. + * @param MultiCurrency $multi_currency The MultiCurrency instance. + * @param MultiCurrencyLocalizationInterface $localization_service The Localization Service instance. */ - public function __construct( MultiCurrency $multi_currency, WC_Payments_Localization_Service $localization_service ) { + public function __construct( MultiCurrency $multi_currency, MultiCurrencyLocalizationInterface $localization_service ) { $this->multi_currency = $multi_currency; $this->localization_service = $localization_service; } diff --git a/includes/multi-currency/Compatibility.php b/multi-currency/src/Compatibility.php similarity index 99% rename from includes/multi-currency/Compatibility.php rename to multi-currency/src/Compatibility.php index 4f60915fb97..87e10cbde78 100644 --- a/includes/multi-currency/Compatibility.php +++ b/multi-currency/src/Compatibility.php @@ -7,8 +7,6 @@ namespace WCPay\MultiCurrency; -use WC_Deposits; -use WC_Deposits_Product_Manager; use WC_Order; use WC_Order_Refund; use WCPay\MultiCurrency\Compatibility\BaseCompatibility; @@ -41,7 +39,7 @@ class Compatibility extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { add_action( 'init', [ $this, 'init_compatibility_classes' ], 11 ); if ( defined( 'DOING_CRON' ) ) { diff --git a/includes/multi-currency/Compatibility/BaseCompatibility.php b/multi-currency/src/Compatibility/BaseCompatibility.php similarity index 95% rename from includes/multi-currency/Compatibility/BaseCompatibility.php rename to multi-currency/src/Compatibility/BaseCompatibility.php index a98073fd113..3e7d1a67a20 100644 --- a/includes/multi-currency/Compatibility/BaseCompatibility.php +++ b/multi-currency/src/Compatibility/BaseCompatibility.php @@ -46,5 +46,5 @@ public function __construct( MultiCurrency $multi_currency, Utils $utils ) { * * @return void */ - abstract protected function init(); + abstract public function init(); } diff --git a/includes/multi-currency/Compatibility/WooCommerceBookings.php b/multi-currency/src/Compatibility/WooCommerceBookings.php similarity index 99% rename from includes/multi-currency/Compatibility/WooCommerceBookings.php rename to multi-currency/src/Compatibility/WooCommerceBookings.php index 5a99534e5d6..756e4eef355 100644 --- a/includes/multi-currency/Compatibility/WooCommerceBookings.php +++ b/multi-currency/src/Compatibility/WooCommerceBookings.php @@ -39,7 +39,7 @@ public function __construct( MultiCurrency $multi_currency, Utils $utils, Fronte * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if Bookings is active. if ( class_exists( 'WC_Bookings' ) ) { if ( ! is_admin() || wp_doing_ajax() ) { diff --git a/includes/multi-currency/Compatibility/WooCommerceDeposits.php b/multi-currency/src/Compatibility/WooCommerceDeposits.php similarity index 99% rename from includes/multi-currency/Compatibility/WooCommerceDeposits.php rename to multi-currency/src/Compatibility/WooCommerceDeposits.php index f92819785c5..e2ffa89d441 100644 --- a/includes/multi-currency/Compatibility/WooCommerceDeposits.php +++ b/multi-currency/src/Compatibility/WooCommerceDeposits.php @@ -20,7 +20,7 @@ class WooCommerceDeposits extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { if ( class_exists( 'WC_Deposits' ) ) { /* * Multi-currency support was added to WooCommerce Deposits in version 2.0.1. diff --git a/includes/multi-currency/Compatibility/WooCommerceFedEx.php b/multi-currency/src/Compatibility/WooCommerceFedEx.php similarity index 97% rename from includes/multi-currency/Compatibility/WooCommerceFedEx.php rename to multi-currency/src/Compatibility/WooCommerceFedEx.php index 738e738150f..8a38d058e40 100644 --- a/includes/multi-currency/Compatibility/WooCommerceFedEx.php +++ b/multi-currency/src/Compatibility/WooCommerceFedEx.php @@ -20,7 +20,7 @@ class WooCommerceFedEx extends BaseCompatibility { * * @return void */ - protected function init() { + 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_return_store_currency', [ $this, 'should_return_store_currency' ] ); diff --git a/includes/multi-currency/Compatibility/WooCommerceNameYourPrice.php b/multi-currency/src/Compatibility/WooCommerceNameYourPrice.php similarity index 99% rename from includes/multi-currency/Compatibility/WooCommerceNameYourPrice.php rename to multi-currency/src/Compatibility/WooCommerceNameYourPrice.php index 155f99e1a4d..fad352e1d1f 100644 --- a/includes/multi-currency/Compatibility/WooCommerceNameYourPrice.php +++ b/multi-currency/src/Compatibility/WooCommerceNameYourPrice.php @@ -21,7 +21,7 @@ class WooCommerceNameYourPrice extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if Name Your Price is active. if ( class_exists( 'WC_Name_Your_Price' ) ) { // Convert meta prices. diff --git a/includes/multi-currency/Compatibility/WooCommercePointsAndRewards.php b/multi-currency/src/Compatibility/WooCommercePointsAndRewards.php similarity index 98% rename from includes/multi-currency/Compatibility/WooCommercePointsAndRewards.php rename to multi-currency/src/Compatibility/WooCommercePointsAndRewards.php index 9d78886eb41..38819d15322 100644 --- a/includes/multi-currency/Compatibility/WooCommercePointsAndRewards.php +++ b/multi-currency/src/Compatibility/WooCommercePointsAndRewards.php @@ -33,7 +33,7 @@ class WooCommercePointsAndRewards extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed filters if Points & Rewards is active and it's not an admin request. if ( is_admin() || ! class_exists( 'WC_Points_Rewards' ) ) { return; diff --git a/includes/multi-currency/Compatibility/WooCommercePreOrders.php b/multi-currency/src/Compatibility/WooCommercePreOrders.php similarity index 96% rename from includes/multi-currency/Compatibility/WooCommercePreOrders.php rename to multi-currency/src/Compatibility/WooCommercePreOrders.php index 3c3fe9d5efc..b16dd91b646 100644 --- a/includes/multi-currency/Compatibility/WooCommercePreOrders.php +++ b/multi-currency/src/Compatibility/WooCommercePreOrders.php @@ -20,7 +20,7 @@ class WooCommercePreOrders extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if Pre-Orders is active. if ( class_exists( 'WC_Pre_Orders' ) ) { add_filter( 'wc_pre_orders_fee', [ $this, 'wc_pre_orders_fee' ] ); diff --git a/includes/multi-currency/Compatibility/WooCommerceProductAddOns.php b/multi-currency/src/Compatibility/WooCommerceProductAddOns.php similarity index 99% rename from includes/multi-currency/Compatibility/WooCommerceProductAddOns.php rename to multi-currency/src/Compatibility/WooCommerceProductAddOns.php index 7d245cd4e4f..add583059f8 100644 --- a/includes/multi-currency/Compatibility/WooCommerceProductAddOns.php +++ b/multi-currency/src/Compatibility/WooCommerceProductAddOns.php @@ -23,7 +23,7 @@ class WooCommerceProductAddOns extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if Product Add Ons is active. if ( class_exists( 'WC_Product_Addons' ) ) { if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) { diff --git a/includes/multi-currency/Compatibility/WooCommerceSubscriptions.php b/multi-currency/src/Compatibility/WooCommerceSubscriptions.php similarity index 97% rename from includes/multi-currency/Compatibility/WooCommerceSubscriptions.php rename to multi-currency/src/Compatibility/WooCommerceSubscriptions.php index 94294827ea9..6701f6739dd 100644 --- a/includes/multi-currency/Compatibility/WooCommerceSubscriptions.php +++ b/multi-currency/src/Compatibility/WooCommerceSubscriptions.php @@ -7,10 +7,8 @@ namespace WCPay\MultiCurrency\Compatibility; -use WC_Payments_Explicit_Price_Formatter; -use WC_Payments_Features; use WC_Subscription; -use WCPay\Logger; +use WCPay\MultiCurrency\Logger; use WCPay\MultiCurrency\FrontendCurrencies; use WCPay\MultiCurrency\MultiCurrency; @@ -61,9 +59,9 @@ class WooCommerceSubscriptions extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if WC Subscriptions or WCPay Subscriptions are active. - if ( class_exists( 'WC_Subscriptions' ) || WC_Payments_Features::is_wcpay_subscriptions_enabled() ) { + if ( class_exists( 'WC_Subscriptions' ) || class_exists( 'WC_Payments_Subscriptions' ) ) { if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) { $this->frontend_currencies = $this->multi_currency->get_frontend_currencies(); @@ -393,6 +391,10 @@ public function maybe_get_explicit_format_for_subscription_total( $html_price, $ return $html_price; } + if ( ! $this->multi_currency->has_additional_currencies_enabled() ) { + return $html_price; + } + /** * Get the currency code from the subscription, then return the explicit price. * Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active. @@ -400,7 +402,15 @@ public function maybe_get_explicit_format_for_subscription_total( $html_price, $ * @psalm-suppress UndefinedDocblockClass */ $currency_code = $this->current_my_account_subscription->get_currency() ?? get_woocommerce_currency(); - return WC_Payments_Explicit_Price_Formatter::get_explicit_price_with_currency( $html_price, $currency_code ); + + // This is sourced from WC_Payments_Explicit_Price_Formatter::get_explicit_price_with_currency. + $price_to_check = html_entity_decode( wp_strip_all_tags( $html_price ) ); + + if ( false === strpos( $price_to_check, trim( $currency_code ) ) ) { + return $html_price . ' ' . $currency_code; + } + + return $html_price; } /** diff --git a/includes/multi-currency/Compatibility/WooCommerceUPS.php b/multi-currency/src/Compatibility/WooCommerceUPS.php similarity index 97% rename from includes/multi-currency/Compatibility/WooCommerceUPS.php rename to multi-currency/src/Compatibility/WooCommerceUPS.php index 6a53f47bff3..427aa060d52 100644 --- a/includes/multi-currency/Compatibility/WooCommerceUPS.php +++ b/multi-currency/src/Compatibility/WooCommerceUPS.php @@ -20,7 +20,7 @@ class WooCommerceUPS extends BaseCompatibility { * * @return void */ - protected function init() { + public function init() { // Add needed actions and filters if UPS is active. if ( class_exists( 'WC_Shipping_UPS_Init' ) ) { add_filter( MultiCurrency::FILTER_PREFIX . 'should_return_store_currency', [ $this, 'should_return_store_currency' ] ); diff --git a/multi-currency/src/CountryFlags.php b/multi-currency/src/CountryFlags.php new file mode 100644 index 00000000000..286e77dfbe2 --- /dev/null +++ b/multi-currency/src/CountryFlags.php @@ -0,0 +1,301 @@ + '🇦🇩', + 'AE' => '🇦🇪', + 'AF' => '🇦🇫', + 'AG' => '🇦🇬', + 'AI' => '🇦🇮', + 'AL' => '🇦🇱', + 'AM' => '🇦🇲', + 'AO' => '🇦🇴', + 'AQ' => '🇦🇶', + 'AR' => '🇦🇷', + 'AS' => '🇦🇸', + 'AT' => '🇦🇹', + 'AU' => '🇦🇺', + 'AW' => '🇦🇼', + 'AX' => '🇦🇽', + 'AZ' => '🇦🇿', + 'BA' => '🇧🇦', + 'BB' => '🇧🇧', + 'BD' => '🇧🇩', + 'BE' => '🇧🇪', + 'BF' => '🇧🇫', + 'BG' => '🇧🇬', + 'BH' => '🇧🇭', + 'BI' => '🇧🇮', + 'BJ' => '🇧🇯', + 'BL' => '🇧🇱', + 'BM' => '🇧🇲', + 'BN' => '🇧🇳', + 'BO' => '🇧🇴', + 'BQ' => '🇧🇶', + 'BR' => '🇧🇷', + 'BS' => '🇧🇸', + 'BT' => '🇧🇹', + 'BV' => '🇧🇻', + 'BW' => '🇧🇼', + 'BY' => '🇧🇾', + 'BZ' => '🇧🇿', + 'CA' => '🇨🇦', + 'CC' => '🇨🇨', + 'CD' => '🇨🇩', + 'CF' => '🇨🇫', + 'CG' => '🇨🇬', + 'CH' => '🇨🇭', + 'CI' => '🇨🇮', + 'CK' => '🇨🇰', + 'CL' => '🇨🇱', + 'CM' => '🇨🇲', + 'CN' => '🇨🇳', + 'CO' => '🇨🇴', + 'CR' => '🇨🇷', + 'CU' => '🇨🇺', + 'CV' => '🇨🇻', + 'CW' => '🇨🇼', + 'CX' => '🇨🇽', + 'CY' => '🇨🇾', + 'CZ' => '🇨🇿', + 'DE' => '🇩🇪', + 'DJ' => '🇩🇯', + 'DK' => '🇩🇰', + 'DM' => '🇩🇲', + 'DO' => '🇩🇴', + 'DZ' => '🇩🇿', + 'EC' => '🇪🇨', + 'EE' => '🇪🇪', + 'EG' => '🇪🇬', + 'EH' => '🇪🇭', + 'ER' => '🇪🇷', + 'ES' => '🇪🇸', + 'ET' => '🇪🇹', + 'EU' => '🇪🇺', + 'FI' => '🇫🇮', + 'FJ' => '🇫🇯', + 'FK' => '🇫🇰', + 'FM' => '🇫🇲', + 'FO' => '🇫🇴', + 'FR' => '🇫🇷', + 'GA' => '🇬🇦', + 'GB' => '🇬🇧', + 'GD' => '🇬🇩', + 'GE' => '🇬🇪', + 'GF' => '🇬🇫', + 'GG' => '🇬🇬', + 'GH' => '🇬🇭', + 'GI' => '🇬🇮', + 'GL' => '🇬🇱', + 'GM' => '🇬🇲', + 'GN' => '🇬🇳', + 'GP' => '🇬🇵', + 'GQ' => '🇬🇶', + 'GR' => '🇬🇷', + 'GS' => '🇬🇸', + 'GT' => '🇬🇹', + 'GU' => '🇬🇺', + 'GW' => '🇬🇼', + 'GY' => '🇬🇾', + 'HK' => '🇭🇰', + 'HM' => '🇭🇲', + 'HN' => '🇭🇳', + 'HR' => '🇭🇷', + 'HT' => '🇭🇹', + 'HU' => '🇭🇺', + 'ID' => '🇮🇩', + 'IE' => '🇮🇪', + 'IL' => '🇮🇱', + 'IM' => '🇮🇲', + 'IN' => '🇮🇳', + 'IO' => '🇮🇴', + 'IQ' => '🇮🇶', + 'IR' => '🇮🇷', + 'IS' => '🇮🇸', + 'IT' => '🇮🇹', + 'JE' => '🇯🇪', + 'JM' => '🇯🇲', + 'JO' => '🇯🇴', + 'JP' => '🇯🇵', + 'KE' => '🇰🇪', + 'KG' => '🇰🇬', + 'KH' => '🇰🇭', + 'KI' => '🇰🇮', + 'KM' => '🇰🇲', + 'KN' => '🇰🇳', + 'KP' => '🇰🇵', + 'KR' => '🇰🇷', + 'KW' => '🇰🇼', + 'KY' => '🇰🇾', + 'KZ' => '🇰🇿', + 'LA' => '🇱🇦', + 'LB' => '🇱🇧', + 'LC' => '🇱🇨', + 'LI' => '🇱🇮', + 'LK' => '🇱🇰', + 'LR' => '🇱🇷', + 'LS' => '🇱🇸', + 'LT' => '🇱🇹', + 'LU' => '🇱🇺', + 'LV' => '🇱🇻', + 'LY' => '🇱🇾', + 'MA' => '🇲🇦', + 'MC' => '🇲🇨', + 'MD' => '🇲🇩', + 'ME' => '🇲🇪', + 'MF' => '🇲🇫', + 'MG' => '🇲🇬', + 'MH' => '🇲🇭', + 'MK' => '🇲🇰', + 'ML' => '🇲🇱', + 'MM' => '🇲🇲', + 'MN' => '🇲🇳', + 'MO' => '🇲🇴', + 'MP' => '🇲🇵', + 'MQ' => '🇲🇶', + 'MR' => '🇲🇷', + 'MS' => '🇲🇸', + 'MT' => '🇲🇹', + 'MU' => '🇲🇺', + 'MV' => '🇲🇻', + 'MW' => '🇲🇼', + 'MX' => '🇲🇽', + 'MY' => '🇲🇾', + 'MZ' => '🇲🇿', + 'NA' => '🇳🇦', + 'NC' => '🇳🇨', + 'NE' => '🇳🇪', + 'NF' => '🇳🇫', + 'NG' => '🇳🇬', + 'NI' => '🇳🇮', + 'NL' => '🇳🇱', + 'NO' => '🇳🇴', + 'NP' => '🇳🇵', + 'NR' => '🇳🇷', + 'NU' => '🇳🇺', + 'NZ' => '🇳🇿', + 'OM' => '🇴🇲', + 'PA' => '🇵🇦', + 'PE' => '🇵🇪', + 'PF' => '🇵🇫', + 'PG' => '🇵🇬', + 'PH' => '🇵🇭', + 'PK' => '🇵🇰', + 'PL' => '🇵🇱', + 'PM' => '🇵🇲', + 'PN' => '🇵🇳', + 'PR' => '🇵🇷', + 'PS' => '🇵🇸', + 'PT' => '🇵🇹', + 'PW' => '🇵🇼', + 'PY' => '🇵🇾', + 'QA' => '🇶🇦', + 'RE' => '🇷🇪', + 'RO' => '🇷🇴', + 'RS' => '🇷🇸', + 'RU' => '🇷🇺', + 'RW' => '🇷🇼', + 'SA' => '🇸🇦', + 'SB' => '🇸🇧', + 'SC' => '🇸🇨', + 'SD' => '🇸🇩', + 'SE' => '🇸🇪', + 'SG' => '🇸🇬', + 'SH' => '🇸🇭', + 'SI' => '🇸🇮', + 'SJ' => '🇸🇯', + 'SK' => '🇸🇰', + 'SL' => '🇸🇱', + 'SM' => '🇸🇲', + 'SN' => '🇸🇳', + 'SO' => '🇸🇴', + 'SR' => '🇸🇷', + 'SS' => '🇸🇸', + 'ST' => '🇸🇹', + 'SV' => '🇸🇻', + 'SX' => '🇸🇽', + 'SY' => '🇸🇾', + 'SZ' => '🇸🇿', + 'TC' => '🇹🇨', + 'TD' => '🇹🇩', + 'TF' => '🇹🇫', + 'TG' => '🇹🇬', + 'TH' => '🇹🇭', + 'TJ' => '🇹🇯', + 'TK' => '🇹🇰', + 'TL' => '🇹🇱', + 'TM' => '🇹🇲', + 'TN' => '🇹🇳', + 'TO' => '🇹🇴', + 'TR' => '🇹🇷', + 'TT' => '🇹🇹', + 'TV' => '🇹🇻', + 'TW' => '🇹🇼', + 'TZ' => '🇹🇿', + 'UA' => '🇺🇦', + 'UG' => '🇺🇬', + 'UM' => '🇺🇲', + 'US' => '🇺🇸', + 'UY' => '🇺🇾', + 'UZ' => '🇺🇿', + 'VA' => '🇻🇦', + 'VC' => '🇻🇨', + 'VE' => '🇻🇪', + 'VG' => '🇻🇬', + 'VI' => '🇻🇮', + 'VN' => '🇻🇳', + 'VU' => '🇻🇺', + 'WF' => '🇼🇫', + 'WS' => '🇼🇸', + 'XK' => '🇽🇰', + 'YE' => '🇾🇪', + 'YT' => '🇾🇹', + 'ZA' => '🇿🇦', + 'ZM' => '🇿🇲', + 'ZW' => '🇿🇼', + ]; + + /** + * Retrieves a flag by country code. + * + * @param string $country country alpha-2 code (ISO 3166) like US. + * @return string + */ + public static function get_by_country( string $country ): string { + return self::EMOJI_COUNTRIES_FLAGS[ $country ] ?? ''; + } + + /** + * Retrieves a flag by currency code. + * + * @param string $currency currency code (ISO 4217) like USD. + * @return string + */ + public static function get_by_currency( string $currency ): string { + $exceptions = [ + 'ANG' => '', + 'BTC' => '', + 'XAF' => '', + 'XCD' => '', + 'XOF' => '', + 'XPF' => '', + ]; + + $flag = $exceptions[ $currency ] ?? self::get_by_country( substr( $currency, 0, -1 ) ); + + return $flag; + } +} diff --git a/includes/multi-currency/Currency.php b/multi-currency/src/Currency.php similarity index 89% rename from includes/multi-currency/Currency.php rename to multi-currency/src/Currency.php index 0d9ae84da42..37b8fd17d12 100644 --- a/includes/multi-currency/Currency.php +++ b/multi-currency/src/Currency.php @@ -7,8 +7,7 @@ namespace WCPay\MultiCurrency; -use WC_Payments_Localization_Service; -use WC_Payments_Utils; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; defined( 'ABSPATH' ) || exit; @@ -67,21 +66,21 @@ class Currency implements \JsonSerializable { private $last_updated; /** - * Instance of WC_Payments_Localization_Service. + * Instance of MultiCurrencyLocalizationInterface. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ private $localization_service; /** * Constructor. * - * @param WC_Payments_Localization_Service $localization_service Localization service instance. - * @param string $code Three letter currency code. - * @param float $rate The conversion rate. - * @param int|null $last_updated The time this currency was last updated. + * @param MultiCurrencyLocalizationInterface $localization_service Localization service instance. + * @param string $code Three letter currency code. + * @param float $rate The conversion rate. + * @param int|null $last_updated The time this currency was last updated. */ - public function __construct( WC_Payments_Localization_Service $localization_service, $code = '', float $rate = 1.0, $last_updated = null ) { + public function __construct( MultiCurrencyLocalizationInterface $localization_service, $code = '', float $rate = 1.0, $last_updated = null ) { $this->localization_service = $localization_service; $this->code = $code; $this->rate = $rate; diff --git a/includes/multi-currency/CurrencySwitcherBlock.php b/multi-currency/src/CurrencySwitcherBlock.php similarity index 97% rename from includes/multi-currency/CurrencySwitcherBlock.php rename to multi-currency/src/CurrencySwitcherBlock.php index 95d4762365c..e13902c6ede 100644 --- a/includes/multi-currency/CurrencySwitcherBlock.php +++ b/multi-currency/src/CurrencySwitcherBlock.php @@ -7,9 +7,7 @@ namespace WCPay\MultiCurrency; -use WC_Payments; use function http_build_query; -use function implode; use function urldecode; defined( 'ABSPATH' ) || exit; @@ -60,7 +58,7 @@ public function init_hooks() { */ public function init_block_widget() { // Automatically load dependencies and version. - WC_Payments::register_script_with_dependencies( 'woocommerce-payments/multi-currency-switcher', 'dist/multi-currency-switcher-block' ); + $this->multi_currency->register_script_with_dependencies( 'woocommerce-payments/multi-currency-switcher', 'dist/multi-currency-switcher-block' ); register_block_type( 'woocommerce-payments/multi-currency-switcher', diff --git a/includes/multi-currency/CurrencySwitcherWidget.php b/multi-currency/src/CurrencySwitcherWidget.php similarity index 100% rename from includes/multi-currency/CurrencySwitcherWidget.php rename to multi-currency/src/CurrencySwitcherWidget.php diff --git a/includes/multi-currency/Exceptions/InvalidCurrencyException.php b/multi-currency/src/Exceptions/InvalidCurrencyException.php similarity index 71% rename from includes/multi-currency/Exceptions/InvalidCurrencyException.php rename to multi-currency/src/Exceptions/InvalidCurrencyException.php index 454fa1c7383..c3ec9046a27 100644 --- a/includes/multi-currency/Exceptions/InvalidCurrencyException.php +++ b/multi-currency/src/Exceptions/InvalidCurrencyException.php @@ -7,11 +7,11 @@ namespace WCPay\MultiCurrency\Exceptions; -use WCPay\Exceptions\Base_Exception; +use Exception; defined( 'ABSPATH' ) || exit; /** * Exception for throwing errors when an invalid currency is used. */ -class InvalidCurrencyException extends Base_Exception {} +class InvalidCurrencyException extends Exception {} diff --git a/includes/multi-currency/Exceptions/InvalidCurrencyRateException.php b/multi-currency/src/Exceptions/InvalidCurrencyRateException.php similarity index 71% rename from includes/multi-currency/Exceptions/InvalidCurrencyRateException.php rename to multi-currency/src/Exceptions/InvalidCurrencyRateException.php index e80cc0cb92d..6f0b5b2c007 100644 --- a/includes/multi-currency/Exceptions/InvalidCurrencyRateException.php +++ b/multi-currency/src/Exceptions/InvalidCurrencyRateException.php @@ -7,11 +7,11 @@ namespace WCPay\MultiCurrency\Exceptions; -use WCPay\Exceptions\Base_Exception; +use Exception; defined( 'ABSPATH' ) || exit; /** * Exception for throwing errors when an invalid currency rate is used. */ -class InvalidCurrencyRateException extends Base_Exception {} +class InvalidCurrencyRateException extends Exception {} diff --git a/includes/multi-currency/FrontendCurrencies.php b/multi-currency/src/FrontendCurrencies.php similarity index 94% rename from includes/multi-currency/FrontendCurrencies.php rename to multi-currency/src/FrontendCurrencies.php index da1342ac55a..065d0db24a8 100644 --- a/includes/multi-currency/FrontendCurrencies.php +++ b/multi-currency/src/FrontendCurrencies.php @@ -8,7 +8,7 @@ namespace WCPay\MultiCurrency; use WC_Order; -use WC_Payments_Localization_Service; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; defined( 'ABSPATH' ) || exit; @@ -24,9 +24,9 @@ class FrontendCurrencies { protected $multi_currency; /** - * WC_Payments_Localization_Service instance. + * MultiCurrencyLocalizationInterface instance. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ protected $localization_service; @@ -89,12 +89,12 @@ class FrontendCurrencies { /** * Constructor. * - * @param MultiCurrency $multi_currency The MultiCurrency instance. - * @param WC_Payments_Localization_Service $localization_service The Localization Service instance. - * @param Utils $utils Utils instance. - * @param Compatibility $compatibility Compatibility instance. + * @param MultiCurrency $multi_currency The MultiCurrency instance. + * @param MultiCurrencyLocalizationInterface $localization_service The Localization Service instance. + * @param Utils $utils Utils instance. + * @param Compatibility $compatibility Compatibility instance. */ - public function __construct( MultiCurrency $multi_currency, WC_Payments_Localization_Service $localization_service, Utils $utils, Compatibility $compatibility ) { + public function __construct( MultiCurrency $multi_currency, MultiCurrencyLocalizationInterface $localization_service, Utils $utils, Compatibility $compatibility ) { $this->multi_currency = $multi_currency; $this->localization_service = $localization_service; $this->utils = $utils; diff --git a/includes/multi-currency/FrontendPrices.php b/multi-currency/src/FrontendPrices.php similarity index 100% rename from includes/multi-currency/FrontendPrices.php rename to multi-currency/src/FrontendPrices.php diff --git a/includes/multi-currency/Geolocation.php b/multi-currency/src/Geolocation.php similarity index 84% rename from includes/multi-currency/Geolocation.php rename to multi-currency/src/Geolocation.php index 5bd0b48b7db..4f5da245958 100644 --- a/includes/multi-currency/Geolocation.php +++ b/multi-currency/src/Geolocation.php @@ -7,7 +7,7 @@ namespace WCPay\MultiCurrency; -use WC_Payments_Localization_Service; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; defined( 'ABSPATH' ) || exit; @@ -16,18 +16,18 @@ */ class Geolocation { /** - * WC_Payments_Localization_Service instance. + * MultiCurrencyLocalizationInterface instance. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ protected $localization_service; /** * Constructor. * - * @param WC_Payments_Localization_Service $localization_service The Localization Service instance. + * @param MultiCurrencyLocalizationInterface $localization_service The Localization Service instance. */ - public function __construct( WC_Payments_Localization_Service $localization_service ) { + public function __construct( MultiCurrencyLocalizationInterface $localization_service ) { $this->localization_service = $localization_service; } diff --git a/multi-currency/src/Interfaces/MultiCurrencyAccountInterface.php b/multi-currency/src/Interfaces/MultiCurrencyAccountInterface.php new file mode 100644 index 00000000000..b827c5ca3d8 --- /dev/null +++ b/multi-currency/src/Interfaces/MultiCurrencyAccountInterface.php @@ -0,0 +1,61 @@ +log( $level, $message, [ 'source' => self::LOG_FILE ] ); + } +} diff --git a/includes/multi-currency/MultiCurrency.php b/multi-currency/src/MultiCurrency.php similarity index 89% rename from includes/multi-currency/MultiCurrency.php rename to multi-currency/src/MultiCurrency.php index 0d80518298e..56e2b5815b4 100644 --- a/includes/multi-currency/MultiCurrency.php +++ b/multi-currency/src/MultiCurrency.php @@ -7,20 +7,16 @@ namespace WCPay\MultiCurrency; -use WC_Payments; -use WC_Payments_Account; -use WC_Payments_Utils; -use WC_Payments_API_Client; -use WC_Payments_Localization_Service; -use WCPay\Constants\Country_Code; -use WCPay\Constants\Currency_Code; -use WCPay\Exceptions\API_Exception; -use WCPay\Database_Cache; -use WCPay\Logger; use WCPay\MultiCurrency\Exceptions\InvalidCurrencyException; use WCPay\MultiCurrency\Exceptions\InvalidCurrencyRateException; -use WCPay\MultiCurrency\Helpers\OrderMetaHelper; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyApiClientInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyCacheInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencySettingsInterface; +use WCPay\MultiCurrency\Logger; use WCPay\MultiCurrency\Notes\NoteMultiCurrencyAvailable; +use WCPay\MultiCurrency\Utils; defined( 'ABSPATH' ) || exit; @@ -41,13 +37,6 @@ class MultiCurrency { */ public $id = 'wcpay_multi_currency'; - /** - * The single instance of the class. - * - * @var ?MultiCurrency - */ - protected static $instance = null; - /** * Static flag to show if the currencies initialization has been completed * @@ -140,32 +129,39 @@ class MultiCurrency { protected $enabled_currencies; /** - * Client for making requests to the WooCommerce Payments API + * Instance of MultiCurrencySettingsInterface. + * + * @var MultiCurrencySettingsInterface + */ + private $settings_service; + + /** + * Client for making requests to the API * - * @var WC_Payments_API_Client + * @var MultiCurrencyApiClientInterface */ private $payments_api_client; /** - * Instance of WC_Payments_Account. + * Instance of MultiCurrencyAccountInterface. * - * @var WC_Payments_Account + * @var MultiCurrencyAccountInterface */ private $payments_account; /** - * Instance of WC_Payments_Localization_Service. + * Instance of MultiCurrencyLocalizationInterface. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ private $localization_service; /** - * Instance of Database_Cache. + * Instance of MultiCurrencyCacheInterface. * - * @var Database_Cache + * @var MultiCurrencyCacheInterface */ - private $database_cache; + private $cache; /** * Tracking instance. @@ -181,43 +177,23 @@ class MultiCurrency { */ protected $simulation_params = []; - /** - * Instance of OrderMetaHelper. - * - * @var OrderMetaHelper - */ - private $order_meta_helper; - - /** - * Main MultiCurrency Instance. - * - * Ensures only one instance of MultiCurrency is loaded or can be loaded. - * - * @static - * @return MultiCurrency - Main instance. - */ - public static function instance() { - if ( is_null( self::$instance ) ) { - self::$instance = new self( WC_Payments::get_payments_api_client(), WC_Payments::get_account_service(), WC_Payments::get_localization_service(), WC_Payments::get_database_cache() ); - self::$instance->init_hooks(); - } - return self::$instance; - } /** * Class constructor. * - * @param WC_Payments_API_Client $payments_api_client Payments API client. - * @param WC_Payments_Account $payments_account Payments Account instance. - * @param WC_Payments_Localization_Service $localization_service Localization Service instance. - * @param Database_Cache $database_cache Database Cache instance. - * @param Utils|null $utils Optional Utils instance. + * @param MultiCurrencySettingsInterface $settings_service Settings service. + * @param MultiCurrencyApiClientInterface $payments_api_client Payments API client. + * @param MultiCurrencyAccountInterface $payments_account Payments Account instance. + * @param MultiCurrencyLocalizationInterface $localization_service Localization Service instance. + * @param MultiCurrencyCacheInterface $cache Cache instance. + * @param Utils|null $utils Optional Utils instance. */ - public function __construct( WC_Payments_API_Client $payments_api_client, WC_Payments_Account $payments_account, WC_Payments_Localization_Service $localization_service, Database_Cache $database_cache, Utils $utils = null ) { + public function __construct( MultiCurrencySettingsInterface $settings_service, MultiCurrencyApiClientInterface $payments_api_client, MultiCurrencyAccountInterface $payments_account, MultiCurrencyLocalizationInterface $localization_service, MultiCurrencyCacheInterface $cache, Utils $utils = null ) { + $this->settings_service = $settings_service; $this->payments_api_client = $payments_api_client; $this->payments_account = $payments_account; $this->localization_service = $localization_service; - $this->database_cache = $database_cache; + $this->cache = $cache; // If a Utils instance is not passed as argument, initialize it. This allows to mock it in tests. $this->utils = $utils ?? new Utils(); $this->geolocation = new Geolocation( $this->localization_service ); @@ -225,6 +201,19 @@ public function __construct( WC_Payments_API_Client $payments_api_client, WC_Pay $this->currency_switcher_block = new CurrencySwitcherBlock( $this, $this->compatibility ); } + /** + * Backwards compatibility for the old `instance()` static method. + * + * We need to use this as some plugins still call `MultiCurrency::instance()` directly. + * + * @return null|MultiCurrency - Main instance. + */ + public static function instance() { + if ( function_exists( 'WC_Payments_Multi_Currency' ) ) { + return WC_Payments_Multi_Currency(); + } + } + /** * Initializes this class' WP hooks. * @@ -242,9 +231,9 @@ public function init_hooks() { add_action( 'rest_api_init', [ $this, 'init_rest_api' ] ); add_action( 'widgets_init', [ $this, 'init_widgets' ] ); - $is_frontend_request = ! is_admin() && ! defined( 'DOING_CRON' ) && ! WC()->is_rest_api_request(); + $is_frontend_request = ! is_admin() && ! defined( 'DOING_CRON' ) && ! Utils::is_admin_api_request(); - if ( $is_frontend_request || \WC_Payments_Utils::is_store_api_request() ) { + if ( $is_frontend_request || Utils::is_store_api_request() ) { // Make sure that this runs after the main init function. add_action( 'init', [ $this, 'update_selected_currency_by_url' ], 11 ); add_action( 'init', [ $this, 'update_selected_currency_by_geolocation' ], 12 ); @@ -252,7 +241,7 @@ public function init_hooks() { add_action( 'woocommerce_created_customer', [ $this, 'set_new_customer_currency_meta' ] ); } - if ( ! \WC_Payments_Utils::is_store_batch_request() && ! \WC_Payments_Utils::is_store_api_request() && WC()->is_rest_api_request() ) { + if ( ! Utils::is_store_batch_request() && ! Utils::is_store_api_request() && WC()->is_rest_api_request() ) { if ( isset( $_GET['currency'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification $get_currency_from_query_param = function () { $currency = sanitize_text_field( wp_unslash( $_GET['currency'] ) ); // phpcs:ignore WordPress.Security.NonceVerification @@ -298,26 +287,22 @@ public function init() { $this->update_manual_rate_currencies_notice_option(); } - $payment_method_compat = new PaymentMethodsCompatibility( $this, WC_Payments::get_gateway() ); - $admin_notices = new AdminNotices(); - $user_settings = new UserSettings( $this ); - new Analytics( $this ); + $admin_notices = new AdminNotices(); + $user_settings = new UserSettings( $this ); + new Analytics( $this, $this->settings_service ); $this->frontend_prices = new FrontendPrices( $this, $this->compatibility ); $this->frontend_currencies = new FrontendCurrencies( $this, $this->localization_service, $this->utils, $this->compatibility ); $this->backend_currencies = new BackendCurrencies( $this, $this->localization_service ); $this->tracking = new Tracking( $this ); - $this->order_meta_helper = new OrderMetaHelper( $this->payments_api_client ); // Init all of the hooks. - $payment_method_compat->init_hooks(); $admin_notices->init_hooks(); $user_settings->init_hooks(); $this->frontend_prices->init_hooks(); $this->frontend_currencies->init_hooks(); $this->backend_currencies->init_hooks(); $this->tracking->init_hooks(); - $this->order_meta_helper->init_hooks(); add_action( 'woocommerce_order_refunded', [ $this, 'add_order_meta_on_refund' ], 50, 2 ); @@ -328,7 +313,7 @@ public function init() { } if ( is_admin() ) { - add_action( 'admin_init', [ __CLASS__, 'add_woo_admin_notes' ] ); + add_action( 'admin_init', [ $this, 'add_woo_admin_notes' ] ); } // Update the customer currencies option after an order status change. @@ -350,7 +335,7 @@ public function init_rest_api() { return; } - $api_controller = new RestController( \WC_Payments::create_api_client() ); + $api_controller = new RestController( $this ); $api_controller->register_routes(); } @@ -373,19 +358,19 @@ public function init_widgets() { * @return array The new settings pages. */ public function init_settings_pages( $settings_pages ): array { - // We don't need to check if Stripe is connected for the + // We don't need to check if the payment provider is connected for the // Settings page generation on the incoming CLI and async job calls. if ( ( defined( 'WP_CLI' ) && WP_CLI ) || ( defined( 'WPCOM_JOBS' ) && WPCOM_JOBS ) ) { return $settings_pages; } - if ( $this->payments_account->is_stripe_connected() ) { + if ( $this->payments_account->is_provider_connected() ) { $settings = new Settings( $this ); $settings->init_hooks(); $settings_pages[] = $settings; } else { - $settings_onboard_cta = new SettingsOnboardCta( $this ); + $settings_onboard_cta = new SettingsOnboardCta( $this, $this->payments_account ); $settings_onboard_cta->init_hooks(); $settings_pages[] = $settings_onboard_cta; @@ -410,7 +395,7 @@ public function enqueue_admin_scripts() { $this->register_admin_scripts(); wp_enqueue_script( 'WCPAY_MULTI_CURRENCY_SETTINGS' ); - WC_Payments_Utils::enqueue_style( 'WCPAY_MULTI_CURRENCY_SETTINGS' ); + wp_enqueue_style( 'WCPAY_MULTI_CURRENCY_SETTINGS' ); } /** @@ -433,7 +418,7 @@ public function add_props_to_wcpay_js_config( $config ) { */ public function clear_cache() { Logger::debug( 'Clearing the cache to force new rates to be fetched from the server.' ); - $this->database_cache->delete( Database_Cache::CURRENCIES_KEY ); + $this->cache->delete( MultiCurrencyCacheInterface::CURRENCIES_KEY ); } /** @@ -444,14 +429,14 @@ public function clear_cache() { * @return ?array */ public function get_cached_currencies() { - $cached_data = $this->database_cache->get( Database_Cache::CURRENCIES_KEY ); - // If connection to server cannot be established, or if Stripe is not connected, or if the account is rejected, return expired data or null. - if ( ! $this->payments_api_client->is_server_connected() || ! $this->payments_account->is_stripe_connected() || $this->payments_account->is_account_rejected() ) { + $cached_data = $this->cache->get( MultiCurrencyCacheInterface::CURRENCIES_KEY ); + // If connection to server cannot be established, or if payment provider is not connected, or if the account is rejected, return expired data or null. + if ( ! $this->payments_api_client->is_server_connected() || ! $this->payments_account->is_provider_connected() || $this->payments_account->is_account_rejected() ) { return $cached_data ?? null; } - return $this->database_cache->get_or_add( - Database_Cache::CURRENCIES_KEY, + return $this->cache->get_or_add( + MultiCurrencyCacheInterface::CURRENCIES_KEY, function () { try { $currency_data = $this->payments_api_client->get_currency_rates( strtolower( get_woocommerce_currency() ) ); @@ -459,7 +444,7 @@ function () { 'currencies' => $currency_data, 'updated' => time(), ]; - } catch ( API_Exception $e ) { + } catch ( \Exception $e ) { return null; } }, @@ -601,7 +586,7 @@ public function update_single_currency_settings( string $currency_code, string $ if ( ! is_numeric( $manual_rate ) || 0 >= $manual_rate ) { $message = 'Invalid manual currency rate passed to update_single_currency_settings: ' . $manual_rate; Logger::error( $message ); - throw new InvalidCurrencyRateException( esc_html( $message ), 'wcpay_multi_currency_invalid_currency_rate', 500 ); + throw new InvalidCurrencyRateException( esc_html( $message ), 500 ); } update_option( 'wcpay_multi_currency_manual_rate_' . $currency_code, $manual_rate ); } @@ -639,94 +624,6 @@ public function maybe_update_customer_currencies_option( $order_id ) { update_option( self::CUSTOMER_CURRENCIES_KEY, $currencies ); } - /** - * Sets up the available currencies, which are alphabetical by name. - * - * @return void - */ - private function initialize_available_currencies() { - // Add default store currency with a rate of 1.0. - $woocommerce_currency = get_woocommerce_currency(); - $this->available_currencies[ $woocommerce_currency ] = new Currency( $this->localization_service, $woocommerce_currency, 1.0 ); - - $available_currencies = []; - - $currencies = $this->get_account_available_currencies(); - $cache_data = $this->get_cached_currencies(); - - foreach ( $currencies as $currency_code ) { - $currency_rate = $cache_data['currencies'][ $currency_code ] ?? 1.0; - $update_time = $cache_data['updated'] ?? null; - $new_currency = new Currency( $this->localization_service, $currency_code, $currency_rate, $update_time ); - - // Add this to our list of available currencies. - $available_currencies[ $new_currency->get_name() ] = $new_currency; - } - - ksort( $available_currencies ); - - foreach ( $available_currencies as $currency ) { - $this->available_currencies[ $currency->get_code() ] = $currency; - } - } - - /** - * Sets up the enabled currencies. - * - * @return void - */ - private function initialize_enabled_currencies() { - $available_currencies = $this->get_available_currencies(); - $enabled_currency_codes = get_option( $this->id . '_enabled_currencies', [] ); - $enabled_currency_codes = is_array( $enabled_currency_codes ) ? $enabled_currency_codes : []; - $default_code = $this->get_default_currency()->get_code(); - $default = []; - $enabled_currency_codes[] = $default_code; - - // This allows to keep the alphabetical sorting by name. - $enabled_currencies = array_filter( - $available_currencies, - function ( $currency ) use ( $enabled_currency_codes ) { - return in_array( $currency->get_code(), $enabled_currency_codes, true ); - } - ); - - $this->enabled_currencies = []; - - foreach ( $enabled_currencies as $enabled_currency ) { - // Get the charm and rounding for each enabled currency and add the currencies to the object property. - $currency = clone $enabled_currency; - $charm = get_option( $this->id . '_price_charm_' . $currency->get_id(), 0.00 ); - $rounding = get_option( $this->id . '_price_rounding_' . $currency->get_id(), $currency->get_is_zero_decimal() ? '100' : '1.00' ); - $currency->set_charm( $charm ); - $currency->set_rounding( $rounding ); - - // If the currency is set to be manual, set the rate to the stored manual rate. - $type = get_option( $this->id . '_exchange_rate_' . $currency->get_id(), 'automatic' ); - if ( 'manual' === $type ) { - $manual_rate = get_option( $this->id . '_manual_rate_' . $currency->get_id(), $currency->get_rate() ); - $currency->set_rate( $manual_rate ); - } - - $this->enabled_currencies[ $currency->get_code() ] = $currency; - } - - // Set default currency to the top of the list. - $default[ $default_code ] = $this->enabled_currencies[ $default_code ]; - unset( $this->enabled_currencies[ $default_code ] ); - $this->enabled_currencies = array_merge( $default, $this->enabled_currencies ); - } - - /** - * Sets the default currency. - * - * @return void - */ - private function set_default_currency() { - $available_currencies = $this->get_available_currencies(); - $this->default_currency = $available_currencies[ get_woocommerce_currency() ] ?? null; - } - /** * Gets the currencies available. Initializes it if needed. * @@ -981,7 +878,7 @@ public function get_raw_conversion( float $amount, string $to_currency, string $ if ( 0 >= $from_currency_rate ) { $message = 'Invalid rate for from_currency in get_raw_conversion: ' . $from_currency_rate; Logger::error( $message ); - throw new InvalidCurrencyRateException( esc_html( $message ), 'wcpay_multi_currency_invalid_currency_rate', 500 ); + throw new InvalidCurrencyRateException( esc_html( $message ), 500 ); } $amount = $amount * ( $to_currency_rate / $from_currency_rate ); @@ -1057,24 +954,20 @@ public function display_geolocation_currency_update_notice() { } $message = sprintf( - /* translators: %1 User's country, %2 Selected currency name, %3 Default store currency name */ - __( 'We noticed you\'re visiting from %1$s. We\'ve updated our prices to %2$s for your shopping convenience. Use %3$s instead.', 'woocommerce-payments' ), + /* translators: %1 User's country, %2 Selected currency name, %3 Default store currency name, %4 Link to switch currency */ + __( 'We noticed you\'re visiting from %1$s. We\'ve updated our prices to %2$s for your shopping convenience. Use %3$s instead.', 'woocommerce-payments' ), apply_filters( self::FILTER_PREFIX . 'override_notice_country', WC()->countries->countries[ $country ] ), apply_filters( self::FILTER_PREFIX . 'override_notice_currency_name', $current_currency->get_name() ), - $currencies[ $store_currency ] + esc_html( $currencies[ $store_currency ] ), + esc_url( '?currency=' . $store_currency ) ); $notice_id = md5( $message ); echo ''; } @@ -1098,14 +991,14 @@ public function set_new_customer_currency_meta( $customer_id ) { * * @return void */ - public static function add_woo_admin_notes() { + public function add_woo_admin_notes() { // Do not try to add notes on ajax requests to improve their performance. if ( wp_doing_ajax() ) { return; } if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '4.4.0', '>=' ) ) { - NoteMultiCurrencyAvailable::set_account( WC_Payments::get_account_service() ); + NoteMultiCurrencyAvailable::set_account( $this->payments_account ); NoteMultiCurrencyAvailable::possibly_add_note(); } } @@ -1122,284 +1015,130 @@ public static function remove_woo_admin_notes() { } /** - * Gets the price after adjusting it with the rounding and charm settings. - * - * @param float $price The price to be adjusted. - * @param bool $apply_charm_pricing Whether charm pricing should be applied. - * @param Currency $currency The currency to be used when adjusting. + * Checks if the merchant has enabled automatic currency switching and geolocation. * - * @return float The adjusted price. + * @return bool */ - protected function get_adjusted_price( $price, $apply_charm_pricing, $currency ): float { - $price = $this->ceil_price( $price, (float) $currency->get_rounding() ); - - if ( $apply_charm_pricing ) { - $price += (float) $currency->get_charm(); - } - - // Do not return negative prices (possible because of $currency->get_charm()). - return max( 0, $price ); + public function is_using_auto_currency_switching(): bool { + return 'yes' === get_option( $this->id . '_enable_auto_currency', 'no' ); } /** - * Ceils the price to the next number based on the rounding value. - * - * @param float $price The price to be ceiled. - * @param float $rounding The rounding option. + * Checks if the merchant has enabled the currency switcher widget. * - * @return float The ceiled price. + * @return bool */ - protected function ceil_price( float $price, float $rounding ): float { - if ( 0.00 === $rounding ) { - return $price; - } - return ceil( $price / $rounding ) * $rounding; + public function is_using_storefront_switcher(): bool { + return 'yes' === get_option( $this->id . '_enable_storefront_switcher', 'no' ); } /** - * Returns the currency code stored for the user or in the session. + * Gets the store settings. * - * @return string|null Currency code. + * @return array The store settings. */ - private function get_stored_currency_code() { - $user_id = get_current_user_id(); - - if ( $user_id ) { - return get_user_meta( $user_id, self::CURRENCY_META_KEY, true ); - } - - WC()->initialize_session(); - $currency_code = WC()->session->get( self::CURRENCY_SESSION_KEY ); - - return is_string( $currency_code ) ? $currency_code : null; + public function get_settings() { + return [ + $this->id . '_enable_auto_currency' => $this->is_using_auto_currency_switching(), + $this->id . '_enable_storefront_switcher' => $this->is_using_storefront_switcher(), + 'site_theme' => wp_get_theme()->get( 'Name' ), + 'date_format' => esc_attr( get_option( 'date_format', 'F j, Y' ) ), + 'time_format' => esc_attr( get_option( 'time_format', 'g:i a' ) ), + 'store_url' => esc_attr( get_page_uri( wc_get_page_id( 'shop' ) ) ), + ]; } /** - * Checks to see if the store currency has changed. If it has, this will - * also update the option containing the store currency. + * Updates the store settings * - * @return bool + * @param array $params Update requested values. + * + * @return void */ - private function check_store_currency_for_change(): bool { - $last_known_currency = get_option( $this->id . '_store_currency', false ); - $woocommerce_currency = get_woocommerce_currency(); - - // If the last known currency was not set, update the option to set it and return false. - if ( ! $last_known_currency ) { - update_option( $this->id . '_store_currency', $woocommerce_currency ); - return false; - } + public function update_settings( $params ) { + $updateable_options = [ + 'wcpay_multi_currency_enable_auto_currency', + 'wcpay_multi_currency_enable_storefront_switcher', + ]; - if ( $last_known_currency !== $woocommerce_currency ) { - update_option( $this->id . '_store_currency', $woocommerce_currency ); - return true; + foreach ( $updateable_options as $key ) { + if ( isset( $params[ $key ] ) ) { + update_option( $key, sanitize_text_field( $params[ $key ] ) ); + } } - - return false; } /** - * Called when the store currency has changed. Puts any manual rate currencies into an option for a notice to display. + * Apply client order currency format and reduces the rounding precision to 2. * - * @return void + * @return void */ - private function update_manual_rate_currencies_notice_option() { - $enabled_currencies = $this->get_enabled_currencies(); - $manual_currencies = []; - - // Check enabled currencies for manual rates. - foreach ( $enabled_currencies as $currency ) { - $rate_type = get_option( $this->id . '_exchange_rate_' . $currency->get_id(), false ); - if ( 'manual' === $rate_type ) { - $manual_currencies[] = $currency->get_name(); + public function set_client_format_and_rounding_precision() { + $screen = get_current_screen(); + if ( in_array( $screen->id, [ 'shop_order', 'woocommerce_page_wc-orders' ], true ) ) : + $order = wc_get_order(); + if ( ! $order ) { + return; } - } + $currency = $order->get_currency(); + $currency_format_num_decimals = $this->backend_currencies->get_price_decimals( $currency ); + $currency_format_decimal_sep = $this->backend_currencies->get_price_decimal_separator( $currency ); + $currency_format_thousand_sep = $this->backend_currencies->get_price_thousand_separator( $currency ); + $currency_format = str_replace( [ '%1$s', '%2$s', ' ' ], [ '%s', '%v', ' ' ], $this->backend_currencies->get_woocommerce_price_format( $currency ) ); - if ( 0 < count( $manual_currencies ) ) { - update_option( $this->id . '_show_store_currency_changed_notice', $manual_currencies ); - } + $rounding_precision = wc_get_price_decimals() ?? wc_get_rounding_precision(); + ?> + + remove_currency_settings( $currency ); - } + public function register_script_with_dependencies( string $handler, string $script, array $additional_dependencies = [] ) { + $script_file = $script . '.js'; + $script_src_url = plugins_url( $script_file, $this->settings_service->get_plugin_file_path() ); + $script_asset_path = plugin_dir_path( $this->settings_service->get_plugin_file_path() ) . $script . '.asset.php'; + $script_asset = file_exists( $script_asset_path ) ? require $script_asset_path : [ 'dependencies' => [] ]; // nosemgrep: audit.php.lang.security.file.inclusion-arg -- server generated path is used. + $all_dependencies = array_merge( $script_asset['dependencies'], $additional_dependencies ); + + wp_register_script( + $handler, + $script_src_url, + $all_dependencies, + $this->get_file_version( $script_file ), + true + ); } /** - * Will remove a currency's settings if it is not enabled. + * Get the file modified time as a cache buster if we're in dev mode. * - * @param mixed $currency Currency object or 3 letter currency code. + * @param string $file Local path to the file. * - * @return void + * @return string */ - private function remove_currency_settings( $currency ) { - $code = is_a( $currency, Currency::class ) ? $currency->get_code() : strtoupper( $currency ); + public function get_file_version( $file ) { + $plugin_path = plugin_dir_path( $this->settings_service->get_plugin_file_path() ); - // Bail if the currency code passed is not 3 characters, or if the currency is presently enabled. - if ( 3 !== strlen( $code ) || isset( $this->get_enabled_currencies()[ $code ] ) ) { - return; - } - - $settings = [ - 'price_charm', - 'price_rounding', - 'manual_rate', - 'exchange_rate', - ]; - - // Go through each setting and remove them. - foreach ( $settings as $setting ) { - delete_option( $this->id . '_' . $setting . '_' . strtolower( $code ) ); - } - } - - /** - * Returns the currencies enabled for the Stripe account that are - * also available in WC. - * - * Can be filtered with the 'wcpay_multi_currency_available_currencies' hook. - * - * @return array Array with the available currencies' codes. - */ - private function get_account_available_currencies(): array { - // If Stripe is not connected, return an empty array. This prevents using MC without being connected to Stripe. - if ( ! $this->payments_account->is_stripe_connected() ) { - return []; - } - - $wc_currencies = array_keys( get_woocommerce_currencies() ); - $account_currencies = $wc_currencies; - - $account = $this->payments_account->get_cached_account_data(); - $supported_currencies = $this->payments_account->get_account_customer_supported_currencies(); - if ( $account && ! empty( $supported_currencies ) ) { - $account_currencies = array_map( 'strtoupper', $supported_currencies ); - } - - /** - * Filter the available currencies for WooCommerce Multi-Currency. - * - * This filter can be used to modify the currencies available for WC Pay - * Multi-Currency. Currencies have to be added in uppercase and should - * also be available in `get_woocommerce_currencies` for them to work. - * - * @since 2.8.0 - * - * @param array $available_currencies Current available currencies. Calculated based on - * WC Pay's account currencies and WC currencies. - */ - return apply_filters( self::FILTER_PREFIX . 'available_currencies', array_intersect( $account_currencies, $wc_currencies ) ); - } - - /** - * Checks if the merchant has enabled automatic currency switching and geolocation. - * - * @return bool - */ - public function is_using_auto_currency_switching(): bool { - return 'yes' === get_option( $this->id . '_enable_auto_currency', 'no' ); - } - - /** - * Checks if the merchant has enabled the currency switcher widget. - * - * @return bool - */ - public function is_using_storefront_switcher(): bool { - return 'yes' === get_option( $this->id . '_enable_storefront_switcher', 'no' ); - } - - /** - * Gets the store settings. - * - * @return array The store settings. - */ - public function get_settings() { - return [ - $this->id . '_enable_auto_currency' => $this->is_using_auto_currency_switching(), - $this->id . '_enable_storefront_switcher' => $this->is_using_storefront_switcher(), - 'site_theme' => wp_get_theme()->get( 'Name' ), - 'date_format' => esc_attr( get_option( 'date_format', 'F j, Y' ) ), - 'time_format' => esc_attr( get_option( 'time_format', 'g:i a' ) ), - 'store_url' => esc_attr( get_page_uri( wc_get_page_id( 'shop' ) ) ), - ]; - } - - /** - * Updates the store settings - * - * @param array $params Update requested values. - * - * @return void - */ - public function update_settings( $params ) { - $updateable_options = [ - 'wcpay_multi_currency_enable_auto_currency', - 'wcpay_multi_currency_enable_storefront_switcher', - ]; - - foreach ( $updateable_options as $key ) { - if ( isset( $params[ $key ] ) ) { - update_option( $key, sanitize_text_field( $params[ $key ] ) ); - } + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG && file_exists( $plugin_path . $file ) ) { + return (string) filemtime( $plugin_path . trim( $file, '/' ) ); } - } - - /** - * Apply client order currency format and reduces the rounding precision to 2. - * - * @return void - */ - public function set_client_format_and_rounding_precision() { - $screen = get_current_screen(); - if ( in_array( $screen->id, [ 'shop_order', 'woocommerce_page_wc-orders' ], true ) ) : - $order = wc_get_order(); - if ( ! $order ) { - return; - } - $currency = $order->get_currency(); - $currency_format_num_decimals = $this->backend_currencies->get_price_decimals( $currency ); - $currency_format_decimal_sep = $this->backend_currencies->get_price_decimal_separator( $currency ); - $currency_format_thousand_sep = $this->backend_currencies->get_price_thousand_separator( $currency ); - $currency_format = str_replace( [ '%1$s', '%2$s', ' ' ], [ '%s', '%v', ' ' ], $this->backend_currencies->get_woocommerce_price_format( $currency ) ); - - $rounding_precision = wc_get_price_decimals() ?? wc_get_rounding_precision(); - ?> - - settings_service->get_plugin_version(); } /** @@ -1431,52 +1170,6 @@ public function possible_simulation_activation() { $this->simulate_client_currency(); } - /** - * Enables simulation of client browser currency. - * - * @return void - */ - private function simulate_client_currency() { - if ( ! $this->simulation_params['enable_auto_currency'] ) { - return; - } - - $countries = WC_Payments_Utils::supported_countries(); - - $predefined_simulation_currencies = [ - Currency_Code::UNITED_STATES_DOLLAR => $countries[ Country_Code::UNITED_STATES ], - Currency_Code::POUND_STERLING => $countries[ Country_Code::UNITED_KINGDOM ], - ]; - - $simulation_currency = Currency_Code::UNITED_STATES_DOLLAR === get_option( 'woocommerce_currency', Currency_Code::UNITED_STATES_DOLLAR ) ? Currency_Code::POUND_STERLING : Currency_Code::UNITED_STATES_DOLLAR; - $simulation_currency_name = $this->available_currencies[ $simulation_currency ]->get_name(); - $simulation_country = $predefined_simulation_currencies[ $simulation_currency ]; - - // Simulate client currency from geolocation. - add_filter( - 'wcpay_multi_currency_override_notice_currency_name', - function ( $selected_currency_name ) use ( $simulation_currency_name ) { - return $simulation_currency_name; - } - ); - - // Simulate client country from geolocation. - add_filter( - 'wcpay_multi_currency_override_notice_country', - function ( $selected_country ) use ( $simulation_country ) { - return $simulation_country; - } - ); - - // Always display the notice on simulation screen, prevent duplicate hooks. - if ( ! has_action( 'wp_footer', [ $this, 'display_geolocation_currency_update_notice' ] ) ) { - add_action( 'wp_footer', [ $this, 'display_geolocation_currency_update_notice' ] ); - } - - // Skip recalculating the cart to prevent infinite loop in simulation. - remove_action( 'wp_loaded', [ $this, 'recalculate_cart' ] ); - } - /** * Returns whether the simulation querystring param is set and active * @@ -1538,45 +1231,6 @@ public function get_multi_currency_onboarding_simulation_variables() { return $values; } - /** - * Adds the required querystring parameters to all urls in preview pages. - * - * @return void - */ - private function add_simulation_params_to_preview_urls() { - $params = $this->simulation_params; - add_filter( - 'wp_footer', - function () use ( $params ) { - ?> - - ceil_price( $price, (float) $currency->get_rounding() ); + + if ( $apply_charm_pricing ) { + $price += (float) $currency->get_charm(); + } + + // Do not return negative prices (possible because of $currency->get_charm()). + return max( 0, $price ); + } + + /** + * Ceils the price to the next number based on the rounding value. + * + * @param float $price The price to be ceiled. + * @param float $rounding The rounding option. + * + * @return float The ceiled price. + */ + protected function ceil_price( float $price, float $rounding ): float { + if ( 0.00 === $rounding ) { + return $price; + } + return ceil( $price / $rounding ) * $rounding; + } + + /** + * Sets up the available currencies, which are alphabetical by name. + * + * @return void + */ + private function initialize_available_currencies() { + // Add default store currency with a rate of 1.0. + $woocommerce_currency = get_woocommerce_currency(); + $this->available_currencies[ $woocommerce_currency ] = new Currency( $this->localization_service, $woocommerce_currency, 1.0 ); + + $available_currencies = []; + + $currencies = $this->get_account_available_currencies(); + $cache_data = $this->get_cached_currencies(); + + foreach ( $currencies as $currency_code ) { + $currency_rate = $cache_data['currencies'][ $currency_code ] ?? 1.0; + $update_time = $cache_data['updated'] ?? null; + $new_currency = new Currency( $this->localization_service, $currency_code, $currency_rate, $update_time ); + + // Add this to our list of available currencies. + $available_currencies[ $new_currency->get_name() ] = $new_currency; + } + + ksort( $available_currencies ); + + foreach ( $available_currencies as $currency ) { + $this->available_currencies[ $currency->get_code() ] = $currency; + } + } + + /** + * Sets up the enabled currencies. + * + * @return void + */ + private function initialize_enabled_currencies() { + $available_currencies = $this->get_available_currencies(); + $enabled_currency_codes = get_option( $this->id . '_enabled_currencies', [] ); + $enabled_currency_codes = is_array( $enabled_currency_codes ) ? $enabled_currency_codes : []; + $default_code = $this->get_default_currency()->get_code(); + $default = []; + $enabled_currency_codes[] = $default_code; + + // This allows to keep the alphabetical sorting by name. + $enabled_currencies = array_filter( + $available_currencies, + function ( $currency ) use ( $enabled_currency_codes ) { + return in_array( $currency->get_code(), $enabled_currency_codes, true ); + } + ); + + $this->enabled_currencies = []; + + foreach ( $enabled_currencies as $enabled_currency ) { + // Get the charm and rounding for each enabled currency and add the currencies to the object property. + $currency = clone $enabled_currency; + $charm = get_option( $this->id . '_price_charm_' . $currency->get_id(), 0.00 ); + $rounding = get_option( $this->id . '_price_rounding_' . $currency->get_id(), $currency->get_is_zero_decimal() ? '100' : '1.00' ); + $currency->set_charm( $charm ); + $currency->set_rounding( $rounding ); + + // If the currency is set to be manual, set the rate to the stored manual rate. + $type = get_option( $this->id . '_exchange_rate_' . $currency->get_id(), 'automatic' ); + if ( 'manual' === $type ) { + $manual_rate = get_option( $this->id . '_manual_rate_' . $currency->get_id(), $currency->get_rate() ); + $currency->set_rate( $manual_rate ); + } + + $this->enabled_currencies[ $currency->get_code() ] = $currency; + } + + // Set default currency to the top of the list. + $default[ $default_code ] = $this->enabled_currencies[ $default_code ]; + unset( $this->enabled_currencies[ $default_code ] ); + $this->enabled_currencies = array_merge( $default, $this->enabled_currencies ); + } + + /** + * Sets the default currency. + * + * @return void + */ + private function set_default_currency() { + $available_currencies = $this->get_available_currencies(); + $this->default_currency = $available_currencies[ get_woocommerce_currency() ] ?? null; + } + + /** + * Returns the currency code stored for the user or in the session. + * + * @return string|null Currency code. + */ + private function get_stored_currency_code() { + $user_id = get_current_user_id(); + + if ( $user_id ) { + return get_user_meta( $user_id, self::CURRENCY_META_KEY, true ); + } + + WC()->initialize_session(); + $currency_code = WC()->session->get( self::CURRENCY_SESSION_KEY ); + + return is_string( $currency_code ) ? $currency_code : null; + } + + /** + * Checks to see if the store currency has changed. If it has, this will + * also update the option containing the store currency. + * + * @return bool + */ + private function check_store_currency_for_change(): bool { + $last_known_currency = get_option( $this->id . '_store_currency', false ); + $woocommerce_currency = get_woocommerce_currency(); + + // If the last known currency was not set, update the option to set it and return false. + if ( ! $last_known_currency ) { + update_option( $this->id . '_store_currency', $woocommerce_currency ); + return false; + } + + if ( $last_known_currency !== $woocommerce_currency ) { + update_option( $this->id . '_store_currency', $woocommerce_currency ); + return true; + } + + return false; + } + + /** + * Called when the store currency has changed. Puts any manual rate currencies into an option for a notice to display. + * + * @return void + */ + private function update_manual_rate_currencies_notice_option() { + $enabled_currencies = $this->get_enabled_currencies(); + $manual_currencies = []; + + // Check enabled currencies for manual rates. + foreach ( $enabled_currencies as $currency ) { + $rate_type = get_option( $this->id . '_exchange_rate_' . $currency->get_id(), false ); + if ( 'manual' === $rate_type ) { + $manual_currencies[] = $currency->get_name(); + } + } + + if ( 0 < count( $manual_currencies ) ) { + update_option( $this->id . '_show_store_currency_changed_notice', $manual_currencies ); + } + } + + /** + * Accepts an array of currencies that should have their settings removed. + * + * @param array $currencies Array of Currency objects or 3 letter currency codes. + * + * @return void + */ + private function remove_currencies_settings( array $currencies ) { + + foreach ( $currencies as $currency ) { + $this->remove_currency_settings( $currency ); + } + } + + /** + * Will remove a currency's settings if it is not enabled. + * + * @param mixed $currency Currency object or 3 letter currency code. + * + * @return void + */ + private function remove_currency_settings( $currency ) { + $code = is_a( $currency, Currency::class ) ? $currency->get_code() : strtoupper( $currency ); + + // Bail if the currency code passed is not 3 characters, or if the currency is presently enabled. + if ( 3 !== strlen( $code ) || isset( $this->get_enabled_currencies()[ $code ] ) ) { + return; + } + + $settings = [ + 'price_charm', + 'price_rounding', + 'manual_rate', + 'exchange_rate', + ]; + + // Go through each setting and remove them. + foreach ( $settings as $setting ) { + delete_option( $this->id . '_' . $setting . '_' . strtolower( $code ) ); + } + } + + /** + * Returns the currencies enabled for the payment provider account that are + * also available in WC. + * + * Can be filtered with the 'wcpay_multi_currency_available_currencies' hook. + * + * @return array Array with the available currencies' codes. + */ + private function get_account_available_currencies(): array { + // If the payment provider is not connected, return an empty array. This prevents using MC without being connected to the payment provider. + if ( ! $this->payments_account->is_provider_connected() ) { + return []; + } + + $wc_currencies = array_keys( get_woocommerce_currencies() ); + $account_currencies = $wc_currencies; + + $account = $this->payments_account->get_cached_account_data(); + $supported_currencies = $this->payments_account->get_account_customer_supported_currencies(); + if ( $account && ! empty( $supported_currencies ) ) { + $account_currencies = array_map( 'strtoupper', $supported_currencies ); + } + + /** + * Filter the available currencies for WooCommerce Multi-Currency. + * + * This filter can be used to modify the currencies available for WC Pay + * Multi-Currency. Currencies have to be added in uppercase and should + * also be available in `get_woocommerce_currencies` for them to work. + * + * @since 2.8.0 + * + * @param array $available_currencies Current available currencies. Calculated based on + * WC Pay's account currencies and WC currencies. + */ + return apply_filters( self::FILTER_PREFIX . 'available_currencies', array_intersect( $account_currencies, $wc_currencies ) ); + } + + /** + * Register the CSS and JS admin scripts. + * + * @return void + */ + private function register_admin_scripts() { + $this->register_script_with_dependencies( 'WCPAY_MULTI_CURRENCY_SETTINGS', 'dist/multi-currency', [ 'WCPAY_ADMIN_SETTINGS' ] ); + + wp_register_style( + 'WCPAY_MULTI_CURRENCY_SETTINGS', + plugins_url( 'dist/multi-currency.css', $this->settings_service->get_plugin_file_path() ), + [ 'wc-components', 'WCPAY_ADMIN_SETTINGS' ], + $this->get_file_version( 'dist/multi-currency.css' ), + 'all' + ); + } + + /** + * Enables simulation of client browser currency. + * + * @return void + */ + private function simulate_client_currency() { + if ( ! $this->simulation_params['enable_auto_currency'] ) { + return; + } + + $countries = $this->payments_account->get_supported_countries(); + + $predefined_simulation_currencies = [ + 'USD' => $countries['US'], + 'GBP' => $countries['GB'], + ]; + + $simulation_currency = 'USD' === get_option( 'woocommerce_currency', 'USD' ) ? 'GBP' : 'USD'; + $simulation_currency_name = $this->available_currencies[ $simulation_currency ]->get_name(); + $simulation_country = $predefined_simulation_currencies[ $simulation_currency ]; + + // Simulate client currency from geolocation. + add_filter( + 'wcpay_multi_currency_override_notice_currency_name', + function ( $selected_currency_name ) use ( $simulation_currency_name ) { + return $simulation_currency_name; + } + ); + + // Simulate client country from geolocation. + add_filter( + 'wcpay_multi_currency_override_notice_country', + function ( $selected_country ) use ( $simulation_country ) { + return $simulation_country; + } + ); + + // Always display the notice on simulation screen, prevent duplicate hooks. + if ( ! has_action( 'wp_footer', [ $this, 'display_geolocation_currency_update_notice' ] ) ) { + add_action( 'wp_footer', [ $this, 'display_geolocation_currency_update_notice' ] ); + } + + // Skip recalculating the cart to prevent infinite loop in simulation. + remove_action( 'wp_loaded', [ $this, 'recalculate_cart' ] ); + } + + /** + * Adds the required querystring parameters to all urls in preview pages. + * + * @return void + */ + private function add_simulation_params_to_preview_urls() { + $params = $this->simulation_params; + add_filter( + 'wp_footer', + function () use ( $params ) { + ?> + + is_stripe_connected() ) { + if ( is_null( self::$account ) || ! self::$account->is_provider_connected() ) { return; } @@ -80,9 +80,9 @@ public static function possibly_add_note() { /** * Sets the account service instance reference on the class. * - * @param WC_Payments_Account $account account service instance. + * @param MultiCurrencyAccountInterface $account account service instance. */ - public static function set_account( WC_Payments_Account $account ) { + public static function set_account( MultiCurrencyAccountInterface $account ) { self::$account = $account; } } diff --git a/includes/multi-currency/RestController.php b/multi-currency/src/RestController.php similarity index 78% rename from includes/multi-currency/RestController.php rename to multi-currency/src/RestController.php index 0266b5f4e76..df36b1186b4 100644 --- a/includes/multi-currency/RestController.php +++ b/multi-currency/src/RestController.php @@ -1,21 +1,29 @@ multi_currency = $multi_currency; + } + + /** * Configure REST API routes. */ @@ -144,7 +169,7 @@ public function register_routes() { * @return \WP_REST_Response|\WP_Error Array of the store currencies structure. */ public function get_store_currencies() { - return rest_ensure_response( WC_Payments_Multi_Currency()->get_store_currencies() ); + return rest_ensure_response( $this->multi_currency->get_store_currencies() ); } /** @@ -157,10 +182,10 @@ public function get_store_currencies() { public function update_enabled_currencies( $request ) { $enabled = $request->get_param( 'enabled' ); try { - WC_Payments_Multi_Currency()->set_enabled_currencies( $enabled ); + $this->multi_currency->set_enabled_currencies( $enabled ); $response = $this->get_store_currencies(); } catch ( InvalidCurrencyException $e ) { - $response = new \WP_Error( $e->get_error_code(), $e->getMessage() ); + $response = new \WP_Error( $e->getCode(), $e->getMessage() ); } return rest_ensure_response( $response ); } @@ -176,9 +201,9 @@ public function get_single_currency_settings( $request ) { $currency_code = $request->get_param( 'currency_code' ); try { - $response = WC_Payments_Multi_Currency()->get_single_currency_settings( $currency_code ); + $response = $this->multi_currency->get_single_currency_settings( $currency_code ); } catch ( InvalidCurrencyException $e ) { - $response = new \WP_Error( $e->get_error_code(), $e->getMessage() ); + $response = new \WP_Error( $e->getCode(), $e->getMessage() ); } return rest_ensure_response( $response ); @@ -199,10 +224,10 @@ public function update_single_currency_settings( $request ) { $manual_rate = $request->get_param( 'manual_rate' ) ?? null; try { - WC_Payments_Multi_Currency()->update_single_currency_settings( $currency_code, $exchange_rate_type, $price_rounding, $price_charm, $manual_rate ); - $response = WC_Payments_Multi_Currency()->get_single_currency_settings( $currency_code ); - } catch ( Base_Exception $e ) { - $response = new \WP_Error( $e->get_error_code(), $e->getMessage() ); + $this->multi_currency->update_single_currency_settings( $currency_code, $exchange_rate_type, $price_rounding, $price_charm, $manual_rate ); + $response = $this->multi_currency->get_single_currency_settings( $currency_code ); + } catch ( Exception $e ) { + $response = new \WP_Error( $e->getCode(), $e->getMessage() ); } return rest_ensure_response( $response ); @@ -214,7 +239,7 @@ public function update_single_currency_settings( $request ) { * @return \WP_REST_Response|\WP_Error The store settings as an array. */ public function get_settings() { - return rest_ensure_response( WC_Payments_Multi_Currency()->get_settings() ); + return rest_ensure_response( $this->multi_currency->get_settings() ); } /** @@ -226,7 +251,14 @@ public function get_settings() { */ public function update_settings( $request ) { $params = $request->get_params(); - WC_Payments_Multi_Currency()->update_settings( $params ); - return rest_ensure_response( WC_Payments_Multi_Currency()->get_settings() ); + $this->multi_currency->update_settings( $params ); + return rest_ensure_response( $this->multi_currency->get_settings() ); + } + + /** + * Verify access. + */ + public function check_permission() { + return current_user_can( 'manage_woocommerce' ); } } diff --git a/includes/multi-currency/Settings.php b/multi-currency/src/Settings.php similarity index 96% rename from includes/multi-currency/Settings.php rename to multi-currency/src/Settings.php index 11c74d56619..8fe0a4aafcd 100644 --- a/includes/multi-currency/Settings.php +++ b/multi-currency/src/Settings.php @@ -88,7 +88,7 @@ public function wcpay_multi_currency_settings_page() { * Load inline Emoji detection script on multi-currency settings page */ public function maybe_add_print_emoji_detection_script() { - if ( WC_Payments_Multi_Currency()->is_multi_currency_settings_page() ) { + if ( $this->multi_currency->is_multi_currency_settings_page() ) { print_emoji_detection_script(); } } diff --git a/includes/multi-currency/SettingsOnboardCta.php b/multi-currency/src/SettingsOnboardCta.php similarity index 74% rename from includes/multi-currency/SettingsOnboardCta.php rename to multi-currency/src/SettingsOnboardCta.php index ee89ea5638c..59a2eee286f 100644 --- a/includes/multi-currency/SettingsOnboardCta.php +++ b/multi-currency/src/SettingsOnboardCta.php @@ -7,6 +7,8 @@ namespace WCPay\MultiCurrency; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; + defined( 'ABSPATH' ) || exit; /** @@ -27,15 +29,24 @@ class SettingsOnboardCta extends \WC_Settings_Page { */ private $multi_currency; + /** + * Instance of MultiCurrencyAccountInterface. + * + * @var MultiCurrencyAccountInterface + */ + private $payments_account; + /** * Constructor. * - * @param MultiCurrency $multi_currency The MultiCurrency instance. + * @param MultiCurrency $multi_currency The MultiCurrency instance. + * @param MultiCurrencyAccountInterface $payments_account Payments Account instance. */ - public function __construct( MultiCurrency $multi_currency ) { - $this->multi_currency = $multi_currency; - $this->id = $this->multi_currency->id; - $this->label = _x( 'Multi-currency', 'Settings tab label', 'woocommerce-payments' ); + public function __construct( MultiCurrency $multi_currency, MultiCurrencyAccountInterface $payments_account ) { + $this->multi_currency = $multi_currency; + $this->payments_account = $payments_account; + $this->id = $this->multi_currency->id; + $this->label = _x( 'Multi-currency', 'Settings tab label', 'woocommerce-payments' ); parent::__construct(); } @@ -53,7 +64,7 @@ public function init_hooks() { * Output the call to action button if needing to onboard. */ public function currencies_settings_onboarding_cta() { - $href = \WC_Payments_Account::get_connect_page_url(); + $href = $this->payments_account->get_provider_onboarding_page_url(); ?>

    diff --git a/includes/multi-currency/StorefrontIntegration.php b/multi-currency/src/StorefrontIntegration.php similarity index 100% rename from includes/multi-currency/StorefrontIntegration.php rename to multi-currency/src/StorefrontIntegration.php diff --git a/includes/multi-currency/Tracking.php b/multi-currency/src/Tracking.php similarity index 100% rename from includes/multi-currency/Tracking.php rename to multi-currency/src/Tracking.php diff --git a/includes/multi-currency/UserSettings.php b/multi-currency/src/UserSettings.php similarity index 100% rename from includes/multi-currency/UserSettings.php rename to multi-currency/src/UserSettings.php diff --git a/includes/multi-currency/Utils.php b/multi-currency/src/Utils.php similarity index 51% rename from includes/multi-currency/Utils.php rename to multi-currency/src/Utils.php index 64e5356a77a..b0c1fba84ea 100644 --- a/includes/multi-currency/Utils.php +++ b/multi-currency/src/Utils.php @@ -57,7 +57,7 @@ public function is_page_with_vars( array $pages, array $vars ): bool { * @return boolean */ public static function is_admin_api_request(): bool { - return 0 === stripos( wp_get_referer(), admin_url() ) && WC()->is_rest_api_request() && ! \WC_Payments_Utils::is_store_api_request(); + return 0 === stripos( wp_get_referer(), admin_url() ) && WC()->is_rest_api_request() && ! self::is_store_api_request(); } @@ -71,4 +71,39 @@ public static function is_admin_api_request(): bool { public static function set_customer_session_cookie( bool $set ) { WC()->session->set_customer_session_cookie( $set ); } + + /** + * Returns true if the request is a store REST API request. + * + * @return bool + */ + public static function is_store_api_request() { + if ( function_exists( 'WC' ) && method_exists( WC(), 'is_store_api_request' ) ) { + return WC()->is_store_api_request(); + } + // The logic below is sourced from `WC()->is_store_api_request()`. + if ( empty( $_SERVER['REQUEST_URI'] ) ) { + return false; + } + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + return false !== strpos( $_SERVER['REQUEST_URI'], trailingslashit( rest_get_url_prefix() ) . 'wc/store/' ); + } + + /** + * Returns true if the request that's currently being processed is a Store API batch request, false + * otherwise. + * + * @return bool True if the request is a Store API batch request, false otherwise. + */ + public static function is_store_batch_request(): bool { + if ( isset( $_REQUEST['rest_route'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + $rest_route = sanitize_text_field( $_REQUEST['rest_route'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.NonceVerification + } else { + $url_parts = wp_parse_url( esc_url_raw( $_SERVER['REQUEST_URI'] ?? '' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash + $request_path = $url_parts ? rtrim( $url_parts['path'], '/' ) : ''; + $rest_route = str_replace( trailingslashit( rest_get_url_prefix() ), '', $request_path ); + } + + return 1 === preg_match( '@^\/wc\/store(\/v[\d]+)?\/batch@', $rest_route ); + } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 6e21606d2bb..a0bf916f7a9 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -102,7 +102,7 @@ tests/* - includes/multi-currency + multi-currency/src src diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 2a5164e8af3..1eb5fab3f04 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -46,13 +46,13 @@ WC_Subscriptions_Product - + \WC_Product_Addons_Helper \WC_Product_Addons_Helper - + \WC_Name_Your_Price_Helpers \WC_Name_Your_Price_Helpers diff --git a/tasks/release.js b/tasks/release.js index 92559af4ffb..72c3eccdef7 100644 --- a/tasks/release.js +++ b/tasks/release.js @@ -14,11 +14,11 @@ const targetFolder = 'release/' + pluginSlug; const filesToCopy = [ 'assets', 'dist', - 'includes', 'i18n', + 'includes', 'languages', - 'src', 'lib', + 'src', 'templates', 'vendor', 'woocommerce-payments.php', @@ -43,6 +43,10 @@ rm( 'dist/*.map' ); // copy the directories to the release folder cp( '-Rf', filesToCopy, targetFolder ); +// copy the multi-currency files +mkdir( '-p', targetFolder + '/multi-currency' ); +cp( '-R', 'multi-currency/src', targetFolder + '/multi-currency/src' ); + const output = fs.createWriteStream( releaseFolder + '/' + pluginSlug + '.zip' ); diff --git a/tests/js/jest.config.js b/tests/js/jest.config.js index 18c11bc9553..03060196d4c 100644 --- a/tests/js/jest.config.js +++ b/tests/js/jest.config.js @@ -2,13 +2,18 @@ const { jsWithBabel: tsjPreset } = require( 'ts-jest/presets' ); module.exports = { rootDir: '../../', - moduleDirectories: [ 'node_modules', '/client' ], + moduleDirectories: [ + 'node_modules', + '/client', + '/multi-currency/client', + ], moduleNameMapper: { '^react$': '/node_modules/react', '^react-dom$': '/node_modules/react-dom', '^moment$': '/node_modules/moment', '^moment-timezone$': '/node_modules/moment-timezone', '^wcpay(.*)$': '/client$1', + '^multi-currency(.*)$': '/multi-currency/client$1', '^iti/utils$': '/node_modules/intl-tel-input/build/js/utils', '^assets(.*?)(\\?.*)?$': '/assets$1', '^@woocommerce/blocks-registry$': diff --git a/tests/unit/multi-currency/compatibility/test-class-woocommerce-subscriptions.php b/tests/unit/multi-currency/compatibility/test-class-woocommerce-subscriptions.php index 5b71b8f8e7f..c2085b1fe5d 100644 --- a/tests/unit/multi-currency/compatibility/test-class-woocommerce-subscriptions.php +++ b/tests/unit/multi-currency/compatibility/test-class-woocommerce-subscriptions.php @@ -6,6 +6,11 @@ */ use WCPay\MultiCurrency\Compatibility\WooCommerceSubscriptions; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyApiClientInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyCacheInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencySettingsInterface; use WCPay\MultiCurrency\MultiCurrency; use WCPay\MultiCurrency\Utils; @@ -62,7 +67,16 @@ class WCPay_Multi_Currency_WooCommerceSubscriptions_Tests extends WCPAY_UnitTest public function set_up() { parent::set_up(); - $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); + $mock_api_client = $this->createMock( MultiCurrencyApiClientInterface::class ); + $mock_account = $this->createMock( MultiCurrencyAccountInterface::class ); + $mock_localization = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_settings = $this->createMock( MultiCurrencySettingsInterface::class ); + + $this->mock_multi_currency = $this->getMockBuilder( MultiCurrency::class ) + ->setConstructorArgs( [ $mock_settings, $mock_api_client, $mock_account, $mock_localization, $mock_cache ] ) + ->getMock(); + $this->mock_utils = $this->createMock( Utils::class ); $this->woocommerce_subscriptions = new WooCommerceSubscriptions( $this->mock_multi_currency, $this->mock_utils ); @@ -828,18 +842,11 @@ public function test_maybe_get_explicit_format_for_subscription_total() { ->willReturn( true ); // Arrange: Set expectation and return for is_initialized and has_additional_currencies_enabled. - $this->mock_multi_currency - ->expects( $this->once() ) - ->method( 'is_initialized' ) - ->willReturn( true ); $this->mock_multi_currency ->expects( $this->once() ) ->method( 'has_additional_currencies_enabled' ) ->willReturn( true ); - // Arrange: Make sure to set our Multi-Currency instance as our mock instance. - WC_Payments_Explicit_Price_Formatter::set_multi_currency_instance( $this->mock_multi_currency ); - // Arrange/Assert: Apply the woocommerce_subscription_price_string_details filter and confirm the filter does not change the passed array. $this->assertSame( [ 1, 2, 3 ], apply_filters( 'woocommerce_subscription_price_string_details', [ 1, 2, 3 ], $mock_subscription ) ); diff --git a/tests/unit/multi-currency/notes/test-class-note-multi-currency-available-test.php b/tests/unit/multi-currency/notes/test-class-note-multi-currency-available-test.php index 5a985dd0511..e434ae9f661 100644 --- a/tests/unit/multi-currency/notes/test-class-note-multi-currency-available-test.php +++ b/tests/unit/multi-currency/notes/test-class-note-multi-currency-available-test.php @@ -6,6 +6,7 @@ */ use WCPay\MultiCurrency\Notes\NoteMultiCurrencyAvailable; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; /** * Class Note_Multi_Currency_Available_Test tests. @@ -55,8 +56,8 @@ public function test_possibly_add_note_without_account() { } public function test_possibly_add_note_with_account_not_connected() { - $account_mock = $this->createMock( WC_Payments_Account::class ); - $account_mock->method( 'is_stripe_connected' )->willReturn( false ); + $account_mock = $this->createMock( MultiCurrencyAccountInterface::class ); + $account_mock->method( 'is_provider_connected' )->willReturn( false ); NoteMultiCurrencyAvailable::set_account( $account_mock ); NoteMultiCurrencyAvailable::possibly_add_note(); @@ -65,8 +66,8 @@ public function test_possibly_add_note_with_account_not_connected() { } public function test_possibly_add_note_with_connected_account() { - $account_mock = $this->createMock( WC_Payments_Account::class ); - $account_mock->method( 'is_stripe_connected' )->willReturn( true ); + $account_mock = $this->createMock( MultiCurrencyAccountInterface::class ); + $account_mock->method( 'is_provider_connected' )->willReturn( true ); NoteMultiCurrencyAvailable::set_account( $account_mock ); NoteMultiCurrencyAvailable::possibly_add_note(); diff --git a/tests/unit/multi-currency/test-class-analytics.php b/tests/unit/multi-currency/test-class-analytics.php index 00b46e2d7ef..178db790140 100644 --- a/tests/unit/multi-currency/test-class-analytics.php +++ b/tests/unit/multi-currency/test-class-analytics.php @@ -1,6 +1,6 @@ create_can_manage_woocommerce_cap_override( true ); add_filter( 'user_has_cap', $cb ); - $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); + $mock_api_client = $this->createMock( MultiCurrencyApiClientInterface::class ); + $mock_account = $this->createMock( MultiCurrencyAccountInterface::class ); + $mock_localization = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_settings = $this->createMock( MultiCurrencySettingsInterface::class ); + + $this->mock_multi_currency = $this->getMockBuilder( MultiCurrency::class ) + ->setConstructorArgs( [ $mock_settings, $mock_api_client, $mock_account, $mock_localization, $mock_cache ] ) + ->getMock(); $this->mock_multi_currency->expects( $this->any() ) ->method( 'get_all_customer_currencies' ) @@ -82,9 +95,12 @@ public function set_up() { ->method( 'get_available_currencies' ) ->willReturn( $this->get_mock_available_currencies() ); - $this->analytics = new Analytics( $this->mock_multi_currency ); + $this->analytics = new Analytics( $this->mock_multi_currency, $mock_settings ); - $this->localization_service = new WC_Payments_Localization_Service(); + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $this->mock_localization_service->expects( $this->any() ) + ->method( 'get_currency_format' ) + ->willReturn( [ 'num_decimals' => 2 ] ); remove_filter( 'user_has_cap', $cb ); } @@ -164,7 +180,7 @@ public function test_update_order_stats_data_with_multi_currency_order_without_m public function test_update_order_stats_data_with_multi_currency_order() { $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $args = $this->order_args_provider( 123, 0, 1, 15.50, 1.50, 0, 14.00 ); $order = wc_create_order(); @@ -179,7 +195,7 @@ public function test_update_order_stats_data_with_multi_currency_order() { public function test_update_order_stats_data_with_large_order() { $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $args = $this->order_args_provider( 123, 0, 1, 130500.75, 20000, 10000, 100500.75 ); $order = wc_create_order(); @@ -194,7 +210,7 @@ public function test_update_order_stats_data_with_large_order() { public function test_update_order_stats_data_with_stripe_exchange_rate() { $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $args = $this->order_args_provider( 123, 0, 1, 15.50, 1.50, 0, 15.00 ); $order = wc_create_order(); @@ -587,14 +603,19 @@ private function create_can_manage_woocommerce_cap_override( bool $can_manage_wo } private function get_mock_available_currencies() { - $this->localization_service = new WC_Payments_Localization_Service(); + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); if ( empty( $this->mock_available_currencies ) ) { + $this->mock_localization_service + ->expects( $this->any() ) + ->method( 'get_currency_format' ) + ->willReturn( [ 'num_decimals' => 2 ] ); + $this->mock_available_currencies = [ - 'GBP' => new Currency( $this->localization_service, 'GBP', 1.2 ), - 'USD' => new Currency( $this->localization_service, 'USD', 1 ), - 'EUR' => new Currency( $this->localization_service, 'EUR', 0.9 ), - 'ISK' => new Currency( $this->localization_service, 'ISK', 30.52 ), - 'NZD' => new Currency( $this->localization_service, 'NZD', 1.4 ), + 'GBP' => new Currency( $this->mock_localization_service, 'GBP', 1.2 ), + 'USD' => new Currency( $this->mock_localization_service, 'USD', 1 ), + 'EUR' => new Currency( $this->mock_localization_service, 'EUR', 0.9 ), + 'ISK' => new Currency( $this->mock_localization_service, 'ISK', 30.52 ), + 'NZD' => new Currency( $this->mock_localization_service, 'NZD', 1.4 ), ]; } diff --git a/tests/unit/multi-currency/test-class-backend-currencies.php b/tests/unit/multi-currency/test-class-backend-currencies.php index 530efc71b7d..8b333c6f189 100644 --- a/tests/unit/multi-currency/test-class-backend-currencies.php +++ b/tests/unit/multi-currency/test-class-backend-currencies.php @@ -6,6 +6,7 @@ */ use WCPay\MultiCurrency\BackendCurrencies; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; use WCPay\MultiCurrency\MultiCurrency; /** @@ -13,9 +14,9 @@ */ class WCPay_Multi_Currency_Backend_Currencies_Tests extends WCPAY_UnitTestCase { /** - * Mock WC_Payments_Localization_Service. + * Mock MultiCurrencyLocalizationInterface. * - * @var WC_Payments_Localization_Service|PHPUnit_Framework_MockObject_MockObject + * @var MultiCurrencyLocalizationInterface|PHPUnit_Framework_MockObject_MockObject */ private $mock_localization_service; @@ -36,7 +37,7 @@ class WCPay_Multi_Currency_Backend_Currencies_Tests extends WCPAY_UnitTestCase { public function set_up() { parent::set_up(); - $this->mock_localization_service = $this->createMock( WC_Payments_Localization_Service::class ); + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); // Mock admin part. diff --git a/tests/unit/multi-currency/test-class-compatibility.php b/tests/unit/multi-currency/test-class-compatibility.php index cefb478ca50..ae4cc75dc31 100644 --- a/tests/unit/multi-currency/test-class-compatibility.php +++ b/tests/unit/multi-currency/test-class-compatibility.php @@ -7,6 +7,7 @@ use WCPay\MultiCurrency\Compatibility; use WCPay\MultiCurrency\Currency; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; use WCPay\MultiCurrency\MultiCurrency; use WCPay\MultiCurrency\Utils; @@ -36,11 +37,11 @@ class WCPay_Multi_Currency_Compatibility_Tests extends WCPAY_UnitTestCase { private $mock_utils; /** - * WC_Payments_Localization_Service. + * MultiCurrencyLocalizationInterface. * - * @var WC_Payments_Localization_Service + * @var MultiCurrencyLocalizationInterface */ - private $localization_service; + private $mock_localization_service; /** * Pre-test setup @@ -48,10 +49,15 @@ class WCPay_Multi_Currency_Compatibility_Tests extends WCPAY_UnitTestCase { public function set_up() { parent::set_up(); - $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); - $this->mock_utils = $this->createMock( Utils::class ); - $this->compatibility = new Compatibility( $this->mock_multi_currency, $this->mock_utils ); - $this->localization_service = new WC_Payments_Localization_Service(); + $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); + $this->mock_utils = $this->createMock( Utils::class ); + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $this->mock_localization_service + ->method( 'get_currency_format' ) + ->with( 'USD' ) + ->willReturn( [ 'num_decimals' => 2 ] ); + + $this->compatibility = new Compatibility( $this->mock_multi_currency, $this->mock_utils ); } public function test_init_compatibility_classes_does_not_add_classes_if_one_enabled_currencies() { @@ -108,7 +114,7 @@ public function test_filter_woocommerce_order_query_with_order_in_default_curren $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $this->mock_utils->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) @@ -132,7 +138,7 @@ public function test_filter_woocommerce_order_query_with_order_with_no_exchange_ $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $this->mock_utils->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) @@ -153,7 +159,7 @@ public function test_filter_woocommerce_order_query_with_no_meta() { $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $this->mock_utils->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) @@ -177,7 +183,7 @@ public function test_filter_woocommerce_order_query() { $this->mock_multi_currency->expects( $this->once() ) ->method( 'get_default_currency' ) - ->willReturn( new Currency( $this->localization_service, 'USD', 1.0 ) ); + ->willReturn( new Currency( $this->mock_localization_service, 'USD', 1.0 ) ); $this->mock_utils->expects( $this->once() ) ->method( 'is_call_in_backtrace' ) diff --git a/tests/unit/multi-currency/test-class-country-flags.php b/tests/unit/multi-currency/test-class-country-flags.php index 0ebe63fb8ef..cd3adff8feb 100644 --- a/tests/unit/multi-currency/test-class-country-flags.php +++ b/tests/unit/multi-currency/test-class-country-flags.php @@ -5,7 +5,6 @@ * @package WooCommerce\Payments\Tests */ -use WCPay\Constants\Country_Code; use WCPay\MultiCurrency\CountryFlags; /** @@ -13,7 +12,7 @@ */ class Country_Flags_Test extends WCPAY_UnitTestCase { public function test_get_by_country_returns_emoji_flag() { - $this->assertEquals( CountryFlags::get_by_country( Country_Code::UNITED_STATES ), '🇺🇸' ); + $this->assertEquals( CountryFlags::get_by_country( 'US' ), '🇺🇸' ); } public function test_get_by_country_returns_empty_string() { diff --git a/tests/unit/multi-currency/test-class-currency-switcher-block.php b/tests/unit/multi-currency/test-class-currency-switcher-block.php index f716f6dbe75..2ac0a69fa11 100644 --- a/tests/unit/multi-currency/test-class-currency-switcher-block.php +++ b/tests/unit/multi-currency/test-class-currency-switcher-block.php @@ -10,6 +10,7 @@ use WCPay\MultiCurrency\Currency; use WCPay\MultiCurrency\CurrencySwitcherBlock; use WCPay\MultiCurrency\MultiCurrency; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; /** * CurrencySwitcherBlock unit tests. @@ -37,23 +38,25 @@ class WCPay_Multi_Currency_Currency_Switcher_Block_Tests extends WCPAY_UnitTestC protected $mock_currencies; /** - * WC_Payments_Localization_Service. - * - * @var WC_Payments_Localization_Service + * @var MockObject\MultiCurrencyLocalizationInterface */ - private $localization_service; + private $mock_localization_service; public function set_up() { parent::set_up(); - $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); - $this->mock_compatibility = $this->createMock( Compatibility::class ); - $this->localization_service = new WC_Payments_Localization_Service(); - $this->mock_currencies = [ - new Currency( $this->localization_service, 'USD', 1 ), - new Currency( $this->localization_service, 'CAD', 1.206823 ), - new Currency( $this->localization_service, 'GBP', 0.708099 ), - new Currency( $this->localization_service, 'EUR', 0.826381 ), + $this->mock_multi_currency = $this->createMock( MultiCurrency::class ); + $this->mock_compatibility = $this->createMock( Compatibility::class ); + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $this->mock_localization_service + ->method( 'get_currency_format' ) + ->willReturn( [ 'num_decimals' => 2 ] ); + + $this->mock_currencies = [ + new Currency( $this->mock_localization_service, 'USD', 1 ), + new Currency( $this->mock_localization_service, 'CAD', 1.206823 ), + new Currency( $this->mock_localization_service, 'GBP', 0.708099 ), + new Currency( $this->mock_localization_service, 'EUR', 0.826381 ), ]; $this->currency_switcher_block = new CurrencySwitcherBlock( @@ -219,8 +222,8 @@ public function test_render_currency_option_will_escape_output() { ->method( 'get_enabled_currencies' ) ->willReturn( [ - new Currency( $this->localization_service, 'USD' ), - new Currency( $this->localization_service, $currency_code, 1 ), + new Currency( $this->mock_localization_service, 'USD' ), + new Currency( $this->mock_localization_service, $currency_code, 1 ), ] ); @@ -275,7 +278,7 @@ public function test_widget_does_not_render_on_single_currency() { $this->mock_multi_currency ->expects( $this->once() ) ->method( 'get_enabled_currencies' ) - ->willReturn( [ new Currency( $this->localization_service, 'USD' ) ] ); + ->willReturn( [ new Currency( $this->mock_localization_service, 'USD' ) ] ); // Act/Assert: Confirm that when calling the renger method nothing is returned. $this->assertSame( '', $this->currency_switcher_block->render_block_widget( [], '' ) ); diff --git a/tests/unit/multi-currency/test-class-frontend-currencies.php b/tests/unit/multi-currency/test-class-frontend-currencies.php index bb560d8af9c..d63147de225 100644 --- a/tests/unit/multi-currency/test-class-frontend-currencies.php +++ b/tests/unit/multi-currency/test-class-frontend-currencies.php @@ -410,6 +410,7 @@ public function test_rest_api_ensure_should_return_store_currency_and_should_con $_SERVER['REQUEST_URI'] = $request_uri; $mccy = new WCPay\MultiCurrency\MultiCurrency( + WC_Payments::get_settings_service(), WC_Payments::get_payments_api_client(), WC_Payments::get_account_service(), WC_Payments::get_localization_service(), diff --git a/tests/unit/multi-currency/test-class-frontend-prices.php b/tests/unit/multi-currency/test-class-frontend-prices.php index c38d343dcc9..13d6b4bb34d 100644 --- a/tests/unit/multi-currency/test-class-frontend-prices.php +++ b/tests/unit/multi-currency/test-class-frontend-prices.php @@ -5,8 +5,6 @@ * @package WooCommerce\Payments\Tests */ -use WCPay\Constants\Country_Code; - /** * WCPay\MultiCurrency\FrontendPrices unit tests. */ @@ -216,7 +214,7 @@ function () { ); WC()->session->init(); - WC()->customer->set_location( Country_Code::UNITED_STATES, 'CA' ); + WC()->customer->set_location( 'US', 'CA' ); $shipping_method = new \WC_Shipping_Flat_Rate(); $shipping_method->tax_status = 'taxable'; @@ -260,7 +258,7 @@ function () { ); WC()->session->init(); - WC()->customer->set_location( Country_Code::UNITED_STATES, 'CA' ); + WC()->customer->set_location( 'US', 'CA' ); $shipping_method = new \WC_Shipping_Flat_Rate(); $shipping_method->tax_status = 'taxable'; diff --git a/tests/unit/multi-currency/test-class-geolocation.php b/tests/unit/multi-currency/test-class-geolocation.php index b66543dbf67..92b0b50b166 100644 --- a/tests/unit/multi-currency/test-class-geolocation.php +++ b/tests/unit/multi-currency/test-class-geolocation.php @@ -5,8 +5,6 @@ * @package WooCommerce\Payments\Tests */ -use WCPay\Constants\Country_Code; - /** * WCPay\MultiCurrency\Geolocation unit tests. */ @@ -51,10 +49,10 @@ public function test_get_country_by_customer_location_returns_geolocation_countr add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); - $this->assertSame( Country_Code::CANADA, $this->geolocation->get_country_by_customer_location() ); + $this->assertSame( 'CA', $this->geolocation->get_country_by_customer_location() ); } public function test_get_country_by_customer_location_returns_default_country_when_no_geolocation() { @@ -68,20 +66,20 @@ function () { add_filter( 'woocommerce_customer_default_location', function () { - return Country_Code::BRAZIL; + return 'BR'; } ); - $this->assertSame( Country_Code::BRAZIL, $this->geolocation->get_country_by_customer_location() ); + $this->assertSame( 'BR', $this->geolocation->get_country_by_customer_location() ); } public function test_get_currency_by_customer_location_returns_geolocation_currency_code() { - $this->mock_localization_service->method( 'get_country_locale_data' )->with( Country_Code::CANADA )->willReturn( [ 'currency_code' => 'CAD' ] ); + $this->mock_localization_service->method( 'get_country_locale_data' )->with( 'CA' )->willReturn( [ 'currency_code' => 'CAD' ] ); add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -89,7 +87,7 @@ function () { } public function test_get_currency_by_customer_location_returns_default_currency_code() { - $this->mock_localization_service->method( 'get_country_locale_data' )->with( Country_Code::BRAZIL )->willReturn( [ 'currency_code' => 'BRL' ] ); + $this->mock_localization_service->method( 'get_country_locale_data' )->with( 'BR' )->willReturn( [ 'currency_code' => 'BRL' ] ); add_filter( 'woocommerce_geolocate_ip', @@ -100,7 +98,7 @@ function () { add_filter( 'woocommerce_customer_default_location', function () { - return Country_Code::BRAZIL; + return 'BR'; } ); diff --git a/tests/unit/multi-currency/test-class-multi-currency.php b/tests/unit/multi-currency/test-class-multi-currency.php index 023fd92868c..6d4eddaab84 100644 --- a/tests/unit/multi-currency/test-class-multi-currency.php +++ b/tests/unit/multi-currency/test-class-multi-currency.php @@ -5,11 +5,13 @@ * @package WooCommerce\Payments\Tests */ -use WCPay\Constants\Country_Code; use WCPay\MultiCurrency\Utils; -use WCPay\Database_Cache; use WCPay\MultiCurrency\Exceptions\InvalidCurrencyException; use WCPay\MultiCurrency\Exceptions\InvalidCurrencyRateException; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyApiClientInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyCacheInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencySettingsInterface; use WCPay\MultiCurrency\MultiCurrency; use WCPay\MultiCurrency\Settings; use WCPay\MultiCurrency\SettingsOnboardCta; @@ -65,14 +67,14 @@ class WCPay_Multi_Currency_Tests extends WCPAY_UnitTestCase { /** * Mock of the API client. * - * @var WC_Payments_API_Client + * @var MultiCurrencyApiClientInterface */ private $mock_api_client; /** - * Mock of the WC_Payments_Account. + * Mock of the MultiCurrencyAccountInterface. * - * @var WC_Payments_Account + * @var MultiCurrencyAccountInterface */ private $mock_account; @@ -84,11 +86,18 @@ class WCPay_Multi_Currency_Tests extends WCPAY_UnitTestCase { private $localization_service; /** - * Mock of Database_Cache. + * Mock of MultiCurrencyCacheInterface. * - * @var Database_Cache; + * @var MultiCurrencyCacheInterface; */ - private $mock_database_cache; + private $mock_cache; + + /** + * Mock of MultiCurrencySettingsInterface. + * + * @var MultiCurrencySettingsInterface; + */ + private $mock_settings; /** * Mock of Utils. @@ -456,7 +465,7 @@ public function test_update_selected_currency_by_geolocation_does_not_set_sessio add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -473,7 +482,7 @@ public function test_update_selected_currency_by_geolocation_updates_session_whe add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -488,7 +497,7 @@ public function test_update_selected_currency_by_geolocation_displays_notice() { add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -509,7 +518,7 @@ public function test_update_selected_currency_by_geolocation_does_not_update_if_ add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -534,7 +543,7 @@ public function test_display_geolocation_currency_update_notice() { add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::CANADA; + return 'CA'; } ); @@ -544,11 +553,11 @@ function () { } public function test_display_geolocation_currency_update_notice_does_not_display_if_using_default_currency() { - WC()->session->set( WCPay\MultiCurrency\MultiCurrency::CURRENCY_SESSION_KEY, Country_Code::UNITED_STATES ); + WC()->session->set( WCPay\MultiCurrency\MultiCurrency::CURRENCY_SESSION_KEY, 'US' ); add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::UNITED_STATES; + return 'US'; } ); @@ -562,7 +571,7 @@ public function test_display_geolocation_currency_update_notice_does_not_display add_filter( 'woocommerce_geolocate_ip', function () { - return Country_Code::UNITED_STATES; + return 'US'; } ); @@ -721,13 +730,13 @@ public function test_get_raw_conversion_throws_exception_on_invalid_from_rate() public function test_get_cached_currencies_with_no_server_connection() { // Need to create a new instance of MultiCurrency with a different $mock_api_client // Because the mock return value of 'is_server_connected' cannot be overridden. - $mock_api_client = $this->createMock( WC_Payments_API_Client::class ); + $mock_api_client = $this->createMock( MultiCurrencyApiClientInterface::class ); $mock_api_client->method( 'is_server_connected' )->willReturn( false ); $this->init_multi_currency( $mock_api_client ); - $this->mock_database_cache->method( 'get' )->willReturn( $this->mock_cached_currencies ); + $this->mock_cache->method( 'get' )->willReturn( $this->mock_cached_currencies ); $this->assertEquals( $this->mock_cached_currencies, @@ -736,7 +745,7 @@ public function test_get_cached_currencies_with_no_server_connection() { } public function test_get_cached_currencies_with_account_rejected() { - $this->mock_database_cache + $this->mock_cache ->expects( $this->once() ) ->method( 'get' ) ->willReturn( null ); @@ -746,7 +755,7 @@ public function test_get_cached_currencies_with_account_rejected() { ->method( 'is_account_rejected' ) ->willReturn( true ); - $this->mock_database_cache + $this->mock_cache ->expects( $this->never() ) ->method( 'get_or_add' ); @@ -758,11 +767,11 @@ public function test_get_cached_currencies_with_account_rejected() { public function test_get_cached_currencies_fetches_from_server() { $get_or_add_call_count = 1; - $mock_database_cache = $this->createMock( Database_Cache::class ); - $mock_database_cache + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_cache ->expects( $this->exactly( 2 ) ) ->method( 'get_or_add' ) - ->with( Database_Cache::CURRENCIES_KEY, $this->anything(), $this->anything() ) + ->with( MultiCurrencyCacheInterface::CURRENCIES_KEY, $this->anything(), $this->anything() ) ->willReturnCallback( function ( $key, $generator, $validator ) use ( &$get_or_add_call_count ) { if ( 1 === $get_or_add_call_count ) { @@ -776,7 +785,7 @@ function ( $key, $generator, $validator ) use ( &$get_or_add_call_count ) { } ); - $this->init_multi_currency( null, true, null, $mock_database_cache ); + $this->init_multi_currency( null, true, null, $mock_cache ); $currency_from = strtolower( get_woocommerce_currency() ); $currencies_to = get_woocommerce_currencies(); @@ -886,7 +895,7 @@ public function test_enabled_currencies_option_as_string_does_not_fatal() { public function test_get_cached_currencies_with_no_stripe_connection() { $this->init_multi_currency( null, false ); - $this->mock_database_cache->method( 'get' )->willReturn( $this->mock_cached_currencies ); + $this->mock_cache->method( 'get' )->willReturn( $this->mock_cached_currencies ); $this->assertEquals( $this->mock_cached_currencies, $this->multi_currency->get_cached_currencies() @@ -1051,14 +1060,14 @@ public function test_get_all_customer_currencies() { $mock_orders[] = $this->add_mock_order_with_currency_meta( 'EUR' ); $mock_orders[] = $this->add_mock_order_with_currency_meta( 'USD' ); - $mock_database_cache = $this->createMock( Database_Cache::class ); - $mock_database_cache + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_cache ->expects( $this->once() ) ->method( 'get_or_add' ) - ->with( Database_Cache::CURRENCIES_KEY, $this->anything(), $this->anything() ) + ->with( MultiCurrencyCacheInterface::CURRENCIES_KEY, $this->anything(), $this->anything() ) ->willReturn( $this->mock_cached_currencies ); - $this->init_multi_currency( null, true, null, $mock_database_cache ); + $this->init_multi_currency( null, true, null, $mock_cache ); $result = $this->multi_currency->get_all_customer_currencies(); @@ -1073,14 +1082,14 @@ public function test_get_all_customer_currencies_with_option_data() { $mock_option_data = [ 'GBP', 'EUR', 'USD' ]; update_option( MultiCurrency::CUSTOMER_CURRENCIES_KEY, $mock_option_data ); - $mock_database_cache = $this->createMock( Database_Cache::class ); - $mock_database_cache + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_cache ->expects( $this->once() ) ->method( 'get_or_add' ) - ->with( Database_Cache::CURRENCIES_KEY, $this->anything(), $this->anything() ) + ->with( MultiCurrencyCacheInterface::CURRENCIES_KEY, $this->anything(), $this->anything() ) ->willReturn( $this->mock_cached_currencies ); - $this->init_multi_currency( null, true, null, $mock_database_cache ); + $this->init_multi_currency( null, true, null, $mock_cache ); $result = $this->multi_currency->get_all_customer_currencies(); @@ -1103,14 +1112,14 @@ public function test_get_all_customer_currencies_with_invalid_option_data( $opti $mock_orders[] = $this->add_mock_order_with_currency_meta( 'EUR' ); $mock_orders[] = $this->add_mock_order_with_currency_meta( 'USD' ); - $mock_database_cache = $this->createMock( Database_Cache::class ); - $mock_database_cache + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_cache ->expects( $this->once() ) ->method( 'get_or_add' ) - ->with( Database_Cache::CURRENCIES_KEY, $this->anything(), $this->anything() ) + ->with( MultiCurrencyCacheInterface::CURRENCIES_KEY, $this->anything(), $this->anything() ) ->willReturn( $this->mock_cached_currencies ); - $this->init_multi_currency( null, true, null, $mock_database_cache ); + $this->init_multi_currency( null, true, null, $mock_cache ); $result = $this->multi_currency->get_all_customer_currencies(); @@ -1414,31 +1423,31 @@ private function remove_currency_settings_mock( $currency_code, $settings ) { } } - private function init_multi_currency( $mock_api_client = null, $wcpay_account_connected = true, $mock_account = null, $mock_database_cache = null ) { - $this->mock_api_client = $this->createMock( WC_Payments_API_Client::class ); + private function init_multi_currency( $mock_api_client = null, $wcpay_account_connected = true, $mock_account = null, $mock_cache = null ) { + $this->mock_api_client = $this->createMock( MultiCurrencyApiClientInterface::class ); - $this->mock_account = $mock_account ?? $this->createMock( WC_Payments_Account::class ); - $this->mock_account->method( 'is_stripe_connected' )->willReturn( $wcpay_account_connected ); + $this->mock_account = $mock_account ?? $this->createMock( MultiCurrencyAccountInterface::class ); + $this->mock_account->method( 'is_provider_connected' )->willReturn( $wcpay_account_connected ); $this->mock_api_client->method( 'is_server_connected' )->willReturn( true ); - $this->mock_database_cache = $this->createMock( Database_Cache::class ); - $this->mock_database_cache->method( 'get_or_add' )->willReturn( $this->mock_cached_currencies ); + $this->mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $this->mock_cache->method( 'get_or_add' )->willReturn( $this->mock_cached_currencies ); $this->mock_utils = $this->createMock( Utils::class ); + $this->mock_settings = $this->createMock( MultiCurrencySettingsInterface::class ); + $this->multi_currency = new MultiCurrency( + $this->mock_settings, $mock_api_client ?? $this->mock_api_client, $this->mock_account, $this->localization_service, - $mock_database_cache ?? $this->mock_database_cache, + $mock_cache ?? $this->mock_cache, $this->mock_utils ); $this->multi_currency->init_widgets(); $this->multi_currency->init(); - - // Fix an issue in WPCOM tests. - WC_Payments_Explicit_Price_Formatter::set_multi_currency_instance( $this->multi_currency ); } private function add_mock_order_with_currency_meta( $currency ) { diff --git a/tests/unit/multi-currency/test-class-rest-controller.php b/tests/unit/multi-currency/test-class-rest-controller.php index 49ac60ad1be..661b991df52 100644 --- a/tests/unit/multi-currency/test-class-rest-controller.php +++ b/tests/unit/multi-currency/test-class-rest-controller.php @@ -5,6 +5,13 @@ * @package WooCommerce\Payments\Tests */ +use PHPUnit\Framework\MockObject\MockObject; +use WCPay\MultiCurrency\Currency; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyAccountInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyApiClientInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyCacheInterface; +use WCPay\MultiCurrency\Interfaces\MultiCurrencyLocalizationInterface; +use WCPay\MultiCurrency\MultiCurrency; use WCPay\MultiCurrency\RestController; /** @@ -24,6 +31,27 @@ class WCPay_Multi_Currency_Rest_Controller_Tests extends WCPAY_UnitTestCase { */ private $controller; + /** + * Mock MultiCurrency. + * + * @var MultiCurrency|MockObject + */ + private $mock_multi_currency; + + /** + * The localization service. + * + * @var MultiCurrencyLocalizationInterface + */ + private $mock_localization_service; + + /** + * Mock available currencies. + * + * @var array An array of available currencies. + */ + private $mock_available_currencies = []; + /** * Pre-test setup */ @@ -33,13 +61,48 @@ public function set_up() { // Set the user so that we can pass the authentication. wp_set_current_user( 1 ); - $mock_api_client = $this->getMockBuilder( WC_Payments_API_Client::class )->disableOriginalConstructor()->getMock(); - $this->controller = new RestController( $mock_api_client ); + $mock_api_client = $this->createMock( MultiCurrencyApiClientInterface::class ); + $mock_account = $this->createMock( MultiCurrencyAccountInterface::class ); + $mock_localization = $this->createMock( MultiCurrencyLocalizationInterface::class ); + $mock_cache = $this->createMock( MultiCurrencyCacheInterface::class ); + $mock_settings = $this->createMock( WC_Payments_Settings_Service::class ); + + $mock_account->method( 'is_provider_connected' )->willReturn( true ); + $mock_api_client->method( 'is_server_connected' )->willReturn( true ); + + $mock_localization + ->method( 'get_currency_format' ) + ->willReturn( + [ + 'currency_pos' => 'right_space', + 'num_decimals' => 2, + ] + ); + + $this->mock_multi_currency = $this->getMockBuilder( MultiCurrency::class ) + ->setConstructorArgs( [ $mock_settings, $mock_api_client, $mock_account, $mock_localization, $mock_cache ] ) + ->enableOriginalConstructor() + ->onlyMethods( [ 'get_available_currencies' ] ) + ->getMock(); + + $this->mock_multi_currency->expects( $this->any() ) + ->method( 'get_available_currencies' ) + ->willReturn( $this->get_mock_available_currencies() ); + + $this->controller = new RestController( $this->mock_multi_currency ); + } + + /** + * Post-test teardown + */ + public function tear_down() { + remove_all_filters( 'wcpay_multi_currency_available_currencies' ); + parent::tear_down(); } public function test_get_store_currencies_gets_expected_response() { // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_store_currencies() ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_store_currencies() ); // Act: Get the store currencies. $response = $this->controller->get_store_currencies(); @@ -56,7 +119,7 @@ public function test_get_store_currencies_gets_expected_response() { */ public function test_update_enabled_currencies_updates_currencies() { // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_store_currencies() ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_store_currencies() ); // Arrange: Delete the enabled currencies option. delete_option( 'wcpay_multi_currency_enabled_currencies' ); @@ -82,9 +145,8 @@ public function test_update_enabled_currencies_throws_exception_on_unavailable_c $error_currencies = [ 'EUR', 'GBP', 'banana' ]; // Arrange: Set expected result. - $error_code = 'wcpay_multi_currency_invalid_currency'; $error_message = 'Invalid currency passed to set_enabled_currencies: ' . implode( ', ', $error_currencies ); - $expected = rest_ensure_response( new WP_Error( $error_code, $error_message ) ); + $expected = rest_ensure_response( new WP_Error( 500, $error_message ) ); // Arrange: Create the new REST request. $request = new WP_REST_Request( 'POST', self::ROUTE . '/update-enabled-currencies' ); @@ -110,7 +172,7 @@ public function test_get_single_currency_settings() { update_option( 'wcpay_multi_currency_price_charm_usd', 0 ); // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_single_currency_settings( 'USD' ) ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_single_currency_settings( 'USD' ) ); // Arrange: Create the new REST request. $request = new WP_REST_Request( 'GET', self::ROUTE . '/currencies/USD' ); @@ -129,9 +191,8 @@ public function test_get_single_currency_settings() { public function test_get_single_currency_settings_throws_exception_on_unavailable_currency() { // Arrange: Set expected result. - $error_code = 'wcpay_multi_currency_invalid_currency'; $error_message = 'Invalid currency passed to get_single_currency_settings: AAA'; - $expected = rest_ensure_response( new WP_Error( $error_code, $error_message ) ); + $expected = rest_ensure_response( new WP_Error( 500, $error_message ) ); // Arrange: Create the new REST request. $request = new WP_REST_Request( 'GET', self::ROUTE . '/currencies/AAA' ); @@ -162,7 +223,7 @@ public function test_update_single_currency_settings() { update_option( 'wcpay_multi_currency_price_charm_usd', 0 ); // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_single_currency_settings( 'USD' ) ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_single_currency_settings( 'USD' ) ); // Arrange: Now remove all the options. delete_option( 'wcpay_multi_currency_exchange_rate_usd' ); @@ -191,9 +252,8 @@ public function test_update_single_currency_settings() { public function test_update_single_currency_settings_throws_exception_on_unavailable_currency() { // Arrange: Set expected result. - $error_code = 'wcpay_multi_currency_invalid_currency'; $error_message = 'Invalid currency passed to update_single_currency_settings: AAA'; - $expected = rest_ensure_response( new WP_Error( $error_code, $error_message ) ); + $expected = rest_ensure_response( new WP_Error( 500, $error_message ) ); // Arrange: Create the new REST request. $request = new WP_REST_Request( 'POST', self::ROUTE . '/currencies/AAA' ); @@ -220,9 +280,8 @@ public function test_update_single_currency_settings_throws_exception_on_unavail */ public function test_update_single_currency_settings_throws_exception_on_invalid_currency_rate( $manual_rate ) { // Arrange: Set expected result. - $error_code = 'wcpay_multi_currency_invalid_currency_rate'; $error_message = 'Invalid manual currency rate passed to update_single_currency_settings: ' . $manual_rate; - $expected = rest_ensure_response( new WP_Error( $error_code, $error_message ) ); + $expected = rest_ensure_response( new WP_Error( 500, $error_message ) ); // Arrange: Create the new REST request. $request = new WP_REST_Request( 'POST', self::ROUTE . '/currencies/USD' ); @@ -260,7 +319,7 @@ public function update_single_currency_settings_throws_exception_on_invalid_curr public function test_get_settings_gets_expected_response() { // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_settings() ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_settings() ); // Act: Get the settings. $response = $this->controller->get_settings(); @@ -275,7 +334,7 @@ public function test_update_multi_currency_settings() { update_option( 'wcpay_multi_currency_enable_storefront_switcher', 'yes' ); // Arrange: Create expected response. - $expected = rest_ensure_response( WC_Payments_Multi_Currency()->get_settings() ); + $expected = rest_ensure_response( $this->mock_multi_currency->get_settings() ); // Arrange: Now remove all the options. delete_option( 'wcpay_multi_currency_enable_auto_currency' ); @@ -296,4 +355,20 @@ public function test_update_multi_currency_settings() { // Assert: Confirm the response is what we expected. $this->assertEquals( $expected, $response ); } + + private function get_mock_available_currencies() { + $this->mock_localization_service = $this->createMock( MultiCurrencyLocalizationInterface::class ); + if ( empty( $this->mock_available_currencies ) ) { + $this->mock_localization_service + ->expects( $this->any() ) + ->method( 'get_currency_format' ) + ->willReturn( [ 'num_decimals' => 2 ] ); + + $this->mock_available_currencies = [ + 'USD' => new Currency( $this->mock_localization_service, 'USD', 1 ), + ]; + } + + return $this->mock_available_currencies; + } } diff --git a/tests/unit/multi-currency/test-class-settings.php b/tests/unit/multi-currency/test-class-settings.php index 1ccbd373a0c..272a691910b 100644 --- a/tests/unit/multi-currency/test-class-settings.php +++ b/tests/unit/multi-currency/test-class-settings.php @@ -5,8 +5,6 @@ * @package WooCommerce\Payments\Tests */ -use WCPay\MultiCurrency\Currency; - /** * WCPay\MultiCurrency\Settings unit tests. */ diff --git a/tests/unit/multi-currency/test-class-utils.php b/tests/unit/multi-currency/test-class-utils.php index 1f3c9cce762..553df2a0cc1 100644 --- a/tests/unit/multi-currency/test-class-utils.php +++ b/tests/unit/multi-currency/test-class-utils.php @@ -60,8 +60,7 @@ public function test_is_admin_api_request_returns_true() { public function test_is_admin_api_request_returns_false_with_store_api() { $_SERVER['HTTP_REFERER'] = 'http://example.org/wp-admin/'; - $_REQUEST['rest_route'] = '/wc/store/v1/checkout'; - $_SERVER['REQUEST_URI'] = trailingslashit( rest_get_url_prefix() ); + $_SERVER['REQUEST_URI'] = trailingslashit( rest_get_url_prefix() ) . 'wc/store/v1/checkout'; $this->assertFalse( $this->utils->is_admin_api_request() ); diff --git a/tests/unit/multi-currency/test-class-payment-methods-compatibility.php b/tests/unit/test-class-wc-payments-currency-manager.php similarity index 84% rename from tests/unit/multi-currency/test-class-payment-methods-compatibility.php rename to tests/unit/test-class-wc-payments-currency-manager.php index bdbbb767cde..6f13664ec75 100644 --- a/tests/unit/multi-currency/test-class-payment-methods-compatibility.php +++ b/tests/unit/test-class-wc-payments-currency-manager.php @@ -1,14 +1,14 @@ multi_currency_mock = $this - ->getMockBuilder( WCPay\MultiCurrency\MultiCurrency::class ) + $this->multi_currency_mock = $this->getMockBuilder( WCPay\MultiCurrency\MultiCurrency::class ) ->disableOriginalConstructor() ->setMethods( [ @@ -68,8 +67,16 @@ public function set_up() { ->getMock(); $this->gateway_mock->method( 'get_account_country' )->willReturn( 'US' ); - $this->payment_methods_compatibility = new \WCPay\MultiCurrency\PaymentMethodsCompatibility( $this->multi_currency_mock, $this->gateway_mock ); - $this->payment_methods_compatibility->init_hooks(); + $this->currency_manager = $this->getMockBuilder( \WCPay\WC_Payments_Currency_Manager::class ) + ->setConstructorArgs( [ $this->gateway_mock ] ) + ->setMethods( [ 'get_multi_currency_instance' ] ) + ->getMock(); + + // Mocking get_multi_currency_instance to return the multi_currency_mock. + $this->currency_manager->method( 'get_multi_currency_instance' ) + ->willReturn( $this->multi_currency_mock ); + + $this->currency_manager->init_hooks(); $this->localization_service = new WC_Payments_Localization_Service(); } @@ -79,7 +86,7 @@ public function test_it_should_not_update_available_currencies_when_enabled_paym $this->gateway_mock->expects( $this->atLeastOnce() )->method( 'get_upe_enabled_payment_method_ids' )->willReturn( [ 'card' ] ); $this->gateway_mock->expects( $this->atLeastOnce() )->method( 'get_account_domestic_currency' )->willReturn( 'USD' ); - $this->payment_methods_compatibility->add_missing_currencies(); + $this->currency_manager->maybe_add_missing_currencies(); } public function test_it_should_not_update_available_currencies_when_not_needed() { @@ -107,7 +114,7 @@ public function test_it_should_not_update_available_currencies_when_not_needed() ); $this->multi_currency_mock->expects( $this->never() )->method( 'set_enabled_currencies' ); - $this->payment_methods_compatibility->add_missing_currencies(); + $this->currency_manager->maybe_add_missing_currencies(); } public function test_it_should_update_available_currencies_when_needed() { @@ -147,7 +154,7 @@ public function test_it_should_update_available_currencies_when_needed() { ) ); - $this->payment_methods_compatibility->add_missing_currencies(); + $this->currency_manager->maybe_add_missing_currencies(); } public function test_it_should_not_update_available_currencies_with_bnpl_methods() { @@ -173,7 +180,7 @@ public function test_it_should_not_update_available_currencies_with_bnpl_methods ); $this->multi_currency_mock->expects( $this->never() )->method( 'set_enabled_currencies' ); - $this->payment_methods_compatibility->add_missing_currencies(); + $this->currency_manager->maybe_add_missing_currencies(); } public function test_it_should_update_available_currencies_with_bnpl_methods() { @@ -207,6 +214,6 @@ public function test_it_should_update_available_currencies_with_bnpl_methods() { ) ); - $this->payment_methods_compatibility->add_missing_currencies(); + $this->currency_manager->maybe_add_missing_currencies(); } } diff --git a/tests/unit/test-class-wc-payments-explicit-price-formatter.php b/tests/unit/test-class-wc-payments-explicit-price-formatter.php index 9c32f3afdb5..728099e4473 100644 --- a/tests/unit/test-class-wc-payments-explicit-price-formatter.php +++ b/tests/unit/test-class-wc-payments-explicit-price-formatter.php @@ -78,6 +78,13 @@ class WC_Payments_Explicit_Price_Formatter_Test extends WCPAY_UnitTestCase { */ private $mock_localization_service; + /** + * Mock of the WC_Payments_Settings_Service. + * + * @var WC_Payments_Settings_Service + */ + private $mock_settings; + /** * Mock of Database_Cache. * @@ -230,7 +237,7 @@ private function init_multi_currency( $mock_api_client = null, $wcpay_account_co $this->mock_api_client = $this->createMock( WC_Payments_API_Client::class ); $this->mock_account = $this->createMock( WC_Payments_Account::class ); - $this->mock_account->method( 'is_stripe_connected' )->willReturn( $wcpay_account_connected ); + $this->mock_account->method( 'is_provider_connected' )->willReturn( $wcpay_account_connected ); $this->mock_localization_service = $this->createMock( WC_Payments_Localization_Service::class ); @@ -247,8 +254,9 @@ private function init_multi_currency( $mock_api_client = null, $wcpay_account_co $this->mock_database_cache = $this->createMock( Database_Cache::class ); $this->mock_database_cache->method( 'get_or_add' )->willReturn( $this->mock_cached_currencies ); + $this->mock_settings = $this->createMock( WC_Payments_Settings_Service::class ); - $this->multi_currency = new MultiCurrency( $mock_api_client ?? $this->mock_api_client, $this->mock_account, $this->mock_localization_service, $this->mock_database_cache ); + $this->multi_currency = new MultiCurrency( $this->mock_settings, $mock_api_client ?? $this->mock_api_client, $this->mock_account, $this->mock_localization_service, $this->mock_database_cache ); $this->multi_currency->init(); WC_Payments_Explicit_Price_Formatter::set_multi_currency_instance( $this->multi_currency ); diff --git a/tsconfig.json b/tsconfig.json index 3b9efabf45e..f7f56cddd52 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,8 +15,9 @@ "paths": { "assets/*": [ "../assets/*" ], "wcpay/*": [ "./*" ], + "multi-currency/*": [ "../multi-currency/client/*" ], "iti/utils": [ "../node_modules/intl-tel-input/build/js/utils" ], - "react": ["../node_modules/@types/react"] + "react": [ "../node_modules/@types/react" ] }, "types": [ "node", diff --git a/webpack/shared.js b/webpack/shared.js index 7ef039967cd..55c249694ea 100644 --- a/webpack/shared.js +++ b/webpack/shared.js @@ -26,11 +26,11 @@ module.exports = { 'subscription-edit-page': './client/subscription-edit-page.js', tos: './client/tos/index.js', 'payment-gateways': './client/payment-gateways/index.js', - 'multi-currency': './client/multi-currency/index.js', + 'multi-currency': './multi-currency/client/index.js', 'multi-currency-switcher-block': - './client/multi-currency/blocks/currency-switcher.js', + './multi-currency/client/blocks/currency-switcher.js', 'multi-currency-analytics': - './client/multi-currency-analytics/index.js', + './multi-currency/client/analytics/index.js', order: './client/order/index.js', 'subscriptions-empty-state': './client/subscriptions-empty-state/index.js', @@ -113,9 +113,18 @@ module.exports = { }, resolve: { extensions: [ '.ts', '.tsx', '.json', '.js', '.jsx' ], - modules: [ path.join( process.cwd(), 'client' ), 'node_modules' ], + modules: [ + path.join( process.cwd(), 'client' ), + path.join( process.cwd(), 'multi-currency', 'client' ), + 'node_modules', + ], alias: { assets: path.resolve( process.cwd(), 'assets' ), + 'multi-currency': path.resolve( + process.cwd(), + 'multi-currency', + 'client' + ), wcpay: path.resolve( process.cwd(), 'client' ), iti: path.resolve( process.cwd(), From b6adaad6e078724b21a53d5ec852b0a60619cd5e Mon Sep 17 00:00:00 2001 From: Nagesh Pai <4162931+nagpai@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:27:30 +0530 Subject: [PATCH 10/45] Bump WC tested up to version 9.4.0 (#9634) Co-authored-by: Nagesh Pai --- changelog/dev-bump-wc-version-9.4 | 4 ++++ woocommerce-payments.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog/dev-bump-wc-version-9.4 diff --git a/changelog/dev-bump-wc-version-9.4 b/changelog/dev-bump-wc-version-9.4 new file mode 100644 index 00000000000..e56d8ba7357 --- /dev/null +++ b/changelog/dev-bump-wc-version-9.4 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Bump WC tested up to version to 9.4.0 diff --git a/woocommerce-payments.php b/woocommerce-payments.php index 7283bb20728..23367190fcd 100644 --- a/woocommerce-payments.php +++ b/woocommerce-payments.php @@ -8,7 +8,7 @@ * Text Domain: woocommerce-payments * Domain Path: /languages * WC requires at least: 7.6 - * WC tested up to: 9.3.3 + * WC tested up to: 9.4.0 * Requires at least: 6.0 * Requires PHP: 7.3 * Version: 8.4.0 From 1e32cbe49df011990b6680c9cbbc512066e61dd0 Mon Sep 17 00:00:00 2001 From: Achyuth Ajoy Date: Mon, 28 Oct 2024 16:55:12 +0530 Subject: [PATCH 11/45] Update cache key for composer dependencies (#9637) --- .github/actions/e2e/env-setup/action.yml | 2 +- .github/actions/setup-repo/action.yml | 2 +- .github/workflows/check-changelog.yml | 2 +- .github/workflows/compatibility.yml | 6 +++--- .github/workflows/coverage.yml | 2 +- .github/workflows/i18n-weekly-release.yml | 4 ++-- .github/workflows/js-lint-test.yml | 2 +- .github/workflows/php-lint-test.yml | 4 ++-- changelog/gh-actions-e2e-optimize-composer-cache | 5 +++++ 9 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 changelog/gh-actions-e2e-optimize-composer-cache diff --git a/.github/actions/e2e/env-setup/action.yml b/.github/actions/e2e/env-setup/action.yml index e1dbd310239..0296fc6eea3 100644 --- a/.github/actions/e2e/env-setup/action.yml +++ b/.github/actions/e2e/env-setup/action.yml @@ -30,7 +30,7 @@ runs: uses: actions/cache@v4 with: path: ./vendor - key: ${{ runner.os }}-vendor-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # Cache node dependencies - name: Cache node dependencies diff --git a/.github/actions/setup-repo/action.yml b/.github/actions/setup-repo/action.yml index d8de18c7d8d..2e1098a6d19 100644 --- a/.github/actions/setup-repo/action.yml +++ b/.github/actions/setup-repo/action.yml @@ -14,7 +14,7 @@ runs: uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} - name: "Set up PHP" uses: ./.github/actions/setup-php diff --git a/.github/workflows/check-changelog.yml b/.github/workflows/check-changelog.yml index 682710edbdd..3aa3936ca64 100644 --- a/.github/workflows/check-changelog.yml +++ b/.github/workflows/check-changelog.yml @@ -22,7 +22,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # setup PHP, but without debug extensions for reasonable performance - name: "Set up PHP" uses: ./.github/actions/setup-php diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 02a2d7d4c7e..e1b7279b70f 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # setup PHP, but without debug extensions for reasonable performance - uses: shivammathur/setup-php@v2 with: @@ -54,7 +54,7 @@ jobs: coverage: none # run CI checks - run: bash bin/run-ci-tests.bash - + generate-wc-compat-beta-matrix: name: "Generate the matrix for compatibility-woocommerce-beta dynamically" runs-on: ubuntu-latest @@ -86,7 +86,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # setup PHP, but without debug extensions for reasonable performance - uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 79ff0d7a01f..1728e6f9fee 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # setup PHP, but without debug extensions for reasonable performance - uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/i18n-weekly-release.yml b/.github/workflows/i18n-weekly-release.yml index 2c7f0fa0396..bd87284be4e 100644 --- a/.github/workflows/i18n-weekly-release.yml +++ b/.github/workflows/i18n-weekly-release.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} - uses: actions/cache@v4 with: path: ~/.npm/ @@ -33,7 +33,7 @@ jobs: run: | npm ci npm run build - + if [[ ! -f woocommerce-payments.zip ]]; then echo "Failed to create release zip" exit 1 diff --git a/.github/workflows/js-lint-test.yml b/.github/workflows/js-lint-test.yml index b3797f4dae0..461984d93da 100644 --- a/.github/workflows/js-lint-test.yml +++ b/.github/workflows/js-lint-test.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} - uses: actions/cache@v4 with: path: ~/.npm/ diff --git a/.github/workflows/php-lint-test.yml b/.github/workflows/php-lint-test.yml index de15390609e..3d4ca36b9ed 100644 --- a/.github/workflows/php-lint-test.yml +++ b/.github/workflows/php-lint-test.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} # setup PHP, but without debug extensions for reasonable performance - name: "Set up PHP" uses: ./.github/actions/setup-php @@ -59,7 +59,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/composer/ - key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}-${{ hashFiles('composer.lock') }} - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} diff --git a/changelog/gh-actions-e2e-optimize-composer-cache b/changelog/gh-actions-e2e-optimize-composer-cache new file mode 100644 index 00000000000..1e618c2233a --- /dev/null +++ b/changelog/gh-actions-e2e-optimize-composer-cache @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Updates gh actions cache-key for composer dependencies. + + From f9b976ee62f0320fa48bf27cc99516d4c592487e Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 15:12:50 +0100 Subject: [PATCH 12/45] Add unit test for Capital Loans page of WP Admin (#9638) --- changelog/dev-add-unit-tests-for-capital-list | 4 + .../test/__snapshots__/index.test.tsx.snap | 496 ++++++++++++++++++ client/capital/test/index.test.tsx | 107 ++++ 3 files changed, 607 insertions(+) create mode 100644 changelog/dev-add-unit-tests-for-capital-list create mode 100644 client/capital/test/__snapshots__/index.test.tsx.snap create mode 100644 client/capital/test/index.test.tsx diff --git a/changelog/dev-add-unit-tests-for-capital-list b/changelog/dev-add-unit-tests-for-capital-list new file mode 100644 index 00000000000..a99e92e7bbe --- /dev/null +++ b/changelog/dev-add-unit-tests-for-capital-list @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Add unit tests for the Capital Loans page component. diff --git a/client/capital/test/__snapshots__/index.test.tsx.snap b/client/capital/test/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000..2b146dd5714 --- /dev/null +++ b/client/capital/test/__snapshots__/index.test.tsx.snap @@ -0,0 +1,496 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CapitalPage renders the TableCard component with loan data 1`] = ` +

    +
    +
    +
    + Viewing test loans. To view live loans, disable test mode in + + WooPayments settings + + . +
    +
    +
    +
    +
    +
    + Active loan overview +
    +
    +
    +
    +
    +
    +
    + Total repaid +
    +
    + + $0.00 + + of $1,100.00 +
    +
    +
    +
    + Repaid this period (until Dec 27, 2024) +
    +
    + + $0.00 + + of $122.23 minimum +
    +
    +
    +
    +
    +
    + Loan disbursed +
    +
    + Oct 21, 2024 +
    +
    +
    +
    + Loan amount +
    +
    + $1,000.00 +
    +
    +
    +
    + Fixed fee +
    +
    + $100.00 +
    +
    +
    +
    + Withhold rate +
    +
    + 15 + % +
    +
    +
    +
    + First paydown +
    +
    + Oct 28, 2024 +
    +
    +
    +
    +
    + @@ -72,6 +72,78 @@ exports[`Deposit overview renders automatic deposit correctly 1`] = `
    `; +exports[`Deposit overview renders automatic withdrawal correctly 1`] = ` +
    +
    +
    +
    +
      +
    • +
      +
      + Withdrawal date: Jan 2, 2020 +
      +
      +
      +
      + + Completed (deducted) +
      +
      +
      +
      + MOCK BANK •••• 1234 (USD) +
      +
      +
    • +
    • + -$20.00 +
    • +
    +
    + +
    +`; + exports[`Deposit overview renders instant deposit correctly 1`] = `
    - Paid + Completed (paid)
    diff --git a/client/deposits/details/test/index.tsx b/client/deposits/details/test/index.tsx index f6fd4d98116..5d4452e9998 100644 --- a/client/deposits/details/test/index.tsx +++ b/client/deposits/details/test/index.tsx @@ -25,6 +25,19 @@ const mockDeposit = { currency: 'USD', } as CachedDeposit; +const mockWithdrawal = { + id: 'po_mock', + date: '2020-01-02 17:46:02', + type: 'withdrawal', + amount: -2000, + status: 'paid', + bankAccount: 'MOCK BANK •••• 1234 (USD)', + automatic: true, + fee: 30, + fee_percentage: 1.5, + currency: 'USD', +} as CachedDeposit; + declare const global: { wcpaySettings: { zeroDecimalCurrencies: string[]; @@ -58,9 +71,20 @@ describe( 'Deposit overview', () => { } ); test( 'renders automatic deposit correctly', () => { - const { container: overview } = render( + const { container: overview, getByText } = render( ); + getByText( /Deposit date:/ ); + getByText( 'Completed (paid)' ); + expect( overview ).toMatchSnapshot(); + } ); + + test( 'renders automatic withdrawal correctly', () => { + const { container: overview, getByText } = render( + + ); + getByText( /Withdrawal date:/ ); + getByText( 'Completed (deducted)' ); expect( overview ).toMatchSnapshot(); } ); diff --git a/client/deposits/filters/config.js b/client/deposits/filters/config.js index c63435bcaca..90d850c7f2f 100644 --- a/client/deposits/filters/config.js +++ b/client/deposits/filters/config.js @@ -7,11 +7,20 @@ import { getSetting } from '@woocommerce/settings'; /** * Internal dependencies */ -import { displayStatus } from 'deposits/strings'; +import { depositStatusLabels } from 'deposits/strings'; -const depositStatusOptions = Object.entries( - displayStatus -).map( ( [ status, label ] ) => ( { label, value: status } ) ); +const depositStatusOptions = Object.entries( depositStatusLabels ) + // Ignore the 'deducted' status, which is only a display status and not to be used in filters. + .filter( ( [ status ] ) => status !== 'deducted' ) + .map( ( [ status, label ] ) => { + if ( status === 'paid' ) { + return { + label: __( 'Completed', 'woocommerce-payments' ), + value: 'paid', + }; + } + return { label, value: status }; + } ); export const filters = [ { diff --git a/client/deposits/filters/test/__snapshots__/index.js.snap b/client/deposits/filters/test/__snapshots__/index.js.snap index 5f02b248a6c..962eccde043 100644 --- a/client/deposits/filters/test/__snapshots__/index.js.snap +++ b/client/deposits/filters/test/__snapshots__/index.js.snap @@ -5,7 +5,7 @@ HTMLOptionsCollection [ ,
    @@ -933,7 +1020,7 @@ exports[`Deposits list renders correctly with multiple currencies 1`] = ` - Paid + Completed (paid) @@ -1037,6 +1124,93 @@ exports[`Deposits list renders correctly with multiple currencies 1`] = ` + + + + + + + + + + + + + Jan 4, 2020 + + + + + Withdrawal + + + + + $40.00 + + + + + + Completed (deducted) + + + + + + MOCK BANK •••• 1234 (USD) + + +
    diff --git a/client/deposits/list/test/index.tsx b/client/deposits/list/test/index.tsx index 7c6ea8f9d17..322ca965f23 100644 --- a/client/deposits/list/test/index.tsx +++ b/client/deposits/list/test/index.tsx @@ -63,6 +63,15 @@ const mockDeposits = [ bankAccount: 'MOCK BANK •••• 1234 (USD)', currency: 'USD', } as CachedDeposit, + { + id: 'po_mock3', + date: '2020-01-04 17:46:02', + type: 'withdrawal', + amount: 4000, + status: 'paid', + bankAccount: 'MOCK BANK •••• 1234 (USD)', + currency: 'USD', + } as CachedDeposit, ]; declare const global: { @@ -317,7 +326,9 @@ describe( 'Deposits list', () => { csvFirstDeposit[ 3 ] ) ).not.toBe( -1 ); // amount - expect( csvFirstDeposit[ 4 ] ).toBe( displayFirstDeposit[ 3 ] ); // status + expect( csvFirstDeposit[ 4 ] ).toBe( + `"${ displayFirstDeposit[ 3 ] }"` + ); // status expect( csvFirstDeposit[ 5 ] ).toBe( `"${ displayFirstDeposit[ 4 ] }"` ); // bank account diff --git a/client/deposits/strings.ts b/client/deposits/strings.ts index f4cbfbfb3a6..03a8c55198d 100644 --- a/client/deposits/strings.ts +++ b/client/deposits/strings.ts @@ -16,8 +16,17 @@ export const displayType = { withdrawal: __( 'Withdrawal', 'woocommerce-payments' ), }; -export const displayStatus: Record< DepositStatus, string > = { - paid: __( 'Paid', 'woocommerce-payments' ), +/** + * Labels to display for each deposit status. + * + * 'deducted' represents a deposit of the type 'withdrawal' and status 'paid'. + */ +export const depositStatusLabels: Record< + DepositStatus | 'deducted', + string +> = { + paid: __( 'Completed (paid)', 'woocommerce-payments' ), + deducted: __( 'Completed (deducted)', 'woocommerce-payments' ), pending: __( 'Pending', 'woocommerce-payments' ), in_transit: __( 'In transit', 'woocommerce-payments' ), canceled: __( 'Canceled', 'woocommerce-payments' ), diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index e123d0319f5..7907fd83007 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -42,7 +42,7 @@ import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; import ClickableCell from 'components/clickable-cell'; import { getDetailsURL } from 'components/details-link'; import { displayType } from 'transactions/strings'; -import { displayStatus as displayDepositStatus } from 'deposits/strings'; +import { depositStatusLabels } from 'deposits/strings'; import { formatStringValue, isExportModalDismissed, @@ -454,7 +454,7 @@ export const TransactionsList = ( ); const depositStatus = txn.deposit_status - ? displayDepositStatus[ txn.deposit_status ] + ? depositStatusLabels[ txn.deposit_status ] : ''; // Map transaction into table row. @@ -597,9 +597,7 @@ export const TransactionsList = ( 'woocommerce-payments' ); - const title = props.depositId - ? __( 'Deposit transactions', 'woocommerce-payments' ) - : __( 'Transactions', 'woocommerce-payments' ); + const title = __( 'Transactions', 'woocommerce-payments' ); const downloadable = !! rows.length; diff --git a/client/transactions/list/test/__snapshots__/index.tsx.snap b/client/transactions/list/test/__snapshots__/index.tsx.snap index e638122bba2..13aebe63c2a 100644 --- a/client/transactions/list/test/__snapshots__/index.tsx.snap +++ b/client/transactions/list/test/__snapshots__/index.tsx.snap @@ -2069,7 +2069,7 @@ exports[`Transactions list renders correctly when filtered by deposit 1`] = ` data-wp-c16t="true" data-wp-component="Text" > - Deposit transactions + Transactions
    - Deposit transactions + Transactions diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx index 8d97397ce01..19c4652a7de 100644 --- a/client/transactions/list/test/index.tsx +++ b/client/transactions/list/test/index.tsx @@ -267,10 +267,11 @@ describe( 'Transactions list', () => { isLoading: false, } ); - const { container } = render( + const { container, getByRole } = render( ); expect( container ).toMatchSnapshot(); + getByRole( 'heading', { name: 'Transactions' } ); expect( mockUseTransactions.mock.calls[ 0 ][ 1 ] ).toBe( 'po_mock' ); } ); From ed04c3462affd4a0d0ccee53c67ff9fbd30b8e7b Mon Sep 17 00:00:00 2001 From: Daniel Guerra <15204776+danielmx-dev@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:00:30 +0200 Subject: [PATCH 31/45] Fix: BNPL payment methods should work when available in the Pay For Order page (#9670) --- changelog/fix-8254-bnpl-pay-for-order | 4 + client/checkout/classic/payment-processing.js | 45 ++++++++- client/checkout/utils/test/upe.test.js | 30 ++++++ client/checkout/utils/upe.js | 12 +++ includes/class-wc-payments-checkout.php | 23 ++++- .../class-wc-payments-customer-service.php | 10 ++ .../class-upe-payment-method.php | 23 ++++- .../test-class-wc-payment-gateway-wcpay.php | 96 +++++++++++++++++++ 8 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 changelog/fix-8254-bnpl-pay-for-order diff --git a/changelog/fix-8254-bnpl-pay-for-order b/changelog/fix-8254-bnpl-pay-for-order new file mode 100644 index 00000000000..77c05e9e0b1 --- /dev/null +++ b/changelog/fix-8254-bnpl-pay-for-order @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +BNPL methods now work properly in Pay for Order when they are available. Default values are also provided when available. diff --git a/client/checkout/classic/payment-processing.js b/client/checkout/classic/payment-processing.js index 514f5364ffb..839e53a2ca7 100644 --- a/client/checkout/classic/payment-processing.js +++ b/client/checkout/classic/payment-processing.js @@ -121,6 +121,41 @@ function submitForm( jQueryForm ) { jQueryForm.removeClass( 'processing' ).submit(); } +/** + * Validates the contents of the address fields based on the requirements from BNPL payment methods. + * + * @param {Object} params The parameters to be sent to `createPaymentMethod`. + * @param {string} paymentMethodType The type of Stripe payment method to create. + * @return {boolean} True, if there are missing address fields. False, if the validation passes or is not applicable. + */ +function isMissingRequiredAddressFieldsForBNPL( params, paymentMethodType ) { + if ( [ 'afterpay_clearpay', 'affirm' ].includes( paymentMethodType ) ) { + return false; + } + const address = params?.billing_details?.address; + + if ( ! address ) { + return false; + } + + const requiredAddressFields = + paymentMethodType === 'affirm' + ? [ 'line1', 'state', 'city', 'postal_code', 'country' ] // Line2 is not required. + : [ 'line1', 'postal_code', 'country' ]; // City and State are not required in Afterpay. + + for ( const field of requiredAddressFields ) { + if ( + address[ field ] === '' || + address[ field ] === null || + typeof address[ field ] === 'undefined' + ) { + return true; + } + } + + return false; +} + /** * Creates a Stripe payment method by calling the Stripe API's createPaymentMethod with the provided elements * and billing details. The billing details are obtained from various form elements on the page. @@ -144,7 +179,7 @@ function createStripePaymentMethod( billing_details: { name: wcpayCustomerData.name || undefined, email: wcpayCustomerData.email, - address: { + address: wcpayCustomerData.address || { country: wcpayCustomerData.billing_country, }, }, @@ -192,6 +227,14 @@ function createStripePaymentMethod( }; } + if ( + getUPEConfig( 'isOrderPay' ) && + isMissingRequiredAddressFieldsForBNPL( params, paymentMethodType ) + ) { + // These payment methods don't accept an address object with partial information, so we just remove the object entirely. + delete params.billing_details.address; + } + return api .getStripeForUPE( paymentMethodType ) .createPaymentMethod( { elements, params: params } ); diff --git a/client/checkout/utils/test/upe.test.js b/client/checkout/utils/test/upe.test.js index 3b868dc0d49..c846f832d87 100644 --- a/client/checkout/utils/test/upe.test.js +++ b/client/checkout/utils/test/upe.test.js @@ -284,6 +284,8 @@ describe( 'UPE checkout utils', () => { if ( checkboxElement ) { checkboxElement.remove(); } + + delete window.wcpayCustomerData; } ); it( 'should not provide terms when cart does not contain subscriptions and the saving checkbox is unchecked', () => { @@ -355,6 +357,34 @@ describe( 'UPE checkout utils', () => { expect( upeSettings.terms.card ).toEqual( 'always' ); } ); + it( 'should define defaultValues when wcpayCustomerData is present', () => { + window.wcpayCustomerData = { + name: 'Test Person', + email: 'test@example.com', + billing_country: 'US', + }; + + const upeSettings = getUpeSettings(); + + expect( upeSettings.defaultValues ).toEqual( { + billingDetails: { + name: 'Test Person', + email: 'test@example.com', + address: { + country: 'US', + }, + }, + } ); + } ); + + it( 'should not define defaultValues if wcpayCustomerData is not present', () => { + window.wcpayCustomerData = null; + + const upeSettings = getUpeSettings(); + + expect( upeSettings.defaultValues ).toBeUndefined(); + } ); + function createCheckboxElementWhich( isChecked ) { // Create the checkbox element const checkboxElement = document.createElement( 'input' ); diff --git a/client/checkout/utils/upe.js b/client/checkout/utils/upe.js index ba4dd5a11f5..504f3495f07 100644 --- a/client/checkout/utils/upe.js +++ b/client/checkout/utils/upe.js @@ -119,6 +119,18 @@ export const getUpeSettings = () => { }; } + if ( window.wcpayCustomerData ) { + upeSettings.defaultValues = { + billingDetails: { + name: window.wcpayCustomerData.name, + email: window.wcpayCustomerData.email, + address: { + country: window.wcpayCustomerData.billing_country, + }, + }, + }; + } + return upeSettings; }; diff --git a/includes/class-wc-payments-checkout.php b/includes/class-wc-payments-checkout.php index 0b1cc59d9cf..214d3178bb8 100644 --- a/includes/class-wc-payments-checkout.php +++ b/includes/class-wc-payments-checkout.php @@ -376,16 +376,35 @@ public function payment_fields() { if ( ! wp_script_is( 'wcpay-upe-checkout', 'enqueued' ) ) { $payment_fields = $this->get_payment_fields_js_config(); wp_enqueue_script( 'wcpay-upe-checkout' ); + /** + * We can't localize the script right away since at this point is not registered yet. + * We also need to make sure it that it only runs once (using a dummy action), even if + * there are multiple payment methods available; otherwise the data will be overwritten + * which is pointless. + * + * The same applies for `wcpayCustomerData` a few lines below. + */ add_action( 'wp_footer', function () use ( $payment_fields ) { - wp_localize_script( 'wcpay-upe-checkout', 'wcpay_upe_config', $payment_fields ); + if ( ! did_action( '__wcpay_upe_config_localized' ) ) { + wp_localize_script( 'wcpay-upe-checkout', 'wcpay_upe_config', $payment_fields ); + } + do_action( '__wcpay_upe_config_localized' ); } ); $prepared_customer_data = $this->customer_service->get_prepared_customer_data(); if ( ! empty( $prepared_customer_data ) ) { - wp_localize_script( 'wcpay-upe-checkout', 'wcpayCustomerData', $prepared_customer_data ); + add_action( + 'wp_footer', + function () use ( $prepared_customer_data ) { + if ( ! did_action( '__wcpay_customer_data_localized' ) ) { + wp_localize_script( 'wcpay-upe-checkout', 'wcpayCustomerData', $prepared_customer_data ); + } + do_action( '__wcpay_customer_data_localized' ); + } + ); } WC_Payments_Utils::enqueue_style( diff --git a/includes/class-wc-payments-customer-service.php b/includes/class-wc-payments-customer-service.php index e55d0b206c1..42d209fd3fd 100644 --- a/includes/class-wc-payments-customer-service.php +++ b/includes/class-wc-payments-customer-service.php @@ -531,6 +531,7 @@ public function get_prepared_customer_data() { $firstname = ''; $lastname = ''; $billing_country = ''; + $address = null; if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order_id = absint( $wp->query_vars['order-pay'] ); @@ -541,6 +542,14 @@ public function get_prepared_customer_data() { $lastname = $order->get_billing_last_name(); $user_email = $order->get_billing_email(); $billing_country = $order->get_billing_country(); + $address = [ + 'city' => $order->get_billing_city(), + 'country' => $order->get_billing_country(), + 'line1' => $order->get_billing_address_1(), + 'line2' => $order->get_billing_address_2(), + 'postal_code' => $order->get_billing_postcode(), + 'state' => $order->get_billing_state(), + ]; } } @@ -560,6 +569,7 @@ public function get_prepared_customer_data() { 'name' => $firstname . ' ' . $lastname, 'email' => $user_email, 'billing_country' => $billing_country, + 'address' => $address, ]; } } diff --git a/includes/payment-methods/class-upe-payment-method.php b/includes/payment-methods/class-upe-payment-method.php index eaaad1bfbad..93bf0797088 100644 --- a/includes/payment-methods/class-upe-payment-method.php +++ b/includes/payment-methods/class-upe-payment-method.php @@ -171,11 +171,28 @@ public function is_enabled_at_checkout( string $account_country ) { // This part ensures that when payment limits for the currency declared, those will be respected (e.g. BNPLs). if ( [] !== $this->limits_per_currency ) { + $order = null; + if ( is_wc_endpoint_url( 'order-pay' ) ) { + $order = wc_get_order( absint( get_query_var( 'order-pay' ) ) ); + $order = is_a( $order, 'WC_Order' ) ? $order : null; + } + $currency = get_woocommerce_currency(); + if ( $order ) { + $currency = $order->get_currency(); + } + // If the currency limits are not defined, we allow the PM for now (gateway has similar validation for limits). - // Additionally, we don't engage with limits verification in no-checkout context (cart is not available or empty). - if ( isset( $this->limits_per_currency[ $currency ], WC()->cart ) ) { - $amount = WC_Payments_Utils::prepare_amount( WC()->cart->get_total( '' ), $currency ); + $total = null; + if ( $order ) { + $total = $order->get_total(); + } elseif ( isset( WC()->cart ) ) { + $total = WC()->cart->get_total( '' ); + } + + if ( isset( $this->limits_per_currency[ $currency ], WC()->cart ) && ! empty( $total ) ) { + $amount = WC_Payments_Utils::prepare_amount( $total, $currency ); + if ( $amount > 0 ) { $range = null; if ( isset( $this->limits_per_currency[ $currency ][ $account_country ] ) ) { diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php index b638c07e6dd..bb90e4f4460 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php @@ -197,6 +197,21 @@ class WC_Payment_Gateway_WCPay_Test extends WCPAY_UnitTestCase { */ private $locale_backup; + + /** + * Backup of $wp->query_vars + * + * @var array + */ + private $wp_query_vars_backup; + + /** + * Backup of $wp_query->query_vars + * + * @var array + */ + private $wp_query_query_vars_backup; + /** * Pre-test setup */ @@ -281,6 +296,11 @@ public function set_up() { wcpay_get_test_container()->replace( OrderService::class, $mock_order_service ); $this->locale_backup = WC()->countries->get_country_locale(); + + global $wp; + global $wp_query; + $this->wp_query_vars_backup = $wp->query_vars; + $this->wp_query_query_vars_backup = $wp_query->query_vars; } /** @@ -318,6 +338,11 @@ public function tear_down() { wcpay_get_test_container()->reset_all_replacements(); WC()->session->set( 'wc_notices', [] ); WC()->countries->locale = $this->locale_backup; + + global $wp; + global $wp_query; + $wp->query_vars = $this->wp_query_vars_backup; + $wp_query->query_vars = $this->wp_query_query_vars_backup; } public function test_process_redirect_payment_intent_processing() { @@ -706,6 +731,77 @@ function ( $order ) { $this->assertFalse( $afterpay_method->is_enabled_at_checkout( 'US' ) ); } + public function test_payment_methods_enabled_based_on_currency_limits() { + WC_Helper_Site_Currency::$mock_site_currency = 'USD'; + + WC()->session->init(); + WC()->cart->empty_cart(); + // Total is 100 USD, which is above both payment methods (Affirm and AfterPay) minimums. + WC()->cart->add_to_cart( WC_Helper_Product::create_simple_product()->get_id(), 10 ); + WC()->cart->calculate_totals(); + + $affirm_method = $this->payment_methods['affirm']; + $afterpay_method = $this->payment_methods['afterpay_clearpay']; + + $this->assertTrue( $affirm_method->is_enabled_at_checkout( 'US' ) ); + $this->assertTrue( $afterpay_method->is_enabled_at_checkout( 'US' ) ); + } + + public function test_payment_methods_disabled_based_on_currency_limits() { + WC_Helper_Site_Currency::$mock_site_currency = 'USD'; + + WC()->session->init(); + WC()->cart->empty_cart(); + // Total is 40 USD, which is below Affirm minimum. + WC()->cart->add_to_cart( WC_Helper_Product::create_simple_product()->get_id(), 4 ); + WC()->cart->calculate_totals(); + + $affirm_method = $this->payment_methods['affirm']; + $afterpay_method = $this->payment_methods['afterpay_clearpay']; + + $this->assertFalse( $affirm_method->is_enabled_at_checkout( 'US' ) ); + $this->assertTrue( $afterpay_method->is_enabled_at_checkout( 'US' ) ); + } + + public function test_payment_methods_enabled_based_on_currency_limits_in_order_pay() { + global $wp; + global $wp_query; + + WC_Helper_Site_Currency::$mock_site_currency = 'USD'; + + // 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 ) ]; + + $affirm_method = $this->payment_methods['affirm']; + $afterpay_method = $this->payment_methods['afterpay_clearpay']; + + $this->assertTrue( $affirm_method->is_enabled_at_checkout( 'US' ) ); + $this->assertTrue( $afterpay_method->is_enabled_at_checkout( 'US' ) ); + } + + public function test_payment_methods_disabled_based_on_currency_limits_in_order_pay() { + global $wp; + global $wp_query; + + WC_Helper_Site_Currency::$mock_site_currency = 'USD'; + + // Total is 40 USD, which is below Affirm minimum. + $order = WC_Helper_Order::create_order( 1, 40 ); + $order_id = $order->get_id(); + $wp->query_vars = [ 'order-pay' => strval( $order_id ) ]; + $wp_query->query_vars = [ 'order-pay' => strval( $order_id ) ]; + $order->set_currency( 'USD' ); + + $affirm_method = $this->payment_methods['affirm']; + $afterpay_method = $this->payment_methods['afterpay_clearpay']; + + $this->assertFalse( $affirm_method->is_enabled_at_checkout( 'US' ) ); + $this->assertTrue( $afterpay_method->is_enabled_at_checkout( 'US' ) ); + } + public function test_only_valid_payment_methods_returned_for_currency() { $card_method = $this->payment_methods['card']; $giropay_method = $this->payment_methods['giropay']; From 636e1f0312d5b471c285f82451dba6b51360420d Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 7 Nov 2024 14:52:04 +0100 Subject: [PATCH 32/45] update: payment method test mode label behavior (#9647) --- .../update-payment-method-test-mode-label | 4 + client/checkout/blocks/index.js | 5 +- .../checkout/blocks/payment-method-label.js | 109 +++++++++----- client/checkout/blocks/style.scss | 32 ++-- client/checkout/classic/event-handlers.js | 66 ++++----- client/checkout/classic/style.scss | 139 +++++++++--------- includes/class-wc-payment-gateway-wcpay.php | 26 +++- ...lass-wc-payments-blocks-payment-method.php | 2 +- includes/class-wc-payments-checkout.php | 41 +++--- .../shopper/klarna-checkout-purchase.spec.ts | 1 + .../shopper/shopper-bnpls-checkout.spec.js | 18 +-- 11 files changed, 246 insertions(+), 197 deletions(-) create mode 100644 changelog/update-payment-method-test-mode-label diff --git a/changelog/update-payment-method-test-mode-label b/changelog/update-payment-method-test-mode-label new file mode 100644 index 00000000000..1c3874ebf5f --- /dev/null +++ b/changelog/update-payment-method-test-mode-label @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +update: payment method "test mode" label at checkout to be displayed only when payment method is selected diff --git a/client/checkout/blocks/index.js b/client/checkout/blocks/index.js index 924afccde1f..68593bababd 100644 --- a/client/checkout/blocks/index.js +++ b/client/checkout/blocks/index.js @@ -110,7 +110,10 @@ Object.entries( enabledPaymentMethodsConfig ) label: ( diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js index 417007816aa..b329a165658 100644 --- a/client/checkout/blocks/payment-method-label.js +++ b/client/checkout/blocks/payment-method-label.js @@ -12,9 +12,50 @@ import './style.scss'; import { useEffect, useState } from '@wordpress/element'; import { getAppearance } from 'wcpay/checkout/upe-styles'; -export default ( { api, upeConfig, upeName, upeAppearanceTheme } ) => { +const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ]; +const PaymentMethodMessageWrapper = ( { + upeName, + countries, + currentCountry, + amount, + appearance, + children, +} ) => { + if ( ! bnplMethods.includes( upeName ) ) { + return null; + } + + if ( amount <= 0 ) { + return null; + } + + if ( ! currentCountry ) { + return null; + } + + if ( ! appearance ) { + return null; + } + + if ( countries.length !== 0 && ! countries.includes( currentCountry ) ) { + return null; + } + + return ( +
    { children }
    + ); +}; + +export default ( { + api, + title, + countries, + iconLight, + iconDark, + upeName, + upeAppearanceTheme, +} ) => { const cartData = wp.data.select( 'wc/store/cart' ).getCartData(); - const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ]; const isTestMode = getUPEConfig( 'testMode' ); const [ appearance, setAppearance ] = useState( getUPEConfig( 'wcBlocksUPEAppearance' ) @@ -35,8 +76,6 @@ export default ( { api, upeConfig, upeName, upeAppearanceTheme } ) => { window.wcBlocksCheckoutData?.storeCountry || 'US'; - const isCreditCard = upeName === 'card'; - useEffect( () => { async function generateUPEAppearance() { // Generate UPE input styles. @@ -56,10 +95,8 @@ export default ( { api, upeConfig, upeName, upeAppearanceTheme } ) => { return ( <>
    - - { upeConfig.title } - - { isCreditCard && isTestMode && ( + { title } + { isTestMode && ( { __( 'Test Mode', 'woocommerce-payments' ) } @@ -67,39 +104,35 @@ export default ( { api, upeConfig, upeName, upeAppearanceTheme } ) => { {
    - { bnplMethods.includes( upeName ) && - ( upeConfig.countries.length === 0 || - upeConfig.countries.includes( currentCountry ) ) && - amount > 0 && - currentCountry && - appearance && ( -
    - - - -
    - ) } + + + + + ); }; diff --git a/client/checkout/blocks/style.scss b/client/checkout/blocks/style.scss index b43b7217d9f..a6aa37953b3 100644 --- a/client/checkout/blocks/style.scss +++ b/client/checkout/blocks/style.scss @@ -43,6 +43,21 @@ button.wcpay-stripelink-modal-trigger:hover { } .wc-block-checkout__payment-method { + input:checked ~ div { + .wc-block-components-radio-control__label { + > .payment-method-label { + .test-mode.badge { + // hiding the badge when the payment method is not selected + display: inline-block; + } + + &__pmme-container { + display: none; + } + } + } + } + .wc-block-components-radio-control__label { width: 100%; display: block !important; @@ -77,6 +92,7 @@ button.wcpay-stripelink-modal-trigger:hover { color: #4d3716; justify-self: start; width: max-content; + display: none; } @include breakpoint( '<480px' ) { @@ -88,13 +104,14 @@ button.wcpay-stripelink-modal-trigger:hover { justify-self: end; } } - } - .bnpl-message { - width: 100%; + &__pmme-container { + width: 100%; + pointer-events: none; - @include breakpoint( '<480px' ) { - margin-top: 8px; + @include breakpoint( '<480px' ) { + margin-top: 8px; + } } } } @@ -112,11 +129,6 @@ button.wcpay-stripelink-modal-trigger:hover { } #payment-method { - label.wc-block-components-radio-control__option-checked { - .StripeElement { - display: none; - } - } /* stylelint-disable-next-line selector-id-pattern */ #radio-control-wc-payment-method-options-woocommerce_payments_affirm__label img { diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js index 6e4b8fb43fe..714142cae62 100644 --- a/client/checkout/classic/event-handlers.js +++ b/client/checkout/classic/event-handlers.js @@ -159,55 +159,43 @@ jQuery( function ( $ ) { async function injectStripePMMEContainers() { const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ]; - const labelBase = 'payment_method_woocommerce_payments_'; const paymentMethods = getUPEConfig( 'paymentMethodsConfig' ); const paymentMethodsKeys = Object.keys( paymentMethods ); const cartData = await api.pmmeGetCartData(); for ( const method of paymentMethodsKeys ) { if ( bnplMethods.includes( method ) ) { - const targetLabel = document.querySelector( - `label[for="${ labelBase }${ method }"]` - ); const containerID = `stripe-pmme-container-${ method }`; + const container = document.getElementById( containerID ); - if ( document.getElementById( containerID ) ) { - document.getElementById( containerID ).innerHTML = ''; + if ( ! container ) { + continue; } - if ( targetLabel ) { - let container = document.getElementById( containerID ); - if ( ! container ) { - container = document.createElement( 'span' ); - container.id = containerID; - container.dataset.paymentMethodType = method; - container.classList.add( 'stripe-pmme-container' ); - targetLabel.appendChild( container ); - } - - const currentCountry = - cartData?.billing_address?.country || - getUPEConfig( 'storeCountry' ); - - if ( - paymentMethods[ method ]?.countries.length === 0 || - paymentMethods[ method ]?.countries?.includes( - currentCountry - ) - ) { - await mountStripePaymentMethodMessagingElement( - api, - container, - { - amount: cartData?.totals?.total_price, - currency: cartData?.totals?.currency_code, - decimalPlaces: - cartData?.totals?.currency_minor_unit, - country: currentCountry, - }, - 'shortcode_checkout' - ); - } + container.innerHTML = ''; + container.dataset.paymentMethodType = method; + + const currentCountry = + cartData?.billing_address?.country || + getUPEConfig( 'storeCountry' ); + if ( + paymentMethods[ method ]?.countries.length === 0 || + paymentMethods[ method ]?.countries?.includes( + currentCountry + ) + ) { + await mountStripePaymentMethodMessagingElement( + api, + container, + { + amount: cartData?.totals?.total_price, + currency: cartData?.totals?.currency_code, + decimalPlaces: + cartData?.totals?.currency_minor_unit, + country: currentCountry, + }, + 'shortcode_checkout' + ); } } } diff --git a/client/checkout/classic/style.scss b/client/checkout/classic/style.scss index 5b452accc83..36a3d29bd94 100644 --- a/client/checkout/classic/style.scss +++ b/client/checkout/classic/style.scss @@ -30,6 +30,11 @@ border: none; } +.woopayments-rich-payment-method-label { + // this will be displayed only on specific scenarios. Otherwise, the "legacy" label will be displayed. + display: none; +} + #payment .payment_methods { li[class*='payment_method_woocommerce_payments'] > label > img { float: right; @@ -41,32 +46,68 @@ &.wc_payment_methods, &.woocommerce-PaymentMethods { - li.payment_method_woocommerce_payments { + li[class*='payment_method_woocommerce_payments'] { display: grid; grid-template-columns: 0fr 0fr 1fr; grid-template-rows: max-content; + .woopayments-plain-payment-method-label { + display: none; + } + > input[name='payment_method'] { - align-self: center; + &:checked ~ label { + .payment-method-title { + margin-right: 8px; // 8px gap between .payment-method-title and .test-mode.badge + } + + .test-mode.badge { + display: inline-block; // hiding the badge when the payment method is not selected + } + + .stripe-pmme-container { + display: none; + } + } } + > label { grid-column: 3; + margin-bottom: 0; + } + + > div.payment_box { + grid-area: 2 / 1 / 3 / 4; + } + + > label:has( .woopayments-rich-payment-method-label ) { + display: inline-flex; + align-items: center; + width: 100%; + + > img { + display: none; // we'll display the image inside `.woopayments-rich-payment-method-label`, instead. + } + } + + .woopayments-rich-payment-method-label { display: grid; - grid-template-columns: 0fr auto; - grid-template-rows: max-content; - grid-gap: 0; + grid-template-columns: 1fr auto; + align-items: center; margin-bottom: 0; + flex-grow: 1; - > .label-title-container { - grid-area: 1 / 2 / 2 / 3; + .label-title-container { + display: block; } .payment-method-title { - margin-right: 8px; + white-space: normal; // Allows wrapping if text is too long + vertical-align: middle; } .test-mode.badge { - display: inline-block; + display: none; background-color: #fff2d7; border-radius: 4px; padding: 4px 6px; @@ -75,73 +116,31 @@ line-height: 16px; color: #4d3716; vertical-align: middle; + white-space: nowrap; // Prevents the badge text from wrapping } img { - float: none; - grid-area: 1 / 4 / 2 / 5; - align-self: baseline; + border: 0; + padding: 0; + height: 24px !important; + max-height: 24px !important; justify-self: end; - margin-left: 1em; + margin: 3px 0; // ensuring the images don't appear squished when all the payment methods are rendered next to each others, like in Elementor. + align-self: center; } - } - > div.payment_box { - grid-area: 2 / 1 / 3 / 4; - } - } - } -} - -li.wc_payment_method:has( .input-radio:not( :checked ) - + label - .stripe-pmme-container ) { - display: grid; - grid-template-columns: min-content 1fr; - grid-template-rows: auto auto; - align-items: baseline; - - .input-radio { - grid-row: 1; - grid-column: 1; - } - - label { - grid-column: 2; - grid-row: 1; - } - - img { - grid-row: 1 / span 2; - align-self: center; - } - .stripe-pmme-container { - width: 100%; - grid-column: 1; - grid-row-start: 2; - pointer-events: none; - } - - .payment_box { - flex: 0 0 100%; - grid-row: 2; - grid-column: 1 / span 2; - } -} - -li.wc_payment_method:has( .input-radio:checked - + label - .stripe-pmme-container ) { - display: block; - - .input-radio:checked { - + label { - .stripe-pmme-container { - display: none; - } - - img { - grid-column: 2; + .stripe-pmme-container { + &:empty { + display: none; // hides container if empty, without affecting alignment + } + + margin-left: 0.25em; // WooCommerce Core will add a   on the left of the payment method's label - this spacing ensures that at least it's consistently aligned. + pointer-events: none; + grid-column: 1 / 2; + grid-row: 2 / 3; + align-self: start; + width: 100%; + } } } } diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 2a50957df4d..f4b87f36354 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -581,16 +581,32 @@ public function get_title() { $title = parent::get_title(); if ( - Payment_Method::CARD === $this->stripe_id && ( is_checkout() || is_add_payment_method_page() ) && ! isset( $_GET['change_payment_method'] ) // phpcs:ignore WordPress.Security.NonceVerification ) { + $test_mode_badge = ''; if ( WC_Payments::mode()->is_test() ) { $test_mode_badge = '' . __( 'Test Mode', 'woocommerce-payments' ) . ''; - } else { - $test_mode_badge = ''; } - return '
     ' . $title . '' . $test_mode_badge . '
    '; + + $bnpl_messaging_container = ''; + if ( $this->payment_method->is_bnpl() ) { + $bnpl_messaging_container = ''; + } + + // the "plain" payment method label is displayed on some sections of the app + // - like "pay for order" when a payment method is pre-selected or a payment has previously failed. + $html = '' . $title . ''; + $html .= '
    '; + $html .= '
    '; + $html .= ' ' . $title . ''; + $html .= $test_mode_badge; + $html .= '
    '; + $html .= $this->get_icon(); + $html .= $bnpl_messaging_container; + $html .= '
    '; + + return $html; } return $title; @@ -3236,7 +3252,7 @@ public function update_fraud_rules_based_on_general_options() { * @return string */ public function get_icon() { - return '' . esc_attr( $this->payment_method->get_title() ) . ' payment method logo'; + return '' . esc_attr( $this->payment_method->get_title() ) . ' payment method logo'; } /** diff --git a/includes/class-wc-payments-blocks-payment-method.php b/includes/class-wc-payments-blocks-payment-method.php index 607bb3cafb0..877d4bf4270 100644 --- a/includes/class-wc-payments-blocks-payment-method.php +++ b/includes/class-wc-payments-blocks-payment-method.php @@ -58,7 +58,7 @@ public function get_payment_method_script_handles() { 'wc-blocks-checkout-style', plugins_url( 'dist/blocks-checkout.css', WCPAY_PLUGIN_FILE ), [], - '1.0', + WC_Payments::get_file_version( 'dist/checkout.css' ), 'all' ); } diff --git a/includes/class-wc-payments-checkout.php b/includes/class-wc-payments-checkout.php index 214d3178bb8..c4593f54b7e 100644 --- a/includes/class-wc-payments-checkout.php +++ b/includes/class-wc-payments-checkout.php @@ -134,9 +134,7 @@ public function register_scripts() { Fraud_Prevention_Service::maybe_append_fraud_prevention_token(); - $script = 'dist/checkout'; - - WC_Payments::register_script_with_dependencies( 'wcpay-upe-checkout', $script, $script_dependencies ); + WC_Payments::register_script_with_dependencies( 'wcpay-upe-checkout', 'dist/checkout', $script_dependencies ); } /** @@ -417,32 +415,29 @@ function () use ( $prepared_customer_data ) { } // Output the form HTML. - ?> - gateway->get_description() ) ) : ?> + if ( ! empty( $this->gateway->get_description() ) ) : ?>

    gateway->get_description() ); ?>

    - + is_test() ) : ?> + if ( WC_Payments::mode()->is_test() && false !== $this->gateway->get_payment_method()->get_testing_instructions() ) : + ?>

    - gateway->get_payment_method()->get_testing_instructions(); - if ( false !== $testing_instructions ) { + '', - 'strong' => '', - 'number' => ' or refer to our testing guide.', + $expected_card + ); + + $this->assertEquals( + $expected_instructions, + $config['paymentMethodsConfig']['card']['testingInstructions'] + ); + } } From daf79d50370d7fa0cdf8a0ece2aada38d6a1cf8d Mon Sep 17 00:00:00 2001 From: Shendy <73803630+shendy-a8c@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:46:17 +0700 Subject: [PATCH 39/45] Rename 'Deposit' to 'Payout' (feature branch) (#9606) Co-authored-by: Nagesh Pai <4162931+nagpai@users.noreply.github.com> Co-authored-by: Nagesh Pai Co-authored-by: bruce aldridge Co-authored-by: Rua Haszard Co-authored-by: Rua Haszard Co-authored-by: Jessy Pappachan <32092402+jessy-p@users.noreply.github.com> Co-authored-by: Jessy Co-authored-by: Eric Jinks <3147296+Jinksi@users.noreply.github.com> --- changelog/add-9673-payouts-rename-notice | 5 + changelog/dev-1-9601-rename-deposit-dev-docs | 5 + changelog/feature-deposits-payouts-rename | 4 + changelog/fix-9525-menu-url-payouts-rename | 5 + changelog/fix-9527-payouts-rename-settings | 5 + ...2-rename-payouts-instant-depost-modal-flow | 5 + changelog/fix-9573-rename-payout-tos-modal | 5 + changelog/fix-9585-rename-instant-payouts | 5 + .../fix-9588-rename-payouts-error-messages | 5 + changelog/fix-payout-rename-account-errors | 5 + .../update-9524-deposit-payout-deposits-list | 5 + ...transactions-list-deposit-to-payout-rename | 5 + ...nsaction-timeline-deposit-to-payout-rename | 5 + ...date-9578-79-rename-deposit-overview-modal | 5 + ...te-9581-rename-deposit-onboarding-tasklist | 5 + changelog/update-9587-doc-url-payout-rename | 5 + .../account-balances/balance-tooltip.tsx | 4 +- client/components/account-balances/index.tsx | 19 +- client/components/account-balances/strings.ts | 2 +- .../account-balances/test/index.test.tsx | 8 +- .../account-status/account-tools/strings.tsx | 2 +- .../test/__snapshots__/index.test.tsx.snap | 2 +- client/components/account-status/index.js | 2 +- .../components/account-status/status-chip.js | 2 +- .../test/__snapshots__/index.js.snap | 8 +- .../deposits-overview/deposit-notices.tsx | 24 +- .../deposits-overview/deposit-schedule.tsx | 12 +- client/components/deposits-overview/index.tsx | 16 +- .../recent-deposits-list.tsx | 2 +- .../test/__snapshots__/index.tsx.snap | 26 +- .../deposits-overview/test/index.tsx | 34 +-- client/components/deposits-status/index.tsx | 2 +- .../test/__snapshots__/index.js.snap | 6 +- client/components/details-link/index.tsx | 2 +- .../test/__snapshots__/index.test.tsx.snap | 2 +- .../details-link/test/index.test.tsx | 4 +- .../modal/index.tsx | 2 +- client/components/test-mode-notice/index.tsx | 6 +- .../test/__snapshots__/index.tsx.snap | 2 +- client/connect-account-page/strings.tsx | 8 +- .../test/__snapshots__/index.test.tsx.snap | 12 +- .../test/info-notice-modal.test.tsx | 2 +- client/data/deposits/actions.js | 6 +- client/data/deposits/resolvers.js | 6 +- client/deposits/details/index.tsx | 18 +- .../details/test/__snapshots__/index.tsx.snap | 12 +- client/deposits/details/test/index.tsx | 4 +- client/deposits/filters/config.js | 27 +- client/deposits/filters/index.js | 2 +- client/deposits/filters/test/index.js | 18 +- client/deposits/index.tsx | 4 +- .../index.tsx | 12 +- .../modal.tsx | 30 +-- .../style.scss | 8 +- .../test/__snapshots__/index.tsx.snap | 26 +- .../test/index.tsx | 8 +- client/deposits/list/index.tsx | 16 +- .../list/test/__snapshots__/index.tsx.snap | 96 +++---- client/deposits/list/test/index.tsx | 2 +- .../deposits/rename-notice/header-image.svg | 30 +++ client/deposits/rename-notice/index.tsx | 88 +++++++ client/deposits/rename-notice/style.scss | 77 ++++++ .../test/__snapshots__/index.tsx.snap | 9 + client/deposits/rename-notice/test/index.tsx | 49 ++++ client/deposits/strings.ts | 2 +- client/globals.d.ts | 1 + client/index.js | 14 +- client/overview/index.js | 2 + .../index.tsx | 6 +- .../test/index.test.tsx | 2 +- .../overview/modal/reset-account/strings.tsx | 2 +- .../modal/update-business-details/strings.tsx | 4 +- .../test/__snapshots__/index.tsx.snap | 6 +- client/overview/task-list/strings.tsx | 14 +- .../task-list/tasks/test/po-tasks.tsx | 18 +- .../tasks/update-business-details-task.tsx | 6 +- client/overview/task-list/test/tasks.js | 4 +- client/overview/test/index.js | 20 ++ client/payment-details/timeline/map-events.js | 34 +-- .../timeline/test/__snapshots__/index.js.snap | 20 +- .../test/__snapshots__/map-events.js.snap | 64 ++--- client/settings/deposits/index.js | 22 +- client/settings/deposits/test/index.test.js | 26 +- client/settings/settings-manager/index.js | 8 +- client/tos/modal/index.js | 2 +- .../modal/test/__snapshots__/index.js.snap | 2 +- client/transactions/list/deposit.tsx | 10 +- client/transactions/list/index.tsx | 16 +- .../list/test/__snapshots__/deposit.tsx.snap | 16 +- .../list/test/__snapshots__/index.tsx.snap | 80 +++--- client/transactions/list/test/deposit.tsx | 8 +- client/transactions/list/test/index.tsx | 18 +- composer.lock | 242 +++++++++--------- docs/dependencies.md | 2 +- .../wp-api-v3/{deposits.md => payouts.md} | 91 +++---- .../source/includes/wp-api-v3/reports.md | 4 +- docs/rest-api/source/index.html.md | 2 +- includes/admin/class-wc-payments-admin.php | 32 ++- .../class-wc-payments-captured-event-note.php | 2 +- includes/class-wc-payments-features.php | 9 + .../class-wc-payments-onboarding-service.php | 12 +- ...wc-payments-webhook-processing-service.php | 2 +- includes/class-wc-payments.php | 1 + ...yments-notes-instant-deposits-eligible.php | 10 +- readme.txt | 2 +- tests/e2e-pw/README.md | 4 +- ...elect-deposits-list-advanced-filters-1.png | Bin 16022 -> 15848 bytes .../merchant/merchant-admin-deposits.spec.ts | 10 +- tests/e2e/utils/flows.js | 2 +- .../fixtures/captured-payments/discount.json | 2 +- .../captured-payments/foreign-card.json | 2 +- .../captured-payments/fx-decimal.json | 2 +- .../captured-payments/fx-foreign-card.json | 2 +- .../captured-payments/fx-partial-capture.json | 2 +- .../captured-payments/fx-with-capped-fee.json | 2 +- tests/fixtures/captured-payments/fx.json | 2 +- .../captured-payments/jpy-payment.json | 2 +- .../captured-payments/only-base-fee.json | 2 +- .../captured-payments/partial-capture.json | 2 +- .../captured-payments/subscription.json | 2 +- .../admin/test-class-wc-payments-admin.php | 4 +- ...wc-payments-webhook-processing-service.php | 2 +- 122 files changed, 1060 insertions(+), 660 deletions(-) create mode 100644 changelog/add-9673-payouts-rename-notice create mode 100644 changelog/dev-1-9601-rename-deposit-dev-docs create mode 100644 changelog/feature-deposits-payouts-rename create mode 100644 changelog/fix-9525-menu-url-payouts-rename create mode 100644 changelog/fix-9527-payouts-rename-settings create mode 100644 changelog/fix-9542-rename-payouts-instant-depost-modal-flow create mode 100644 changelog/fix-9573-rename-payout-tos-modal create mode 100644 changelog/fix-9585-rename-instant-payouts create mode 100644 changelog/fix-9588-rename-payouts-error-messages create mode 100644 changelog/fix-payout-rename-account-errors create mode 100644 changelog/update-9524-deposit-payout-deposits-list create mode 100644 changelog/update-9543-transactions-list-deposit-to-payout-rename create mode 100644 changelog/update-9564-transaction-timeline-deposit-to-payout-rename create mode 100644 changelog/update-9578-79-rename-deposit-overview-modal create mode 100644 changelog/update-9581-rename-deposit-onboarding-tasklist create mode 100644 changelog/update-9587-doc-url-payout-rename rename client/deposits/{instant-deposits => instant-payouts}/index.tsx (84%) rename client/deposits/{instant-deposits => instant-payouts}/modal.tsx (68%) rename client/deposits/{instant-deposits => instant-payouts}/style.scss (74%) rename client/deposits/{instant-deposits => instant-payouts}/test/__snapshots__/index.tsx.snap (70%) rename client/deposits/{instant-deposits => instant-payouts}/test/index.tsx (90%) create mode 100644 client/deposits/rename-notice/header-image.svg create mode 100644 client/deposits/rename-notice/index.tsx create mode 100644 client/deposits/rename-notice/style.scss create mode 100644 client/deposits/rename-notice/test/__snapshots__/index.tsx.snap create mode 100644 client/deposits/rename-notice/test/index.tsx rename docs/rest-api/source/includes/wp-api-v3/{deposits.md => payouts.md} (70%) diff --git a/changelog/add-9673-payouts-rename-notice b/changelog/add-9673-payouts-rename-notice new file mode 100644 index 00000000000..31a258dee71 --- /dev/null +++ b/changelog/add-9673-payouts-rename-notice @@ -0,0 +1,5 @@ +Significance: patch +Type: add +Comment: Added notice to indicate the rename of Deposits to Payouts. User facing changelog will be added as part of feature branch + + diff --git a/changelog/dev-1-9601-rename-deposit-dev-docs b/changelog/dev-1-9601-rename-deposit-dev-docs new file mode 100644 index 00000000000..bd7141bb971 --- /dev/null +++ b/changelog/dev-1-9601-rename-deposit-dev-docs @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Rename 'deposit' to 'payout' within the developer doc description texts. The change is a part of a larger renaming project. + + diff --git a/changelog/feature-deposits-payouts-rename b/changelog/feature-deposits-payouts-rename new file mode 100644 index 00000000000..cd080e4557b --- /dev/null +++ b/changelog/feature-deposits-payouts-rename @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Rename 'deposit' to 'payout' across various places in the WooPayments admin UI, docs and doc URLs. diff --git a/changelog/fix-9525-menu-url-payouts-rename b/changelog/fix-9525-menu-url-payouts-rename new file mode 100644 index 00000000000..51956b98d3b --- /dev/null +++ b/changelog/fix-9525-menu-url-payouts-rename @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Part of feature Renaming deposits to payouts + + diff --git a/changelog/fix-9527-payouts-rename-settings b/changelog/fix-9527-payouts-rename-settings new file mode 100644 index 00000000000..7690c2fee06 --- /dev/null +++ b/changelog/fix-9527-payouts-rename-settings @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Payouts rename: Settings page, part of a larger change + + diff --git a/changelog/fix-9542-rename-payouts-instant-depost-modal-flow b/changelog/fix-9542-rename-payouts-instant-depost-modal-flow new file mode 100644 index 00000000000..5d97529f78a --- /dev/null +++ b/changelog/fix-9542-rename-payouts-instant-depost-modal-flow @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Part of a larger change - renaming deposit to payout; this PR fixes instant payout modal + button + + diff --git a/changelog/fix-9573-rename-payout-tos-modal b/changelog/fix-9573-rename-payout-tos-modal new file mode 100644 index 00000000000..839d4364380 --- /dev/null +++ b/changelog/fix-9573-rename-payout-tos-modal @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Rename Deposit to Payout in TOS modal. Changelog to be included in featture branch + + diff --git a/changelog/fix-9585-rename-instant-payouts b/changelog/fix-9585-rename-instant-payouts new file mode 100644 index 00000000000..029812b1b97 --- /dev/null +++ b/changelog/fix-9585-rename-instant-payouts @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Renamed instant deposit to instant payout. Main changelog will be part of feature branch + + diff --git a/changelog/fix-9588-rename-payouts-error-messages b/changelog/fix-9588-rename-payouts-error-messages new file mode 100644 index 00000000000..c20e31649e1 --- /dev/null +++ b/changelog/fix-9588-rename-payouts-error-messages @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Updated the payouts component error notices. User facing changelog will be on the feature branch. + + diff --git a/changelog/fix-payout-rename-account-errors b/changelog/fix-payout-rename-account-errors new file mode 100644 index 00000000000..5d661f369d7 --- /dev/null +++ b/changelog/fix-payout-rename-account-errors @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Renamed deposit to Payout in account error messages. Main changelog will be added on feature branch. + + diff --git a/changelog/update-9524-deposit-payout-deposits-list b/changelog/update-9524-deposit-payout-deposits-list new file mode 100644 index 00000000000..e25a0d70f23 --- /dev/null +++ b/changelog/update-9524-deposit-payout-deposits-list @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Change deposit to payout in deposits list page. + + diff --git a/changelog/update-9543-transactions-list-deposit-to-payout-rename b/changelog/update-9543-transactions-list-deposit-to-payout-rename new file mode 100644 index 00000000000..72b9c3ffa67 --- /dev/null +++ b/changelog/update-9543-transactions-list-deposit-to-payout-rename @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Update 'deposit' to 'payout' in UI labels of the transaction list. This PR is a part of a project doing this rename across all parts of the UI. + + diff --git a/changelog/update-9564-transaction-timeline-deposit-to-payout-rename b/changelog/update-9564-transaction-timeline-deposit-to-payout-rename new file mode 100644 index 00000000000..d143380b730 --- /dev/null +++ b/changelog/update-9564-transaction-timeline-deposit-to-payout-rename @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Part of a larger change to change 'deposit' to 'payout'. This PR specificially changes it for the transaction details timeline. + + diff --git a/changelog/update-9578-79-rename-deposit-overview-modal b/changelog/update-9578-79-rename-deposit-overview-modal new file mode 100644 index 00000000000..0eca2ef1120 --- /dev/null +++ b/changelog/update-9578-79-rename-deposit-overview-modal @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Rename 'deposit' to 'payout' in modals within 'Overview' section. PR is a part of a larger renaming project. + + diff --git a/changelog/update-9581-rename-deposit-onboarding-tasklist b/changelog/update-9581-rename-deposit-onboarding-tasklist new file mode 100644 index 00000000000..a6f4c688343 --- /dev/null +++ b/changelog/update-9581-rename-deposit-onboarding-tasklist @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Rename 'deposit' to 'payout' within the onboarding tasklist section. The change is a part of a larger project of doing this rename across the codebase. + + diff --git a/changelog/update-9587-doc-url-payout-rename b/changelog/update-9587-doc-url-payout-rename new file mode 100644 index 00000000000..62933e0a818 --- /dev/null +++ b/changelog/update-9587-doc-url-payout-rename @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Update 'deposit' to 'payout' in URLs of WooPayments docs occuring across the codebase. Part of a larger renaming project. + + diff --git a/client/components/account-balances/balance-tooltip.tsx b/client/components/account-balances/balance-tooltip.tsx index 257d041ca3c..2bf267856f6 100644 --- a/client/components/account-balances/balance-tooltip.tsx +++ b/client/components/account-balances/balance-tooltip.tsx @@ -36,7 +36,7 @@ export const TotalBalanceTooltip: React.FC< TotalBalanceTooltipProps > = ( { <> { interpolateComponents( { mixedString: __( - '{{bold}}Total balance{{/bold}} combines both pending funds (transactions under processing) and available funds (ready for deposit). {{learnMoreLink}}Learn more{{/learnMoreLink}}', + '{{bold}}Total balance{{/bold}} combines both pending funds (transactions under processing) and available funds (ready for payout). {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), components: { @@ -105,7 +105,7 @@ export const AvailableBalanceTooltip: React.FC< AvailableBalanceTooltipProps > =

    { interpolateComponents( { mixedString: __( - '{{bold}}Available funds{{/bold}} have completed processing and are ready to be deposited into your bank account. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + '{{bold}}Available funds{{/bold}} have completed processing and are ready to be dispatched to your bank account. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), components: { diff --git a/client/components/account-balances/index.tsx b/client/components/account-balances/index.tsx index fa3be8b8422..a72f884bed8 100644 --- a/client/components/account-balances/index.tsx +++ b/client/components/account-balances/index.tsx @@ -15,7 +15,7 @@ import type * as AccountOverview from 'wcpay/types/account-overview'; import BalanceBlock from './balance-block'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; import InlineNotice from '../inline-notice'; -import InstantDepositButton from 'deposits/instant-deposits'; +import InstantPayoutButton from 'wcpay/deposits/instant-payouts'; import SendMoneyIcon from 'assets/images/icons/send-money.svg?asset'; import { TotalBalanceTooltip, @@ -154,9 +154,8 @@ const AccountBalances: React.FC = () => { > { sprintf( __( - /* translators: %$1$s: Available instant deposit amount, %2$s: Instant deposit fee percentage */ - /* 'Instantly deposit %1$s and get funds in your bank account in 30 mins for a %2$s%% fee.' */ - 'Get %1$s via instant deposit. Funds are typically in your bank account within 30 mins. Fee: %2$s%%.', + /* translators: %$1$s: Available instant payout amount, %2$s: Instant payout fee percentage */ + 'Get %1$s via instant payout. Funds are typically in your bank account within 30 mins. Fee: %2$s%%.', 'woocommerce-payments' ), formatCurrency( @@ -170,7 +169,7 @@ const AccountBalances: React.FC = () => { ) } - { } buttonLabel={ __( - 'Learn more about instant deposit', + 'Learn more about instant payouts', 'woocommerce-payments' ) } content={ - /* 'With instant deposit you can receive requested funds in your bank account within 30 mins for a 1.5% fee. Learn more' */ + /* 'With instant payout you can receive requested funds in your bank account within 30 mins for a 1.5% fee. Learn more' */ interpolateComponents( { mixedString: sprintf( __( - /* translators: %s: Instant deposit fee percentage */ - 'With {{strong}}instant deposit{{/strong}} you can receive requested funds in your bank account within 30 mins for a %s%% fee. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + /* translators: %s: Instant payout fee percentage */ + 'With {{strong}}instant payout{{/strong}} you can receive requested funds in your bank account within 30 mins for a %s%% fee. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), selectedOverview.instantBalance @@ -199,7 +198,7 @@ const AccountBalances: React.FC = () => { strong: , learnMoreLink: ( { } ); fireEvent.click( tooltipButton ); const tooltip = screen.getByRole( 'tooltip', { - name: /Available funds have completed processing and are ready to be deposited into your bank account./, + name: /Available funds have completed processing and are ready to be dispatched to your bank account./, } ); expect( within( tooltip ).getByRole( 'link' ) ).toHaveAttribute( 'href', - 'https://woocommerce.com/document/woopayments/deposits/deposit-schedule/' + 'https://woocommerce.com/document/woopayments/payouts/payout-schedule/' ); } ); @@ -289,11 +289,11 @@ describe( 'AccountBalances', () => { } ); fireEvent.click( tooltipButton ); const tooltip = screen.getByRole( 'tooltip', { - name: /Total balance combines both pending funds \(transactions under processing\) and available funds \(ready for deposit\)\./, + name: /Total balance combines both pending funds \(transactions under processing\) and available funds \(ready for payout\)\./, } ); expect( within( tooltip ).getByRole( 'link' ) ).toHaveAttribute( 'href', - 'https://woocommerce.com/document/woopayments/deposits/deposit-schedule/' + 'https://woocommerce.com/document/woopayments/payouts/payout-schedule/' ); } ); diff --git a/client/components/account-status/account-tools/strings.tsx b/client/components/account-status/account-tools/strings.tsx index f47e8416574..83ca0f9c1bc 100644 --- a/client/components/account-status/account-tools/strings.tsx +++ b/client/components/account-status/account-tools/strings.tsx @@ -17,7 +17,7 @@ export default { 'woocommerce-payments' ) : __( - 'Payments and deposits are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning.', + 'Payments and payouts are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning.', 'woocommerce-payments' ), reset: __( 'Reset account', 'woocommerce-payments' ), diff --git a/client/components/account-status/account-tools/test/__snapshots__/index.test.tsx.snap b/client/components/account-status/account-tools/test/__snapshots__/index.test.tsx.snap index a3217d4df35..b2adb97b886 100644 --- a/client/components/account-status/account-tools/test/__snapshots__/index.test.tsx.snap +++ b/client/components/account-status/account-tools/test/__snapshots__/index.test.tsx.snap @@ -18,7 +18,7 @@ exports[`AccountTools should render in test/sandbox mode onboarding 1`] = ` Account Tools

    - Payments and deposits are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning. + Payments and payouts are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning.

    - Deposits: + Payouts:

    - Payments and deposits are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning. + Payments and payouts are disabled until account setup is completed. If you are experiencing problems completing account setup, or need to change the email/country associated with your account, you can reset your account and start from the beginning.

    { { interpolateComponents( { /** translators: {{strong}}: placeholders are opening and closing strong tags. {{suspendLink}}: is a link element */ mixedString: __( - 'Your deposits are {{strong}}temporarily suspended{{/strong}}. {{suspendLink}}Learn more{{/suspendLink}}', + 'Your payouts are {{strong}}temporarily suspended{{/strong}}. {{suspendLink}}Learn more{{/suspendLink}}', 'woocommerce-payments' ), components: { @@ -37,7 +37,7 @@ export const SuspendedDepositNotice: React.FC = () => { suspendLink: ( ), @@ -54,7 +54,7 @@ export const DepositIncludesLoanPayoutNotice: React.FC = () => ( { interpolateComponents( { mixedString: __( - 'This deposit will include funds from your WooCommerce Capital loan. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + 'This payout will include funds from your WooCommerce Capital loan. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), components: { @@ -86,7 +86,7 @@ export const NewAccountWaitingPeriodNotice: React.FC = () => ( > { interpolateComponents( { mixedString: __( - 'Your first deposit is held for 7-14 days. {{whyLink}}Why?{{/whyLink}}', + 'Your first payout is held for 7-14 days. {{whyLink}}Why?{{/whyLink}}', 'woocommerce-payments' ), components: { @@ -96,7 +96,7 @@ export const NewAccountWaitingPeriodNotice: React.FC = () => ( ), }, @@ -114,7 +114,7 @@ export const DepositTransitDaysNotice: React.FC = () => ( className="wcpay-deposit-transit-days-notice" > { __( - 'It may take 1-3 business days for deposits to reach your bank account.', + 'It may take 1-3 business days for payouts to reach your bank account.', 'woocommerce-payments' ) } @@ -134,7 +134,7 @@ export const NegativeBalanceDepositsPausedNotice: React.FC = () => ( mixedString: sprintf( /* translators: %s: WooPayments */ __( - 'Deposits may be interrupted while your %s balance remains negative. {{whyLink}}Why?{{/whyLink}}', + 'Payouts may be interrupted while your %s balance remains negative. {{whyLink}}Why?{{/whyLink}}', 'woocommerce-payments' ), 'WooPayments' @@ -169,7 +169,7 @@ export const DepositMinimumBalanceNotice: React.FC< { mixedString: sprintf( /* translators: %s: a formatted currency amount, e.g. $5.00 USD */ __( - 'Deposits are paused while your available funds balance remains below %s. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + 'Payouts are paused while your available funds balance remains below %s. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), minimumDepositAmountFormatted @@ -181,7 +181,7 @@ export const DepositMinimumBalanceNotice: React.FC< { ), }, @@ -197,7 +197,7 @@ export const NoFundsAvailableForDepositNotice: React.FC = () => ( { interpolateComponents( { mixedString: __( - 'You have no funds available to deposit. {{whyLink}}Why?{{/whyLink}}', + 'You have no funds available. {{whyLink}}Why?{{/whyLink}}', 'woocommerce-payments' ), components: { @@ -207,7 +207,7 @@ export const NoFundsAvailableForDepositNotice: React.FC = () => ( ), }, @@ -240,7 +240,7 @@ export const DepositFailureNotice: React.FC< { > { interpolateComponents( { mixedString: __( - 'Deposits are currently paused because a recent deposit failed. Please {{updateLink}}update your bank account details{{/updateLink}}.', + 'Payouts are currently paused because a recent payout failed. Please {{updateLink}}update your bank account details{{/updateLink}}.', 'woocommerce-payments' ), components: { diff --git a/client/components/deposits-overview/deposit-schedule.tsx b/client/components/deposits-overview/deposit-schedule.tsx index f589bf1ad12..c463dfa335f 100644 --- a/client/components/deposits-overview/deposit-schedule.tsx +++ b/client/components/deposits-overview/deposit-schedule.tsx @@ -119,7 +119,7 @@ const DepositSchedule: React.FC< DepositScheduleProps > = ( { const nextDepositHelpContent = ( <> { __( - 'Deposits are initiated based on the following criteria:', + 'Payouts are initiated based on the following criteria:', 'woocommerce-payments' ) }
    @@ -173,7 +173,7 @@ exports[`Deposits Overview information Component Renders 1`] = ` data-wp-c16t="true" data-wp-component="FlexItem" > - It may take 1-3 business days for deposits to reach your bank account. + It may take 1-3 business days for payouts to reach your bank account.
    - Deposit history + Payout history
    January 2, 2020 @@ -310,7 +310,7 @@ exports[`Deposits Overview information Component Renders 1`] = ` January 3, 2020 @@ -358,7 +358,7 @@ exports[`Deposits Overview information Component Renders 1`] = ` January 4, 2020 @@ -392,13 +392,13 @@ exports[`Deposits Overview information Component Renders 1`] = ` class="components-button is-secondary" type="button" > - View full deposits history + View full payout history - Change deposit schedule + Change payout schedule
    @@ -455,14 +455,14 @@ exports[`Suspended Deposit Notice Renders Component Renders 1`] = ` data-wp-c16t="true" data-wp-component="FlexItem" > - Your deposits are + Your payouts are temporarily suspended . Learn more diff --git a/client/components/deposits-overview/test/index.tsx b/client/components/deposits-overview/test/index.tsx index d6d5be60bdf..edaa068a100 100644 --- a/client/components/deposits-overview/test/index.tsx +++ b/client/components/deposits-overview/test/index.tsx @@ -266,8 +266,8 @@ describe( 'Deposits Overview information', () => { const { container, getByText } = render( ); // Check that the button and link is rendered. - getByText( 'View full deposits history' ); - getByText( 'Change deposit schedule' ); + getByText( 'View full payout history' ); + getByText( 'Change payout schedule' ); expect( container ).toMatchSnapshot(); } ); @@ -304,7 +304,7 @@ describe( 'Deposits Overview information', () => { setSelectedCurrency: mockSetSelectedCurrency, } ); const { getByText, queryByText } = render( ); - getByText( /Your first deposit is held for/, { + getByText( /Your first payout is held for/, { ignore: '.a11y-speak-region', } ); expect( queryByText( 'Change deposit schedule' ) ).toBeFalsy(); @@ -327,10 +327,10 @@ describe( 'Deposits Overview information', () => { const { getByText, queryByText } = render( ); - getByText( /Your deposits are temporarily suspended/ ); + getByText( /Your payouts are temporarily suspended/ ); // Check that the buttons are rendered as expected. - getByText( 'View full deposits history' ); + getByText( 'View full payout history' ); // This one is not rendered when deposits are blocked. expect( queryByText( 'Change deposit schedule' ) ).toBeFalsy(); } ); @@ -395,7 +395,7 @@ describe( 'Deposits Overview information', () => { expect( queryByText( - 'deposit will include funds from your WooCommerce Capital loan', + 'payout will include funds from your WooCommerce Capital loan', { exact: false, ignore: '.a11y-speak-region', @@ -424,7 +424,7 @@ describe( 'Deposits Overview information', () => { } ); const { queryByText } = render( ); - expect( queryByText( /Your first deposit is held for/ ) ).toBeFalsy(); + expect( queryByText( /Your first payout is held for/ ) ).toBeFalsy(); } ); test( 'Confirm new account waiting period notice shows if within waiting period', () => { @@ -442,12 +442,12 @@ describe( 'Deposits Overview information', () => { } ); const { getByText, getByRole } = render( ); - getByText( /Your first deposit is held for/, { + getByText( /Your first payout is held for/, { ignore: '.a11y-speak-region', } ); expect( getByRole( 'link', { name: /Why\?/ } ) ).toHaveAttribute( 'href', - 'https://woocommerce.com/document/woopayments/deposits/deposit-schedule/#new-accounts' + 'https://woocommerce.com/document/woopayments/payouts/payout-schedule/#new-accounts' ); } ); } ); @@ -574,7 +574,7 @@ describe( 'DepositFailureNotice Renders', () => { const { queryByText } = render( ); expect( queryByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -613,7 +613,7 @@ describe( 'DepositFailureNotice Renders', () => { const { queryByText } = render( ); expect( queryByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -652,7 +652,7 @@ describe( 'DepositFailureNotice Renders', () => { const { queryByText } = render( ); expect( queryByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -677,7 +677,7 @@ describe( 'Paused Deposit notice Renders', () => { } ); const { getByText } = render( ); - getByText( /Deposits may be interrupted/, { + getByText( /Payouts may be interrupted/, { ignore: '.a11y-speak-region', } ); } ); @@ -691,7 +691,7 @@ describe( 'Paused Deposit notice Renders', () => { mockDepositOverviews( [ accountOverview ] ); const { queryByText } = render( ); - expect( queryByText( /Deposits may be interrupted/ ) ).toBeFalsy(); + expect( queryByText( /Payouts may be interrupted/ ) ).toBeFalsy(); } ); test( 'When available balance is negative', () => { const accountOverview = createMockNewAccountOverview( @@ -703,7 +703,7 @@ describe( 'Paused Deposit notice Renders', () => { mockDepositOverviews( [ accountOverview ] ); const { queryByText } = render( ); - expect( queryByText( /Deposits may be interrupted/ ) ).toBeFalsy(); + expect( queryByText( /Payouts may be interrupted/ ) ).toBeFalsy(); } ); } ); @@ -732,7 +732,7 @@ describe( 'Minimum Deposit Amount Notice', () => { const { getByText } = render( ); getByText( - /Deposits are paused while your available funds balance remains below €5.00/, + /Payouts are paused while your available funds balance remains below €5.00/, { ignore: '.a11y-speak-region', } @@ -752,7 +752,7 @@ describe( 'Minimum Deposit Amount Notice', () => { const { queryByText } = render( ); expect( queryByText( - /Deposits are paused while your available funds balance remains below/ + /Payouts are paused while your available funds balance remains below/ ) ).toBeFalsy(); } ); diff --git a/client/components/deposits-status/index.tsx b/client/components/deposits-status/index.tsx index b5c61b2cf1c..6e562296322 100644 --- a/client/components/deposits-status/index.tsx +++ b/client/components/deposits-status/index.tsx @@ -63,7 +63,7 @@ const DepositsStatusSuspended: React.FC< DepositsStatusProps > = ( props ) => { const { iconSize } = props; const learnMoreHref = - 'https://woocommerce.com/document/woopayments/deposits/why-deposits-suspended/'; + 'https://woocommerce.com/document/woopayments/payouts/why-payouts-suspended/'; const description = createInterpolateElement( /* translators: - suspended accounts FAQ URL */ diff --git a/client/components/deposits-status/test/__snapshots__/index.js.snap b/client/components/deposits-status/test/__snapshots__/index.js.snap index 9d92c7cd823..b0813fd50e2 100644 --- a/client/components/deposits-status/test/__snapshots__/index.js.snap +++ b/client/components/deposits-status/test/__snapshots__/index.js.snap @@ -20,7 +20,7 @@ exports[`DepositsStatus renders blocked status 1`] = ` Temporarily suspended ( @@ -51,7 +51,7 @@ exports[`DepositsStatus renders blocked status 2`] = ` Temporarily suspended ( @@ -174,7 +174,7 @@ exports[`DepositsStatus renders pending verification status 1`] = ` Temporarily suspended ( diff --git a/client/components/details-link/index.tsx b/client/components/details-link/index.tsx index 03226a5eb8e..0675a6f3e82 100644 --- a/client/components/details-link/index.tsx +++ b/client/components/details-link/index.tsx @@ -15,7 +15,7 @@ import { getAdminUrl } from 'wcpay/utils'; /** * The parent segment is the first part of the URL after the /payments/ path. */ -type ParentSegment = 'deposits' | 'transactions'; +type ParentSegment = 'payouts' | 'transactions'; export const getDetailsURL = ( /** diff --git a/client/components/details-link/test/__snapshots__/index.test.tsx.snap b/client/components/details-link/test/__snapshots__/index.test.tsx.snap index a2b861aebf1..6ea9065b623 100644 --- a/client/components/details-link/test/__snapshots__/index.test.tsx.snap +++ b/client/components/details-link/test/__snapshots__/index.test.tsx.snap @@ -6,7 +6,7 @@ exports[`Details link renders dispute details with ID 1`] = `
    { test( 'renders dispute details with ID', () => { const { container: link } = render( - + ); expect( link ).toMatchSnapshot(); } ); test( 'empty render with no ID', () => { const { container: link } = render( - + ); expect( link ).toMatchSnapshot(); } ); diff --git a/client/components/sandbox-mode-switch-to-live-notice/modal/index.tsx b/client/components/sandbox-mode-switch-to-live-notice/modal/index.tsx index eaa7dcef0aa..43278a178d0 100644 --- a/client/components/sandbox-mode-switch-to-live-notice/modal/index.tsx +++ b/client/components/sandbox-mode-switch-to-live-notice/modal/index.tsx @@ -82,7 +82,7 @@ const SetupLivePaymentsModal: React.FC< Props > = ( { ) } { __( - 'We will need your banking details in order to process any deposits to you.', + 'We will need your banking details in order to process any payouts to you.', 'woocommerce-payments' ) }
    diff --git a/client/components/test-mode-notice/index.tsx b/client/components/test-mode-notice/index.tsx index 6f13da9def2..7b8ac27073d 100644 --- a/client/components/test-mode-notice/index.tsx +++ b/client/components/test-mode-notice/index.tsx @@ -31,7 +31,7 @@ interface Props { const nounToUse = { documents: __( 'document', 'woocommerce-payments' ), - deposits: __( 'deposit', 'woocommerce-payments' ), + deposits: __( 'payout', 'woocommerce-payments' ), disputes: __( 'dispute', 'woocommerce-payments' ), loans: __( 'loan', 'woocommerce-payments' ), payments: __( 'order', 'woocommerce-payments' ), @@ -143,7 +143,9 @@ const getNoticeContent = ( 'Viewing test %1$s. To view live %1s, disable test mode in {{settingsLink}}%2s settings{{/settingsLink}}.', 'woocommerce-payments' ), - currentPage, + 'deposits' === currentPage + ? 'payouts' + : currentPage, 'WooPayments' ), components: { diff --git a/client/components/test-mode-notice/test/__snapshots__/index.tsx.snap b/client/components/test-mode-notice/test/__snapshots__/index.tsx.snap index d3f06d3f8bb..92877be2b29 100644 --- a/client/components/test-mode-notice/test/__snapshots__/index.tsx.snap +++ b/client/components/test-mode-notice/test/__snapshots__/index.tsx.snap @@ -22,7 +22,7 @@ exports[`Test mode notification Returns valid component for deposits page 1`] =
    - Viewing test deposits. To view live deposits, disable test mode in + Viewing test payouts. To view live payouts, disable test mode in diff --git a/client/connect-account-page/strings.tsx b/client/connect-account-page/strings.tsx index 808b9c13705..95c1be5a59d 100644 --- a/client/connect-account-page/strings.tsx +++ b/client/connect-account-page/strings.tsx @@ -37,7 +37,7 @@ export default { ), paymentMethods: { deposits: { - title: __( 'Deposits', 'woocommerce-payments' ), + title: __( 'Payouts', 'woocommerce-payments' ), value: __( 'Automatic - Daily', 'woocommerce-payments' ), }, capture: { @@ -58,7 +58,7 @@ export default { 'woocommerce-payments' ), usp3: __( - 'Earn recurring revenue and get deposits into your bank account.', + 'Earn recurring revenue and get payouts into your bank account.', 'woocommerce-payments' ), sandboxMode: { @@ -111,7 +111,7 @@ export default { 'woocommerce-payments' ), }, - button: __( 'enable deposits.', 'woocommerce-payments' ), + button: __( 'enable payouts.', 'woocommerce-payments' ), }, infoModal: { title: sprintf( @@ -252,7 +252,7 @@ export default { step2: { heading: __( 'Provide a few business details', 'woocommerce-payments' ), description: __( - 'Next we’ll ask you to verify your business and payment details to enable deposits.', + 'Next we’ll ask you to verify your business and payment details to enable payouts.', 'woocommerce-payments' ), }, diff --git a/client/connect-account-page/test/__snapshots__/index.test.tsx.snap b/client/connect-account-page/test/__snapshots__/index.test.tsx.snap index 256570bcc8c..3cfc2216716 100644 --- a/client/connect-account-page/test/__snapshots__/index.test.tsx.snap +++ b/client/connect-account-page/test/__snapshots__/index.test.tsx.snap @@ -50,7 +50,7 @@ exports[`ConnectAccountPage should render correctly 1`] = ` class="components-button is-link" type="button" > - enable deposits. + enable payouts.
    @@ -133,7 +133,7 @@ exports[`ConnectAccountPage should render correctly 1`] = ` >

    - Deposits + Payouts

    Automatic - Daily @@ -315,7 +315,7 @@ exports[`ConnectAccountPage should render correctly with WooPay eligible 1`] = ` class="components-button is-link" type="button" > - enable deposits. + enable payouts.
    @@ -398,7 +398,7 @@ exports[`ConnectAccountPage should render correctly with WooPay eligible 1`] = ` >

    - Deposits + Payouts

    Automatic - Daily @@ -542,7 +542,7 @@ exports[`ConnectAccountPage should render correctly with an incentive 1`] = ` class="components-button is-link" type="button" > - enable deposits. + enable payouts.
    @@ -625,7 +625,7 @@ exports[`ConnectAccountPage should render correctly with an incentive 1`] = ` >

    - Deposits + Payouts

    Automatic - Daily diff --git a/client/connect-account-page/test/info-notice-modal.test.tsx b/client/connect-account-page/test/info-notice-modal.test.tsx index 0b24620aa81..207935034dc 100644 --- a/client/connect-account-page/test/info-notice-modal.test.tsx +++ b/client/connect-account-page/test/info-notice-modal.test.tsx @@ -27,7 +27,7 @@ describe( 'Connect Account Page – Info Notice Modal', () => { render( ); const enableDeposits = screen.getByRole( 'button', { - name: /enable deposits./i, + name: /enable payouts./i, } ); userEvent.click( enableDeposits ); diff --git a/client/data/deposits/actions.js b/client/data/deposits/actions.js index 60b4479ab24..d4063e3e0a8 100644 --- a/client/data/deposits/actions.js +++ b/client/data/deposits/actions.js @@ -112,7 +112,7 @@ export function* submitInstantDeposit( currency ) { yield dispatch( 'core/notices' ).createSuccessNotice( sprintf( __( - 'Instant deposit for %s in transit.', + 'Instant payout for %s in transit.', 'woocommerce-payments' ), formatCurrency( deposit.amount ) @@ -123,7 +123,7 @@ export function* submitInstantDeposit( currency ) { label: __( 'View details', 'woocommerce-payments' ), url: getAdminUrl( { page: 'wc-admin', - path: '/payments/deposits/details', + path: '/payments/payouts/details', id: deposit.id, } ), }, @@ -132,7 +132,7 @@ export function* submitInstantDeposit( currency ) { ); } catch { yield dispatch( 'core/notices' ).createErrorNotice( - __( 'Error creating instant deposit.', 'woocommerce-payments' ) + __( 'Error creating instant payout.', 'woocommerce-payments' ) ); } finally { yield dispatch( STORE_NAME ).finishResolution( 'getInstantDeposit', [ diff --git a/client/data/deposits/resolvers.js b/client/data/deposits/resolvers.js index 52961f25570..a4a0ad14e26 100644 --- a/client/data/deposits/resolvers.js +++ b/client/data/deposits/resolvers.js @@ -45,7 +45,7 @@ export function* getDeposit( id ) { yield controls.dispatch( 'core/notices', 'createErrorNotice', - __( 'Error retrieving deposit.', 'woocommerce-payments' ) + __( 'Error retrieving payout.', 'woocommerce-payments' ) ); } } @@ -64,7 +64,7 @@ export function* getAllDepositsOverviews() { 'core/notices', 'createErrorNotice', __( - "Error retrieving all deposits' overviews.", + "Error retrieving all payouts' overviews.", 'woocommerce-payments' ) ); @@ -129,7 +129,7 @@ export function* getDeposits( query ) { yield controls.dispatch( 'core/notices', 'createErrorNotice', - __( 'Error retrieving deposits.', 'woocommerce-payments' ) + __( 'Error retrieving payouts.', 'woocommerce-payments' ) ); yield updateErrorForDepositQuery( query, null, e ); } diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index f2a835676d4..2176b079377 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -40,6 +40,7 @@ import { } from 'multi-currency/interface/functions'; import { depositStatusLabels } from '../strings'; import './style.scss'; +import { PayoutsRenameNotice } from '../rename-notice'; /** * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. @@ -121,9 +122,9 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { const isWithdrawal = deposit.type === 'withdrawal'; - let depositDateLabel = __( 'Deposit date', 'woocommerce-payments' ); + let depositDateLabel = __( 'Payout date', 'woocommerce-payments' ); if ( ! deposit.automatic ) { - depositDateLabel = __( 'Instant deposit date', 'woocommerce-payments' ); + depositDateLabel = __( 'Instant payout date', 'woocommerce-payments' ); } if ( isWithdrawal ) { depositDateLabel = __( 'Withdrawal date', 'woocommerce-payments' ); @@ -167,7 +168,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { 'Withdrawal overview', 'woocommerce-payments' ) - : __( 'Deposit overview', 'woocommerce-payments' ) + : __( 'Payout overview', 'woocommerce-payments' ) } > { () => [ @@ -181,7 +182,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { 'woocommerce-payments' ) : __( - 'Deposit amount', + 'Payout amount', 'woocommerce-payments' ) } @@ -214,7 +215,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { 'woocommerce-payments' ) : __( - 'Net deposit amount', + 'Net payout amount', 'woocommerce-payments' ) } @@ -246,6 +247,7 @@ export const DepositDetails: React.FC< DepositDetailsProps > = ( { return ( + { isLoading ? ( @@ -264,7 +266,7 @@ export const DepositDetails: React.FC< DepositDetailsProps > = ( { { __( - 'Deposit transactions', + 'Payout transactions', 'woocommerce-payments' ) } @@ -273,12 +275,12 @@ export const DepositDetails: React.FC< DepositDetailsProps > = ( { { interpolateComponents( { /* Translators: {{learnMoreLink}} is a link element (
    ). */ mixedString: __( - `We're unable to show transaction history on instant deposits. {{learnMoreLink}}Learn more{{/learnMoreLink}}`, + `We're unable to show transaction history on instant payouts. {{learnMoreLink}}Learn more{{/learnMoreLink}}`, 'woocommerce-payments' ), components: { learnMoreLink: ( - + ), }, } ) } diff --git a/client/deposits/details/test/__snapshots__/index.tsx.snap b/client/deposits/details/test/__snapshots__/index.tsx.snap index 53ef5a35d23..fecce68ae08 100644 --- a/client/deposits/details/test/__snapshots__/index.tsx.snap +++ b/client/deposits/details/test/__snapshots__/index.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Deposit overview renders automatic deposit correctly 1`] = ` +exports[`Deposit overview renders automatic payout correctly 1`] = `
    - Deposit date: Jan 2, 2020 + Payout date: Jan 2, 2020
    @@ -173,7 +173,7 @@ exports[`Deposit overview renders instant deposit correctly 1`] = `
    - Instant deposit date: Jan 2, 2020 + Instant payout date: Jan 2, 2020
    - Deposit amount + Payout amount
    - Net deposit amount + Net payout amount
    { }; } ); - test( 'renders automatic deposit correctly', () => { + test( 'renders automatic payout correctly', () => { const { container: overview, getByText } = render( ); - getByText( /Deposit date:/ ); + getByText( /Payout date:/ ); getByText( 'Completed (paid)' ); expect( overview ).toMatchSnapshot(); } ); diff --git a/client/deposits/filters/config.js b/client/deposits/filters/config.js index 90d850c7f2f..ee5b198e7d5 100644 --- a/client/deposits/filters/config.js +++ b/client/deposits/filters/config.js @@ -24,7 +24,7 @@ const depositStatusOptions = Object.entries( depositStatusLabels ) export const filters = [ { - label: __( 'Deposit currency', 'woocommerce-payments' ), + label: __( 'Payout currency', 'woocommerce-payments' ), param: 'store_currency_is', staticParams: [ 'paged', @@ -62,7 +62,7 @@ export const filters = [ showFilters: () => true, filters: [ { - label: __( 'All deposits', 'woocommerce-payments' ), + label: __( 'All payouts', 'woocommerce-payments' ), value: 'all', }, { @@ -83,21 +83,18 @@ export const advancedFilters = { /** translators: A sentence describing filters for deposits. See screen shot for context: https://d.pr/i/NcGpwL */ title: wooCommerceVersion < 7.8 - ? __( - 'Deposits match {{select /}} filters', - 'woocommerce-payments' - ) - : __( 'Deposits match filters', 'woocommerce-payments' ), filters: { date: { labels: { add: __( 'Date', 'woocommerce-payments' ), remove: __( - 'Remove deposit date filter', + 'Remove payout date filter', 'woocommerce-payments' ), rule: __( - 'Select a deposit date filter match', + 'Select a payout date filter match', 'woocommerce-payments' ), /* translators: A sentence describing a deposit date filter. See screen shot for context: https://d.pr/i/NcGpwL */ @@ -111,7 +108,7 @@ export const advancedFilters = { 'Date ', 'woocommerce-payments' ), - filter: __( 'Select a deposit date', 'woocommerce-payments' ), + filter: __( 'Select a payout date', 'woocommerce-payments' ), }, rules: [ { @@ -135,11 +132,11 @@ export const advancedFilters = { labels: { add: __( 'Status', 'woocommerce-payments' ), remove: __( - 'Remove deposit status filter', + 'Remove payout status filter', 'woocommerce-payments' ), rule: __( - 'Select a deposit status filter match', + 'Select a payout status filter match', 'woocommerce-payments' ), /* translators: A sentence describing a deposit status filter. See screen shot for context: https://d.pr/i/NcGpwL */ @@ -153,20 +150,20 @@ export const advancedFilters = { 'Status ', 'woocommerce-payments' ), - filter: __( 'Select a deposit status', 'woocommerce-payments' ), + filter: __( 'Select a payout status', 'woocommerce-payments' ), }, rules: [ { value: 'is', /* translators: Sentence fragment, logical, "Is" refers to searching for deposits matching a chosen deposit status. Screenshot for context: https://d.pr/i/NcGpwL */ - label: _x( 'Is', 'deposit status', 'woocommerce-payments' ), + label: _x( 'Is', 'payout status', 'woocommerce-payments' ), }, { value: 'is_not', /* translators: Sentence fragment, logical, "Is not" refers to searching for deposits that don\'t match a chosen deposit status. Screenshot for context: https://d.pr/i/NcGpwL */ label: _x( 'Is not', - 'deposit status', + 'payout status', 'woocommerce-payments' ), }, diff --git a/client/deposits/filters/index.js b/client/deposits/filters/index.js index 0d6d57afecc..1393109a06e 100644 --- a/client/deposits/filters/index.js +++ b/client/deposits/filters/index.js @@ -38,7 +38,7 @@ export const DepositsFilters = ( props ) => { filters={ populateDepositCurrencies( filters ) } advancedFilters={ advancedFilters } showDatePicker={ false } - path="/payments/deposits" + path="/payments/payouts" query={ getQuery() } />
    diff --git a/client/deposits/filters/test/index.js b/client/deposits/filters/test/index.js index 04071451934..fc00ab0bcde 100644 --- a/client/deposits/filters/test/index.js +++ b/client/deposits/filters/test/index.js @@ -34,7 +34,7 @@ describe( 'Deposits filters', () => { const { rerender } = render( ); // select advanced filter view - user.click( screen.getByRole( 'button', { name: /All deposits/i } ) ); + user.click( screen.getByRole( 'button', { name: /All payouts/i } ) ); user.click( screen.getByRole( 'button', { name: /Advanced filters/i } ) ); @@ -54,7 +54,7 @@ describe( 'Deposits filters', () => { beforeEach( () => { addAdvancedFilter( 'Date' ); ruleSelector = screen.getByRole( 'combobox', { - name: /deposit date filter/i, + name: /payout date filter/i, } ); } ); @@ -105,13 +105,13 @@ describe( 'Deposits filters', () => { beforeEach( () => { addAdvancedFilter( 'Status' ); ruleSelector = screen.getByRole( 'combobox', { - name: /deposit status filter/i, + name: /payout status filter/i, } ); } ); test( 'should render all status', () => { const statusSelect = screen.getByRole( 'combobox', { - name: /deposit status$/i, + name: /payout status$/i, } ); expect( statusSelect.options ).toMatchSnapshot(); } ); @@ -121,7 +121,7 @@ describe( 'Deposits filters', () => { // need to include $ in name, otherwise "Select a deposit status filter" is also matched. user.selectOptions( - screen.getByRole( 'combobox', { name: /deposit status$/i } ), + screen.getByRole( 'combobox', { name: /payout status$/i } ), 'paid' ); user.click( screen.getByRole( 'link', { name: /Filter/ } ) ); @@ -134,7 +134,7 @@ describe( 'Deposits filters', () => { // need to include $ in name, otherwise "Select a deposit status filter" is also matched. user.selectOptions( - screen.getByRole( 'combobox', { name: /deposit status$/i } ), + screen.getByRole( 'combobox', { name: /payout status$/i } ), 'pending' ); user.click( screen.getByRole( 'link', { name: /Filter/ } ) ); @@ -147,7 +147,7 @@ describe( 'Deposits filters', () => { // need to include $ in name, otherwise "Select a deposit status filter" is also matched. user.selectOptions( - screen.getByRole( 'combobox', { name: /deposit status$/i } ), + screen.getByRole( 'combobox', { name: /payout status$/i } ), 'in_transit' ); user.click( screen.getByRole( 'link', { name: /Filter/ } ) ); @@ -160,7 +160,7 @@ describe( 'Deposits filters', () => { // need to include $ in name, otherwise "Select a deposit status filter" is also matched. user.selectOptions( - screen.getByRole( 'combobox', { name: /deposit status$/i } ), + screen.getByRole( 'combobox', { name: /payout status$/i } ), 'canceled' ); user.click( screen.getByRole( 'link', { name: /Filter/ } ) ); @@ -173,7 +173,7 @@ describe( 'Deposits filters', () => { // need to include $ in name, otherwise "Select a deposit status filter" is also matched. user.selectOptions( - screen.getByRole( 'combobox', { name: /deposit status$/i } ), + screen.getByRole( 'combobox', { name: /payout status$/i } ), 'failed' ); user.click( screen.getByRole( 'link', { name: /Filter/ } ) ); diff --git a/client/deposits/index.tsx b/client/deposits/index.tsx index 938aa8df577..d799ff3d385 100644 --- a/client/deposits/index.tsx +++ b/client/deposits/index.tsx @@ -17,6 +17,7 @@ import { __ } from '@wordpress/i18n'; import { TestModeNotice } from 'components/test-mode-notice'; import BannerNotice from 'components/banner-notice'; import DepositSchedule from 'components/deposits-overview/deposit-schedule'; +import { PayoutsRenameNotice } from './rename-notice'; import { useAllDepositsOverviews } from 'data'; import { useSettings } from 'wcpay/data'; import DepositsList from './list'; @@ -118,7 +119,7 @@ const DepositFailureNotice: React.FC = () => { > { interpolateComponents( { mixedString: __( - 'Deposits are currently paused because a recent deposit failed. Please {{updateLink}}update your bank account details{{/updateLink}}.', + 'Payouts are currently paused because a recent payout failed. Please {{updateLink}}update your bank account details{{/updateLink}}.', 'woocommerce-payments' ), components: { @@ -149,6 +150,7 @@ const DepositsPage: React.FC = () => { return ( + diff --git a/client/deposits/instant-deposits/index.tsx b/client/deposits/instant-payouts/index.tsx similarity index 84% rename from client/deposits/instant-deposits/index.tsx rename to client/deposits/instant-payouts/index.tsx index 64c49bc069f..81ed4b54bbb 100644 --- a/client/deposits/instant-deposits/index.tsx +++ b/client/deposits/instant-payouts/index.tsx @@ -13,7 +13,7 @@ import { useState } from '@wordpress/element'; */ import './style.scss'; import { formatCurrency } from 'multi-currency/interface/functions'; -import InstantDepositModal from './modal'; +import InstantPayoutModal from './modal'; import { useInstantDeposit } from 'wcpay/data'; import type * as AccountOverview from 'wcpay/types/account-overview'; @@ -26,10 +26,10 @@ const isButtonDisabled = ( instantBalance: AccountOverview.InstantBalance ) => { return buttonDisabled; }; -interface InstantDepositButtonProps { +interface InstantPayoutButtonProps { instantBalance: AccountOverview.InstantBalance; } -const InstantDepositButton: React.FC< InstantDepositButtonProps > = ( { +const InstantPayoutButton: React.FC< InstantPayoutButtonProps > = ( { instantBalance, } ) => { const [ isModalOpen, setModalOpen ] = useState( false ); @@ -52,7 +52,7 @@ const InstantDepositButton: React.FC< InstantDepositButtonProps > = ( { > { sprintf( __( - /* translators: %s: Available instant deposit amount */ + /* translators: %s: Available instant payout amount */ 'Get %s now', 'woocommerce-payments' ), @@ -63,7 +63,7 @@ const InstantDepositButton: React.FC< InstantDepositButtonProps > = ( { ) } { ( isModalOpen || inProgress ) && ( - = ( { ); }; -export default InstantDepositButton; +export default InstantPayoutButton; diff --git a/client/deposits/instant-deposits/modal.tsx b/client/deposits/instant-payouts/modal.tsx similarity index 68% rename from client/deposits/instant-deposits/modal.tsx rename to client/deposits/instant-payouts/modal.tsx index e9d0f530aeb..ac8fdab1f0d 100644 --- a/client/deposits/instant-deposits/modal.tsx +++ b/client/deposits/instant-payouts/modal.tsx @@ -18,27 +18,27 @@ import { import type * as AccountOverview from 'wcpay/types/account-overview'; import './style.scss'; -interface InstantDepositModalProps { +interface InstantPayoutModalProps { instantBalance: AccountOverview.InstantBalance; onClose: () => void; onSubmit: () => void; inProgress: boolean; } -const InstantDepositModal: React.FC< InstantDepositModalProps > = ( { +const InstantPayoutModal: React.FC< InstantPayoutModalProps > = ( { instantBalance: { amount, fee, net, fee_percentage: percentage }, onClose, onSubmit, inProgress, } ) => { const learnMoreHref = - 'https://woocommerce.com/document/woopayments/deposits/instant-deposits/'; + 'https://woocommerce.com/document/woopayments/payouts/instant-payouts/'; const feePercentage = `${ percentage }%`; const description = createInterpolateElement( /* translators: %s: amount representing the fee percentage,
    : instant payout doc URL */ sprintf( __( - 'Need cash in a hurry? Instant deposits are available within 30 minutes for a nominal %s service fee. Learn more', + 'Need cash in a hurry? Instant payouts are available within 30 minutes for a nominal %s service fee. Learn more', 'woocommerce-payments' ), feePercentage @@ -57,20 +57,20 @@ const InstantDepositModal: React.FC< InstantDepositModalProps > = ( { return (

    { description }

      -
    • +
    • { __( - 'Balance available for instant deposit: ', + 'Balance available for instant payout: ', 'woocommerce-payments' ) } { formatCurrency( amount ) }
    • -
    • +
    • { sprintf( /* translators: %s - amount representing the fee percentage */ __( '%s service fee: ', 'woocommerce-payments' ), @@ -78,13 +78,13 @@ const InstantDepositModal: React.FC< InstantDepositModalProps > = ( { ) } -{ formatCurrency( fee ) }
    • -
    • - { __( 'Net deposit amount: ', 'woocommerce-payments' ) } +
    • + { __( 'Net payout amount: ', 'woocommerce-payments' ) } { formatExplicitCurrency( net ) }
    -
    +
    @@ -95,8 +95,8 @@ const InstantDepositModal: React.FC< InstantDepositModalProps > = ( { disabled={ inProgress } > { sprintf( - /* translators: %s: Monetary amount to deposit */ - __( 'Deposit %s now', 'woocommerce-payments' ), + /* translators: %s: Monetary amount to pay out */ + __( 'Pay out %s now', 'woocommerce-payments' ), formatExplicitCurrency( net ) ) } @@ -105,4 +105,4 @@ const InstantDepositModal: React.FC< InstantDepositModalProps > = ( { ); }; -export default InstantDepositModal; +export default InstantPayoutModal; diff --git a/client/deposits/instant-deposits/style.scss b/client/deposits/instant-payouts/style.scss similarity index 74% rename from client/deposits/instant-deposits/style.scss rename to client/deposits/instant-payouts/style.scss index 8232fd2508b..b3c16187b5f 100644 --- a/client/deposits/instant-deposits/style.scss +++ b/client/deposits/instant-payouts/style.scss @@ -1,4 +1,4 @@ -div.wcpay-instant-deposits-modal { +div.wcpay-instant-payout-modal { max-width: 500px; p { @@ -20,17 +20,17 @@ div.wcpay-instant-deposits-modal { } } - .wcpay-instant-deposits-modal__balance { + .wcpay-instant-payout-modal__balance { padding-bottom: 0; } - .wcpay-instant-deposits-modal__fee { + .wcpay-instant-payout-modal__fee { span { color: $studio-red-50; } } - .wcpay-instant-deposits-modal__net { + .wcpay-instant-payout-modal__net { border-top: 1px solid $studio-gray-5; span { diff --git a/client/deposits/instant-deposits/test/__snapshots__/index.tsx.snap b/client/deposits/instant-payouts/test/__snapshots__/index.tsx.snap similarity index 70% rename from client/deposits/instant-deposits/test/__snapshots__/index.tsx.snap rename to client/deposits/instant-payouts/test/__snapshots__/index.tsx.snap index 2eec0aa2041..aca4e115afd 100644 --- a/client/deposits/instant-deposits/test/__snapshots__/index.tsx.snap +++ b/client/deposits/instant-payouts/test/__snapshots__/index.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Instant deposit button and modal button renders correctly with balance 1`] = ` +exports[`Instant payout button and modal button renders correctly with balance 1`] = `

    - Need cash in a hurry? Instant deposits are available within 30 minutes for a nominal 1.5% service fee. + Need cash in a hurry? Instant payouts are available within 30 minutes for a nominal 1.5% service fee. @@ -66,15 +66,15 @@ exports[`Instant deposit button and modal modal renders correctly 1`] = `

    • - Balance available for instant deposit: + Balance available for instant payout: $123.45
    • 1.5% service fee: @@ -83,16 +83,16 @@ exports[`Instant deposit button and modal modal renders correctly 1`] = `
    • - Net deposit amount: + Net payout amount: $122.22
    diff --git a/client/deposits/instant-deposits/test/index.tsx b/client/deposits/instant-payouts/test/index.tsx similarity index 90% rename from client/deposits/instant-deposits/test/index.tsx rename to client/deposits/instant-payouts/test/index.tsx index d8b2d8ec8e6..4af2ee92233 100644 --- a/client/deposits/instant-deposits/test/index.tsx +++ b/client/deposits/instant-payouts/test/index.tsx @@ -7,7 +7,7 @@ import { fireEvent, render, screen } from '@testing-library/react'; /** * Internal dependencies */ -import InstantDepositButton from '../'; +import InstantDepositButton from '..'; import { useInstantDeposit } from 'wcpay/data'; import type * as AccountOverview from 'wcpay/types/account-overview'; @@ -41,7 +41,7 @@ declare const global: { }; }; -describe( 'Instant deposit button and modal', () => { +describe( 'Instant payout button and modal', () => { beforeEach( () => { jest.clearAllMocks(); global.wcpaySettings = { @@ -74,7 +74,7 @@ describe( 'Instant deposit button and modal', () => { ); expect( - screen.queryByRole( 'dialog', { name: /instant deposit/i } ) + screen.queryByRole( 'dialog', { name: /instant payout/i } ) ).not.toBeInTheDocument(); fireEvent.click( screen.getByRole( 'button', { @@ -82,7 +82,7 @@ describe( 'Instant deposit button and modal', () => { } ) ); const modal = screen.queryByRole( 'dialog', { - name: /instant deposit/i, + name: /instant payout/i, } ); expect( modal ).toBeInTheDocument(); expect( modal ).toMatchSnapshot(); diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 7e640e16338..c991e7e7ec3 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -120,19 +120,19 @@ export const DepositsList = (): JSX.Element => { const rows = deposits.map( ( deposit ) => { const clickable = ( children: React.ReactNode ): JSX.Element => ( recordEvent( 'wcpay_deposits_row_click' ) } > { children } ); const detailsLink = ( - + ); const dateDisplay = ( recordEvent( 'wcpay_deposits_row_click' ) } > { dateI18n( @@ -186,8 +186,8 @@ export const DepositsList = (): JSX.Element => { summary = [ { label: _n( - 'deposit', - 'deposits', + 'payout', + 'payouts', depositsSummary.count, 'woocommerce-payments' ), @@ -212,7 +212,7 @@ export const DepositsList = (): JSX.Element => { depositsSummary.store_currencies || ( isCurrencyFiltered ? [ getQuery().store_currency_is ] : [] ); - const title = __( 'Deposits', 'woocommerce-payments' ); + const title = __( 'Payouts', 'woocommerce-payments' ); const downloadable = !! rows.length; @@ -317,7 +317,7 @@ export const DepositsList = (): JSX.Element => { const csvColumns = [ { ...columns[ 0 ], - label: __( 'Deposit Id', 'woocommerce-payments' ), + label: __( 'Payout Id', 'woocommerce-payments' ), }, ...columns.slice( 1 ), ]; @@ -365,7 +365,7 @@ export const DepositsList = (): JSX.Element => { - Deposit currency + Payout currency :
    - All deposits + All payouts
    @@ -96,7 +96,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` data-wp-c16t="true" data-wp-component="Text" > - Deposit history + Payout history
    - Deposit history + Payout history @@ -321,7 +321,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` > Jan 2, 2020 @@ -354,10 +354,10 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` - Deposit + Payout $20.00 @@ -378,7 +378,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` MOCK BANK •••• 1234 (USD) @@ -408,7 +408,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` > Jan 3, 2020 @@ -441,7 +441,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` Withdrawal @@ -453,7 +453,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` $30.00 @@ -465,7 +465,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` MOCK BANK •••• 1234 (USD) @@ -495,7 +495,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` > Jan 4, 2020 @@ -528,7 +528,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` Withdrawal @@ -540,7 +540,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` $40.00 @@ -552,7 +552,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` MOCK BANK •••• 1234 (USD) @@ -599,7 +599,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` - deposit + payout
  • - Deposit currency + Payout currency :
    - All deposits + All payouts
    @@ -732,7 +732,7 @@ exports[`Deposits list renders correctly with multiple currencies 1`] = ` data-wp-c16t="true" data-wp-component="Text" > - Deposit history + Payout history
  • diff --git a/client/deposits/list/test/index.tsx b/client/deposits/list/test/index.tsx index 322ca965f23..d70561fb6d4 100644 --- a/client/deposits/list/test/index.tsx +++ b/client/deposits/list/test/index.tsx @@ -281,7 +281,7 @@ describe( 'Deposits list', () => { getByRole( 'button', { name: 'Download' } ).click(); const expected = [ - '"Deposit Id"', + '"Payout Id"', 'Date', 'Type', 'Amount', diff --git a/client/deposits/rename-notice/header-image.svg b/client/deposits/rename-notice/header-image.svg new file mode 100644 index 00000000000..563468de2b4 --- /dev/null +++ b/client/deposits/rename-notice/header-image.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/deposits/rename-notice/index.tsx b/client/deposits/rename-notice/index.tsx new file mode 100644 index 00000000000..61ff61b142e --- /dev/null +++ b/client/deposits/rename-notice/index.tsx @@ -0,0 +1,88 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import React, { useState, useEffect } from 'react'; +import { TourKit } from '@woocommerce/components'; +import { useDispatch } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import './style.scss'; + +export const PayoutsRenameNotice = () => { + const isPayoutsRenameNoticeDismissed = + wcpaySettings.isPayoutsRenameNoticeDismissed; + const { updateOptions } = useDispatch( 'wc/admin/options' ); + const [ showTour, setShowTour ] = useState( false ); + + const onClose = () => { + updateOptions( { + wcpay_payouts_rename_notice_dismissed: true, + } ); + setShowTour( false ); + wcpaySettings.isPayoutsRenameNoticeDismissed = true; + }; + + useEffect( () => { + if ( ! isPayoutsRenameNoticeDismissed ) { + setShowTour( true ); + } + }, [ isPayoutsRenameNoticeDismissed ] ); + + if ( ! showTour ) return null; + + return ( + Learn More.", + 'woocommerce-payments' + ), + { + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), + } + ), + }, + }, + }, + ], + closeHandler: onClose, + } } + > + ); +}; diff --git a/client/deposits/rename-notice/style.scss b/client/deposits/rename-notice/style.scss new file mode 100644 index 00000000000..3a2c51b7334 --- /dev/null +++ b/client/deposits/rename-notice/style.scss @@ -0,0 +1,77 @@ +.wc-admin-payments-overview-payouts-rename-tour { + color: #1e1e1e; + + .woocommerce-tour-kit-step { + border-radius: 8px; + border: 1px solid #dcdcdc; + + /* Shadow / Popover */ + box-shadow: 0 2px 6px 0 rgba( 0, 0, 0, 0.05 ); + width: 257px; + padding-bottom: 0; + } + + .components-card__footer { + display: none; + } + + .tour-kit-frame__container { + box-shadow: none; + background: none; + } + + .components-elevation { + display: none; + } + + .woocommerce-tour-kit-step__body { + background-image: url( './header-image.svg' ); + background-repeat: no-repeat; + background-position-x: center; + background-position-y: 5px; + padding-bottom: 24px; + } + .woocommerce-tour-kit-step-navigation { + display: none; + } + h2 { + padding-top: 100px; + font-size: 14px; + line-height: 24px; + } + p.woocommerce-tour-kit-step__description { + font-size: 13px; + line-height: 18px; + margin-top: 8px; + } + .tour-kit-frame__arrow::before { + box-shadow: none !important; + } + + .tour-kit-frame__container[data-popper-placement^='bottom'] { + & > .tour-kit-frame__arrow { + background: #fff; + &::before { + border-top: 1px solid var( --gutenberg-gray-300, #ddd ); + border-left: 1px solid var( --gutenberg-gray-300, #ddd ); + } + } + } + + .tour-kit-frame__container[data-popper-placement^='right'] { + & > .tour-kit-frame__arrow { + background: #fff; + &::before { + border-bottom: 1px solid var( --gutenberg-gray-300, #ddd ); + border-left: 1px solid var( --gutenberg-gray-300, #ddd ); + } + } + } + + a { + display: block; + text-decoration: none; + margin-top: 12px; + padding-block: 11px; + } +} diff --git a/client/deposits/rename-notice/test/__snapshots__/index.tsx.snap b/client/deposits/rename-notice/test/__snapshots__/index.tsx.snap new file mode 100644 index 00000000000..8d2131ec55c --- /dev/null +++ b/client/deposits/rename-notice/test/__snapshots__/index.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PayoutsRenameNotice should render notice if isPayoutsRenameNoticeDismissed is false 1`] = ` +
    +
    + Tour Component +
    +
    +`; diff --git a/client/deposits/rename-notice/test/index.tsx b/client/deposits/rename-notice/test/index.tsx new file mode 100644 index 00000000000..9f9f2d0c7a8 --- /dev/null +++ b/client/deposits/rename-notice/test/index.tsx @@ -0,0 +1,49 @@ +/** @format */ +/** + * External dependencies + */ +import React from 'react'; +import { render } from '@testing-library/react'; + +/** + * Internal dependencies + */ +import { PayoutsRenameNotice } from '..'; + +jest.mock( '@wordpress/api-fetch', () => jest.fn() ); + +jest.mock( '@wordpress/data', () => ( { + useDispatch: jest.fn().mockReturnValue( { updateOptions: jest.fn() } ), +} ) ); + +jest.mock( '@woocommerce/components', () => ( { + TourKit: () =>
    Tour Component
    , +} ) ); + +declare const global: { + wcpaySettings: { + isPayoutsRenameNoticeDismissed: boolean; + }; +}; + +describe( 'PayoutsRenameNotice', () => { + afterEach( () => { + jest.clearAllMocks(); + } ); + + test( 'should render null if isPayoutsRenameNoticeDismissed is true', () => { + global.wcpaySettings = { + isPayoutsRenameNoticeDismissed: true, + }; + const { container } = render( ); + expect( container.firstChild ).toBeNull(); + } ); + + test( 'should render notice if isPayoutsRenameNoticeDismissed is false', () => { + global.wcpaySettings = { + isPayoutsRenameNoticeDismissed: false, + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); +} ); diff --git a/client/deposits/strings.ts b/client/deposits/strings.ts index 03a8c55198d..7dbf7df2dad 100644 --- a/client/deposits/strings.ts +++ b/client/deposits/strings.ts @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import type { DepositStatus } from 'wcpay/types/deposits'; export const displayType = { - deposit: __( 'Deposit', 'woocommerce-payments' ), + deposit: __( 'Payout', 'woocommerce-payments' ), withdrawal: __( 'Withdrawal', 'woocommerce-payments' ), }; diff --git a/client/globals.d.ts b/client/globals.d.ts index 1d208d50f74..0d10d7de86b 100644 --- a/client/globals.d.ts +++ b/client/globals.d.ts @@ -89,6 +89,7 @@ declare global { fraudProtection: { isWelcomeTourDismissed?: boolean; }; + isPayoutsRenameNoticeDismissed: boolean; progressiveOnboarding?: { isEnabled: boolean; isComplete: boolean; diff --git a/client/index.js b/client/index.js index 11083acffac..c42dfd42788 100644 --- a/client/index.js +++ b/client/index.js @@ -97,9 +97,9 @@ addFilter( pages.push( { container: DepositsPage, - path: '/payments/deposits', + path: '/payments/payouts', wpOpenMenu: menuID, - breadcrumbs: [ rootLink, __( 'Deposits', 'woocommerce-payments' ) ], + breadcrumbs: [ rootLink, __( 'Payouts', 'woocommerce-payments' ) ], navArgs: { id: 'wc-payments-deposits', }, @@ -107,19 +107,19 @@ addFilter( } ); pages.push( { container: DepositDetailsPage, - path: '/payments/deposits/details', + path: '/payments/payouts/details', wpOpenMenu: menuID, breadcrumbs: [ rootLink, [ - '/payments/deposits', - __( 'Deposits', 'woocommerce-payments' ), + '/payments/payouts', + __( 'Payouts', 'woocommerce-payments' ), ], - __( 'Deposit details', 'woocommerce-payments' ), + __( 'Payout details', 'woocommerce-payments' ), ], navArgs: { id: 'wc-payments-deposit-details', - parentPath: '/payments/deposits', + parentPath: '/payments/payouts', }, capability: 'manage_woocommerce', } ); diff --git a/client/overview/index.js b/client/overview/index.js index f6d5afd4a72..edb215993c7 100644 --- a/client/overview/index.js +++ b/client/overview/index.js @@ -32,6 +32,7 @@ import { useDisputes, useGetSettings, useSettings } from 'data'; import SandboxModeSwitchToLiveNotice from 'wcpay/components/sandbox-mode-switch-to-live-notice'; import './style.scss'; import BannerNotice from 'wcpay/components/banner-notice'; +import { PayoutsRenameNotice } from 'wcpay/deposits/rename-notice'; const OverviewPageError = () => { const queryParams = getQuery(); @@ -194,6 +195,7 @@ const OverviewPage = () => { { showConnectionSuccess && } { ! accountRejected && ! accountUnderReview && ( + { showTaskList && ( diff --git a/client/overview/modal/progressive-onboarding-eligibility/index.tsx b/client/overview/modal/progressive-onboarding-eligibility/index.tsx index 564e8494a70..d4b3f79021f 100644 --- a/client/overview/modal/progressive-onboarding-eligibility/index.tsx +++ b/client/overview/modal/progressive-onboarding-eligibility/index.tsx @@ -82,7 +82,7 @@ const ProgressiveOnboardingEligibilityModal: React.FC = () => {

    { __( - 'Start selling now and fast track the setup process, or continue the process to set up deposits with WooPayments.', + 'Start selling now and fast track the setup process, or continue the process to set up payouts with WooPayments.', 'woocommerce-payments' ) }

    @@ -120,14 +120,14 @@ const ProgressiveOnboardingEligibilityModal: React.FC = () => { { __( 'Flexible process', 'woocommerce-payments' ) } { __( - 'You have a $5,000 balance limit or 30 days from your first transaction to verify and set up deposits in your account.', + 'You have a $5,000 balance limit or 30 days from your first transaction to verify and set up payouts in your account.', 'woocommerce-payments' ) }
    @@ -1441,7 +1441,7 @@ exports[`PaymentDetailsTimeline renders subscription fee correctly 1`] = ` - $0.66 will be added to a future deposit. + $0.66 will be added to a future payout.
    - Net deposit: $0.66 + Net payout: $0.66
    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 3d0a3862088..e9c43d19b15 100644 --- a/client/payment-details/timeline/test/__snapshots__/map-events.js.snap +++ b/client/payment-details/timeline/test/__snapshots__/map-events.js.snap @@ -14,9 +14,9 @@ exports[`mapTimelineEvents Multi-Currency events formats captured events with fe "headline": $20.97 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -27,7 +27,7 @@ exports[`mapTimelineEvents Multi-Currency events formats captured events with fe "1.00 EUR → 1.19944 USD: $21.59 USD", "Fee (2.9% + $0.30): -$0.62", undefined, - "Net deposit: $20.97 USD", + "Net payout: $20.97 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of €18.00 EUR was successfully charged.", @@ -52,9 +52,9 @@ exports[`mapTimelineEvents Multi-Currency events formats captured events without "headline": $20.97 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -65,7 +65,7 @@ exports[`mapTimelineEvents Multi-Currency events formats captured events without "1.00 EUR → 1.19944 USD: $21.59 USD", "Fee: €0.52", undefined, - "Net deposit: $20.97 USD", + "Net payout: $20.97 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of €18.00 EUR was successfully charged.", @@ -91,7 +91,7 @@ exports[`mapTimelineEvents Multi-Currency events formats dispute_needs_response "Fee: $15.00", ], "date": 2020-04-02T02:06:14.000Z, - "headline": "$36.60 USD will be deducted from a future deposit.", + "headline": "$36.60 USD will be deducted from a future payout.", "icon": , }, { @@ -122,9 +122,9 @@ exports[`mapTimelineEvents Multi-Currency events formats dispute_won events 1`] "headline": $44.99 USD was added to your - Apr 5, 2020 deposit + Apr 5, 2020 payout . , @@ -152,7 +152,7 @@ exports[`mapTimelineEvents Multi-Currency events formats full_refund events 1`] { "body": [], "date": 2020-04-04T13:51:06.000Z, - "headline": "21,64 $ USD will be deducted from a future deposit.", + "headline": "21,64 $ USD will be deducted from a future payout.", "icon": , }, { @@ -180,7 +180,7 @@ exports[`mapTimelineEvents Multi-Currency events formats partial_refund events 1 { "body": [], "date": 2020-04-03T18:58:01.000Z, - "headline": "6,00 $ USD will be deducted from a future deposit.", + "headline": "6,00 $ USD will be deducted from a future payout.", "icon": , }, { @@ -374,9 +374,9 @@ exports[`mapTimelineEvents single currency events formats captured events with f "headline": $59.50 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -414,7 +414,7 @@ exports[`mapTimelineEvents single currency events formats captured events with f , - "Net deposit: $59.50 USD", + "Net payout: $59.50 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of $63.00 USD was successfully charged.", @@ -439,9 +439,9 @@ exports[`mapTimelineEvents single currency events formats captured events with j "headline": $59.50 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -452,7 +452,7 @@ exports[`mapTimelineEvents single currency events formats captured events with j undefined, "Base fee (1.95% + $0.15): -$3.50", undefined, - "Net deposit: $59.50 USD", + "Net payout: $59.50 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of $63.00 USD was successfully charged.", @@ -477,9 +477,9 @@ exports[`mapTimelineEvents single currency events formats captured events withou "headline": $59.50 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -490,7 +490,7 @@ exports[`mapTimelineEvents single currency events formats captured events withou undefined, "Fee: $3.50", undefined, - "Net deposit: $59.50 USD", + "Net payout: $59.50 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of $63.00 USD was successfully charged.", @@ -535,7 +535,7 @@ exports[`mapTimelineEvents single currency events formats dispute_needs_response "Fee: $15.00", ], "date": 2020-04-02T02:06:14.000Z, - "headline": "$110.00 USD will be deducted from a future deposit.", + "headline": "$110.00 USD will be deducted from a future payout.", "icon": , }, { @@ -566,9 +566,9 @@ exports[`mapTimelineEvents single currency events formats dispute_won events 1`] "headline": $115.00 USD was added to your - Apr 5, 2020 deposit + Apr 5, 2020 payout . , @@ -599,7 +599,7 @@ exports[`mapTimelineEvents single currency events formats financing paydown even , ], "date": 2022-02-01T12:04:04.000Z, - "headline": "$110.00 will be subtracted from a future deposit.", + "headline": "$110.00 will be subtracted from a future payout.", "icon": , }, ] @@ -616,7 +616,7 @@ exports[`mapTimelineEvents single currency events formats full_refund events 1`] { "body": [], "date": 2020-04-04T13:51:06.000Z, - "headline": "$100.00 USD will be deducted from a future deposit.", + "headline": "$100.00 USD will be deducted from a future payout.", "icon": , }, { @@ -644,7 +644,7 @@ exports[`mapTimelineEvents single currency events formats partial_refund events { "body": [], "date": 2020-04-03T18:58:01.000Z, - "headline": "$50.00 USD will be deducted from a future deposit.", + "headline": "$50.00 USD will be deducted from a future payout.", "icon": , }, { @@ -675,9 +675,9 @@ exports[`mapTimelineEvents single currency events in person payments - tap to pa "headline": $19.19 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -699,7 +699,7 @@ exports[`mapTimelineEvents single currency events in person payments - tap to pa , - "Net deposit: $19.19 USD", + "Net payout: $19.19 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of $19.80 USD was successfully charged.", @@ -724,9 +724,9 @@ exports[`mapTimelineEvents single currency events should not render fee breakup "headline": $59.50 USD was added to your - Apr 2, 2020 deposit + Apr 2, 2020 payout . , @@ -737,7 +737,7 @@ exports[`mapTimelineEvents single currency events should not render fee breakup undefined, "Fee (1.95% + $0.15): -$3.50", undefined, - "Net deposit: $59.50 USD", + "Net payout: $59.50 USD", ], "date": 2020-04-01T14:37:54.000Z, "headline": "A payment of $63.00 USD was successfully charged.", diff --git a/client/settings/deposits/index.js b/client/settings/deposits/index.js index 05c114f79fd..19ad1956419 100644 --- a/client/settings/deposits/index.js +++ b/client/settings/deposits/index.js @@ -131,17 +131,17 @@ const CustomizeDepositSchedule = () => {

    { depositScheduleInterval === 'monthly' && __( - 'Deposits scheduled on a weekend will be sent on the next business day.', + 'Payouts scheduled on a weekend will be sent on the next business day.', 'woocommerce-payments' ) } { depositScheduleInterval === 'weekly' && __( - 'Deposits that fall on a holiday will initiate on the next business day.', + 'Payouts that fall on a holiday will initiate on the next business day.', 'woocommerce-payments' ) } { depositScheduleInterval === 'daily' && __( - 'Deposits will occur every business day.', + 'Payouts will occur every business day.', 'woocommerce-payments' ) }

    @@ -161,14 +161,14 @@ const DepositsSchedule = () => { { interpolateComponents( { mixedString: __( - 'Deposit scheduling is currently unavailable for your store. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + 'Payout scheduling is currently unavailable for your store. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), components: { learnMoreLink: ( // eslint-disable-next-line jsx-a11y/anchor-has-content @@ -183,15 +183,15 @@ const DepositsSchedule = () => { { interpolateComponents( { mixedString: __( - 'Your first deposit will be held for 7-14 days. ' + - 'Deposit scheduling will be available after this period. {{learnMoreLink}}Learn more{{/learnMoreLink}}', + 'Your first payout will be held for 7-14 days. ' + + 'Payout scheduling will be available after this period. {{learnMoreLink}}Learn more{{/learnMoreLink}}', 'woocommerce-payments' ), components: { learnMoreLink: ( // eslint-disable-next-line jsx-a11y/anchor-has-content @@ -220,13 +220,13 @@ const Deposits = () => { return ( -

    { __( 'Deposit schedule', 'woocommerce-payments' ) }

    +

    { __( 'Payout schedule', 'woocommerce-payments' ) }

    - { __( 'Deposit bank account', 'woocommerce-payments' ) } + { __( 'Payout bank account', 'woocommerce-payments' ) }

    { hasErroredExternalAccount ? ( { ) : (

    { __( - 'Manage and update your deposit account information to receive payments and deposits.', + 'Manage and update your bank account information to receive payouts.', 'woocommerce-payments' ) }{ ' ' } { accountLink && ( diff --git a/client/settings/deposits/test/index.test.js b/client/settings/deposits/test/index.test.js index 1bd3bb9d101..f562bceee31 100644 --- a/client/settings/deposits/test/index.test.js +++ b/client/settings/deposits/test/index.test.js @@ -108,7 +108,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Deposit scheduling is currently unavailable for your store/, + /Payout scheduling is currently unavailable for your store/, { ignore: '.a11y-speak-region', } @@ -128,7 +128,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Deposit scheduling is currently unavailable for your store/, + /Payout scheduling is currently unavailable for your store/, { ignore: '.a11y-speak-region', } @@ -146,7 +146,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Deposit scheduling is currently unavailable for your store/, + /Payout scheduling is currently unavailable for your store/, { ignore: '.a11y-speak-region', } @@ -165,7 +165,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Your first deposit will be held for/, + /Your first payout will be held for/, { ignore: '.a11y-speak-region', } @@ -184,7 +184,7 @@ describe( 'Deposits', () => { ); expect( - screen.queryByText( /Your first deposit will be held for/, { + screen.queryByText( /Your first payout will be held for/, { ignore: '.a11y-speak-region', } ) ).toBeFalsy(); @@ -285,7 +285,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -294,7 +294,7 @@ describe( 'Deposits', () => { expect( screen.queryByText( - /Manage and update your deposit account information to receive payments and deposits./, + /Manage and update your bank account information to receive payouts./, { ignore: '.a11y-speak-region', } @@ -311,7 +311,7 @@ describe( 'Deposits', () => { expect( screen.queryByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -319,7 +319,7 @@ describe( 'Deposits', () => { ).toBeFalsy(); const depositsMessage = screen.getByText( - /Manage and update your deposit account information to receive payments and deposits./, + /Manage and update your bank account information to receive payouts./, { ignore: '.a11y-speak-region', } @@ -352,7 +352,7 @@ describe( 'Deposits', () => { ); const depositsMessage = screen.getByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -361,7 +361,7 @@ describe( 'Deposits', () => { expect( screen.queryByText( - /Manage and update your deposit account information to receive payments and deposits./, + /Manage and update your bank account information to receive payouts./, { ignore: '.a11y-speak-region', } @@ -395,7 +395,7 @@ describe( 'Deposits', () => { expect( screen.queryByText( - /Deposits are currently paused because a recent deposit failed./, + /Payouts are currently paused because a recent payout failed./, { ignore: '.a11y-speak-region', } @@ -403,7 +403,7 @@ describe( 'Deposits', () => { ).toBeFalsy(); const depositsMessage = screen.getByText( - /Manage and update your deposit account information to receive payments and deposits./, + /Manage and update your bank account information to receive payouts./, { ignore: '.a11y-speak-region', } diff --git a/client/settings/settings-manager/index.js b/client/settings/settings-manager/index.js index 2668afa04c5..fc238c706b2 100644 --- a/client/settings/settings-manager/index.js +++ b/client/settings/settings-manager/index.js @@ -84,17 +84,17 @@ const DepositsDescription = () => { return ( <> -

    { __( 'Deposits', 'woocommerce-payments' ) }

    +

    { __( 'Payouts', 'woocommerce-payments' ) }

    { sprintf( __( - 'Funds are available for deposit %s business days after they’re received.', + 'Funds are available for payout %s business days after they’re received.', 'woocommerce-payments' ), depositDelayDays ) }

    - + { __( 'Learn more about pending schedules', 'woocommerce-payments' @@ -242,7 +242,7 @@ const SettingsManager = () => { -
    +