diff --git a/changelog/fix-tokenized-cart-zero-decimal-currency b/changelog/fix-tokenized-cart-zero-decimal-currency new file mode 100644 index 00000000000..887eaef6d70 --- /dev/null +++ b/changelog/fix-tokenized-cart-zero-decimal-currency @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: fix: tokenized PRB with zero decimal currency + + diff --git a/client/tokenized-payment-request/transformers/__tests__/wc-to-stripe.test.js b/client/tokenized-payment-request/transformers/__tests__/wc-to-stripe.test.js new file mode 100644 index 00000000000..45ad34a1979 --- /dev/null +++ b/client/tokenized-payment-request/transformers/__tests__/wc-to-stripe.test.js @@ -0,0 +1,54 @@ +/** + * Internal dependencies + */ +import { transformPrice } from '../wc-to-stripe'; + +global.wcpayPaymentRequestParams = {}; +global.wcpayPaymentRequestParams.checkout = {}; + +describe( 'wc-to-stripe transformers', () => { + describe( 'transformPrice', () => { + afterEach( () => { + delete global.wcpayPaymentRequestParams.checkout.currency_decimals; + } ); + + it( 'transforms the price', () => { + expect( transformPrice( 180, { currency_minor_unit: 2 } ) ).toBe( + 180 + ); + } ); + + it( 'transforms the price if the currency is configured with one decimal', () => { + // with one decimal, `180` would mean `18.0`. + // But since Stripe expects the price to be in cents, the return value should be `1800` + expect( transformPrice( 180, { currency_minor_unit: 1 } ) ).toBe( + 1800 + ); + } ); + + it( 'transforms the price if the currency is configured with two decimals', () => { + // with two decimals, `1800` would mean `18.00`. + // But since Stripe expects the price to be in cents, the return value should be `1800` + expect( transformPrice( 1800, { currency_minor_unit: 2 } ) ).toBe( + 1800 + ); + } ); + + it( 'transforms the price if the currency is a zero decimal currency (e.g.: Yen)', () => { + global.wcpayPaymentRequestParams.checkout.currency_decimals = 0; + // with zero decimals, `18` would mean `18`. + expect( transformPrice( 18, { currency_minor_unit: 0 } ) ).toBe( + 18 + ); + } ); + + it( 'transforms the price if the currency a zero decimal currency (e.g.: Yen) but it is configured with one decimal', () => { + global.wcpayPaymentRequestParams.checkout.currency_decimals = 0; + // with zero decimals, `18` would mean `18`. + // But since Stripe expects the price to be in the minimum currency amount, the return value should be `18` + expect( transformPrice( 180, { currency_minor_unit: 1 } ) ).toBe( + 18 + ); + } ); + } ); +} ); diff --git a/client/tokenized-payment-request/transformers/wc-to-stripe.js b/client/tokenized-payment-request/transformers/wc-to-stripe.js index 1f932b47602..aabc78820d5 100644 --- a/client/tokenized-payment-request/transformers/wc-to-stripe.js +++ b/client/tokenized-payment-request/transformers/wc-to-stripe.js @@ -3,6 +3,11 @@ */ import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { getPaymentRequestData } from '../frontend-utils'; + /** * GooglePay/ApplePay expect the prices to be formatted in cents. * But WooCommerce has a setting to define the number of decimals for amounts. @@ -14,9 +19,13 @@ import { __ } from '@wordpress/i18n'; * * @return {number} the price amount for GooglePay/ApplePay, always expressed in cents. */ -export const transformPrice = ( price, priceObject ) => +export const transformPrice = ( price, priceObject ) => { + const currencyDecimals = + getPaymentRequestData( 'checkout' )?.currency_decimals ?? 2; + // making sure the decimals are always correctly represented for GooglePay/ApplePay, since they don't allow us to specify the decimals. - price * 10 ** ( 2 - priceObject.currency_minor_unit ); + return price * 10 ** ( currencyDecimals - priceObject.currency_minor_unit ); +}; /** * Transforms the data from the Store API Cart response to `displayItems` for the Stripe PRB. diff --git a/includes/class-wc-payments-payment-request-button-handler.php b/includes/class-wc-payments-payment-request-button-handler.php index b4d97e97ce2..bb4be82c487 100644 --- a/includes/class-wc-payments-payment-request-button-handler.php +++ b/includes/class-wc-payments-payment-request-button-handler.php @@ -970,6 +970,7 @@ public function scripts() { ], 'checkout' => [ 'currency_code' => strtolower( get_woocommerce_currency() ), + 'currency_decimals' => WC_Payments::get_localization_service()->get_currency_format( get_woocommerce_currency() )['num_decimals'], 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ), 'needs_shipping' => WC()->cart->needs_shipping(), // Defaults to 'required' to match how core initializes this option.