Mock
Test billing flows without hitting real payment APIs.
Setup
lib/billing.test.ts
import { createEnhancedClient } from "@fuime/billing-sdk";
import { mock } from "@fuime/billing-sdk/mock";
const mockAdapter = mock();
const billing = createEnhancedClient({
provider: mockAdapter,
cache: { ttlMs: 60_000 },
plans: {
prod_pro: { features: ["api", "export"] },
prod_enterprise: { features: "*" },
},
});Creating test subscriptions
// Add a subscription for testing
mockAdapter.addSubscription({
userId: "user_123",
productId: "prod_pro",
status: "active",
currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
// Now entitlement checks work
const hasAccess = await billing.hasFeature("user_123", "api");
// trueSimulating webhooks
// Trigger a webhook event
const event = mockAdapter.triggerWebhook({
type: "subscription.updated",
userId: "user_123",
productId: "prod_enterprise",
});
// The cache is automatically invalidatedTest scenarios
Test upgrade flow
test("user can upgrade", async () => {
const mockAdapter = mock();
const billing = createEnhancedClient({
provider: mockAdapter,
plans: {
prod_free: { features: ["basic"] },
prod_pro: { features: ["basic", "api", "export"] },
},
});
// Start on free plan
mockAdapter.addSubscription({
userId: "user_123",
productId: "prod_free",
status: "active",
});
expect(await billing.hasFeature("user_123", "api")).toBe(false);
// Simulate upgrade
mockAdapter.updateSubscription("user_123", {
productId: "prod_pro",
});
// Clear cache to see new state
billing.invalidateCache("user_123");
expect(await billing.hasFeature("user_123", "api")).toBe(true);
});Test cancellation
test("cancelled user loses access", async () => {
mockAdapter.addSubscription({
userId: "user_123",
productId: "prod_pro",
status: "active",
});
expect(await billing.hasFeature("user_123", "api")).toBe(true);
// Cancel subscription
mockAdapter.cancelSubscription("user_123");
billing.invalidateCache("user_123");
expect(await billing.hasFeature("user_123", "api")).toBe(false);
});Resetting state
beforeEach(() => {
mockAdapter.reset(); // Clears all subscriptions
});