Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix BNPL payment method messaging loading state #9355

Merged
merged 8 commits into from
Aug 30, 2024
Merged
4 changes: 4 additions & 0 deletions changelog/fix-9244-pmme-skeleton
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Prevent preload of BNPL messaging if minimum order amount isn't hit.
32 changes: 20 additions & 12 deletions client/product-details/bnpl-site-messaging/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ import { getAppearance, getFontRulesFromPage } from 'wcpay/checkout/upe-styles';
import { getUPEConfig } from 'wcpay/utils/checkout';
import apiRequest from 'wcpay/checkout/utils/request';

/**
* Initializes the appearance of the payment element by retrieving the UPE configuration
* from the API and saving the appearance if it doesn't exist. If the appearance already exists,
* it is simply returned.
*
* @param {Object} api The API object used to save the UPE configuration.
* @return {Promise<Object>} The appearance object for the UPE.
*/
const elementsLocations = {
bnplProductPage: {
configKey: 'upeBnplProductPageAppearance',
Expand All @@ -27,6 +19,16 @@ const elementsLocations = {
},
};

/**
* Initializes the appearance of the payment element by retrieving the UPE configuration
* from the API and saving the appearance if it doesn't exist. If the appearance already exists,
* it is simply returned.
*
* @param {Object} api The API object used to save the UPE configuration.
* @param {string} location The location of the UPE.
*
* @return {Promise<Object>} The appearance object for the UPE.
*/
async function initializeAppearance( api, location ) {
const { configKey, appearanceKey } = elementsLocations[ location ];

Expand All @@ -53,16 +55,25 @@ export const initializeBnplSiteMessaging = async () => {
isCart,
isCartBlock,
cartTotal,
minimumOrderAmount,
} = window.wcpayStripeSiteMessaging;

let amount;
let elementLocation = 'bnplProductPage';
const minOrderAmount = parseInt( minimumOrderAmount, 10 ) || 0;
const paymentMessageContainer = document.getElementById(
'payment-method-message'
);

if ( isCart || isCartBlock ) {
amount = parseInt( cartTotal, 10 ) || 0;
elementLocation = 'bnplClassicCart';
} else {
amount = parseInt( productVariations.base_product.amount, 10 ) || 0;

if ( amount < minOrderAmount ) {
paymentMessageContainer.style.setProperty( 'display', 'none' );
}
}

let paymentMessageElement;
Expand Down Expand Up @@ -140,9 +151,6 @@ export const initializeBnplSiteMessaging = async () => {
}

// Set the `--wc-bnpl-margin-bottom` CSS variable to the computed bottom margin of the price element.
const paymentMessageContainer = document.getElementById(
'payment-method-message'
);
paymentMessageContainer.style.setProperty(
'--wc-bnpl-margin-bottom',
bottomMargin
Expand Down Expand Up @@ -208,7 +216,7 @@ export const initializeBnplSiteMessaging = async () => {
pmme.style.setProperty( '--wc-bnpl-margin-bottom', '-4px' );
}, 2000 );
} else {
paymentMessageLoading.remove();
paymentMessageLoading?.remove();
}
} );
}
Expand Down
27 changes: 14 additions & 13 deletions includes/class-wc-payments-payment-method-messaging-element.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,20 @@ public function init() {
'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 ),
'nonce' => wp_create_nonce( 'wcpay-get-cart-total' ),
'wcAjaxUrl' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
'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%%' ),
]
);

Expand Down
68 changes: 66 additions & 2 deletions includes/class-wc-payments-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,62 @@ public static function get_filtered_error_status_code( Exception $e ): int {
return $status_code ?? 400;
}

/**
* 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.
*
* @param string $currency The currency.
*
* @return int The minimum amount.
*/
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;
default:
$minimum_amount = 50;
break;
}

self::cache_minimum_amount( $currency, $minimum_amount );

return $minimum_amount;
}

/**
* Saves the minimum amount required for transactions in a given currency.
*
Expand All @@ -716,12 +772,20 @@ 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 ) {
public static function get_cached_minimum_amount( $currency, $fallback_to_local_list = false ) {
$cached = get_transient( 'wcpay_minimum_amount_' . strtolower( $currency ) );
return (int) $cached ? (int) $cached : null;

if ( (int) $cached ) {
return (int) $cached;
} elseif ( $fallback_to_local_list ) {
return self::get_stripe_minimum_amount( $currency );
}

return null;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/test-class-wc-payments-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,12 @@ 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() ] );
Expand Down
Loading