@kernhq/module-billing 0.4.0 → 0.5.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/contract.d.ts +16 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +20 -3
- package/dist/contract.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +125 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/router.d.ts +10 -72
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +12 -1
- package/dist/server/router.js.map +1 -1
- package/dist/server/services/catalogue.d.ts +30 -0
- package/dist/server/services/catalogue.d.ts.map +1 -0
- package/dist/server/services/catalogue.js +87 -0
- package/dist/server/services/catalogue.js.map +1 -0
- package/dist/server/services/entitlements.d.ts.map +1 -1
- package/dist/server/services/entitlements.js +28 -8
- package/dist/server/services/entitlements.js.map +1 -1
- package/dist/server/services/stripe.d.ts +34 -0
- package/dist/server/services/stripe.d.ts.map +1 -1
- package/dist/server/services/stripe.js +205 -17
- package/dist/server/services/stripe.js.map +1 -1
- package/package.json +6 -6
- package/src/client/i18n.ts +64 -0
- package/src/client/settings/PlanSettings.svelte +163 -8
- package/src/contract.ts +22 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { UpsertPlan } from '../../contract.js';
|
|
4
|
+
import * as plans from './plans.js';
|
|
5
|
+
import { paymentsEnabled } from './stripe.js';
|
|
6
|
+
/**
|
|
7
|
+
* Getting the plan catalogue onto an instance, and refusing to pretend one is there when it is not.
|
|
8
|
+
*
|
|
9
|
+
* Both halves exist for the same failure: a new workspace whose default plan is missing gets **no
|
|
10
|
+
* subscription row at all**, and no row resolves to *unlimited* — which is the right answer for
|
|
11
|
+
* every self-hosted Kern and exactly the wrong one for an instance that takes money. The only
|
|
12
|
+
* signal used to be a `warn` nobody reads, so a Kern Cloud one typo away from giving everything
|
|
13
|
+
* away looked completely healthy.
|
|
14
|
+
*/
|
|
15
|
+
/** The file `KERN_PLANS_FILE` points at: a JSON array of plans, in the shape the admin console writes. */
|
|
16
|
+
const PlansFile = z.array(UpsertPlan);
|
|
17
|
+
/**
|
|
18
|
+
* Create any plan in `KERN_PLANS_FILE` whose slug this instance does not have yet.
|
|
19
|
+
*
|
|
20
|
+
* **Create, never update.** A plan is data an instance admin edits — prices, limits, what the
|
|
21
|
+
* pricing page says — and an image that rewrote the catalogue on every boot would silently revert
|
|
22
|
+
* their work at the next deploy. So a slug that already exists is left exactly as it is, and the
|
|
23
|
+
* file is only ever the answer to "a fresh instance has no plans at all".
|
|
24
|
+
*
|
|
25
|
+
* Returns the slugs it created, so the boot log says what happened rather than that something did.
|
|
26
|
+
*/
|
|
27
|
+
export async function seedPlansFromFile(kernel) {
|
|
28
|
+
const path = process.env.KERN_PLANS_FILE;
|
|
29
|
+
if (!path)
|
|
30
|
+
return [];
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = PlansFile.parse(JSON.parse(await readFile(path, 'utf8')));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
// Loud, and not fatal: an unreadable seed file must not take down a service that is otherwise
|
|
37
|
+
// configured, and the boot check below is what refuses when the result is actually unusable.
|
|
38
|
+
kernel.log.error({ err: String(err), path }, 'billing: KERN_PLANS_FILE could not be read as a list of plans; no plan was seeded');
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const created = [];
|
|
42
|
+
for (const plan of parsed) {
|
|
43
|
+
if (await plans.bySlug(kernel, plan.slug))
|
|
44
|
+
continue;
|
|
45
|
+
// `id` is dropped: the file describes a catalogue, not this database's primary keys, and two
|
|
46
|
+
// instances seeded from one file must not be told they share a row.
|
|
47
|
+
const { id: _ignored, ...rest } = plan;
|
|
48
|
+
await plans.upsert(kernel, rest);
|
|
49
|
+
created.push(plan.slug);
|
|
50
|
+
}
|
|
51
|
+
if (created.length)
|
|
52
|
+
kernel.log.info({ created, path }, 'billing: seeded plans from KERN_PLANS_FILE');
|
|
53
|
+
return created;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Refuse to start, or say loudly why every new workspace is about to be unlimited.
|
|
57
|
+
*
|
|
58
|
+
* Two conditions, and they are not equally recoverable:
|
|
59
|
+
*
|
|
60
|
+
* - **No `KERN_DEFAULT_PLAN_SLUG` on an instance with a Stripe key.** Pure configuration, fixable
|
|
61
|
+
* in the environment without the service running, and it means every single signup is unlimited.
|
|
62
|
+
* That one throws.
|
|
63
|
+
* - **A slug naming a plan that does not exist.** Also wrong, and it is what a brand-new instance
|
|
64
|
+
* looks like for as long as it takes somebody to create the plan — so refusing here would lock
|
|
65
|
+
* the operator out of the console they need in order to fix it. That one is an `error` in the log
|
|
66
|
+
* and a refusal on the path that matters, which is `core.workspace.created`.
|
|
67
|
+
*
|
|
68
|
+
* An instance with no Stripe key is a self-hosted Kern: unlimited is the correct and intended
|
|
69
|
+
* answer there, and nothing here fires.
|
|
70
|
+
*/
|
|
71
|
+
export async function assertDefaultPlan(kernel) {
|
|
72
|
+
if (!paymentsEnabled())
|
|
73
|
+
return;
|
|
74
|
+
const slug = process.env.KERN_DEFAULT_PLAN_SLUG;
|
|
75
|
+
if (!slug)
|
|
76
|
+
throw new Error('STRIPE_SECRET_KEY is set, so this instance sells subscriptions, but KERN_DEFAULT_PLAN_SLUG is ' +
|
|
77
|
+
'empty. A workspace created without a plan has no subscription row, and no subscription row ' +
|
|
78
|
+
'means unlimited seats, unlimited storage and SSO included — every signup would get the whole ' +
|
|
79
|
+
'product for nothing.\n' +
|
|
80
|
+
'Set KERN_DEFAULT_PLAN_SLUG to the slug of the plan new workspaces start on, or unset ' +
|
|
81
|
+
'STRIPE_SECRET_KEY if this instance is not meant to take payments.');
|
|
82
|
+
if (!(await plans.bySlug(kernel, slug)))
|
|
83
|
+
kernel.log.error({ slug }, 'billing: KERN_DEFAULT_PLAN_SLUG names a plan that does not exist, so every new workspace ' +
|
|
84
|
+
'will be created without a subscription and resolve to unlimited. Create the plan in the ' +
|
|
85
|
+
'instance console, seed it with KERN_PLANS_FILE, or point the variable at a plan that exists.');
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=catalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../../src/server/services/catalogue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;;;GAQG;AAEH,0GAA0G;AAC1G,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AAErC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,IAAI,MAAiC,CAAA;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8FAA8F;QAC9F,6FAA6F;QAC7F,MAAM,CAAC,GAAG,CAAC,KAAK,CACd,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,EAC1B,mFAAmF,CACpF,CAAA;QACD,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,SAAQ;QACnD,6FAA6F;QAC7F,oEAAoE;QACpE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;QACtC,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAChC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,4CAA4C,CAAC,CAAA;IACpG,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,IAAI,CAAC,eAAe,EAAE;QAAE,OAAM;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAA;IAC/C,IAAI,CAAC,IAAI;QACP,MAAM,IAAI,KAAK,CACb,gGAAgG;YAC9F,6FAA6F;YAC7F,+FAA+F;YAC/F,wBAAwB;YACxB,uFAAuF;YACvF,mEAAmE,CACtE,CAAA;IACH,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,KAAK,CACd,EAAE,IAAI,EAAE,EACR,2FAA2F;YACzF,0FAA0F;YAC1F,8FAA8F,CACjG,CAAA;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../../src/server/services/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../../src/server/services/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAsDzD,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CA6ChG"}
|
|
@@ -11,6 +11,29 @@ import { overrides, plans, subscriptions } from '../schema.js';
|
|
|
11
11
|
const ENTITLED = new Set(['trialing', 'active', 'past_due']);
|
|
12
12
|
/** Statuses where the workspace may read but not write. */
|
|
13
13
|
const INACTIVE = new Set(['suspended', 'canceled']);
|
|
14
|
+
/**
|
|
15
|
+
* What a workspace keeps once its subscription stops entitling it.
|
|
16
|
+
*
|
|
17
|
+
* This used to be `PlanLimits.parse({})`, which is *unlimited* — so cancelling or being suspended
|
|
18
|
+
* made a workspace strictly more capable than paying for it did, which is the opposite of what
|
|
19
|
+
* suspension means. Nothing noticed, because every field parsed and `active: false` reached only a
|
|
20
|
+
* banner.
|
|
21
|
+
*
|
|
22
|
+
* The rule now: never wider than the plan, and nothing left that lets the workspace **grow**. Seats
|
|
23
|
+
* and storage stop at what is already there, and SSO is a paid feature that stops being included.
|
|
24
|
+
*
|
|
25
|
+
* `apiRateLimit` and `auditRetentionDays` are deliberately untouched. Narrowing either would break
|
|
26
|
+
* *reading* — the budget is spent by every workspace-scoped request, retention decides what a
|
|
27
|
+
* pruner deletes — and being able to read and export what is yours is the one promise suspension
|
|
28
|
+
* makes. See ADR 0003 §6.
|
|
29
|
+
*
|
|
30
|
+
* `seats` cannot express a zero floor (`PlanLimits.seats` is positive-or-null, and 0 would fail the
|
|
31
|
+
* parse the plan screen does on the way out), so the floor is 1: no new member, and not one person
|
|
32
|
+
* removed from the ones already there.
|
|
33
|
+
*/
|
|
34
|
+
function frozen(limits) {
|
|
35
|
+
return { ...limits, seats: limits.seats ?? 1, storageBytes: 0, sso: false };
|
|
36
|
+
}
|
|
14
37
|
/**
|
|
15
38
|
* What a workspace is allowed to do, resolved from its plan and any override an admin has set.
|
|
16
39
|
*
|
|
@@ -52,15 +75,12 @@ export async function resolve(kernel, workspaceId) {
|
|
|
52
75
|
// a default the admin never chose — which means dropping the keys the patch does not mention
|
|
53
76
|
// rather than spreading them as `undefined`.
|
|
54
77
|
const patch = ovr?.limits ? definedOnly(PlanLimitsPatch.parse(ovr.limits)) : null;
|
|
55
|
-
const merged = patch
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: // Not entitled: the plan's allowances do not apply, but nothing is taken away that would
|
|
60
|
-
// stop the customer reading and exporting what is theirs.
|
|
61
|
-
PlanLimits.parse({});
|
|
78
|
+
const merged = patch ? { ...base, ...patch } : base;
|
|
79
|
+
// The status decides entitlement; the plan and the override decide the numbers. An override is an
|
|
80
|
+
// operator comping a *limit*, never an operator un-suspending a workspace — that is `setStatus`,
|
|
81
|
+
// and keeping the two apart is what stops a comped account quietly outliving its own payment.
|
|
62
82
|
return {
|
|
63
|
-
...merged,
|
|
83
|
+
...(ENTITLED.has(row.status) ? merged : frozen(merged)),
|
|
64
84
|
planName: row.planName ?? null,
|
|
65
85
|
active: !INACTIVE.has(row.status),
|
|
66
86
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../../../src/server/services/entitlements.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,eAAe,EAA2B,MAAM,mBAAmB,CAAA;AACxF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAqB,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;AAErG,2DAA2D;AAC3D,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAqB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAA;AAE5F;;;;;;;GAOG;AACH,6FAA6F;AAC7F,SAAS,WAAW,CAAmB,KAAQ;IAC7C,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAe,CAAA;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,MAAc,EAAE,WAAmB;IAC/D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;SACnB,MAAM,CAAC;QACN,MAAM,EAAE,aAAa,CAAC,MAAM;QAC5B,QAAQ,EAAE,KAAK,CAAC,IAAI;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC;SACD,IAAI,CAAC,aAAa,CAAC;SACnB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;SACnD,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;SACjD,KAAK,CAAC,CAAC,CAAC,CAAA;IAEX,iGAAiG;IACjG,0CAA0C;IAC1C,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAEjD,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO;QACjB,MAAM,CAAC,GAAG,CAAC,IAAI,CACb,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EACnD,yEAAyE,CAC1E,CAAA;IACH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAEhE,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;SACnB,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;SACpC,IAAI,CAAC,SAAS,CAAC;SACf,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;SAC7C,KAAK,CAAC,CAAC,CAAC,CAAA;IAEX,mGAAmG;IACnG,6FAA6F;IAC7F,6CAA6C;IAC7C,MAAM,KAAK,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACjF,MAAM,MAAM,GAAG,KAAK
|
|
1
|
+
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../../../src/server/services/entitlements.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Kernel } from '@kernhq/kernel';
|
|
2
2
|
import Stripe from 'stripe';
|
|
3
|
+
import type { SubscriptionStatus } from '../../contract.js';
|
|
3
4
|
/**
|
|
4
5
|
* Stripe, or nothing at all.
|
|
5
6
|
*
|
|
@@ -9,14 +10,45 @@ import Stripe from 'stripe';
|
|
|
9
10
|
*/
|
|
10
11
|
export declare function client(): Stripe | null;
|
|
11
12
|
export declare const paymentsEnabled: () => boolean;
|
|
13
|
+
/** Stripe's subscription statuses, mapped onto the five this module recognises. */
|
|
14
|
+
export declare function mapStatus(s: Stripe.Subscription.Status): SubscriptionStatus;
|
|
15
|
+
/**
|
|
16
|
+
* How many seats this workspace should be billed for right now.
|
|
17
|
+
*
|
|
18
|
+
* The count comes from core through the shared seat definition — guests never consume one — rather
|
|
19
|
+
* than from anything this module decides. It used to default to `1` whenever the caller did not
|
|
20
|
+
* pass a number, and no caller ever passes one, so a twelve-person workspace bought a single seat
|
|
21
|
+
* and stayed on it until the next membership event happened to fire `syncSeats`.
|
|
22
|
+
*
|
|
23
|
+
* Core being unreachable must not stop somebody paying us, so the stored counter is the fallback,
|
|
24
|
+
* and 1 is the floor — Stripe rejects a quantity of 0.
|
|
25
|
+
*/
|
|
26
|
+
export declare function billableSeats(kernel: Kernel, workspaceId: string, asked?: number): Promise<number>;
|
|
27
|
+
/** Where Stripe sends the person back to, with `checkout=` for the screen to render. */
|
|
28
|
+
export declare function returnUrl(baseUrl: string, returnPath: string, outcome: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Put a workspace on a plan.
|
|
31
|
+
*
|
|
32
|
+
* **A workspace that already has a subscription is repriced, never sent through Checkout again.**
|
|
33
|
+
* Checkout in `subscription` mode creates a *new* subscription every time it completes, and
|
|
34
|
+
* `applySubscription` then overwrites `stripeSubscriptionId` — so an upgrade left the old
|
|
35
|
+
* subscription running with nothing pointing at it, and the customer was charged for both, for
|
|
36
|
+
* ever, while the pricing page promised "change any time; we bill the difference pro rata".
|
|
37
|
+
*
|
|
38
|
+
* Repricing is what keeps that promise: one subscription item moved to the new price with
|
|
39
|
+
* `create_prorations`, so Stripe credits the unused part of the old plan and charges the new one
|
|
40
|
+
* from today.
|
|
41
|
+
*/
|
|
12
42
|
export declare function checkout(kernel: Kernel, input: {
|
|
13
43
|
workspaceId: string;
|
|
14
44
|
planSlug: string;
|
|
15
45
|
seats?: number;
|
|
16
46
|
email?: string;
|
|
17
47
|
baseUrl: string;
|
|
48
|
+
returnPath?: string;
|
|
18
49
|
}): Promise<{
|
|
19
50
|
url: string;
|
|
51
|
+
changed: boolean;
|
|
20
52
|
}>;
|
|
21
53
|
export declare function portal(kernel: Kernel, input: {
|
|
22
54
|
workspaceId: string;
|
|
@@ -26,6 +58,8 @@ export declare function portal(kernel: Kernel, input: {
|
|
|
26
58
|
}>;
|
|
27
59
|
/** Keep the seat quantity on Stripe in step with the workspace's actual membership. */
|
|
28
60
|
export declare function syncSeats(kernel: Kernel, workspaceId: string, seats: number): Promise<void>;
|
|
61
|
+
/** A grace clock `GRACE_DAYS` from now. */
|
|
62
|
+
export declare function graceFrom(now?: Date): Date;
|
|
29
63
|
/**
|
|
30
64
|
* Apply one webhook.
|
|
31
65
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../src/server/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,MAAM,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../src/server/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAK3D;;;;;;GAMG;AACH,wBAAgB,MAAM,IAAI,MAAM,GAAG,IAAI,CAYtC;AAqBD,eAAO,MAAM,eAAe,eAA+C,CAAA;AAY3E,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,kBAAkB,CA2B3E;AAyBD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWxG;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GACA,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAgD5C;AAED,wBAAsB,MAAM,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAU1B;AAED,uFAAuF;AACvF,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjG;AAiDD,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,GAAG,OAAa,GAAG,IAAI,CAIhD;AAyFD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA2B7C;AAsED,gEAAgE;AAChE,eAAO,MAAM,UAAU,KAAK,CAAA"}
|
|
@@ -3,6 +3,7 @@ import { eq } from 'drizzle-orm';
|
|
|
3
3
|
import Stripe from 'stripe';
|
|
4
4
|
import { invoices, plans, subscriptions, webhookEvents } from '../schema.js';
|
|
5
5
|
import * as subs from './subscriptions.js';
|
|
6
|
+
import * as usage from './usage.js';
|
|
6
7
|
/**
|
|
7
8
|
* Stripe, or nothing at all.
|
|
8
9
|
*
|
|
@@ -16,12 +17,33 @@ export function client() {
|
|
|
16
17
|
return null;
|
|
17
18
|
return new Stripe(key, {
|
|
18
19
|
// Pinned deliberately: an account-level API version change must not reach a running instance
|
|
19
|
-
// before its image has been built against it.
|
|
20
|
-
|
|
20
|
+
// before its image has been built against it. This must match the version bundled with the
|
|
21
|
+
// installed `stripe` package — the SDK's types reject any other literal.
|
|
22
|
+
apiVersion: '2026-08-26.dahlia',
|
|
21
23
|
appInfo: { name: 'Kern', url: 'https://kernaio.com' },
|
|
22
24
|
maxNetworkRetries: 2,
|
|
25
|
+
...apiBase(),
|
|
23
26
|
});
|
|
24
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Where the Stripe API lives. `api.stripe.com` unless `STRIPE_API_BASE` says otherwise.
|
|
30
|
+
*
|
|
31
|
+
* It exists so that what this module *sends* Stripe can be asserted — that an upgrade repriced one
|
|
32
|
+
* subscription item instead of opening a second subscription, that the quantity was the workspace's
|
|
33
|
+
* real seat count, that the success URL is a route that exists. None of that is observable from the
|
|
34
|
+
* outside, and all three shipped wrong. Point it at `stripe-mock` or at a test double.
|
|
35
|
+
*
|
|
36
|
+
* Not a documented operator setting: an instance that sets this is not talking to Stripe, and the
|
|
37
|
+
* only honest reason to do that is a test.
|
|
38
|
+
*/
|
|
39
|
+
function apiBase() {
|
|
40
|
+
const base = process.env.STRIPE_API_BASE;
|
|
41
|
+
if (!base)
|
|
42
|
+
return {};
|
|
43
|
+
const url = new URL(base);
|
|
44
|
+
const protocol = url.protocol === 'https:' ? 'https' : 'http';
|
|
45
|
+
return { host: url.hostname, port: Number(url.port || (protocol === 'https' ? 443 : 80)), protocol };
|
|
46
|
+
}
|
|
25
47
|
export const paymentsEnabled = () => Boolean(process.env.STRIPE_SECRET_KEY);
|
|
26
48
|
function required() {
|
|
27
49
|
const s = client();
|
|
@@ -30,15 +52,23 @@ function required() {
|
|
|
30
52
|
return s;
|
|
31
53
|
}
|
|
32
54
|
/** Stripe's subscription statuses, mapped onto the five this module recognises. */
|
|
33
|
-
function mapStatus(s) {
|
|
55
|
+
export function mapStatus(s) {
|
|
34
56
|
switch (s) {
|
|
35
57
|
case 'trialing':
|
|
36
58
|
return 'trialing';
|
|
37
59
|
case 'active':
|
|
38
60
|
return 'active';
|
|
39
61
|
case 'past_due':
|
|
40
|
-
case 'unpaid':
|
|
41
62
|
return 'past_due';
|
|
63
|
+
/**
|
|
64
|
+
* `unpaid` is Stripe saying it has finished trying: the dunning window is over and it will not
|
|
65
|
+
* attempt the card again. It used to map to `past_due`, which is an *entitled* status here — and
|
|
66
|
+
* only the grace clock ends it, which nothing sets on this path. So the workspace Stripe had
|
|
67
|
+
* given up on stayed entitled for ever. It is suspended: read-only, resumable the moment a
|
|
68
|
+
* payment succeeds, and nothing deleted.
|
|
69
|
+
*/
|
|
70
|
+
case 'unpaid':
|
|
71
|
+
return 'suspended';
|
|
42
72
|
case 'canceled':
|
|
43
73
|
case 'incomplete_expired':
|
|
44
74
|
return 'canceled';
|
|
@@ -65,6 +95,53 @@ async function customerFor(kernel, workspaceId, email) {
|
|
|
65
95
|
await subs.upsert(kernel, workspaceId, { stripeCustomerId: customer.id });
|
|
66
96
|
return customer.id;
|
|
67
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Stripe subscription states an existing subscription can be repriced in.
|
|
100
|
+
*
|
|
101
|
+
* Everything else — cancelled, expired, given up on, never completed — is not a subscription to
|
|
102
|
+
* move; it is one to replace, which means Checkout and a fresh card.
|
|
103
|
+
*/
|
|
104
|
+
const REPRICEABLE = new Set(['active', 'trialing', 'past_due']);
|
|
105
|
+
/**
|
|
106
|
+
* How many seats this workspace should be billed for right now.
|
|
107
|
+
*
|
|
108
|
+
* The count comes from core through the shared seat definition — guests never consume one — rather
|
|
109
|
+
* than from anything this module decides. It used to default to `1` whenever the caller did not
|
|
110
|
+
* pass a number, and no caller ever passes one, so a twelve-person workspace bought a single seat
|
|
111
|
+
* and stayed on it until the next membership event happened to fire `syncSeats`.
|
|
112
|
+
*
|
|
113
|
+
* Core being unreachable must not stop somebody paying us, so the stored counter is the fallback,
|
|
114
|
+
* and 1 is the floor — Stripe rejects a quantity of 0.
|
|
115
|
+
*/
|
|
116
|
+
export async function billableSeats(kernel, workspaceId, asked) {
|
|
117
|
+
if (asked !== undefined)
|
|
118
|
+
return Math.max(1, asked);
|
|
119
|
+
try {
|
|
120
|
+
return Math.max(1, await usage.recountSeats(kernel, workspaceId));
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
kernel.log.warn({ err: String(err), workspaceId }, 'billing: could not recount seats for checkout; using the stored counter');
|
|
124
|
+
return Math.max(1, (await usage.read(kernel, workspaceId)).seats);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Where Stripe sends the person back to, with `checkout=` for the screen to render. */
|
|
128
|
+
export function returnUrl(baseUrl, returnPath, outcome) {
|
|
129
|
+
const url = `${baseUrl.replace(/\/$/, '')}${returnPath.startsWith('/') ? returnPath : `/${returnPath}`}`;
|
|
130
|
+
return `${url}${url.includes('?') ? '&' : '?'}checkout=${outcome}`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Put a workspace on a plan.
|
|
134
|
+
*
|
|
135
|
+
* **A workspace that already has a subscription is repriced, never sent through Checkout again.**
|
|
136
|
+
* Checkout in `subscription` mode creates a *new* subscription every time it completes, and
|
|
137
|
+
* `applySubscription` then overwrites `stripeSubscriptionId` — so an upgrade left the old
|
|
138
|
+
* subscription running with nothing pointing at it, and the customer was charged for both, for
|
|
139
|
+
* ever, while the pricing page promised "change any time; we bill the difference pro rata".
|
|
140
|
+
*
|
|
141
|
+
* Repricing is what keeps that promise: one subscription item moved to the new price with
|
|
142
|
+
* `create_prorations`, so Stripe credits the unused part of the old plan and charges the new one
|
|
143
|
+
* from today.
|
|
144
|
+
*/
|
|
68
145
|
export async function checkout(kernel, input) {
|
|
69
146
|
const stripe = required();
|
|
70
147
|
const [plan] = await kernel.database.db.select().from(plans).where(eq(plans.slug, input.planSlug)).limit(1);
|
|
@@ -72,23 +149,46 @@ export async function checkout(kernel, input) {
|
|
|
72
149
|
throw KernError.notFound('Plan');
|
|
73
150
|
if (!plan.stripePriceId)
|
|
74
151
|
throw KernError.conflict('That plan has no Stripe price attached', 'billing.plan.no_price');
|
|
152
|
+
const quantity = plan.perSeat ? await billableSeats(kernel, input.workspaceId, input.seats) : 1;
|
|
153
|
+
const back = input.returnPath ?? '/';
|
|
154
|
+
const existing = await subs.get(kernel, input.workspaceId);
|
|
155
|
+
if (existing?.stripeSubscriptionId) {
|
|
156
|
+
const remote = await stripe.subscriptions.retrieve(existing.stripeSubscriptionId).catch(() => null);
|
|
157
|
+
const item = remote?.items.data[0];
|
|
158
|
+
if (remote && item && REPRICEABLE.has(remote.status)) {
|
|
159
|
+
const updated = await stripe.subscriptions.update(remote.id, {
|
|
160
|
+
items: [{ id: item.id, price: plan.stripePriceId, quantity }],
|
|
161
|
+
// the customer is billed the difference from today, not from the next period
|
|
162
|
+
proration_behavior: 'create_prorations',
|
|
163
|
+
// choosing a plan is a decision to keep the subscription, so a pending cancellation is off
|
|
164
|
+
cancel_at_period_end: false,
|
|
165
|
+
// the metadata is how a webhook finds its way home, and `kern_plan_id` is how the row
|
|
166
|
+
// learns which plan it is on — losing either here would strand the subscription
|
|
167
|
+
metadata: { kern_workspace_id: input.workspaceId, kern_plan_id: plan.id },
|
|
168
|
+
});
|
|
169
|
+
// Applied now rather than waiting for `customer.subscription.updated`: the person is looking
|
|
170
|
+
// at the screen. The webhook arrives too and writes the same thing, which is why it is safe.
|
|
171
|
+
await applySubscription(kernel, updated);
|
|
172
|
+
return { url: returnUrl(input.baseUrl, back, 'changed'), changed: true };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
75
175
|
const customer = await customerFor(kernel, input.workspaceId, input.email);
|
|
76
176
|
const session = await stripe.checkout.sessions.create({
|
|
77
177
|
mode: 'subscription',
|
|
78
178
|
customer,
|
|
79
|
-
line_items: [{ price: plan.stripePriceId, quantity
|
|
179
|
+
line_items: [{ price: plan.stripePriceId, quantity }],
|
|
80
180
|
subscription_data: {
|
|
81
181
|
...(plan.trialDays > 0 ? { trial_period_days: plan.trialDays } : {}),
|
|
82
182
|
metadata: { kern_workspace_id: input.workspaceId, kern_plan_id: plan.id },
|
|
83
183
|
},
|
|
84
184
|
// The card is taken up front, trial or not, so the subscription converts without asking again.
|
|
85
185
|
payment_method_collection: 'always',
|
|
86
|
-
success_url:
|
|
87
|
-
cancel_url:
|
|
186
|
+
success_url: returnUrl(input.baseUrl, back, 'done'),
|
|
187
|
+
cancel_url: returnUrl(input.baseUrl, back, 'cancelled'),
|
|
88
188
|
});
|
|
89
189
|
if (!session.url)
|
|
90
190
|
throw KernError.conflict('Stripe did not return a checkout URL', 'billing.stripe.no_url');
|
|
91
|
-
return { url: session.url };
|
|
191
|
+
return { url: session.url, changed: false };
|
|
92
192
|
}
|
|
93
193
|
export async function portal(kernel, input) {
|
|
94
194
|
const stripe = required();
|
|
@@ -134,10 +234,52 @@ async function claim(kernel, id, type) {
|
|
|
134
234
|
.returning({ id: webhookEvents.id });
|
|
135
235
|
return rows.length > 0;
|
|
136
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Give the claim back, so Stripe's retry is handled rather than answered "already applied".
|
|
239
|
+
*
|
|
240
|
+
* The claim is taken *before* the handler runs, because taking it after leaves two concurrent
|
|
241
|
+
* deliveries both applying the event. That ordering had a worse failure of its own: a handler that
|
|
242
|
+
* threw left the id claimed for ever, so every retry Stripe sent was recognised as a duplicate and
|
|
243
|
+
* dropped — the event was lost, permanently and silently, and a paid invoice was never mirrored.
|
|
244
|
+
*
|
|
245
|
+
* The two are not both fixable by one insert, so the claim is released on failure instead of being
|
|
246
|
+
* held in a transaction with the work. A transaction is the tidier answer and cannot be had here:
|
|
247
|
+
* `applyInvoice` runs inside `withWorkspace`, which takes its own connection to set the RLS context,
|
|
248
|
+
* so the insert and the write are on different connections by construction. The window this leaves
|
|
249
|
+
* — a duplicate delivery arriving while the first is failing — ends in the duplicate being dropped,
|
|
250
|
+
* which is what would have happened anyway, and Stripe retries either way.
|
|
251
|
+
*/
|
|
252
|
+
async function release(kernel, id) {
|
|
253
|
+
await kernel.database.db
|
|
254
|
+
.delete(webhookEvents)
|
|
255
|
+
.where(eq(webhookEvents.id, id))
|
|
256
|
+
.catch((err) => kernel.log.error({ err: String(err), event: id }, 'billing: could not release a webhook claim; Stripe will treat its retry as a duplicate'));
|
|
257
|
+
}
|
|
137
258
|
/** The workspace a Stripe object belongs to, from the metadata we set when creating it. */
|
|
138
259
|
function workspaceOf(o) {
|
|
139
260
|
return o.metadata?.kern_workspace_id ?? null;
|
|
140
261
|
}
|
|
262
|
+
/** A grace clock `GRACE_DAYS` from now. */
|
|
263
|
+
export function graceFrom(now = new Date()) {
|
|
264
|
+
const grace = new Date(now);
|
|
265
|
+
grace.setUTCDate(grace.getUTCDate() + GRACE_DAYS);
|
|
266
|
+
return grace;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Write what Stripe says about a subscription into our row.
|
|
270
|
+
*
|
|
271
|
+
* The grace clock is the delicate part. This used to write `graceEndsAt: null` unconditionally, and
|
|
272
|
+
* Stripe emits `customer.subscription.updated` for the same failed payment as
|
|
273
|
+
* `invoice.payment_failed` — in no guaranteed order. So the update wiped the clock the invoice
|
|
274
|
+
* event had just set, `billing.close-grace-periods` requires a non-null clock to act, and the
|
|
275
|
+
* `past_due → suspended` transition therefore almost never fired: a workspace that stopped paying
|
|
276
|
+
* kept working indefinitely.
|
|
277
|
+
*
|
|
278
|
+
* The clock now belongs to the *status*, not to the event that happened to arrive last. Past due
|
|
279
|
+
* keeps a clock already running and starts one if there is none — which also covers the order where
|
|
280
|
+
* `subscription.updated` is the only thing that arrives. Any other status means the payment problem
|
|
281
|
+
* is over, and clears it.
|
|
282
|
+
*/
|
|
141
283
|
async function applySubscription(kernel, s) {
|
|
142
284
|
const workspaceId = workspaceOf(s);
|
|
143
285
|
if (!workspaceId) {
|
|
@@ -147,16 +289,26 @@ async function applySubscription(kernel, s) {
|
|
|
147
289
|
const planId = s.metadata?.kern_plan_id ?? null;
|
|
148
290
|
const item = s.items.data[0];
|
|
149
291
|
const periodEnd = item?.current_period_end ?? null;
|
|
292
|
+
const status = mapStatus(s.status);
|
|
293
|
+
let graceEndsAt = null;
|
|
294
|
+
if (status === 'past_due') {
|
|
295
|
+
const [row] = await kernel.database.db
|
|
296
|
+
.select({ graceEndsAt: subscriptions.graceEndsAt })
|
|
297
|
+
.from(subscriptions)
|
|
298
|
+
.where(eq(subscriptions.workspaceId, workspaceId))
|
|
299
|
+
.limit(1);
|
|
300
|
+
graceEndsAt = row?.graceEndsAt ?? graceFrom();
|
|
301
|
+
}
|
|
150
302
|
await subs.upsert(kernel, workspaceId, {
|
|
151
303
|
...(planId ? { planId } : {}),
|
|
152
|
-
status
|
|
304
|
+
status,
|
|
153
305
|
seatsPurchased: item?.quantity ?? 0,
|
|
154
306
|
stripeSubscriptionId: s.id,
|
|
155
307
|
stripeCustomerId: typeof s.customer === 'string' ? s.customer : s.customer.id,
|
|
156
308
|
cancelAtPeriodEnd: s.cancel_at_period_end,
|
|
157
309
|
trialEndsAt: s.trial_end ? new Date(s.trial_end * 1000) : null,
|
|
158
310
|
currentPeriodEnd: periodEnd ? new Date(periodEnd * 1000) : null,
|
|
159
|
-
graceEndsAt
|
|
311
|
+
graceEndsAt,
|
|
160
312
|
});
|
|
161
313
|
}
|
|
162
314
|
async function applyInvoice(kernel, inv) {
|
|
@@ -221,6 +373,18 @@ export async function handleWebhook(kernel, raw, signature) {
|
|
|
221
373
|
kernel.log.info({ event: event.id, type: event.type }, 'billing: webhook already applied');
|
|
222
374
|
return { handled: false, type: event.type };
|
|
223
375
|
}
|
|
376
|
+
try {
|
|
377
|
+
await apply(kernel, stripe, event);
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
// The claim goes back so Stripe's retry is a real delivery rather than a duplicate. Rethrowing
|
|
381
|
+
// is what turns the retry on: the route answers 5xx and Stripe sends the event again.
|
|
382
|
+
await release(kernel, event.id);
|
|
383
|
+
throw err;
|
|
384
|
+
}
|
|
385
|
+
return { handled: true, type: event.type };
|
|
386
|
+
}
|
|
387
|
+
async function apply(kernel, stripe, event) {
|
|
224
388
|
switch (event.type) {
|
|
225
389
|
case 'customer.subscription.created':
|
|
226
390
|
case 'customer.subscription.updated':
|
|
@@ -230,8 +394,30 @@ export async function handleWebhook(kernel, raw, signature) {
|
|
|
230
394
|
case 'checkout.session.completed': {
|
|
231
395
|
const session = event.data.object;
|
|
232
396
|
const subId = typeof session.subscription === 'string' ? session.subscription : session.subscription?.id;
|
|
233
|
-
if (subId)
|
|
234
|
-
|
|
397
|
+
if (!subId)
|
|
398
|
+
break;
|
|
399
|
+
const remote = await stripe.subscriptions.retrieve(subId);
|
|
400
|
+
await applySubscription(kernel, remote);
|
|
401
|
+
/**
|
|
402
|
+
* The quantity was taken from the seat count at the moment the session was opened, and
|
|
403
|
+
* somebody may have joined or left while the card was being typed. `syncSeats` is a no-op when
|
|
404
|
+
* the two already agree, so this costs one read on the normal path.
|
|
405
|
+
*
|
|
406
|
+
* The whole thing is inside the try, `await billableSeats` included: written as an argument to
|
|
407
|
+
* a `.catch()`ed call it would be evaluated *before* the handler is attached, so a failure
|
|
408
|
+
* counting seats would escape, fail the webhook, and cost us the record of a payment that has
|
|
409
|
+
* already happened. A wrong seat count is a billing correction the nightly reconcile makes; a
|
|
410
|
+
* lost `checkout.session.completed` is a customer who paid and got nothing.
|
|
411
|
+
*/
|
|
412
|
+
const workspaceId = workspaceOf(remote);
|
|
413
|
+
if (workspaceId) {
|
|
414
|
+
try {
|
|
415
|
+
await syncSeats(kernel, workspaceId, await billableSeats(kernel, workspaceId));
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
kernel.log.warn({ err: String(err), workspaceId }, 'billing: could not sync seats after checkout; the nightly reconcile will correct it');
|
|
419
|
+
}
|
|
420
|
+
}
|
|
235
421
|
break;
|
|
236
422
|
}
|
|
237
423
|
case 'invoice.paid':
|
|
@@ -243,16 +429,19 @@ export async function handleWebhook(kernel, raw, signature) {
|
|
|
243
429
|
const customer = typeof inv.customer === 'string' ? inv.customer : inv.customer?.id;
|
|
244
430
|
if (customer) {
|
|
245
431
|
const [row] = await kernel.database.db
|
|
246
|
-
.select({ workspaceId: subscriptions.workspaceId })
|
|
432
|
+
.select({ workspaceId: subscriptions.workspaceId, graceEndsAt: subscriptions.graceEndsAt })
|
|
247
433
|
.from(subscriptions)
|
|
248
434
|
.where(eq(subscriptions.stripeCustomerId, customer))
|
|
249
435
|
.limit(1);
|
|
250
436
|
if (row) {
|
|
251
437
|
// A failed payment starts a clock, it does not close the workspace. Stripe keeps retrying
|
|
252
438
|
// for its own dunning window; the grace period is what decides when we stop waiting.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
await subs.upsert(kernel, row.workspaceId, {
|
|
439
|
+
// A clock already running is kept: a second failed invoice inside the window must not
|
|
440
|
+
// hand the customer another fourteen days.
|
|
441
|
+
await subs.upsert(kernel, row.workspaceId, {
|
|
442
|
+
status: 'past_due',
|
|
443
|
+
graceEndsAt: row.graceEndsAt ?? graceFrom(),
|
|
444
|
+
});
|
|
256
445
|
}
|
|
257
446
|
}
|
|
258
447
|
break;
|
|
@@ -260,7 +449,6 @@ export async function handleWebhook(kernel, raw, signature) {
|
|
|
260
449
|
default:
|
|
261
450
|
kernel.log.debug({ type: event.type }, 'billing: unhandled Stripe event');
|
|
262
451
|
}
|
|
263
|
-
return { handled: true, type: event.type };
|
|
264
452
|
}
|
|
265
453
|
/** How long a workspace keeps working after a payment fails. */
|
|
266
454
|
export const GRACE_DAYS = 14;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/server/services/stripe.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/server/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAe,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5E,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAC1C,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,MAAM;IACpB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAA;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,OAAO,IAAI,MAAM,CAAC,GAAG,EAAE;QACrB,6FAA6F;QAC7F,2FAA2F;QAC3F,yEAAyE;QACzE,UAAU,EAAE,mBAAmB;QAC/B,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,EAAE;QACrD,iBAAiB,EAAE,CAAC;QACpB,GAAG,OAAO,EAAE;KACb,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,OAAO;IACd,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IAC7D,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAA;AACtG,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAE3E,SAAS,QAAQ;IACf,MAAM,CAAC,GAAG,MAAM,EAAE,CAAA;IAClB,IAAI,CAAC,CAAC;QACJ,MAAM,SAAS,CAAC,QAAQ,CACtB,kDAAkD,EAClD,+BAA+B,CAChC,CAAA;IACH,OAAO,CAAC,CAAA;AACV,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,CAA6B;IACrD,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB;;;;;;WAMG;QACH,KAAK,QAAQ;YACX,OAAO,WAAW,CAAA;QACpB,KAAK,UAAU,CAAC;QAChB,KAAK,oBAAoB;YACvB,OAAO,UAAU,CAAA;QACnB,+FAA+F;QAC/F,KAAK,YAAY,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,WAAW,CAAA;QACpB;YACE,OAAO,WAAW,CAAA;IACtB,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,WAAmB,EAAE,KAAc;IAC5E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACpD,IAAI,QAAQ,EAAE,gBAAgB;QAAE,OAAO,QAAQ,CAAC,gBAAgB,CAAA;IAChE,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;IACzB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAC7C,KAAK;QACL,4FAA4F;QAC5F,yEAAyE;QACzE,QAAQ,EAAE,EAAE,iBAAiB,EAAE,WAAW,EAAE;KAC7C,CAAC,CAAA;IACF,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAA;IACzE,OAAO,QAAQ,CAAC,EAAE,CAAA;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,WAAW,GAA4C,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;AAExG;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,WAAmB,EAAE,KAAc;IACrF,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAA;IACnE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,CAAC,IAAI,CACb,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,EACjC,yEAAyE,CAC1E,CAAA;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACnE,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,UAAkB,EAAE,OAAe;IAC5E,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE,EAAE,CAAA;IACxG,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,OAAO,EAAE,CAAA;AACpE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAc,EACd,KAOC;IAED,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;IACzB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC3G,IAAI,CAAC,IAAI;QAAE,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,CAAC,IAAI,CAAC,aAAa;QACrB,MAAM,SAAS,CAAC,QAAQ,CAAC,wCAAwC,EAAE,uBAAuB,CAAC,CAAA;IAE7F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/F,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAA;IAEpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAC1D,IAAI,QAAQ,EAAE,oBAAoB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACnG,MAAM,IAAI,GAAG,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAClC,IAAI,MAAM,IAAI,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACrD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;gBAC3D,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC;gBAC7D,6EAA6E;gBAC7E,kBAAkB,EAAE,mBAAmB;gBACvC,2FAA2F;gBAC3F,oBAAoB,EAAE,KAAK;gBAC3B,sFAAsF;gBACtF,gFAAgF;gBAChF,QAAQ,EAAE,EAAE,iBAAiB,EAAE,KAAK,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE;aAC1E,CAAC,CAAA;YACF,6FAA6F;YAC7F,6FAA6F;YAC7F,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YACxC,OAAO,EAAE,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1E,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpD,IAAI,EAAE,cAAc;QACpB,QAAQ;QACR,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC;QACrD,iBAAiB,EAAE;YACjB,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,QAAQ,EAAE,EAAE,iBAAiB,EAAE,KAAK,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE;SAC1E;QACD,+FAA+F;QAC/F,yBAAyB,EAAE,QAAQ;QACnC,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC;QACnD,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC;KACxD,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,CAAC,GAAG;QAAE,MAAM,SAAS,CAAC,QAAQ,CAAC,sCAAsC,EAAE,uBAAuB,CAAC,CAAA;IAC3G,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,MAAc,EACd,KAAiD;IAEjD,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;IACzB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IACrD,IAAI,CAAC,GAAG,EAAE,gBAAgB;QACxB,MAAM,SAAS,CAAC,QAAQ,CAAC,2CAA2C,EAAE,4BAA4B,CAAC,CAAA;IACrG,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;QACzD,QAAQ,EAAE,GAAG,CAAC,gBAAgB;QAC9B,UAAU,EAAE,KAAK,CAAC,SAAS;KAC5B,CAAC,CAAA;IACF,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAA;AAC7B,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,WAAmB,EAAE,KAAa;IAChF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAA;IACvB,IAAI,CAAC,MAAM;QAAE,OAAM;IACnB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC/C,IAAI,CAAC,GAAG,EAAE,oBAAoB;QAAE,OAAM;IACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;QAAE,OAAM;IAC5C,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,oBAAoB,EAAE;QAC1D,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACzC,2FAA2F;QAC3F,kBAAkB,EAAE,mBAAmB;KACxC,CAAC,CAAA;IACF,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAA;AACnE,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,KAAK,CAAC,MAAc,EAAE,EAAU,EAAE,IAAY;IAC3D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;SAClC,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;SACpB,mBAAmB,EAAE;SACrB,SAAS,CAAC,EAAE,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAA;IACtC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;AACxB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,KAAK,UAAU,OAAO,CAAC,MAAc,EAAE,EAAU;IAC/C,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;SACrB,MAAM,CAAC,aAAa,CAAC;SACrB,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;SAC/B,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CACtB,MAAM,CAAC,GAAG,CAAC,KAAK,CACd,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAC/B,wFAAwF,CACzF,CACF,CAAA;AACL,CAAC;AAED,2FAA2F;AAC3F,SAAS,WAAW,CAAC,CAAwC;IAC3D,OAAO,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAA;AAC9C,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,SAAS,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE;IACxC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3B,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,CAAA;IACjD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,CAAsB;IACrE,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,qDAAqD,CAAC,CAAA;QAC9F,OAAM;IACR,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,IAAI,IAAI,CAAA;IAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC5B,MAAM,SAAS,GAAG,IAAI,EAAE,kBAAkB,IAAI,IAAI,CAAA;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;IAElC,IAAI,WAAW,GAAgB,IAAI,CAAA;IACnC,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;aACnC,MAAM,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC;aAClD,IAAI,CAAC,aAAa,CAAC;aACnB,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;aACjD,KAAK,CAAC,CAAC,CAAC,CAAA;QACX,WAAW,GAAG,GAAG,EAAE,WAAW,IAAI,SAAS,EAAE,CAAA;IAC/C,CAAC;IAED,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE;QACrC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,MAAM;QACN,cAAc,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC;QACnC,oBAAoB,EAAE,CAAC,CAAC,EAAE;QAC1B,gBAAgB,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE;QAC7E,iBAAiB,EAAE,CAAC,CAAC,oBAAoB;QACzC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC9D,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC/D,WAAW;KACZ,CAAC,CAAA;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,GAAmB;IAC7D,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAA;IACnF,IAAI,CAAC,QAAQ;QAAE,OAAM;IACrB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;SACnC,MAAM,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC;SAClD,IAAI,CAAC,aAAa,CAAC;SACnB,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;SACnD,KAAK,CAAC,CAAC,CAAC,CAAA;IACX,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAA;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IACjC,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QAC5D,MAAM,EAAE;aACL,MAAM,CAAC,QAAQ,CAAC;aAChB,MAAM,CAAC;YACN,WAAW;YACX,eAAe,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;YAC/B,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI;YAC1B,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,OAAO;YAC7B,UAAU,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;YAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,KAAK;YAC/B,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;YAC5E,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;YACtE,SAAS,EAAE,GAAG,CAAC,kBAAkB,IAAI,IAAI;YACzC,MAAM,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI;SAChC,CAAC;aACD,kBAAkB,CAAC;YAClB,MAAM,EAAE,QAAQ,CAAC,eAAe;YAChC,GAAG,EAAE;gBACH,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,OAAO;gBAC7B,UAAU,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;gBAC1B,SAAS,EAAE,GAAG,CAAC,kBAAkB,IAAI,IAAI;gBACzC,MAAM,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI;aAChC;SACF,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,GAAoB,EACpB,SAAiB;IAEjB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;IACzB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAA;IAChD,IAAI,CAAC,MAAM;QACT,MAAM,SAAS,CAAC,QAAQ,CAAC,wCAAwC,EAAE,kCAAkC,CAAC,CAAA;IAExG,IAAI,KAAmB,CAAA;IACvB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,SAAS,CAAC,UAAU,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACrF,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,kCAAkC,CAAC,CAAA;QAC1F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IAC7C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,+FAA+F;QAC/F,sFAAsF;QACtF,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;QAC/B,MAAM,GAAG,CAAA;IACX,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;AAC5C,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,MAAc,EAAE,MAAc,EAAE,KAAmB;IACtE,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,+BAA+B,CAAC;QACrC,KAAK,+BAA+B,CAAC;QACrC,KAAK,+BAA+B;YAClC,MAAM,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAClD,MAAK;QACP,KAAK,4BAA4B,CAAC,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAA;YACjC,MAAM,KAAK,GAAG,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAA;YACxG,IAAI,CAAC,KAAK;gBAAE,MAAK;YACjB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACzD,MAAM,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YACvC;;;;;;;;;;eAUG;YACH,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;YACvC,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC;oBACH,MAAM,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAA;gBAChF,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,GAAG,CAAC,IAAI,CACb,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,EACjC,qFAAqF,CACtF,CAAA;gBACH,CAAC;YACH,CAAC;YACD,MAAK;QACP,CAAC;QACD,KAAK,cAAc;YACjB,MAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC7C,MAAK;QACP,KAAK,wBAAwB,CAAC,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAA;YAC7B,MAAM,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YAC/B,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAA;YACnF,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;qBACnC,MAAM,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC;qBAC1F,IAAI,CAAC,aAAa,CAAC;qBACnB,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;qBACnD,KAAK,CAAC,CAAC,CAAC,CAAA;gBACX,IAAI,GAAG,EAAE,CAAC;oBACR,0FAA0F;oBAC1F,qFAAqF;oBACrF,sFAAsF;oBACtF,2CAA2C;oBAC3C,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE;wBACzC,MAAM,EAAE,UAAU;wBAClB,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS,EAAE;qBAC5C,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,MAAK;QACP,CAAC;QACD;YACE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,iCAAiC,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,CAAA"}
|