diff --git a/changelog/fix-9456-bnpl-respect-limits-per-currency b/changelog/fix-9456-bnpl-respect-limits-per-currency new file mode 100644 index 00000000000..be78120fabc --- /dev/null +++ b/changelog/fix-9456-bnpl-respect-limits-per-currency @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Prevent dead space on product pages when no BNPL offers are available. diff --git a/client/product-details/bnpl-site-messaging/index.js b/client/product-details/bnpl-site-messaging/index.js index 36cb845fb71..1bbe51e0edf 100644 --- a/client/product-details/bnpl-site-messaging/index.js +++ b/client/product-details/bnpl-site-messaging/index.js @@ -55,12 +55,11 @@ export const initializeBnplSiteMessaging = async () => { isCart, isCartBlock, cartTotal, - minimumOrderAmount, + isBnplAvailable, } = window.wcpayStripeSiteMessaging; let amount; let elementLocation = 'bnplProductPage'; - const minOrderAmount = parseInt( minimumOrderAmount, 10 ) || 0; const paymentMessageContainer = document.getElementById( 'payment-method-message' ); @@ -71,7 +70,7 @@ export const initializeBnplSiteMessaging = async () => { } else { amount = parseInt( productVariations.base_product.amount, 10 ) || 0; - if ( amount < minOrderAmount ) { + if ( ! isBnplAvailable ) { paymentMessageContainer.style.setProperty( 'display', 'none' ); } } diff --git a/client/product-details/index.js b/client/product-details/index.js index 93358a26f3b..b686e1a342f 100644 --- a/client/product-details/index.js +++ b/client/product-details/index.js @@ -104,13 +104,29 @@ jQuery( async function ( $ ) { 'get_cart_total' ), { - security: window.wcpayStripeSiteMessaging.nonce, + security: window.wcpayStripeSiteMessaging.nonce.get_cart_total, + } + ); + }; + + const isBnplAvailable = ( price, currency, country ) => { + return request( + buildAjaxURL( + window.wcpayStripeSiteMessaging.wcAjaxUrl, + 'check_bnpl_availability' + ), + { + security: + window.wcpayStripeSiteMessaging.nonce.is_bnpl_available, + price: price, + currency: currency, + country: country, } ); }; // Update BNPL message based on the quantity change - quantityInput.on( 'change', ( event ) => { + quantityInput.on( 'change', async ( event ) => { let amount = baseProductAmount; const variationId = $( VARIATION_ID_SELECTOR ).val(); @@ -123,6 +139,22 @@ jQuery( async function ( $ ) { } updateBnplPaymentMessage( amount, productCurrency, event.target.value ); + + // Check if changes in quantity/price affect BNPL availability and show/hide BNPL messaging accordingly. + try { + const response = await isBnplAvailable( + amount * event.target.value, + productCurrency, + window.wcpayStripeSiteMessaging.country + ); + if ( response.success && response.data.is_available ) { + $( '#payment-method-message' ).slideDown(); + } else { + $( '#payment-method-message' ).slideUp(); + } + } catch { + // Do nothing. + } } ); $( document.body ).on( 'updated_cart_totals', () => { diff --git a/includes/class-wc-payments-payment-method-messaging-element.php b/includes/class-wc-payments-payment-method-messaging-element.php index aeffbc78c61..b279d2b09bf 100644 --- a/includes/class-wc-payments-payment-method-messaging-element.php +++ b/includes/class-wc-payments-payment-method-messaging-element.php @@ -90,6 +90,9 @@ public function init() { 'currency' => $currency_code, ], ]; + + $product_price = $product_variations['base_product']['amount']; + foreach ( $product->get_children() as $variation_id ) { $variation = wc_get_product( $variation_id ); if ( $variation ) { @@ -98,11 +101,13 @@ public function init() { 'amount' => WC_Payments_Utils::prepare_amount( $price, $currency_code ), 'currency' => $currency_code, ]; + + $product_price = $product_variations['base_product']['amount']; } } } - $enabled_upe_payment_methods = $this->gateway->get_payment_method_ids_enabled_at_checkout(); + $enabled_upe_payment_methods = $this->gateway->get_upe_enabled_payment_method_ids(); // Filter non BNPL out of the list of payment methods. $bnpl_payment_methods = array_intersect( $enabled_upe_payment_methods, Payment_Method::BNPL_PAYMENT_METHODS ); @@ -118,26 +123,36 @@ public function init() { WC_Payments::get_file_version( 'dist/product-details.css' ), ); + $country = empty( $billing_country ) ? $store_country : $billing_country; + + $script_data = [ + 'productId' => 'base_product', + 'productVariations' => $product_variations, + 'country' => $country, + 'locale' => WC_Payments_Utils::convert_to_stripe_locale( get_locale() ), + 'accountId' => $this->account->get_stripe_account_id(), + 'publishableKey' => $this->account->get_publishable_key( WC_Payments::mode()->is_test() ), + 'paymentMethods' => array_values( $bnpl_payment_methods ), + 'currencyCode' => $currency_code, + 'isCart' => is_cart(), + 'isCartBlock' => $is_cart_block, + 'cartTotal' => WC_Payments_Utils::prepare_amount( $cart_total, $currency_code ), + 'nonce' => [ + 'get_cart_total' => wp_create_nonce( 'wcpay-get-cart-total' ), + 'is_bnpl_available' => wp_create_nonce( 'wcpay-is-bnpl-available' ), + ], + 'wcAjaxUrl' => WC_AJAX::get_endpoint( '%%endpoint%%' ), + ]; + + if ( $product ) { + $script_data['isBnplAvailable'] = WC_Payments_Utils::is_any_bnpl_method_available( array_values( $bnpl_payment_methods ), $country, $currency_code, $product_price ); + } + // Create script tag with config. wp_localize_script( 'WCPAY_PRODUCT_DETAILS', 'wcpayStripeSiteMessaging', - [ - 'productId' => 'base_product', - 'productVariations' => $product_variations, - 'country' => empty( $billing_country ) ? $store_country : $billing_country, - 'locale' => WC_Payments_Utils::convert_to_stripe_locale( get_locale() ), - 'accountId' => $this->account->get_stripe_account_id(), - 'publishableKey' => $this->account->get_publishable_key( WC_Payments::mode()->is_test() ), - 'paymentMethods' => array_values( $bnpl_payment_methods ), - 'currencyCode' => $currency_code, - 'isCart' => is_cart(), - 'isCartBlock' => $is_cart_block, - 'cartTotal' => WC_Payments_Utils::prepare_amount( $cart_total, $currency_code ), - 'minimumOrderAmount' => WC_Payments_Utils::get_cached_minimum_amount( $currency_code, true ), - 'nonce' => wp_create_nonce( 'wcpay-get-cart-total' ), - 'wcAjaxUrl' => WC_AJAX::get_endpoint( '%%endpoint%%' ), - ] + $script_data ); // Ensure wcpayConfig is available in the page. diff --git a/includes/class-wc-payments-utils.php b/includes/class-wc-payments-utils.php index 755901e15ca..a08df66c0b3 100644 --- a/includes/class-wc-payments-utils.php +++ b/includes/class-wc-payments-utils.php @@ -716,59 +716,163 @@ public static function get_filtered_error_status_code( Exception $e ): int { } /** - * Retrieves Stripe minimum order value authorized per currency. - * The values are based on Stripe's recommendations. - * See https://docs.stripe.com/currencies#minimum-and-maximum-charge-amounts. + * Get the BNPL limits per currency for a specific payment method. * - * @param string $currency The currency. - * - * @return int The minimum amount. + * @param string $payment_method The payment method name ('affirm', 'afterpay_clearpay', or 'klarna'). + * @return array The BNPL limits per currency for the specified payment method. */ - public static function get_stripe_minimum_amount( $currency ) { - switch ( $currency ) { - case 'AED': - case 'MYR': - case 'PLN': - case 'RON': - $minimum_amount = 200; - break; - case 'BGN': - $minimum_amount = 100; - break; - case 'CZK': - $minimum_amount = 1500; - break; - case 'DKK': - $minimum_amount = 250; - break; - case 'GBP': - $minimum_amount = 30; - break; - case 'HKD': - $minimum_amount = 400; - break; - case 'HUF': - $minimum_amount = 17500; - break; - case 'JPY': - $minimum_amount = 5000; - break; - case 'MXN': - case 'THB': - $minimum_amount = 1000; - break; - case 'NOK': - case 'SEK': - $minimum_amount = 300; - break; + public static function get_bnpl_limits_per_currency( $payment_method ) { + switch ( $payment_method ) { + case 'affirm': + return [ + Currency_Code::CANADIAN_DOLLAR => [ + Country_Code::CANADA => [ + 'min' => 5000, + 'max' => 3000000, + ], // Represents CAD 50 - 30,000 CAD. + ], + Currency_Code::UNITED_STATES_DOLLAR => [ + Country_Code::UNITED_STATES => [ + 'min' => 5000, + 'max' => 3000000, + ], + ], // Represents USD 50 - 30,000 USD. + ]; + case 'afterpay_clearpay': + return [ + Currency_Code::AUSTRALIAN_DOLLAR => [ + Country_Code::AUSTRALIA => [ + 'min' => 100, + 'max' => 200000, + ], // Represents AUD 1 - 2,000 AUD. + ], + Currency_Code::CANADIAN_DOLLAR => [ + Country_Code::CANADA => [ + 'min' => 100, + 'max' => 200000, + ], // Represents CAD 1 - 2,000 CAD. + ], + Currency_Code::NEW_ZEALAND_DOLLAR => [ + Country_Code::NEW_ZEALAND => [ + 'min' => 100, + 'max' => 200000, + ], // Represents NZD 1 - 2,000 NZD. + ], + Currency_Code::POUND_STERLING => [ + Country_Code::UNITED_KINGDOM => [ + 'min' => 100, + 'max' => 120000, + ], // Represents GBP 1 - 1,200 GBP. + ], + Currency_Code::UNITED_STATES_DOLLAR => [ + Country_Code::UNITED_STATES => [ + 'min' => 100, + 'max' => 400000, + ], // Represents USD 1 - 4,000 USD. + ], + ]; + case 'klarna': + return [ + Currency_Code::UNITED_STATES_DOLLAR => [ + Country_Code::UNITED_STATES => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents USD 1 - 10,000 USD. + ], + Currency_Code::POUND_STERLING => [ + Country_Code::UNITED_KINGDOM => [ + 'min' => 100, + 'max' => 500000, + ], // Represents GBP 1 - 5,000 GBP. + ], + Currency_Code::EURO => [ + Country_Code::AUSTRIA => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents EUR 1 - 10,000 EUR. + Country_Code::BELGIUM => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents EUR 1 - 10,000 EUR. + Country_Code::GERMANY => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents EUR 1 - 10,000 EUR. + Country_Code::NETHERLANDS => [ + 'min' => 100, + 'max' => 500000, + ], // Represents EUR 1 - 5,000 EUR. + Country_Code::FINLAND => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents EUR 1 - 10,000 EUR. + Country_Code::SPAIN => [ + 'min' => 100, + 'max' => 1000000, + ], // Represents EUR 1 - 10,000 EUR. + Country_Code::IRELAND => [ + 'min' => 100, + 'max' => 400000, + ], // Represents EUR 1 - 4,000 EUR. + Country_Code::ITALY => [ + 'min' => 100, + 'max' => 400000, + ], // Represents EUR 1 - 4,000 EUR. + Country_Code::FRANCE => [ + 'min' => 100, + 'max' => 400000, + ], // Represents EUR 1 - 4,000 EUR. + ], + Currency_Code::DANISH_KRONE => [ + Country_Code::DENMARK => [ + 'min' => 100, + 'max' => 10000000, + ], // Represents DKK 1 - 100,000 DKK. + ], + Currency_Code::NORWEGIAN_KRONE => [ + Country_Code::NORWAY => [ + 'min' => 100, + 'max' => 10000000, + ], // Represents NOK 1 - 100,000 NOK. + ], + Currency_Code::SWEDISH_KRONA => [ + Country_Code::SWEDEN => [ + 'min' => 100, + 'max' => 10000000, + ], // Represents SEK 1 - 100,000 SEK. + ], + ]; default: - $minimum_amount = 50; - break; + return []; } + } - self::cache_minimum_amount( $currency, $minimum_amount ); + /** + * Check if any BNPL method is available for a given country, currency, and price. + * + * @param array $enabled_methods Array of enabled BNPL methods. + * @param string $country_code Country code. + * @param string $currency_code Currency code. + * @param float $price Product price. + * @return bool True if any BNPL method is available, false otherwise. + */ + public static function is_any_bnpl_method_available( array $enabled_methods, string $country_code, string $currency_code, float $price ): bool { + $price_in_cents = $price; + + foreach ( $enabled_methods as $method ) { + $limits = self::get_bnpl_limits_per_currency( $method ); - return $minimum_amount; + if ( isset( $limits[ $currency_code ][ $country_code ] ) ) { + $min_amount = $limits[ $currency_code ][ $country_code ]['min']; + $max_amount = $limits[ $currency_code ][ $country_code ]['max']; + + if ( $price_in_cents >= $min_amount && $price_in_cents <= $max_amount ) { + return true; + } + } + } + + return false; } /** @@ -785,20 +889,12 @@ public static function cache_minimum_amount( $currency, $amount ) { * Checks if there is a minimum amount required for transactions in a given currency. * * @param string $currency The currency to check for. - * @param bool $fallback_to_local_list Whether to fallback to the local Stripe list if the cached value is not available. * * @return int|null Either the minimum amount, or `null` if not available. */ - public static function get_cached_minimum_amount( $currency, $fallback_to_local_list = false ) { + public static function get_cached_minimum_amount( $currency ) { $cached = get_transient( 'wcpay_minimum_amount_' . strtolower( $currency ) ); - - if ( (int) $cached ) { - return (int) $cached; - } elseif ( $fallback_to_local_list ) { - return self::get_stripe_minimum_amount( $currency ); - } - - return null; + return (int) $cached ? (int) $cached : null; } /** diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 42923e906d5..8f8ef1f645d 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -618,6 +618,7 @@ function () { add_action( 'woocommerce_proceed_to_checkout', [ __CLASS__, 'load_stripe_bnpl_site_messaging' ], 5 ); add_action( 'woocommerce_blocks_enqueue_cart_block_scripts_after', [ __CLASS__, 'load_stripe_bnpl_site_messaging' ] ); add_action( 'wc_ajax_wcpay_get_cart_total', [ __CLASS__, 'ajax_get_cart_total' ] ); + add_action( 'wc_ajax_wcpay_check_bnpl_availability', [ __CLASS__, 'ajax_check_bnpl_availability' ] ); } } @@ -1731,6 +1732,26 @@ public static function ajax_get_cart_total() { wp_send_json( [ 'total' => WC_Payments_Utils::prepare_amount( $cart_total, $currency_code ) ] ); } + /** + * Check if BNPL is available for the given price, currency, and country. + */ + public static function ajax_check_bnpl_availability() { + check_ajax_referer( 'wcpay-is-bnpl-available', 'security' ); + + $price = floatval( $_POST['price'] ); + $currency = sanitize_text_field( wp_unslash( $_POST['currency'] ) ); + $country = sanitize_text_field( wp_unslash( $_POST['country'] ) ); + + $enabled_bnpl_payment_methods = array_intersect( + Payment_Method::BNPL_PAYMENT_METHODS, + self::get_gateway()->get_upe_enabled_payment_method_ids() + ); + + $is_available = WC_Payments_Utils::is_any_bnpl_method_available( $enabled_bnpl_payment_methods, $country, $currency, $price ); + + wp_send_json_success( [ 'is_available' => $is_available ] ); + } + /** * Adds custom email field. */ diff --git a/includes/payment-methods/class-affirm-payment-method.php b/includes/payment-methods/class-affirm-payment-method.php index f65794e555d..0d2656fd700 100644 --- a/includes/payment-methods/class-affirm-payment-method.php +++ b/includes/payment-methods/class-affirm-payment-method.php @@ -35,20 +35,7 @@ public function __construct( $token_service ) { $this->dark_icon_url = plugins_url( 'assets/images/payment-methods/affirm-logo-dark.svg', WCPAY_PLUGIN_FILE ); $this->currencies = [ Currency_Code::UNITED_STATES_DOLLAR, Currency_Code::CANADIAN_DOLLAR ]; $this->accept_only_domestic_payment = true; - $this->limits_per_currency = [ - Currency_Code::CANADIAN_DOLLAR => [ - Country_Code::CANADA => [ - 'min' => 5000, - 'max' => 3000000, - ], // Represents CAD 50 - 30,000 CAD. - ], - Currency_Code::UNITED_STATES_DOLLAR => [ - Country_Code::UNITED_STATES => [ - 'min' => 5000, - 'max' => 3000000, - ], // Represents USD 50 - 30,000 USD. - ], - ]; + $this->limits_per_currency = WC_Payments_Utils::get_bnpl_limits_per_currency( self::PAYMENT_METHOD_STRIPE_ID ); $this->countries = [ Country_Code::UNITED_STATES, Country_Code::CANADA ]; } diff --git a/includes/payment-methods/class-afterpay-payment-method.php b/includes/payment-methods/class-afterpay-payment-method.php index f4d1ef541aa..89062a4a7f9 100644 --- a/includes/payment-methods/class-afterpay-payment-method.php +++ b/includes/payment-methods/class-afterpay-payment-method.php @@ -34,38 +34,7 @@ public function __construct( $token_service ) { $this->currencies = [ Currency_Code::UNITED_STATES_DOLLAR, Currency_Code::CANADIAN_DOLLAR, Currency_Code::AUSTRALIAN_DOLLAR, Currency_Code::NEW_ZEALAND_DOLLAR, Currency_Code::POUND_STERLING ]; $this->countries = [ Country_Code::UNITED_STATES, Country_Code::CANADA, Country_Code::AUSTRALIA, Country_Code::NEW_ZEALAND, Country_Code::UNITED_KINGDOM ]; $this->accept_only_domestic_payment = true; - $this->limits_per_currency = [ - Currency_Code::AUSTRALIAN_DOLLAR => [ - Country_Code::AUSTRALIA => [ - 'min' => 100, - 'max' => 200000, - ], // Represents AUD 1 - 2,000 AUD. - ], - Currency_Code::CANADIAN_DOLLAR => [ - Country_Code::CANADA => [ - 'min' => 100, - 'max' => 200000, - ], // Represents CAD 1 - 2,000 CAD. - ], - Currency_Code::NEW_ZEALAND_DOLLAR => [ - Country_Code::NEW_ZEALAND => [ - 'min' => 100, - 'max' => 200000, - ], // Represents NZD 1 - 2,000 NZD. - ], - Currency_Code::POUND_STERLING => [ - Country_Code::UNITED_KINGDOM => [ - 'min' => 100, - 'max' => 120000, - ], // Represents GBP 1 - 1,200 GBP. - ], - Currency_Code::UNITED_STATES_DOLLAR => [ - Country_Code::UNITED_STATES => [ - 'min' => 100, - 'max' => 400000, - ], // Represents USD 1 - 4,000 USD. - ], - ]; + $this->limits_per_currency = WC_Payments_Utils::get_bnpl_limits_per_currency( self::PAYMENT_METHOD_STRIPE_ID ); } /** diff --git a/includes/payment-methods/class-klarna-payment-method.php b/includes/payment-methods/class-klarna-payment-method.php index 44b93f56979..1e439a8d476 100644 --- a/includes/payment-methods/class-klarna-payment-method.php +++ b/includes/payment-methods/class-klarna-payment-method.php @@ -34,76 +34,7 @@ public function __construct( $token_service ) { $this->currencies = [ Currency_Code::UNITED_STATES_DOLLAR, Currency_Code::POUND_STERLING, Currency_Code::EURO, Currency_Code::DANISH_KRONE, Currency_Code::NORWEGIAN_KRONE, Currency_Code::SWEDISH_KRONA ]; $this->accept_only_domestic_payment = true; $this->countries = [ Country_Code::UNITED_STATES, Country_Code::UNITED_KINGDOM, Country_Code::AUSTRIA, Country_Code::GERMANY, Country_Code::NETHERLANDS, Country_Code::BELGIUM, Country_Code::SPAIN, Country_Code::ITALY, Country_Code::IRELAND, Country_Code::DENMARK, Country_Code::FINLAND, Country_Code::NORWAY, Country_Code::SWEDEN, Country_Code::FRANCE ]; - $this->limits_per_currency = [ - Currency_Code::UNITED_STATES_DOLLAR => [ - Country_Code::UNITED_STATES => [ - 'min' => 0, - 'max' => 1000000, - ], - ], - Currency_Code::POUND_STERLING => [ - Country_Code::UNITED_KINGDOM => [ - 'min' => 0, - 'max' => 1150000, - ], - ], - Currency_Code::EURO => [ - Country_Code::AUSTRIA => [ - 'min' => 1, - 'max' => 1000000, - ], - Country_Code::BELGIUM => [ - 'min' => 1, - 'max' => 1000000, - ], - Country_Code::GERMANY => [ - 'min' => 1, - 'max' => 1000000, - ], - Country_Code::NETHERLANDS => [ - 'min' => 1, - 'max' => 1500000, - ], - Country_Code::FINLAND => [ - 'min' => 0, - 'max' => 1000000, - ], - Country_Code::SPAIN => [ - 'min' => 0, - 'max' => 1000000, - ], - Country_Code::IRELAND => [ - 'min' => 0, - 'max' => 400000, - ], - Country_Code::ITALY => [ - 'min' => 0, - 'max' => 1000000, - ], - Country_Code::FRANCE => [ - 'min' => 3500, - 'max' => 400000, - ], - ], - Currency_Code::DANISH_KRONE => [ - Country_Code::DENMARK => [ - 'min' => 100, - 'max' => 100000000, - ], - ], - Currency_Code::NORWEGIAN_KRONE => [ - Country_Code::NORWAY => [ - 'min' => 0, - 'max' => 100000000, - ], - ], - Currency_Code::SWEDISH_KRONA => [ - Country_Code::SWEDEN => [ - 'min' => 0, - 'max' => 15000000, - ], - ], - ]; + $this->limits_per_currency = WC_Payments_Utils::get_bnpl_limits_per_currency( self::PAYMENT_METHOD_STRIPE_ID ); } /** diff --git a/tests/unit/test-class-wc-payments-utils.php b/tests/unit/test-class-wc-payments-utils.php index a7aba06f9ab..f6f5affd667 100644 --- a/tests/unit/test-class-wc-payments-utils.php +++ b/tests/unit/test-class-wc-payments-utils.php @@ -931,12 +931,6 @@ public function test_get_cached_minimum_amount_returns_null_without_cache() { $this->assertNull( $result ); } - public function test_get_cached_minimum_amount_returns_amount_fallbacking_from_stripe_list() { - delete_transient( 'wcpay_minimum_amount_usd' ); - $result = WC_Payments_Utils::get_cached_minimum_amount( 'usd', true ); - $this->assertSame( 50, $result ); - } - public function test_get_last_refund_from_order_id_returns_correct_refund() { $order = WC_Helper_Order::create_order(); $refund_1 = wc_create_refund( [ 'order_id' => $order->get_id() ] ); @@ -1119,4 +1113,86 @@ public function test_is_store_api_request_with_malformed_url() { $this->assertFalse( WC_Payments_Utils::is_store_api_request() ); } + + public function test_is_any_bnpl_method_available() { + // Price within range for Afterpay/Clearpay in the US. + $this->assertTrue( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay' ], + 'US', + 'USD', + 100 + ) + ); + + // Price within range for Klarna in the US. + $this->assertTrue( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'klarna' ], + 'US', + 'USD', + 500 + ) + ); + + // Price below minimum for all methods. + $this->assertFalse( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay', 'klarna', 'affirm' ], + 'US', + 'USD', + 0.50 + ) + ); + + // Price above maximum for all methods. + $this->assertFalse( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay', 'klarna', 'affirm' ], + 'US', + 'USD', + 4000000 + ) + ); + + // Unsupported country. + $this->assertFalse( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay', 'klarna', 'affirm' ], + 'RU', + 'RUB', + 100 + ) + ); + + // Unsupported currency. + $this->assertFalse( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay', 'klarna', 'affirm' ], + 'US', + 'JPY', + 100 + ) + ); + + // Empty enabled methods array. + $this->assertFalse( + WC_Payments_Utils::is_any_bnpl_method_available( + [], + 'US', + 'USD', + 100 + ) + ); + + // Different country, same currency (Afterpay/Clearpay in Canada). + $this->assertTrue( + WC_Payments_Utils::is_any_bnpl_method_available( + [ 'afterpay_clearpay' ], + 'CA', + 'CAD', + 100 + ) + ); + } }