Repository navigation
amounts: format with each currency's decimal places - #6
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access that start with 'Devin'.
Original prompt from antonio.garcia
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…r handling - currencyFractionDigits: ISO 4217 minor-unit overrides where CLDR display digits differ (COP, HUF, IDR, PKR, TWD, MGA, MRU at 2; IQD at 3), plus an optional fallback for display-side callers - schemas validate significant decimals so "10000.00" CLP is accepted while "10000.5" still 400s; shared via core significantDecimals - web budget/parse helpers fall back to 2 decimals on unknown currency instead of throwing mid-render - CrossmintApiError joins NestJS-style message arrays - approveRequest normalizes the stored amount before revoking the card and wraps failure in a 400 HttpError - widen chat amount patterns to 3 decimals and keep toDecimalString as the strict gate; parseBudgetAmount treats a comma with exactly 3 trailing digits as a thousands separator - search_products maxPrice filter multiplies by the currency's minor-unit factor; new optional currency param - restore unrelated esbuild lockfile churn Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- toDecimalString: string inputs now throw on any significant digit past the currency's decimals at any magnitude (the old magnitude-scaled tolerance accepted "500000000.5" CLP and silently rounded up); float dust tolerance is isolated to number inputs and scaled to a few ulps - parsePrice accepts four-decimal catalog prices (CLF), so a CLF price keeps its currency instead of falling back to a USD budget Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Summary
Amounts were always formatted with 2 decimals, which breaks zero-decimal currencies (CLP, JPY, KRW) and 3-decimal currencies (KWD, BHD). On 2026-10-23 a user approving a CLP 10,000 budget got repeated
502errors: the server sent{"value":"10000.00","currency":"CLP"}to Crossmint and the order-intent provider rejected it because CLP supports at most 0 decimal places.toDecimalStringin@agent-commerce/corenow takes a requiredcurrencyargument and formats to exactly that currency's decimals via a newcurrencyFractionDigitshelper (resolved throughIntl.NumberFormat, with anIntl.supportedValuesOf("currency")membership check sinceNumberFormataccepts any 3-letter-shaped code). Inputs with a real digit beyond the allowed decimals throw instead of silently rounding, and every caller — MCP tools, CLI commands,budget.ts,payment-choice.ts,runCeiling, and the approve handler — passes its currency through, so CLP produces"10000", USD"25.00", KWD"1.250".Changes beyond the helper:
amountSchema/decimalAmountinpackages/server/src/schemas.tsvalidate decimal places against the currency viasuperRefine, so{"value":"10000.5","currency":"CLP"}now returns400from this server instead of reaching Crossmint as a502.approveRequestre-normalizes the stored amount withtoDecimalStringat send time, which also fixes legacy rows persisted in the old"10000.00"format.shopify-catalog.ts:money.amountis in minor units (theprice.maxsearch filter multiplies by 100, i.e. cents), so it now divides by10^decimalsand formats with the currency's decimals instead of a fixed/100+toFixed(2). Theprice: { max: maxPrice * 100 }filter still assumes 2-decimal currencies — left as-is since the catalog filter is approximate, but worth knowing.CrossmintApiErroronly read a top-levelmessage. Crossmint's current 502 body is{error: true, message, code}, which that code already covers, so the generic "Crossmint request failed with 502" the user saw likely came from an older deployment or a non-JSON gateway body. The extraction is hardened anyway:bodyMessagenow also searcheserror/provider/providerError/detailsobjects for a nestedmessage/detail/errorstring.apps/webhad no test script; addedvitest runso the newbudget.test.tsruns underpnpm test.Test plan
pnpm build,pnpm lint,pnpm typecheck,pnpm test— all green (12/12 turbo tasks).packages/core/test/amount.test.ts— per-currency digits (USD/CLP/JPY/KWD/BHD),"10000"/"500"/"5.50"/"1.250"formatting, extra-decimal and unknown-currency throws.packages/core/test/errors.test.ts— nested message extraction.apps/web/lib/chat/budget.test.ts—budgetAmounts/parseBudgetAmount/budgetForemit"10000"for CLP.packages/server/test/handlers.test.ts—POST /v1/agent-card-requests/:id/approveon a CLP request sendsamount.value === "10000"to the mocked Crossmint client;amountSchemarejects{"value":"10000.5","currency":"CLP"}with400.Link to Devin session: https://crossmint.devinenterprise.com/sessions/d80cdb1ac71f4bef9ae514e311de28f3
Open in Devin Desktop: https://crossmint.devinenterprise.com/desktop/session/d80cdb1ac71f4bef9ae514e311de28f3?variant=devin
Requested by: @antonio-xm