Stripe charges 100 times too much in yen
Code written for dollars multiplies the price by 100, because Stripe takes dollars in cents. Yen has no cents. From Stripe's Zero-decimal currencies: for these currencies "the charge and the amount are the same, without requiring multiplication. For example, to charge 500 JPY, provide an amount value of 500." Multiply a ¥1,980 plan by 100 and Stripe charges ¥198,000.
The code that does it
On the server, the amount you send:
// Node: 100 times too much in JPY
await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{
quantity: 1,
price_data: {
currency: "jpy",
unit_amount: Math.round(price * 100),
product_data: { name: "Pro plan" },
},
}],
success_url, cancel_url,
});
The same goes for amount in stripe.paymentIntents.create and unit_amount in stripe.prices.create. Prices you type into the Stripe Dashboard are not affected; the bug is in code that converts.
In the app, the amount you show. Stripe sends amounts back the same way, so dividing by 100 turns ¥1,980 into ¥19.80:
// React: ¥1,980 shows as 19.80
<p>Paid: {(session.amount_total / 100).toFixed(2)}</p>
The fix
Keep one helper for each direction, and decide by Stripe's list rather than by the currency's usual decimals.
// Stripe's zero-decimal currencies but UGX,
// which Stripe still takes with two decimals.
const ZERO_DECIMAL = new Set([
"bif", "clp", "djf", "gnf", "jpy", "kmf", "krw", "mga",
"pyg", "rwf", "vnd", "vuv", "xaf", "xof", "xpf",
]);
export function toStripeAmount(amount, currency) {
return ZERO_DECIMAL.has(currency.toLowerCase())
? Math.round(amount)
: Math.round(amount * 100);
}
export function fromStripeAmount(amount, currency) {
return ZERO_DECIMAL.has(currency.toLowerCase()) ? amount : amount / 100;
}
Then unit_amount: toStripeAmount(price, currency) on the server. For display, let the browser pick the decimals:
new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" })
.format(fromStripeAmount(session.amount_total, "jpy"));
// "¥1,980"
Why not take the decimals from Intl for Stripe too: Stripe's Special cases differ from the usual ones. Icelandic króna and Ugandan shilling have no decimals in everyday use, but Stripe takes them as two-decimal values ending in 00 ("to charge 5 ISK, provide an amount value of 500").
The smallest charge in yen
From Stripe's minimum and maximum charge amounts: the minimum is 50 JPY when your payouts settle in yen. If they settle in another currency, the yen amount has to meet that currency's minimum after conversion (0.50 USD, 0.50 EUR, 0.30 GBP). A ¥50 test charge from a US account may not.
The rest of a Japanese check
Yen is usually shown without decimals too: toFixed(2) anywhere in a price or a receipt prints ¥1,980.00, which reads as a foreign site. The same pass catches Enter while typing Japanese (the fix), kanji drawn in their Chinese shapes without lang="ja" (the fix), headings that break in the middle of a word (the fix), postal codes rejected when typed in Japanese (the fix), dates saved one day early in Japan (the fix), address forms in the American order (the fix), phone numbers sent to +81 with the 0 still in (the fix), PDFs with the Japanese missing (the fix), search that finds nothing in Japanese (the fix), previews cut down to “...” in Japanese (the fix), lists sorted in the wrong order (the fix), email subjects that arrive garbled (the fix), CSV files that open garbled in Excel (the fix), Japanese titles that give an empty URL slug (the fix), file names garbled on upload and download (the fix), dates in the Japanese era that come out wrong (the fix), full-width spaces that get past your checks (the fix), public holidays your app gets wrong (the fix), and vertical text that comes out sideways or upside down (the fix).
Lesstep translates web apps into Japanese and delivers them as one pull request. In Complete (US$249), we check your prices, checkout and receipts in yen, and fix this and the rest in the same pull request. The first step is a free sample.
Stripe is a trademark of Stripe, Inc. This page is not affiliated with or endorsed by Stripe, Inc.
More: Enter sends the message while typing Japanese · Kanji in their Chinese shapes · Japanese headings that break in the middle of a word · Forms that reject numbers typed in Japanese · Dates that come out wrong for Japanese users · Address forms in the Japanese order · Japanese phone numbers in forms · Japanese text missing from your PDFs · Search that finds nothing in Japanese · Length limits that break in Japanese · Japanese lists sorted in the wrong order · Japanese email subjects that arrive garbled · Japanese CSV files that open garbled in Excel · Japanese titles that give an empty URL slug · Japanese file names garbled on upload and download · Japanese era dates that come out wrong · Full-width spaces that get past your checks · Japanese public holidays your app gets wrong · Japanese vertical text that comes out sideways or upside down · Japanese localization checklist · Japanese localization for your web app