diff --git a/.changeset/payment-option-safe-integer-amount.md b/.changeset/payment-option-safe-integer-amount.md new file mode 100644 index 0000000..0069538 --- /dev/null +++ b/.changeset/payment-option-safe-integer-amount.md @@ -0,0 +1,5 @@ +--- +"@agentcommercekit/ack-pay": patch +--- + +Reject unsafe integer numbers on the payment option amount number arm so valibot matches zod (and Number.isSafeInteger), keeping the string arm for amounts larger than the safe integer range. diff --git a/packages/ack-pay/src/schemas/payment-option.test.ts b/packages/ack-pay/src/schemas/payment-option.test.ts index 166b449..f6f016d 100644 --- a/packages/ack-pay/src/schemas/payment-option.test.ts +++ b/packages/ack-pay/src/schemas/payment-option.test.ts @@ -34,4 +34,11 @@ describe("paymentOptionSchema amount", () => { expect(acceptsAmount(amount)).toEqual({ valibot: false, zod: false }) }, ) + + it.each([Number.MAX_SAFE_INTEGER + 1, 1e21])( + "rejects an unsafe integer number amount %s", + (amount) => { + expect(acceptsAmount(amount)).toEqual({ valibot: false, zod: false }) + }, + ) }) diff --git a/packages/ack-pay/src/schemas/valibot.ts b/packages/ack-pay/src/schemas/valibot.ts index 75fc980..8de7239 100644 --- a/packages/ack-pay/src/schemas/valibot.ts +++ b/packages/ack-pay/src/schemas/valibot.ts @@ -14,7 +14,8 @@ const timestampSchema = v.pipe( export const paymentOptionSchema = v.object({ id: v.string(), amount: v.union([ - v.pipe(v.number(), v.integer(), v.gtValue(0)), + // Unsafe integers are not exact; use the string arm for large amounts. + v.pipe(v.number(), v.safeInteger(), v.gtValue(0)), positiveIntegerString, ]), decimals: v.pipe(v.number(), v.integer(), v.toMinValue(0)),