@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,580 @@
1
+ {
2
+ "id": "367a87d6-92d5-4217-98f7-d4214a69b19d",
3
+ "prevId": "00000000-0000-0000-0000-000000000000",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "mod_billing.invoices": {
8
+ "name": "invoices",
9
+ "schema": "mod_billing",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "uuid",
14
+ "primaryKey": true,
15
+ "notNull": true,
16
+ "default": "uuidv7()"
17
+ },
18
+ "workspace_id": {
19
+ "name": "workspace_id",
20
+ "type": "uuid",
21
+ "primaryKey": false,
22
+ "notNull": true
23
+ },
24
+ "stripe_invoice_id": {
25
+ "name": "stripe_invoice_id",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": false
29
+ },
30
+ "number": {
31
+ "name": "number",
32
+ "type": "text",
33
+ "primaryKey": false,
34
+ "notNull": false
35
+ },
36
+ "status": {
37
+ "name": "status",
38
+ "type": "text",
39
+ "primaryKey": false,
40
+ "notNull": true
41
+ },
42
+ "total_minor": {
43
+ "name": "total_minor",
44
+ "type": "integer",
45
+ "primaryKey": false,
46
+ "notNull": true,
47
+ "default": 0
48
+ },
49
+ "currency": {
50
+ "name": "currency",
51
+ "type": "text",
52
+ "primaryKey": false,
53
+ "notNull": true,
54
+ "default": "'usd'"
55
+ },
56
+ "period_start": {
57
+ "name": "period_start",
58
+ "type": "timestamp with time zone",
59
+ "primaryKey": false,
60
+ "notNull": false
61
+ },
62
+ "period_end": {
63
+ "name": "period_end",
64
+ "type": "timestamp with time zone",
65
+ "primaryKey": false,
66
+ "notNull": false
67
+ },
68
+ "hosted_url": {
69
+ "name": "hosted_url",
70
+ "type": "text",
71
+ "primaryKey": false,
72
+ "notNull": false
73
+ },
74
+ "pdf_url": {
75
+ "name": "pdf_url",
76
+ "type": "text",
77
+ "primaryKey": false,
78
+ "notNull": false
79
+ },
80
+ "created_at": {
81
+ "name": "created_at",
82
+ "type": "timestamp with time zone",
83
+ "primaryKey": false,
84
+ "notNull": true,
85
+ "default": "now()"
86
+ }
87
+ },
88
+ "indexes": {
89
+ "invoices_ws_idx": {
90
+ "name": "invoices_ws_idx",
91
+ "columns": [
92
+ {
93
+ "expression": "workspace_id",
94
+ "isExpression": false,
95
+ "asc": true,
96
+ "nulls": "last"
97
+ },
98
+ {
99
+ "expression": "created_at",
100
+ "isExpression": false,
101
+ "asc": true,
102
+ "nulls": "last"
103
+ }
104
+ ],
105
+ "isUnique": false,
106
+ "concurrently": false,
107
+ "method": "btree",
108
+ "with": {}
109
+ },
110
+ "invoices_stripe_idx": {
111
+ "name": "invoices_stripe_idx",
112
+ "columns": [
113
+ {
114
+ "expression": "stripe_invoice_id",
115
+ "isExpression": false,
116
+ "asc": true,
117
+ "nulls": "last"
118
+ }
119
+ ],
120
+ "isUnique": true,
121
+ "concurrently": false,
122
+ "method": "btree",
123
+ "with": {}
124
+ }
125
+ },
126
+ "foreignKeys": {},
127
+ "compositePrimaryKeys": {},
128
+ "uniqueConstraints": {},
129
+ "policies": {},
130
+ "checkConstraints": {},
131
+ "isRLSEnabled": false
132
+ },
133
+ "mod_billing.overrides": {
134
+ "name": "overrides",
135
+ "schema": "mod_billing",
136
+ "columns": {
137
+ "workspace_id": {
138
+ "name": "workspace_id",
139
+ "type": "uuid",
140
+ "primaryKey": true,
141
+ "notNull": true
142
+ },
143
+ "limits": {
144
+ "name": "limits",
145
+ "type": "jsonb",
146
+ "primaryKey": false,
147
+ "notNull": true,
148
+ "default": "'{}'::jsonb"
149
+ },
150
+ "note": {
151
+ "name": "note",
152
+ "type": "text",
153
+ "primaryKey": false,
154
+ "notNull": true,
155
+ "default": "''"
156
+ },
157
+ "created_by": {
158
+ "name": "created_by",
159
+ "type": "uuid",
160
+ "primaryKey": false,
161
+ "notNull": false
162
+ },
163
+ "created_at": {
164
+ "name": "created_at",
165
+ "type": "timestamp with time zone",
166
+ "primaryKey": false,
167
+ "notNull": true,
168
+ "default": "now()"
169
+ },
170
+ "updated_at": {
171
+ "name": "updated_at",
172
+ "type": "timestamp with time zone",
173
+ "primaryKey": false,
174
+ "notNull": true,
175
+ "default": "now()"
176
+ }
177
+ },
178
+ "indexes": {},
179
+ "foreignKeys": {},
180
+ "compositePrimaryKeys": {},
181
+ "uniqueConstraints": {},
182
+ "policies": {},
183
+ "checkConstraints": {},
184
+ "isRLSEnabled": false
185
+ },
186
+ "mod_billing.plans": {
187
+ "name": "plans",
188
+ "schema": "mod_billing",
189
+ "columns": {
190
+ "id": {
191
+ "name": "id",
192
+ "type": "uuid",
193
+ "primaryKey": true,
194
+ "notNull": true,
195
+ "default": "uuidv7()"
196
+ },
197
+ "slug": {
198
+ "name": "slug",
199
+ "type": "text",
200
+ "primaryKey": false,
201
+ "notNull": true
202
+ },
203
+ "name": {
204
+ "name": "name",
205
+ "type": "text",
206
+ "primaryKey": false,
207
+ "notNull": true
208
+ },
209
+ "description": {
210
+ "name": "description",
211
+ "type": "text",
212
+ "primaryKey": false,
213
+ "notNull": true,
214
+ "default": "''"
215
+ },
216
+ "price_minor": {
217
+ "name": "price_minor",
218
+ "type": "integer",
219
+ "primaryKey": false,
220
+ "notNull": true,
221
+ "default": 0
222
+ },
223
+ "currency": {
224
+ "name": "currency",
225
+ "type": "text",
226
+ "primaryKey": false,
227
+ "notNull": true,
228
+ "default": "'usd'"
229
+ },
230
+ "interval": {
231
+ "name": "interval",
232
+ "type": "text",
233
+ "primaryKey": false,
234
+ "notNull": true,
235
+ "default": "'month'"
236
+ },
237
+ "per_seat": {
238
+ "name": "per_seat",
239
+ "type": "boolean",
240
+ "primaryKey": false,
241
+ "notNull": true,
242
+ "default": true
243
+ },
244
+ "trial_days": {
245
+ "name": "trial_days",
246
+ "type": "integer",
247
+ "primaryKey": false,
248
+ "notNull": true,
249
+ "default": 0
250
+ },
251
+ "limits": {
252
+ "name": "limits",
253
+ "type": "jsonb",
254
+ "primaryKey": false,
255
+ "notNull": true,
256
+ "default": "'{}'::jsonb"
257
+ },
258
+ "stripe_price_id": {
259
+ "name": "stripe_price_id",
260
+ "type": "text",
261
+ "primaryKey": false,
262
+ "notNull": false
263
+ },
264
+ "highlights": {
265
+ "name": "highlights",
266
+ "type": "jsonb",
267
+ "primaryKey": false,
268
+ "notNull": true,
269
+ "default": "'[]'::jsonb"
270
+ },
271
+ "published": {
272
+ "name": "published",
273
+ "type": "boolean",
274
+ "primaryKey": false,
275
+ "notNull": true,
276
+ "default": false
277
+ },
278
+ "order": {
279
+ "name": "order",
280
+ "type": "integer",
281
+ "primaryKey": false,
282
+ "notNull": true,
283
+ "default": 100
284
+ },
285
+ "archived_at": {
286
+ "name": "archived_at",
287
+ "type": "timestamp with time zone",
288
+ "primaryKey": false,
289
+ "notNull": false
290
+ },
291
+ "created_at": {
292
+ "name": "created_at",
293
+ "type": "timestamp with time zone",
294
+ "primaryKey": false,
295
+ "notNull": true,
296
+ "default": "now()"
297
+ },
298
+ "updated_at": {
299
+ "name": "updated_at",
300
+ "type": "timestamp with time zone",
301
+ "primaryKey": false,
302
+ "notNull": true,
303
+ "default": "now()"
304
+ }
305
+ },
306
+ "indexes": {
307
+ "plans_slug_idx": {
308
+ "name": "plans_slug_idx",
309
+ "columns": [
310
+ {
311
+ "expression": "slug",
312
+ "isExpression": false,
313
+ "asc": true,
314
+ "nulls": "last"
315
+ }
316
+ ],
317
+ "isUnique": true,
318
+ "concurrently": false,
319
+ "method": "btree",
320
+ "with": {}
321
+ },
322
+ "plans_published_idx": {
323
+ "name": "plans_published_idx",
324
+ "columns": [
325
+ {
326
+ "expression": "published",
327
+ "isExpression": false,
328
+ "asc": true,
329
+ "nulls": "last"
330
+ },
331
+ {
332
+ "expression": "order",
333
+ "isExpression": false,
334
+ "asc": true,
335
+ "nulls": "last"
336
+ }
337
+ ],
338
+ "isUnique": false,
339
+ "concurrently": false,
340
+ "method": "btree",
341
+ "with": {}
342
+ }
343
+ },
344
+ "foreignKeys": {},
345
+ "compositePrimaryKeys": {},
346
+ "uniqueConstraints": {},
347
+ "policies": {},
348
+ "checkConstraints": {},
349
+ "isRLSEnabled": false
350
+ },
351
+ "mod_billing.subscriptions": {
352
+ "name": "subscriptions",
353
+ "schema": "mod_billing",
354
+ "columns": {
355
+ "workspace_id": {
356
+ "name": "workspace_id",
357
+ "type": "uuid",
358
+ "primaryKey": true,
359
+ "notNull": true
360
+ },
361
+ "plan_id": {
362
+ "name": "plan_id",
363
+ "type": "uuid",
364
+ "primaryKey": false,
365
+ "notNull": false
366
+ },
367
+ "status": {
368
+ "name": "status",
369
+ "type": "text",
370
+ "primaryKey": false,
371
+ "notNull": true,
372
+ "default": "'trialing'"
373
+ },
374
+ "seats_purchased": {
375
+ "name": "seats_purchased",
376
+ "type": "integer",
377
+ "primaryKey": false,
378
+ "notNull": true,
379
+ "default": 0
380
+ },
381
+ "trial_ends_at": {
382
+ "name": "trial_ends_at",
383
+ "type": "timestamp with time zone",
384
+ "primaryKey": false,
385
+ "notNull": false
386
+ },
387
+ "current_period_end": {
388
+ "name": "current_period_end",
389
+ "type": "timestamp with time zone",
390
+ "primaryKey": false,
391
+ "notNull": false
392
+ },
393
+ "cancel_at_period_end": {
394
+ "name": "cancel_at_period_end",
395
+ "type": "boolean",
396
+ "primaryKey": false,
397
+ "notNull": true,
398
+ "default": false
399
+ },
400
+ "stripe_customer_id": {
401
+ "name": "stripe_customer_id",
402
+ "type": "text",
403
+ "primaryKey": false,
404
+ "notNull": false
405
+ },
406
+ "stripe_subscription_id": {
407
+ "name": "stripe_subscription_id",
408
+ "type": "text",
409
+ "primaryKey": false,
410
+ "notNull": false
411
+ },
412
+ "grace_ends_at": {
413
+ "name": "grace_ends_at",
414
+ "type": "timestamp with time zone",
415
+ "primaryKey": false,
416
+ "notNull": false
417
+ },
418
+ "created_at": {
419
+ "name": "created_at",
420
+ "type": "timestamp with time zone",
421
+ "primaryKey": false,
422
+ "notNull": true,
423
+ "default": "now()"
424
+ },
425
+ "updated_at": {
426
+ "name": "updated_at",
427
+ "type": "timestamp with time zone",
428
+ "primaryKey": false,
429
+ "notNull": true,
430
+ "default": "now()"
431
+ }
432
+ },
433
+ "indexes": {
434
+ "subscriptions_status_idx": {
435
+ "name": "subscriptions_status_idx",
436
+ "columns": [
437
+ {
438
+ "expression": "status",
439
+ "isExpression": false,
440
+ "asc": true,
441
+ "nulls": "last"
442
+ }
443
+ ],
444
+ "isUnique": false,
445
+ "concurrently": false,
446
+ "method": "btree",
447
+ "with": {}
448
+ },
449
+ "subscriptions_stripe_sub_idx": {
450
+ "name": "subscriptions_stripe_sub_idx",
451
+ "columns": [
452
+ {
453
+ "expression": "stripe_subscription_id",
454
+ "isExpression": false,
455
+ "asc": true,
456
+ "nulls": "last"
457
+ }
458
+ ],
459
+ "isUnique": true,
460
+ "concurrently": false,
461
+ "method": "btree",
462
+ "with": {}
463
+ }
464
+ },
465
+ "foreignKeys": {},
466
+ "compositePrimaryKeys": {},
467
+ "uniqueConstraints": {},
468
+ "policies": {},
469
+ "checkConstraints": {},
470
+ "isRLSEnabled": false
471
+ },
472
+ "mod_billing.webhook_events": {
473
+ "name": "webhook_events",
474
+ "schema": "mod_billing",
475
+ "columns": {
476
+ "id": {
477
+ "name": "id",
478
+ "type": "text",
479
+ "primaryKey": true,
480
+ "notNull": true
481
+ },
482
+ "type": {
483
+ "name": "type",
484
+ "type": "text",
485
+ "primaryKey": false,
486
+ "notNull": true
487
+ },
488
+ "received_at": {
489
+ "name": "received_at",
490
+ "type": "timestamp with time zone",
491
+ "primaryKey": false,
492
+ "notNull": true,
493
+ "default": "now()"
494
+ }
495
+ },
496
+ "indexes": {
497
+ "webhook_events_received_idx": {
498
+ "name": "webhook_events_received_idx",
499
+ "columns": [
500
+ {
501
+ "expression": "received_at",
502
+ "isExpression": false,
503
+ "asc": true,
504
+ "nulls": "last"
505
+ }
506
+ ],
507
+ "isUnique": false,
508
+ "concurrently": false,
509
+ "method": "btree",
510
+ "with": {}
511
+ }
512
+ },
513
+ "foreignKeys": {},
514
+ "compositePrimaryKeys": {},
515
+ "uniqueConstraints": {},
516
+ "policies": {},
517
+ "checkConstraints": {},
518
+ "isRLSEnabled": false
519
+ },
520
+ "mod_billing.workspace_usage": {
521
+ "name": "workspace_usage",
522
+ "schema": "mod_billing",
523
+ "columns": {
524
+ "workspace_id": {
525
+ "name": "workspace_id",
526
+ "type": "uuid",
527
+ "primaryKey": true,
528
+ "notNull": true
529
+ },
530
+ "seats": {
531
+ "name": "seats",
532
+ "type": "integer",
533
+ "primaryKey": false,
534
+ "notNull": true,
535
+ "default": 0
536
+ },
537
+ "storage_bytes": {
538
+ "name": "storage_bytes",
539
+ "type": "bigint",
540
+ "primaryKey": false,
541
+ "notNull": true,
542
+ "default": 0
543
+ },
544
+ "reconciled_at": {
545
+ "name": "reconciled_at",
546
+ "type": "timestamp with time zone",
547
+ "primaryKey": false,
548
+ "notNull": false
549
+ },
550
+ "updated_at": {
551
+ "name": "updated_at",
552
+ "type": "timestamp with time zone",
553
+ "primaryKey": false,
554
+ "notNull": true,
555
+ "default": "now()"
556
+ }
557
+ },
558
+ "indexes": {},
559
+ "foreignKeys": {},
560
+ "compositePrimaryKeys": {},
561
+ "uniqueConstraints": {},
562
+ "policies": {},
563
+ "checkConstraints": {},
564
+ "isRLSEnabled": false
565
+ }
566
+ },
567
+ "enums": {},
568
+ "schemas": {
569
+ "mod_billing": "mod_billing"
570
+ },
571
+ "sequences": {},
572
+ "roles": {},
573
+ "policies": {},
574
+ "views": {},
575
+ "_meta": {
576
+ "columns": {},
577
+ "schemas": {},
578
+ "tables": {}
579
+ }
580
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1787478118425,
9
+ "tag": "0000_init",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "7",
15
+ "when": 1787478118426,
16
+ "tag": "0001_rls",
17
+ "breakpoints": true
18
+ }
19
+ ]
20
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@kernhq/module-billing",
3
+ "version": "0.2.0",
4
+ "description": "Kern billing module: plans, entitlements, subscriptions and Stripe — the mechanism that lets any Kern instance sell seats on itself",
5
+ "homepage": "https://kernaio.com",
6
+ "license": "AGPL-3.0-only",
7
+ "type": "module",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/KernAIO/modules",
11
+ "directory": "packages/billing"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "migrations",
19
+ "src/client",
20
+ "src/contract.ts"
21
+ ],
22
+ "exports": {
23
+ "./contract": {
24
+ "types": "./dist/contract.d.ts",
25
+ "import": "./dist/contract.js",
26
+ "default": "./dist/contract.js"
27
+ },
28
+ "./server": {
29
+ "types": "./dist/server/index.d.ts",
30
+ "import": "./dist/server/index.js",
31
+ "default": "./dist/server/index.js"
32
+ },
33
+ "./client": {
34
+ "types": "./src/client/index.ts",
35
+ "svelte": "./src/client/index.ts",
36
+ "default": "./src/client/index.ts"
37
+ },
38
+ "./client/*": {
39
+ "types": "./src/client/*",
40
+ "svelte": "./src/client/*",
41
+ "default": "./src/client/*"
42
+ },
43
+ "./migrations": "./migrations"
44
+ },
45
+ "dependencies": {
46
+ "@kernhq/contracts": "^0.2.0",
47
+ "@kernhq/kernel": "^0.2.0",
48
+ "@kernhq/sdk": "^0.1.0",
49
+ "@orpc/contract": "^1.15.0",
50
+ "@orpc/server": "^1.15.0",
51
+ "drizzle-orm": "^0.45.0",
52
+ "stripe": "^22.5.0",
53
+ "zod": "^4.1.0"
54
+ },
55
+ "devDependencies": {
56
+ "@kernhq/tsconfig": "^0.1.0",
57
+ "@types/node": "^24.0.0",
58
+ "@types/pg": "^8.15.0",
59
+ "drizzle-kit": "^0.31.0",
60
+ "pg": "^8.16.0",
61
+ "typescript": "~5.9.3",
62
+ "vitest": "^4.0.0"
63
+ },
64
+ "scripts": {
65
+ "build": "tsc -p tsconfig.json",
66
+ "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
67
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
68
+ "lint": "biome check .",
69
+ "test": "vitest run",
70
+ "db:generate": "drizzle-kit generate",
71
+ "test:unit": "vitest run --exclude 'src/**/*.int.test.ts'"
72
+ }
73
+ }
@@ -0,0 +1,15 @@
1
+ import { createModuleClient, type KernClientOptions } from '@kernhq/sdk'
2
+ import type { ContractRouterClient } from '@orpc/contract'
3
+ import type { BillingContract } from '../contract.js'
4
+
5
+ /**
6
+ * Typed oRPC client for `/api/billing` (see `../contract`).
7
+ *
8
+ * The shape is generated from the contract, so every procedure, input and output stays in step with
9
+ * the server without a second declaration: if the contract changes, callers stop compiling.
10
+ */
11
+ export type BillingApi = ContractRouterClient<BillingContract>
12
+
13
+ export function createBillingClient(opts: KernClientOptions): BillingApi {
14
+ return createModuleClient<BillingApi>(opts, 'billing')
15
+ }