# Stripe JPY amount wrong: zero-decimal currencies need no x100

## The symptom

Customers in Japan, Korea, or Vietnam are charged 100x what you intended, or your code throws amount validation errors on currencies you thought were fine. The charge amount on the receipt does not match what your UI showed.

## Confirm the cause

Stripe amounts are integers in the currency's smallest unit. For most currencies that is cents, so you multiply dollars by 100. But 16 currencies have no minor unit: for those, the integer IS the major unit.

Zero-decimal (pass the whole amount, no x100): BIF, CLP, DJF, GNF, JPY, KMF, KRW, MGA, PYG, RWF, UGX, VND, VUV, XAF, XOF, XPF.

Three-decimal (multiply by 1000): BHD, IQD, JOD, KWD, LYD, OMR, TND.

Everything else: multiply by 100 as usual.

So `amount: 1000, currency: 'jpy'` is 1000 yen (correct). `amount: 100000, currency: 'jpy'` is 100,000 yen (the bug).

## The fix

Centralize conversion in one function used by every charge path:

```js
const ZERO_DECIMAL = new Set(['BIF','CLP','DJF','GNF','JPY','KMF','KRW','MGA','PYG','RWF','UGX','VND','VUV','XAF','XOF','XPF']);
const THREE_DECIMAL = new Set(['BHD','IQD','JOD','KWD','LYD','OMR','TND']);

function toStripeAmount(major, currency) {
  const c = currency.toUpperCase();
  if (ZERO_DECIMAL.has(c)) return Math.round(major);
  if (THREE_DECIMAL.has(c)) return Math.round(major * 1000);
  return Math.round(major * 100);
}
```

Round the major amount *before* converting: `Math.round(10.50 * 100)` avoids float dust like `220.00000000000003` that Stripe rejects as non-integer. Never divide by 100 unconditionally when *displaying* either: 1000 JPY displayed as 10.00 is the same bug in reverse.

## Verify the fix

Unit-test the function across all three classes plus USD. Run one real test-mode charge per class (USD 10.50 -> 1050, JPY 1000 -> 1000, KWD 1.234 -> 1234) and confirm the receipt amounts match. Audit every `* 100` in your codebase for a currency-aware path.
