@krizaka/coupons 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ECCGFGSV.js → chunk-V2HCEF26.js} +346 -62
- package/dist/chunk-V2HCEF26.js.map +1 -0
- package/dist/{chunk-JMQO6PTK.js → chunk-XXFDAM5G.js} +43 -13
- package/dist/chunk-XXFDAM5G.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/settings.js +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/testkit.js +270 -47
- package/dist/testkit.js.map +1 -1
- package/package.json +10 -10
- package/dist/chunk-ECCGFGSV.js.map +0 -1
- package/dist/chunk-JMQO6PTK.js.map +0 -1
package/dist/testkit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testkit.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport { expect, it } from \"vitest\";\n\nimport type { Coupons } from \"./coupons\";\nimport { type Actor, COUPON_DEFAULTS, type CouponSettings, type CreditsPort, type NewCoupon, type Query } from \"./index\";\n\n/** What a store (through the module) is run against: the module on that store, a clock the test moves, and fakes for its neighbours. */\nexport interface CouponsHarness {\n coupons: Coupons;\n /** The query every call goes through (the transaction; a no-op for memory). */\n tx: Query;\n /** Moves the injected clock. */\n setNow(instant: Date): void;\n /** Changes the settings the module reads at each call. */\n setSettings(patch: Partial<CouponSettings>): void;\n /** A fresh account id (a row in the owner table, when the store has one). */\n newAccount(): Promise<string>;\n /** The messages appended to the outbox so far. */\n announced(): { type: string; id: string | undefined; payload: Record<string, unknown> }[];\n /** The audit actions recorded so far, with their target. */\n audited(): { action: string; target: { type: string; id: string } | null | undefined; detail: Record<string, unknown> | undefined }[];\n /** The credits ledger's calls (a fake, or the real one wrapped to record). */\n credits: { granted: Parameters<CreditsPort[\"grant\"]>[1][]; expired: string[] };\n /** The limiter: the attempts counted, and a way to refuse the next ones. */\n limiter: { attempts: { action: string; ip: string | null | undefined; subject: string | null | undefined }[]; refuse(retryAfterSeconds: number | null): void };\n}\n\n/** Midday on 26 October 2026 in Toronto: the day the scenarios start on. */\nexport const T0 = new Date(\"2026-10-26T16:00:00.000Z\");\nconst HOUR = 3600;\nconst at = (seconds: number) => new Date(T0.valueOf() + seconds * 1000);\nconst ADMIN: Actor = { id: null, label: \"Eric (admin)\" };\nconst START = at(-HOUR);\n\n/** A discount of 20 % for three months on a monthly plan. */\nexport const discount = (over: Partial<NewCoupon> = {}): NewCoupon => ({\n code: `PROMO${randomUUID().replace(/-/g, \"\").slice(0, 8)}`,\n kind: \"discount\",\n purpose: \"promo\",\n startsAt: START,\n percentOffBp: 2000,\n duration: \"repeating\",\n durationMonths: 3,\n ...over,\n});\n\n/** 20 free audits, at most 5 a day, valid 30 days. */\nexport const credit = (over: Partial<NewCoupon> = {}): NewCoupon => ({\n code: `DEMO${randomUUID().replace(/-/g, \"\").slice(0, 8)}`,\n kind: \"credit\",\n purpose: \"demo\",\n startsAt: START,\n credits: 20,\n capPerDay: 5,\n creditsValidDays: 30,\n onExhausted: \"stop\",\n ...over,\n});\n\nasync function code(work: Promise<unknown>): Promise<string> {\n try {\n await work;\n } catch (error) {\n return (error as { code: string }).code;\n }\n return \"answered\";\n}\n\n/**\n * What every coupons store must do, behind the same module: the window and the refusals, the counting rule of the uses, reservation,\n * confirmation and release, refunds, credit coupons, partners, the admin. `make` gives an EMPTY module, the clock at {@link T0},\n * `coupons.enabled` on.\n */\nexport function couponsContract(name: string, make: () => Promise<CouponsHarness>) {\n const ctx = (accountId: string, over: Record<string, unknown> = {}) => ({ mode: \"checkout\" as const, accountId, plan: \"pro\", amountMinor: 10_000, currency: \"cad\", ...over });\n const apply = (h: CouponsHarness, codeTyped: string, accountId: string, over: Record<string, unknown> = {}) =>\n h.coupons.apply(h.tx, { code: codeTyped, accountId, plan: \"pro\", amountMinor: 10_000, currency: \"cad\", idempotencyKey: randomUUID(), ...over });\n const made = async (h: CouponsHarness, over: Partial<NewCoupon> = {}) => h.coupons.admin.create(h.tx, ADMIN, discount({ currency: \"cad\", ...over }));\n const paid = (redemptionId: string, over: Record<string, unknown> = {}) => ({ redemptionId, paymentRef: `pi_${randomUUID().slice(0, 8)}`, amountPaidMinor: 8000, amountDiscountMinor: 2000, currency: \"cad\", ...over });\n\n it(`${name}: creates a coupon, reads its code without case, spaces or hyphens, refuses a taken code, and audits`, async () => {\n const h = await make();\n const c = await h.coupons.admin.create(h.tx, ADMIN, discount({ code: \"Summer-2026\" }));\n expect(c).toMatchObject({ code: \"Summer-2026\", codeKey: \"SUMMER2026\", status: \"active\", termsVersion: 1 });\n expect(await code(h.coupons.admin.create(h.tx, ADMIN, discount({ code: \"summer 2026\" })))).toBe(\"COUPON_CODE_TAKEN\");\n const a = await h.newAccount();\n for (const typed of [\" summer-2026 \", \"SUMMER 2026\", \"summer_2026\"]) expect(await h.coupons.validate(h.tx, typed, ctx(a))).toMatchObject({ ok: true, coupon: { id: c.id } });\n expect(h.audited().map((e) => e.action)).toEqual([\"coupon.created\"]);\n expect(h.audited()[0]).toMatchObject({ target: { type: \"coupon\", id: c.id }, detail: { code: \"Summer-2026\" } });\n });\n\n it(`${name}: the window opens at its start and closes at its end, to the second`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h, { startsAt: at(HOUR), endsAt: at(2 * HOUR) });\n const state = async (seconds: number) => {\n h.setNow(at(seconds));\n const v = await h.coupons.validate(h.tx, c.code, ctx(a));\n return v.ok ? \"ok\" : v.code;\n };\n expect(await state(HOUR - 1)).toBe(\"COUPON_NOT_STARTED\");\n expect(await state(HOUR)).toBe(\"ok\");\n expect(await state(2 * HOUR - 1)).toBe(\"ok\");\n expect(await state(2 * HOUR)).toBe(\"COUPON_EXPIRED\");\n });\n\n it(`${name}: pause, resume and archive; an archived coupon stays archived; each is audited`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n await h.coupons.admin.pause(h.tx, ADMIN, c.id);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({ ok: false, code: \"COUPON_PAUSED\" });\n await h.coupons.admin.resume(h.tx, ADMIN, c.id);\n expect((await h.coupons.validate(h.tx, c.code, ctx(a))).ok).toBe(true);\n await h.coupons.admin.archive(h.tx, ADMIN, c.id);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({ ok: false, code: \"COUPON_PAUSED\" });\n expect(await code(h.coupons.admin.resume(h.tx, ADMIN, c.id))).toBe(\"COUPON_BAD_INPUT\");\n expect(h.audited().map((e) => e.action)).toEqual([\"coupon.created\", \"coupon.paused\", \"coupon.resumed\", \"coupon.archived\"]);\n });\n\n it(`${name}: refuses with a typed code: unknown, kind, plan, minimum, currency, new customers, provider, too large, disabled`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const refusal = async (typed: string, over: Record<string, unknown> = {}) => {\n const v = await h.coupons.validate(h.tx, typed, ctx(a, over));\n return v.ok ? \"ok\" : v.code;\n };\n const plan = await made(h, { plans: [\"pro\"], minimumMinor: 5000 });\n const fresh = await made(h, { newCustomersOnly: true });\n h.setSettings({ maxPercentOff: 70 }); // a coupon is created within the bound, then the admin lowers it\n const big = await made(h, { percentOffBp: 6000 });\n h.setSettings({ maxPercentOff: 50 });\n const fixed = await made(h, { percentOffBp: null, amountOffMinor: 6000, currency: \"cad\", duration: \"once\", durationMonths: null });\n const cr = await h.coupons.admin.create(h.tx, ADMIN, credit());\n expect(await refusal(\"NOPE\")).toBe(\"COUPON_UNKNOWN\");\n expect(await refusal(\"???\")).toBe(\"COUPON_UNKNOWN\");\n expect(await refusal(plan.code, { plan: \"audit\" })).toBe(\"COUPON_NOT_FOR_PLAN\");\n expect(await refusal(plan.code, { amountMinor: 4999 })).toBe(\"COUPON_MINIMUM\");\n expect(await refusal(plan.code, { currency: \"usd\" })).toBe(\"COUPON_CURRENCY\");\n expect(await refusal(plan.code)).toBe(\"ok\");\n expect(await refusal(fresh.code, { hasPaidBefore: true })).toBe(\"COUPON_NEW_CUSTOMERS_ONLY\");\n expect(await refusal(fresh.code, { hasPaidBefore: false })).toBe(\"ok\");\n expect(await refusal(plan.code, { providerSupportsDiscounts: false })).toBe(\"COUPON_PROVIDER\");\n expect(await refusal(big.code)).toBe(\"COUPON_TOO_LARGE\");\n expect(await refusal(fixed.code)).toBe(\"COUPON_TOO_LARGE\");\n expect(await refusal(fixed.code, { amountMinor: 12_000 })).toBe(\"ok\");\n expect(await refusal(cr.code)).toBe(\"COUPON_KIND\");\n h.setSettings({ maxPercentOff: 70 });\n expect(await refusal(big.code)).toBe(\"ok\");\n h.setSettings({ enabled: false });\n expect(await refusal(plan.code)).toBe(\"COUPON_DISABLED\");\n });\n\n it(`${name}: counts an attempt before any lookup, and refuses past the limit with the time to wait`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n await h.coupons.validate(h.tx, c.code, ctx(a, { ip: \"203.0.113.9\" }));\n await h.coupons.validate(h.tx, \"WHATEVER\", ctx(a));\n expect(h.limiter.attempts).toEqual([\n { action: \"couponAttempt\", ip: \"203.0.113.9\", subject: a },\n { action: \"couponAttempt\", ip: undefined, subject: a },\n ]);\n h.limiter.refuse(42);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({ ok: false, code: \"COUPON_RATE_LIMITED\", retryAfterSeconds: 42 });\n await expect(apply(h, c.code, a)).rejects.toMatchObject({ code: \"COUPON_RATE_LIMITED\", status: 429, retryAfterSeconds: 42 });\n h.limiter.refuse(null);\n expect((await h.coupons.validate(h.tx, c.code, ctx(a))).ok).toBe(true);\n });\n\n it(`${name}: says what a discount gives in numbers (the provider computes the real amount and its taxes)`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const pct = await made(h);\n const fixed = await made(h, { percentOffBp: null, amountOffMinor: 500, currency: \"cad\", duration: \"once\", durationMonths: null });\n expect(await h.coupons.validate(h.tx, pct.code, ctx(a))).toMatchObject({ ok: true, effect: { kind: \"discount\", percentOffBp: 2000, duration: \"repeating\", durationMonths: 3, discountMinor: 2000, payableMinor: 8000 } });\n expect(await h.coupons.validate(h.tx, fixed.code, ctx(a, { amountMinor: 3900 }))).toMatchObject({ effect: { discountMinor: 500, payableMinor: 3400, amountOffMinor: 500 } });\n });\n\n it(`${name}: reserves a use under the counting rule: reserved uses count until their deadline, released ones never`, async () => {\n const h = await make();\n const [a1, a2, a3] = [await h.newAccount(), await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 2 });\n const r1 = await apply(h, c.code, a1);\n expect(await code(apply(h, c.code, a1))).toBe(\"COUPON_ACCOUNT_LIMIT\");\n await apply(h, c.code, a2);\n expect(await code(apply(h, c.code, a3))).toBe(\"COUPON_EXHAUSTED\");\n expect(await h.coupons.validate(h.tx, c.code, ctx(a3))).toEqual({ ok: false, code: \"COUPON_EXHAUSTED\" });\n expect(await h.coupons.release(h.tx, { redemptionId: r1.redemptionId })).toBe(true);\n expect((await apply(h, c.code, a3)).replayed).toBe(false);\n h.setNow(at(61 * 60)); // the two reservations still held lapse after 60 minutes\n expect((await h.coupons.validate(h.tx, c.code, ctx(a1))).ok).toBe(true);\n });\n\n it(`${name}: apply is idempotent by key, and a key is not shared between coupons or accounts`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h);\n const other = await made(h);\n const first = await apply(h, c.code, a1, { idempotencyKey: \"co-1\" });\n const again = await apply(h, c.code, a1, { idempotencyKey: \"co-1\" });\n expect(first.replayed).toBe(false);\n expect(again).toMatchObject({ redemptionId: first.redemptionId, replayed: true });\n expect(await code(apply(h, c.code, a2, { idempotencyKey: \"co-1\" }))).toBe(\"COUPON_KEY_REUSED\");\n expect(await code(apply(h, other.code, a1, { idempotencyKey: \"co-1\" }))).toBe(\"COUPON_KEY_REUSED\");\n expect(first.discount).toEqual({ couponId: c.id, termsVersion: 1, percentOffBp: 2000, duration: \"repeating\", durationMonths: 3 });\n expect(first.effect).toMatchObject({ discountMinor: 2000, payableMinor: 8000 });\n expect(first.reservedUntil).toEqual(at(HOUR));\n });\n\n it(`${name}: hands the provider terms for an amount-off coupon with its currency`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h, { percentOffBp: null, amountOffMinor: 500, currency: \"cad\", duration: \"once\", durationMonths: null });\n expect((await apply(h, c.code, a)).discount).toEqual({ couponId: c.id, termsVersion: 1, amountOffMinor: 500, currency: \"cad\", duration: \"once\" });\n });\n\n it(`${name}: confirms a use once, from the checkout's metadata or from the session when the attach never ran`, async () => {\n const h = await make();\n const [a1, a2, a3] = [await h.newAccount(), await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 5 });\n const r1 = await apply(h, c.code, a1);\n const p1 = paid(r1.redemptionId, { paymentRef: \"pi_1\" });\n expect(await h.coupons.redeem(h.tx, p1)).toBe(\"confirmed\");\n expect(await h.coupons.redeem(h.tx, p1)).toBe(\"already\");\n const r2 = await apply(h, c.code, a2);\n await h.coupons.attachCheckout(h.tx, r2.redemptionId, \"cs_2\", at(2 * HOUR));\n expect(await h.coupons.redeem(h.tx, { checkoutRef: \"cs_2\", paymentRef: \"pi_2\", amountPaidMinor: 8000, amountDiscountMinor: 2000, currency: \"cad\" })).toBe(\"confirmed\");\n const r3 = await apply(h, c.code, a3);\n expect(await h.coupons.redeem(h.tx, paid(r3.redemptionId, { paymentRef: \"pi_1\" }))).toBe(\"already\");\n expect(await h.coupons.redeem(h.tx, { redemptionId: randomUUID(), checkoutRef: \"cs_none\", paymentRef: \"pi_x\", amountPaidMinor: 1, amountDiscountMinor: 0, currency: \"cad\" })).toBe(\"unknown\");\n const [row] = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id, status: \"confirmed\" })).filter((r) => r.id === r1.redemptionId);\n expect(row).toMatchObject({ status: \"confirmed\", paymentRef: \"pi_1\", amountPaidMinor: 8000, amountDiscountMinor: 2000, overLimit: false, accountId: a1 });\n });\n\n it(`${name}: attaching the session makes the reservation outlive it`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n const r = await apply(h, c.code, a);\n await h.coupons.attachCheckout(h.tx, r.redemptionId, \"cs_9\", at(3 * HOUR));\n h.setNow(at(3 * HOUR + 4 * 60));\n expect(await h.coupons.release(h.tx, { checkoutRef: \"cs_9\" })).toBe(true);\n expect(await code(h.coupons.attachCheckout(h.tx, randomUUID(), \"cs_x\"))).toBe(\"COUPON_REDEMPTION_NOT_FOUND\");\n });\n\n it(`${name}: a payment after the release is honoured and flagged: released becomes confirmed with over_limit`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 1 });\n const r1 = await apply(h, c.code, a1);\n await h.coupons.release(h.tx, { redemptionId: r1.redemptionId });\n const r2 = await apply(h, c.code, a2);\n await h.coupons.redeem(h.tx, paid(r2.redemptionId));\n expect(await h.coupons.redeem(h.tx, paid(r1.redemptionId))).toBe(\"confirmed\");\n const late = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id })).find((r) => r.id === r1.redemptionId);\n expect(late).toMatchObject({ status: \"confirmed\", overLimit: true });\n expect(await h.coupons.validate(h.tx, c.code, ctx(await h.newAccount()))).toEqual({ ok: false, code: \"COUPON_EXHAUSTED\" });\n });\n\n it(`${name}: the sweeper releases reservations past their deadline, and a released one is not released twice`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h);\n const r1 = await apply(h, c.code, a1);\n await apply(h, c.code, a2);\n expect(await h.coupons.releaseExpired(h.tx)).toBe(0);\n h.setNow(at(HOUR));\n expect(await h.coupons.releaseExpired(h.tx)).toBe(2);\n expect(await h.coupons.releaseExpired(h.tx)).toBe(0);\n expect(await h.coupons.release(h.tx, { redemptionId: r1.redemptionId })).toBe(false);\n expect(await h.coupons.release(h.tx, { redemptionId: randomUUID() })).toBe(false);\n expect(h.announced().filter((m) => m.type === \"evt.coupon.released.v1\")).toHaveLength(2);\n });\n\n it(`${name}: a refund adds up and never changes the status; a partial one keeps the use, a full one gives it back only if the setting says so`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 1 });\n const r1 = await apply(h, c.code, a1);\n await h.coupons.redeem(h.tx, paid(r1.redemptionId, { paymentRef: \"pi_a\", amountPaidMinor: 8000 }));\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 2000 })).toBe(\"refunded\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 2000 })).toBe(\"already\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 1000 })).toBe(\"already\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_zz\", amountRefunded: 1 })).toBe(\"unknown\");\n expect(await h.coupons.refund(h.tx, { paymentRef: null, invoiceRef: null, amountRefunded: 1 })).toBe(\"unknown\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 8000 })).toBe(\"refunded\");\n const row = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id }))[0]!;\n expect(row).toMatchObject({ status: \"confirmed\", amountRefundedMinor: 8000, useRestored: false });\n expect(await code(apply(h, c.code, a2))).toBe(\"COUPON_EXHAUSTED\");\n\n const b = await made(h, { maxRedemptions: 1 });\n h.setSettings({ refundRestoresUse: true });\n const rb = await apply(h, b.code, a1);\n await h.coupons.redeem(h.tx, paid(rb.redemptionId, { paymentRef: \"pi_b\", amountPaidMinor: 8000 }));\n await h.coupons.refund(h.tx, { paymentRef: \"pi_b\", amountRefunded: 7999 });\n expect(await code(apply(h, b.code, a2))).toBe(\"COUPON_EXHAUSTED\");\n await h.coupons.refund(h.tx, { invoiceRef: \"pi_b\", amountRefunded: 8000 });\n expect((await apply(h, b.code, a2)).replayed).toBe(false);\n expect(h.announced().filter((m) => m.type === \"evt.coupon.refunded.v1\").map((m) => m.payload.useRestored)).toEqual([false, false, false, true]);\n });\n\n it(`${name}: the terms are locked after the first use; the rules stay editable; a change is audited with before and after`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n const before = await h.coupons.admin.update(h.tx, ADMIN, c.id, { percentOffBp: 1500 });\n expect(before.percentOffBp).toBe(1500);\n await apply(h, c.code, a);\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, c.id, { percentOffBp: 3000 }))).toBe(\"COUPON_TERMS_LOCKED\");\n const after = await h.coupons.admin.update(h.tx, ADMIN, c.id, { maxRedemptions: 10, endsAt: at(5 * HOUR) });\n expect(after).toMatchObject({ maxRedemptions: 10, percentOffBp: 1500 });\n expect(await h.coupons.admin.update(h.tx, ADMIN, c.id, { maxRedemptions: 10 })).toMatchObject({ maxRedemptions: 10 });\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, c.id, { endsAt: START }))).toBe(\"COUPON_BAD_INPUT\");\n const entry = h.audited().filter((e) => e.action === \"coupon.updated\").at(-1)!;\n expect(entry.detail).toMatchObject({ changed: [\"maxRedemptions\", \"endsAt\"], before: { maxRedemptions: null, endsAt: null }, after: { maxRedemptions: 10 } });\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, randomUUID(), { maxRedemptions: 1 }))).toBe(\"COUPON_NOT_FOUND\");\n });\n\n it(`${name}: the admin cannot create what the rules refuse: a free discount, a fixed amount forever, a credit coupon without a cap of its own`, async () => {\n const h = await make();\n const bad = (over: Partial<NewCoupon>) => code(h.coupons.admin.create(h.tx, ADMIN, discount({ currency: \"cad\", ...over })));\n expect(await bad({ percentOffBp: 10_000 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ percentOffBp: 5001 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ percentOffBp: null, amountOffMinor: 500, duration: \"forever\", durationMonths: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ percentOffBp: null, amountOffMinor: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ amountOffMinor: 5 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ duration: \"once\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ code: \"ABC\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ code: \"bad!code123\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ endsAt: START })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ credits: 5 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ purpose: \"partner\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ partnerId: randomUUID() })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ purpose: \"partner\", partnerId: randomUUID() })).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n const credits = (over: Partial<NewCoupon>) => code(h.coupons.admin.create(h.tx, ADMIN, credit(over)));\n expect(await credits({ credits: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ creditsValidDays: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ onExhausted: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ percentOffBp: 1000 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ capPerDay: 0 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({})).toBe(\"answered\");\n });\n\n it(`${name}: a credit coupon grants capped credits that expire, once per key, and the use counts`, async () => {\n const h = await make();\n const [a, b] = [await h.newAccount(), await h.newAccount()];\n const c = await h.coupons.admin.create(h.tx, ADMIN, credit({ capPerMonth: 12, maxRedemptions: 1, onExhausted: \"fallback_paid\" }));\n expect(await h.coupons.validate(h.tx, c.code, { mode: \"redeem\", accountId: a })).toMatchObject({ ok: true, effect: { kind: \"credit\", credits: 20, capPerDay: 5, capPerMonth: 12, validDays: 30, onExhausted: \"fallback_paid\" } });\n const got = await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" });\n expect(got).toMatchObject({ credits: 20, replayed: false });\n expect(got.expiresAt).toEqual(at(30 * 24 * HOUR));\n expect(h.credits.granted).toEqual([{ accountId: a, amount: 20, capPerDay: 5, capPerMonth: 12, expiresAt: at(30 * 24 * HOUR), onExhausted: \"fallback_paid\", source: `coupon:${got.redemptionId}`, idempotencyKey: `coupon-${got.redemptionId}` }]);\n expect((await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" })).replayed).toBe(true);\n expect(h.credits.granted).toHaveLength(1);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-1\" }))).toBe(\"COUPON_KEY_REUSED\");\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-2\" }))).toBe(\"COUPON_EXHAUSTED\");\n const kind = await made(h);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: kind.code, accountId: b, idempotencyKey: \"rc-3\" }))).toBe(\"COUPON_KIND\");\n expect(await code(apply(h, c.code, b))).toBe(\"COUPON_KIND\");\n });\n\n it(`${name}: revoking a credit redemption writes off what is left, keeps the use counted, and refuses what is not a credit`, async () => {\n const h = await make();\n const [a, b] = [await h.newAccount(), await h.newAccount()];\n const c = await h.coupons.admin.create(h.tx, ADMIN, credit({ maxRedemptions: 1 }));\n const got = await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" });\n await h.coupons.revoke(h.tx, ADMIN, got.redemptionId);\n await h.coupons.revoke(h.tx, ADMIN, got.redemptionId);\n expect(h.credits.expired).toEqual([got.grantId]);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-2\" }))).toBe(\"COUPON_EXHAUSTED\");\n const d = await made(h);\n const r = await apply(h, d.code, a);\n expect(await code(h.coupons.revoke(h.tx, ADMIN, r.redemptionId))).toBe(\"COUPON_NOT_REVOCABLE\");\n expect(await code(h.coupons.revoke(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_REDEMPTION_NOT_FOUND\");\n expect(h.audited().filter((e) => e.action === \"coupon.revoked\")).toHaveLength(1);\n });\n\n it(`${name}: announces the redemption with the partner and the amounts, and the last allowed use, once each`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const partner = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Marie Tremblay\", contactEmail: \"Marie@Example.com\" });\n const c = await made(h, { purpose: \"partner\", partnerId: partner.id, maxRedemptions: 2 });\n for (const [i, a] of [a1, a2].entries()) {\n const r = await apply(h, c.code, a);\n await h.coupons.redeem(h.tx, paid(r.redemptionId, { paymentRef: `pi_${i}` }));\n }\n const redeemed = h.announced().filter((m) => m.type === \"evt.coupon.redeemed.v1\");\n expect(redeemed).toHaveLength(2);\n expect(redeemed[0]!.payload).toMatchObject({ couponId: c.id, kind: \"discount\", purpose: \"partner\", partnerId: partner.id, amountPaidMinor: 8000, amountDiscountMinor: 2000, currency: \"cad\", credits: null, overLimit: false });\n expect(h.announced().filter((m) => m.type === \"evt.coupon.exhausted.v1\")).toHaveLength(1);\n });\n\n it(`${name}: partners: the view is net of refunds and never names a customer; erasing keeps the numbers and blanks the person`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const partner = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Marie Tremblay\", contactEmail: \"marie@example.com\" });\n const other = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Paul\" });\n const c = await made(h, { purpose: \"partner\", partnerId: partner.id });\n const d = await h.coupons.admin.create(h.tx, ADMIN, credit({ purpose: \"partner\", partnerId: partner.id }));\n const r1 = await apply(h, c.code, a1);\n await h.coupons.redeem(h.tx, paid(r1.redemptionId, { paymentRef: \"pi_p1\", amountPaidMinor: 10_000, amountDiscountMinor: 2000 }));\n await h.coupons.refund(h.tx, { paymentRef: \"pi_p1\", amountRefunded: 1000 });\n await h.coupons.redeemCredit(h.tx, { code: d.code, accountId: a2, idempotencyKey: \"rc-p\" });\n const report = await h.coupons.admin.partners.report(h.tx, ADMIN, partner.id);\n expect(report).toEqual(expect.arrayContaining([{ couponId: c.id, code: c.code, currency: \"cad\", redemptions: 1, discountMinor: 2000, revenueMinor: 9000, creditsGranted: 0 }, { couponId: d.id, code: d.code, currency: null, redemptions: 1, discountMinor: 0, revenueMinor: 0, creditsGranted: 20 }]));\n expect(JSON.stringify(report)).not.toContain(a1);\n expect(await h.coupons.admin.partners.report(h.tx, ADMIN, other.id)).toEqual([]);\n expect(await h.coupons.admin.partners.report(h.tx, ADMIN, partner.id, { from: at(HOUR), to: at(2 * HOUR) })).toEqual([]);\n expect(h.audited().filter((e) => e.action === \"partner.report_read\")).toHaveLength(3);\n expect(await code(h.coupons.admin.partners.report(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n\n const exported = await h.coupons.admin.partners.export(h.tx, partner.id);\n expect(exported.partner).toMatchObject({ displayName: \"Marie Tremblay\", contactEmail: \"marie@example.com\" });\n const erased = await h.coupons.admin.partners.erase(h.tx, ADMIN, partner.id);\n expect(erased).toMatchObject({ displayName: null, contactEmail: null, status: \"archived\" });\n expect(erased.erasedAt).not.toBeNull();\n expect((await h.coupons.admin.partners.erase(h.tx, ADMIN, partner.id)).erasedAt).toEqual(erased.erasedAt);\n expect((await h.coupons.admin.partners.export(h.tx, partner.id)).rows).toHaveLength(2);\n expect((await h.coupons.admin.partners.list(h.tx)).map((p) => p.id)).toEqual([other.id]);\n expect((await h.coupons.admin.partners.list(h.tx, { includeArchived: true })).map((p) => p.id)).toEqual(expect.arrayContaining([partner.id, other.id]));\n });\n\n it(`${name}: partners: a name and a well-formed e-mail, archive and restore`, async () => {\n const h = await make();\n expect(await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"\" }))).toBe(\"COUPON_BAD_INPUT\");\n expect(await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"x\".repeat(81) }))).toBe(\"COUPON_BAD_INPUT\");\n expect(await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Marie\", contactEmail: \"nope\" }))).toBe(\"COUPON_BAD_INPUT\");\n const p = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \" Marie \" });\n expect(p).toMatchObject({ displayName: \"Marie\", contactEmail: null, status: \"active\" });\n expect((await h.coupons.admin.partners.archive(h.tx, ADMIN, p.id)).status).toBe(\"archived\");\n expect((await h.coupons.admin.partners.restore(h.tx, ADMIN, p.id)).status).toBe(\"active\");\n expect(await code(h.coupons.admin.partners.archive(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n expect(await h.coupons.admin.partners.get(h.tx, randomUUID())).toBeNull();\n });\n\n it(`${name}: a price drop pauses the fixed-amount coupons that would now be too large, and only those`, async () => {\n const h = await make();\n const small = await made(h, { percentOffBp: null, amountOffMinor: 1000, currency: \"cad\", duration: \"once\", durationMonths: null, plans: [\"pro\"] });\n const other = await made(h, { percentOffBp: null, amountOffMinor: 1000, currency: \"cad\", duration: \"once\", durationMonths: null, plans: [\"audit\"] });\n const percent = await made(h);\n expect(await h.coupons.admin.checkPrices(h.tx, [{ plan: \"pro\", amountMinor: 2000, currency: \"cad\" }])).toEqual([]);\n expect(await h.coupons.admin.checkPrices(h.tx, [{ plan: \"pro\", amountMinor: 1999, currency: \"cad\" }, { plan: \"pro\", amountMinor: 100, currency: \"usd\" }])).toEqual([small.id]);\n expect((await h.coupons.admin.get(h.tx, small.id))?.status).toBe(\"paused\");\n expect((await h.coupons.admin.get(h.tx, other.id))?.status).toBe(\"active\");\n expect((await h.coupons.admin.get(h.tx, percent.id))?.status).toBe(\"active\");\n expect(h.audited().filter((e) => e.action === \"coupon.paused_by_price\")).toHaveLength(1);\n });\n\n it(`${name}: lists coupons and redemptions with filters and pages`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const d = await made(h);\n const c = await h.coupons.admin.create(h.tx, ADMIN, credit());\n await h.coupons.admin.pause(h.tx, ADMIN, d.id);\n expect((await h.coupons.admin.list(h.tx)).map((x) => x.id)).toEqual(expect.arrayContaining([d.id, c.id]));\n expect((await h.coupons.admin.list(h.tx, { kind: \"credit\" })).map((x) => x.id)).toEqual([c.id]);\n expect((await h.coupons.admin.list(h.tx, { status: \"paused\" })).map((x) => x.id)).toEqual([d.id]);\n expect((await h.coupons.admin.list(h.tx, { purpose: \"demo\" })).map((x) => x.id)).toEqual([c.id]);\n expect(await h.coupons.admin.list(h.tx, { limit: 1, offset: 5 })).toEqual([]);\n expect(await h.coupons.admin.get(h.tx, randomUUID())).toBeNull();\n await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-l\" });\n expect(await h.coupons.admin.redemptions(h.tx, { couponId: c.id })).toHaveLength(1);\n expect(await h.coupons.admin.redemptions(h.tx, { status: \"released\" })).toEqual([]);\n expect(await h.coupons.admin.redemptions(h.tx, { from: at(HOUR) })).toEqual([]);\n expect(await h.coupons.admin.redemptions(h.tx, { to: at(-5 * HOUR) })).toEqual([]);\n });\n\n it(`${name}: generates a free code of the configured length from an alphabet without I, L, O and U`, async () => {\n const h = await make();\n h.setSettings({ generatedCodeLength: 12 });\n const generated = await h.coupons.admin.generateCode(h.tx);\n expect(generated).toMatch(/^[0-9A-HJKMNP-TV-Z]{12}$/);\n const c = await h.coupons.admin.create(h.tx, ADMIN, discount({ code: generated }));\n expect(c.codeKey).toBe(generated);\n });\n}\n\nexport { COUPON_DEFAULTS };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/testkit.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport { expect, it } from \"vitest\";\n\nimport type { Coupons } from \"./coupons\";\nimport {\n type Actor,\n COUPON_DEFAULTS,\n type CouponSettings,\n type CreditsPort,\n type NewCoupon,\n type Query,\n} from \"./index\";\n\n/** What a store (through the module) is run against: the module on that store, a clock the test moves, and fakes for its neighbours. */\nexport interface CouponsHarness {\n coupons: Coupons;\n /** The query every call goes through (the transaction; a no-op for memory). */\n tx: Query;\n /** Moves the injected clock. */\n setNow(instant: Date): void;\n /** Changes the settings the module reads at each call. */\n setSettings(patch: Partial<CouponSettings>): void;\n /** A fresh account id (a row in the owner table, when the store has one). */\n newAccount(): Promise<string>;\n /** The messages appended to the outbox so far. */\n announced(): { type: string; id: string | undefined; payload: Record<string, unknown> }[];\n /** The audit actions recorded so far, with their target. */\n audited(): {\n action: string;\n target: { type: string; id: string } | null | undefined;\n detail: Record<string, unknown> | undefined;\n }[];\n /** The credits ledger's calls (a fake, or the real one wrapped to record). */\n credits: { granted: Parameters<CreditsPort[\"grant\"]>[1][]; expired: string[] };\n /** The limiter: the attempts counted, and a way to refuse the next ones. */\n limiter: {\n attempts: { action: string; ip: string | null | undefined; subject: string | null | undefined }[];\n refuse(retryAfterSeconds: number | null): void;\n };\n}\n\n/** Midday on 26 October 2026 in Toronto: the day the scenarios start on. */\nexport const T0 = new Date(\"2026-10-26T16:00:00.000Z\");\nconst HOUR = 3600;\nconst at = (seconds: number) => new Date(T0.valueOf() + seconds * 1000);\nconst ADMIN: Actor = { id: null, label: \"Eric (admin)\" };\nconst START = at(-HOUR);\n\n/** A discount of 20 % for three months on a monthly plan. */\nexport const discount = (over: Partial<NewCoupon> = {}): NewCoupon => ({\n code: `PROMO${randomUUID().replace(/-/g, \"\").slice(0, 8)}`,\n kind: \"discount\",\n purpose: \"promo\",\n startsAt: START,\n percentOffBp: 2000,\n duration: \"repeating\",\n durationMonths: 3,\n ...over,\n});\n\n/** 20 free audits, at most 5 a day, valid 30 days. */\nexport const credit = (over: Partial<NewCoupon> = {}): NewCoupon => ({\n code: `DEMO${randomUUID().replace(/-/g, \"\").slice(0, 8)}`,\n kind: \"credit\",\n purpose: \"demo\",\n startsAt: START,\n credits: 20,\n capPerDay: 5,\n creditsValidDays: 30,\n onExhausted: \"stop\",\n ...over,\n});\n\nasync function code(work: Promise<unknown>): Promise<string> {\n try {\n await work;\n } catch (error) {\n return (error as { code: string }).code;\n }\n return \"answered\";\n}\n\n/**\n * What every coupons store must do, behind the same module: the window and the refusals, the counting rule of the uses, reservation,\n * confirmation and release, refunds, credit coupons, partners, the admin. `make` gives an EMPTY module, the clock at {@link T0},\n * `coupons.enabled` on.\n */\nexport function couponsContract(name: string, make: () => Promise<CouponsHarness>) {\n const ctx = (accountId: string, over: Record<string, unknown> = {}) => ({\n mode: \"checkout\" as const,\n accountId,\n plan: \"pro\",\n amountMinor: 10_000,\n currency: \"cad\",\n ...over,\n });\n const apply = (h: CouponsHarness, codeTyped: string, accountId: string, over: Record<string, unknown> = {}) =>\n h.coupons.apply(h.tx, {\n code: codeTyped,\n accountId,\n plan: \"pro\",\n amountMinor: 10_000,\n currency: \"cad\",\n idempotencyKey: randomUUID(),\n ...over,\n });\n const made = async (h: CouponsHarness, over: Partial<NewCoupon> = {}) =>\n h.coupons.admin.create(h.tx, ADMIN, discount({ currency: \"cad\", ...over }));\n const paid = (redemptionId: string, over: Record<string, unknown> = {}) => ({\n redemptionId,\n paymentRef: `pi_${randomUUID().slice(0, 8)}`,\n amountPaidMinor: 8000,\n amountDiscountMinor: 2000,\n currency: \"cad\",\n ...over,\n });\n\n it(`${name}: creates a coupon, reads its code without case, spaces or hyphens, refuses a taken code, and audits`, async () => {\n const h = await make();\n const c = await h.coupons.admin.create(h.tx, ADMIN, discount({ code: \"Summer-2026\" }));\n expect(c).toMatchObject({ code: \"Summer-2026\", codeKey: \"SUMMER2026\", status: \"active\", termsVersion: 1 });\n expect(await code(h.coupons.admin.create(h.tx, ADMIN, discount({ code: \"summer 2026\" })))).toBe(\n \"COUPON_CODE_TAKEN\",\n );\n const a = await h.newAccount();\n for (const typed of [\" summer-2026 \", \"SUMMER 2026\", \"summer_2026\"])\n expect(await h.coupons.validate(h.tx, typed, ctx(a))).toMatchObject({ ok: true, coupon: { id: c.id } });\n expect(h.audited().map((e) => e.action)).toEqual([\"coupon.created\"]);\n expect(h.audited()[0]).toMatchObject({ target: { type: \"coupon\", id: c.id }, detail: { code: \"Summer-2026\" } });\n });\n\n it(`${name}: the window opens at its start and closes at its end, to the second`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h, { startsAt: at(HOUR), endsAt: at(2 * HOUR) });\n const state = async (seconds: number) => {\n h.setNow(at(seconds));\n const v = await h.coupons.validate(h.tx, c.code, ctx(a));\n return v.ok ? \"ok\" : v.code;\n };\n expect(await state(HOUR - 1)).toBe(\"COUPON_NOT_STARTED\");\n expect(await state(HOUR)).toBe(\"ok\");\n expect(await state(2 * HOUR - 1)).toBe(\"ok\");\n expect(await state(2 * HOUR)).toBe(\"COUPON_EXPIRED\");\n });\n\n it(`${name}: pause, resume and archive; an archived coupon stays archived; each is audited`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n await h.coupons.admin.pause(h.tx, ADMIN, c.id);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({ ok: false, code: \"COUPON_PAUSED\" });\n await h.coupons.admin.resume(h.tx, ADMIN, c.id);\n expect((await h.coupons.validate(h.tx, c.code, ctx(a))).ok).toBe(true);\n await h.coupons.admin.archive(h.tx, ADMIN, c.id);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({ ok: false, code: \"COUPON_PAUSED\" });\n expect(await code(h.coupons.admin.resume(h.tx, ADMIN, c.id))).toBe(\"COUPON_BAD_INPUT\");\n expect(h.audited().map((e) => e.action)).toEqual([\n \"coupon.created\",\n \"coupon.paused\",\n \"coupon.resumed\",\n \"coupon.archived\",\n ]);\n });\n\n it(`${name}: refuses with a typed code: unknown, kind, plan, minimum, currency, new customers, provider, too large, disabled`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const refusal = async (typed: string, over: Record<string, unknown> = {}) => {\n const v = await h.coupons.validate(h.tx, typed, ctx(a, over));\n return v.ok ? \"ok\" : v.code;\n };\n const plan = await made(h, { plans: [\"pro\"], minimumMinor: 5000 });\n const fresh = await made(h, { newCustomersOnly: true });\n h.setSettings({ maxPercentOff: 70 }); // a coupon is created within the bound, then the admin lowers it\n const big = await made(h, { percentOffBp: 6000 });\n h.setSettings({ maxPercentOff: 50 });\n const fixed = await made(h, {\n percentOffBp: null,\n amountOffMinor: 6000,\n currency: \"cad\",\n duration: \"once\",\n durationMonths: null,\n });\n const cr = await h.coupons.admin.create(h.tx, ADMIN, credit());\n expect(await refusal(\"NOPE\")).toBe(\"COUPON_UNKNOWN\");\n expect(await refusal(\"???\")).toBe(\"COUPON_UNKNOWN\");\n expect(await refusal(plan.code, { plan: \"audit\" })).toBe(\"COUPON_NOT_FOR_PLAN\");\n expect(await refusal(plan.code, { amountMinor: 4999 })).toBe(\"COUPON_MINIMUM\");\n expect(await refusal(plan.code, { currency: \"usd\" })).toBe(\"COUPON_CURRENCY\");\n expect(await refusal(plan.code)).toBe(\"ok\");\n expect(await refusal(fresh.code, { hasPaidBefore: true })).toBe(\"COUPON_NEW_CUSTOMERS_ONLY\");\n expect(await refusal(fresh.code, { hasPaidBefore: false })).toBe(\"ok\");\n expect(await refusal(plan.code, { providerSupportsDiscounts: false })).toBe(\"COUPON_PROVIDER\");\n expect(await refusal(big.code)).toBe(\"COUPON_TOO_LARGE\");\n expect(await refusal(fixed.code)).toBe(\"COUPON_TOO_LARGE\");\n expect(await refusal(fixed.code, { amountMinor: 12_000 })).toBe(\"ok\");\n expect(await refusal(cr.code)).toBe(\"COUPON_KIND\");\n h.setSettings({ maxPercentOff: 70 });\n expect(await refusal(big.code)).toBe(\"ok\");\n h.setSettings({ enabled: false });\n expect(await refusal(plan.code)).toBe(\"COUPON_DISABLED\");\n });\n\n it(`${name}: counts an attempt before any lookup, and refuses past the limit with the time to wait`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n await h.coupons.validate(h.tx, c.code, ctx(a, { ip: \"203.0.113.9\" }));\n await h.coupons.validate(h.tx, \"WHATEVER\", ctx(a));\n expect(h.limiter.attempts).toEqual([\n { action: \"couponAttempt\", ip: \"203.0.113.9\", subject: a },\n { action: \"couponAttempt\", ip: undefined, subject: a },\n ]);\n h.limiter.refuse(42);\n expect(await h.coupons.validate(h.tx, c.code, ctx(a))).toEqual({\n ok: false,\n code: \"COUPON_RATE_LIMITED\",\n retryAfterSeconds: 42,\n });\n await expect(apply(h, c.code, a)).rejects.toMatchObject({\n code: \"COUPON_RATE_LIMITED\",\n status: 429,\n retryAfterSeconds: 42,\n });\n h.limiter.refuse(null);\n expect((await h.coupons.validate(h.tx, c.code, ctx(a))).ok).toBe(true);\n });\n\n it(`${name}: says what a discount gives in numbers (the provider computes the real amount and its taxes)`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const pct = await made(h);\n const fixed = await made(h, {\n percentOffBp: null,\n amountOffMinor: 500,\n currency: \"cad\",\n duration: \"once\",\n durationMonths: null,\n });\n expect(await h.coupons.validate(h.tx, pct.code, ctx(a))).toMatchObject({\n ok: true,\n effect: {\n kind: \"discount\",\n percentOffBp: 2000,\n duration: \"repeating\",\n durationMonths: 3,\n discountMinor: 2000,\n payableMinor: 8000,\n },\n });\n expect(await h.coupons.validate(h.tx, fixed.code, ctx(a, { amountMinor: 3900 }))).toMatchObject({\n effect: { discountMinor: 500, payableMinor: 3400, amountOffMinor: 500 },\n });\n });\n\n it(`${name}: reserves a use under the counting rule: reserved uses count until their deadline, released ones never`, async () => {\n const h = await make();\n const [a1, a2, a3] = [await h.newAccount(), await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 2 });\n const r1 = await apply(h, c.code, a1);\n expect(await code(apply(h, c.code, a1))).toBe(\"COUPON_ACCOUNT_LIMIT\");\n await apply(h, c.code, a2);\n expect(await code(apply(h, c.code, a3))).toBe(\"COUPON_EXHAUSTED\");\n expect(await h.coupons.validate(h.tx, c.code, ctx(a3))).toEqual({ ok: false, code: \"COUPON_EXHAUSTED\" });\n expect(await h.coupons.release(h.tx, { redemptionId: r1.redemptionId })).toBe(true);\n expect((await apply(h, c.code, a3)).replayed).toBe(false);\n h.setNow(at(61 * 60)); // the two reservations still held lapse after 60 minutes\n expect((await h.coupons.validate(h.tx, c.code, ctx(a1))).ok).toBe(true);\n });\n\n it(`${name}: apply is idempotent by key, and a key is not shared between coupons or accounts`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h);\n const other = await made(h);\n const first = await apply(h, c.code, a1, { idempotencyKey: \"co-1\" });\n const again = await apply(h, c.code, a1, { idempotencyKey: \"co-1\" });\n expect(first.replayed).toBe(false);\n expect(again).toMatchObject({ redemptionId: first.redemptionId, replayed: true });\n expect(await code(apply(h, c.code, a2, { idempotencyKey: \"co-1\" }))).toBe(\"COUPON_KEY_REUSED\");\n expect(await code(apply(h, other.code, a1, { idempotencyKey: \"co-1\" }))).toBe(\"COUPON_KEY_REUSED\");\n expect(first.discount).toEqual({\n couponId: c.id,\n termsVersion: 1,\n percentOffBp: 2000,\n duration: \"repeating\",\n durationMonths: 3,\n });\n expect(first.effect).toMatchObject({ discountMinor: 2000, payableMinor: 8000 });\n expect(first.reservedUntil).toEqual(at(HOUR));\n });\n\n it(`${name}: hands the provider terms for an amount-off coupon with its currency`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h, {\n percentOffBp: null,\n amountOffMinor: 500,\n currency: \"cad\",\n duration: \"once\",\n durationMonths: null,\n });\n expect((await apply(h, c.code, a)).discount).toEqual({\n couponId: c.id,\n termsVersion: 1,\n amountOffMinor: 500,\n currency: \"cad\",\n duration: \"once\",\n });\n });\n\n it(`${name}: confirms a use once, from the checkout's metadata or from the session when the attach never ran`, async () => {\n const h = await make();\n const [a1, a2, a3] = [await h.newAccount(), await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 5 });\n const r1 = await apply(h, c.code, a1);\n const p1 = paid(r1.redemptionId, { paymentRef: \"pi_1\" });\n expect(await h.coupons.redeem(h.tx, p1)).toBe(\"confirmed\");\n expect(await h.coupons.redeem(h.tx, p1)).toBe(\"already\");\n const r2 = await apply(h, c.code, a2);\n await h.coupons.attachCheckout(h.tx, r2.redemptionId, \"cs_2\", at(2 * HOUR));\n expect(\n await h.coupons.redeem(h.tx, {\n checkoutRef: \"cs_2\",\n paymentRef: \"pi_2\",\n amountPaidMinor: 8000,\n amountDiscountMinor: 2000,\n currency: \"cad\",\n }),\n ).toBe(\"confirmed\");\n const r3 = await apply(h, c.code, a3);\n expect(await h.coupons.redeem(h.tx, paid(r3.redemptionId, { paymentRef: \"pi_1\" }))).toBe(\"already\");\n expect(\n await h.coupons.redeem(h.tx, {\n redemptionId: randomUUID(),\n checkoutRef: \"cs_none\",\n paymentRef: \"pi_x\",\n amountPaidMinor: 1,\n amountDiscountMinor: 0,\n currency: \"cad\",\n }),\n ).toBe(\"unknown\");\n const [row] = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id, status: \"confirmed\" })).filter(\n (r) => r.id === r1.redemptionId,\n );\n expect(row).toMatchObject({\n status: \"confirmed\",\n paymentRef: \"pi_1\",\n amountPaidMinor: 8000,\n amountDiscountMinor: 2000,\n overLimit: false,\n accountId: a1,\n });\n });\n\n it(`${name}: attaching the session makes the reservation outlive it`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n const r = await apply(h, c.code, a);\n await h.coupons.attachCheckout(h.tx, r.redemptionId, \"cs_9\", at(3 * HOUR));\n h.setNow(at(3 * HOUR + 4 * 60));\n expect(await h.coupons.release(h.tx, { checkoutRef: \"cs_9\" })).toBe(true);\n expect(await code(h.coupons.attachCheckout(h.tx, randomUUID(), \"cs_x\"))).toBe(\"COUPON_REDEMPTION_NOT_FOUND\");\n });\n\n it(`${name}: a payment after the release is honoured and flagged: released becomes confirmed with over_limit`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 1 });\n const r1 = await apply(h, c.code, a1);\n await h.coupons.release(h.tx, { redemptionId: r1.redemptionId });\n const r2 = await apply(h, c.code, a2);\n await h.coupons.redeem(h.tx, paid(r2.redemptionId));\n expect(await h.coupons.redeem(h.tx, paid(r1.redemptionId))).toBe(\"confirmed\");\n const late = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id })).find((r) => r.id === r1.redemptionId);\n expect(late).toMatchObject({ status: \"confirmed\", overLimit: true });\n expect(await h.coupons.validate(h.tx, c.code, ctx(await h.newAccount()))).toEqual({\n ok: false,\n code: \"COUPON_EXHAUSTED\",\n });\n });\n\n it(`${name}: the sweeper releases reservations past their deadline, and a released one is not released twice`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h);\n const r1 = await apply(h, c.code, a1);\n await apply(h, c.code, a2);\n expect(await h.coupons.releaseExpired(h.tx)).toBe(0);\n h.setNow(at(HOUR));\n expect(await h.coupons.releaseExpired(h.tx)).toBe(2);\n expect(await h.coupons.releaseExpired(h.tx)).toBe(0);\n expect(await h.coupons.release(h.tx, { redemptionId: r1.redemptionId })).toBe(false);\n expect(await h.coupons.release(h.tx, { redemptionId: randomUUID() })).toBe(false);\n expect(h.announced().filter((m) => m.type === \"evt.coupon.released.v1\")).toHaveLength(2);\n });\n\n it(`${name}: a refund adds up and never changes the status; a partial one keeps the use, a full one gives it back only if the setting says so`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const c = await made(h, { maxRedemptions: 1 });\n const r1 = await apply(h, c.code, a1);\n await h.coupons.redeem(h.tx, paid(r1.redemptionId, { paymentRef: \"pi_a\", amountPaidMinor: 8000 }));\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 2000 })).toBe(\"refunded\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 2000 })).toBe(\"already\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 1000 })).toBe(\"already\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_zz\", amountRefunded: 1 })).toBe(\"unknown\");\n expect(await h.coupons.refund(h.tx, { paymentRef: null, invoiceRef: null, amountRefunded: 1 })).toBe(\"unknown\");\n expect(await h.coupons.refund(h.tx, { paymentRef: \"pi_a\", amountRefunded: 8000 })).toBe(\"refunded\");\n const row = (await h.coupons.admin.redemptions(h.tx, { couponId: c.id }))[0]!;\n expect(row).toMatchObject({ status: \"confirmed\", amountRefundedMinor: 8000, useRestored: false });\n expect(await code(apply(h, c.code, a2))).toBe(\"COUPON_EXHAUSTED\");\n\n const b = await made(h, { maxRedemptions: 1 });\n h.setSettings({ refundRestoresUse: true });\n const rb = await apply(h, b.code, a1);\n await h.coupons.redeem(h.tx, paid(rb.redemptionId, { paymentRef: \"pi_b\", amountPaidMinor: 8000 }));\n await h.coupons.refund(h.tx, { paymentRef: \"pi_b\", amountRefunded: 7999 });\n expect(await code(apply(h, b.code, a2))).toBe(\"COUPON_EXHAUSTED\");\n await h.coupons.refund(h.tx, { invoiceRef: \"pi_b\", amountRefunded: 8000 });\n expect((await apply(h, b.code, a2)).replayed).toBe(false);\n expect(\n h\n .announced()\n .filter((m) => m.type === \"evt.coupon.refunded.v1\")\n .map((m) => m.payload.useRestored),\n ).toEqual([false, false, false, true]);\n });\n\n it(`${name}: the terms are locked after the first use; the rules stay editable; a change is audited with before and after`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const c = await made(h);\n const before = await h.coupons.admin.update(h.tx, ADMIN, c.id, { percentOffBp: 1500 });\n expect(before.percentOffBp).toBe(1500);\n await apply(h, c.code, a);\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, c.id, { percentOffBp: 3000 }))).toBe(\"COUPON_TERMS_LOCKED\");\n const after = await h.coupons.admin.update(h.tx, ADMIN, c.id, { maxRedemptions: 10, endsAt: at(5 * HOUR) });\n expect(after).toMatchObject({ maxRedemptions: 10, percentOffBp: 1500 });\n expect(await h.coupons.admin.update(h.tx, ADMIN, c.id, { maxRedemptions: 10 })).toMatchObject({\n maxRedemptions: 10,\n });\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, c.id, { endsAt: START }))).toBe(\"COUPON_BAD_INPUT\");\n const entry = h\n .audited()\n .filter((e) => e.action === \"coupon.updated\")\n .at(-1)!;\n expect(entry.detail).toMatchObject({\n changed: [\"maxRedemptions\", \"endsAt\"],\n before: { maxRedemptions: null, endsAt: null },\n after: { maxRedemptions: 10 },\n });\n expect(await code(h.coupons.admin.update(h.tx, ADMIN, randomUUID(), { maxRedemptions: 1 }))).toBe(\n \"COUPON_NOT_FOUND\",\n );\n });\n\n it(`${name}: the admin cannot create what the rules refuse: a free discount, a fixed amount forever, a credit coupon without a cap of its own`, async () => {\n const h = await make();\n const bad = (over: Partial<NewCoupon>) =>\n code(h.coupons.admin.create(h.tx, ADMIN, discount({ currency: \"cad\", ...over })));\n expect(await bad({ percentOffBp: 10_000 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ percentOffBp: 5001 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ percentOffBp: null, amountOffMinor: 500, duration: \"forever\", durationMonths: null })).toBe(\n \"COUPON_BAD_INPUT\",\n );\n expect(await bad({ percentOffBp: null, amountOffMinor: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ amountOffMinor: 5 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ duration: \"once\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ code: \"ABC\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ code: \"bad!code123\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ endsAt: START })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ credits: 5 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ purpose: \"partner\" })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ partnerId: randomUUID() })).toBe(\"COUPON_BAD_INPUT\");\n expect(await bad({ purpose: \"partner\", partnerId: randomUUID() })).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n const credits = (over: Partial<NewCoupon>) => code(h.coupons.admin.create(h.tx, ADMIN, credit(over)));\n expect(await credits({ credits: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ creditsValidDays: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ onExhausted: null })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ percentOffBp: 1000 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({ capPerDay: 0 })).toBe(\"COUPON_BAD_INPUT\");\n expect(await credits({})).toBe(\"answered\");\n });\n\n it(`${name}: a credit coupon grants capped credits that expire, once per key, and the use counts`, async () => {\n const h = await make();\n const [a, b] = [await h.newAccount(), await h.newAccount()];\n const c = await h.coupons.admin.create(\n h.tx,\n ADMIN,\n credit({ capPerMonth: 12, maxRedemptions: 1, onExhausted: \"fallback_paid\" }),\n );\n expect(await h.coupons.validate(h.tx, c.code, { mode: \"redeem\", accountId: a })).toMatchObject({\n ok: true,\n effect: {\n kind: \"credit\",\n credits: 20,\n capPerDay: 5,\n capPerMonth: 12,\n validDays: 30,\n onExhausted: \"fallback_paid\",\n },\n });\n const got = await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" });\n expect(got).toMatchObject({ credits: 20, replayed: false });\n expect(got.expiresAt).toEqual(at(30 * 24 * HOUR));\n expect(h.credits.granted).toEqual([\n {\n accountId: a,\n amount: 20,\n capPerDay: 5,\n capPerMonth: 12,\n expiresAt: at(30 * 24 * HOUR),\n onExhausted: \"fallback_paid\",\n source: `coupon:${got.redemptionId}`,\n idempotencyKey: `coupon-${got.redemptionId}`,\n },\n ]);\n expect((await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" })).replayed).toBe(\n true,\n );\n expect(h.credits.granted).toHaveLength(1);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-1\" }))).toBe(\n \"COUPON_KEY_REUSED\",\n );\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-2\" }))).toBe(\n \"COUPON_EXHAUSTED\",\n );\n const kind = await made(h);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: kind.code, accountId: b, idempotencyKey: \"rc-3\" }))).toBe(\n \"COUPON_KIND\",\n );\n expect(await code(apply(h, c.code, b))).toBe(\"COUPON_KIND\");\n });\n\n it(`${name}: revoking a credit redemption writes off what is left, keeps the use counted, and refuses what is not a credit`, async () => {\n const h = await make();\n const [a, b] = [await h.newAccount(), await h.newAccount()];\n const c = await h.coupons.admin.create(h.tx, ADMIN, credit({ maxRedemptions: 1 }));\n const got = await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-1\" });\n await h.coupons.revoke(h.tx, ADMIN, got.redemptionId);\n await h.coupons.revoke(h.tx, ADMIN, got.redemptionId);\n expect(h.credits.expired).toEqual([got.grantId]);\n expect(await code(h.coupons.redeemCredit(h.tx, { code: c.code, accountId: b, idempotencyKey: \"rc-2\" }))).toBe(\n \"COUPON_EXHAUSTED\",\n );\n const d = await made(h);\n const r = await apply(h, d.code, a);\n expect(await code(h.coupons.revoke(h.tx, ADMIN, r.redemptionId))).toBe(\"COUPON_NOT_REVOCABLE\");\n expect(await code(h.coupons.revoke(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_REDEMPTION_NOT_FOUND\");\n expect(h.audited().filter((e) => e.action === \"coupon.revoked\")).toHaveLength(1);\n });\n\n it(`${name}: announces the redemption with the partner and the amounts, and the last allowed use, once each`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const partner = await h.coupons.admin.partners.create(h.tx, ADMIN, {\n displayName: \"Marie Tremblay\",\n contactEmail: \"Marie@Example.com\",\n });\n const c = await made(h, { purpose: \"partner\", partnerId: partner.id, maxRedemptions: 2 });\n for (const [i, a] of [a1, a2].entries()) {\n const r = await apply(h, c.code, a);\n await h.coupons.redeem(h.tx, paid(r.redemptionId, { paymentRef: `pi_${i}` }));\n }\n const redeemed = h.announced().filter((m) => m.type === \"evt.coupon.redeemed.v1\");\n expect(redeemed).toHaveLength(2);\n expect(redeemed[0]!.payload).toMatchObject({\n couponId: c.id,\n kind: \"discount\",\n purpose: \"partner\",\n partnerId: partner.id,\n amountPaidMinor: 8000,\n amountDiscountMinor: 2000,\n currency: \"cad\",\n credits: null,\n overLimit: false,\n });\n expect(h.announced().filter((m) => m.type === \"evt.coupon.exhausted.v1\")).toHaveLength(1);\n });\n\n it(`${name}: partners: the view is net of refunds and never names a customer; erasing keeps the numbers and blanks the person`, async () => {\n const h = await make();\n const [a1, a2] = [await h.newAccount(), await h.newAccount()];\n const partner = await h.coupons.admin.partners.create(h.tx, ADMIN, {\n displayName: \"Marie Tremblay\",\n contactEmail: \"marie@example.com\",\n });\n const other = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Paul\" });\n const c = await made(h, { purpose: \"partner\", partnerId: partner.id });\n const d = await h.coupons.admin.create(h.tx, ADMIN, credit({ purpose: \"partner\", partnerId: partner.id }));\n const r1 = await apply(h, c.code, a1);\n await h.coupons.redeem(\n h.tx,\n paid(r1.redemptionId, { paymentRef: \"pi_p1\", amountPaidMinor: 10_000, amountDiscountMinor: 2000 }),\n );\n await h.coupons.refund(h.tx, { paymentRef: \"pi_p1\", amountRefunded: 1000 });\n await h.coupons.redeemCredit(h.tx, { code: d.code, accountId: a2, idempotencyKey: \"rc-p\" });\n const report = await h.coupons.admin.partners.report(h.tx, ADMIN, partner.id);\n expect(report).toEqual(\n expect.arrayContaining([\n {\n couponId: c.id,\n code: c.code,\n currency: \"cad\",\n redemptions: 1,\n discountMinor: 2000,\n revenueMinor: 9000,\n creditsGranted: 0,\n },\n {\n couponId: d.id,\n code: d.code,\n currency: null,\n redemptions: 1,\n discountMinor: 0,\n revenueMinor: 0,\n creditsGranted: 20,\n },\n ]),\n );\n expect(JSON.stringify(report)).not.toContain(a1);\n expect(await h.coupons.admin.partners.report(h.tx, ADMIN, other.id)).toEqual([]);\n expect(\n await h.coupons.admin.partners.report(h.tx, ADMIN, partner.id, { from: at(HOUR), to: at(2 * HOUR) }),\n ).toEqual([]);\n expect(h.audited().filter((e) => e.action === \"partner.report_read\")).toHaveLength(3);\n expect(await code(h.coupons.admin.partners.report(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n\n const exported = await h.coupons.admin.partners.export(h.tx, partner.id);\n expect(exported.partner).toMatchObject({ displayName: \"Marie Tremblay\", contactEmail: \"marie@example.com\" });\n const erased = await h.coupons.admin.partners.erase(h.tx, ADMIN, partner.id);\n expect(erased).toMatchObject({ displayName: null, contactEmail: null, status: \"archived\" });\n expect(erased.erasedAt).not.toBeNull();\n expect((await h.coupons.admin.partners.erase(h.tx, ADMIN, partner.id)).erasedAt).toEqual(erased.erasedAt);\n expect((await h.coupons.admin.partners.export(h.tx, partner.id)).rows).toHaveLength(2);\n expect((await h.coupons.admin.partners.list(h.tx)).map((p) => p.id)).toEqual([other.id]);\n expect((await h.coupons.admin.partners.list(h.tx, { includeArchived: true })).map((p) => p.id)).toEqual(\n expect.arrayContaining([partner.id, other.id]),\n );\n });\n\n it(`${name}: partners: a name and a well-formed e-mail, archive and restore`, async () => {\n const h = await make();\n expect(await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"\" }))).toBe(\"COUPON_BAD_INPUT\");\n expect(await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"x\".repeat(81) }))).toBe(\n \"COUPON_BAD_INPUT\",\n );\n expect(\n await code(h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \"Marie\", contactEmail: \"nope\" })),\n ).toBe(\"COUPON_BAD_INPUT\");\n const p = await h.coupons.admin.partners.create(h.tx, ADMIN, { displayName: \" Marie \" });\n expect(p).toMatchObject({ displayName: \"Marie\", contactEmail: null, status: \"active\" });\n expect((await h.coupons.admin.partners.archive(h.tx, ADMIN, p.id)).status).toBe(\"archived\");\n expect((await h.coupons.admin.partners.restore(h.tx, ADMIN, p.id)).status).toBe(\"active\");\n expect(await code(h.coupons.admin.partners.archive(h.tx, ADMIN, randomUUID()))).toBe(\"COUPON_PARTNER_NOT_FOUND\");\n expect(await h.coupons.admin.partners.get(h.tx, randomUUID())).toBeNull();\n });\n\n it(`${name}: a price drop pauses the fixed-amount coupons that would now be too large, and only those`, async () => {\n const h = await make();\n const small = await made(h, {\n percentOffBp: null,\n amountOffMinor: 1000,\n currency: \"cad\",\n duration: \"once\",\n durationMonths: null,\n plans: [\"pro\"],\n });\n const other = await made(h, {\n percentOffBp: null,\n amountOffMinor: 1000,\n currency: \"cad\",\n duration: \"once\",\n durationMonths: null,\n plans: [\"audit\"],\n });\n const percent = await made(h);\n expect(await h.coupons.admin.checkPrices(h.tx, [{ plan: \"pro\", amountMinor: 2000, currency: \"cad\" }])).toEqual([]);\n expect(\n await h.coupons.admin.checkPrices(h.tx, [\n { plan: \"pro\", amountMinor: 1999, currency: \"cad\" },\n { plan: \"pro\", amountMinor: 100, currency: \"usd\" },\n ]),\n ).toEqual([small.id]);\n expect((await h.coupons.admin.get(h.tx, small.id))?.status).toBe(\"paused\");\n expect((await h.coupons.admin.get(h.tx, other.id))?.status).toBe(\"active\");\n expect((await h.coupons.admin.get(h.tx, percent.id))?.status).toBe(\"active\");\n expect(h.audited().filter((e) => e.action === \"coupon.paused_by_price\")).toHaveLength(1);\n });\n\n it(`${name}: lists coupons and redemptions with filters and pages`, async () => {\n const h = await make();\n const a = await h.newAccount();\n const d = await made(h);\n const c = await h.coupons.admin.create(h.tx, ADMIN, credit());\n await h.coupons.admin.pause(h.tx, ADMIN, d.id);\n expect((await h.coupons.admin.list(h.tx)).map((x) => x.id)).toEqual(expect.arrayContaining([d.id, c.id]));\n expect((await h.coupons.admin.list(h.tx, { kind: \"credit\" })).map((x) => x.id)).toEqual([c.id]);\n expect((await h.coupons.admin.list(h.tx, { status: \"paused\" })).map((x) => x.id)).toEqual([d.id]);\n expect((await h.coupons.admin.list(h.tx, { purpose: \"demo\" })).map((x) => x.id)).toEqual([c.id]);\n expect(await h.coupons.admin.list(h.tx, { limit: 1, offset: 5 })).toEqual([]);\n expect(await h.coupons.admin.get(h.tx, randomUUID())).toBeNull();\n await h.coupons.redeemCredit(h.tx, { code: c.code, accountId: a, idempotencyKey: \"rc-l\" });\n expect(await h.coupons.admin.redemptions(h.tx, { couponId: c.id })).toHaveLength(1);\n expect(await h.coupons.admin.redemptions(h.tx, { status: \"released\" })).toEqual([]);\n expect(await h.coupons.admin.redemptions(h.tx, { from: at(HOUR) })).toEqual([]);\n expect(await h.coupons.admin.redemptions(h.tx, { to: at(-5 * HOUR) })).toEqual([]);\n });\n\n it(`${name}: generates a free code of the configured length from an alphabet without I, L, O and U`, async () => {\n const h = await make();\n h.setSettings({ generatedCodeLength: 12 });\n const generated = await h.coupons.admin.generateCode(h.tx);\n expect(generated).toMatch(/^[0-9A-HJKMNP-TV-Z]{12}$/);\n const c = await h.coupons.admin.create(h.tx, ADMIN, discount({ code: generated }));\n expect(c.codeKey).toBe(generated);\n });\n}\n\nexport { COUPON_DEFAULTS };\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/coupons",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Discount and credit coupons the admin manages, for every Krizaka product: a code with a window and limits, reserved at checkout under a row lock, confirmed once on the payment event, released when unpaid, refunded; partners for attribution. The database is the source of truth; the provider applies the discount from immutable terms; credit coupons grant capped credits. No database client, no wall clock, no network.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
"typescript": "^5.9.3",
|
|
58
58
|
"vitest": "^4.1.11",
|
|
59
59
|
"zod": "^4.6.7",
|
|
60
|
-
"@krizaka/
|
|
61
|
-
"@krizaka/
|
|
62
|
-
"@krizaka/
|
|
63
|
-
"@krizaka/
|
|
64
|
-
"@krizaka/
|
|
65
|
-
"@krizaka/
|
|
60
|
+
"@krizaka/audit": "^0.1.6",
|
|
61
|
+
"@krizaka/config": "^0.20.0",
|
|
62
|
+
"@krizaka/payments": "^0.7.1",
|
|
63
|
+
"@krizaka/rate-limit": "^0.2.2",
|
|
64
|
+
"@krizaka/credits": "^0.1.4",
|
|
65
|
+
"@krizaka/outbox": "^0.1.4"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@krizaka/db": "^0.3.
|
|
69
|
-
"@krizaka/
|
|
70
|
-
"@krizaka/
|
|
68
|
+
"@krizaka/db": "^0.3.1",
|
|
69
|
+
"@krizaka/settings": "^0.6.0",
|
|
70
|
+
"@krizaka/time": "^0.2.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"vitest": ">=4",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kernel.ts","../src/events.ts","../src/persistence.ts","../src/admin.ts","../src/flows.ts","../src/coupons.ts","../src/memory.ts","../src/schema.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport { COUPONS_TOPIC, uuidOf } from \"./events\";\nimport { dbCouponStore } from \"./persistence\";\nimport {\n type Actor,\n type AuditPort,\n type Clock,\n type CouponSettings,\n type CouponStore,\n type CreditsPort,\n type OutboxPort,\n type Query,\n type RateLimitPort,\n} from \"./port\";\nimport { checkSettings } from \"./rules\";\n\nexport interface CouponsDeps {\n clock: Clock;\n /** The settings, read at each call so the admin's change applies at once: an object, or a function over the settings service. */\n settings: CouponSettings | (() => CouponSettings | Promise<CouponSettings>);\n /** The trail (`createAudit(...)` of `@krizaka/audit`): every admin change and every redemption is recorded in the caller's transaction. */\n audit: AuditPort;\n /** The limiter (`createLimiter(...)` of `@krizaka/rate-limit`): each attempt with a code counts one hit on `COUPON_ATTEMPT_ACTION`. */\n rateLimit: RateLimitPort;\n /** The credits ledger (`createCredits(...)` of `@krizaka/credits`): needed for credit coupons. */\n credits?: CreditsPort;\n /** The outbox (`createOutbox(...)` of `@krizaka/outbox`): the events are appended in the caller's transaction. */\n outbox?: OutboxPort;\n /** The store over a query (a pool or a transaction). Default: the PostgreSQL store on schema `coupons`. */\n storeFor?: (query: Query) => CouponStore;\n /** The Postgres schema, when the product renamed the module's (used by the default store). */\n schema?: string;\n /** Fresh ids; default random UUIDs. */\n newId?: () => string;\n}\n\n/** The system as an actor (a webhook, a sweeper). */\nexport const SYSTEM: Actor = { id: null, label: \"system\" };\n\n/** What the flows and the admin share: the clock, the settings, the store, the trail and the outbox. */\nexport interface Kernel {\n deps: CouponsDeps;\n now(): Date;\n settings(): Promise<CouponSettings>;\n store(query: Query): CouponStore;\n newId(): string;\n record(tx: Query, actor: Actor, action: string, target: { type: string; id: string }, detail?: Record<string, unknown>): Promise<void>;\n announce(tx: Query, type: string, seed: string, payload: Record<string, unknown>): Promise<void>;\n}\n\nexport function makeKernel(deps: CouponsDeps): Kernel {\n const storeFor = deps.storeFor ?? ((query: Query) => dbCouponStore(query, deps.schema ?? \"coupons\"));\n return {\n deps,\n now: () => deps.clock.now(),\n settings: async () => checkSettings(typeof deps.settings === \"function\" ? await deps.settings() : deps.settings),\n store: storeFor,\n newId: deps.newId ?? randomUUID,\n record: (tx, actor, action, target, detail = {}) => deps.audit.record(tx, { actorId: actor.id, actorLabel: actor.label, action, target, detail }),\n async announce(tx, type, seed, payload) {\n await deps.outbox?.append(tx, { topic: COUPONS_TOPIC, type, payload, id: uuidOf(seed) });\n },\n };\n}\n","import { createHash } from \"node:crypto\";\n\n/** The topic the coupons' events are appended to on the outbox. */\nexport const COUPONS_TOPIC = \"coupons\";\n\nexport const COUPON_EVENT = {\n redeemed: \"evt.coupon.redeemed.v1\",\n released: \"evt.coupon.released.v1\",\n refunded: \"evt.coupon.refunded.v1\",\n exhausted: \"evt.coupon.exhausted.v1\",\n} as const;\n\n/** A stable UUID (version 5 shaped) from a seed: the same fact announced twice is one outbox message. */\nexport function uuidOf(seed: string): string {\n const h = createHash(\"sha256\").update(`krizaka.coupons:${seed}`).digest();\n h[6] = (h[6]! & 0x0f) | 0x50;\n h[8] = (h[8]! & 0x3f) | 0x80;\n const x = h.subarray(0, 16).toString(\"hex\");\n return `${x.slice(0, 8)}-${x.slice(8, 12)}-${x.slice(12, 16)}-${x.slice(16, 20)}-${x.slice(20, 32)}`;\n}\n\nconst nullableString = { type: [\"string\", \"null\"] } as const;\nconst nullableInt = { type: [\"integer\", \"null\"] } as const;\n\n/** The JSON Schema of each event's payload. `evt.coupon.redeemed.v1` is the hook of the later commissions module: it carries the partner and the amounts. */\nexport const COUPON_EVENT_SCHEMAS = {\n [COUPON_EVENT.redeemed]: {\n type: \"object\",\n additionalProperties: false,\n required: [\"redemptionId\", \"couponId\", \"kind\", \"purpose\", \"partnerId\", \"amountPaidMinor\", \"amountDiscountMinor\", \"currency\", \"credits\", \"overLimit\"],\n properties: {\n redemptionId: { type: \"string\" },\n couponId: { type: \"string\" },\n kind: { enum: [\"discount\", \"credit\"] },\n purpose: { enum: [\"partner\", \"promo\", \"demo\", \"support\"] },\n partnerId: nullableString,\n amountPaidMinor: nullableInt,\n amountDiscountMinor: nullableInt,\n currency: nullableString,\n credits: nullableInt,\n overLimit: { type: \"boolean\" },\n },\n },\n [COUPON_EVENT.released]: { type: \"object\", additionalProperties: false, required: [\"redemptionId\", \"couponId\"], properties: { redemptionId: { type: \"string\" }, couponId: { type: \"string\" } } },\n [COUPON_EVENT.refunded]: {\n type: \"object\",\n additionalProperties: false,\n required: [\"redemptionId\", \"couponId\", \"partnerId\", \"amountRefundedMinor\", \"currency\", \"useRestored\"],\n properties: { redemptionId: { type: \"string\" }, couponId: { type: \"string\" }, partnerId: nullableString, amountRefundedMinor: { type: \"integer\", minimum: 0 }, currency: nullableString, useRestored: { type: \"boolean\" } },\n },\n [COUPON_EVENT.exhausted]: { type: \"object\", additionalProperties: false, required: [\"couponId\", \"partnerId\"], properties: { couponId: { type: \"string\" }, partnerId: nullableString } },\n} as const;\n","import { ident } from \"@krizaka/db\";\n\nimport {\n type Coupon,\n COUPON_ERROR,\n CouponError,\n type CouponPatch,\n type CouponStore,\n type Partner,\n type PartnerRow,\n type Query,\n type Redemption,\n type RedemptionPatch,\n} from \"./port\";\n\ntype Row = Record<string, unknown>;\nconst n = (v: unknown): number => Number(v);\nconst nOrNull = (v: unknown): number | null => (v === null || v === undefined ? null : Number(v));\nconst d = (v: unknown): Date => new Date(v as string | Date);\nconst dOrNull = (v: unknown): Date | null => (v === null || v === undefined ? null : new Date(v as string | Date));\n\nconst toCoupon = (r: Row): Coupon => ({\n id: r.id as string, code: r.code as string, codeKey: r.code_key as string, kind: r.kind as Coupon[\"kind\"], purpose: r.purpose as Coupon[\"purpose\"], partnerId: (r.partner_id as string | null) ?? null,\n status: r.status as Coupon[\"status\"], startsAt: d(r.starts_at), endsAt: dOrNull(r.ends_at), maxRedemptions: nOrNull(r.max_redemptions), maxPerAccount: n(r.max_per_account),\n newCustomersOnly: r.new_customers_only as boolean, plans: r.plans as string[], percentOffBp: nOrNull(r.percent_off_bp), amountOffMinor: nOrNull(r.amount_off_minor), currency: (r.currency as string | null) ?? null,\n duration: (r.duration as Coupon[\"duration\"]) ?? null, durationMonths: nOrNull(r.duration_months), minimumMinor: nOrNull(r.minimum_minor), credits: nOrNull(r.credits), capPerDay: nOrNull(r.cap_per_day),\n capPerMonth: nOrNull(r.cap_per_month), creditsValidDays: nOrNull(r.credits_valid_days), onExhausted: (r.on_exhausted as Coupon[\"onExhausted\"]) ?? null, termsVersion: n(r.terms_version),\n createdAt: d(r.created_at), createdBy: (r.created_by as string | null) ?? null, updatedAt: d(r.updated_at),\n});\n\nconst toPartner = (r: Row): Partner => ({\n id: r.id as string, displayName: (r.display_name as string | null) ?? null, contactEmail: (r.contact_email as string | null) ?? null, status: r.status as Partner[\"status\"],\n createdAt: d(r.created_at), createdBy: (r.created_by as string | null) ?? null, erasedAt: dOrNull(r.erased_at),\n});\n\nconst toRedemption = (r: Row): Redemption => ({\n id: r.id as string, couponId: r.coupon_id as string, accountId: (r.account_id as string | null) ?? null, partnerId: (r.partner_id as string | null) ?? null, status: r.status as Redemption[\"status\"],\n idempotencyKey: r.idempotency_key as string, reservedUntil: dOrNull(r.reserved_until), checkoutRef: (r.checkout_ref as string | null) ?? null, paymentRef: (r.payment_ref as string | null) ?? null,\n amountPaidMinor: nOrNull(r.amount_paid_minor), amountDiscountMinor: nOrNull(r.amount_discount_minor), amountRefundedMinor: n(r.amount_refunded_minor), useRestored: r.use_restored as boolean,\n currency: (r.currency as string | null) ?? null, creditGrantId: (r.credit_grant_id as string | null) ?? null, overLimit: r.over_limit as boolean, createdAt: d(r.created_at), confirmedAt: dOrNull(r.confirmed_at), endedAt: dOrNull(r.ended_at),\n});\n\n/** Field name to column, for the columns a patch may set. Nothing else ever reaches SQL text. */\nconst COUPON_COLUMNS: Record<keyof CouponPatch, string> = {\n code: \"code\", purpose: \"purpose\", partnerId: \"partner_id\", status: \"status\", startsAt: \"starts_at\", endsAt: \"ends_at\", maxRedemptions: \"max_redemptions\", maxPerAccount: \"max_per_account\", newCustomersOnly: \"new_customers_only\",\n plans: \"plans\", percentOffBp: \"percent_off_bp\", amountOffMinor: \"amount_off_minor\", currency: \"currency\", duration: \"duration\", durationMonths: \"duration_months\", minimumMinor: \"minimum_minor\", credits: \"credits\",\n capPerDay: \"cap_per_day\", capPerMonth: \"cap_per_month\", creditsValidDays: \"credits_valid_days\", onExhausted: \"on_exhausted\", termsVersion: \"terms_version\",\n};\nconst REDEMPTION_COLUMNS: Record<keyof RedemptionPatch, string> = {\n status: \"status\", reservedUntil: \"reserved_until\", checkoutRef: \"checkout_ref\", paymentRef: \"payment_ref\", amountPaidMinor: \"amount_paid_minor\", amountDiscountMinor: \"amount_discount_minor\",\n amountRefundedMinor: \"amount_refunded_minor\", useRestored: \"use_restored\", currency: \"currency\", creditGrantId: \"credit_grant_id\", overLimit: \"over_limit\", confirmedAt: \"confirmed_at\", endedAt: \"ended_at\",\n};\nconst PARTNER_COLUMNS = { displayName: \"display_name\", contactEmail: \"contact_email\", status: \"status\", erasedAt: \"erased_at\" } as const;\n\n/** `col = $n, …` and the values, for the keys of `patch` that are set (a key with `undefined` is left alone). */\nfunction setClause(patch: Record<string, unknown>, columns: Record<string, string>, first: number): { sql: string; values: unknown[] } {\n const keys = Object.keys(patch).filter((key) => patch[key] !== undefined && key in columns);\n return { sql: keys.map((key, i) => `${columns[key]} = $${first + i}`).join(\", \"), values: keys.map((key) => patch[key]) };\n}\n\nconst COUNTS = `((status IN ('confirmed', 'revoked') AND NOT use_restored) OR (status = 'reserved' AND reserved_until > $NOW))`;\n\n/**\n * The PostgreSQL store of coupons, in schema `coupons` (see `couponsSchema`), over the `Query` it is given: the caller's\n * transaction, so the work commits or rolls back with the product's. `couponByKey(…, true)` is `SELECT … FOR UPDATE`: the last use of a\n * code is taken by one request at a time. The guarded transition of `updateRedemption` is one `UPDATE … WHERE status = ANY(…)`.\n * Times come from the caller (the injected clock), never from the database's `now()`.\n * @param schema the Postgres schema of the module, default `coupons`\n */\nexport function dbCouponStore(query: Query, schema = \"coupons\"): CouponStore {\n const s = ident(schema);\n const coupon = `${s}.coupon`;\n const partner = `${s}.partner`;\n const redemption = `${s}.redemption`;\n return {\n async insertCoupon(c) {\n try {\n await query(\n `INSERT INTO ${coupon} (id, code, code_key, kind, purpose, partner_id, status, starts_at, ends_at, max_redemptions, max_per_account, new_customers_only, plans, percent_off_bp, amount_off_minor, currency, duration, duration_months, minimum_minor, credits, cap_per_day, cap_per_month, credits_valid_days, on_exhausted, terms_version, created_at, created_by, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)`,\n [c.id, c.code, c.codeKey, c.kind, c.purpose, c.partnerId, c.status, c.startsAt, c.endsAt, c.maxRedemptions, c.maxPerAccount, c.newCustomersOnly, c.plans, c.percentOffBp, c.amountOffMinor, c.currency, c.duration, c.durationMonths, c.minimumMinor, c.credits, c.capPerDay, c.capPerMonth, c.creditsValidDays, c.onExhausted, c.termsVersion, c.createdAt, c.createdBy, c.updatedAt],\n );\n } catch (error) {\n if ((error as { code?: string }).code === \"23505\") throw new CouponError(COUPON_ERROR.codeTaken, \"This code is already used\");\n throw error;\n }\n },\n async couponById(id, forUpdate = false) {\n const [row] = await query<Row>(`SELECT * FROM ${coupon} WHERE id = $1${forUpdate ? \" FOR UPDATE\" : \"\"}`, [id]);\n return row ? toCoupon(row) : null;\n },\n async couponByKey(codeKey, forUpdate = false) {\n const [row] = await query<Row>(`SELECT * FROM ${coupon} WHERE code_key = $1${forUpdate ? \" FOR UPDATE\" : \"\"}`, [codeKey]);\n return row ? toCoupon(row) : null;\n },\n async updateCoupon(id, patch, at) {\n const set = setClause(patch, COUPON_COLUMNS, 3);\n const [row] = await query<Row>(`UPDATE ${coupon} SET ${set.sql ? `${set.sql}, ` : \"\"}updated_at = $2 WHERE id = $1 RETURNING *`, [id, at, ...set.values]);\n return row ? toCoupon(row) : null;\n },\n async listCoupons(f) {\n const rows = await query<Row>(\n `SELECT * FROM ${coupon} WHERE ($1::text IS NULL OR status = $1) AND ($2::text IS NULL OR kind = $2) AND ($3::text IS NULL OR purpose = $3) AND ($4::uuid IS NULL OR partner_id = $4)\n ORDER BY created_at DESC, id LIMIT $5 OFFSET $6`,\n [f.status ?? null, f.kind ?? null, f.purpose ?? null, f.partnerId ?? null, f.limit, f.offset],\n );\n return rows.map(toCoupon);\n },\n async hasRedemptions(couponId) {\n return (await query(`SELECT 1 FROM ${redemption} WHERE coupon_id = $1 LIMIT 1`, [couponId])).length > 0;\n },\n async insertPartner(p) {\n await query(`INSERT INTO ${partner} (id, display_name, contact_email, status, created_at, created_by, erased_at) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [p.id, p.displayName, p.contactEmail, p.status, p.createdAt, p.createdBy, p.erasedAt]);\n },\n async partnerById(id) {\n const [row] = await query<Row>(`SELECT * FROM ${partner} WHERE id = $1`, [id]);\n return row ? toPartner(row) : null;\n },\n async updatePartner(id, patch) {\n const set = setClause(patch, PARTNER_COLUMNS, 2);\n if (!set.sql) return (await this.partnerById(id)) ?? null;\n const [row] = await query<Row>(`UPDATE ${partner} SET ${set.sql} WHERE id = $1 RETURNING *`, [id, ...set.values]);\n return row ? toPartner(row) : null;\n },\n async listPartners(o) {\n const rows = await query<Row>(`SELECT * FROM ${partner} WHERE ($1 OR status = 'active') ORDER BY created_at DESC, id LIMIT $2 OFFSET $3`, [o.includeArchived, o.limit, o.offset]);\n return rows.map(toPartner);\n },\n async insertRedemption(r) {\n await query(\n `INSERT INTO ${redemption} (id, coupon_id, account_id, partner_id, status, idempotency_key, reserved_until, checkout_ref, payment_ref, amount_paid_minor, amount_discount_minor, amount_refunded_minor, use_restored, currency, credit_grant_id, over_limit, created_at, confirmed_at, ended_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`,\n [r.id, r.couponId, r.accountId, r.partnerId, r.status, r.idempotencyKey, r.reservedUntil, r.checkoutRef, r.paymentRef, r.amountPaidMinor, r.amountDiscountMinor, r.amountRefundedMinor, r.useRestored, r.currency, r.creditGrantId, r.overLimit, r.createdAt, r.confirmedAt, r.endedAt],\n );\n },\n async redemptionById(id) {\n const [row] = await query<Row>(`SELECT * FROM ${redemption} WHERE id = $1`, [id]);\n return row ? toRedemption(row) : null;\n },\n async redemptionByKey(key) {\n const [row] = await query<Row>(`SELECT * FROM ${redemption} WHERE idempotency_key = $1`, [key]);\n return row ? toRedemption(row) : null;\n },\n async redemptionByCheckoutRef(ref) {\n const [row] = await query<Row>(`SELECT * FROM ${redemption} WHERE checkout_ref = $1`, [ref]);\n return row ? toRedemption(row) : null;\n },\n async redemptionByPaymentRef(ref) {\n const [row] = await query<Row>(`SELECT * FROM ${redemption} WHERE payment_ref = $1`, [ref]);\n return row ? toRedemption(row) : null;\n },\n async updateRedemption(id, from, patch) {\n const set = setClause(patch, REDEMPTION_COLUMNS, 3);\n const [row] = await query<Row>(`UPDATE ${redemption} SET ${set.sql} WHERE id = $1 AND ($2::text[] IS NULL OR status = ANY($2::text[])) RETURNING *`, [id, from ? [...from] : null, ...set.values]);\n return row ? toRedemption(row) : null;\n },\n async usage(couponId, accountId, now) {\n const counted = COUNTS.replaceAll(\"$NOW\", () => \"$3\");\n const [row] = await query<{ total: number; account: number }>(\n `SELECT count(*) FILTER (WHERE ${counted})::int AS total, count(*) FILTER (WHERE ${counted} AND account_id = $2)::int AS account FROM ${redemption} WHERE coupon_id = $1`,\n [couponId, accountId, now],\n );\n return { total: row?.total ?? 0, account: row?.account ?? 0 };\n },\n async listRedemptions(f) {\n const rows = await query<Row>(\n `SELECT r.* FROM ${redemption} r\n WHERE ($1::uuid IS NULL OR r.coupon_id = $1) AND ($2::uuid IS NULL OR r.partner_id = $2) AND ($3::text IS NULL OR r.status = $3)\n AND ($4::timestamptz IS NULL OR r.created_at >= $4) AND ($5::timestamptz IS NULL OR r.created_at < $5)\n ORDER BY r.created_at DESC, r.id LIMIT $6 OFFSET $7`,\n [f.couponId ?? null, f.partnerId ?? null, f.status ?? null, f.from ?? null, f.to ?? null, f.limit, f.offset],\n );\n return rows.map(toRedemption);\n },\n async releaseExpired(now, limit) {\n const rows = await query<Row>(\n `UPDATE ${redemption} SET status = 'released', reserved_until = NULL, ended_at = $1\n WHERE id IN (SELECT id FROM ${redemption} WHERE status = 'reserved' AND reserved_until <= $1 ORDER BY reserved_until, id LIMIT $2 FOR UPDATE SKIP LOCKED)\n RETURNING *`,\n [now, limit],\n );\n return rows.map(toRedemption);\n },\n async partnerReport(partnerId, period, now): Promise<PartnerRow[]> {\n const rows = await query<Row>(\n `SELECT c.id AS coupon_id, c.code, r.currency,\n count(*)::int AS redemptions,\n COALESCE(sum(r.amount_discount_minor), 0) AS discount,\n COALESCE(sum(COALESCE(r.amount_paid_minor, 0) - r.amount_refunded_minor), 0) AS revenue,\n COALESCE(sum(CASE WHEN c.kind = 'credit' THEN c.credits ELSE 0 END), 0) AS credits_granted\n FROM ${redemption} r JOIN ${coupon} c ON c.id = r.coupon_id\n WHERE r.partner_id = $1\n AND ((r.status IN ('confirmed', 'revoked') AND NOT r.use_restored) OR (r.status = 'reserved' AND r.reserved_until > $2))\n AND ($3::timestamptz IS NULL OR COALESCE(r.confirmed_at, r.created_at) >= $3) AND ($4::timestamptz IS NULL OR COALESCE(r.confirmed_at, r.created_at) < $4)\n GROUP BY c.id, c.code, r.currency ORDER BY c.code, r.currency NULLS FIRST`,\n [partnerId, now, period.from ?? null, period.to ?? null],\n );\n return rows.map((r) => ({ couponId: r.coupon_id as string, code: r.code as string, currency: (r.currency as string | null) ?? null, redemptions: n(r.redemptions), discountMinor: n(r.discount), revenueMinor: n(r.revenue), creditsGranted: n(r.credits_granted) }));\n },\n async activeAmountCoupons() {\n return (await query<Row>(`SELECT * FROM ${coupon} WHERE status = 'active' AND amount_off_minor IS NOT NULL ORDER BY created_at, id`)).map(toCoupon);\n },\n };\n}\n","import { type Kernel, SYSTEM } from \"./kernel\";\nimport {\n type Actor,\n type Coupon,\n COUPON_ERROR,\n COUPON_STATUS,\n CouponError,\n type CouponFilter,\n type CouponPatch,\n type Partner,\n type PartnerRow,\n type Query,\n type Redemption,\n type RedemptionFilter,\n} from \"./port\";\nimport { checkCoupon, generateCode, type NewCoupon, normalizeCode } from \"./rules\";\n\nconst bad = (message: string) => new CouponError(COUPON_ERROR.badInput, message);\n// @fixed: a page of the admin's lists\nconst PAGE = 50;\n// @fixed: the most rows one admin list returns\nconst MAX_PAGE = 200;\n// @fixed: tries at a generated code that is free (the alphabet has 32^8 or more codes)\nconst CODE_TRIES = 5;\n\n/** The fields that are a coupon's TERMS: locked once anyone has used the coupon (the provider's object is made from them, per version). */\nconst TERMS = [\"percentOffBp\", \"amountOffMinor\", \"currency\", \"duration\", \"durationMonths\", \"credits\", \"capPerDay\", \"capPerMonth\", \"creditsValidDays\", \"onExhausted\"] as const;\n\nconst page = (limit: number | undefined, offset: number | undefined) => ({ limit: Math.min(Math.max(limit ?? PAGE, 1), MAX_PAGE), offset: Math.max(offset ?? 0, 0) });\n\nexport interface PartnerInput {\n displayName: string;\n contactEmail?: string | null;\n}\n\n/** The admin's side (ADR-002 §6): coupons and partners, every change audited in the caller's transaction. */\nexport function makeAdmin(k: Kernel) {\n const must = async <T>(found: Promise<T | null>, code: typeof COUPON_ERROR.notFound | typeof COUPON_ERROR.partnerNotFound, what: string): Promise<T> => {\n const value = await found;\n if (!value) throw new CouponError(code, what);\n return value;\n };\n const partnerOk = async (tx: Query, partnerId: string | null | undefined) => {\n if (partnerId) await must(k.store(tx).partnerById(partnerId), COUPON_ERROR.partnerNotFound, `No partner ${partnerId}`);\n };\n\n async function generateFreeCode(query: Query): Promise<string> {\n const length = (await k.settings()).generatedCodeLength;\n for (let i = 0; i < CODE_TRIES; i += 1) {\n const code = generateCode(length);\n if (!(await k.store(query).couponByKey(code))) return code;\n }\n throw bad(\"Could not find a free code, try again\");\n }\n\n async function create(tx: Query, actor: Actor, input: NewCoupon): Promise<Coupon> {\n const settings = await k.settings();\n checkCoupon(input, settings);\n const codeKey = normalizeCode(input.code)!;\n const store = k.store(tx);\n if (await store.couponByKey(codeKey)) throw new CouponError(COUPON_ERROR.codeTaken, \"This code is already used\");\n await partnerOk(tx, input.partnerId);\n const now = k.now();\n const coupon: Coupon = {\n id: k.newId(), code: input.code.trim(), codeKey, kind: input.kind, purpose: input.purpose, partnerId: input.partnerId ?? null, status: COUPON_STATUS.active,\n startsAt: input.startsAt, endsAt: input.endsAt ?? null, maxRedemptions: input.maxRedemptions ?? null, maxPerAccount: input.maxPerAccount ?? 1, newCustomersOnly: input.newCustomersOnly ?? false, plans: input.plans ?? [],\n percentOffBp: input.percentOffBp ?? null, amountOffMinor: input.amountOffMinor ?? null, currency: input.currency ?? null, duration: input.duration ?? null, durationMonths: input.durationMonths ?? null, minimumMinor: input.minimumMinor ?? null,\n credits: input.credits ?? null, capPerDay: input.capPerDay ?? null, capPerMonth: input.capPerMonth ?? null, creditsValidDays: input.creditsValidDays ?? null, onExhausted: input.onExhausted ?? null,\n termsVersion: 1, createdAt: now, createdBy: actor.id, updatedAt: now,\n };\n await store.insertCoupon(coupon);\n await k.record(tx, actor, \"coupon.created\", { type: \"coupon\", id: coupon.id }, { code: coupon.code, kind: coupon.kind, purpose: coupon.purpose, partnerId: coupon.partnerId });\n return coupon;\n }\n\n async function update(tx: Query, actor: Actor, id: string, patch: CouponPatch): Promise<Coupon> {\n const store = k.store(tx);\n const before = await must(store.couponById(id, true), COUPON_ERROR.notFound, `No coupon ${id}`);\n const changed = (Object.keys(patch) as (keyof CouponPatch)[]).filter((key) => patch[key] !== undefined && JSON.stringify(patch[key]) !== JSON.stringify(before[key]));\n if (changed.length === 0) return before;\n if (changed.some((key) => (TERMS as readonly string[]).includes(key)) && (await store.hasRedemptions(id))) throw new CouponError(COUPON_ERROR.termsLocked, \"The terms of a coupon that has been used are locked: make a new coupon\");\n const next = { ...before, ...patch };\n checkCoupon(next, await k.settings());\n await partnerOk(tx, patch.partnerId);\n const after = (await store.updateCoupon(id, patch, k.now()))!;\n await k.record(tx, actor, \"coupon.updated\", { type: \"coupon\", id }, { changed, before: Object.fromEntries(changed.map((key) => [key, before[key]])), after: Object.fromEntries(changed.map((key) => [key, after[key]])) });\n return after;\n }\n\n async function setStatus(tx: Query, actor: Actor, id: string, status: Coupon[\"status\"], action: string): Promise<Coupon> {\n const store = k.store(tx);\n const before = await must(store.couponById(id, true), COUPON_ERROR.notFound, `No coupon ${id}`);\n if (before.status === status) return before;\n if (before.status === COUPON_STATUS.archived) throw bad(\"An archived coupon stays archived\");\n const after = (await store.updateCoupon(id, { status }, k.now()))!;\n await k.record(tx, actor, action, { type: \"coupon\", id }, { from: before.status, to: status });\n return after;\n }\n\n /** Pauses the live fixed-amount coupons that a price drop would make too large (the bound `coupons.maxPercentOff`); answers their ids. */\n async function checkPrices(tx: Query, prices: readonly { plan: string; amountMinor: number; currency: string }[]): Promise<string[]> {\n const { maxPercentOff } = await k.settings();\n const paused: string[] = [];\n for (const c of await k.store(tx).activeAmountCoupons()) {\n const applicable = prices.filter((p) => p.currency === c.currency && (c.plans.length === 0 || c.plans.includes(p.plan)));\n if (!applicable.some((p) => c.amountOffMinor! * 100 > p.amountMinor * maxPercentOff)) continue;\n await k.store(tx).updateCoupon(c.id, { status: COUPON_STATUS.paused }, k.now());\n await k.record(tx, SYSTEM, \"coupon.paused_by_price\", { type: \"coupon\", id: c.id }, { maxPercentOff });\n paused.push(c.id);\n }\n return paused;\n }\n\n async function createPartner(tx: Query, actor: Actor, input: PartnerInput): Promise<Partner> {\n const name = input.displayName.trim();\n const email = input.contactEmail?.trim().toLowerCase() || null;\n if (name.length < 1 || name.length > 80) throw bad(\"A partner has a display name of 1 to 80 characters\");\n if (email !== null && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) throw bad(\"A contact e-mail looks like a@b.c\");\n const partner: Partner = { id: k.newId(), displayName: name, contactEmail: email, status: \"active\", createdAt: k.now(), createdBy: actor.id, erasedAt: null };\n await k.store(tx).insertPartner(partner);\n await k.record(tx, actor, \"partner.created\", { type: \"partner\", id: partner.id }, {});\n return partner;\n }\n\n async function setPartnerStatus(tx: Query, actor: Actor, id: string, status: Partner[\"status\"]): Promise<Partner> {\n await must(k.store(tx).partnerById(id), COUPON_ERROR.partnerNotFound, `No partner ${id}`);\n const after = (await k.store(tx).updatePartner(id, { status }))!;\n await k.record(tx, actor, `partner.${status === \"active\" ? \"restored\" : \"archived\"}`, { type: \"partner\", id }, {});\n return after;\n }\n\n /** Loi 25: blanks the name and the e-mail and keeps the aggregates (the redemptions are accounting records). */\n async function erasePartner(tx: Query, actor: Actor, id: string): Promise<Partner> {\n const partner = await must(k.store(tx).partnerById(id), COUPON_ERROR.partnerNotFound, `No partner ${id}`);\n if (partner.erasedAt) return partner;\n const after = (await k.store(tx).updatePartner(id, { displayName: null, contactEmail: null, status: \"archived\", erasedAt: k.now() }))!;\n await k.record(tx, actor, \"partner.erased\", { type: \"partner\", id }, {});\n return after;\n }\n\n /** Loi 25: the partner's record and its aggregates. */\n async function exportPartner(query: Query, id: string): Promise<{ partner: Partner; rows: PartnerRow[] }> {\n const partner = await must(k.store(query).partnerById(id), COUPON_ERROR.partnerNotFound, `No partner ${id}`);\n return { partner, rows: await k.store(query).partnerReport(id, {}, k.now()) };\n }\n\n /** The partner view, per coupon, in a period. Admin only; every read is audited (so it takes the transaction). */\n async function partnerReport(tx: Query, actor: Actor, id: string, period: { from?: Date; to?: Date } = {}): Promise<PartnerRow[]> {\n await must(k.store(tx).partnerById(id), COUPON_ERROR.partnerNotFound, `No partner ${id}`);\n await k.record(tx, actor, \"partner.report_read\", { type: \"partner\", id }, {});\n return k.store(tx).partnerReport(id, period, k.now());\n }\n\n return {\n generateCode: generateFreeCode,\n create,\n update,\n pause: (tx: Query, actor: Actor, id: string) => setStatus(tx, actor, id, COUPON_STATUS.paused, \"coupon.paused\"),\n resume: (tx: Query, actor: Actor, id: string) => setStatus(tx, actor, id, COUPON_STATUS.active, \"coupon.resumed\"),\n archive: (tx: Query, actor: Actor, id: string) => setStatus(tx, actor, id, COUPON_STATUS.archived, \"coupon.archived\"),\n get: (query: Query, id: string) => k.store(query).couponById(id),\n list: (query: Query, filter: Partial<CouponFilter> = {}) => k.store(query).listCoupons({ ...filter, ...page(filter.limit, filter.offset) }),\n redemptions: (query: Query, filter: Partial<RedemptionFilter> = {}): Promise<Redemption[]> => k.store(query).listRedemptions({ ...filter, ...page(filter.limit, filter.offset) }),\n checkPrices,\n partners: {\n create: createPartner,\n archive: (tx: Query, actor: Actor, id: string) => setPartnerStatus(tx, actor, id, \"archived\"),\n restore: (tx: Query, actor: Actor, id: string) => setPartnerStatus(tx, actor, id, \"active\"),\n erase: erasePartner,\n get: (query: Query, id: string) => k.store(query).partnerById(id),\n list: (query: Query, options: { includeArchived?: boolean; limit?: number; offset?: number } = {}) => k.store(query).listPartners({ includeArchived: options.includeArchived ?? false, ...page(options.limit, options.offset) }),\n export: exportPartner,\n report: partnerReport,\n },\n };\n}\n\nexport type CouponAdmin = ReturnType<typeof makeAdmin>;\n","import { addHours, addMinutes } from \"@krizaka/time\";\n\nimport { COUPON_EVENT } from \"./events\";\nimport { type Kernel, SYSTEM } from \"./kernel\";\nimport {\n type Actor,\n type Coupon,\n COUPON_CODE,\n COUPON_ERROR,\n CouponError,\n type CouponRefusal,\n type DiscountTerms,\n KIND,\n type Query,\n type Redemption,\n REDEMPTION_STATUS,\n} from \"./port\";\nimport { type CheckoutContext, countsAsUse, COUPON_ATTEMPT_ACTION, type CreditEffect, type DiscountEffect, effectOf, normalizeCode, refusalOf, termsOf } from \"./rules\";\n\n/** The coupon as a checkout shows it. Never the limits or the partner. */\nexport interface CouponView {\n id: string;\n code: string;\n kind: Coupon[\"kind\"];\n purpose: Coupon[\"purpose\"];\n endsAt: Date | null;\n}\n\nexport type Validation = { ok: true; coupon: CouponView; effect: DiscountEffect | CreditEffect } | { ok: false; code: CouponRefusal; retryAfterSeconds?: number };\n\nexport interface ApplyInput extends Omit<CheckoutContext, \"mode\"> {\n code: string;\n /** One key per checkout attempt: a replay returns the same reservation. */\n idempotencyKey: string;\n}\n\nexport interface Applied {\n redemptionId: string;\n /** The immutable terms for the payments port's `checkout({ discount })`. */\n discount: DiscountTerms;\n effect: DiscountEffect;\n reservedUntil: Date;\n replayed: boolean;\n}\n\nexport interface RedeemInput {\n /** From the checkout's metadata (`redemption_id`); found first. */\n redemptionId?: string | null;\n /** The checkout session's reference; found when the metadata is missing. */\n checkoutRef?: string | null;\n /** The payment (a payment intent), or the invoice for a subscription; null for a free checkout. */\n paymentRef: string | null;\n amountPaidMinor: number | null;\n amountDiscountMinor: number;\n currency: string | null;\n}\n\nexport interface CreditRedemption {\n redemptionId: string;\n grantId: string;\n credits: number;\n expiresAt: Date;\n replayed: boolean;\n}\n\nconst view = (c: Coupon): CouponView => ({ id: c.id, code: c.code, kind: c.kind, purpose: c.purpose, endsAt: c.endsAt });\nconst REFUSALS: readonly string[] = Object.values(COUPON_CODE);\nconst isRefusal = (code: string): code is CouponRefusal => REFUSALS.includes(code);\nconst refuse = (code: CouponRefusal, retryAfterSeconds?: number) => new CouponError(code, code, retryAfterSeconds);\n// @fixed: a session's reservation outlives its session by five minutes, so a payment at the very end is still found (ADR-002 §4.1)\nconst ATTACH_GRACE_MINUTES = 5;\n// @fixed: the sweeper's batch\nconst SWEEP_BATCH = 500;\n\n/** The flows of a coupon's life at checkout and at redemption (ADR-002 §4). */\nexport function makeFlows(k: Kernel) {\n /** Counts one attempt with a code; refuses past the limit, before any lookup. */\n async function attempt(ctx: Pick<CheckoutContext, \"accountId\" | \"ip\">): Promise<void> {\n const r = await k.deps.rateLimit.check(COUPON_ATTEMPT_ACTION, { ip: ctx.ip, subject: ctx.accountId });\n if (!r.ok) throw refuse(COUPON_CODE.rateLimited, r.retryAfterSeconds);\n }\n\n /** The coupon behind a typed code, or the refusal. */\n async function lookup(tx: Query, code: string, forUpdate: boolean): Promise<Coupon> {\n const key = normalizeCode(code);\n const coupon = key ? await k.store(tx).couponByKey(key, forUpdate) : null;\n if (!coupon) throw refuse(COUPON_CODE.unknown);\n return coupon;\n }\n\n async function check(tx: Query, coupon: Coupon, ctx: CheckoutContext): Promise<void> {\n const settings = await k.settings();\n const now = k.now();\n const usage = await k.store(tx).usage(coupon.id, ctx.accountId, now);\n const refusal = refusalOf(coupon, ctx, usage, now, settings);\n if (refusal) throw refuse(refusal);\n }\n\n const mustBeEnabled = async () => {\n if (!(await k.settings()).enabled) throw refuse(COUPON_CODE.disabled);\n };\n\n const needsAmount = (ctx: CheckoutContext) => {\n if (ctx.mode === \"checkout\" && (ctx.plan === undefined || ctx.amountMinor === undefined || ctx.currency === undefined)) {\n throw new CouponError(COUPON_ERROR.badInput, \"A checkout names its plan, amount and currency\");\n }\n };\n\n async function validate(query: Query, code: string, ctx: CheckoutContext): Promise<Validation> {\n try {\n await mustBeEnabled();\n needsAmount(ctx);\n await attempt(ctx);\n const coupon = await lookup(query, code, false);\n await check(query, coupon, ctx);\n return { ok: true, coupon: view(coupon), effect: effectOf(coupon, ctx.amountMinor ?? 0) };\n } catch (error) {\n if (error instanceof CouponError && isRefusal(error.code)) return { ok: false, code: error.code, ...(error.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: error.retryAfterSeconds }) };\n throw error;\n }\n }\n\n async function apply(tx: Query, input: ApplyInput): Promise<Applied> {\n const ctx: CheckoutContext = { ...input, mode: \"checkout\" };\n await mustBeEnabled();\n needsAmount(ctx);\n await attempt(ctx);\n const coupon = await lookup(tx, input.code, true);\n const store = k.store(tx);\n const known = await store.redemptionByKey(input.idempotencyKey);\n if (known) {\n if (known.couponId !== coupon.id || known.accountId !== input.accountId) throw new CouponError(COUPON_ERROR.keyReused, \"This idempotency key was used for another coupon or account\");\n return { redemptionId: known.id, discount: termsOf(coupon), effect: effectOf(coupon, input.amountMinor!) as DiscountEffect, reservedUntil: known.reservedUntil ?? known.createdAt, replayed: true };\n }\n await check(tx, coupon, ctx);\n const now = k.now();\n const reservedUntil = addMinutes(now, (await k.settings()).reservationMinutes);\n const redemption: Redemption = {\n id: k.newId(), couponId: coupon.id, accountId: input.accountId, partnerId: coupon.partnerId, status: REDEMPTION_STATUS.reserved, idempotencyKey: input.idempotencyKey,\n reservedUntil, checkoutRef: null, paymentRef: null, amountPaidMinor: null, amountDiscountMinor: null, amountRefundedMinor: 0, useRestored: false, currency: input.currency ?? null,\n creditGrantId: null, overLimit: false, createdAt: now, confirmedAt: null, endedAt: null,\n };\n await store.insertRedemption(redemption);\n return { redemptionId: redemption.id, discount: termsOf(coupon), effect: effectOf(coupon, input.amountMinor!) as DiscountEffect, reservedUntil, replayed: false };\n }\n\n async function attachCheckout(tx: Query, redemptionId: string, checkoutRef: string, sessionExpiresAt?: Date | null): Promise<void> {\n const store = k.store(tx);\n const r = await store.redemptionById(redemptionId);\n if (!r) throw new CouponError(COUPON_ERROR.redemptionNotFound, `No redemption ${redemptionId}`);\n await store.updateRedemption(redemptionId, null, { checkoutRef });\n if (r.status !== REDEMPTION_STATUS.reserved || !sessionExpiresAt) return;\n const wanted = addMinutes(sessionExpiresAt, ATTACH_GRACE_MINUTES);\n if (!r.reservedUntil || wanted > r.reservedUntil) await store.updateRedemption(redemptionId, [REDEMPTION_STATUS.reserved], { reservedUntil: wanted });\n }\n\n /** The reservation a payment event is about: by the metadata's id first, then by the session. */\n async function findFor(tx: Query, ref: { redemptionId?: string | null; checkoutRef?: string | null }): Promise<Redemption | null> {\n const store = k.store(tx);\n return (ref.redemptionId ? await store.redemptionById(ref.redemptionId) : null) ?? (ref.checkoutRef ? await store.redemptionByCheckoutRef(ref.checkoutRef) : null);\n }\n\n async function redeem(tx: Query, input: RedeemInput): Promise<\"confirmed\" | \"already\" | \"unknown\"> {\n const store = k.store(tx);\n const found = await findFor(tx, input);\n if (!found) return \"unknown\";\n const coupon = (await store.couponById(found.couponId, true))!;\n const r = (await store.redemptionById(found.id))!;\n const withRef = input.paymentRef ? await store.redemptionByPaymentRef(input.paymentRef) : null;\n if (withRef || (r.status !== REDEMPTION_STATUS.reserved && r.status !== REDEMPTION_STATUS.released)) return \"already\";\n const now = k.now();\n const others = (await store.usage(coupon.id, null, now)).total - (countsAsUse(r, now) ? 1 : 0);\n const late = r.status === REDEMPTION_STATUS.released;\n const overLimit = late || (coupon.maxRedemptions !== null && others + 1 > coupon.maxRedemptions);\n const done = await store.updateRedemption(r.id, [REDEMPTION_STATUS.reserved, REDEMPTION_STATUS.released], {\n status: REDEMPTION_STATUS.confirmed, paymentRef: input.paymentRef, amountPaidMinor: input.amountPaidMinor, amountDiscountMinor: input.amountDiscountMinor, currency: input.currency ?? r.currency,\n reservedUntil: null, confirmedAt: now, endedAt: null, overLimit,\n });\n if (!done) return \"already\";\n await confirmed(tx, coupon, done, others + 1);\n return \"confirmed\";\n }\n\n /** The audit line and the events of a confirmed use. */\n async function confirmed(tx: Query, coupon: Coupon, r: Redemption, usesNow: number): Promise<void> {\n await k.record(tx, SYSTEM, \"coupon.redeemed\", { type: \"coupon\", id: coupon.id }, { redemptionId: r.id, kind: coupon.kind, overLimit: r.overLimit, amountPaidMinor: r.amountPaidMinor, amountDiscountMinor: r.amountDiscountMinor });\n await k.announce(tx, COUPON_EVENT.redeemed, `redeemed:${r.id}`, {\n redemptionId: r.id, couponId: coupon.id, kind: coupon.kind, purpose: coupon.purpose, partnerId: r.partnerId, amountPaidMinor: r.amountPaidMinor, amountDiscountMinor: r.amountDiscountMinor, currency: r.currency, credits: coupon.kind === KIND.credit ? coupon.credits : null, overLimit: r.overLimit,\n });\n if (coupon.maxRedemptions !== null && usesNow === coupon.maxRedemptions) await k.announce(tx, COUPON_EVENT.exhausted, `exhausted:${coupon.id}`, { couponId: coupon.id, partnerId: coupon.partnerId });\n }\n\n async function release(tx: Query, ref: { redemptionId?: string; checkoutRef?: string }): Promise<boolean> {\n const found = await findFor(tx, ref);\n if (!found) return false;\n const done = await k.store(tx).updateRedemption(found.id, [REDEMPTION_STATUS.reserved], { status: REDEMPTION_STATUS.released, reservedUntil: null, endedAt: k.now() });\n if (!done) return false;\n await k.announce(tx, COUPON_EVENT.released, `released:${done.id}`, { redemptionId: done.id, couponId: done.couponId });\n return true;\n }\n\n async function releaseExpired(tx: Query): Promise<number> {\n const released = await k.store(tx).releaseExpired(k.now(), SWEEP_BATCH);\n for (const r of released) await k.announce(tx, COUPON_EVENT.released, `released:${r.id}`, { redemptionId: r.id, couponId: r.couponId });\n return released.length;\n }\n\n async function refund(tx: Query, input: { paymentRef?: string | null; invoiceRef?: string | null; amountRefunded: number }): Promise<\"refunded\" | \"already\" | \"unknown\"> {\n const store = k.store(tx);\n const r = (input.paymentRef ? await store.redemptionByPaymentRef(input.paymentRef) : null) ?? (input.invoiceRef ? await store.redemptionByPaymentRef(input.invoiceRef) : null);\n if (!r) return \"unknown\";\n const total = Math.max(r.amountRefundedMinor, input.amountRefunded);\n if (total === r.amountRefundedMinor) return \"already\";\n const full = r.amountPaidMinor !== null && r.amountPaidMinor > 0 && total >= r.amountPaidMinor;\n const restore = full && (await k.settings()).refundRestoresUse && !r.useRestored;\n const done = (await store.updateRedemption(r.id, null, { amountRefundedMinor: total, ...(restore ? { useRestored: true } : {}) }))!;\n await k.record(tx, SYSTEM, \"coupon.refunded\", { type: \"coupon\", id: r.couponId }, { redemptionId: r.id, amountRefundedMinor: total, useRestored: done.useRestored });\n await k.announce(tx, COUPON_EVENT.refunded, `refunded:${r.id}:${total}`, { redemptionId: r.id, couponId: r.couponId, partnerId: r.partnerId, amountRefundedMinor: total, currency: r.currency, useRestored: done.useRestored });\n return \"refunded\";\n }\n\n async function redeemCredit(tx: Query, input: { code: string; accountId: string; idempotencyKey: string; ip?: string | null; hasPaidBefore?: boolean }): Promise<CreditRedemption> {\n const credits = k.deps.credits;\n if (!credits) throw new CouponError(COUPON_ERROR.badSettings, \"Credit coupons need the credits ledger in the module's dependencies\");\n await mustBeEnabled();\n await attempt(input);\n const coupon = await lookup(tx, input.code, true);\n const store = k.store(tx);\n const known = await store.redemptionByKey(input.idempotencyKey);\n if (known) return replayOf(known, coupon, input.accountId);\n await check(tx, coupon, { mode: \"redeem\", accountId: input.accountId, ...(input.hasPaidBefore === undefined ? {} : { hasPaidBefore: input.hasPaidBefore }) });\n const now = k.now();\n const expiresAt = addHours(now, coupon.creditsValidDays! * 24);\n const redemption: Redemption = {\n id: k.newId(), couponId: coupon.id, accountId: input.accountId, partnerId: coupon.partnerId, status: REDEMPTION_STATUS.confirmed, idempotencyKey: input.idempotencyKey,\n reservedUntil: null, checkoutRef: null, paymentRef: null, amountPaidMinor: null, amountDiscountMinor: null, amountRefundedMinor: 0, useRestored: false, currency: null,\n creditGrantId: null, overLimit: false, createdAt: now, confirmedAt: now, endedAt: null,\n };\n await store.insertRedemption(redemption);\n const { grantId } = await credits.grant(tx, { accountId: input.accountId, amount: coupon.credits!, capPerDay: coupon.capPerDay, capPerMonth: coupon.capPerMonth, expiresAt, onExhausted: coupon.onExhausted!, source: `coupon:${redemption.id}`, idempotencyKey: `coupon-${redemption.id}` });\n const done = (await store.updateRedemption(redemption.id, null, { creditGrantId: grantId }))!;\n await confirmed(tx, coupon, done, (await store.usage(coupon.id, null, now)).total);\n return { redemptionId: done.id, grantId, credits: coupon.credits!, expiresAt, replayed: false };\n }\n\n function replayOf(known: Redemption, coupon: Coupon, accountId: string): CreditRedemption {\n if (known.couponId !== coupon.id || known.accountId !== accountId || !known.creditGrantId) throw new CouponError(COUPON_ERROR.keyReused, \"This idempotency key was used for another coupon or account\");\n return { redemptionId: known.id, grantId: known.creditGrantId, credits: coupon.credits!, expiresAt: addHours(known.createdAt, coupon.creditsValidDays! * 24), replayed: true };\n }\n\n async function revoke(tx: Query, actor: Actor, redemptionId: string): Promise<void> {\n const store = k.store(tx);\n const r = await store.redemptionById(redemptionId);\n if (!r) throw new CouponError(COUPON_ERROR.redemptionNotFound, `No redemption ${redemptionId}`);\n if (r.status === REDEMPTION_STATUS.revoked) return;\n if (r.status !== REDEMPTION_STATUS.confirmed || !r.creditGrantId || !k.deps.credits) throw new CouponError(COUPON_ERROR.notRevocable, \"Only a confirmed credit redemption can be revoked\");\n await store.couponById(r.couponId, true);\n const done = await store.updateRedemption(r.id, [REDEMPTION_STATUS.confirmed], { status: REDEMPTION_STATUS.revoked, endedAt: k.now() });\n if (!done) return;\n const { expired } = await k.deps.credits.expire(tx, r.creditGrantId);\n await k.record(tx, actor, \"coupon.revoked\", { type: \"coupon\", id: r.couponId }, { redemptionId: r.id, creditsWrittenOff: expired });\n }\n\n return { validate, apply, attachCheckout, redeem, release, releaseExpired, refund, redeemCredit, revoke };\n}\n","import { type CouponAdmin, makeAdmin } from \"./admin\";\nimport { type Applied, type ApplyInput, type CreditRedemption, makeFlows, type RedeemInput, type Validation } from \"./flows\";\nimport { type CouponsDeps, makeKernel } from \"./kernel\";\nimport type { Actor, Query } from \"./port\";\nimport type { CheckoutContext } from \"./rules\";\n\n/** The coupons of a product (ADR-002 §5.1). Every function takes the `Query` it works through: pass `tx` to join a unit of work. */\nexport interface Coupons {\n /** Read-only: what a code would give, or why not. Counts one attempt on the limiter, before any lookup. Never reserves. */\n validate(query: Query, code: string, context: CheckoutContext): Promise<Validation>;\n /** Reserves a use of a discount code for a checkout (a short transaction: no network inside), idempotent by key. */\n apply(tx: Query, input: ApplyInput): Promise<Applied>;\n /** Stores the provider's session on the reservation and makes it outlive the session. */\n attachCheckout(tx: Query, redemptionId: string, checkoutRef: string, sessionExpiresAt?: Date | null): Promise<void>;\n /** On the payment event, in its transaction: confirms the use once. A payment after the release is confirmed anyway (`overLimit`). */\n redeem(tx: Query, input: RedeemInput): Promise<\"confirmed\" | \"already\" | \"unknown\">;\n /** Frees an unpaid reservation (a failed session, an expired checkout). `false` when there was none to free. */\n release(tx: Query, ref: { redemptionId?: string; checkoutRef?: string }): Promise<boolean>;\n /** The sweeper: frees the reservations past their deadline. */\n releaseExpired(tx: Query): Promise<number>;\n /** A refund (cumulative amount). Gives the use back only on a full refund with `coupons.refundRestoresUse`. */\n refund(tx: Query, input: { paymentRef?: string | null; invoiceRef?: string | null; amountRefunded: number }): Promise<\"refunded\" | \"already\" | \"unknown\">;\n /** Redeems a credit code: the same rules, then a capped grant in the credits ledger, in the same transaction. Idempotent by key. */\n redeemCredit(tx: Query, input: { code: string; accountId: string; idempotencyKey: string; ip?: string | null; hasPaidBefore?: boolean }): Promise<CreditRedemption>;\n /** The admin revokes a credit redemption: what is left of its grant is written off; credits already spent are never taken back. */\n revoke(tx: Query, actor: Actor, redemptionId: string): Promise<void>;\n admin: CouponAdmin;\n}\n\n/**\n * The coupons of a product.\n * @param deps the clock, the settings, the trail, the limiter, and optionally the credits ledger, the outbox, the store\n */\nexport function createCoupons(deps: CouponsDeps): Coupons {\n const kernel = makeKernel(deps);\n return { ...makeFlows(kernel), admin: makeAdmin(kernel) };\n}\n","import { type Coupon, COUPON_STATUS, type CouponStore, KIND, type Partner, type PartnerRow, type Redemption, REDEMPTION_STATUS } from \"./port\";\nimport { countsAsUse } from \"./rules\";\n\nconst PARTNER_ACTIVE: Partner[\"status\"] = \"active\";\nconst clone = <T extends object>(v: T): T => ({ ...v });\n\n/**\n * An in-memory store, for unit tests and demos: the same behaviour as the database's (the contract kit runs on both) except that\n * `forUpdate` locks nothing, because it has no transaction to hold a lock in. Concurrency is proved on PostgreSQL.\n */\nexport function memoryCouponStore(): CouponStore {\n const coupons = new Map<string, Coupon>();\n const partners = new Map<string, Partner>();\n const redemptions = new Map<string, Redemption>();\n const unique = (field: \"idempotencyKey\" | \"checkoutRef\" | \"paymentRef\", value: string | null, except: string) => {\n if (value !== null && [...redemptions.values()].some((r) => r.id !== except && r[field] === value)) throw new Error(`duplicate ${field}`);\n };\n const page = <T>(rows: T[], limit: number, offset: number) => rows.slice(offset, offset + limit);\n const newestFirst = <T extends { createdAt: Date; id: string }>(rows: T[]) => rows.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf() || a.id.localeCompare(b.id));\n return {\n async insertCoupon(c) {\n if ([...coupons.values()].some((x) => x.codeKey === c.codeKey)) throw Object.assign(new Error(\"duplicate code_key\"), { code: \"23505\" });\n coupons.set(c.id, clone(c));\n },\n async couponById(id) {\n const c = coupons.get(id);\n return c ? clone(c) : null;\n },\n async couponByKey(codeKey) {\n const c = [...coupons.values()].find((x) => x.codeKey === codeKey);\n return c ? clone(c) : null;\n },\n async updateCoupon(id, patch, at) {\n const c = coupons.get(id);\n if (!c) return null;\n for (const [key, value] of Object.entries(patch)) if (value !== undefined) (c as unknown as Record<string, unknown>)[key] = value;\n c.updatedAt = at;\n return clone(c);\n },\n async listCoupons(f) {\n const rows = [...coupons.values()].filter((c) => (!f.status || c.status === f.status) && (!f.kind || c.kind === f.kind) && (!f.purpose || c.purpose === f.purpose) && (!f.partnerId || c.partnerId === f.partnerId));\n return page(newestFirst(rows), f.limit, f.offset).map(clone);\n },\n async hasRedemptions(couponId) {\n return [...redemptions.values()].some((r) => r.couponId === couponId);\n },\n async insertPartner(p) {\n partners.set(p.id, clone(p));\n },\n async partnerById(id) {\n const p = partners.get(id);\n return p ? clone(p) : null;\n },\n async updatePartner(id, patch) {\n const p = partners.get(id);\n if (!p) return null;\n for (const [key, value] of Object.entries(patch)) if (value !== undefined) (p as unknown as Record<string, unknown>)[key] = value;\n return clone(p);\n },\n async listPartners(o) {\n return page(newestFirst([...partners.values()].filter((p) => o.includeArchived || p.status === PARTNER_ACTIVE)), o.limit, o.offset).map(clone);\n },\n async insertRedemption(r) {\n unique(\"idempotencyKey\", r.idempotencyKey, r.id);\n unique(\"checkoutRef\", r.checkoutRef, r.id);\n unique(\"paymentRef\", r.paymentRef, r.id);\n redemptions.set(r.id, clone(r));\n },\n async redemptionById(id) {\n const r = redemptions.get(id);\n return r ? clone(r) : null;\n },\n async redemptionByKey(key) {\n const r = [...redemptions.values()].find((x) => x.idempotencyKey === key);\n return r ? clone(r) : null;\n },\n async redemptionByCheckoutRef(ref) {\n const r = [...redemptions.values()].find((x) => x.checkoutRef === ref);\n return r ? clone(r) : null;\n },\n async redemptionByPaymentRef(ref) {\n const r = [...redemptions.values()].find((x) => x.paymentRef === ref);\n return r ? clone(r) : null;\n },\n async updateRedemption(id, from, patch) {\n const r = redemptions.get(id);\n if (!r || (from && !from.includes(r.status))) return null;\n unique(\"checkoutRef\", patch.checkoutRef ?? null, id);\n unique(\"paymentRef\", patch.paymentRef ?? null, id);\n for (const [key, value] of Object.entries(patch)) if (value !== undefined) (r as unknown as Record<string, unknown>)[key] = value;\n return clone(r);\n },\n async usage(couponId, accountId, now) {\n const live = [...redemptions.values()].filter((r) => r.couponId === couponId && countsAsUse(r, now));\n return { total: live.length, account: accountId === null ? 0 : live.filter((r) => r.accountId === accountId).length };\n },\n async listRedemptions(f) {\n const rows = [...redemptions.values()].filter((r) => (!f.couponId || r.couponId === f.couponId) && (!f.partnerId || r.partnerId === f.partnerId) && (!f.status || r.status === f.status) && (!f.from || r.createdAt >= f.from) && (!f.to || r.createdAt < f.to));\n return page(newestFirst(rows), f.limit, f.offset).map(clone);\n },\n async releaseExpired(now, limit) {\n const due = [...redemptions.values()].filter((r) => r.status === REDEMPTION_STATUS.reserved && r.reservedUntil !== null && r.reservedUntil <= now).sort((a, b) => a.reservedUntil!.valueOf() - b.reservedUntil!.valueOf() || a.id.localeCompare(b.id)).slice(0, limit);\n for (const r of due) Object.assign(r, { status: REDEMPTION_STATUS.released, reservedUntil: null, endedAt: now });\n return due.map(clone);\n },\n async partnerReport(partnerId, period, now): Promise<PartnerRow[]> {\n const rows = new Map<string, PartnerRow>();\n for (const r of redemptions.values()) {\n const at = r.confirmedAt ?? r.createdAt;\n if (r.partnerId !== partnerId || !countsAsUse(r, now) || (period.from && at < period.from) || (period.to && at >= period.to)) continue;\n const c = coupons.get(r.couponId)!;\n const key = `${c.id}|${r.currency ?? \"\"}`;\n const row = rows.get(key) ?? { couponId: c.id, code: c.code, currency: r.currency, redemptions: 0, discountMinor: 0, revenueMinor: 0, creditsGranted: 0 };\n row.redemptions += 1;\n row.discountMinor += r.amountDiscountMinor ?? 0;\n row.revenueMinor += (r.amountPaidMinor ?? 0) - r.amountRefundedMinor;\n row.creditsGranted += c.kind === KIND.credit ? (c.credits ?? 0) : 0;\n rows.set(key, row);\n }\n return [...rows.values()].sort((a, b) => a.code.localeCompare(b.code) || (a.currency ?? \"\").localeCompare(b.currency ?? \"\"));\n },\n async activeAmountCoupons() {\n return [...coupons.values()].filter((c) => c.status === COUPON_STATUS.active && c.amountOffMinor !== null).sort((a, b) => a.createdAt.valueOf() - b.createdAt.valueOf() || a.id.localeCompare(b.id)).map(clone);\n },\n };\n}\n","import { defineModule } from \"@krizaka/db\";\n\n/**\n * The tables of « coupons » (ADR-002), in their own Postgres schema: `partner` (a person who sells for us: a name and an optional\n * e-mail, erasable), `coupon` (the code, its window, its limits and its terms: a discount or capped credits) and `redemption` (a use:\n * reserved, confirmed, released or revoked, with the amounts and the provider's references). The one product table referenced is the\n * owner (`owner`, the account table): a deleted account leaves its redemptions (`ON DELETE SET NULL`), because uses and revenue stay.\n * Version 1 is frozen once released: a change is version 2.\n */\nexport const couponsSchema = defineModule({\n module: \"coupons\",\n tables: [\"owner\"],\n migrations: [\n {\n version: 1,\n name: \"partners, coupons and redemptions\",\n sql: (c) => `\nCREATE TABLE ${c.schema}.partner (\n id uuid PRIMARY KEY,\n display_name text CHECK (display_name IS NULL OR char_length(display_name) BETWEEN 1 AND 80),\n contact_email text CHECK (contact_email IS NULL OR contact_email = lower(contact_email)),\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')),\n created_at timestamptz NOT NULL,\n created_by uuid REFERENCES ${c.tables.owner}(id) ON DELETE SET NULL,\n erased_at timestamptz,\n CHECK (erased_at IS NOT NULL OR display_name IS NOT NULL),\n CHECK (erased_at IS NULL OR (display_name IS NULL AND contact_email IS NULL))\n);\nCREATE TABLE ${c.schema}.coupon (\n id uuid PRIMARY KEY,\n code text NOT NULL CHECK (char_length(code) BETWEEN 4 AND 60),\n code_key text NOT NULL UNIQUE CHECK (code_key = upper(code_key) AND char_length(code_key) BETWEEN 4 AND 40),\n kind text NOT NULL CHECK (kind IN ('discount', 'credit')),\n purpose text NOT NULL CHECK (purpose IN ('partner', 'promo', 'demo', 'support')),\n partner_id uuid REFERENCES ${c.schema}.partner(id) ON DELETE RESTRICT,\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'paused', 'archived')),\n starts_at timestamptz NOT NULL,\n ends_at timestamptz,\n max_redemptions integer CHECK (max_redemptions > 0),\n max_per_account integer NOT NULL DEFAULT 1 CHECK (max_per_account > 0),\n new_customers_only boolean NOT NULL DEFAULT false,\n plans text[] NOT NULL DEFAULT '{}',\n percent_off_bp integer CHECK (percent_off_bp BETWEEN 1 AND 9999),\n amount_off_minor integer CHECK (amount_off_minor > 0),\n currency text CHECK (currency ~ '^[a-z]{3}$'),\n duration text CHECK (duration IN ('once', 'repeating', 'forever')),\n duration_months integer CHECK (duration_months BETWEEN 1 AND 36),\n minimum_minor integer CHECK (minimum_minor >= 0),\n credits integer CHECK (credits > 0),\n cap_per_day integer CHECK (cap_per_day > 0),\n cap_per_month integer CHECK (cap_per_month > 0),\n credits_valid_days integer CHECK (credits_valid_days > 0),\n on_exhausted text CHECK (on_exhausted IN ('stop', 'fallback_paid')),\n terms_version integer NOT NULL DEFAULT 1,\n created_at timestamptz NOT NULL,\n created_by uuid REFERENCES ${c.tables.owner}(id) ON DELETE SET NULL,\n updated_at timestamptz NOT NULL,\n CHECK (ends_at IS NULL OR ends_at > starts_at),\n CHECK ((purpose = 'partner') = (partner_id IS NOT NULL)),\n CHECK (kind <> 'discount' OR (credits IS NULL AND num_nonnulls(percent_off_bp, amount_off_minor) = 1\n AND (amount_off_minor IS NULL OR currency IS NOT NULL) AND duration IS NOT NULL\n AND ((duration = 'repeating') = (duration_months IS NOT NULL))\n AND (amount_off_minor IS NULL OR duration <> 'forever'))),\n CHECK (kind <> 'credit' OR (credits IS NOT NULL AND on_exhausted IS NOT NULL\n AND credits_valid_days IS NOT NULL\n AND num_nulls(percent_off_bp, amount_off_minor, duration) = 3))\n);\nCREATE INDEX coupon_partner ON ${c.schema}.coupon (partner_id) WHERE partner_id IS NOT NULL;\nCREATE TABLE ${c.schema}.redemption (\n id uuid PRIMARY KEY,\n coupon_id uuid NOT NULL REFERENCES ${c.schema}.coupon(id) ON DELETE RESTRICT,\n account_id uuid REFERENCES ${c.tables.owner}(id) ON DELETE SET NULL,\n partner_id uuid REFERENCES ${c.schema}.partner(id) ON DELETE SET NULL,\n status text NOT NULL CHECK (status IN ('reserved', 'confirmed', 'released', 'revoked')),\n idempotency_key text NOT NULL UNIQUE,\n reserved_until timestamptz,\n checkout_ref text UNIQUE,\n payment_ref text UNIQUE,\n amount_paid_minor bigint,\n amount_discount_minor bigint,\n amount_refunded_minor bigint NOT NULL DEFAULT 0 CHECK (amount_refunded_minor >= 0),\n use_restored boolean NOT NULL DEFAULT false,\n currency text,\n credit_grant_id uuid,\n over_limit boolean NOT NULL DEFAULT false,\n created_at timestamptz NOT NULL,\n confirmed_at timestamptz,\n ended_at timestamptz\n);\nCREATE INDEX redemption_uses ON ${c.schema}.redemption (coupon_id, account_id) WHERE status <> 'released' AND NOT use_restored;\nCREATE INDEX redemption_reserved ON ${c.schema}.redemption (reserved_until) WHERE status = 'reserved';\nCREATE INDEX redemption_partner ON ${c.schema}.redemption (partner_id, confirmed_at) WHERE partner_id IS NOT NULL;`,\n },\n ],\n});\n"],"mappings":"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","names":["page"]}
|