first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
# money
## Purpose
Boundary Zod schema for `MoneyLike` — `{ amount: bigint, currency: string }`. Payment APIs work in minor units (cents, pence, satoshi, lamports) and must never round-trip through IEEE-754 floats because rounding losses on `0.10 + 0.20` are exactly the kind of defect that ends up in a compliance audit. JSON cannot natively encode `bigint`, so the schema accepts either a real `bigint` literal, a finite JSON-number, or a numeric string and coerces via `BigInt()`.
The currency code accepts 3-8 uppercase alphanumeric characters covering ISO 4217 (`USD`, `EUR`, `JPY`), crypto (`BTC`, `ETH`), stablecoins (`USDC`), and Solana micro-units (`LAMPORTS`).
## Features
- `z.preprocess`-backed amount coercion handles three input shapes (`bigint` / `number` / numeric `string`) and refuses everything else (objects, floats expressed as strings, `NaN`).
- Currency regex `^[A-Z0-9]+$` rejects lowercase, spaces, and mixed alphanumeric junk before any downstream lookup.
- `nonnegative()` on the bigint prevents negative-amount intents which providers universally reject.
- Inferred `Money` type drops in everywhere `MoneyLike` does because the structure is identical.
## Usage: Basic
```ts
import { MoneySchema } from "@suaveplan/api-plugin-payment/schemas/money";
const parsed = MoneySchema.parse({ amount: 999n, currency: "USD" });
// { amount: 999n, currency: "USD" }
```
## Usage: Advanced
```ts
const result = MoneySchema.safeParse(await c.req.json());
if (!result.success) {
return c.json({ error: "invalid_money", issues: result.error.issues }, 400);
}
const { amount, currency } = result.data;
const intent = await provider.createIntent({ amount: { amount, currency } });
```
## API Reference
- `MoneySchema` — `z.object({ amount: bigint (coerced), currency: string (3..8, [A-Z0-9]+) })`.
- `Money` — `z.infer<typeof MoneySchema>` — `{ amount: bigint; currency: string }`.
## Implementation Notes
`z.preprocess` is used rather than `z.coerce.bigint()` because the latter happily produces `BigInt("12.5")` → `RangeError` and the error message is opaque to API consumers. The custom preprocessor inspects the input shape, only forwards values that BigInt can safely convert, and lets the downstream `z.bigint()` issue produce a clean failure message. Floating-point inputs are intentionally rejected — a fractional amount in minor units is a programming error, not a roundable value.