@kernhq/module-billing 0.2.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.
Files changed (46) hide show
  1. package/LICENSE +662 -0
  2. package/dist/contract.d.ts +965 -0
  3. package/dist/contract.d.ts.map +1 -0
  4. package/dist/contract.js +265 -0
  5. package/dist/contract.js.map +1 -0
  6. package/dist/server/index.d.ts +11 -0
  7. package/dist/server/index.d.ts.map +1 -0
  8. package/dist/server/index.js +165 -0
  9. package/dist/server/index.js.map +1 -0
  10. package/dist/server/router.d.ts +697 -0
  11. package/dist/server/router.d.ts.map +1 -0
  12. package/dist/server/router.js +126 -0
  13. package/dist/server/router.js.map +1 -0
  14. package/dist/server/schema.d.ts +1030 -0
  15. package/dist/server/schema.d.ts.map +1 -0
  16. package/dist/server/schema.js +137 -0
  17. package/dist/server/schema.js.map +1 -0
  18. package/dist/server/services/entitlements.d.ts +3 -0
  19. package/dist/server/services/entitlements.d.ts.map +1 -0
  20. package/dist/server/services/entitlements.js +68 -0
  21. package/dist/server/services/entitlements.js.map +1 -0
  22. package/dist/server/services/plans.d.ts +20 -0
  23. package/dist/server/services/plans.d.ts.map +1 -0
  24. package/dist/server/services/plans.js +122 -0
  25. package/dist/server/services/plans.js.map +1 -0
  26. package/dist/server/services/stripe.d.ts +42 -0
  27. package/dist/server/services/stripe.d.ts.map +1 -0
  28. package/dist/server/services/stripe.js +267 -0
  29. package/dist/server/services/stripe.js.map +1 -0
  30. package/dist/server/services/subscriptions.d.ts +35 -0
  31. package/dist/server/services/subscriptions.d.ts.map +1 -0
  32. package/dist/server/services/subscriptions.js +193 -0
  33. package/dist/server/services/subscriptions.js.map +1 -0
  34. package/dist/server/services/usage.d.ts +40 -0
  35. package/dist/server/services/usage.d.ts.map +1 -0
  36. package/dist/server/services/usage.js +98 -0
  37. package/dist/server/services/usage.js.map +1 -0
  38. package/migrations/0000_init.sql +82 -0
  39. package/migrations/0001_rls.sql +18 -0
  40. package/migrations/meta/0000_snapshot.json +580 -0
  41. package/migrations/meta/_journal.json +20 -0
  42. package/package.json +73 -0
  43. package/src/client/api.ts +15 -0
  44. package/src/client/format.test.ts +64 -0
  45. package/src/client/index.ts +81 -0
  46. package/src/contract.ts +311 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/server/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgC,MAAM,gBAAgB,CAAA;AAgC1E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsH3C"}
@@ -0,0 +1,126 @@
1
+ import { o, requires, workspaceScoped } from '@kernhq/kernel';
2
+ import { implement, ORPCError } from '@orpc/server';
3
+ import { BILLING_PERMISSIONS, billingContract, billingEvents, MODULE_ID, PlanLimits } from '../contract.js';
4
+ import * as entitlements from './services/entitlements.js';
5
+ import * as plansSvc from './services/plans.js';
6
+ import * as stripeSvc from './services/stripe.js';
7
+ import * as subsSvc from './services/subscriptions.js';
8
+ import * as usageSvc from './services/usage.js';
9
+ const os = implement(billingContract).$context();
10
+ /**
11
+ * The instance console's audience.
12
+ *
13
+ * Not `workspaceScoped`: these procedures deliberately cross workspaces, which is the whole point of
14
+ * an operator screen. Membership therefore proves nothing here and the instance-admin flag is the
15
+ * only thing that does.
16
+ */
17
+ const instanceAdmin = o.middleware(async ({ context, next }) => {
18
+ const { principal } = context;
19
+ if (principal.kind === 'anonymous')
20
+ throw new ORPCError('UNAUTHORIZED');
21
+ if (!principal.instanceAdmin && principal.kind !== 'service')
22
+ throw new ORPCError('FORBIDDEN', { message: 'Instance administrators only' });
23
+ return next();
24
+ });
25
+ /** Any signed-in principal — enough to be shown what the plans cost. */
26
+ const signedIn = o.middleware(async ({ context, next }) => {
27
+ if (context.principal.kind === 'anonymous')
28
+ throw new ORPCError('UNAUTHORIZED');
29
+ return next();
30
+ });
31
+ export function billingRouter(kernel) {
32
+ const scoped = os.use(workspaceScoped(MODULE_ID));
33
+ return os.router({
34
+ plans: {
35
+ list: os.plans.list.use(signedIn).handler(async ({ input, context }) => {
36
+ // an unpublished plan is a draft, and a draft is the operator's business
37
+ if (input.includeUnpublished &&
38
+ !context.principal.instanceAdmin &&
39
+ context.principal.kind !== 'service')
40
+ throw new ORPCError('FORBIDDEN', { message: 'Instance administrators only' });
41
+ return plansSvc.list(kernel, input.includeUnpublished);
42
+ }),
43
+ // No middleware at all: this is what a marketing site reads, and it has no session.
44
+ public: os.plans.public.handler(async () => plansSvc.publicList(kernel)),
45
+ upsert: os.plans.upsert.use(instanceAdmin).handler(async ({ input }) => {
46
+ const plan = await plansSvc.upsert(kernel, input);
47
+ await kernel.emit(billingEvents.catalogueChanged, { planSlug: plan.slug });
48
+ return plan;
49
+ }),
50
+ setPublished: os.plans.setPublished.use(instanceAdmin).handler(async ({ input }) => {
51
+ const plan = await plansSvc.setPublished(kernel, input.id, input.published);
52
+ // what the marketing site shows has changed; whoever rebuilds it listens for this
53
+ await kernel.emit(billingEvents.catalogueChanged, { planSlug: plan.slug });
54
+ return plan;
55
+ }),
56
+ archive: os.plans.archive.use(instanceAdmin).handler(async ({ input }) => {
57
+ await plansSvc.archive(kernel, input.id);
58
+ return { ok: true };
59
+ }),
60
+ },
61
+ subscription: {
62
+ get: scoped.subscription.get.use(requires(BILLING_PERMISSIONS.view)).handler(async ({ input }) => {
63
+ const [subscription, used, limits] = await Promise.all([
64
+ subsSvc.get(kernel, input.workspaceId),
65
+ usageSvc.read(kernel, input.workspaceId),
66
+ entitlements.resolve(kernel, input.workspaceId),
67
+ ]);
68
+ return {
69
+ subscription,
70
+ usage: {
71
+ seats: used.seats,
72
+ storageBytes: used.storageBytes,
73
+ updatedAt: used.updatedAt.toISOString(),
74
+ },
75
+ limits: PlanLimits.parse(limits),
76
+ active: limits.active ?? true,
77
+ paymentsEnabled: stripeSvc.paymentsEnabled(),
78
+ };
79
+ }),
80
+ invoices: scoped.subscription.invoices
81
+ .use(requires(BILLING_PERMISSIONS.view))
82
+ .handler(async ({ input }) => ({
83
+ items: await subsSvc.listInvoices(kernel, input.workspaceId, input.limit),
84
+ nextCursor: null,
85
+ })),
86
+ checkout: scoped.subscription.checkout
87
+ .use(requires(BILLING_PERMISSIONS.manage))
88
+ .handler(async ({ input, context }) => stripeSvc.checkout(kernel, {
89
+ workspaceId: input.workspaceId,
90
+ planSlug: input.planSlug,
91
+ seats: input.seats,
92
+ email: context.principal.email ?? undefined,
93
+ baseUrl: kernel.env.KERN_BASE_URL,
94
+ })),
95
+ portal: scoped.subscription.portal
96
+ .use(requires(BILLING_PERMISSIONS.manage))
97
+ .handler(async ({ input }) => stripeSvc.portal(kernel, {
98
+ workspaceId: input.workspaceId,
99
+ returnUrl: `${kernel.env.KERN_BASE_URL.replace(/\/$/, '')}${input.returnPath}`,
100
+ })),
101
+ },
102
+ admin: {
103
+ workspaces: os.admin.workspaces.use(instanceAdmin).handler(async ({ input }) => ({
104
+ items: await subsSvc.adminList(kernel, {
105
+ q: input.q,
106
+ status: input.status,
107
+ limit: input.limit,
108
+ }),
109
+ nextCursor: null,
110
+ })),
111
+ setPlan: os.admin.setPlan
112
+ .use(instanceAdmin)
113
+ .handler(async ({ input }) => subsSvc.setPlan(kernel, input.workspaceId, input.planId, input.seatsPurchased)),
114
+ override: os.admin.override
115
+ .use(instanceAdmin)
116
+ .handler(async ({ input, context }) => subsSvc.setOverride(kernel, input.workspaceId, input.limits, context.principal.userId ?? null)),
117
+ extendTrial: os.admin.extendTrial
118
+ .use(instanceAdmin)
119
+ .handler(async ({ input }) => subsSvc.extendTrial(kernel, input.workspaceId, input.days)),
120
+ setStatus: os.admin.setStatus
121
+ .use(instanceAdmin)
122
+ .handler(async ({ input }) => subsSvc.setStatus(kernel, input.workspaceId, input.status)),
123
+ },
124
+ });
125
+ }
126
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/server/router.ts"],"names":[],"mappings":"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"}