1%
@@ -102,7 +102,7 @@ exports[`Account fees utility functions formatMethodFeesTooltip() displays base
- Foreign exchange fee
+ Currency conversion fee
1%
diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php
index e7ad01fe210..d78671d1298 100644
--- a/includes/admin/class-wc-payments-admin.php
+++ b/includes/admin/class-wc-payments-admin.php
@@ -6,6 +6,7 @@
*/
use Automattic\Jetpack\Identity_Crisis as Jetpack_Identity_Crisis;
+use WCPay\Constants\Intent_Status;
use WCPay\Core\Server\Request;
use WCPay\Database_Cache;
use WCPay\Logger;
@@ -1253,7 +1254,7 @@ public function show_woopay_payment_method_name_admin( $order_id ) {
*/
public function display_wcpay_transaction_fee( $order_id ) {
$order = wc_get_order( $order_id );
- if ( ! $order || ! $order->get_meta( '_wcpay_transaction_fee' ) ) {
+ if ( ! $order || ! $order->get_meta( '_wcpay_transaction_fee' ) || Intent_Status::REQUIRES_CAPTURE === $order->get_meta( WC_Payments_Order_Service::INTENTION_STATUS_META_KEY ) ) {
return;
}
?>
diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php
index 4f579caf2b1..d1be21241b9 100644
--- a/includes/class-wc-payment-gateway-wcpay.php
+++ b/includes/class-wc-payment-gateway-wcpay.php
@@ -20,7 +20,19 @@
use WCPay\Constants\Intent_Status;
use WCPay\Constants\Payment_Type;
use WCPay\Constants\Payment_Method;
-use WCPay\Exceptions\{ Add_Payment_Method_Exception, Amount_Too_Small_Exception, Process_Payment_Exception, Intent_Authentication_Exception, API_Exception, Invalid_Address_Exception, Fraud_Prevention_Enabled_Exception, Invalid_Phone_Number_Exception, Rate_Limiter_Enabled_Exception, Order_ID_Mismatch_Exception, Order_Not_Found_Exception, New_Process_Payment_Exception };
+use WCPay\Exceptions\{Add_Payment_Method_Exception,
+ Amount_Too_Small_Exception,
+ API_Merchant_Exception,
+ Process_Payment_Exception,
+ Intent_Authentication_Exception,
+ API_Exception,
+ Invalid_Address_Exception,
+ Fraud_Prevention_Enabled_Exception,
+ Invalid_Phone_Number_Exception,
+ Rate_Limiter_Enabled_Exception,
+ Order_ID_Mismatch_Exception,
+ Order_Not_Found_Exception,
+ New_Process_Payment_Exception};
use WCPay\Core\Server\Request\Cancel_Intention;
use WCPay\Core\Server\Request\Capture_Intention;
use WCPay\Core\Server\Request\Create_And_Confirm_Intention;
@@ -1270,6 +1282,9 @@ public function process_payment( $order_id ) {
);
$error_details = esc_html( rtrim( $e->getMessage(), '.' ) );
+ if ( $e instanceof API_Merchant_Exception ) {
+ $error_details = $error_details . '. ' . esc_html( rtrim( $e->get_merchant_message(), '.' ) );
+ }
if ( $e instanceof API_Exception && 'card_error' === $e->get_error_type() ) {
// If the payment failed with a 'card_error' API exception, initialize the fraud meta box
diff --git a/includes/class-wc-payments-captured-event-note.php b/includes/class-wc-payments-captured-event-note.php
index 10c48567952..07e902d8632 100644
--- a/includes/class-wc-payments-captured-event-note.php
+++ b/includes/class-wc-payments-captured-event-note.php
@@ -327,9 +327,9 @@ private function fee_label_mapping( int $fixed_rate, bool $is_capped ) {
$res['additional-fx'] = 0 !== $fixed_rate
/* translators: %1$s% is the fee percentage and %2$s is the fixed rate */
- ? __( 'Foreign exchange fee: %1$s%% + %2$s', 'woocommerce-payments' )
+ ? __( 'Currency conversion fee: %1$s%% + %2$s', 'woocommerce-payments' )
/* translators: %1$s% is the fee percentage */
- : __( 'Foreign exchange fee: %1$s%%', 'woocommerce-payments' );
+ : __( 'Currency conversion fee: %1$s%%', 'woocommerce-payments' );
$res['additional-wcpay-subscription'] = 0 !== $fixed_rate
/* translators: %1$s% is the fee percentage and %2$s is the fixed rate */
diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php
index 32c75df5fc7..4ad2d32625e 100644
--- a/includes/class-wc-payments.php
+++ b/includes/class-wc-payments.php
@@ -354,6 +354,7 @@ public static function init() {
include_once __DIR__ . '/exceptions/class-base-exception.php';
include_once __DIR__ . '/exceptions/class-api-exception.php';
+ include_once __DIR__ . '/exceptions/class-api-merchant-exception.php';
include_once __DIR__ . '/exceptions/class-connection-exception.php';
include_once __DIR__ . '/core/class-mode.php';
diff --git a/includes/compat/blocks/class-blocks-data-extractor.php b/includes/compat/blocks/class-blocks-data-extractor.php
index 673cae7f352..becc393a5da 100644
--- a/includes/compat/blocks/class-blocks-data-extractor.php
+++ b/includes/compat/blocks/class-blocks-data-extractor.php
@@ -59,6 +59,15 @@ private function get_available_blocks() {
$blocks[] = new \Mailchimp_Woocommerce_Newsletter_Blocks_Integration();
}
+ if ( class_exists( '\WCK\Blocks\CheckoutIntegration' ) ) {
+ // phpcs:ignore
+ /**
+ * @psalm-suppress UndefinedClass
+ * @phpstan-ignore-next-line
+ */
+ $blocks[] = new \WCK\Blocks\CheckoutIntegration();
+ }
+
return $blocks;
}
diff --git a/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php b/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php
index 31ec70bedf8..d2584f9b824 100644
--- a/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php
+++ b/includes/compat/subscriptions/trait-wc-payment-gateway-wcpay-subscriptions.php
@@ -11,6 +11,7 @@
use WCPay\Core\Server\Request\Get_Intention;
use WCPay\Exceptions\API_Exception;
+use WCPay\Exceptions\API_Merchant_Exception;
use WCPay\Exceptions\Invalid_Payment_Method_Exception;
use WCPay\Exceptions\Add_Payment_Method_Exception;
use WCPay\Exceptions\Order_Not_Found_Exception;
@@ -342,6 +343,11 @@ public function scheduled_subscription_payment( $amount, $renewal_order ) {
$renewal_order->update_status( 'failed' );
if ( ! empty( $payment_information ) ) {
+ $error_details = esc_html( rtrim( $e->getMessage(), '.' ) );
+ if ( $e instanceof API_Merchant_Exception ) {
+ $error_details = $error_details . '. ' . esc_html( rtrim( $e->get_merchant_message(), '.' ) );
+ }
+
$note = sprintf(
WC_Payments_Utils::esc_interpolated_html(
/* translators: %1: the failed payment amount, %2: error message */
@@ -358,7 +364,7 @@ public function scheduled_subscription_payment( $amount, $renewal_order ) {
wc_price( $amount, [ 'currency' => WC_Payments_Utils::get_order_intent_currency( $renewal_order ) ] ),
$renewal_order
),
- esc_html( rtrim( $e->getMessage(), '.' ) )
+ $error_details
);
$renewal_order->add_order_note( $note );
}
diff --git a/includes/exceptions/class-api-merchant-exception.php b/includes/exceptions/class-api-merchant-exception.php
new file mode 100644
index 00000000000..ac10bd271bc
--- /dev/null
+++ b/includes/exceptions/class-api-merchant-exception.php
@@ -0,0 +1,49 @@
+merchant_message = $merchant_message;
+
+ parent::__construct( $message, $error_code, $http_code, $error_type, $decline_code, $code, $previous );
+ }
+
+ /**
+ * Returns the merchant message.
+ *
+ * @return string Merchant message.
+ */
+ public function get_merchant_message(): string {
+ return $this->merchant_message;
+ }
+}
diff --git a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php
index 672f2584c67..86d1a82c54d 100644
--- a/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php
+++ b/includes/express-checkout/class-wc-payments-express-checkout-button-helper.php
@@ -415,7 +415,7 @@ public function should_show_express_checkout_button() {
return true;
}
- // Non-shipping product and billing is calculated based on shopper billing addres. Excludes Pay for Order page.
+ // Non-shipping product and tax is calculated based on shopper billing address. Excludes Pay for Order page.
if (
// If the product doesn't needs shipping.
(
@@ -426,8 +426,10 @@ public function should_show_express_checkout_button() {
( ( $this->is_cart() || $this->is_checkout() ) && ! WC()->cart->needs_shipping() )
)
- // ...and billing is calculated based on billing address.
- && wc_tax_enabled() && 'billing' === get_option( 'woocommerce_tax_based_on' )
+ // ...and tax is calculated based on billing address.
+ && wc_tax_enabled()
+ && 'billing' === get_option( 'woocommerce_tax_based_on' )
+ && 'yes' !== get_option( 'woocommerce_prices_include_tax' )
) {
return false;
}
diff --git a/includes/multi-currency/Compatibility/WooCommerceFedEx.php b/includes/multi-currency/Compatibility/WooCommerceFedEx.php
index 8a38d058e40..15c25b4ba27 100644
--- a/includes/multi-currency/Compatibility/WooCommerceFedEx.php
+++ b/includes/multi-currency/Compatibility/WooCommerceFedEx.php
@@ -8,13 +8,25 @@
namespace WCPay\MultiCurrency\Compatibility;
use WCPay\MultiCurrency\MultiCurrency;
-use WCPay\MultiCurrency\Utils;
/**
* Class that controls Multi Currency Compatibility with WooCommerce FedEx Plugin.
*/
class WooCommerceFedEx extends BaseCompatibility {
+ /**
+ * Calls to look for in the backtrace when determining whether
+ * to return store currency or skip converting product prices.
+ */
+ private const WC_SHIPPING_FEDEX_CALLS = [
+ 'WC_Shipping_Fedex->set_settings',
+ 'WC_Shipping_Fedex->per_item_shipping',
+ 'WC_Shipping_Fedex->box_shipping',
+ 'WC_Shipping_Fedex->get_fedex_api_request',
+ 'WC_Shipping_Fedex->get_fedex_requests',
+ 'WC_Shipping_Fedex->process_result',
+ ];
+
/**
* Init the class.
*
@@ -23,10 +35,31 @@ class WooCommerceFedEx extends BaseCompatibility {
public function init() {
// Add needed actions and filters if FedEx is active.
if ( class_exists( 'WC_Shipping_Fedex_Init' ) ) {
+ add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ] );
add_filter( MultiCurrency::FILTER_PREFIX . 'should_return_store_currency', [ $this, 'should_return_store_currency' ] );
}
}
+ /**
+ * Checks to see if the product's price should be converted.
+ *
+ * @param bool $return Whether to convert the product's price or not. Default is true.
+ *
+ * @return bool True if it should be converted.
+ */
+ public function should_convert_product_price( bool $return ): bool {
+ // If it's already false, return it.
+ if ( ! $return ) {
+ return $return;
+ }
+
+ if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) {
+ return false;
+ }
+
+ return $return;
+ }
+
/**
* Determine whether to return the store currency or not.
*
@@ -40,15 +73,7 @@ public function should_return_store_currency( bool $return ): bool {
return $return;
}
- $calls = [
- 'WC_Shipping_Fedex->set_settings',
- 'WC_Shipping_Fedex->per_item_shipping',
- 'WC_Shipping_Fedex->box_shipping',
- 'WC_Shipping_Fedex->get_fedex_api_request',
- 'WC_Shipping_Fedex->get_fedex_requests',
- 'WC_Shipping_Fedex->process_result',
- ];
- if ( $this->utils->is_call_in_backtrace( $calls ) ) {
+ if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) {
return true;
}
diff --git a/includes/wc-payment-api/class-wc-payments-api-client.php b/includes/wc-payment-api/class-wc-payments-api-client.php
index b3adf5bf7eb..e90094d57de 100644
--- a/includes/wc-payment-api/class-wc-payments-api-client.php
+++ b/includes/wc-payment-api/class-wc-payments-api-client.php
@@ -9,6 +9,7 @@
use WCPay\Constants\Intent_Status;
use WCPay\Exceptions\API_Exception;
+use WCPay\Exceptions\API_Merchant_Exception;
use WCPay\Exceptions\Amount_Too_Small_Exception;
use WCPay\Exceptions\Amount_Too_Large_Exception;
use WCPay\Exceptions\Connection_Exception;
@@ -2419,6 +2420,13 @@ protected function check_response_for_errors( $response ) {
);
Logger::error( "$error_message ($error_code)" );
+
+ if ( 'card_declined' === $error_code && isset( $response_body['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message'] ) ) {
+ $merchant_message = $response_body['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message'];
+
+ throw new API_Merchant_Exception( $message, $error_code, $response_code, $merchant_message, $error_type, $decline_code );
+ }
+
throw new API_Exception( $message, $error_code, $response_code, $error_type, $decline_code );
}
}
diff --git a/tests/fixtures/captured-payments/discount.json b/tests/fixtures/captured-payments/discount.json
index 2fa6a911d74..5bf6f936c45 100644
--- a/tests/fixtures/captured-payments/discount.json
+++ b/tests/fixtures/captured-payments/discount.json
@@ -60,7 +60,7 @@
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
"additional-international": "International card fee: 1%",
- "additional-fx": "Foreign exchange fee: 1%",
+ "additional-fx": "Currency conversion fee: 1%",
"discount": {
"label": "Discount",
"variable": "Variable fee: -4.9%",
diff --git a/tests/fixtures/captured-payments/foreign-card.json b/tests/fixtures/captured-payments/foreign-card.json
index 234878b2372..df45c326d62 100644
--- a/tests/fixtures/captured-payments/foreign-card.json
+++ b/tests/fixtures/captured-payments/foreign-card.json
@@ -53,7 +53,7 @@
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
"additional-international": "International card fee: 1%",
- "additional-fx": "Foreign exchange fee: 1%"
+ "additional-fx": "Currency conversion fee: 1%"
},
"netString": "Net payout: $95.47 USD"
}
diff --git a/tests/fixtures/captured-payments/fx-decimal.json b/tests/fixtures/captured-payments/fx-decimal.json
index b95e9318c84..2f065036122 100644
--- a/tests/fixtures/captured-payments/fx-decimal.json
+++ b/tests/fixtures/captured-payments/fx-decimal.json
@@ -45,7 +45,7 @@
"feeString": "Fee (3.9% + $0.30): -$4.39",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
- "additional-fx": "Foreign exchange fee: 1%"
+ "additional-fx": "Currency conversion fee: 1%"
},
"netString": "Net payout: $100.65 USD"
}
diff --git a/tests/fixtures/captured-payments/fx-partial-capture.json b/tests/fixtures/captured-payments/fx-partial-capture.json
index f10ff7aa9e9..691390d4852 100644
--- a/tests/fixtures/captured-payments/fx-partial-capture.json
+++ b/tests/fixtures/captured-payments/fx-partial-capture.json
@@ -57,7 +57,7 @@
"feeString": "Fee (3.51% + £0.21): -$0.88",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
- "additional-fx": "Foreign exchange fee: 1%",
+ "additional-fx": "Currency conversion fee: 1%",
"discount": {
"label": "Discount",
"variable": "Variable fee: -0.39%",
diff --git a/tests/fixtures/captured-payments/fx-with-capped-fee.json b/tests/fixtures/captured-payments/fx-with-capped-fee.json
index 8c1b602a3eb..4c31a8435d7 100644
--- a/tests/fixtures/captured-payments/fx-with-capped-fee.json
+++ b/tests/fixtures/captured-payments/fx-with-capped-fee.json
@@ -55,7 +55,7 @@
"feeBreakdown": {
"base": "Base fee: capped at $6.00",
"additional-international": "International card fee: 1.5%",
- "additional-fx": "Foreign exchange fee: 1%"
+ "additional-fx": "Currency conversion fee: 1%"
},
"netString": "Net payout: $971.04 USD"
}
diff --git a/tests/fixtures/captured-payments/fx.json b/tests/fixtures/captured-payments/fx.json
index 8ceee7b7438..f18ca9297ab 100644
--- a/tests/fixtures/captured-payments/fx.json
+++ b/tests/fixtures/captured-payments/fx.json
@@ -46,7 +46,7 @@
"feeString": "Fee (3.9% + $0.30): -$4.20",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
- "additional-fx": "Foreign exchange fee: 1%"
+ "additional-fx": "Currency conversion fee: 1%"
},
"netString": "Net payout: $95.84 USD"
}
diff --git a/tests/fixtures/captured-payments/jpy-payment.json b/tests/fixtures/captured-payments/jpy-payment.json
index 6c7a6b3ee05..4b4c6c152c9 100644
--- a/tests/fixtures/captured-payments/jpy-payment.json
+++ b/tests/fixtures/captured-payments/jpy-payment.json
@@ -57,7 +57,7 @@
"feeBreakdown": {
"base": "Base fee: 3.6%",
"additional-international": "International card fee: 2%",
- "additional-fx": "Foreign exchange fee: 2%"
+ "additional-fx": "Currency conversion fee: 2%"
},
"netString": "Net payout: ¥4,507 JPY"
}
diff --git a/tests/fixtures/captured-payments/subscription.json b/tests/fixtures/captured-payments/subscription.json
index b7312ea0c02..d0e1fe705e4 100644
--- a/tests/fixtures/captured-payments/subscription.json
+++ b/tests/fixtures/captured-payments/subscription.json
@@ -53,7 +53,7 @@
"feeString": "Fee (4.9% + $0.30): -$3.04",
"feeBreakdown": {
"base": "Base fee: 2.9% + $0.30",
- "additional-fx": "Foreign exchange fee: 1%",
+ "additional-fx": "Currency conversion fee: 1%",
"additional-wcpay-subscription": "Subscription transaction fee: 1%"
},
"netString": "Net payout: $52.87 USD"
diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php
new file mode 100644
index 00000000000..0b10752c0f5
--- /dev/null
+++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-handler.php
@@ -0,0 +1,136 @@
+shipping()->unregister_shipping_methods();
+
+ $this->mock_wcpay_account = $this->createMock( WC_Payments_Account::class );
+ $this->mock_wcpay_gateway = $this->createMock( WC_Payment_Gateway_WCPay::class );
+ $this->mock_ece_button_helper = $this->createMock( WC_Payments_Express_Checkout_Button_Helper::class );
+ $this->mock_express_checkout_ajax_handler = $this->createMock( WC_Payments_Express_Checkout_Ajax_Handler::class );
+
+ $this->system_under_test = new WC_Payments_Express_Checkout_Button_Handler(
+ $this->mock_wcpay_account,
+ $this->mock_wcpay_gateway,
+ $this->mock_ece_button_helper,
+ $this->mock_express_checkout_ajax_handler
+ );
+
+ // Set up shipping zones and methods.
+ $this->zone = new WC_Shipping_Zone();
+ $this->zone->set_zone_name( 'Worldwide' );
+ $this->zone->set_zone_order( 1 );
+ $this->zone->save();
+
+ $flat_rate = $this->zone->add_shipping_method( 'flat_rate' );
+ $this->flat_rate_id = $flat_rate;
+
+ $local_pickup = $this->zone->add_shipping_method( 'local_pickup' );
+ $this->local_pickup_id = $local_pickup;
+ }
+
+ public function tear_down() {
+ parent::tear_down();
+
+ // Clean up shipping zones and methods.
+ $this->zone->delete();
+ }
+
+ public function test_filter_cart_needs_shipping_address_regular_products() {
+ $this->assertEquals(
+ true,
+ $this->system_under_test->filter_cart_needs_shipping_address( true ),
+ 'Should not modify shipping address requirement for regular products'
+ );
+ }
+
+
+ public function test_filter_cart_needs_shipping_address_subscription_products() {
+ WC_Subscriptions_Cart::set_cart_contains_subscription( true );
+ $this->mock_ece_button_helper->method( 'is_checkout' )->willReturn( true );
+
+ $this->zone->delete_shipping_method( $this->flat_rate_id );
+ $this->zone->delete_shipping_method( $this->local_pickup_id );
+
+ $this->assertFalse(
+ $this->system_under_test->filter_cart_needs_shipping_address( true ),
+ 'Should not require shipping address for subscription without shipping methods'
+ );
+
+ remove_filter( 'woocommerce_shipping_method_count', '__return_zero' );
+ WC_Subscriptions_Cart::set_cart_contains_subscription( false );
+ }
+}
diff --git a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php
index 2432c61172c..8006faac78f 100644
--- a/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php
+++ b/tests/unit/express-checkout/test-class-wc-payments-express-checkout-button-helper.php
@@ -28,13 +28,6 @@ class WC_Payments_Express_Checkout_Button_Helper_Test extends WCPAY_UnitTestCase
*/
private $mock_wcpay_account;
- /**
- * Express Checkout Helper instance.
- *
- * @var WC_Payments_Express_Checkout_Button_Helper
- */
- private $express_checkout_helper;
-
/**
* Test shipping zone.
*
@@ -61,21 +54,7 @@ class WC_Payments_Express_Checkout_Button_Helper_Test extends WCPAY_UnitTestCase
*
* @var WC_Payments_Express_Checkout_Button_Helper
*/
- private $mock_express_checkout_helper;
-
- /**
- * Express Checkout Ajax Handler instance.
- *
- * @var WC_Payments_Express_Checkout_Ajax_Handler
- */
- private $mock_express_checkout_ajax_handler;
-
- /**
- * Express Checkout ECE Button Handler instance.
- *
- * @var WC_Payments_Express_Checkout_Button_Handler
- */
- private $mock_express_checkout_ece_button_handler;
+ private $system_under_test;
/**
* Test product to add to the cart
@@ -92,23 +71,7 @@ public function set_up() {
$this->mock_wcpay_account = $this->createMock( WC_Payments_Account::class );
$this->mock_wcpay_gateway = $this->make_wcpay_gateway();
- $this->mock_express_checkout_helper = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account );
- $this->mock_express_checkout_ajax_handler = $this->getMockBuilder( WC_Payments_Express_Checkout_Ajax_Handler::class )
- ->setConstructorArgs(
- [
- $this->mock_express_checkout_helper,
- ]
- )
- ->getMock();
-
- $this->mock_ece_button_helper = $this->getMockBuilder( WC_Payments_Express_Checkout_Button_Helper::class )
- ->setConstructorArgs(
- [
- $this->mock_wcpay_gateway,
- $this->mock_wcpay_account,
- ]
- )
- ->getMock();
+ $this->system_under_test = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account );
WC_Helper_Shipping::delete_simple_flat_rate();
$zone = new WC_Shipping_Zone();
@@ -128,7 +91,7 @@ public function set_up() {
WC()->session->init();
WC()->cart->add_to_cart( $this->simple_product->get_id(), 1 );
- $this->mock_express_checkout_helper->update_shipping_method( [ self::get_shipping_option_rate_id( $this->flat_rate_id ) ] );
+ $this->system_under_test->update_shipping_method( [ self::get_shipping_option_rate_id( $this->flat_rate_id ) ] );
WC()->cart->calculate_totals();
}
@@ -195,34 +158,34 @@ public function test_common_get_button_settings() {
'height' => '48',
'radius' => '',
],
- $this->mock_express_checkout_helper->get_common_button_settings()
+ $this->system_under_test->get_common_button_settings()
);
}
public function test_cart_prices_include_tax_with_tax_disabled() {
add_filter( 'wc_tax_enabled', '__return_false' );
- $this->assertTrue( $this->mock_express_checkout_helper->cart_prices_include_tax() );
+ $this->assertTrue( $this->system_under_test->cart_prices_include_tax() );
}
public function test_cart_prices_include_tax_with_tax_enabled_and_display_incl() {
add_filter( 'wc_tax_enabled', '__return_true' ); // reset in tear_down.
add_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] ); // reset in tear_down.
- $this->assertTrue( $this->mock_express_checkout_helper->cart_prices_include_tax() );
+ $this->assertTrue( $this->system_under_test->cart_prices_include_tax() );
}
public function test_cart_prices_include_tax_with_tax_enabled_and_display_excl() {
add_filter( 'wc_tax_enabled', '__return_true' ); // reset in tear_down.
add_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_excl' ] ); // reset in tear_down.
- $this->assertFalse( $this->mock_express_checkout_helper->cart_prices_include_tax() );
+ $this->assertFalse( $this->system_under_test->cart_prices_include_tax() );
}
public function test_get_total_label() {
$this->mock_wcpay_account->method( 'get_statement_descriptor' )
->willReturn( 'Google Pay' );
- $result = $this->mock_express_checkout_helper->get_total_label();
+ $result = $this->system_under_test->get_total_label();
$this->assertEquals( 'Google Pay (via WooCommerce)', $result );
}
@@ -238,49 +201,54 @@ function () {
}
);
- $result = $this->mock_express_checkout_helper->get_total_label();
+ $result = $this->system_under_test->get_total_label();
$this->assertEquals( 'Google Pay (via WooPayments)', $result );
remove_all_filters( 'wcpay_payment_request_total_label_suffix' );
}
- public function test_filter_cart_needs_shipping_address_returns_false() {
- sleep( 1 );
- $this->zone->delete_shipping_method( $this->flat_rate_id );
- $this->zone->delete_shipping_method( $this->local_pickup_id );
+ public function test_should_show_express_checkout_button_for_non_shipping_but_price_includes_tax() {
+ $this->mock_wcpay_account
+ ->method( 'is_stripe_connected' )
+ ->willReturn( true );
- WC_Subscriptions_Cart::set_cart_contains_subscription( true );
+ WC_Payments::mode()->dev();
- $this->mock_ece_button_helper
- ->method( 'is_product' )
- ->willReturn( true );
+ add_filter( 'woocommerce_is_checkout', '__return_true' );
+ add_filter( 'wc_shipping_enabled', '__return_false' );
+ add_filter( 'wc_tax_enabled', '__return_true' );
- $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler(
- $this->mock_wcpay_account,
- $this->mock_wcpay_gateway,
- $this->mock_ece_button_helper,
- $this->mock_express_checkout_ajax_handler
- );
+ update_option( 'woocommerce_tax_based_on', 'billing' );
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
- $this->assertFalse( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) );
+ $this->assertTrue( $this->system_under_test->should_show_express_checkout_button() );
+
+ remove_filter( 'woocommerce_is_checkout', '__return_true' );
+ remove_filter( 'wc_tax_enabled', '__return_true' );
+ remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] );
}
- public function test_filter_cart_needs_shipping_address_returns_true() {
- WC_Subscriptions_Cart::set_cart_contains_subscription( true );
- $this->mock_ece_button_helper
- ->method( 'is_product' )
+ public function test_should_not_show_express_checkout_button_for_non_shipping_but_price_does_not_include_tax() {
+ $this->mock_wcpay_account
+ ->method( 'is_stripe_connected' )
->willReturn( true );
- $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler(
- $this->mock_wcpay_account,
- $this->mock_wcpay_gateway,
- $this->mock_ece_button_helper,
- $this->mock_express_checkout_ajax_handler
- );
+ WC_Payments::mode()->dev();
+
+ add_filter( 'woocommerce_is_checkout', '__return_true' );
+ add_filter( 'wc_shipping_enabled', '__return_false' );
+ add_filter( 'wc_tax_enabled', '__return_true' );
+
+ update_option( 'woocommerce_tax_based_on', 'billing' );
+ update_option( 'woocommerce_prices_include_tax', 'no' );
- $this->assertTrue( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) );
+ $this->assertFalse( $this->system_under_test->should_show_express_checkout_button() );
+
+ remove_filter( 'woocommerce_is_checkout', '__return_true' );
+ remove_filter( 'wc_tax_enabled', '__return_true' );
+ remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_incl' ] );
}
/**
diff --git a/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php b/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php
index 60e130390fd..e52927230ca 100644
--- a/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php
+++ b/tests/unit/multi-currency/compatibility/test-class-woocommerce-fedex.php
@@ -35,6 +35,20 @@ class WCPay_Multi_Currency_WooCommerceFedEx_Tests extends WCPAY_UnitTestCase {
*/
private $woocommerce_fedex;
+ /**
+ * Calls to check in the backtrace.
+ *
+ * @var array
+ */
+ private $woocommerce_fedex_calls = [
+ 'WC_Shipping_Fedex->set_settings',
+ 'WC_Shipping_Fedex->per_item_shipping',
+ 'WC_Shipping_Fedex->box_shipping',
+ 'WC_Shipping_Fedex->get_fedex_api_request',
+ 'WC_Shipping_Fedex->get_fedex_requests',
+ 'WC_Shipping_Fedex->process_result',
+ ];
+
/**
* Pre-test setup
*/
@@ -54,37 +68,45 @@ public function test_should_return_store_currency_returns_true_if_true_passed()
// If the calls are found, it should return true.
public function test_should_return_store_currency_returns_true_if_calls_found() {
- $calls = [
- 'WC_Shipping_Fedex->set_settings',
- 'WC_Shipping_Fedex->per_item_shipping',
- 'WC_Shipping_Fedex->box_shipping',
- 'WC_Shipping_Fedex->get_fedex_api_request',
- 'WC_Shipping_Fedex->get_fedex_requests',
- 'WC_Shipping_Fedex->process_result',
- ];
$this->mock_utils
->expects( $this->once() )
->method( 'is_call_in_backtrace' )
- ->with( $calls )
+ ->with( $this->woocommerce_fedex_calls )
->willReturn( true );
+
$this->assertTrue( $this->woocommerce_fedex->should_return_store_currency( false ) );
}
- // If the calls are found, it should return true.
+ // If the calls are not found, it should return false.
public function test_should_return_store_currency_returns_false_if_no_calls_found() {
- $calls = [
- 'WC_Shipping_Fedex->set_settings',
- 'WC_Shipping_Fedex->per_item_shipping',
- 'WC_Shipping_Fedex->box_shipping',
- 'WC_Shipping_Fedex->get_fedex_api_request',
- 'WC_Shipping_Fedex->get_fedex_requests',
- 'WC_Shipping_Fedex->process_result',
- ];
$this->mock_utils
->expects( $this->once() )
->method( 'is_call_in_backtrace' )
- ->with( $calls )
+ ->with( $this->woocommerce_fedex_calls )
->willReturn( false );
+
$this->assertFalse( $this->woocommerce_fedex->should_return_store_currency( false ) );
}
+
+ // If true is passed to should_convert_product_price and no calls are found, it should return true.
+ public function test_should_convert_product_price_returns_true_if_true_passed_and_no_calls_found() {
+ $this->mock_utils
+ ->expects( $this->once() )
+ ->method( 'is_call_in_backtrace' )
+ ->with( $this->woocommerce_fedex_calls )
+ ->willReturn( false );
+
+ $this->assertTrue( $this->woocommerce_fedex->should_convert_product_price( true ) );
+ }
+
+ // If calls are found, should_convert_product_price should return false even if true was passed.
+ public function test_should_convert_product_price_returns_false_if_calls_found() {
+ $this->mock_utils
+ ->expects( $this->once() )
+ ->method( 'is_call_in_backtrace' )
+ ->with( $this->woocommerce_fedex_calls )
+ ->willReturn( true );
+
+ $this->assertFalse( $this->woocommerce_fedex->should_convert_product_price( true ) );
+ }
}
diff --git a/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php b/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php
index 3fc4a56c8f6..fb95bcf1591 100644
--- a/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php
+++ b/tests/unit/wc-payment-api/test-class-wc-payments-api-client.php
@@ -7,7 +7,9 @@
use WCPay\Constants\Country_Code;
use WCPay\Constants\Intent_Status;
+use WCPay\Core\Server\Request\Create_And_Confirm_Intention;
use WCPay\Exceptions\API_Exception;
+use WCPay\Exceptions\API_Merchant_Exception;
use WCPay\Internal\Logger;
use WCPay\Exceptions\Connection_Exception;
use WCPay\Fraud_Prevention\Fraud_Prevention_Service;
@@ -1195,6 +1197,24 @@ public function test_get_tracking_info() {
$this->assertEquals( $expect, $result );
}
+ public function test_throws_api_merchant_exception() {
+ $mock_response = [];
+ $mock_response['error']['code'] = 'card_declined';
+ $mock_response['error']['payment_intent']['charges']['data'][0]['outcome']['seller_message'] = 'Bank declined';
+ $this->set_http_mock_response(
+ 401,
+ $mock_response
+ );
+
+ try {
+ // This is a dummy call to trigger the response so that our test can validate the exception.
+ $this->payments_api_client->create_subscription();
+ } catch ( API_Merchant_Exception $e ) {
+ $this->assertSame( 'card_declined', $e->get_error_code() );
+ $this->assertSame( 'Bank declined', $e->get_merchant_message() );
+ }
+ }
+
/**
* Set up http mock response.
*