From 0374e8d85e85d2983aa82b4fd72c37098bc72024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Mon, 14 Jun 2021 23:51:23 +0000 Subject: [PATCH 01/13] port #710 to current codebase status --- client/order/index.js | 35 +++++++++++++++++++ client/utils/order.js | 16 +++++++++ includes/admin/class-wc-payments-admin.php | 25 +++++++++++++ ...ss-wc-rest-payments-webhook-controller.php | 2 ++ includes/class-wc-payment-gateway-wcpay.php | 18 +++++++++- webpack.config.js | 1 + 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 client/order/index.js create mode 100644 client/utils/order.js diff --git a/client/order/index.js b/client/order/index.js new file mode 100644 index 00000000000..b09c3cea7c3 --- /dev/null +++ b/client/order/index.js @@ -0,0 +1,35 @@ +/* global jQuery */ + +/** + * Internal dependencies + */ +import { getConfig } from 'utils/order'; + +jQuery( function ( $ ) { + const disableManualRefunds = getConfig( 'disableManualRefunds' ) ?? false; + const manualRefundsTip = getConfig( 'manualRefundsTip' ) ?? ''; + + if ( disableManualRefunds ) { + /** + * The script is included in the footer, so all the DOM must already be in place. + * This allows us to modify the tip before it gets used on document.ready. + */ + $( '.do-manual-refund' ).ecah( function () { + const $refundButton = $( this ); + + // Disable the manual refund button. + $refundButton + .addClass( 'disabled' ) + .attr( 'readonly', 'readonly' ) + .on( 'click', function () { + return false; + } ); + + // Add the right label to indicate why the button is disabled. + $refundButton.attr( { + // Tips shoudl be accessible through $.data(), but jQuery.tipTip uses attributes. + 'data-tip': manualRefundsTip, + } ); + } ); + } +} ); diff --git a/client/utils/order.js b/client/utils/order.js new file mode 100644 index 00000000000..d821a7e6d06 --- /dev/null +++ b/client/utils/order.js @@ -0,0 +1,16 @@ +/* global wcpay_order_config, wc */ + +/** + * Retrieves a configuration value. + * + * @param {string} name The name of the config parameter. + * @return {*} The value of the parameter of null. + */ +export const getConfig = ( name ) => { + // Config for the Edit Order screen. + const config = + wcpay_order_config ?? // eslint-disable-line camelcase + wc.wcSettings.getSetting( 'woocommerce_payments_data' ); + + return config?.[ name ]; +}; diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index 6ba59c5fe26..a3d47afdcc7 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -291,6 +291,14 @@ public function register_payments_scripts() { WC_Payments::get_file_version( 'dist/tos.css' ) ); + wp_register_script( + 'WCPAY_ADMIN_ORDER_ACTIONS', + plugins_url( 'dist/order.js', WCPAY_PLUGIN_FILE ), + [ 'jquery-tiptip' ], + WC_Payments::get_file_version( 'dist/order.js' ), + true + ); + $settings_script_src_url = plugins_url( 'dist/settings.js', WCPAY_PLUGIN_FILE ); $settings_script_asset_path = WCPAY_ABSPATH . 'dist/settings.asset.php'; $settings_script_asset = file_exists( $settings_script_asset_path ) ? require_once $settings_script_asset_path : [ 'dependencies' => [] ]; @@ -403,6 +411,23 @@ public function enqueue_payments_scripts() { wp_enqueue_script( 'WCPAY_PAYMENT_GATEWAYS_PAGE' ); wp_enqueue_style( 'WCPAY_PAYMENT_GATEWAYS_PAGE' ); } + + $screen = get_current_screen(); + if ( 'shop_order' === $screen->id ) { + $order = wc_get_order(); + + if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $order->get_payment_method() ) { + wp_localize_script( + 'WCPAY_ADMIN_ORDER_ACTIONS', + 'wcpay_order_config', + [ + 'disableManualRefunds' => ! $this->wcpay_gateway->has_refund_failed( $order ), + 'manualRefundsTip' => __( 'Refunds are available only through WooCommerce Payments.', 'woocommerce-payments' ), + ] + ); + wp_enqueue_script( 'WCPAY_ADMIN_ORDER_ACTIONS' ); + } + } } /** diff --git a/includes/admin/class-wc-rest-payments-webhook-controller.php b/includes/admin/class-wc-rest-payments-webhook-controller.php index 38e3dfcf4fc..18265e99601 100644 --- a/includes/admin/class-wc-rest-payments-webhook-controller.php +++ b/includes/admin/class-wc-rest-payments-webhook-controller.php @@ -198,6 +198,8 @@ private function process_webhook_refund_updated( $event_body ) { $refund_id ); $order->add_order_note( $note ); + $order->update_meta_data( '_wcpay_refund_status', 'failed' ); + $order->save(); } /** diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 32f007dc34f..50ae7ca439a 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1093,7 +1093,9 @@ protected function get_payment_token( $order ) { * @return bool */ public function can_refund_order( $order ) { - return $order && $order->get_meta( '_charge_id', true ); + return $order + && $order->get_meta( '_charge_id', true ) + && 'failed' !== $order->get_meta( '_wcpay_refund_status', true ); } /** @@ -1143,6 +1145,8 @@ public function process_refund( $order_id, $amount = null, $reason = '' ) { Logger::log( $note ); $order->add_order_note( $note ); + $order->update_meta_data( '_wcpay_refund_status', 'failed' ); + $order->save(); Tracker::track_admin( 'wcpay_edit_order_refund_failure', [ 'reason' => $note ] ); return new WP_Error( 'wcpay_edit_order_refund_failure', $e->getMessage() ); @@ -1164,10 +1168,22 @@ public function process_refund( $order_id, $amount = null, $reason = '' ) { } $order->add_order_note( $note ); + $order->update_meta_data( '_wcpay_refund_status', 'successful' ); + $order->save(); return true; } + /** + * Checks whether a refund through the gateway has already failed. + * + * @param WC_Order $order The order to check. + * @return boolean + */ + public function has_refund_failed( $order ) { + return 'failed' === $order->get_meta( '_wcpay_refund_status', true ); + } + /** * Overrides the original method in woo's WC_Settings_API in order to conditionally render the enabled checkbox. * diff --git a/webpack.config.js b/webpack.config.js index da4dc33234a..14f76897b6f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -19,6 +19,7 @@ const webpackConfig = { './client/additional-methods-setup/index.js', 'payment-gateways': './client/payment-gateways/index.js', 'multi-currency': './client/multi-currency/index.js', + order: './client/order/index.js', }, output: { filename: '[name].js', From 4094e0b9be754abb377f3395543811cf1d2afb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Mon, 14 Jun 2021 23:53:01 +0000 Subject: [PATCH 02/13] fix phpcs ignores --- includes/class-wc-payment-gateway-wcpay.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 50ae7ca439a..f39a788abd1 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -393,7 +393,7 @@ public function needs_setup() { * @return bool */ public static function is_current_page_settings() { - return count( self::$settings_url_params ) === count( array_intersect_assoc( $_GET, self::$settings_url_params ) ); // phpcs:disable WordPress.Security.NonceVerification.Recommended + return count( self::$settings_url_params ) === count( array_intersect_assoc( $_GET, self::$settings_url_params ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } /** @@ -488,11 +488,11 @@ public function output_payments_settings_screen() { global $hide_save_button; $hide_save_button = true; - if ( ! empty( $_GET['method'] ) ) : + if ( ! empty( $_GET['method'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
@@ -580,7 +580,7 @@ public function save_payment_method_checkbox( $force_checked = false ) { * @return array|null An array with customer data or nothing. */ public function get_prepared_customer_data() { - if ( ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() ) { + if ( ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return null; } @@ -589,7 +589,7 @@ public function get_prepared_customer_data() { $firstname = ''; $lastname = ''; - if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) { + 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'] ); $order = wc_get_order( $order_id ); From aa52b500e3d5d41ebb17eae2d6e3dec643e5b878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Wed, 16 Jun 2021 00:20:40 +0000 Subject: [PATCH 03/13] fix manual refund button tooltip --- client/order/index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/order/index.js b/client/order/index.js index b09c3cea7c3..31fa98a0039 100644 --- a/client/order/index.js +++ b/client/order/index.js @@ -14,7 +14,7 @@ jQuery( function ( $ ) { * The script is included in the footer, so all the DOM must already be in place. * This allows us to modify the tip before it gets used on document.ready. */ - $( '.do-manual-refund' ).ecah( function () { + $( '.do-manual-refund' ).each( function () { const $refundButton = $( this ); // Disable the manual refund button. @@ -27,9 +27,12 @@ jQuery( function ( $ ) { // Add the right label to indicate why the button is disabled. $refundButton.attr( { - // Tips shoudl be accessible through $.data(), but jQuery.tipTip uses attributes. - 'data-tip': manualRefundsTip, + // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate + // the tooltip. + title: manualRefundsTip, } ); + // Regenerate the tipTip tooltip. + $refundButton.tipTip(); } ); } } ); From 7e97721f74881b283d9caf2bd1db7de85c543ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Wed, 16 Jun 2021 02:16:49 +0000 Subject: [PATCH 04/13] clean up the order jQuery code --- client/order/index.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/client/order/index.js b/client/order/index.js index 31fa98a0039..a99e7f62ef0 100644 --- a/client/order/index.js +++ b/client/order/index.js @@ -21,18 +21,17 @@ jQuery( function ( $ ) { $refundButton .addClass( 'disabled' ) .attr( 'readonly', 'readonly' ) + // Add the right label to indicate why the button is disabled. + .attr( { + // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate + // the tooltip. + title: manualRefundsTip, + } ) .on( 'click', function () { return false; - } ); - - // Add the right label to indicate why the button is disabled. - $refundButton.attr( { - // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate - // the tooltip. - title: manualRefundsTip, - } ); - // Regenerate the tipTip tooltip. - $refundButton.tipTip(); + } ) + // Regenerate the tipTip tooltip. + .tipTip(); } ); } } ); From 453f33a111d5a1b214916b13aa0a0ffd52b7ce77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Wed, 16 Jun 2021 02:16:58 +0000 Subject: [PATCH 05/13] add tests --- .../test-class-wc-rest-payments-webhook.php | 73 +++++++++++++++++++ .../test-class-wc-payment-gateway-wcpay.php | 54 ++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/tests/unit/admin/test-class-wc-rest-payments-webhook.php b/tests/unit/admin/test-class-wc-rest-payments-webhook.php index bc68f572c4f..b5c6f747578 100644 --- a/tests/unit/admin/test-class-wc-rest-payments-webhook.php +++ b/tests/unit/admin/test-class-wc-rest-payments-webhook.php @@ -156,6 +156,79 @@ public function test_webhook_with_no_data_property() { $this->assertEquals( [ 'result' => 'bad_request' ], $response_data ); } + public function test_valid_failed_refund_webhook_sets_failed_meta() { + // Setup test request data. + $this->request_body['type'] = 'charge.refund.updated'; + $this->request_body['data']['object'] = [ + 'status' => 'failed', + 'charge' => 'test_charge_id', + 'id' => 'test_refund_id', + 'amount' => 999, + 'currency' => 'gbp', + ]; + + $this->request->set_body( wp_json_encode( $this->request_body ) ); + + $mock_order = $this->getMockBuilder( WC_Order::class ) + ->disableOriginalConstructor() + ->setMethods( [ 'add_order_note', 'update_meta_data' ] ) + ->getMock(); + + $mock_order + ->expects( $this->once() ) + ->method( 'add_order_note' ) + ->with( + $this->matchesRegularExpression( + '~^A refund of ()?£9.99()? was unsuccessful using WooCommerce Payments \(test_refund_id\).$~' + ) + ); + + // The expects condition here is the real test; we expect that the 'update_meta_data' function + // is called with the appropriate values. + $mock_order + ->expects( $this->once() ) + ->method( 'update_meta_data' ) + ->with( '_wcpay_refund_status', 'failed' ); + + $this->mock_db_wrapper + ->expects( $this->once() ) + ->method( 'order_from_charge_id' ) + ->with( 'test_charge_id' ) + ->willReturn( $mock_order ); + + // Run the test. + $this->controller->handle_webhook( $this->request ); + } + + public function test_non_failed_refund_update_webhook_does_not_set_failed_meta() { + // Setup test request data. + $this->request_body['type'] = 'charge.refund.updated'; + $this->request_body['data']['object'] = [ + 'status' => 'success', + ]; + + $this->request->set_body( wp_json_encode( $this->request_body ) ); + + $mock_order = $this->getMockBuilder( WC_Order::class ) + ->disableOriginalConstructor() + ->setMethods( [ 'update_meta_data' ] ) + ->getMock(); + + $this->mock_db_wrapper + ->expects( $this->never() ) + ->method( 'order_from_charge_id' ); + + // The expects condition here is the real test; we expect that the 'update_meta_data' function + // is never called to update the meta data. + $mock_order + ->expects( $this->never() ) + ->method( 'update_meta_data' ); + + // Run the test. + $this->controller->handle_webhook( $this->request ); + + } + /** * Test a valid failed refund update webhook. */ diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php index 1ab786339f9..b834209cbf6 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php @@ -257,6 +257,60 @@ public function test_process_refund_on_uncaptured_payment() { $this->assertEquals( 'uncaptured-payment', $result->get_error_code() ); } + public function test_process_refund_success_does_not_set_refund_failed_meta() { + $intent_id = 'pi_xxxxxxxxxxxxx'; + $charge_id = 'ch_yyyyyyyyyyyyy'; + + $order = WC_Helper_Order::create_order(); + $order->update_meta_data( '_intent_id', $intent_id ); + $order->update_meta_data( '_charge_id', $charge_id ); + $order->save(); + + $this->mock_api_client->expects( $this->once() )->method( 'refund_charge' )->will( + $this->returnValue( + [ + 'id' => 're_123456789', + 'object' => 'refund', + 'amount' => 19.99, + 'balance_transaction' => 'txn_987654321', + 'charge' => 'ch_121212121212', + 'created' => 1610123467, + 'payment_intent' => 'pi_1234567890', + 'reason' => null, + 'reciept_number' => null, + 'source_transfer_reversal' => null, + 'status' => 'succeeded', + 'transfer_reversal' => null, + 'currency' => 'usd', + ] + ) + ); + + $this->wcpay_gateway->process_refund( $order->get_id(), 19.99 ); + $this->assertEquals( '', $order->get_meta( '_wcpay_refund_status', true ) ); + } + + public function test_process_refund_failure_sets_refund_failed_meta() { + $intent_id = 'pi_xxxxxxxxxxxxx'; + $charge_id = 'ch_yyyyyyyyyyyyy'; + + $order = WC_Helper_Order::create_order(); + $order->update_meta_data( '_intent_id', $intent_id ); + $order->update_meta_data( '_charge_id', $charge_id ); + $order->update_status( 'processing' ); + $order->save(); + + $order_id = $order->get_id(); + + $this->mock_api_client + ->expects( $this->once() ) + ->method( 'refund_charge' ) + ->willThrowException( new \Exception( 'Test message' ) ); + + $result = $this->wcpay_gateway->process_refund( $order_id, 19.99 ); + $this->assertEquals( 'failed', $order->get_meta( '_wcpay_refund_status', true ) ); + } + public function test_process_refund_on_api_error() { $intent_id = 'pi_xxxxxxxxxxxxx'; $charge_id = 'ch_yyyyyyyyyyyyy'; From 32f8bb30d27cc3823dd43df7bd3ea761384316d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Wed, 16 Jun 2021 15:48:22 +0000 Subject: [PATCH 06/13] fix failing tests --- tests/unit/test-class-wc-payment-gateway-wcpay.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php index b834209cbf6..b33e001b776 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php @@ -287,7 +287,10 @@ public function test_process_refund_success_does_not_set_refund_failed_meta() { ); $this->wcpay_gateway->process_refund( $order->get_id(), 19.99 ); - $this->assertEquals( '', $order->get_meta( '_wcpay_refund_status', true ) ); + + // Reload the order information to get the new meta. + $order = wc_get_order( $order->get_id() ); + $this->assertEquals( 'successful', $order->get_meta( '_wcpay_refund_status', true ) ); } public function test_process_refund_failure_sets_refund_failed_meta() { @@ -307,7 +310,10 @@ public function test_process_refund_failure_sets_refund_failed_meta() { ->method( 'refund_charge' ) ->willThrowException( new \Exception( 'Test message' ) ); - $result = $this->wcpay_gateway->process_refund( $order_id, 19.99 ); + $this->wcpay_gateway->process_refund( $order_id, 19.99 ); + + // Reload the order information to get the new meta. + $order = wc_get_order( $order_id ); $this->assertEquals( 'failed', $order->get_meta( '_wcpay_refund_status', true ) ); } From b9b42980b5741cfbe1bc18c40d0dff0c836db976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Thu, 17 Jun 2021 01:05:11 +0000 Subject: [PATCH 07/13] make better use of the `has_refund_failed` helper function --- includes/class-wc-payment-gateway-wcpay.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index f39a788abd1..f1e968f3ec8 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1095,7 +1095,7 @@ protected function get_payment_token( $order ) { public function can_refund_order( $order ) { return $order && $order->get_meta( '_charge_id', true ) - && 'failed' !== $order->get_meta( '_wcpay_refund_status', true ); + && ! $this->has_refund_failed( $order ); } /** From 9baeffa0782bc779a3a2f09632ed4ee5dfcae3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Thu, 17 Jun 2021 01:05:58 +0000 Subject: [PATCH 08/13] fix typo in test mock --- tests/unit/test-class-wc-payment-gateway-wcpay.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php index b33e001b776..66297f2e4c9 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php @@ -277,7 +277,7 @@ public function test_process_refund_success_does_not_set_refund_failed_meta() { 'created' => 1610123467, 'payment_intent' => 'pi_1234567890', 'reason' => null, - 'reciept_number' => null, + 'receipt_number' => null, 'source_transfer_reversal' => null, 'status' => 'succeeded', 'transfer_reversal' => null, From df9ec00af79d2e2c2c820fda1b408b641aa0cc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3fer=20R?= Date: Thu, 17 Jun 2021 01:13:59 +0000 Subject: [PATCH 09/13] use `has_refund_failed` helper method in tests --- tests/unit/test-class-wc-payment-gateway-wcpay.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php index 66297f2e4c9..e4e9aebf093 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php @@ -290,7 +290,7 @@ public function test_process_refund_success_does_not_set_refund_failed_meta() { // Reload the order information to get the new meta. $order = wc_get_order( $order->get_id() ); - $this->assertEquals( 'successful', $order->get_meta( '_wcpay_refund_status', true ) ); + $this->assertFalse( $this->wcpay_gateway->has_refund_failed( $order ) ); } public function test_process_refund_failure_sets_refund_failed_meta() { @@ -314,7 +314,7 @@ public function test_process_refund_failure_sets_refund_failed_meta() { // Reload the order information to get the new meta. $order = wc_get_order( $order_id ); - $this->assertEquals( 'failed', $order->get_meta( '_wcpay_refund_status', true ) ); + $this->assertTrue( $this->wcpay_gateway->has_refund_failed( $order ) ); } public function test_process_refund_on_api_error() { From c990025673b1027383a6e22ea2377636c37c1036 Mon Sep 17 00:00:00 2001 From: Paul Dechov Date: Fri, 18 Jun 2021 15:36:09 -0400 Subject: [PATCH 10/13] Revert can_refund_order change so that gateway refund button always shows --- includes/class-wc-payment-gateway-wcpay.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index f1e968f3ec8..dc163c58608 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1093,9 +1093,7 @@ protected function get_payment_token( $order ) { * @return bool */ public function can_refund_order( $order ) { - return $order - && $order->get_meta( '_charge_id', true ) - && ! $this->has_refund_failed( $order ); + return $order && $order->get_meta( '_charge_id', true ); } /** From 8eface0aae0ab75c02e16be913fd6a65e4f8081b Mon Sep 17 00:00:00 2001 From: Paul Dechov Date: Fri, 18 Jun 2021 15:38:57 -0400 Subject: [PATCH 11/13] Hide manual refund button initially, and update the tip only after it's shown --- client/order/index.js | 28 ++++++++++------------ includes/admin/class-wc-payments-admin.php | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/client/order/index.js b/client/order/index.js index a99e7f62ef0..32656f64367 100644 --- a/client/order/index.js +++ b/client/order/index.js @@ -9,29 +9,25 @@ jQuery( function ( $ ) { const disableManualRefunds = getConfig( 'disableManualRefunds' ) ?? false; const manualRefundsTip = getConfig( 'manualRefundsTip' ) ?? ''; - if ( disableManualRefunds ) { - /** - * The script is included in the footer, so all the DOM must already be in place. - * This allows us to modify the tip before it gets used on document.ready. - */ - $( '.do-manual-refund' ).each( function () { - const $refundButton = $( this ); + /** + * The script is included in the footer, so all the DOM must already be in place. + * This allows us to modify the tip before it gets used on document.ready. + */ + $( '.do-manual-refund' ).each( function () { + const $refundButton = $( this ); - // Disable the manual refund button. + if ( disableManualRefunds ) { + $refundButton.hide(); + } else { + // Adjust the messaging on the manual refund button. $refundButton - .addClass( 'disabled' ) - .attr( 'readonly', 'readonly' ) - // Add the right label to indicate why the button is disabled. .attr( { // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate // the tooltip. title: manualRefundsTip, } ) - .on( 'click', function () { - return false; - } ) // Regenerate the tipTip tooltip. .tipTip(); - } ); - } + } + } ); } ); diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index a3d47afdcc7..19548d5b537 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -422,7 +422,7 @@ public function enqueue_payments_scripts() { 'wcpay_order_config', [ 'disableManualRefunds' => ! $this->wcpay_gateway->has_refund_failed( $order ), - 'manualRefundsTip' => __( 'Refunds are available only through WooCommerce Payments.', 'woocommerce-payments' ), + 'manualRefundsTip' => __( 'Refunding manually requires reimbursing your customer offline via cash, check, etc. The refund amounts entered here will only be used to balance your analytics.', 'woocommerce-payments' ), ] ); wp_enqueue_script( 'WCPAY_ADMIN_ORDER_ACTIONS' ); From dc1c520425ff9407f0b2b6a7744fc3f4478828bb Mon Sep 17 00:00:00 2001 From: Paul Dechov Date: Mon, 21 Jun 2021 12:29:57 -0400 Subject: [PATCH 12/13] Adjust manual refund button after 'Refund' button is clicked This addresses cases when the refund buttons are initialized or (re-initialized) after page load. --- client/order/index.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/client/order/index.js b/client/order/index.js index 32656f64367..b30106879f3 100644 --- a/client/order/index.js +++ b/client/order/index.js @@ -9,25 +9,25 @@ jQuery( function ( $ ) { const disableManualRefunds = getConfig( 'disableManualRefunds' ) ?? false; const manualRefundsTip = getConfig( 'manualRefundsTip' ) ?? ''; - /** - * The script is included in the footer, so all the DOM must already be in place. - * This allows us to modify the tip before it gets used on document.ready. - */ - $( '.do-manual-refund' ).each( function () { - const $refundButton = $( this ); + $( '#woocommerce-order-items' ).on( + 'click', + 'button.refund-items', + function () { + const $refundButton = $( '.do-manual-refund' ); - if ( disableManualRefunds ) { - $refundButton.hide(); - } else { - // Adjust the messaging on the manual refund button. - $refundButton - .attr( { - // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate - // the tooltip. - title: manualRefundsTip, - } ) - // Regenerate the tipTip tooltip. - .tipTip(); + if ( disableManualRefunds ) { + $refundButton.hide(); + } else { + // Adjust the messaging on the manual refund button. + $refundButton + .attr( { + // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate + // the tooltip. + title: manualRefundsTip, + } ) + // Regenerate the tipTip tooltip. + .tipTip(); + } } - } ); + ); } ); From 871583ba99ff679cfcba4c0f842fefe5b726cd86 Mon Sep 17 00:00:00 2001 From: Paul Dechov Date: Mon, 21 Jun 2021 12:32:05 -0400 Subject: [PATCH 13/13] Refactor: change variable name to avoid ambiguity --- client/order/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/order/index.js b/client/order/index.js index b30106879f3..1d306c0df9b 100644 --- a/client/order/index.js +++ b/client/order/index.js @@ -13,13 +13,13 @@ jQuery( function ( $ ) { 'click', 'button.refund-items', function () { - const $refundButton = $( '.do-manual-refund' ); + const $manualRefundButton = $( '.do-manual-refund' ); if ( disableManualRefunds ) { - $refundButton.hide(); + $manualRefundButton.hide(); } else { // Adjust the messaging on the manual refund button. - $refundButton + $manualRefundButton .attr( { // Tips are readable through $.data(), but jQuery.tipTip use the title attribute to generate // the tooltip.