import { nanoid } from 'nanoid' import type Stripe from 'stripe' import * as Runtime from '../../../test/runtime.js' import * as ApiKeys from '../../ApiKeys.js' import * as Db from '../../db/Db.js' import * as RequestUsage from '../../db/tables/requestUsage.js' import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js' import * as StripeCustomers from '../../db/tables/stripeCustomers.js' import * as Store from '../../internal/Store.js' import * as Viem from '../../internal/Viem.js' import * as Billing from './Billing.js' const create = () => Db.postgres({ connectionString: Runtime.postgresUrl, schema: `t_${nanoid()}` }) /** Baseline billable mainnet sponsorship input; tests override per case. */ const input = { apiKeyId: 'key_1', billable: true, chainId: Viem.chainId.mainnet, environment: 'production', orgId: 'org_1', projectId: 'prj_1', transaction: `0x76${'cc'.repeat(16)}`, } satisfies Omit /** Inserts and finalizes one billable row; `seed` derives a unique sign payload. */ async function finalized( db: Db.Db, options: { feeAmount: string; orgId?: string; seed: string }, ): Promise { const row = await SponsoredTransactions.upsert(db, { ...input, ...(options.orgId ? { orgId: options.orgId } : {}), signPayload: `0x${options.seed.repeat(32)}`, }) await SponsoredTransactions.finalize(db, row.id, { feeAmount: options.feeAmount, finalizedAt: new Date().toISOString(), }) return row } /** Managed meter/price ids per kind, keyed by lookup key. */ const priceByLookup: Record = { [Billing.meters.apiRequests.lookupKey]: 'price_req', [Billing.meters.feePayerSpend.lookupKey]: 'price_fee', } const meterByEvent: Record = { [Billing.meters.apiRequests.eventName]: 'mtr_req', [Billing.meters.feePayerSpend.eventName]: 'mtr_fee', } /** A stubbed subscription: live status plus which managed kinds it carries as items. */ type StubSubscription = { items?: readonly Billing.MeterKind[]; status: string } /** * A Stripe stub covering the reporter/subscription surface: fixtures resolve * per kind, subscriptions carry per-kind items, and meter-event and * item/subscription creation are captured for assertions. */ function stubStripe(options: { meterEvent?: ((params: { identifier: string }) => void) | undefined subscriptions?: readonly StubSubscription[] | undefined subscriptionsList?: (() => Promise) | undefined }) { const meterEvents: { event_name: string identifier: string payload?: { stripe_customer_id: string; value: string } timestamp?: number }[] = [] const subscriptionsCreated: { items: { price: string }[] }[] = [] const itemsCreated: { price: string; subscription: string }[] = [] const expand = (subscription: StubSubscription) => ({ id: `sub_${subscriptionsCreated.length}_${Math.random().toString(36).slice(2, 8)}`, items: { data: (subscription.items ?? ['feePayerSpend']).map((kind) => ({ price: { id: priceByLookup[Billing.meters[kind].lookupKey] }, })), }, status: subscription.status, }) const subscriptions = (options.subscriptions ?? []).map(expand) const stripe = { billing: { meterEvents: { create: async (params: { event_name: string identifier: string payload?: { stripe_customer_id: string; value: string } timestamp?: number }) => { options.meterEvent?.(params) meterEvents.push(params) return {} }, }, meters: { create: async (params: { event_name: string }) => ({ event_name: params.event_name, id: meterByEvent[params.event_name] ?? 'mtr_new', }), list: async () => ({ data: Object.entries(meterByEvent).map(([event_name, id]) => ({ event_name, id })), }), }, }, prices: { create: async (params: { lookup_key: string }) => ({ id: priceByLookup[params.lookup_key] ?? 'price_new', }), list: async (params: { lookup_keys: string[] }) => ({ data: params.lookup_keys .map((key) => priceByLookup[key]) .filter(Boolean) .map((id) => ({ id })), }), }, subscriptionItems: { create: async (params: { price: string; subscription: string }) => { itemsCreated.push(params) return {} }, }, subscriptions: { create: async (params: { items: { price: string }[] }) => { subscriptionsCreated.push(params) return {} }, list: async () => { await options.subscriptionsList?.() return { data: subscriptions } }, }, } return { itemsCreated, meterEvents, stripe: stripe as never as Stripe, subscriptionsCreated } } describe('periodStart', () => { test('behavior: anchors to the UTC calendar month start', () => { expect(Billing.periodStart('month', new Date('2026-03-15T12:34:56.789Z'))).toBe('2026-03-01T00:00:00.000Z') // prettier-ignore }) test('behavior: month boundaries, year rollovers, and leap years resolve in UTC', () => { expect(Billing.periodStart('month', new Date('2025-12-31T23:59:59.999Z'))).toBe('2025-12-01T00:00:00.000Z') // prettier-ignore expect(Billing.periodStart('month', new Date('2026-01-01T00:00:00.000Z'))).toBe('2026-01-01T00:00:00.000Z') // prettier-ignore expect(Billing.periodStart('month', new Date('2028-02-29T10:00:00.000Z'))).toBe('2028-02-01T00:00:00.000Z') // prettier-ignore // A UTC month starts while western timezones still sit in the prior one. expect(Billing.periodStart('month', new Date('2026-07-01T03:00:00.000Z'))).toBe('2026-07-01T00:00:00.000Z') // prettier-ignore }) }) describe('toBaseUnits', () => { test('behavior: round-trips decimal strings through fee-token base units', () => { expect(Billing.toBaseUnits('0.35')).toBe(350_000n) expect(Billing.toBaseUnits('250')).toBe(250_000_000n) expect(Billing.fromBaseUnits(350_000n)).toBe('0.35') expect(Billing.fromBaseUnits(250_000_000n)).toBe('250') }) }) describe('status', () => { test('behavior: reflects the stored billing source status', async () => { const db = create() await db.migrate() expect(await Billing.status(db, 'org_1')).toBeUndefined() expect(await Billing.active(db, 'org_1')).toBe(false) await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'past_due') expect(await Billing.status(db, 'org_1')).toBe('past_due') expect(await Billing.active(db, 'org_1')).toBe(false) await StripeCustomers.setStatus(db, 'org_1', 'active') expect(await Billing.active(db, 'org_1')).toBe(true) await db.close() }) }) describe('createReporter', () => { test('behavior: a poisoned row never stalls the queue', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) const poison = await finalized(db, { feeAmount: '100000', seed: '01' }) const good = await finalized(db, { feeAmount: '250000', seed: '02' }) const { stripe } = stubStripe({ meterEvent: (params) => { if (params.identifier === poison.id) throw new Error('boom') }, subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) const reporter = Billing.createReporter({ db, stripe }) expect(await reporter.tick()).toStrictEqual({ failed: 1, reported: 1, skipped: 0 }) // The good row marked; the poisoned row stays unmarked for retry. expect((await SponsoredTransactions.get(db, good.id))?.meterReportedAt).not.toBeNull() expect((await SponsoredTransactions.get(db, poison.id))?.meterReportedAt).toBeNull() expect(await reporter.tick()).toStrictEqual({ failed: 1, reported: 0, skipped: 0 }) await db.close() }) test('behavior: marks a meter event that Stripe already accepted', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) const row = await finalized(db, { feeAmount: '100000', seed: '03' }) const { stripe } = stubStripe({ meterEvent: (params) => { throw new Error(`An event already exists with identifier ${params.identifier}.`) }, subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) expect(await Billing.createReporter({ db, stripe }).tick()).toStrictEqual({ failed: 0, reported: 1, skipped: 0, }) expect((await SponsoredTransactions.get(db, row.id))?.meterReportedAt).not.toBeNull() expect(await Billing.createReporter({ db, stripe }).tick()).toStrictEqual({ failed: 0, reported: 0, skipped: 0, }) await db.close() }) test('behavior: canceled customers skip without meter events', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'canceled') const row = await finalized(db, { feeAmount: '100000', seed: '03' }) const { meterEvents, stripe } = stubStripe({ subscriptions: [{ status: 'active' }] }) expect(await Billing.createReporter({ db, stripe }).tick()).toStrictEqual({ failed: 0, reported: 0, skipped: 1, }) expect(meterEvents).toHaveLength(0) // Skipped rows still mark: nobody remains to invoice, ever. expect((await SponsoredTransactions.get(db, row.id))?.meterReportedAt).not.toBeNull() await db.close() }) test('behavior: ensures the metered subscription once and stamps its backlog at now', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await finalized(db, { feeAmount: '100000', seed: '04' }) await finalized(db, { feeAmount: '250000', seed: '05' }) const { meterEvents, stripe, subscriptionsCreated } = stubStripe({ subscriptions: [] }) expect(await Billing.createReporter({ db, stripe }).tick()).toStrictEqual({ failed: 0, reported: 2, skipped: 0, }) // One ensure per org per tick, not per row. expect(subscriptionsCreated).toHaveLength(1) // The just-created subscription bills nothing before its start, so the // backlog carries no timestamp and lands on the first invoice. expect(meterEvents).toHaveLength(2) for (const event of meterEvents) expect(event.timestamp).toBeUndefined() await db.close() }) test('behavior: an existing subscription keeps finalization timestamps', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) const row = await finalized(db, { feeAmount: '100000', seed: '06' }) const { meterEvents, stripe, subscriptionsCreated } = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) expect(await Billing.createReporter({ db, stripe }).tick()).toStrictEqual({ failed: 0, reported: 1, skipped: 0, }) expect(subscriptionsCreated).toHaveLength(0) const finalizedAt = new Date((await SponsoredTransactions.get(db, row.id))!.finalizedAt!) expect(meterEvents[0]?.timestamp).toBe(Math.floor(finalizedAt.getTime() / 1000)) await db.close() }) }) describe('createKeyBillingSyncer', () => { test('behavior: converges drifted sandbox key snapshots to the billing table', async () => { const db = create() await db.migrate() const kv = Store.memory() // A sandbox key minted while billing was inactive; billing later activates // but the webhook re-sync never lands, so the record snapshot is stale. const { token } = await ApiKeys.mint(kv, { billingActive: false, environment: 'sandbox', orgId: 'org_1', scopes: ['data:read'], }) await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_1', }) await StripeCustomers.setStatus(db, 'org_1', 'active', 'sandbox') const syncer = Billing.createKeyBillingSyncer({ db, kv }) expect(await syncer.tick()).toStrictEqual({ checked: 1, updated: 1 }) expect((await ApiKeys.resolve(kv, token))?.billingActive).toBe(true) // A converged fleet re-reads but writes nothing. expect(await syncer.tick()).toStrictEqual({ checked: 1, updated: 0 }) await db.close() }) test('behavior: reconciles a lapse back to the public quota', async () => { const db = create() await db.migrate() const kv = Store.memory() const { token } = await ApiKeys.mint(kv, { billingActive: true, environment: 'sandbox', orgId: 'org_1', scopes: ['data:read'], }) await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_1', }) await StripeCustomers.setStatus(db, 'org_1', 'past_due', 'sandbox') expect(await Billing.createKeyBillingSyncer({ db, kv }).tick()).toStrictEqual({ checked: 1, updated: 1, }) // Non-`active` billing throttles: the snapshot flips back to false. expect((await ApiKeys.resolve(kv, token))?.billingActive).toBe(false) await db.close() }) test('behavior: leaves production billing sources untouched', async () => { const db = create() await db.migrate() const kv = Store.memory() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'active') // The default syncer reconciles sandbox only; a production-only org is // outside its driver set. expect(await Billing.createKeyBillingSyncer({ db, kv }).tick()).toStrictEqual({ checked: 0, updated: 0, }) await db.close() }) }) describe('ensureSubscription', () => { test('behavior: adds the missing item to a live subscription', async () => { const { itemsCreated, stripe, subscriptionsCreated } = stubStripe({ subscriptions: [{ items: ['feePayerSpend'], status: 'active' }], }) const result = await Billing.ensureSubscription(stripe, 'cus_1', [ 'feePayerSpend', 'apiRequests', ]) expect(result.created).toBe(false) expect(subscriptionsCreated).toHaveLength(0) expect(itemsCreated).toEqual([{ price: 'price_req', subscription: expect.any(String) }]) expect(result.items.apiRequests?.created).toBe(true) expect(result.items.feePayerSpend?.created).toBe(false) }) test('behavior: creates a subscription carrying every requested item', async () => { const { stripe, subscriptionsCreated } = stubStripe({ subscriptions: [] }) const result = await Billing.ensureSubscription(stripe, 'cus_1', [ 'feePayerSpend', 'apiRequests', ]) expect(result.created).toBe(true) expect(subscriptionsCreated).toHaveLength(1) expect(subscriptionsCreated[0]!.items.map((item) => item.price).sort()).toEqual([ 'price_fee', 'price_req', ]) }) test('behavior: fails closed on multiple live managed subscriptions', async () => { const { stripe } = stubStripe({ subscriptions: [ { items: ['feePayerSpend'], status: 'active' }, { items: ['apiRequests'], status: 'active' }, ], }) await expect( Billing.ensureSubscription(stripe, 'cus_1', ['feePayerSpend', 'apiRequests']), ).rejects.toBeInstanceOf(Billing.DuplicateSubscriptionError) }) }) describe('createRequestUsageReporter', () => { // A closed hour inside the settlement window for the fixed clock below. const bucketStart = '2026-01-01T12:00:00.000Z' const now = () => Date.parse('2026-01-02T00:20:00.000Z') test('behavior: seals the initial count, then bills only the delta on growth', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'active') // Tick 1: fresh customer, initial seal of 5. The just-created item bills at // now (no backdate). const first = stubStripe({ subscriptions: [] }) const reporter1 = Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 5, orgId: 'org_1' }], stripe: first.stripe, }) expect(await reporter1.tick()).toStrictEqual({ failed: 0, reported: 1, skipped: 0 }) expect(first.meterEvents).toEqual([ { event_name: 'api_request_count', identifier: expect.any(String), payload: expect.objectContaining({ value: '5' }) }, // prettier-ignore ]) expect(first.meterEvents[0]!.timestamp).toBeUndefined() expect((await RequestUsage.getBucket(db, { bucketStart, environment: 'production', orgId: 'org_1' }))?.reportedCount).toBe(5) // prettier-ignore // Tick 2: count grows to 8; only the delta of 3 bills, backdated (item exists). const second = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) const reporter2 = Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 8, orgId: 'org_1' }], stripe: second.stripe, }) expect(await reporter2.tick()).toStrictEqual({ failed: 0, reported: 1, skipped: 0 }) expect(second.meterEvents[0]?.payload).toMatchObject({ value: '3' }) expect(second.meterEvents[0]?.timestamp).toBe(Math.floor(Date.parse(bucketStart) / 1000)) expect((await RequestUsage.getBucket(db, { bucketStart, environment: 'production', orgId: 'org_1' }))?.reportedCount).toBe(8) // prettier-ignore // Tick 3: no growth; nothing bills. const third = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) expect( await Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 8, orgId: 'org_1' }], stripe: third.stripe, }).tick(), ).toStrictEqual({ failed: 0, reported: 0, skipped: 0 }) expect(third.meterEvents).toHaveLength(0) await db.close() }) test('behavior: overlapping ticks cannot claim the same observed watermark twice', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'active') const paused = Promise.withResolvers() const resume = Promise.withResolvers() const delayed = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], subscriptionsList: async () => { paused.resolve() await resume.promise }, }) const delayedTick = Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 5, orgId: 'org_1' }], stripe: delayed.stripe, }).tick() // Pause after this tick observes an empty watermark, then let an // overlapping tick report and settle that same count first. await paused.promise const winner = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) expect( await Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 5, orgId: 'org_1' }], stripe: winner.stripe, }).tick(), ).toStrictEqual({ failed: 0, reported: 1, skipped: 0 }) resume.resolve() expect(await delayedTick).toStrictEqual({ failed: 0, reported: 0, skipped: 0 }) expect(winner.meterEvents).toHaveLength(1) expect(delayed.meterEvents).toHaveLength(0) expect((await RequestUsage.getBucket(db, { bucketStart, environment: 'production', orgId: 'org_1' }))?.reportedCount).toBe(5) // prettier-ignore await db.close() }) test('behavior: seals only hours older than the settlement delay', async () => { const db = create() await db.migrate() const readCounts = vi.fn(async () => [] as { bucketStart: string; count: number; orgId: string }[]) // prettier-ignore const { stripe } = stubStripe({ subscriptions: [] }) await Billing.createRequestUsageReporter({ db, now, readCounts, stripe }).tick() // Cutoff is the start of the hour 15 minutes before `now`. expect(readCounts).toHaveBeenCalledWith({ environment: 'production', from: '2025-12-31T00:00:00.000Z', to: '2026-01-02T00:00:00.000Z', }) await db.close() }) test('behavior: an org without a billing source is skipped and never sealed', async () => { const db = create() await db.migrate() const { meterEvents, stripe } = stubStripe({ subscriptions: [] }) expect( await Billing.createRequestUsageReporter({ db, now, readCounts: async () => [{ bucketStart, count: 4, orgId: 'org_1' }], stripe, }).tick(), ).toStrictEqual({ failed: 0, reported: 0, skipped: 1 }) expect(meterEvents).toHaveLength(0) expect(await RequestUsage.getBucket(db, { bucketStart, environment: 'production', orgId: 'org_1' })).toBeUndefined() // prettier-ignore await db.close() }) test('behavior: bills sandbox usage on the sandbox client', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_sandbox', }) await StripeCustomers.setStatus(db, 'org_1', 'active', 'sandbox') const production = stubStripe({ subscriptions: [] }) const sandbox = stubStripe({ subscriptions: [] }) const reporter = Billing.createRequestUsageReporter({ db, now, readCounts: async ({ environment }) => environment === 'sandbox' ? [{ bucketStart, count: 7, orgId: 'org_1' }] : [], sandboxStripe: sandbox.stripe, stripe: production.stripe, }) expect(await reporter.tick()).toStrictEqual({ failed: 0, reported: 1, skipped: 0 }) expect(production.meterEvents).toHaveLength(0) expect(sandbox.meterEvents[0]?.payload).toMatchObject({ stripe_customer_id: 'cus_sandbox', value: '7', }) expect((await RequestUsage.getBucket(db, { bucketStart, environment: 'sandbox', orgId: 'org_1' }))?.reportedCount).toBe(7) // prettier-ignore await db.close() }) test('behavior: re-sends a stranded pending event and advances the watermark', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'active') // Simulate a crash after sealing but before the Stripe ack. await RequestUsage.insertPending(db, { bucketStart, deltaCount: 6, environment: 'production', identifier: 'rq_production_deadbeef0001_0_0', orgId: 'org_1', sequence: 0, stripeCustomerId: 'cus_1', }) const { meterEvents, stripe } = stubStripe({ subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) expect( await Billing.createRequestUsageReporter({ db, now, // No new counts; recovery must still drain the pending event. readCounts: async () => [], stripe, }).tick(), ).toStrictEqual({ failed: 0, reported: 1, skipped: 0 }) expect(meterEvents[0]?.payload).toMatchObject({ value: '6' }) expect((await RequestUsage.getBucket(db, { bucketStart, environment: 'production', orgId: 'org_1' }))?.reportedCount).toBe(6) // prettier-ignore await db.close() }) test('behavior: exposes the durable retry backlog for alerting', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_1' }) await StripeCustomers.setStatus(db, 'org_1', 'active') const { stripe } = stubStripe({ meterEvent() { throw new Error('Stripe unavailable') }, subscriptions: [{ items: ['feePayerSpend', 'apiRequests'], status: 'active' }], }) const reporter = Billing.createRequestUsageReporter({ db, now: () => Date.now(), readCounts: async () => [{ bucketStart, count: 4, orgId: 'org_1' }], stripe, }) expect(await reporter.tick()).toStrictEqual({ failed: 1, reported: 0, skipped: 0 }) expect(await reporter.pending()).toEqual([ { count: 1, environment: 'production', oldestPendingAgeSeconds: expect.any(Number), }, ]) await db.close() }) })