From 291de9c1fb99b7dd30866cda7c6420a6c0477a0e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 13 Mar 2018 11:35:20 +0000 Subject: [PATCH 1/2] Override supports for PayPal to see if credentials exist --- .../paypal/class-wc-gateway-paypal.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/includes/gateways/paypal/class-wc-gateway-paypal.php b/includes/gateways/paypal/class-wc-gateway-paypal.php index 8b3f73d8940..8afd9ea0cee 100644 --- a/includes/gateways/paypal/class-wc-gateway-paypal.php +++ b/includes/gateways/paypal/class-wc-gateway-paypal.php @@ -87,6 +87,31 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway { } } + /** + * Check if a gateway supports a given feature. + * + * Gateways should override this to declare support (or lack of support) for a feature. + * For backward compatibility, gateways support 'products' by default, but nothing else. + * + * @param string $feature string The name of a feature to test support for. + * @return bool True if the gateway supports the feature, false otherwise. + */ + public function supports( $feature ) { + if ( 'refunds' === $feature ) { + // Ensure the gateway has API credentials. + $has_api_creds = false; + + if ( $this->testmode ) { + $has_api_creds = $this->get_option( 'sandbox_api_username' ) && $this->get_option( 'sandbox_api_password' ) && $this->get_option( 'sandbox_api_signature' ); + } else { + $has_api_creds = $this->get_option( 'api_username' ) && $this->get_option( 'api_password' ) && $this->get_option( 'api_signature' ); + } + + return $has_api_creds; + } + return parent::supports( $feature ); + } + /** * Logging method. * From 5ac7aac369db2ec107ff42cc083c7cde414a8d80 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 3 Apr 2018 17:09:09 +0100 Subject: [PATCH 2/2] Improve can_refund_order and add unit tests --- .../abstracts/abstract-wc-payment-gateway.php | 12 +++ .../meta-boxes/views/html-order-items.php | 12 +-- .../paypal/class-wc-gateway-paypal.php | 38 +++------- tests/bootstrap.php | 1 + .../class-wc-mock-payment-gateway.php | 21 ++++++ tests/unit-tests/gateways/gateways.php | 75 +++++++++++++++++++ 6 files changed, 126 insertions(+), 33 deletions(-) create mode 100644 tests/framework/class-wc-mock-payment-gateway.php create mode 100644 tests/unit-tests/gateways/gateways.php diff --git a/includes/abstracts/abstract-wc-payment-gateway.php b/includes/abstracts/abstract-wc-payment-gateway.php index 1a175a8ecb9..dc4ac9d9a4e 100644 --- a/includes/abstracts/abstract-wc-payment-gateway.php +++ b/includes/abstracts/abstract-wc-payment-gateway.php @@ -388,6 +388,18 @@ abstract class WC_Payment_Gateway extends WC_Settings_API { return apply_filters( 'woocommerce_payment_gateway_supports', in_array( $feature, $this->supports ), $feature, $this ); } + /** + * Can the order be refunded via this gateway? + * + * Should be extended by gateways to do their own checks. + * + * @param WC_Order $order Order object. + * @return bool If false, the automatic refund button is hidden in the UI. + */ + public function can_refund_order( $order ) { + return $order && $this->supports( 'refunds' ); + } + /** * Core credit card form which gateways can used if needed. Deprecated - inherit WC_Payment_Gateway_CC instead. * diff --git a/includes/admin/meta-boxes/views/html-order-items.php b/includes/admin/meta-boxes/views/html-order-items.php index f7ad8772cfc..0fa97c60bbd 100644 --- a/includes/admin/meta-boxes/views/html-order-items.php +++ b/includes/admin/meta-boxes/views/html-order-items.php @@ -292,12 +292,14 @@ if ( wc_tax_enabled() ) {
' . wc_price( 0, array( 'currency' => $order->get_currency() ) ) . ''; - $gateway_supports_refunds = false !== $payment_gateway && $payment_gateway->supports( 'refunds' ); - $gateway_name = false !== $payment_gateway ? ( ! empty( $payment_gateway->method_title ) ? $payment_gateway->method_title : $payment_gateway->get_title() ) : __( 'Payment gateway', 'woocommerce' ); + $refund_amount = '' . wc_price( 0, array( 'currency' => $order->get_currency() ) ) . ''; + $gateway_name = false !== $payment_gateway ? ( ! empty( $payment_gateway->method_title ) ? $payment_gateway->method_title : $payment_gateway->get_title() ) : __( 'Payment gateway', 'woocommerce' ); + + if ( false !== $payment_gateway && $payment_gateway->can_refund_order( $order ) ) { + /* translators: refund amount, gateway name */ + echo ''; + } ?> - - diff --git a/includes/gateways/paypal/class-wc-gateway-paypal.php b/includes/gateways/paypal/class-wc-gateway-paypal.php index 8afd9ea0cee..137e3339625 100644 --- a/includes/gateways/paypal/class-wc-gateway-paypal.php +++ b/includes/gateways/paypal/class-wc-gateway-paypal.php @@ -87,31 +87,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway { } } - /** - * Check if a gateway supports a given feature. - * - * Gateways should override this to declare support (or lack of support) for a feature. - * For backward compatibility, gateways support 'products' by default, but nothing else. - * - * @param string $feature string The name of a feature to test support for. - * @return bool True if the gateway supports the feature, false otherwise. - */ - public function supports( $feature ) { - if ( 'refunds' === $feature ) { - // Ensure the gateway has API credentials. - $has_api_creds = false; - - if ( $this->testmode ) { - $has_api_creds = $this->get_option( 'sandbox_api_username' ) && $this->get_option( 'sandbox_api_password' ) && $this->get_option( 'sandbox_api_signature' ); - } else { - $has_api_creds = $this->get_option( 'api_username' ) && $this->get_option( 'api_password' ) && $this->get_option( 'api_signature' ); - } - - return $has_api_creds; - } - return parent::supports( $feature ); - } - /** * Logging method. * @@ -319,7 +294,15 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway { * @return bool */ public function can_refund_order( $order ) { - return $order && $order->get_transaction_id(); + $has_api_creds = false; + + if ( $this->testmode ) { + $has_api_creds = $this->get_option( 'sandbox_api_username' ) && $this->get_option( 'sandbox_api_password' ) && $this->get_option( 'sandbox_api_signature' ); + } else { + $has_api_creds = $this->get_option( 'api_username' ) && $this->get_option( 'api_password' ) && $this->get_option( 'api_signature' ); + } + + return $order && $order->get_transaction_id() && $has_api_creds; } /** @@ -346,8 +329,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway { $order = wc_get_order( $order_id ); if ( ! $this->can_refund_order( $order ) ) { - $this->log( 'Refund Failed: No transaction ID', 'error' ); - return new WP_Error( 'error', __( 'Refund failed: No transaction ID', 'woocommerce' ) ); + return new WP_Error( 'error', __( 'Refund failed.', 'woocommerce' ) ); } $this->init_api(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 0f84df44aa3..44c40ef539b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -105,6 +105,7 @@ class WC_Unit_Tests_Bootstrap { require_once $this->tests_dir . '/framework/class-wc-mock-session-handler.php'; require_once $this->tests_dir . '/framework/class-wc-mock-wc-data.php'; require_once $this->tests_dir . '/framework/class-wc-mock-wc-object-query.php'; + require_once $this->tests_dir . '/framework/class-wc-mock-payment-gateway.php'; require_once $this->tests_dir . '/framework/class-wc-payment-token-stub.php'; require_once $this->tests_dir . '/framework/vendor/class-wp-test-spy-rest-server.php'; diff --git a/tests/framework/class-wc-mock-payment-gateway.php b/tests/framework/class-wc-mock-payment-gateway.php new file mode 100644 index 00000000000..ed11be78a67 --- /dev/null +++ b/tests/framework/class-wc-mock-payment-gateway.php @@ -0,0 +1,21 @@ +id = 'mock'; + $this->has_fields = false; + $this->order_button_text = __( 'Proceed to PayPal', 'woocommerce' ); + $this->method_title = 'Mock Gateway'; + $this->method_description = 'Mock Gateway for unit tests'; + $this->supports = array( + 'products', + ); + + // Load the settings. + $this->init_form_fields(); + $this->init_settings(); + } +} + diff --git a/tests/unit-tests/gateways/gateways.php b/tests/unit-tests/gateways/gateways.php new file mode 100644 index 00000000000..6eb3254f350 --- /dev/null +++ b/tests/unit-tests/gateways/gateways.php @@ -0,0 +1,75 @@ +assertTrue( $gateway->supports( 'products' ) ); + $this->assertFalse( $gateway->supports( 'made-up-feature' ) ); + } + + /** + * Test for supports() method. + */ + public function test_can_refund_order() { + $gateway = new WC_Mock_Payment_Gateway(); + $order = WC_Helper_Order::create_order(); + + $order->set_payment_method( 'mock' ); + $order->set_transaction_id( '12345' ); + $order->save(); + + $this->assertFalse( $gateway->can_refund_order( $order ) ); + + $gateway->supports[] = 'refunds'; + + $this->assertTrue( $gateway->can_refund_order( $order ) ); + + // Cleanup. + $order->delete( true ); + } + + /** + * Test for PayPal supports() method. + */ + public function test_paypal_can_refund_order() { + $gateway = new WC_Gateway_Paypal(); + $order = WC_Helper_Order::create_order(); + + $order->set_payment_method( 'paypal' ); + $order->set_transaction_id( '12345' ); + $order->save(); + + // Refunds won't work without credentials. + $this->assertFalse( $gateway->can_refund_order( $order ) ); + + // Add API credentials. + $settings = array( + 'testmode' => 'yes', + 'sandbox_api_username' => 'test', + 'sandbox_api_password' => 'test', + 'sandbox_api_signature' => 'test', + ); + update_option( 'woocommerce_paypal_settings ', $settings ); + $gateway = new WC_Gateway_Paypal(); + $this->assertTrue( $gateway->can_refund_order( $order ) ); + + // Refund requires transaction ID. + $order->set_transaction_id( '' ); + $order->save(); + $this->assertFalse( $gateway->can_refund_order( $order ) ); + + // Cleanup. + delete_option( 'woocommerce_paypal_settings' ); + $order->delete( true ); + } +} +