@wopr-network/platform-core 1.45.0 → 1.46.0
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/billing/stripe/billing-period-summary-repository.d.ts +22 -0
- package/dist/billing/stripe/billing-period-summary-repository.js +14 -0
- package/dist/billing/stripe/index.d.ts +10 -0
- package/dist/billing/stripe/index.js +5 -0
- package/dist/billing/stripe/metered-price-map.d.ts +26 -0
- package/dist/billing/stripe/metered-price-map.js +75 -0
- package/dist/billing/stripe/stripe-payment-processor.test.js +1 -0
- package/dist/billing/stripe/stripe-usage-reconciliation.d.ts +31 -0
- package/dist/billing/stripe/stripe-usage-reconciliation.js +84 -0
- package/dist/billing/stripe/stripe-usage-reconciliation.test.d.ts +1 -0
- package/dist/billing/stripe/stripe-usage-reconciliation.test.js +109 -0
- package/dist/billing/stripe/tenant-store.d.ts +9 -0
- package/dist/billing/stripe/tenant-store.js +7 -0
- package/dist/billing/stripe/usage-report-repository.d.ts +39 -0
- package/dist/billing/stripe/usage-report-repository.js +30 -0
- package/dist/billing/stripe/usage-report-repository.test.d.ts +1 -0
- package/dist/billing/stripe/usage-report-repository.test.js +77 -0
- package/dist/billing/stripe/usage-report-writer.d.ts +41 -0
- package/dist/billing/stripe/usage-report-writer.js +95 -0
- package/dist/billing/stripe/usage-report-writer.test.d.ts +1 -0
- package/dist/billing/stripe/usage-report-writer.test.js +167 -0
- package/dist/gateway/credit-gate.js +5 -0
- package/dist/gateway/credit-gate.test.js +53 -0
- package/dist/gateway/types.d.ts +2 -0
- package/dist/monetization/stripe/stripe-payment-processor.test.js +1 -0
- package/package.json +1 -1
- package/src/billing/stripe/billing-period-summary-repository.ts +32 -0
- package/src/billing/stripe/index.ts +17 -0
- package/src/billing/stripe/metered-price-map.ts +95 -0
- package/src/billing/stripe/stripe-payment-processor.test.ts +1 -0
- package/src/billing/stripe/stripe-usage-reconciliation.test.ts +127 -0
- package/src/billing/stripe/stripe-usage-reconciliation.ts +129 -0
- package/src/billing/stripe/tenant-store.ts +9 -0
- package/src/billing/stripe/usage-report-repository.test.ts +87 -0
- package/src/billing/stripe/usage-report-repository.ts +77 -0
- package/src/billing/stripe/usage-report-writer.test.ts +194 -0
- package/src/billing/stripe/usage-report-writer.ts +139 -0
- package/src/gateway/credit-gate.test.ts +62 -0
- package/src/gateway/credit-gate.ts +6 -0
- package/src/gateway/types.ts +2 -0
- package/src/monetization/stripe/stripe-payment-processor.test.ts +1 -0
|
@@ -240,3 +240,65 @@ describe("debitCredits with allowNegative and onBalanceExhausted", () => {
|
|
|
240
240
|
expect(onBalanceExhausted).not.toHaveBeenCalled();
|
|
241
241
|
});
|
|
242
242
|
});
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// creditBalanceCheck — metered tenant bypass
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
async function buildHonoContextWithMode(
|
|
249
|
+
tenantId: string,
|
|
250
|
+
inferenceMode: string,
|
|
251
|
+
): Promise<import("hono").Context<GatewayAuthEnv>> {
|
|
252
|
+
let capturedCtx!: import("hono").Context<GatewayAuthEnv>;
|
|
253
|
+
const app = new Hono<GatewayAuthEnv>();
|
|
254
|
+
app.get("/test", (c) => {
|
|
255
|
+
c.set("gatewayTenant", {
|
|
256
|
+
id: tenantId,
|
|
257
|
+
spendLimits: { maxSpendPerHour: null, maxSpendPerMonth: null },
|
|
258
|
+
inferenceMode,
|
|
259
|
+
} as GatewayTenant);
|
|
260
|
+
capturedCtx = c;
|
|
261
|
+
return c.json({});
|
|
262
|
+
});
|
|
263
|
+
await app.request("/test");
|
|
264
|
+
return capturedCtx;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
describe("creditBalanceCheck metered tenant bypass", () => {
|
|
268
|
+
beforeEach(async () => {
|
|
269
|
+
await truncateAllTables(pool);
|
|
270
|
+
await new DrizzleLedger(db).seedSystemAccounts();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("skips balance check for metered tenants even with zero balance", async () => {
|
|
274
|
+
const ledger = new DrizzleLedger(db);
|
|
275
|
+
// No credits — would normally fail with credits_exhausted
|
|
276
|
+
const c = await buildHonoContextWithMode("metered-t1", "metered");
|
|
277
|
+
const deps: CreditGateDeps = { creditLedger: ledger, topUpUrl: "/billing" };
|
|
278
|
+
const error = await creditBalanceCheck(c, deps, 0);
|
|
279
|
+
expect(error).toBeNull();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("still debits metered tenants for P&L tracking", async () => {
|
|
283
|
+
const ledger = new DrizzleLedger(db);
|
|
284
|
+
await ledger.credit("metered-t1", Credit.fromCents(0), "purchase", { description: "setup" }).catch(() => {});
|
|
285
|
+
const mockLedger = {
|
|
286
|
+
debit: vi.fn().mockResolvedValue(undefined),
|
|
287
|
+
balance: vi.fn(),
|
|
288
|
+
credit: vi.fn(),
|
|
289
|
+
} as unknown as import("@wopr-network/platform-core/credits").ILedger;
|
|
290
|
+
const deps: CreditGateDeps = { creditLedger: mockLedger, topUpUrl: "/billing" };
|
|
291
|
+
await debitCredits(deps, "metered-tenant", 0.05, 1.5, "chat-completions", "openrouter");
|
|
292
|
+
expect(mockLedger.debit).toHaveBeenCalled();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("non-metered (managed) tenant still gets balance checked", async () => {
|
|
296
|
+
const ledger = new DrizzleLedger(db);
|
|
297
|
+
// No credits seeded — should fail
|
|
298
|
+
const c = await buildHonoContextWithMode("managed-t1", "managed");
|
|
299
|
+
const deps: CreditGateDeps = { creditLedger: ledger, topUpUrl: "/billing" };
|
|
300
|
+
const error = await creditBalanceCheck(c, deps, 0);
|
|
301
|
+
// Balance is 0, within grace buffer — passes
|
|
302
|
+
expect(error).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -61,6 +61,12 @@ export async function creditBalanceCheck(
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Metered tenants are invoiced via Stripe subscription, not prepaid credits.
|
|
65
|
+
// Skip balance enforcement but still allow debit (for P&L tracking).
|
|
66
|
+
if (tenant.inferenceMode === "metered") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
const balance = await deps.creditLedger.balance(tenant.id);
|
|
65
71
|
const required = Math.max(0, estimatedCostCents);
|
|
66
72
|
const graceBuffer = deps.graceBufferCents ?? 50; // default -$0.50
|
package/src/gateway/types.ts
CHANGED
|
@@ -57,6 +57,8 @@ export interface GatewayTenant {
|
|
|
57
57
|
instanceId?: string;
|
|
58
58
|
/** User-configured spending caps (null fields = no cap). */
|
|
59
59
|
spendingCaps?: SpendingCaps;
|
|
60
|
+
/** Billing mode — "metered" tenants are invoiced via Stripe, not prepaid credits. */
|
|
61
|
+
inferenceMode?: "metered" | "managed" | "byok";
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/** Fetch function type for dependency injection in tests. */
|