diff --git a/.changeset/eager-numbers-relate.md b/.changeset/eager-numbers-relate.md
new file mode 100644
index 00000000..6d59e8a8
--- /dev/null
+++ b/.changeset/eager-numbers-relate.md
@@ -0,0 +1,5 @@
+---
+"@godaddy/react": patch
+---
+
+Use formatCurrency util to respect all currency precisions
diff --git a/packages/react/src/components/checkout/form/checkout-form.tsx b/packages/react/src/components/checkout/form/checkout-form.tsx
index e0381b53..0fcf874b 100644
--- a/packages/react/src/components/checkout/form/checkout-form.tsx
+++ b/packages/react/src/components/checkout/form/checkout-form.tsx
@@ -38,6 +38,7 @@ import { ShippingMethodForm } from '@/components/checkout/shipping/shipping-meth
import { Target } from '@/components/checkout/target/target';
import { TipsForm } from '@/components/checkout/tips/tips-form';
import { DraftOrderTotals } from '@/components/checkout/totals/totals';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import {
Accordion,
AccordionContent,
@@ -106,16 +107,16 @@ export function CheckoutForm({
const { data: order } = draftOrder;
// Order summary calculations
- const subtotal = (totals?.subTotal?.value || 0) / 100;
- const orderDiscount = (totals?.discountTotal?.value || 0) / 100;
+ const subtotal = totals?.subTotal?.value || 0;
+ const orderDiscount = totals?.discountTotal?.value || 0;
const shipping =
- (order?.shippingLines?.reduce(
+ order?.shippingLines?.reduce(
(sum, line) => sum + (line?.amount?.value || 0),
0
- ) || 0) / 100;
- const taxTotal = (totals?.taxTotal?.value || 0) / 100;
- const orderTotal = (totals?.total?.value || 0) / 100;
- const tipTotal = (tipAmount || 0) / 100;
+ ) || 0;
+ const taxTotal = totals?.taxTotal?.value || 0;
+ const orderTotal = totals?.total?.value || 0;
+ const tipTotal = tipAmount || 0;
const currencyCode = totals?.total?.currencyCode || 'USD';
const itemCount = items.reduce((sum, item) => sum + (item?.quantity || 0), 0);
@@ -427,10 +428,7 @@ export function CheckoutForm({
{t.totals.orderSummary}
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(orderTotal)}
+ {formatCurrency({ amount: orderTotal, currencyCode })}
diff --git a/packages/react/src/components/checkout/line-items/line-items.tsx b/packages/react/src/components/checkout/line-items/line-items.tsx
index 9bb14866..c8b1b216 100644
--- a/packages/react/src/components/checkout/line-items/line-items.tsx
+++ b/packages/react/src/components/checkout/line-items/line-items.tsx
@@ -3,6 +3,7 @@
import { Image } from 'lucide-react';
import { useGoDaddyContext } from '@/godaddy-provider';
import type { SKUProduct } from '@/types';
+import { formatCurrency } from '../utils/format-currency';
export interface Note {
content: string | null;
@@ -129,10 +130,10 @@ export function DraftOrderLineItems({
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(item.originalPrice * item.quantity)}
+ {formatCurrency({
+ amount: item.originalPrice * item.quantity,
+ currencyCode,
+ })}
diff --git a/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx b/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx
index 8ada5640..de00d7fd 100644
--- a/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx
+++ b/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx
@@ -34,6 +34,7 @@ import { filterAndSortShippingMethods } from '@/components/checkout/shipping/uti
import { useGetShippingMethodByAddress } from '@/components/checkout/shipping/utils/use-get-shipping-methods';
import { useGetTaxes } from '@/components/checkout/taxes/utils/use-get-taxes';
import { mapOrderToFormValues } from '@/components/checkout/utils/checkout-transformers';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import { Skeleton } from '@/components/ui/skeleton';
import { useGoDaddyContext } from '@/godaddy-provider';
import { GraphQLErrorWithCodes } from '@/lib/graphql-with-errors';
@@ -100,7 +101,13 @@ export function ExpressCheckoutButton() {
type: 'SHIPPING' as const,
subtotalPrice: {
currencyCode: currency,
- value: Number(amount) * 100 || 0,
+ value: Number(
+ formatCurrency({
+ amount: Number(amount) || 0,
+ currencyCode: currency,
+ isInCents: false,
+ })
+ ),
},
},
],
@@ -133,10 +140,10 @@ export function ExpressCheckoutButton() {
});
const methods = sortedMethods?.map(method => {
- const shippingMethodPrice = new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: method.cost?.currencyCode || 'USD',
- }).format((method.cost?.value || 0) / 100);
+ const shippingMethodPrice = formatCurrency({
+ amount: method.cost?.value || 0,
+ currencyCode: method.cost?.currencyCode || 'USD',
+ });
return {
id: method?.displayName?.replace(/\s+/g, '-')?.toLowerCase(),
@@ -144,7 +151,11 @@ export function ExpressCheckoutButton() {
detail: method.description
? `(${method.description}) ${shippingMethodPrice}`
: `${shippingMethodPrice}`,
- amount: ((method.cost?.value || 0) / 100).toString(),
+ amount: formatCurrency({
+ amount: method.cost?.value || 0,
+ currencyCode,
+ returnRaw: true,
+ }),
};
});
@@ -173,7 +184,11 @@ export function ExpressCheckoutButton() {
// Always add the discount line item, using state variables directly
updatedLineItems.push({
label: t.totals.discount,
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
isPending: false,
});
@@ -194,7 +209,11 @@ export function ExpressCheckoutButton() {
couponCode: {
code: appliedCouponCode,
label: t.totals.discount,
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
},
};
} else {
@@ -328,7 +347,11 @@ export function ExpressCheckoutButton() {
couponConfig = {
code: appliedCouponCode,
label: t.totals.discount,
- amount: (priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
};
}
@@ -439,7 +462,13 @@ export function ExpressCheckoutButton() {
{
subTotal: {
currencyCode: currencyCode,
- value: Number(selectedMethod.amount) * 100,
+ value: Number(
+ formatCurrency({
+ amount: Number(selectedMethod.amount),
+ currencyCode,
+ isInCents: false,
+ })
+ ),
},
name: selectedMethod.name,
},
@@ -520,14 +549,22 @@ export function ExpressCheckoutButton() {
if (godaddyTotals.shipping.value > 0) {
finalLineItems.push({
label: 'Shipping',
- amount: (godaddyTotals.shipping.value / 100).toString(),
+ amount: formatCurrency({
+ amount: godaddyTotals.shipping.value,
+ currencyCode,
+ returnRaw: true,
+ }),
});
}
if (godaddyTotals.taxes.value > 0) {
finalLineItems.push({
label: t.totals.estimatedTaxes,
- amount: (godaddyTotals.taxes.value / 100).toString(),
+ amount: formatCurrency({
+ amount: godaddyTotals.taxes.value,
+ currencyCode,
+ returnRaw: true,
+ }),
});
}
@@ -555,7 +592,11 @@ export function ExpressCheckoutButton() {
let shippingLines: ReturnType;
if (shippingAddress && shippingMethod) {
const selectedMethodInfo = {
- amount: (godaddyTotals.shipping.value / 100).toString(),
+ amount: formatCurrency({
+ amount: godaddyTotals.shipping.value,
+ currencyCode,
+ returnRaw: true,
+ }),
name: shippingMethod,
};
shippingLines = convertAddressToShippingLines(
@@ -580,21 +621,33 @@ export function ExpressCheckoutButton() {
if (godaddyTotals.shipping.value > 0) {
finalLineItems.push({
label: t.totals.shipping,
- amount: (godaddyTotals.shipping.value / 100).toString(),
+ amount: formatCurrency({
+ amount: godaddyTotals.shipping.value,
+ currencyCode,
+ returnRaw: true,
+ }),
});
}
if (godaddyTotals.taxes.value > 0) {
finalLineItems.push({
label: t.totals.estimatedTaxes,
- amount: (godaddyTotals.taxes.value / 100).toString(),
+ amount: formatCurrency({
+ amount: godaddyTotals.taxes.value,
+ currencyCode,
+ returnRaw: true,
+ }),
});
}
// Add the discount line item
finalLineItems.push({
label: t.totals.discount,
- amount: (-adjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -adjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
});
// Calculate the total amount
@@ -611,7 +664,11 @@ export function ExpressCheckoutButton() {
couponCode: {
code: couponCode,
label: t.totals.discount,
- amount: (-adjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -adjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
},
};
} else {
@@ -844,7 +901,13 @@ export function ExpressCheckoutButton() {
...value,
shipping: {
currencyCode: 'USD',
- value: Number(shippingAmount) * 100 || 0,
+ value: Number(
+ formatCurrency({
+ amount: Number(shippingAmount) || 0,
+ currencyCode,
+ isInCents: false,
+ })
+ ),
},
}));
@@ -897,7 +960,11 @@ export function ExpressCheckoutButton() {
if (taxesResult?.value) {
poyntLineItems.push({
label: t.totals.estimatedTaxes,
- amount: (taxesResult.value / 100).toString(),
+ amount: formatCurrency({
+ amount: taxesResult.value,
+ currencyCode,
+ returnRaw: true,
+ }),
isPending: false,
});
setGoDaddyTotals(value => ({
@@ -925,7 +992,11 @@ export function ExpressCheckoutButton() {
if (priceAdjustment && appliedCouponCode) {
poyntLineItems.push({
label: t.totals.discount,
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
isPending: false,
});
}
@@ -949,7 +1020,11 @@ export function ExpressCheckoutButton() {
updatedOrder.couponCode = {
code: appliedCouponCode,
label: t.totals.discount,
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
};
}
@@ -979,7 +1054,13 @@ export function ExpressCheckoutButton() {
...value,
shipping: {
currencyCode: 'USD',
- value: Number(methods?.[0]?.amount) * 100 || 0,
+ value: Number(
+ formatCurrency({
+ amount: Number(methods?.[0]?.amount) || 0,
+ currencyCode,
+ isInCents: false,
+ })
+ ),
},
}));
@@ -1060,7 +1141,11 @@ export function ExpressCheckoutButton() {
if (taxesResult?.value) {
poyntLineItems.push({
label: t.totals.estimatedTaxes,
- amount: (taxesResult.value / 100).toString(),
+ amount: formatCurrency({
+ amount: taxesResult.value,
+ currencyCode,
+ returnRaw: true,
+ }),
isPending: false,
});
setGoDaddyTotals(value => ({
@@ -1094,7 +1179,11 @@ export function ExpressCheckoutButton() {
if (priceAdjustment && appliedCouponCode) {
poyntLineItems.push({
label: t.totals.discount,
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
isPending: false,
});
}
@@ -1119,7 +1208,11 @@ export function ExpressCheckoutButton() {
updatedOrder.couponCode = {
code: appliedCouponCode,
label: appliedCouponCode || 'Discount',
- amount: (-priceAdjustment / 100).toString(),
+ amount: formatCurrency({
+ amount: -priceAdjustment,
+ currencyCode,
+ returnRaw: true,
+ }),
};
} else {
updatedOrder.couponCode = {
diff --git a/packages/react/src/components/checkout/payment/payment-form.tsx b/packages/react/src/components/checkout/payment/payment-form.tsx
index c753e7be..9c864831 100644
--- a/packages/react/src/components/checkout/payment/payment-form.tsx
+++ b/packages/react/src/components/checkout/payment/payment-form.tsx
@@ -34,6 +34,7 @@ import {
DraftOrderTotals,
type DraftOrderTotalsProps,
} from '@/components/checkout/totals/totals';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import {
Accordion,
AccordionContent,
@@ -447,10 +448,10 @@ export function PaymentForm(
{t.totals.orderSummary}
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: props.currencyCode,
- }).format(props.total || 0)}
+ {formatCurrency({
+ amount: props.total || 0,
+ currencyCode: props.currencyCode || 'USD',
+ })}
diff --git a/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts b/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
index 144309ce..9ad3793b 100644
--- a/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
+++ b/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
@@ -8,6 +8,7 @@ import {
} from '@/components/checkout/order/use-draft-order';
import { useDraftOrderProductsMap } from '@/components/checkout/order/use-draft-order-products';
import { mapSkusToItemsDisplay } from '@/components/checkout/utils/checkout-transformers';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
// Apple Pay request interface
export interface ApplePayRequest {
@@ -194,15 +195,45 @@ export function useBuildPaymentRequest(): {
);
// Convert amounts from cents to dollars for display
- const subtotal = (totals?.subTotal?.value || 0) / 100;
- const tax = (totals?.taxTotal?.value || 0) / 100;
- const shipping =
- (order?.shippingLines?.reduce(
- (sum, line) => sum + (line?.amount?.value || 0),
- 0
- ) || 0) / 100;
- const discount = (totals?.discountTotal?.value || 0) / 100;
- const total = (totals?.total?.value || 0) / 100;
+ const subtotal = Number(
+ formatCurrency({
+ amount: totals?.subTotal?.value || 0,
+ currencyCode,
+ returnRaw: true,
+ })
+ );
+ const tax = Number(
+ formatCurrency({
+ amount: totals?.taxTotal?.value || 0,
+ currencyCode,
+ returnRaw: true,
+ })
+ );
+ const shipping = Number(
+ formatCurrency({
+ amount:
+ order?.shippingLines?.reduce(
+ (sum, line) => sum + (line?.amount?.value || 0),
+ 0
+ ) || 0,
+ currencyCode,
+ returnRaw: true,
+ })
+ );
+ const discount = Number(
+ formatCurrency({
+ amount: totals?.discountTotal?.value || 0,
+ currencyCode,
+ returnRaw: true,
+ })
+ );
+ const total = Number(
+ formatCurrency({
+ amount: totals?.total?.value || 0,
+ currencyCode,
+ returnRaw: true,
+ })
+ );
const countryCode = useMemo(
() => session?.shipping?.originAddress?.countryCode || 'US',
@@ -254,52 +285,38 @@ export function useBuildPaymentRequest(): {
merchantCapabilities: ['supports3DS'],
total: {
label: 'Order Total',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(total),
+ amount: total.toFixed(2),
type: 'final',
},
lineItems: [
...(items || []).map(lineItem => ({
label: lineItem?.name || '',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format((lineItem?.originalPrice || 0) * (lineItem?.quantity || 0)),
+ amount: formatCurrency({
+ amount: (lineItem?.originalPrice || 0) * (lineItem?.quantity || 0),
+ currencyCode,
+ returnRaw: true,
+ }),
type: 'LINE_ITEM',
status: 'FINAL',
})),
{
label: 'Subtotal',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(subtotal),
+ amount: subtotal.toFixed(2),
type: 'final',
},
{
label: 'Tax',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(tax),
+ amount: tax.toFixed(2),
type: 'final',
},
{
label: 'Shipping',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(shipping),
+ amount: shipping.toFixed(2),
type: 'final',
},
{
label: 'Discount',
- amount: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(-1 * discount),
+ amount: (-1 * discount).toFixed(2),
type: 'final',
},
].filter(item => Number.parseFloat(item.amount) !== 0),
@@ -338,16 +355,20 @@ export function useBuildPaymentRequest(): {
},
transactionInfo: {
totalPriceStatus: 'FINAL',
- totalPrice: new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(total),
+ totalPrice: total.toFixed(2),
totalPriceLabel: 'Total',
currencyCode,
displayItems: [
...(items || []).map(lineItem => ({
label: lineItem?.name || '',
- price: (lineItem?.originalPrice || 0) * (lineItem?.quantity || 0),
+ price: Number(
+ formatCurrency({
+ amount:
+ (lineItem?.originalPrice || 0) * (lineItem?.quantity || 0),
+ currencyCode,
+ returnRaw: true,
+ })
+ ),
type: 'LINE_ITEM',
status: 'FINAL',
})),
@@ -411,7 +432,11 @@ export function useBuildPaymentRequest(): {
name: lineItem?.name || '',
unit_amount: {
currency_code: currencyCode,
- value: (lineItem?.originalPrice || 0).toFixed(2),
+ value: formatCurrency({
+ amount: lineItem?.originalPrice || 0,
+ currencyCode,
+ returnRaw: true,
+ }),
},
quantity: (lineItem?.quantity || 1).toString(),
})),
@@ -500,15 +525,17 @@ export function useBuildPaymentRequest(): {
const poyntExpressRequest: PoyntExpressRequest = {
total: {
label: 'Order Total',
- amount: subtotal.toString(),
+ amount: subtotal.toFixed(2),
},
lineItems: [
...(items || []).map(lineItem => {
return {
label: lineItem?.name || '',
- amount: (
- (lineItem?.originalPrice || 0) * (lineItem?.quantity || 1)
- ).toString(),
+ amount: formatCurrency({
+ amount: (lineItem?.originalPrice || 0) * (lineItem?.quantity || 1),
+ currencyCode,
+ returnRaw: true,
+ }),
};
}),
],
@@ -517,15 +544,17 @@ export function useBuildPaymentRequest(): {
const poyntStandardRequest: PoyntStandardRequest = {
total: {
label: 'Order Total',
- amount: total.toString(),
+ amount: total.toFixed(2),
},
lineItems: [
...(items || []).map(lineItem => {
return {
label: lineItem?.name || '',
- amount: (
- (lineItem?.originalPrice || 0) * (lineItem?.quantity || 1)
- ).toString(),
+ amount: formatCurrency({
+ amount: (lineItem?.originalPrice || 0) * (lineItem?.quantity || 1),
+ currencyCode,
+ returnRaw: true,
+ }),
};
}),
{
diff --git a/packages/react/src/components/checkout/shipping/shipping-method.tsx b/packages/react/src/components/checkout/shipping/shipping-method.tsx
index c9023d20..51bb3d84 100644
--- a/packages/react/src/components/checkout/shipping/shipping-method.tsx
+++ b/packages/react/src/components/checkout/shipping/shipping-method.tsx
@@ -14,6 +14,7 @@ import { ShippingMethodSkeleton } from '@/components/checkout/shipping/shipping-
import { filterAndSortShippingMethods } from '@/components/checkout/shipping/utils/filter-shipping-methods';
import { useApplyShippingMethod } from '@/components/checkout/shipping/utils/use-apply-shipping-method';
import { useDraftOrderShippingMethods } from '@/components/checkout/shipping/utils/use-draft-order-shipping-methods';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { useGoDaddyContext } from '@/godaddy-provider';
@@ -259,10 +260,11 @@ export function ShippingMethodForm() {
{t.general.free}
) : (
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: shippingMethods[0]?.cost?.currencyCode || 'USD',
- }).format((shippingMethods[0]?.cost?.value || 0) / 100)}
+ {formatCurrency({
+ amount: shippingMethods[0]?.cost?.value || 0,
+ currencyCode:
+ shippingMethods[0]?.cost?.currencyCode || 'USD',
+ })}
)}
@@ -301,10 +303,10 @@ export function ShippingMethodForm() {
{t.general.free}
) : (
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: method?.cost?.currencyCode || 'USD',
- }).format((method?.cost?.value || 0) / 100)}
+ {formatCurrency({
+ amount: method?.cost?.value || 0,
+ currencyCode: method?.cost?.currencyCode || 'USD',
+ })}
)}
diff --git a/packages/react/src/components/checkout/tips/tips-form.tsx b/packages/react/src/components/checkout/tips/tips-form.tsx
index bfc0aeab..360a0eda 100644
--- a/packages/react/src/components/checkout/tips/tips-form.tsx
+++ b/packages/react/src/components/checkout/tips/tips-form.tsx
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { useCheckoutContext } from '@/components/checkout/checkout';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import { Button } from '@/components/ui/button';
import {
FormControl,
@@ -30,14 +31,6 @@ export function TipsForm({ total, currencyCode }: TipsFormProps) {
return Math.round(((total * percentage) / 100) * 100);
};
- const formatCurrency = (amount: number): string => {
- // Convert from cents to dollars before formatting
- return new Intl.NumberFormat('en-US', {
- style: 'currency',
- currency: currencyCode || 'USD',
- }).format(amount / 100);
- };
-
const handlePercentageSelect = (percentage: number) => {
const tipAmount = calculateTipAmount(percentage);
form.setValue('tipAmount', tipAmount);
@@ -116,7 +109,10 @@ export function TipsForm({ total, currencyCode }: TipsFormProps) {
>
{percentage}%
- {formatCurrency(calculateTipAmount(percentage))}
+ {formatCurrency({
+ amount: calculateTipAmount(percentage),
+ currencyCode: currencyCode || 'USD',
+ })}
))}
diff --git a/packages/react/src/components/checkout/totals/totals.tsx b/packages/react/src/components/checkout/totals/totals.tsx
index ca4c4751..f1e91367 100644
--- a/packages/react/src/components/checkout/totals/totals.tsx
+++ b/packages/react/src/components/checkout/totals/totals.tsx
@@ -1,5 +1,6 @@
import { DiscountStandalone } from '@/components/checkout/discount/discount-standalone';
import { TotalLineItemSkeleton } from '@/components/checkout/totals/totals-skeleton';
+import { formatCurrency } from '@/components/checkout/utils/format-currency';
import { useGoDaddyContext } from '@/godaddy-provider';
export interface DraftOrderTotalsProps {
@@ -39,12 +40,7 @@ function TotalLineItem({
{description}
) : null}
-
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(value)}
-
+ {formatCurrency({ amount: value, currencyCode })}
);
}
@@ -143,10 +139,7 @@ export function DraftOrderTotals({
{currencyCode}{' '}
- {new Intl.NumberFormat('en-us', {
- style: 'currency',
- currency: currencyCode,
- }).format(total)}
+ {formatCurrency({ amount: total, currencyCode })}
diff --git a/packages/react/src/components/checkout/utils/checkout-transformers.ts b/packages/react/src/components/checkout/utils/checkout-transformers.ts
index cb107ad0..02b92f1d 100644
--- a/packages/react/src/components/checkout/utils/checkout-transformers.ts
+++ b/packages/react/src/components/checkout/utils/checkout-transformers.ts
@@ -147,15 +147,12 @@ export function mapSkusToItemsDisplay(
image: orderItem.details?.productAssetUrl || skuDetails?.mediaUrls?.[0],
quantity: orderItem.quantity || 0,
originalPrice:
- (orderItem.totals?.subTotal?.value ?? 0) /
- (orderItem.quantity || 0) /
- 100,
+ (orderItem.totals?.subTotal?.value ?? 0) / (orderItem.quantity || 0),
price:
- ((orderItem.totals?.subTotal?.value ?? 0) +
- (orderItem.totals?.feeTotal?.value ?? 0) -
- // (orderItem.totals?.taxTotal?.value ?? 0) - // do we need taxTotal here?
- (orderItem.totals?.discountTotal?.value ?? 0)) /
- 100,
+ (orderItem.totals?.subTotal?.value ?? 0) +
+ (orderItem.totals?.feeTotal?.value ?? 0) -
+ // (orderItem.totals?.taxTotal?.value ?? 0) - // do we need taxTotal here?
+ (orderItem.totals?.discountTotal?.value ?? 0),
notes: orderItem.notes
?.filter(
note =>
diff --git a/packages/react/src/components/checkout/utils/format-currency.ts b/packages/react/src/components/checkout/utils/format-currency.ts
new file mode 100644
index 00000000..1b71c3e0
--- /dev/null
+++ b/packages/react/src/components/checkout/utils/format-currency.ts
@@ -0,0 +1,90 @@
+/**
+ * Currency configuration map with symbols and decimal precision.
+ */
+export const currencyConfigs: Record<
+ string,
+ { symbol: string; precision: number; pattern?: string }
+> = {
+ AUD: { symbol: '$', precision: 2 },
+ CAD: { symbol: '$', precision: 2 },
+ HKD: { symbol: '$', precision: 2 },
+ SGD: { symbol: '$', precision: 2 },
+ NZD: { symbol: '$', precision: 2 },
+ USD: { symbol: '$', precision: 2 },
+ VND: { symbol: '₫', precision: 0 },
+ EUR: { symbol: '€', precision: 2 },
+ GBP: { symbol: '£', precision: 2 },
+ ARS: { symbol: '$', precision: 2 },
+ CLP: { symbol: '$', precision: 0 },
+ COP: { symbol: '$', precision: 2 },
+ PHP: { symbol: '₱', precision: 2 },
+ MXN: { symbol: '$', precision: 2 },
+ BRL: { symbol: 'R$', precision: 2 },
+ INR: { symbol: '₹', precision: 2 },
+ IDR: { symbol: 'Rp', precision: 2 },
+ PEN: { symbol: 'S/', precision: 2 },
+ AED: { symbol: 'د.إ', precision: 2, pattern: '#!' },
+ ILS: { symbol: '₪', precision: 2 },
+ TRY: { symbol: '₺', precision: 2 },
+ ZAR: { symbol: 'R', precision: 2 },
+ CNY: { symbol: '¥', precision: 2 },
+};
+
+export interface FormatCurrencyOptions {
+ /** Numeric amount to format or convert */
+ amount: number;
+ /** ISO 4217 currency code (e.g. 'USD', 'VND', 'CLP') */
+ currencyCode: string;
+ /** Optional locale, defaults to 'en-US' */
+ locale?: string;
+ /**
+ * Indicates whether the input is already in cents (minor units).
+ * - true → format to currency string (default)
+ * - false → convert to minor units and return as string
+ */
+ isInCents?: boolean;
+ /**
+ * Return raw numeric value without currency symbol.
+ * - true → returns "10.00" instead of "$10.00"
+ * - false → returns full currency string (default)
+ */
+ returnRaw?: boolean;
+}
+
+/**
+ * Formats or converts a currency amount.
+ *
+ * - When `isInCents = true` (default): returns formatted string like "$123.45"
+ * - When `isInCents = false`: returns string representing minor units like "12345"
+ * - When `returnRaw = true`: returns numeric value without currency symbol like "123.45"
+ */
+export function formatCurrency({
+ amount,
+ currencyCode,
+ locale = 'en-US',
+ isInCents = true,
+ returnRaw = false,
+}: FormatCurrencyOptions): string {
+ const config = currencyConfigs[currencyCode];
+
+ if (!config) {
+ return amount.toString();
+ }
+
+ const { precision } = config;
+
+ if (!isInCents) {
+ // Convert major units to minor units and return as string
+ return Math.round(amount * Math.pow(10, precision)).toString();
+ }
+
+ // Format value already in minor units
+ const value = amount / Math.pow(10, precision);
+
+ return new Intl.NumberFormat(locale, {
+ style: returnRaw ? 'decimal' : 'currency',
+ currency: returnRaw ? undefined : currencyCode,
+ minimumFractionDigits: precision,
+ maximumFractionDigits: precision,
+ }).format(value);
+}