@voltro/plugin-billing 0.33.0 → 0.34.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/CHANGELOG.md +1801 -0
- package/dist/index.d.ts +481 -14
- package/dist/index.js +876 -446
- package/dist/rpc.d.ts +18 -0
- package/dist/rpc.js +33 -11
- package/dist/types.d.ts +145 -0
- package/dist/types.js +8 -3
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,114 +1,329 @@
|
|
|
1
|
-
import { BillingError as e, BillingService as t, DEFAULT_PLAN as n, EntitlementExceeded as r } from "./types.js";
|
|
2
|
-
import { billingRpcClientImports as
|
|
3
|
-
import { Effect as
|
|
1
|
+
import { BillingError as e, BillingService as t, DEFAULT_PLAN as n, EntitlementExceeded as r, SubscriptionLocked as i } from "./types.js";
|
|
2
|
+
import { billingRpcClientImports as a, changePlanDescriptor as o, changeSeatsDescriptor as s, entitlementStatusDescriptor as c, invoicesDescriptor as l, portalUrlDescriptor as u, previewChangeDescriptor as d, reportUsageDescriptor as f, startCheckoutDescriptor as ee, subscriptionDescriptor as te } from "./rpc.js";
|
|
3
|
+
import { Effect as p, Layer as m, Schedule as h, Schema as ne } from "effect";
|
|
4
4
|
import { pluginEnv as g } from "@voltro/env";
|
|
5
|
-
import { composeRpcInterceptors as
|
|
6
|
-
import { and as _, eq as v, gte as
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { composeRpcInterceptors as re, definePlugin as ie, pluginInstanceName as ae } from "@voltro/protocol";
|
|
6
|
+
import { and as _, eq as v, gte as y, id as b, integer as x, registerRetention as S, retentionTtlMsFromEnv as C, table as w, text as T, timestamp as E } from "@voltro/database";
|
|
7
|
+
import { createLogger as D } from "@voltro/logger";
|
|
8
|
+
import O from "stripe";
|
|
9
|
+
import { defineIncomingWebhook as oe, mountIncomingWebhook as k } from "@voltro/plugin-webhooks";
|
|
10
|
+
import { stripeProvider as A } from "@voltro/plugin-webhooks/providers";
|
|
11
11
|
//#region src/entitlement.ts
|
|
12
|
-
var
|
|
12
|
+
var j = (e, t, n, r) => ({
|
|
13
13
|
allowed: !Number.isFinite(t) || r <= 0 || n + r <= t,
|
|
14
14
|
entitlement: e,
|
|
15
15
|
limit: t,
|
|
16
16
|
used: n,
|
|
17
17
|
cost: r
|
|
18
|
-
}),
|
|
19
|
-
let a =
|
|
20
|
-
if (a === null) return i <= 0 ? void 0 : yield*
|
|
18
|
+
}), M = (e) => e.tenantId, se = (e, n, i) => p.gen(function* () {
|
|
19
|
+
let a = M(e.request.subject);
|
|
20
|
+
if (a === null) return i <= 0 ? void 0 : yield* p.fail(new r({
|
|
21
21
|
entitlement: n,
|
|
22
22
|
limit: 0,
|
|
23
23
|
used: 0,
|
|
24
24
|
cost: i
|
|
25
25
|
}));
|
|
26
26
|
yield* (yield* t).consumeEntitlement(a, n, i);
|
|
27
|
-
}), N = (e
|
|
27
|
+
}), N = (e) => p.gen(function* () {
|
|
28
|
+
let n = M(e.request.subject);
|
|
29
|
+
if (n === null) return;
|
|
30
|
+
let r = yield* (yield* t).entitlementStatus(n);
|
|
31
|
+
if (!r.entitled) return yield* p.fail(new i({
|
|
32
|
+
tenantId: n,
|
|
33
|
+
status: r.status,
|
|
34
|
+
lockedSince: (r.lockedSince ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
35
|
+
lockout: r.lockout
|
|
36
|
+
}));
|
|
37
|
+
}), ce = (e, t) => (n, i) => {
|
|
28
38
|
let a = e[i.tag];
|
|
29
39
|
if (a === void 0) return n;
|
|
30
40
|
let o = a.cost ?? 1, s = i.subject.tenantId;
|
|
31
|
-
return s === null ? o <= 0 ? n :
|
|
41
|
+
return s === null ? o <= 0 ? n : p.fail(new r({
|
|
32
42
|
entitlement: a.entitlement,
|
|
33
43
|
limit: 0,
|
|
34
44
|
used: 0,
|
|
35
45
|
cost: o
|
|
36
|
-
})) :
|
|
37
|
-
}, P = "_voltro_billing_customers", F = "_voltro_billing_subscriptions", I = "_voltro_billing_invoices", L = "_voltro_billing_usage",
|
|
38
|
-
id:
|
|
39
|
-
tenantId:
|
|
40
|
-
provider:
|
|
41
|
-
providerCustomerId:
|
|
42
|
-
createdAt:
|
|
43
|
-
updatedAt:
|
|
44
|
-
}).index("byBillingCustomerTenant", ["tenantId"]).index("byBillingProviderCustomer", ["providerCustomerId"]),
|
|
45
|
-
id:
|
|
46
|
-
tenantId:
|
|
47
|
-
provider:
|
|
48
|
-
providerSubscriptionId:
|
|
49
|
-
plan:
|
|
50
|
-
status:
|
|
51
|
-
quantity:
|
|
52
|
-
currentPeriodStart:
|
|
53
|
-
currentPeriodEnd:
|
|
54
|
-
cancelAt:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
46
|
+
})) : p.flatMap(t.consumeEntitlement(s, a.entitlement, o), () => n);
|
|
47
|
+
}, P = "_voltro_billing_customers", F = "_voltro_billing_subscriptions", I = "_voltro_billing_invoices", L = "_voltro_billing_usage", le = w(P, {
|
|
48
|
+
id: b({ prefix: "bcust" }),
|
|
49
|
+
tenantId: T(),
|
|
50
|
+
provider: T(),
|
|
51
|
+
providerCustomerId: T(),
|
|
52
|
+
createdAt: E(),
|
|
53
|
+
updatedAt: E()
|
|
54
|
+
}).index("byBillingCustomerTenant", ["tenantId"]).index("byBillingProviderCustomer", ["providerCustomerId"]), ue = w(F, {
|
|
55
|
+
id: b({ prefix: "bsub" }),
|
|
56
|
+
tenantId: T(),
|
|
57
|
+
provider: T(),
|
|
58
|
+
providerSubscriptionId: T(),
|
|
59
|
+
plan: T(),
|
|
60
|
+
status: T(),
|
|
61
|
+
quantity: x().default(1),
|
|
62
|
+
currentPeriodStart: E().nullable(),
|
|
63
|
+
currentPeriodEnd: E().nullable(),
|
|
64
|
+
cancelAt: E().nullable(),
|
|
65
|
+
pastDueSince: E().nullable(),
|
|
66
|
+
statusEventAt: E().nullable(),
|
|
67
|
+
createdAt: E(),
|
|
68
|
+
updatedAt: E()
|
|
69
|
+
}).index("byBillingSubscriptionTenant", ["tenantId"]).index("byBillingProviderSubscription", ["providerSubscriptionId"]).index("byBillingSubscriptionStatus", ["status"]), de = w(I, {
|
|
70
|
+
id: b({ prefix: "binv" }),
|
|
71
|
+
tenantId: T(),
|
|
72
|
+
provider: T(),
|
|
73
|
+
providerInvoiceId: T(),
|
|
74
|
+
amountMinor: x(),
|
|
75
|
+
currency: T(),
|
|
76
|
+
status: T(),
|
|
77
|
+
statusEventAt: E().nullable(),
|
|
78
|
+
createdAt: E()
|
|
79
|
+
}).index("byBillingInvoiceTenant", ["tenantId"]).index("byBillingProviderInvoice", ["providerInvoiceId"]), fe = w(L, {
|
|
80
|
+
id: b({ prefix: "buse" }),
|
|
81
|
+
tenantId: T().maxLength(191),
|
|
82
|
+
entitlementKey: T().maxLength(191),
|
|
83
|
+
period: T().maxLength(16),
|
|
84
|
+
used: x().default(0),
|
|
85
|
+
reportedToProvider: x().default(0),
|
|
86
|
+
updatedAt: E()
|
|
74
87
|
}).unique("byBillingUsageWindow", [
|
|
75
88
|
"tenantId",
|
|
76
89
|
"entitlementKey",
|
|
77
90
|
"period"
|
|
78
|
-
]),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
], R =
|
|
91
|
+
]), pe = () => [
|
|
92
|
+
le,
|
|
93
|
+
ue,
|
|
94
|
+
de,
|
|
95
|
+
fe
|
|
96
|
+
], R = "_voltro_billing_dunning_notices", me = w(R, {
|
|
97
|
+
id: b({ prefix: "bdun" }),
|
|
98
|
+
tenantId: T().maxLength(191),
|
|
99
|
+
episode: T().maxLength(32),
|
|
100
|
+
stepId: T().maxLength(64),
|
|
101
|
+
sentAt: E()
|
|
102
|
+
}).unique("byBillingDunningNotice", [
|
|
103
|
+
"tenantId",
|
|
104
|
+
"episode",
|
|
105
|
+
"stepId"
|
|
106
|
+
]), he = "locked", ge = [
|
|
107
|
+
{
|
|
108
|
+
id: "payment-failed",
|
|
109
|
+
afterHours: 0
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "reminder",
|
|
113
|
+
afterHours: 72
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "final-warning",
|
|
117
|
+
afterHours: 144
|
|
118
|
+
}
|
|
119
|
+
], _e = 168, ve = "hard", ye = (e, t, n) => {
|
|
120
|
+
if (e === void 0 || e.trim() === "") return t;
|
|
121
|
+
let r = Number(e);
|
|
122
|
+
if (!Number.isFinite(r) || r < 0) throw Error(`billingPlugin: ${n} must be a non-negative number, got '${e}'`);
|
|
123
|
+
return r;
|
|
124
|
+
}, be = (e, t) => {
|
|
125
|
+
let n = t.VOLTRO_BILLING_DUNNING;
|
|
126
|
+
if (n !== void 0 && n !== "" && n !== "on" && n !== "off") throw Error(`billingPlugin: VOLTRO_BILLING_DUNNING must be 'on' or 'off', got '${n}'`);
|
|
127
|
+
let r = n === "off" ? !1 : n === "on" ? !0 : e?.enabled ?? !0, i = t.VOLTRO_BILLING_LOCKOUT;
|
|
128
|
+
if (i !== void 0 && i !== "" && i !== "hard" && i !== "soft") throw Error(`billingPlugin: VOLTRO_BILLING_LOCKOUT must be 'hard' or 'soft', got '${i}'`);
|
|
129
|
+
let a = i === "hard" || i === "soft" ? i : e?.lockout ?? "hard", o = ye(t.VOLTRO_BILLING_GRACE_HOURS, e?.graceHours ?? 168, "VOLTRO_BILLING_GRACE_HOURS"), s = e?.steps ?? ge, c = t.VOLTRO_BILLING_DUNNING_STEP_HOURS, l = s;
|
|
130
|
+
if (c !== void 0 && c.trim() !== "") {
|
|
131
|
+
let e = c.split(",").map((e) => e.trim());
|
|
132
|
+
if (e.length !== s.length) throw Error(`billingPlugin: VOLTRO_BILLING_DUNNING_STEP_HOURS lists ${e.length} value(s) but the sequence declares ${s.length} step(s) (${s.map((e) => e.id).join(", ")})`);
|
|
133
|
+
l = s.map((t, n) => ({
|
|
134
|
+
id: t.id,
|
|
135
|
+
afterHours: ye(e[n], t.afterHours, "VOLTRO_BILLING_DUNNING_STEP_HOURS")
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
let u = /* @__PURE__ */ new Set();
|
|
139
|
+
for (let e of l) {
|
|
140
|
+
if (e.id === "locked") throw Error(`billingPlugin: '${he}' is the reserved lockout step id`);
|
|
141
|
+
if (u.has(e.id)) throw Error(`billingPlugin: duplicate dunning step id '${e.id}'`);
|
|
142
|
+
u.add(e.id);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
enabled: r,
|
|
146
|
+
graceHours: o,
|
|
147
|
+
lockout: a,
|
|
148
|
+
steps: [...l].sort((e, t) => e.afterHours - t.afterHours),
|
|
149
|
+
...e?.notify === void 0 ? {} : { notify: e.notify },
|
|
150
|
+
...e?.resolveRecipient === void 0 ? {} : { resolveRecipient: e.resolveRecipient },
|
|
151
|
+
...e?.portalReturnUrl === void 0 ? {} : { portalReturnUrl: e.portalReturnUrl }
|
|
152
|
+
};
|
|
153
|
+
}, xe = 36e5, Se = (e) => String(e.getTime()), Ce = (e, t) => new Date(e.getTime() + t * xe), we = (e, t, n) => {
|
|
154
|
+
let r = (n.getTime() - t.getTime()) / xe;
|
|
155
|
+
return e.filter((e) => r >= e.afterHours);
|
|
156
|
+
}, z = (e, t, r, i) => {
|
|
157
|
+
let a = {
|
|
158
|
+
tenantId: e?.tenantId ?? i ?? "",
|
|
159
|
+
lockout: t.lockout
|
|
160
|
+
};
|
|
161
|
+
if (e === null) return {
|
|
162
|
+
...a,
|
|
163
|
+
plan: n,
|
|
164
|
+
billedPlan: n,
|
|
165
|
+
status: "none",
|
|
166
|
+
entitled: !0,
|
|
167
|
+
inGrace: !1,
|
|
168
|
+
graceEndsAt: null,
|
|
169
|
+
lockedSince: null
|
|
170
|
+
};
|
|
171
|
+
if (e.status === "active" || e.status === "trialing") return {
|
|
172
|
+
...a,
|
|
173
|
+
plan: e.plan,
|
|
174
|
+
billedPlan: e.plan,
|
|
175
|
+
status: e.status,
|
|
176
|
+
entitled: !0,
|
|
177
|
+
inGrace: !1,
|
|
178
|
+
graceEndsAt: null,
|
|
179
|
+
lockedSince: null
|
|
180
|
+
};
|
|
181
|
+
if (e.status === "pastDue") {
|
|
182
|
+
if (!t.enabled) return {
|
|
183
|
+
...a,
|
|
184
|
+
plan: n,
|
|
185
|
+
billedPlan: e.plan,
|
|
186
|
+
status: e.status,
|
|
187
|
+
entitled: !0,
|
|
188
|
+
inGrace: !1,
|
|
189
|
+
graceEndsAt: null,
|
|
190
|
+
lockedSince: null
|
|
191
|
+
};
|
|
192
|
+
if (e.pastDueSince === null) return {
|
|
193
|
+
...a,
|
|
194
|
+
plan: e.plan,
|
|
195
|
+
billedPlan: e.plan,
|
|
196
|
+
status: e.status,
|
|
197
|
+
entitled: !0,
|
|
198
|
+
inGrace: !0,
|
|
199
|
+
graceEndsAt: null,
|
|
200
|
+
lockedSince: null
|
|
201
|
+
};
|
|
202
|
+
let i = Ce(e.pastDueSince, t.graceHours), o = r.getTime() >= i.getTime();
|
|
203
|
+
return {
|
|
204
|
+
...a,
|
|
205
|
+
plan: o && t.lockout === "hard" ? n : e.plan,
|
|
206
|
+
billedPlan: e.plan,
|
|
207
|
+
status: e.status,
|
|
208
|
+
entitled: !o,
|
|
209
|
+
inGrace: !o,
|
|
210
|
+
graceEndsAt: i,
|
|
211
|
+
lockedSince: o ? i : null
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
...a,
|
|
216
|
+
plan: n,
|
|
217
|
+
billedPlan: e.plan,
|
|
218
|
+
status: e.status,
|
|
219
|
+
entitled: !0,
|
|
220
|
+
inGrace: !1,
|
|
221
|
+
graceEndsAt: null,
|
|
222
|
+
lockedSince: null
|
|
223
|
+
};
|
|
224
|
+
}, Te = (e) => e.toISOString().slice(0, 10), Ee = (e) => {
|
|
225
|
+
let t = e.portalUrl, n = t === null ? "<p>Please update your payment method in the billing settings.</p>" : `<p><a href="${t}">Update your payment method</a></p>`, r = t === null ? "Please update your payment method in the billing settings." : `Update your payment method: ${t}`;
|
|
226
|
+
if (e.locked) return {
|
|
227
|
+
subject: "Your subscription has been suspended",
|
|
228
|
+
html: `<p>We were unable to collect payment, and your subscription is now suspended.</p>${n}`,
|
|
229
|
+
text: `We were unable to collect payment, and your subscription is now suspended.\n\n${r}`
|
|
230
|
+
};
|
|
231
|
+
let i = Te(e.graceEndsAt), a = e.stepId === "payment-failed" ? "Your last payment could not be collected." : "Your payment is still outstanding.";
|
|
232
|
+
return {
|
|
233
|
+
subject: "Action needed: your payment could not be collected",
|
|
234
|
+
html: `<p>${a} Your subscription stays active until ${i}.</p>${n}`,
|
|
235
|
+
text: `${a} Your subscription stays active until ${i}.\n\n${r}`
|
|
236
|
+
};
|
|
237
|
+
}, De = (e) => (t) => t.to === null ? p.void : e.send({
|
|
238
|
+
to: t.to,
|
|
239
|
+
subject: t.subject,
|
|
240
|
+
html: t.html,
|
|
241
|
+
text: t.text
|
|
242
|
+
}).pipe(p.asVoid), Oe = (e, t, n) => `${e}\u0000${t}\u0000${n}`, ke = () => {
|
|
243
|
+
let e = /* @__PURE__ */ new Map();
|
|
244
|
+
return {
|
|
245
|
+
claim: (t, n, r, i) => p.sync(() => {
|
|
246
|
+
let a = Oe(t, n, r);
|
|
247
|
+
return e.has(a) ? !1 : (e.set(a, {
|
|
248
|
+
tenantId: t,
|
|
249
|
+
episode: n,
|
|
250
|
+
stepId: r,
|
|
251
|
+
sentAt: i
|
|
252
|
+
}), !0);
|
|
253
|
+
}),
|
|
254
|
+
sentSteps: (t, n) => p.sync(() => [...e.values()].filter((e) => e.tenantId === t && e.episode === n))
|
|
255
|
+
};
|
|
256
|
+
}, Ae = () => `bdun_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`, je = (t) => ({
|
|
257
|
+
claim: (n, r, i, a) => p.tryPromise({
|
|
258
|
+
try: async () => {
|
|
259
|
+
let e = Ae(), o = await t.insertIgnore(R, {
|
|
260
|
+
id: e,
|
|
261
|
+
tenantId: n,
|
|
262
|
+
episode: r,
|
|
263
|
+
stepId: i,
|
|
264
|
+
sentAt: a
|
|
265
|
+
}, { conflictColumns: [
|
|
266
|
+
"tenantId",
|
|
267
|
+
"episode",
|
|
268
|
+
"stepId"
|
|
269
|
+
] });
|
|
270
|
+
return String(o.id) === e;
|
|
271
|
+
},
|
|
272
|
+
catch: (t) => new e({
|
|
273
|
+
source: "dunningNoticeStore",
|
|
274
|
+
message: String(t?.message ?? t),
|
|
275
|
+
transient: !0
|
|
276
|
+
})
|
|
277
|
+
}),
|
|
278
|
+
sentSteps: (n, r) => p.tryPromise({
|
|
279
|
+
try: async () => (await t.query({
|
|
280
|
+
table: R,
|
|
281
|
+
predicate: _(v("tenantId", n), v("episode", r)),
|
|
282
|
+
order: [],
|
|
283
|
+
take: void 0,
|
|
284
|
+
skip: void 0,
|
|
285
|
+
projection: void 0
|
|
286
|
+
})).map((e) => ({
|
|
287
|
+
tenantId: String(e.tenantId),
|
|
288
|
+
episode: String(e.episode),
|
|
289
|
+
stepId: String(e.stepId),
|
|
290
|
+
sentAt: e.sentAt == null ? null : new Date(e.sentAt)
|
|
291
|
+
})),
|
|
292
|
+
catch: (t) => new e({
|
|
293
|
+
source: "dunningNoticeStore",
|
|
294
|
+
message: String(t?.message ?? t),
|
|
295
|
+
transient: !0
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
}), B = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`, V = (t, n) => p.tryPromise({
|
|
84
299
|
try: t,
|
|
85
300
|
catch: (t) => new e({
|
|
86
301
|
source: n,
|
|
87
302
|
message: String(t?.message ?? t),
|
|
88
303
|
transient: !0
|
|
89
304
|
})
|
|
90
|
-
}),
|
|
305
|
+
}), H = (e) => ({
|
|
91
306
|
table: e.table,
|
|
92
307
|
predicate: e.predicate,
|
|
93
308
|
order: e.order ?? [],
|
|
94
309
|
take: e.take,
|
|
95
310
|
skip: void 0,
|
|
96
311
|
projection: void 0
|
|
97
|
-
}),
|
|
312
|
+
}), Me = () => {
|
|
98
313
|
let e = /* @__PURE__ */ new Map();
|
|
99
314
|
return {
|
|
100
|
-
upsert: (t) =>
|
|
315
|
+
upsert: (t) => p.sync(() => {
|
|
101
316
|
e.set(t.tenantId, t);
|
|
102
317
|
}),
|
|
103
|
-
getByTenant: (t) =>
|
|
318
|
+
getByTenant: (t) => p.sync(() => e.get(t) ?? null)
|
|
104
319
|
};
|
|
105
|
-
},
|
|
320
|
+
}, Ne = (e) => ({
|
|
106
321
|
tenantId: String(e.tenantId),
|
|
107
322
|
provider: String(e.provider),
|
|
108
323
|
providerCustomerId: String(e.providerCustomerId)
|
|
109
|
-
}),
|
|
110
|
-
upsert: (t) =>
|
|
111
|
-
let n = await e.query(
|
|
324
|
+
}), Pe = (e) => ({
|
|
325
|
+
upsert: (t) => V(async () => {
|
|
326
|
+
let n = await e.query(H({
|
|
112
327
|
table: P,
|
|
113
328
|
predicate: v("tenantId", t.tenantId),
|
|
114
329
|
take: 1
|
|
@@ -118,48 +333,53 @@ var A = (e, t, n, r) => ({
|
|
|
118
333
|
providerCustomerId: t.providerCustomerId,
|
|
119
334
|
updatedAt: r
|
|
120
335
|
}) : await e.insert(P, {
|
|
121
|
-
id:
|
|
336
|
+
id: B("bcust"),
|
|
122
337
|
...t,
|
|
123
338
|
createdAt: r,
|
|
124
339
|
updatedAt: r
|
|
125
340
|
});
|
|
126
341
|
}, "customerStore"),
|
|
127
|
-
getByTenant: (t) =>
|
|
128
|
-
let n = await e.query(
|
|
342
|
+
getByTenant: (t) => V(async () => {
|
|
343
|
+
let n = await e.query(H({
|
|
129
344
|
table: P,
|
|
130
345
|
predicate: v("tenantId", t),
|
|
131
346
|
take: 1
|
|
132
347
|
}));
|
|
133
|
-
return n[0] ?
|
|
348
|
+
return n[0] ? Ne(n[0]) : null;
|
|
134
349
|
}, "customerStore")
|
|
135
|
-
}),
|
|
350
|
+
}), U = (e, t) => e === null ? !1 : t === null || t.getTime() < e.getTime(), Fe = () => {
|
|
136
351
|
let e = /* @__PURE__ */ new Map();
|
|
137
352
|
return {
|
|
138
|
-
upsert: (t) =>
|
|
139
|
-
e.
|
|
353
|
+
upsert: (t) => p.sync(() => {
|
|
354
|
+
let n = e.get(t.tenantId);
|
|
355
|
+
n && U(n.statusEventAt, t.statusEventAt) || e.set(t.tenantId, {
|
|
356
|
+
...t,
|
|
357
|
+
pastDueSince: n ? n.pastDueSince : t.pastDueSince
|
|
358
|
+
});
|
|
140
359
|
}),
|
|
141
|
-
getByTenant: (t) =>
|
|
142
|
-
|
|
360
|
+
getByTenant: (t) => p.sync(() => e.get(t) ?? null),
|
|
361
|
+
listByStatus: (t) => p.sync(() => [...e.values()].filter((e) => e.status === t)),
|
|
362
|
+
markCanceled: (t) => p.sync(() => {
|
|
143
363
|
for (let [n, r] of e) r.providerSubscriptionId === t && e.set(n, {
|
|
144
364
|
...r,
|
|
145
365
|
status: "canceled"
|
|
146
366
|
});
|
|
147
367
|
}),
|
|
148
|
-
patchByTenant: (t, n) =>
|
|
368
|
+
patchByTenant: (t, n) => p.sync(() => {
|
|
149
369
|
let r = e.get(t);
|
|
150
370
|
r && e.set(t, {
|
|
151
371
|
...r,
|
|
152
372
|
...n
|
|
153
373
|
});
|
|
154
374
|
}),
|
|
155
|
-
setStatus: (t, n) =>
|
|
375
|
+
setStatus: (t, n) => p.sync(() => {
|
|
156
376
|
for (let [r, i] of e) i.providerSubscriptionId === t && e.set(r, {
|
|
157
377
|
...i,
|
|
158
378
|
status: n
|
|
159
379
|
});
|
|
160
380
|
})
|
|
161
381
|
};
|
|
162
|
-
},
|
|
382
|
+
}, Ie = (e) => ({
|
|
163
383
|
tenantId: String(e.tenantId),
|
|
164
384
|
provider: String(e.provider),
|
|
165
385
|
providerSubscriptionId: String(e.providerSubscriptionId),
|
|
@@ -168,26 +388,32 @@ var A = (e, t, n, r) => ({
|
|
|
168
388
|
quantity: e.quantity == null ? 1 : Number(e.quantity),
|
|
169
389
|
currentPeriodStart: e.currentPeriodStart == null ? null : new Date(e.currentPeriodStart),
|
|
170
390
|
currentPeriodEnd: e.currentPeriodEnd == null ? null : new Date(e.currentPeriodEnd),
|
|
171
|
-
cancelAt: e.cancelAt == null ? null : new Date(e.cancelAt)
|
|
172
|
-
|
|
391
|
+
cancelAt: e.cancelAt == null ? null : new Date(e.cancelAt),
|
|
392
|
+
pastDueSince: e.pastDueSince == null ? null : new Date(e.pastDueSince),
|
|
393
|
+
statusEventAt: e.statusEventAt == null ? null : new Date(e.statusEventAt)
|
|
394
|
+
}), Le = async (e, t) => (await e.query(H({
|
|
173
395
|
table: "_voltro_billing_subscriptions",
|
|
174
396
|
predicate: v("providerSubscriptionId", t),
|
|
175
397
|
take: 1
|
|
176
|
-
})))[0] ?? null,
|
|
177
|
-
upsert: (t) =>
|
|
178
|
-
let n = await
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
398
|
+
})))[0] ?? null, Re = (e) => ({
|
|
399
|
+
upsert: (t) => V(async () => {
|
|
400
|
+
let n = await Le(e, t.providerSubscriptionId), r = /* @__PURE__ */ new Date();
|
|
401
|
+
if (n) {
|
|
402
|
+
if (U(n.statusEventAt == null ? null : new Date(n.statusEventAt), t.statusEventAt)) return;
|
|
403
|
+
await e.update(F, String(n.id), {
|
|
404
|
+
tenantId: t.tenantId,
|
|
405
|
+
provider: t.provider,
|
|
406
|
+
plan: t.plan,
|
|
407
|
+
status: t.status,
|
|
408
|
+
quantity: t.quantity,
|
|
409
|
+
currentPeriodStart: t.currentPeriodStart,
|
|
410
|
+
currentPeriodEnd: t.currentPeriodEnd,
|
|
411
|
+
cancelAt: t.cancelAt,
|
|
412
|
+
statusEventAt: t.statusEventAt,
|
|
413
|
+
updatedAt: r
|
|
414
|
+
});
|
|
415
|
+
} else await e.insert(F, {
|
|
416
|
+
id: B("bsub"),
|
|
191
417
|
tenantId: t.tenantId,
|
|
192
418
|
provider: t.provider,
|
|
193
419
|
providerSubscriptionId: t.providerSubscriptionId,
|
|
@@ -197,12 +423,14 @@ var A = (e, t, n, r) => ({
|
|
|
197
423
|
currentPeriodStart: t.currentPeriodStart,
|
|
198
424
|
currentPeriodEnd: t.currentPeriodEnd,
|
|
199
425
|
cancelAt: t.cancelAt,
|
|
426
|
+
pastDueSince: null,
|
|
427
|
+
statusEventAt: t.statusEventAt,
|
|
200
428
|
createdAt: r,
|
|
201
429
|
updatedAt: r
|
|
202
430
|
});
|
|
203
431
|
}, "subscriptionStore"),
|
|
204
|
-
getByTenant: (t) =>
|
|
205
|
-
let n = await e.query(
|
|
432
|
+
getByTenant: (t) => V(async () => {
|
|
433
|
+
let n = await e.query(H({
|
|
206
434
|
table: F,
|
|
207
435
|
predicate: v("tenantId", t),
|
|
208
436
|
order: [{
|
|
@@ -211,17 +439,25 @@ var A = (e, t, n, r) => ({
|
|
|
211
439
|
}],
|
|
212
440
|
take: 1
|
|
213
441
|
}));
|
|
214
|
-
return n[0] ?
|
|
442
|
+
return n[0] ? Ie(n[0]) : null;
|
|
215
443
|
}, "subscriptionStore"),
|
|
216
|
-
|
|
217
|
-
|
|
444
|
+
listByStatus: (t) => V(async () => (await e.query(H({
|
|
445
|
+
table: F,
|
|
446
|
+
predicate: v("status", t),
|
|
447
|
+
order: [{
|
|
448
|
+
column: "updatedAt",
|
|
449
|
+
direction: "asc"
|
|
450
|
+
}]
|
|
451
|
+
}))).map(Ie), "subscriptionStore"),
|
|
452
|
+
markCanceled: (t) => V(async () => {
|
|
453
|
+
let n = await Le(e, t);
|
|
218
454
|
n && await e.update(F, String(n.id), {
|
|
219
455
|
status: "canceled",
|
|
220
456
|
updatedAt: /* @__PURE__ */ new Date()
|
|
221
457
|
});
|
|
222
458
|
}, "subscriptionStore"),
|
|
223
|
-
patchByTenant: (t, n) =>
|
|
224
|
-
let r = (await e.query(
|
|
459
|
+
patchByTenant: (t, n) => V(async () => {
|
|
460
|
+
let r = (await e.query(H({
|
|
225
461
|
table: F,
|
|
226
462
|
predicate: v("tenantId", t),
|
|
227
463
|
order: [{
|
|
@@ -234,62 +470,72 @@ var A = (e, t, n, r) => ({
|
|
|
234
470
|
...n.plan === void 0 ? {} : { plan: n.plan },
|
|
235
471
|
...n.quantity === void 0 ? {} : { quantity: n.quantity },
|
|
236
472
|
...n.status === void 0 ? {} : { status: n.status },
|
|
473
|
+
...n.currentPeriodStart === void 0 ? {} : { currentPeriodStart: n.currentPeriodStart },
|
|
474
|
+
...n.currentPeriodEnd === void 0 ? {} : { currentPeriodEnd: n.currentPeriodEnd },
|
|
475
|
+
...n.pastDueSince === void 0 ? {} : { pastDueSince: n.pastDueSince },
|
|
476
|
+
...n.statusEventAt === void 0 ? {} : { statusEventAt: n.statusEventAt },
|
|
237
477
|
updatedAt: /* @__PURE__ */ new Date()
|
|
238
478
|
});
|
|
239
479
|
}, "subscriptionStore"),
|
|
240
|
-
setStatus: (t, n) =>
|
|
241
|
-
let r = await
|
|
480
|
+
setStatus: (t, n) => V(async () => {
|
|
481
|
+
let r = await Le(e, t);
|
|
242
482
|
r && await e.update(F, String(r.id), {
|
|
243
483
|
status: n,
|
|
244
484
|
updatedAt: /* @__PURE__ */ new Date()
|
|
245
485
|
});
|
|
246
486
|
}, "subscriptionStore")
|
|
247
|
-
}),
|
|
487
|
+
}), ze = () => {
|
|
248
488
|
let e = /* @__PURE__ */ new Map();
|
|
249
489
|
return {
|
|
250
|
-
upsert: (t) =>
|
|
251
|
-
e.
|
|
490
|
+
upsert: (t) => p.sync(() => {
|
|
491
|
+
let n = e.get(t.providerInvoiceId);
|
|
492
|
+
n && U(n.statusEventAt, t.statusEventAt) || e.set(t.providerInvoiceId, t);
|
|
252
493
|
}),
|
|
253
|
-
listByTenant: (t) =>
|
|
494
|
+
listByTenant: (t) => p.sync(() => [...e.values()].filter((e) => e.tenantId === t))
|
|
254
495
|
};
|
|
255
|
-
},
|
|
496
|
+
}, Be = (e) => ({
|
|
256
497
|
tenantId: String(e.tenantId),
|
|
257
498
|
provider: String(e.provider),
|
|
258
499
|
providerInvoiceId: String(e.providerInvoiceId),
|
|
259
500
|
amountMinor: Number(e.amountMinor),
|
|
260
501
|
currency: String(e.currency),
|
|
261
|
-
status: String(e.status)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
502
|
+
status: String(e.status),
|
|
503
|
+
statusEventAt: e.statusEventAt == null ? null : new Date(e.statusEventAt)
|
|
504
|
+
}), Ve = (e) => ({
|
|
505
|
+
upsert: (t) => V(async () => {
|
|
506
|
+
let n = (await e.query(H({
|
|
265
507
|
table: I,
|
|
266
508
|
predicate: v("providerInvoiceId", t.providerInvoiceId),
|
|
267
509
|
take: 1
|
|
268
|
-
}));
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
510
|
+
})))[0];
|
|
511
|
+
if (n) {
|
|
512
|
+
if (U(n.statusEventAt == null ? null : new Date(n.statusEventAt), t.statusEventAt)) return;
|
|
513
|
+
await e.update(I, String(n.id), {
|
|
514
|
+
amountMinor: t.amountMinor,
|
|
515
|
+
currency: t.currency,
|
|
516
|
+
status: t.status,
|
|
517
|
+
statusEventAt: t.statusEventAt
|
|
518
|
+
});
|
|
519
|
+
} else await e.insert(I, {
|
|
520
|
+
id: B("binv"),
|
|
275
521
|
...t,
|
|
276
522
|
createdAt: /* @__PURE__ */ new Date()
|
|
277
523
|
});
|
|
278
524
|
}, "invoiceStore"),
|
|
279
|
-
listByTenant: (t) =>
|
|
525
|
+
listByTenant: (t) => V(async () => (await e.query(H({
|
|
280
526
|
table: I,
|
|
281
527
|
predicate: v("tenantId", t),
|
|
282
528
|
order: [{
|
|
283
529
|
column: "createdAt",
|
|
284
530
|
direction: "desc"
|
|
285
531
|
}]
|
|
286
|
-
}))).map(
|
|
287
|
-
}),
|
|
532
|
+
}))).map(Be), "invoiceStore")
|
|
533
|
+
}), W = (e, t, n) => `${e}\u0000${t}\u0000${n}`, He = () => {
|
|
288
534
|
let e = /* @__PURE__ */ new Map();
|
|
289
535
|
return {
|
|
290
|
-
used: (t, n, r) =>
|
|
291
|
-
consume: (t, n, r, i, a) =>
|
|
292
|
-
let o =
|
|
536
|
+
used: (t, n, r) => p.sync(() => e.get(W(t, n, r))?.used ?? 0),
|
|
537
|
+
consume: (t, n, r, i, a) => p.sync(() => {
|
|
538
|
+
let o = W(t, n, r), s = e.get(o), c = s?.used ?? 0, l = c + i <= a;
|
|
293
539
|
return l && e.set(o, {
|
|
294
540
|
tenantId: t,
|
|
295
541
|
entitlementKey: n,
|
|
@@ -304,8 +550,8 @@ var A = (e, t, n, r) => ({
|
|
|
304
550
|
cost: i
|
|
305
551
|
};
|
|
306
552
|
}),
|
|
307
|
-
increment: (t, n, r, i) =>
|
|
308
|
-
let a =
|
|
553
|
+
increment: (t, n, r, i) => p.sync(() => {
|
|
554
|
+
let a = W(t, n, r), o = e.get(a), s = (o?.used ?? 0) + i;
|
|
309
555
|
return e.set(a, {
|
|
310
556
|
tenantId: t,
|
|
311
557
|
entitlementKey: n,
|
|
@@ -314,34 +560,34 @@ var A = (e, t, n, r) => ({
|
|
|
314
560
|
reportedToProvider: o?.reportedToProvider ?? 0
|
|
315
561
|
}), s;
|
|
316
562
|
}),
|
|
317
|
-
pending: () =>
|
|
318
|
-
markReported: (t, n, r, i) =>
|
|
319
|
-
let a =
|
|
563
|
+
pending: () => p.sync(() => [...e.values()].filter((e) => e.used > e.reportedToProvider)),
|
|
564
|
+
markReported: (t, n, r, i) => p.sync(() => {
|
|
565
|
+
let a = W(t, n, r), o = e.get(a);
|
|
320
566
|
o && e.set(a, {
|
|
321
567
|
...o,
|
|
322
568
|
reportedToProvider: i
|
|
323
569
|
});
|
|
324
570
|
})
|
|
325
571
|
};
|
|
326
|
-
},
|
|
572
|
+
}, Ue = (e) => ({
|
|
327
573
|
tenantId: String(e.tenantId),
|
|
328
574
|
entitlementKey: String(e.entitlementKey),
|
|
329
575
|
period: String(e.period),
|
|
330
576
|
used: Number(e.used ?? 0),
|
|
331
577
|
reportedToProvider: Number(e.reportedToProvider ?? 0)
|
|
332
|
-
}),
|
|
578
|
+
}), G = async (e, t, n, r) => (await e.query(H({
|
|
333
579
|
table: "_voltro_billing_usage",
|
|
334
580
|
predicate: _(v("tenantId", t), v("entitlementKey", n), v("period", r)),
|
|
335
581
|
take: 1
|
|
336
|
-
})))[0] ?? null,
|
|
337
|
-
used: (t, n, r) =>
|
|
338
|
-
let i = await
|
|
582
|
+
})))[0] ?? null, We = (e) => ({
|
|
583
|
+
used: (t, n, r) => V(async () => {
|
|
584
|
+
let i = await G(e, t, n, r);
|
|
339
585
|
return i ? Number(i.used ?? 0) : 0;
|
|
340
586
|
}, "usageStore"),
|
|
341
|
-
consume: (t, n, r, i, a) =>
|
|
587
|
+
consume: (t, n, r, i, a) => V(async () => {
|
|
342
588
|
for (let o = 0; o < 32; o++) {
|
|
343
589
|
o > 0 && await new Promise((e) => setTimeout(e, o));
|
|
344
|
-
let s = await
|
|
590
|
+
let s = await G(e, t, n, r), c = s ? Number(s.used ?? 0) : 0;
|
|
345
591
|
if (c + i > a) return {
|
|
346
592
|
allowed: !1,
|
|
347
593
|
entitlement: n,
|
|
@@ -361,7 +607,7 @@ var A = (e, t, n, r) => ({
|
|
|
361
607
|
cost: i
|
|
362
608
|
};
|
|
363
609
|
} else {
|
|
364
|
-
let o =
|
|
610
|
+
let o = B("buse"), s = await e.insertIgnore(L, {
|
|
365
611
|
id: o,
|
|
366
612
|
tenantId: t,
|
|
367
613
|
entitlementKey: n,
|
|
@@ -385,10 +631,10 @@ var A = (e, t, n, r) => ({
|
|
|
385
631
|
}
|
|
386
632
|
throw Error(`billing usage consume: contention on (${t}, ${n}, ${r}) exceeded 32 attempts`);
|
|
387
633
|
}, "usageStore"),
|
|
388
|
-
increment: (t, n, r, i) =>
|
|
634
|
+
increment: (t, n, r, i) => V(async () => {
|
|
389
635
|
for (let a = 0; a < 32; a++) {
|
|
390
636
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
391
|
-
let o = await
|
|
637
|
+
let o = await G(e, t, n, r);
|
|
392
638
|
if (o) {
|
|
393
639
|
let t = Number(o.used ?? 0);
|
|
394
640
|
if (await e.updateMany("_voltro_billing_usage", {
|
|
@@ -396,7 +642,7 @@ var A = (e, t, n, r) => ({
|
|
|
396
642
|
updatedAt: /* @__PURE__ */ new Date()
|
|
397
643
|
}, { where: _(v("id", String(o.id)), v("used", t)) }) === 1) return t + i;
|
|
398
644
|
} else {
|
|
399
|
-
let a =
|
|
645
|
+
let a = B("buse"), o = await e.insertIgnore(L, {
|
|
400
646
|
id: a,
|
|
401
647
|
tenantId: t,
|
|
402
648
|
entitlementKey: n,
|
|
@@ -414,149 +660,250 @@ var A = (e, t, n, r) => ({
|
|
|
414
660
|
}
|
|
415
661
|
throw Error(`billing usage increment: contention on (${t}, ${n}, ${r}) exceeded 32 attempts`);
|
|
416
662
|
}, "usageStore"),
|
|
417
|
-
pending: () =>
|
|
418
|
-
markReported: (t, n, r, i) =>
|
|
419
|
-
let a = await
|
|
663
|
+
pending: () => V(async () => (await e.query(H({ table: L }))).map(Ue).filter((e) => e.used > e.reportedToProvider), "usageStore"),
|
|
664
|
+
markReported: (t, n, r, i) => V(async () => {
|
|
665
|
+
let a = await G(e, t, n, r);
|
|
420
666
|
a && await e.update(L, String(a.id), {
|
|
421
667
|
reportedToProvider: i,
|
|
422
668
|
updatedAt: /* @__PURE__ */ new Date()
|
|
423
669
|
});
|
|
424
670
|
}, "usageStore")
|
|
425
|
-
}),
|
|
426
|
-
customers:
|
|
427
|
-
subscriptions:
|
|
428
|
-
invoices:
|
|
429
|
-
usage:
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
671
|
+
}), Ge = () => ({
|
|
672
|
+
customers: Me(),
|
|
673
|
+
subscriptions: Fe(),
|
|
674
|
+
invoices: ze(),
|
|
675
|
+
usage: He(),
|
|
676
|
+
dunningNotices: ke()
|
|
677
|
+
}), Ke = (e) => ({
|
|
678
|
+
customers: Pe(e),
|
|
679
|
+
subscriptions: Re(e),
|
|
680
|
+
invoices: Ve(e),
|
|
681
|
+
usage: We(e),
|
|
682
|
+
dunningNotices: je(e)
|
|
683
|
+
}), K = D({ scope: "@voltro/plugin-billing" }), q = (e = /* @__PURE__ */ new Date()) => `${e.getUTCFullYear()}-${String(e.getUTCMonth() + 1).padStart(2, "0")}`, qe = (e) => ({ stores: e ?? Ge() }), J = (e, t) => e.pipe(p.retry({
|
|
684
|
+
schedule: h.recurs(Math.max(0, t - 1)),
|
|
437
685
|
while: (e) => e.transient
|
|
438
|
-
})),
|
|
439
|
-
let { provider:
|
|
686
|
+
})), Je = (t) => {
|
|
687
|
+
let { provider: n, plans: i } = t, a = t.attempts ?? 3, o = t.checkout, s = t.now ?? (() => /* @__PURE__ */ new Date()), c = qe(t.stores), l = t.dunning ?? be(void 0, {}), u = (e) => c.stores.subscriptions.getByTenant(e).pipe(p.map((t) => z(t, l, s(), e))), d = (e) => u(e).pipe(p.map((e) => e.plan)), f = (e) => c.stores.subscriptions.getByTenant(e), ee = (e, n) => p.gen(function* () {
|
|
440
688
|
if (t.resolveEntitlementLimit !== void 0) {
|
|
441
689
|
let r = yield* t.resolveEntitlementLimit(e, n);
|
|
442
690
|
if (r !== null) return r;
|
|
443
691
|
}
|
|
444
|
-
let r = yield*
|
|
445
|
-
return
|
|
446
|
-
}),
|
|
447
|
-
return
|
|
448
|
-
}), m = (e, t, n) =>
|
|
449
|
-
let i = yield*
|
|
692
|
+
let r = yield* d(e);
|
|
693
|
+
return i.entitlementLimit(r, n);
|
|
694
|
+
}), te = (e, t, n) => p.gen(function* () {
|
|
695
|
+
return j(t, yield* ee(e, t), yield* c.stores.usage.used(e, t, q(s())), n);
|
|
696
|
+
}), m = (e, t, n) => p.gen(function* () {
|
|
697
|
+
let i = yield* ee(e, t);
|
|
450
698
|
if (!Number.isFinite(i) || n <= 0) return;
|
|
451
|
-
let a =
|
|
452
|
-
if (!o.allowed) return yield*
|
|
699
|
+
let a = q(s()), o = yield* c.stores.usage.consume(e, t, a, n, i);
|
|
700
|
+
if (!o.allowed) return yield* p.fail(new r({
|
|
453
701
|
entitlement: t,
|
|
454
702
|
limit: o.limit,
|
|
455
703
|
used: o.used,
|
|
456
704
|
cost: n
|
|
457
705
|
}));
|
|
458
|
-
}), h = (e, t, n) => n <= 0 ?
|
|
459
|
-
if (!
|
|
460
|
-
let e = yield*
|
|
706
|
+
}), h = (e, t, n) => n <= 0 ? p.void : c.stores.usage.increment(e, t, q(s()), n).pipe(p.asVoid), ne = () => p.gen(function* () {
|
|
707
|
+
if (!n.supportsMeteredUsage) return;
|
|
708
|
+
let e = yield* c.stores.usage.pending();
|
|
461
709
|
for (let t of e) {
|
|
462
710
|
let e = t.used - t.reportedToProvider;
|
|
463
|
-
e <= 0 || (yield*
|
|
711
|
+
e <= 0 || (yield* J(n.reportUsage({
|
|
464
712
|
tenantId: t.tenantId,
|
|
465
713
|
entitlementKey: t.entitlementKey,
|
|
466
714
|
quantity: e
|
|
467
|
-
}),
|
|
715
|
+
}), a), yield* c.stores.usage.markReported(t.tenantId, t.entitlementKey, t.period, t.used));
|
|
468
716
|
}
|
|
469
|
-
}),
|
|
470
|
-
let
|
|
471
|
-
if (
|
|
717
|
+
}), g = (t) => p.gen(function* () {
|
|
718
|
+
let r = i.priceIdFor(t.plan);
|
|
719
|
+
if (r === null) return yield* p.fail(new e({
|
|
472
720
|
source: "billing",
|
|
473
721
|
message: `plan '${t.plan}' has no provider priceId — not a paid plan`,
|
|
474
722
|
transient: !1
|
|
475
723
|
}));
|
|
476
|
-
let
|
|
477
|
-
return yield*
|
|
724
|
+
let s = yield* c.stores.customers.getByTenant(t.tenantId);
|
|
725
|
+
return yield* J(n.createCheckoutSession({
|
|
478
726
|
tenantId: t.tenantId,
|
|
479
|
-
priceId:
|
|
727
|
+
priceId: r,
|
|
480
728
|
successUrl: t.successUrl,
|
|
481
729
|
cancelUrl: t.cancelUrl,
|
|
482
|
-
...
|
|
730
|
+
...s === null ? {} : { providerCustomerId: s.providerCustomerId },
|
|
483
731
|
...t.quantity === void 0 ? {} : { quantity: t.quantity },
|
|
484
|
-
...
|
|
485
|
-
...
|
|
486
|
-
}),
|
|
487
|
-
}),
|
|
488
|
-
let
|
|
489
|
-
return
|
|
732
|
+
...o?.adjustableQuantity === void 0 ? {} : { adjustableQuantity: o.adjustableQuantity },
|
|
733
|
+
...o?.trialDays === void 0 ? {} : { trialDays: o.trialDays }
|
|
734
|
+
}), a);
|
|
735
|
+
}), re = (t, r) => p.gen(function* () {
|
|
736
|
+
let i = yield* c.stores.customers.getByTenant(t);
|
|
737
|
+
return i === null ? yield* p.fail(new e({
|
|
490
738
|
source: "billing",
|
|
491
739
|
message: `no provider customer linked for tenant '${t}'`,
|
|
492
740
|
transient: !1
|
|
493
|
-
})) : yield*
|
|
741
|
+
})) : yield* J(n.createPortalSession({
|
|
494
742
|
tenantId: t,
|
|
495
|
-
providerCustomerId:
|
|
496
|
-
returnUrl:
|
|
497
|
-
}),
|
|
498
|
-
}),
|
|
743
|
+
providerCustomerId: i.providerCustomerId,
|
|
744
|
+
returnUrl: r
|
|
745
|
+
}), a);
|
|
746
|
+
}), ie = (e) => p.gen(function* () {
|
|
747
|
+
if (l.resolveRecipient !== void 0) {
|
|
748
|
+
let t = yield* l.resolveRecipient(e);
|
|
749
|
+
if (t !== null) return t;
|
|
750
|
+
}
|
|
751
|
+
if (n.customerEmail === void 0) return null;
|
|
752
|
+
let t = yield* c.stores.customers.getByTenant(e);
|
|
753
|
+
return t === null ? null : yield* n.customerEmail(t.providerCustomerId);
|
|
754
|
+
}), ae = (e) => {
|
|
755
|
+
let t = l.portalReturnUrl;
|
|
756
|
+
return t === void 0 ? p.succeed(null) : re(e, t).pipe(p.map((e) => e.url), p.catchAll(() => p.succeed(null)));
|
|
757
|
+
}, _ = (e, t, n, r, i) => p.gen(function* () {
|
|
758
|
+
let a = yield* ie(e.tenantId).pipe(p.catchAll(() => p.succeed(null))), o = yield* ae(e.tenantId), s = t.lockedSince !== null, c = Ee({
|
|
759
|
+
stepId: n,
|
|
760
|
+
locked: s,
|
|
761
|
+
graceEndsAt: i,
|
|
762
|
+
portalUrl: o
|
|
763
|
+
}), u = {
|
|
764
|
+
stepId: n,
|
|
765
|
+
tenantId: e.tenantId,
|
|
766
|
+
to: a,
|
|
767
|
+
plan: e.plan,
|
|
768
|
+
status: e.status,
|
|
769
|
+
pastDueSince: r,
|
|
770
|
+
graceEndsAt: i,
|
|
771
|
+
locked: s,
|
|
772
|
+
lockout: l.lockout,
|
|
773
|
+
portalUrl: o,
|
|
774
|
+
...c
|
|
775
|
+
};
|
|
776
|
+
K.info("billing dunning notice", {
|
|
777
|
+
tenant: e.tenantId,
|
|
778
|
+
step: n,
|
|
779
|
+
locked: s,
|
|
780
|
+
graceEndsAt: i.toISOString(),
|
|
781
|
+
delivered: l.notify !== void 0 && a !== null
|
|
782
|
+
});
|
|
783
|
+
let d = l.notify;
|
|
784
|
+
d !== void 0 && (yield* d(u).pipe(p.catchAllCause((t) => p.sync(() => {
|
|
785
|
+
K.warn("billing dunning notify failed", {
|
|
786
|
+
tenant: e.tenantId,
|
|
787
|
+
step: n,
|
|
788
|
+
cause: String(t)
|
|
789
|
+
});
|
|
790
|
+
}))));
|
|
791
|
+
}), v = (e) => p.gen(function* () {
|
|
792
|
+
let t = yield* c.stores.subscriptions.getByTenant(e);
|
|
793
|
+
if (t === null) return z(null, l, s(), e);
|
|
794
|
+
let r = yield* n.fetchSubscription(t.providerSubscriptionId).pipe(p.catchAll((t) => p.sync(() => (K.warn("billing dunning reconcile: provider unreachable, not escalating", {
|
|
795
|
+
tenant: e,
|
|
796
|
+
source: t.source,
|
|
797
|
+
message: t.message
|
|
798
|
+
}), null)))), i = t;
|
|
799
|
+
if (r !== null) {
|
|
800
|
+
let n = s(), a = r.status === "pastDue" ? t.status === "pastDue" && t.pastDueSince !== null ? t.pastDueSince : n : null;
|
|
801
|
+
yield* c.stores.subscriptions.patchByTenant(e, {
|
|
802
|
+
status: r.status,
|
|
803
|
+
...r.plan === null ? {} : { plan: r.plan },
|
|
804
|
+
quantity: r.quantity,
|
|
805
|
+
currentPeriodStart: r.currentPeriodStart,
|
|
806
|
+
currentPeriodEnd: r.currentPeriodEnd,
|
|
807
|
+
pastDueSince: a,
|
|
808
|
+
statusEventAt: n
|
|
809
|
+
}), i = {
|
|
810
|
+
...t,
|
|
811
|
+
status: r.status,
|
|
812
|
+
plan: r.plan ?? t.plan,
|
|
813
|
+
quantity: r.quantity,
|
|
814
|
+
currentPeriodStart: r.currentPeriodStart,
|
|
815
|
+
currentPeriodEnd: r.currentPeriodEnd,
|
|
816
|
+
pastDueSince: a,
|
|
817
|
+
statusEventAt: n
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
let a = z(i, l, s(), e);
|
|
821
|
+
if (!l.enabled || i.status !== "pastDue" || i.pastDueSince === null) return a;
|
|
822
|
+
let o = i.pastDueSince, u = Se(o), d = a.graceEndsAt ?? o, f = we(l.steps, o, s()).map((e) => e.id);
|
|
823
|
+
a.lockedSince !== null && f.push(he);
|
|
824
|
+
for (let t of f) (yield* c.stores.dunningNotices.claim(e, u, t, s())) && (yield* _(i, a, t, o, d));
|
|
825
|
+
return a;
|
|
826
|
+
}), y = () => p.gen(function* () {
|
|
827
|
+
let e = yield* c.stores.subscriptions.listByStatus("pastDue"), t = 0;
|
|
828
|
+
for (let n of e) yield* v(n.tenantId).pipe(p.catchAll((e) => p.sync(() => {
|
|
829
|
+
K.warn("billing dunning sweep: tenant failed", {
|
|
830
|
+
tenant: n.tenantId,
|
|
831
|
+
message: e.message
|
|
832
|
+
});
|
|
833
|
+
}))), t += 1;
|
|
834
|
+
return t;
|
|
835
|
+
}), b = (e) => {
|
|
836
|
+
if (!l.enabled) return p.void;
|
|
837
|
+
if (e._tag === "invoicePaymentFailed") return v(e.tenantId).pipe(p.asVoid);
|
|
838
|
+
if (e._tag !== "invoicePaid" && e._tag !== "subscriptionUpserted") return p.void;
|
|
839
|
+
let t = e.tenantId;
|
|
840
|
+
return c.stores.subscriptions.getByTenant(t).pipe(p.flatMap((e) => e !== null && (e.status === "pastDue" || e.pastDueSince !== null) ? v(t).pipe(p.asVoid) : p.void));
|
|
841
|
+
}, x = (e) => {
|
|
499
842
|
switch (e._tag) {
|
|
500
|
-
case "subscriptionUpserted": return
|
|
843
|
+
case "subscriptionUpserted": return c.stores.subscriptions.upsert({
|
|
501
844
|
tenantId: e.tenantId,
|
|
502
|
-
provider:
|
|
845
|
+
provider: n.name,
|
|
503
846
|
providerSubscriptionId: e.providerSubscriptionId,
|
|
504
847
|
plan: e.plan,
|
|
505
848
|
status: e.status,
|
|
506
849
|
quantity: e.quantity ?? 1,
|
|
507
850
|
currentPeriodStart: e.currentPeriodStart ?? null,
|
|
508
851
|
currentPeriodEnd: e.currentPeriodEnd,
|
|
509
|
-
cancelAt: e.cancelAt
|
|
852
|
+
cancelAt: e.cancelAt,
|
|
853
|
+
pastDueSince: null,
|
|
854
|
+
statusEventAt: e.occurredAt
|
|
510
855
|
});
|
|
511
|
-
case "subscriptionCanceled": return
|
|
512
|
-
case "invoicePaid": return
|
|
856
|
+
case "subscriptionCanceled": return c.stores.subscriptions.markCanceled(e.providerSubscriptionId);
|
|
857
|
+
case "invoicePaid": return c.stores.invoices.upsert({
|
|
513
858
|
tenantId: e.tenantId,
|
|
514
|
-
provider:
|
|
859
|
+
provider: n.name,
|
|
515
860
|
providerInvoiceId: e.providerInvoiceId,
|
|
516
861
|
amountMinor: e.amountMinor,
|
|
517
862
|
currency: e.currency,
|
|
518
|
-
status: "paid"
|
|
863
|
+
status: "paid",
|
|
864
|
+
statusEventAt: e.occurredAt
|
|
519
865
|
});
|
|
520
|
-
case "invoicePaymentFailed": return
|
|
866
|
+
case "invoicePaymentFailed": return c.stores.invoices.upsert({
|
|
521
867
|
tenantId: e.tenantId,
|
|
522
|
-
provider:
|
|
868
|
+
provider: n.name,
|
|
523
869
|
providerInvoiceId: e.providerInvoiceId,
|
|
524
870
|
amountMinor: e.amountMinor,
|
|
525
871
|
currency: e.currency,
|
|
526
|
-
status: "open"
|
|
872
|
+
status: "open",
|
|
873
|
+
statusEventAt: e.occurredAt
|
|
527
874
|
});
|
|
528
|
-
case "customerLinked": return
|
|
875
|
+
case "customerLinked": return c.stores.customers.upsert({
|
|
529
876
|
tenantId: e.tenantId,
|
|
530
|
-
provider:
|
|
877
|
+
provider: n.name,
|
|
531
878
|
providerCustomerId: e.providerCustomerId
|
|
532
879
|
});
|
|
533
880
|
}
|
|
534
|
-
},
|
|
535
|
-
let
|
|
536
|
-
if (
|
|
881
|
+
}, S = (e) => x(e).pipe(p.zipRight(b(e))), C = (t, r) => p.gen(function* () {
|
|
882
|
+
let o = yield* c.stores.subscriptions.getByTenant(t);
|
|
883
|
+
if (o === null) return yield* p.fail(new e({
|
|
537
884
|
source: "billing",
|
|
538
885
|
message: `no subscription for tenant '${t}' to change`,
|
|
539
886
|
transient: !1
|
|
540
887
|
}));
|
|
541
|
-
let s =
|
|
542
|
-
if (
|
|
888
|
+
let s = r.plan ?? o.plan, l = r.quantity ?? o.quantity;
|
|
889
|
+
if (r.plan !== void 0 && !i.has(s)) return yield* p.fail(new e({
|
|
543
890
|
source: "billing",
|
|
544
891
|
message: `unknown plan '${s}' — not in the plan registry`,
|
|
545
892
|
transient: !1
|
|
546
893
|
}));
|
|
547
|
-
if (!Number.isInteger(
|
|
894
|
+
if (!Number.isInteger(l) || l <= 0) return yield* p.fail(new e({
|
|
548
895
|
source: "billing",
|
|
549
|
-
message: `seat quantity must be a positive integer, got ${
|
|
896
|
+
message: `seat quantity must be a positive integer, got ${l}`,
|
|
550
897
|
transient: !1
|
|
551
898
|
}));
|
|
552
|
-
let u = yield*
|
|
553
|
-
providerSubscriptionId:
|
|
554
|
-
...
|
|
555
|
-
...
|
|
899
|
+
let u = yield* J(n.updateSubscription({
|
|
900
|
+
providerSubscriptionId: o.providerSubscriptionId,
|
|
901
|
+
...r.plan === void 0 ? {} : { priceId: i.priceIdFor(s) ?? s },
|
|
902
|
+
...r.quantity === void 0 ? {} : { quantity: l },
|
|
556
903
|
proration: "prorate"
|
|
557
|
-
}),
|
|
558
|
-
return yield*
|
|
559
|
-
...
|
|
904
|
+
}), a);
|
|
905
|
+
return yield* c.stores.subscriptions.patchByTenant(t, {
|
|
906
|
+
...r.plan === void 0 ? {} : { plan: s },
|
|
560
907
|
quantity: u.quantity,
|
|
561
908
|
...u.currentPeriodStart === null ? {} : { currentPeriodStart: u.currentPeriodStart },
|
|
562
909
|
...u.currentPeriodEnd === null ? {} : { currentPeriodEnd: u.currentPeriodEnd }
|
|
@@ -570,46 +917,49 @@ var A = (e, t, n, r) => ({
|
|
|
570
917
|
});
|
|
571
918
|
return {
|
|
572
919
|
service: {
|
|
573
|
-
subscription:
|
|
574
|
-
plan:
|
|
575
|
-
|
|
920
|
+
subscription: f,
|
|
921
|
+
plan: d,
|
|
922
|
+
entitlementStatus: u,
|
|
923
|
+
reconcileDunning: v,
|
|
924
|
+
dunningSweep: y,
|
|
925
|
+
checkEntitlement: te,
|
|
576
926
|
consumeEntitlement: m,
|
|
577
927
|
reportUsage: h,
|
|
578
|
-
flushUsage:
|
|
579
|
-
startCheckout:
|
|
580
|
-
portalUrl:
|
|
581
|
-
applyEvent:
|
|
582
|
-
changePlan: (e, t) =>
|
|
583
|
-
changeSeats: (e, t) =>
|
|
584
|
-
previewChange: (t,
|
|
585
|
-
let
|
|
586
|
-
if (
|
|
928
|
+
flushUsage: ne,
|
|
929
|
+
startCheckout: g,
|
|
930
|
+
portalUrl: re,
|
|
931
|
+
applyEvent: S,
|
|
932
|
+
changePlan: (e, t) => C(e, { plan: t }),
|
|
933
|
+
changeSeats: (e, t) => C(e, { quantity: t }),
|
|
934
|
+
previewChange: (t, r) => p.gen(function* () {
|
|
935
|
+
let o = yield* c.stores.subscriptions.getByTenant(t);
|
|
936
|
+
if (o === null) return yield* p.fail(new e({
|
|
587
937
|
source: "billing",
|
|
588
938
|
message: `no subscription for tenant '${t}' to preview`,
|
|
589
939
|
transient: !1
|
|
590
940
|
}));
|
|
591
|
-
let s =
|
|
592
|
-
providerSubscriptionId:
|
|
593
|
-
...
|
|
594
|
-
...
|
|
941
|
+
let s = r.plan ?? o.plan, l = yield* J(n.previewSubscriptionChange({
|
|
942
|
+
providerSubscriptionId: o.providerSubscriptionId,
|
|
943
|
+
...r.plan === void 0 ? {} : { priceId: i.priceIdFor(s) ?? s },
|
|
944
|
+
...r.quantity === void 0 ? {} : { quantity: r.quantity },
|
|
595
945
|
proration: "prorate"
|
|
596
|
-
}),
|
|
946
|
+
}), a);
|
|
597
947
|
return {
|
|
598
948
|
tenantId: t,
|
|
599
949
|
plan: s,
|
|
600
|
-
quantity:
|
|
601
|
-
prorationMinor:
|
|
602
|
-
currency:
|
|
950
|
+
quantity: l.quantity,
|
|
951
|
+
prorationMinor: l.prorationMinor,
|
|
952
|
+
currency: l.currency
|
|
603
953
|
};
|
|
604
954
|
}),
|
|
605
|
-
invoices: (e) =>
|
|
606
|
-
let t = yield*
|
|
607
|
-
return t === null ? [] : yield*
|
|
955
|
+
invoices: (e) => p.gen(function* () {
|
|
956
|
+
let t = yield* c.stores.customers.getByTenant(e);
|
|
957
|
+
return t === null ? [] : yield* J(n.listInvoices({ providerCustomerId: t.providerCustomerId }), a);
|
|
608
958
|
})
|
|
609
959
|
},
|
|
610
|
-
holder:
|
|
960
|
+
holder: c
|
|
611
961
|
};
|
|
612
|
-
},
|
|
962
|
+
}, Ye = (e) => m.succeed(t, e), Xe = "_voltro_ai_usage", Ze = (e) => {
|
|
613
963
|
for (let [t, n] of Object.entries(e)) {
|
|
614
964
|
if (t.length === 0) throw Error("billing metering: a meter key must be non-empty");
|
|
615
965
|
if (n.from === "rpc") {
|
|
@@ -618,29 +968,33 @@ var A = (e, t, n, r) => ({
|
|
|
618
968
|
if (typeof n.table != "string" || n.table.length === 0) throw Error(`billing metering: cdc meter '${t}' needs a 'table'`);
|
|
619
969
|
} else if (n.from !== "ai") throw Error(`billing metering: meter '${t}' has unknown source '${String(n.from)}'`);
|
|
620
970
|
}
|
|
621
|
-
},
|
|
622
|
-
|
|
971
|
+
}, Qe = /* @__PURE__ */ new WeakMap(), $e = (e) => {
|
|
972
|
+
if (!e.global && !e.sticky) return e;
|
|
973
|
+
let t = Qe.get(e);
|
|
974
|
+
return t === void 0 && (t = new RegExp(e.source, e.flags.replace(/[gy]/g, "")), Qe.set(e, t)), t;
|
|
975
|
+
}, et = (e, t) => typeof e == "string" ? t === e : $e(e).test(t), tt = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "rpc" && (e.kind === void 0 || e.kind === n) && et(e.match, t)).map(([e]) => e), nt = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "cdc" && e.table === t && (e.op ?? "insert") === n).map(([e]) => e), rt = (e) => Object.entries(e).filter(([, e]) => e.from === "ai").map(([e, t]) => [e, t.metric ?? "tokens"]), it = (e) => e.tenantId, at = (e, t, n, r) => p.forEach(n, (n) => e.reportUsage(t, n, r).pipe(p.catchAll(() => p.void)), { discard: !0 }), ot = (e, t) => (n, r) => {
|
|
976
|
+
let i = tt(t, r.tag, r.kind);
|
|
623
977
|
if (i.length === 0) return n;
|
|
624
|
-
let a =
|
|
625
|
-
return a === null ? n : n.pipe(
|
|
626
|
-
},
|
|
627
|
-
let r =
|
|
628
|
-
if (r.length === 0) return
|
|
978
|
+
let a = it(r.subject);
|
|
979
|
+
return a === null ? n : n.pipe(p.tap(() => at(e, a, i, 1)));
|
|
980
|
+
}, st = (e, t) => (n) => {
|
|
981
|
+
let r = nt(t, n.table, n.op);
|
|
982
|
+
if (r.length === 0) return p.void;
|
|
629
983
|
let i = n.new?.tenantId ?? n.old?.tenantId ?? null;
|
|
630
|
-
return i === null ?
|
|
631
|
-
},
|
|
632
|
-
let i =
|
|
984
|
+
return i === null ? p.void : at(e, i, r, 1);
|
|
985
|
+
}, ct = (e) => new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), 1, 0, 0, 0, 0)), lt = (e, t, n, r = () => /* @__PURE__ */ new Date()) => p.gen(function* () {
|
|
986
|
+
let i = rt(n);
|
|
633
987
|
if (i.length === 0) return;
|
|
634
988
|
let a = r();
|
|
635
|
-
|
|
989
|
+
q(a);
|
|
636
990
|
let o = {
|
|
637
|
-
table:
|
|
638
|
-
predicate:
|
|
991
|
+
table: Xe,
|
|
992
|
+
predicate: y("calledAt", ct(a)),
|
|
639
993
|
order: [],
|
|
640
994
|
take: void 0,
|
|
641
995
|
skip: void 0,
|
|
642
996
|
projection: void 0
|
|
643
|
-
}, s = yield*
|
|
997
|
+
}, s = yield* p.promise(() => Promise.resolve(t.query(o)).then((e) => e).catch(() => [])), c = /* @__PURE__ */ new Map();
|
|
644
998
|
for (let e of s) {
|
|
645
999
|
let t = e.tenantId ?? null;
|
|
646
1000
|
if (t === null) continue;
|
|
@@ -653,17 +1007,17 @@ var A = (e, t, n, r) => ({
|
|
|
653
1007
|
for (let [t, n] of i) for (let [r, i] of c) {
|
|
654
1008
|
let a = n === "costMicroUsd" ? i.cost : i.tokens;
|
|
655
1009
|
if (a <= 0) continue;
|
|
656
|
-
let o = a - (yield* e.checkEntitlement(r, t, 0).pipe(
|
|
657
|
-
o > 0 && (yield* e.reportUsage(r, t, o).pipe(
|
|
1010
|
+
let o = a - (yield* e.checkEntitlement(r, t, 0).pipe(p.map((e) => e.used), p.catchAll(() => p.succeed(0))));
|
|
1011
|
+
o > 0 && (yield* e.reportUsage(r, t, o).pipe(p.catchAll(() => p.void)));
|
|
658
1012
|
}
|
|
659
|
-
}),
|
|
1013
|
+
}), ut = (e, t, n, r) => lt(e, t, n, r).pipe(p.andThen(e.flushUsage().pipe(p.catchAll(() => p.void)))), dt = (e) => Object.values(e).some((e) => e.from === "cdc"), ft = (e) => e === void 0 ? 0 : e === "unlimited" ? Infinity : e, pt = (e) => {
|
|
660
1014
|
let t = /* @__PURE__ */ new Map();
|
|
661
1015
|
for (let [n, r] of Object.entries(e)) r.priceId !== void 0 && t.set(r.priceId, n);
|
|
662
1016
|
return {
|
|
663
1017
|
has: (t) => Object.prototype.hasOwnProperty.call(e, t),
|
|
664
1018
|
entitlementLimit: (t, n) => {
|
|
665
1019
|
let r = e[t];
|
|
666
|
-
return r === void 0 ? 0 :
|
|
1020
|
+
return r === void 0 ? 0 : ft(r.entitlements[n]);
|
|
667
1021
|
},
|
|
668
1022
|
planForPriceId: (e) => t.get(e) ?? null,
|
|
669
1023
|
priceIdFor: (t) => e[t]?.priceId ?? null,
|
|
@@ -674,14 +1028,14 @@ var A = (e, t, n, r) => ({
|
|
|
674
1028
|
currencyFor: (t) => (e[t]?.currency ?? "usd").toLowerCase(),
|
|
675
1029
|
planIds: () => Object.keys(e)
|
|
676
1030
|
};
|
|
677
|
-
},
|
|
678
|
-
id:
|
|
679
|
-
windowKey:
|
|
680
|
-
claimedBy:
|
|
681
|
-
claimedAt:
|
|
682
|
-
}).unique("billingFlushClaimWindow", ["windowKey"]),
|
|
1031
|
+
}, mt = () => pt({ [n]: { entitlements: {} } }), Y = "_voltro_billing_flush_claims", ht = w(Y, {
|
|
1032
|
+
id: b({ scheme: "numeric" }),
|
|
1033
|
+
windowKey: T(),
|
|
1034
|
+
claimedBy: T(),
|
|
1035
|
+
claimedAt: E().default("now")
|
|
1036
|
+
}).unique("billingFlushClaimWindow", ["windowKey"]), gt = (e, t) => `billing-flush:${Math.floor(e / Math.max(1, t))}`, _t = async (e, t, n, r) => {
|
|
683
1037
|
try {
|
|
684
|
-
return (await e.insertIgnore(
|
|
1038
|
+
return (await e.insertIgnore(Y, {
|
|
685
1039
|
windowKey: t,
|
|
686
1040
|
claimedBy: n,
|
|
687
1041
|
claimedAt: new Date(r)
|
|
@@ -689,38 +1043,38 @@ var A = (e, t, n, r) => ({
|
|
|
689
1043
|
} catch {
|
|
690
1044
|
return !0;
|
|
691
1045
|
}
|
|
692
|
-
},
|
|
693
|
-
maxNetworkRetries:
|
|
694
|
-
timeout:
|
|
1046
|
+
}, vt = 3, yt = 2e4, bt = (e) => e.client ?? new O(e.apiKey, {
|
|
1047
|
+
maxNetworkRetries: vt,
|
|
1048
|
+
timeout: yt,
|
|
695
1049
|
appInfo: {
|
|
696
1050
|
name: "voltro",
|
|
697
1051
|
url: "https://voltro.dev"
|
|
698
1052
|
}
|
|
699
|
-
}),
|
|
1053
|
+
}), xt = (e) => e instanceof O.errors.StripeConnectionError || e instanceof O.errors.StripeAPIError || e instanceof O.errors.StripeRateLimitError, St = (e) => e instanceof O.errors.StripeError ? e.code ?? e.type : void 0, X = (t, n) => p.tryPromise({
|
|
700
1054
|
try: n,
|
|
701
1055
|
catch: (n) => new e({
|
|
702
1056
|
source: t,
|
|
703
|
-
message:
|
|
704
|
-
transient:
|
|
1057
|
+
message: Ct(n),
|
|
1058
|
+
transient: xt(n)
|
|
705
1059
|
})
|
|
706
|
-
}),
|
|
707
|
-
if (e instanceof
|
|
708
|
-
let t =
|
|
1060
|
+
}), Ct = (e) => {
|
|
1061
|
+
if (e instanceof O.errors.StripeError) {
|
|
1062
|
+
let t = St(e);
|
|
709
1063
|
return t === void 0 ? e.message : `${e.message} (${t})`;
|
|
710
1064
|
}
|
|
711
1065
|
return String(e?.message ?? e);
|
|
712
|
-
},
|
|
1066
|
+
}, wt = (e) => {
|
|
713
1067
|
let t = e.items?.data?.[0];
|
|
714
1068
|
return {
|
|
715
|
-
start:
|
|
716
|
-
end:
|
|
1069
|
+
start: Z(t?.current_period_start),
|
|
1070
|
+
end: Z(t?.current_period_end)
|
|
717
1071
|
};
|
|
718
|
-
},
|
|
1072
|
+
}, Z = (e) => typeof e == "number" && Number.isFinite(e) ? /* @__PURE__ */ new Date(e * 1e3) : null, Tt = (e) => {
|
|
719
1073
|
let t = e.items?.data ?? [];
|
|
720
1074
|
if (t.length === 0) return 1;
|
|
721
1075
|
let n = t.reduce((e, t) => e + (t.quantity ?? 0), 0);
|
|
722
1076
|
return n > 0 ? n : 1;
|
|
723
|
-
},
|
|
1077
|
+
}, Et = {
|
|
724
1078
|
active: "active",
|
|
725
1079
|
trialing: "trialing",
|
|
726
1080
|
past_due: "pastDue",
|
|
@@ -729,79 +1083,84 @@ var A = (e, t, n, r) => ({
|
|
|
729
1083
|
incomplete: "incomplete",
|
|
730
1084
|
incomplete_expired: "canceled",
|
|
731
1085
|
paused: "pastDue"
|
|
732
|
-
},
|
|
1086
|
+
}, Q = (e) => {
|
|
733
1087
|
let t = e.metadata?.tenantId;
|
|
734
1088
|
if (typeof t == "string" && t.length > 0) return t;
|
|
735
1089
|
let n = e.client_reference_id;
|
|
736
1090
|
return typeof n == "string" && n.length > 0 ? n : null;
|
|
737
|
-
},
|
|
1091
|
+
}, Dt = (e) => {
|
|
738
1092
|
let t = e.items?.data?.[0]?.price;
|
|
739
1093
|
return typeof t?.id == "string" ? t.id : null;
|
|
740
|
-
},
|
|
1094
|
+
}, Ot = (e, t) => {
|
|
741
1095
|
let n = e.metadata?.plan;
|
|
742
1096
|
if (typeof n == "string" && n.length > 0) return n;
|
|
743
|
-
let r =
|
|
1097
|
+
let r = Dt(e);
|
|
744
1098
|
return r !== null && t ? t(r) : null;
|
|
745
|
-
},
|
|
1099
|
+
}, kt = (e, t) => {
|
|
1100
|
+
let n = Z(e.created);
|
|
746
1101
|
switch (e.type) {
|
|
747
1102
|
case "customer.subscription.created":
|
|
748
1103
|
case "customer.subscription.updated":
|
|
749
1104
|
case "customer.subscription.resumed":
|
|
750
1105
|
case "customer.subscription.paused": {
|
|
751
|
-
let
|
|
752
|
-
if (
|
|
753
|
-
let
|
|
1106
|
+
let r = e.data.object, i = Q(r), a = Ot(r, t), o = Et[r.status];
|
|
1107
|
+
if (i === null || a === null || o === void 0) return null;
|
|
1108
|
+
let s = wt(r);
|
|
754
1109
|
return {
|
|
755
1110
|
_tag: "subscriptionUpserted",
|
|
756
|
-
tenantId:
|
|
757
|
-
providerSubscriptionId:
|
|
758
|
-
plan:
|
|
759
|
-
status:
|
|
760
|
-
quantity:
|
|
761
|
-
currentPeriodStart:
|
|
762
|
-
currentPeriodEnd:
|
|
763
|
-
cancelAt:
|
|
1111
|
+
tenantId: i,
|
|
1112
|
+
providerSubscriptionId: r.id,
|
|
1113
|
+
plan: a,
|
|
1114
|
+
status: o,
|
|
1115
|
+
quantity: Tt(r),
|
|
1116
|
+
currentPeriodStart: s.start,
|
|
1117
|
+
currentPeriodEnd: s.end,
|
|
1118
|
+
cancelAt: Z(r.cancel_at),
|
|
1119
|
+
occurredAt: n
|
|
764
1120
|
};
|
|
765
1121
|
}
|
|
766
1122
|
case "customer.subscription.deleted": {
|
|
767
|
-
let t = e.data.object,
|
|
768
|
-
return
|
|
1123
|
+
let t = e.data.object, r = Q(t);
|
|
1124
|
+
return r === null ? null : {
|
|
769
1125
|
_tag: "subscriptionCanceled",
|
|
770
|
-
tenantId:
|
|
771
|
-
providerSubscriptionId: t.id
|
|
1126
|
+
tenantId: r,
|
|
1127
|
+
providerSubscriptionId: t.id,
|
|
1128
|
+
occurredAt: n
|
|
772
1129
|
};
|
|
773
1130
|
}
|
|
774
1131
|
case "invoice.paid":
|
|
775
1132
|
case "invoice.payment_failed": {
|
|
776
|
-
let t = e.data.object,
|
|
777
|
-
return
|
|
1133
|
+
let t = e.data.object, r = Q(t);
|
|
1134
|
+
return r === null || typeof t.id != "string" ? null : {
|
|
778
1135
|
_tag: e.type === "invoice.paid" ? "invoicePaid" : "invoicePaymentFailed",
|
|
779
|
-
tenantId:
|
|
1136
|
+
tenantId: r,
|
|
780
1137
|
providerInvoiceId: t.id,
|
|
781
1138
|
amountMinor: t.amount_due,
|
|
782
|
-
currency: t.currency
|
|
1139
|
+
currency: t.currency,
|
|
1140
|
+
occurredAt: n
|
|
783
1141
|
};
|
|
784
1142
|
}
|
|
785
1143
|
case "customer.created": {
|
|
786
|
-
let t = e.data.object,
|
|
787
|
-
return
|
|
1144
|
+
let t = e.data.object, r = Q(t);
|
|
1145
|
+
return r === null ? null : {
|
|
788
1146
|
_tag: "customerLinked",
|
|
789
|
-
tenantId:
|
|
790
|
-
providerCustomerId: t.id
|
|
1147
|
+
tenantId: r,
|
|
1148
|
+
providerCustomerId: t.id,
|
|
1149
|
+
occurredAt: n
|
|
791
1150
|
};
|
|
792
1151
|
}
|
|
793
1152
|
default: return null;
|
|
794
1153
|
}
|
|
795
|
-
},
|
|
1154
|
+
}, At = {
|
|
796
1155
|
prorate: "create_prorations",
|
|
797
1156
|
none: "none",
|
|
798
1157
|
always_invoice: "always_invoice"
|
|
799
|
-
},
|
|
800
|
-
let n =
|
|
801
|
-
let n =
|
|
1158
|
+
}, jt = (t) => {
|
|
1159
|
+
let n = bt(t), r = t.planForPriceId, i = (e) => n.subscriptions.retrieve(e, { expand: ["items.data.price"] }), a = (e, t) => {
|
|
1160
|
+
let n = wt(e), i = e.items?.data?.[0]?.price;
|
|
802
1161
|
return {
|
|
803
|
-
plan:
|
|
804
|
-
quantity:
|
|
1162
|
+
plan: Ot(e, r) ?? "free",
|
|
1163
|
+
quantity: Tt(e),
|
|
805
1164
|
prorationMinor: t,
|
|
806
1165
|
currency: (i?.currency ?? "usd").toLowerCase(),
|
|
807
1166
|
currentPeriodStart: n.start,
|
|
@@ -815,7 +1174,7 @@ var A = (e, t, n, r) => ({
|
|
|
815
1174
|
...t.priceId === void 0 ? {} : { price: t.priceId },
|
|
816
1175
|
...t.quantity === void 0 ? {} : { quantity: t.quantity }
|
|
817
1176
|
}],
|
|
818
|
-
proration_behavior:
|
|
1177
|
+
proration_behavior: At[t.proration ?? "prorate"],
|
|
819
1178
|
...t.priceId !== void 0 && r ? { metadata: {
|
|
820
1179
|
...e.metadata,
|
|
821
1180
|
plan: r(t.priceId) ?? ""
|
|
@@ -825,7 +1184,7 @@ var A = (e, t, n, r) => ({
|
|
|
825
1184
|
return {
|
|
826
1185
|
name: "stripe",
|
|
827
1186
|
supportsMeteredUsage: t.supportsMeteredUsage ?? !0,
|
|
828
|
-
createCheckoutSession: (e) =>
|
|
1187
|
+
createCheckoutSession: (e) => X("stripe.checkout.sessions.create", async () => {
|
|
829
1188
|
let t = await n.checkout.sessions.create({
|
|
830
1189
|
mode: "subscription",
|
|
831
1190
|
line_items: [{
|
|
@@ -857,11 +1216,11 @@ var A = (e, t, n, r) => ({
|
|
|
857
1216
|
if (typeof t.url != "string") throw Error("checkout session has no url");
|
|
858
1217
|
return { url: t.url };
|
|
859
1218
|
}),
|
|
860
|
-
createPortalSession: (e) =>
|
|
1219
|
+
createPortalSession: (e) => X("stripe.billingPortal.sessions.create", async () => ({ url: (await n.billingPortal.sessions.create({
|
|
861
1220
|
customer: e.providerCustomerId,
|
|
862
1221
|
return_url: e.returnUrl
|
|
863
1222
|
})).url })),
|
|
864
|
-
reportUsage: (e) =>
|
|
1223
|
+
reportUsage: (e) => X("stripe.billing.meterEvents.create", async () => {
|
|
865
1224
|
await n.billing.meterEvents.create({
|
|
866
1225
|
event_name: e.entitlementKey,
|
|
867
1226
|
identifier: `${e.tenantId}:${e.entitlementKey}:${e.periodEnd?.toISOString() ?? "current"}`,
|
|
@@ -871,11 +1230,11 @@ var A = (e, t, n, r) => ({
|
|
|
871
1230
|
}
|
|
872
1231
|
});
|
|
873
1232
|
}),
|
|
874
|
-
updateSubscription: (e) =>
|
|
875
|
-
let t = await i(e.providerSubscriptionId), r = await n.subscriptions.update(e.providerSubscriptionId, o(t, e)), s = await
|
|
1233
|
+
updateSubscription: (e) => X("stripe.subscriptions.update", async () => {
|
|
1234
|
+
let t = await i(e.providerSubscriptionId), r = await n.subscriptions.update(e.providerSubscriptionId, o(t, e)), s = await Nt(n, r);
|
|
876
1235
|
return a(r, s);
|
|
877
1236
|
}),
|
|
878
|
-
previewSubscriptionChange: (e) =>
|
|
1237
|
+
previewSubscriptionChange: (e) => X("stripe.invoices.createPreview", async () => {
|
|
879
1238
|
let t = await i(e.providerSubscriptionId), s = o(t, e), c = (await n.invoices.createPreview({
|
|
880
1239
|
subscription: e.providerSubscriptionId,
|
|
881
1240
|
subscription_details: {
|
|
@@ -889,7 +1248,27 @@ var A = (e, t, n, r) => ({
|
|
|
889
1248
|
...e.priceId !== void 0 && r ? { plan: r(e.priceId) ?? "free" } : {}
|
|
890
1249
|
};
|
|
891
1250
|
}),
|
|
892
|
-
|
|
1251
|
+
fetchSubscription: (e) => X("stripe.subscriptions.retrieve", async () => {
|
|
1252
|
+
try {
|
|
1253
|
+
let t = await i(e), n = wt(t);
|
|
1254
|
+
return {
|
|
1255
|
+
status: Et[t.status] ?? "incomplete",
|
|
1256
|
+
plan: Ot(t, r),
|
|
1257
|
+
quantity: Tt(t),
|
|
1258
|
+
currentPeriodStart: n.start,
|
|
1259
|
+
currentPeriodEnd: n.end,
|
|
1260
|
+
cancelAt: Z(t.cancel_at)
|
|
1261
|
+
};
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
if (e instanceof O.errors.StripeInvalidRequestError && e.statusCode === 404) return null;
|
|
1264
|
+
throw e;
|
|
1265
|
+
}
|
|
1266
|
+
}),
|
|
1267
|
+
customerEmail: (e) => X("stripe.customers.retrieve", async () => {
|
|
1268
|
+
let t = await n.customers.retrieve(e);
|
|
1269
|
+
return t.deleted === !0 ? null : typeof t.email == "string" && t.email.length > 0 ? t.email : null;
|
|
1270
|
+
}),
|
|
1271
|
+
listInvoices: (e) => X("stripe.invoices.list", async () => (await n.invoices.list({
|
|
893
1272
|
customer: e.providerCustomerId,
|
|
894
1273
|
limit: e.limit ?? 20
|
|
895
1274
|
})).data.map((e) => ({
|
|
@@ -897,11 +1276,11 @@ var A = (e, t, n, r) => ({
|
|
|
897
1276
|
amountMinor: e.amount_due,
|
|
898
1277
|
currency: e.currency,
|
|
899
1278
|
status: e.status ?? "unknown",
|
|
900
|
-
createdAt:
|
|
1279
|
+
createdAt: Z(e.created),
|
|
901
1280
|
hostedUrl: e.hosted_invoice_url ?? null,
|
|
902
1281
|
pdfUrl: e.invoice_pdf ?? null
|
|
903
1282
|
}))),
|
|
904
|
-
verifyWebhook: (t) =>
|
|
1283
|
+
verifyWebhook: (t) => p.try({
|
|
905
1284
|
try: () => n.webhooks.constructEvent(Buffer.from(t.rawBody), t.signatureHeader, t.secret),
|
|
906
1285
|
catch: (t) => new e({
|
|
907
1286
|
source: "stripe.webhooks.constructEvent",
|
|
@@ -909,27 +1288,36 @@ var A = (e, t, n, r) => ({
|
|
|
909
1288
|
transient: !1
|
|
910
1289
|
})
|
|
911
1290
|
}),
|
|
912
|
-
normalizeEvent: (e) =>
|
|
1291
|
+
normalizeEvent: (e) => p.succeed(Mt(e) ? kt(e, r) : null)
|
|
913
1292
|
};
|
|
914
|
-
},
|
|
1293
|
+
}, Mt = (e) => typeof e == "object" && !!e && typeof e.type == "string" && typeof e.data == "object", Nt = async (e, t) => {
|
|
915
1294
|
try {
|
|
916
1295
|
return (await e.invoices.createPreview({ subscription: t.id })).lines.data.filter((e) => e.parent?.subscription_item_details?.proration === !0).reduce((e, t) => e + t.amount, 0);
|
|
917
1296
|
} catch {
|
|
918
1297
|
return 0;
|
|
919
1298
|
}
|
|
920
|
-
},
|
|
921
|
-
let t = e?.baseUrl ?? "https://mock.billing.local", n = [], r = [], i = (e) => typeof e == "object" && !!e && typeof e._tag == "string";
|
|
1299
|
+
}, Pt = (e) => {
|
|
1300
|
+
let t = e?.baseUrl ?? "https://mock.billing.local", n = [], r = [], i = [], a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s = (e) => typeof e == "object" && !!e && typeof e._tag == "string";
|
|
922
1301
|
return {
|
|
923
1302
|
name: "mock",
|
|
924
1303
|
supportsMeteredUsage: e?.supportsMeteredUsage ?? !0,
|
|
925
1304
|
pushed: n,
|
|
926
1305
|
updates: r,
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1306
|
+
fetched: i,
|
|
1307
|
+
setSubscription: (e, t) => {
|
|
1308
|
+
t === null ? a.delete(e) : a.set(e, t);
|
|
1309
|
+
},
|
|
1310
|
+
setCustomerEmail: (e, t) => {
|
|
1311
|
+
t === null ? o.delete(e) : o.set(e, t);
|
|
1312
|
+
},
|
|
1313
|
+
fetchSubscription: (e) => p.sync(() => (i.push(e), a.get(e) ?? null)),
|
|
1314
|
+
customerEmail: (e) => p.sync(() => o.get(e) ?? null),
|
|
1315
|
+
createCheckoutSession: (e) => p.succeed({ url: `${t}/checkout?tenant=${encodeURIComponent(e.tenantId)}&price=${encodeURIComponent(e.priceId)}` }),
|
|
1316
|
+
createPortalSession: (e) => p.succeed({ url: `${t}/portal?customer=${encodeURIComponent(e.providerCustomerId)}` }),
|
|
1317
|
+
reportUsage: (e) => p.sync(() => {
|
|
930
1318
|
n.push(e);
|
|
931
1319
|
}),
|
|
932
|
-
updateSubscription: (e) =>
|
|
1320
|
+
updateSubscription: (e) => p.sync(() => (r.push(e), {
|
|
933
1321
|
plan: e.priceId ?? "mock-plan",
|
|
934
1322
|
quantity: e.quantity ?? 1,
|
|
935
1323
|
prorationMinor: 0,
|
|
@@ -937,7 +1325,7 @@ var A = (e, t, n, r) => ({
|
|
|
937
1325
|
currentPeriodStart: null,
|
|
938
1326
|
currentPeriodEnd: null
|
|
939
1327
|
})),
|
|
940
|
-
previewSubscriptionChange: (e) =>
|
|
1328
|
+
previewSubscriptionChange: (e) => p.succeed({
|
|
941
1329
|
plan: e.priceId ?? "mock-plan",
|
|
942
1330
|
quantity: e.quantity ?? 1,
|
|
943
1331
|
prorationMinor: 0,
|
|
@@ -945,55 +1333,55 @@ var A = (e, t, n, r) => ({
|
|
|
945
1333
|
currentPeriodStart: null,
|
|
946
1334
|
currentPeriodEnd: null
|
|
947
1335
|
}),
|
|
948
|
-
listInvoices: () =>
|
|
949
|
-
normalizeEvent: (e) =>
|
|
1336
|
+
listInvoices: () => p.succeed([]),
|
|
1337
|
+
normalizeEvent: (e) => p.succeed(s(e) ? e : null)
|
|
950
1338
|
};
|
|
951
|
-
},
|
|
1339
|
+
}, Ft = (e) => {
|
|
952
1340
|
let { provider: t, env: n } = e;
|
|
953
1341
|
if (t !== void 0 && typeof t == "object") return t;
|
|
954
1342
|
if ((t ?? (n.STRIPE_SECRET_KEY ? "stripe" : "mock")) === "stripe") {
|
|
955
1343
|
let t = e.apiKey ?? n.STRIPE_SECRET_KEY;
|
|
956
1344
|
if (!t) throw Error("billingPlugin: provider 'stripe' needs an apiKey (option or STRIPE_SECRET_KEY env)");
|
|
957
|
-
return
|
|
1345
|
+
return jt({
|
|
958
1346
|
apiKey: t,
|
|
959
1347
|
...e.planForPriceId ? { planForPriceId: e.planForPriceId } : {}
|
|
960
1348
|
});
|
|
961
1349
|
}
|
|
962
|
-
return
|
|
963
|
-
},
|
|
964
|
-
id:
|
|
965
|
-
path:
|
|
966
|
-
provider:
|
|
967
|
-
payload:
|
|
1350
|
+
return Pt();
|
|
1351
|
+
}, It = D({ scope: "@voltro/plugin-billing" }), Lt = "billing", $ = "/billing/webhook", Rt = (e) => oe({
|
|
1352
|
+
id: Lt,
|
|
1353
|
+
path: $,
|
|
1354
|
+
provider: A(),
|
|
1355
|
+
payload: ne.Unknown,
|
|
968
1356
|
handler: async (t) => {
|
|
969
|
-
let n = e.provider.verifyWebhook !== void 0 && e.webhookSecret !== null && e.webhookSecret !== void 0 ? await
|
|
1357
|
+
let n = e.provider.verifyWebhook !== void 0 && e.webhookSecret !== null && e.webhookSecret !== void 0 ? await p.runPromise(e.provider.verifyWebhook({
|
|
970
1358
|
rawBody: t.rawBody,
|
|
971
1359
|
signatureHeader: t.headers["stripe-signature"] ?? "",
|
|
972
1360
|
secret: e.webhookSecret ?? ""
|
|
973
|
-
})) : t.body, r = await
|
|
1361
|
+
})) : t.body, r = await p.runPromise(e.provider.normalizeEvent(n));
|
|
974
1362
|
if (r === null) return;
|
|
975
|
-
await
|
|
1363
|
+
await p.runPromise(e.service.applyEvent(r));
|
|
976
1364
|
let i = e.onEvent?.[r._tag];
|
|
977
|
-
i !== void 0 && await
|
|
978
|
-
|
|
1365
|
+
i !== void 0 && await p.runPromise(i(r).pipe(p.catchAllCause((e) => p.sync(() => {
|
|
1366
|
+
It.warn("billing onEvent side effect failed", {
|
|
979
1367
|
event: r._tag,
|
|
980
1368
|
cause: String(e)
|
|
981
1369
|
});
|
|
982
1370
|
}))));
|
|
983
1371
|
}
|
|
984
|
-
}),
|
|
1372
|
+
}), zt = (e) => k(Rt(e), {
|
|
985
1373
|
resolveSecret: () => Promise.resolve(e.webhookSecret),
|
|
986
1374
|
...e.idempotencyCache === void 0 ? {} : { idempotencyCache: e.idempotencyCache },
|
|
987
1375
|
log: (e) => {
|
|
988
|
-
e.status >= 400 &&
|
|
1376
|
+
e.status >= 400 && It.warn("billing webhook rejected", { ...e });
|
|
989
1377
|
}
|
|
990
|
-
}),
|
|
1378
|
+
}), Bt = (e) => e === null ? null : {
|
|
991
1379
|
plan: e.plan,
|
|
992
1380
|
status: e.status,
|
|
993
1381
|
quantity: e.quantity,
|
|
994
1382
|
currentPeriodEnd: e.currentPeriodEnd ? e.currentPeriodEnd.toISOString() : null,
|
|
995
1383
|
cancelAt: e.cancelAt ? e.cancelAt.toISOString() : null
|
|
996
|
-
},
|
|
1384
|
+
}, Vt = g([{
|
|
997
1385
|
name: "STRIPE_SECRET_KEY",
|
|
998
1386
|
required: !1,
|
|
999
1387
|
secret: !0,
|
|
@@ -1003,40 +1391,50 @@ var A = (e, t, n, r) => ({
|
|
|
1003
1391
|
required: !1,
|
|
1004
1392
|
secret: !0,
|
|
1005
1393
|
description: "Stripe webhook signing secret (whsec_…) for verifying webhook payloads."
|
|
1006
|
-
}]),
|
|
1007
|
-
let n = process.env,
|
|
1394
|
+
}]), Ht = "@voltro/plugin-billing", Ut = (t = {}) => {
|
|
1395
|
+
let n = process.env, m = pt(t.plans ?? {}), h = Ft({
|
|
1008
1396
|
...t.provider === void 0 ? {} : { provider: t.provider },
|
|
1009
1397
|
...t.apiKey === void 0 ? {} : { apiKey: t.apiKey },
|
|
1010
|
-
planForPriceId:
|
|
1398
|
+
planForPriceId: m.planForPriceId,
|
|
1011
1399
|
env: n
|
|
1012
|
-
}),
|
|
1013
|
-
|
|
1400
|
+
}), ne = t.webhookSecret ?? Vt.read("STRIPE_WEBHOOK_SECRET") ?? null, g = be(t.dunning, n);
|
|
1401
|
+
S({
|
|
1402
|
+
source: "plugin",
|
|
1014
1403
|
table: L,
|
|
1015
1404
|
timeColumn: "updatedAt",
|
|
1016
|
-
ttlMs:
|
|
1405
|
+
ttlMs: C(process.env.VOLTRO_BILLING_USAGE_TTL_HOURS, 24 * 400),
|
|
1017
1406
|
label: "billing usage counters"
|
|
1018
|
-
}),
|
|
1019
|
-
|
|
1407
|
+
}), S({
|
|
1408
|
+
source: "plugin",
|
|
1409
|
+
table: Y,
|
|
1020
1410
|
timeColumn: "claimedAt",
|
|
1021
|
-
ttlMs:
|
|
1411
|
+
ttlMs: C(process.env.VOLTRO_BILLING_FLUSH_CLAIM_TTL_HOURS, 1),
|
|
1022
1412
|
label: "billing flush claims"
|
|
1413
|
+
}), S({
|
|
1414
|
+
source: "plugin",
|
|
1415
|
+
table: R,
|
|
1416
|
+
timeColumn: "sentAt",
|
|
1417
|
+
ttlMs: C(process.env.VOLTRO_BILLING_DUNNING_TTL_HOURS, 24 * 400),
|
|
1418
|
+
label: "billing dunning notices"
|
|
1023
1419
|
});
|
|
1024
|
-
let { service:
|
|
1025
|
-
provider:
|
|
1026
|
-
plans:
|
|
1420
|
+
let { service: _, holder: v } = Je({
|
|
1421
|
+
provider: h,
|
|
1422
|
+
plans: m,
|
|
1027
1423
|
...t.attempts === void 0 ? {} : { attempts: t.attempts },
|
|
1028
1424
|
...t.checkout === void 0 ? {} : { checkout: t.checkout },
|
|
1029
|
-
...t.resolveEntitlementLimit === void 0 ? {} : { resolveEntitlementLimit: t.resolveEntitlementLimit }
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1425
|
+
...t.resolveEntitlementLimit === void 0 ? {} : { resolveEntitlementLimit: t.resolveEntitlementLimit },
|
|
1426
|
+
dunning: g
|
|
1427
|
+
}), y = Ye(_), b = zt({
|
|
1428
|
+
provider: h,
|
|
1429
|
+
service: _,
|
|
1430
|
+
webhookSecret: ne,
|
|
1034
1431
|
...t.onEvent === void 0 ? {} : { onEvent: t.onEvent }
|
|
1035
|
-
}),
|
|
1432
|
+
}), x = {
|
|
1036
1433
|
method: "POST",
|
|
1037
|
-
path:
|
|
1434
|
+
path: $,
|
|
1435
|
+
originGuard: "exempt",
|
|
1038
1436
|
handle: async (e) => {
|
|
1039
|
-
let t = await
|
|
1437
|
+
let t = await b({
|
|
1040
1438
|
method: e.method,
|
|
1041
1439
|
path: e.path,
|
|
1042
1440
|
headers: e.headers,
|
|
@@ -1049,12 +1447,12 @@ var A = (e, t, n, r) => ({
|
|
|
1049
1447
|
...t.headers === void 0 ? {} : { headers: { ...t.headers } }
|
|
1050
1448
|
};
|
|
1051
1449
|
}
|
|
1052
|
-
},
|
|
1450
|
+
}, w = [
|
|
1053
1451
|
{
|
|
1054
1452
|
...ee,
|
|
1055
1453
|
execute: (e, t) => {
|
|
1056
1454
|
let n = e;
|
|
1057
|
-
return
|
|
1455
|
+
return _.startCheckout({
|
|
1058
1456
|
tenantId: t.request.subject.tenantId ?? "",
|
|
1059
1457
|
plan: n.plan,
|
|
1060
1458
|
successUrl: n.successUrl,
|
|
@@ -1064,24 +1462,37 @@ var A = (e, t, n, r) => ({
|
|
|
1064
1462
|
}
|
|
1065
1463
|
},
|
|
1066
1464
|
{
|
|
1067
|
-
...
|
|
1465
|
+
...u,
|
|
1068
1466
|
execute: (e, t) => {
|
|
1069
1467
|
let n = e;
|
|
1070
|
-
return
|
|
1468
|
+
return _.portalUrl(t.request.subject.tenantId ?? "", n.returnUrl);
|
|
1071
1469
|
}
|
|
1072
1470
|
},
|
|
1073
1471
|
{
|
|
1074
|
-
...
|
|
1075
|
-
execute: (e, t) =>
|
|
1472
|
+
...te,
|
|
1473
|
+
execute: (e, t) => _.subscription(t.request.subject.tenantId ?? "").pipe(p.map((e) => ({ subscription: Bt(e) })))
|
|
1076
1474
|
},
|
|
1077
1475
|
{
|
|
1078
|
-
...
|
|
1476
|
+
...c,
|
|
1477
|
+
execute: (e, t) => _.entitlementStatus(t.request.subject.tenantId ?? "").pipe(p.map((e) => ({
|
|
1478
|
+
plan: e.plan,
|
|
1479
|
+
billedPlan: e.billedPlan,
|
|
1480
|
+
status: e.status,
|
|
1481
|
+
entitled: e.entitled,
|
|
1482
|
+
inGrace: e.inGrace,
|
|
1483
|
+
graceEndsAt: e.graceEndsAt ? e.graceEndsAt.toISOString() : null,
|
|
1484
|
+
lockedSince: e.lockedSince ? e.lockedSince.toISOString() : null,
|
|
1485
|
+
lockout: e.lockout
|
|
1486
|
+
})))
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
...d,
|
|
1079
1490
|
execute: (e, t) => {
|
|
1080
1491
|
let n = e;
|
|
1081
|
-
return
|
|
1492
|
+
return _.previewChange(t.request.subject.tenantId ?? "", {
|
|
1082
1493
|
...n.plan === void 0 ? {} : { plan: n.plan },
|
|
1083
1494
|
...n.quantity === void 0 ? {} : { quantity: n.quantity }
|
|
1084
|
-
}).pipe(
|
|
1495
|
+
}).pipe(p.map((e) => ({
|
|
1085
1496
|
plan: e.plan,
|
|
1086
1497
|
quantity: e.quantity,
|
|
1087
1498
|
prorationMinor: e.prorationMinor,
|
|
@@ -1090,24 +1501,24 @@ var A = (e, t, n, r) => ({
|
|
|
1090
1501
|
}
|
|
1091
1502
|
},
|
|
1092
1503
|
{
|
|
1093
|
-
...
|
|
1094
|
-
execute: (e, t) =>
|
|
1504
|
+
...l,
|
|
1505
|
+
execute: (e, t) => _.invoices(t.request.subject.tenantId ?? "").pipe(p.map((e) => ({ invoices: e.map((e) => ({
|
|
1095
1506
|
...e,
|
|
1096
1507
|
createdAt: e.createdAt?.toISOString() ?? null
|
|
1097
1508
|
})) })))
|
|
1098
1509
|
},
|
|
1099
1510
|
{
|
|
1100
|
-
...
|
|
1511
|
+
...f,
|
|
1101
1512
|
execute: (e, t) => {
|
|
1102
1513
|
let n = e;
|
|
1103
|
-
return
|
|
1514
|
+
return _.reportUsage(t.request.subject.tenantId ?? "", n.key, n.quantity).pipe(p.map(() => ({ ok: !0 })));
|
|
1104
1515
|
}
|
|
1105
1516
|
},
|
|
1106
1517
|
{
|
|
1107
|
-
...
|
|
1518
|
+
...o,
|
|
1108
1519
|
execute: (e, t) => {
|
|
1109
1520
|
let n = e;
|
|
1110
|
-
return
|
|
1521
|
+
return _.changePlan(t.request.subject.tenantId ?? "", n.plan).pipe(p.map((e) => ({
|
|
1111
1522
|
plan: e.plan,
|
|
1112
1523
|
quantity: e.quantity,
|
|
1113
1524
|
prorationMinor: e.prorationMinor,
|
|
@@ -1116,10 +1527,10 @@ var A = (e, t, n, r) => ({
|
|
|
1116
1527
|
}
|
|
1117
1528
|
},
|
|
1118
1529
|
{
|
|
1119
|
-
...
|
|
1530
|
+
...s,
|
|
1120
1531
|
execute: (e, t) => {
|
|
1121
1532
|
let n = e;
|
|
1122
|
-
return
|
|
1533
|
+
return _.changeSeats(t.request.subject.tenantId ?? "", n.quantity).pipe(p.map((e) => ({
|
|
1123
1534
|
plan: e.plan,
|
|
1124
1535
|
quantity: e.quantity,
|
|
1125
1536
|
prorationMinor: e.prorationMinor,
|
|
@@ -1127,101 +1538,120 @@ var A = (e, t, n, r) => ({
|
|
|
1127
1538
|
})));
|
|
1128
1539
|
}
|
|
1129
1540
|
}
|
|
1130
|
-
],
|
|
1541
|
+
], T = (e, t) => ({
|
|
1131
1542
|
kind: "json",
|
|
1132
1543
|
status: e,
|
|
1133
1544
|
data: { error: t }
|
|
1134
|
-
}),
|
|
1545
|
+
}), E = [{
|
|
1135
1546
|
method: "GET",
|
|
1136
1547
|
path: "/stats",
|
|
1137
1548
|
description: "Billing config: provider, declared plans, enforced tags",
|
|
1138
|
-
handler: () =>
|
|
1549
|
+
handler: () => p.succeed({
|
|
1139
1550
|
kind: "json",
|
|
1140
1551
|
data: {
|
|
1141
|
-
provider:
|
|
1142
|
-
supportsMeteredUsage:
|
|
1143
|
-
plans:
|
|
1552
|
+
provider: h.name,
|
|
1553
|
+
supportsMeteredUsage: h.supportsMeteredUsage,
|
|
1554
|
+
plans: m.planIds(),
|
|
1144
1555
|
enforce: t.enforce ? Object.keys(t.enforce) : [],
|
|
1145
|
-
webhookPath:
|
|
1556
|
+
webhookPath: $
|
|
1146
1557
|
}
|
|
1147
1558
|
})
|
|
1148
1559
|
}, {
|
|
1149
1560
|
method: "GET",
|
|
1150
1561
|
path: "/usage",
|
|
1151
1562
|
description: "Pending (un-flushed) metered usage counters",
|
|
1152
|
-
handler: () =>
|
|
1563
|
+
handler: () => v.stores.usage.pending().pipe(p.map((e) => ({
|
|
1153
1564
|
kind: "json",
|
|
1154
1565
|
data: { usage: e }
|
|
1155
|
-
})),
|
|
1156
|
-
}],
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1566
|
+
})), p.catchAll((e) => p.succeed(T(500, e.message))))
|
|
1567
|
+
}], D = [
|
|
1568
|
+
{
|
|
1569
|
+
schema: e,
|
|
1570
|
+
import: {
|
|
1571
|
+
module: "@voltro/plugin-billing/errors",
|
|
1572
|
+
name: "BillingError"
|
|
1573
|
+
}
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
schema: r,
|
|
1577
|
+
import: {
|
|
1578
|
+
module: "@voltro/plugin-billing/errors",
|
|
1579
|
+
name: "EntitlementExceeded"
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
schema: i,
|
|
1584
|
+
import: {
|
|
1585
|
+
module: "@voltro/plugin-billing/errors",
|
|
1586
|
+
name: "SubscriptionLocked"
|
|
1587
|
+
}
|
|
1167
1588
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
let
|
|
1589
|
+
], O = t.enforce, oe = O !== void 0 && Object.keys(O).length > 0 ? ce(O, _) : void 0, k = t.metering;
|
|
1590
|
+
k !== void 0 && Ze(k);
|
|
1591
|
+
let A = k !== void 0 && Object.keys(k).length > 0 ? ot(_, k) : void 0, j = k !== void 0 && dt(k), M = re([A, oe].filter((e) => e !== void 0)), se, N, P = [
|
|
1171
1592
|
"store:write",
|
|
1172
1593
|
"inspect:read",
|
|
1173
1594
|
"inspect:write",
|
|
1174
1595
|
"network:outbound:*",
|
|
1175
|
-
...
|
|
1176
|
-
...
|
|
1177
|
-
...
|
|
1596
|
+
...M === void 0 ? [] : ["rpc:intercept:mutation", "rpc:intercept:action"],
|
|
1597
|
+
...A === void 0 ? [] : ["rpc:intercept:query"],
|
|
1598
|
+
...j ? ["store:changes:read"] : []
|
|
1178
1599
|
];
|
|
1179
|
-
return
|
|
1180
|
-
name:
|
|
1600
|
+
return ie({
|
|
1601
|
+
name: ae({
|
|
1602
|
+
base: Ht,
|
|
1603
|
+
alias: t.alias,
|
|
1604
|
+
instance: t.name
|
|
1605
|
+
}),
|
|
1606
|
+
baseName: Ht,
|
|
1181
1607
|
description: "Billing — subscriptions, plans, entitlements + usage metering over a pluggable BillingProvider (Stripe + mock). Ships the BillingService, the requireEntitlement() in-handler guard + the enforce-map interceptor, the /billing/webhook receiver (riding @voltro/plugin-webhooks for signature + idempotency), and the _voltro_billing_* tables. Money is integer minor units.",
|
|
1182
1608
|
permissions: P,
|
|
1183
|
-
declaredEnv:
|
|
1184
|
-
services:
|
|
1185
|
-
routes:
|
|
1186
|
-
rpcClientDescriptors:
|
|
1187
|
-
httpRoutes: [
|
|
1188
|
-
inspectEndpoints:
|
|
1189
|
-
extendSchema: { tables: [
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1609
|
+
declaredEnv: Vt.declared,
|
|
1610
|
+
services: y,
|
|
1611
|
+
routes: w,
|
|
1612
|
+
rpcClientDescriptors: a,
|
|
1613
|
+
httpRoutes: [x],
|
|
1614
|
+
inspectEndpoints: E,
|
|
1615
|
+
extendSchema: { tables: [
|
|
1616
|
+
...pe(),
|
|
1617
|
+
ht,
|
|
1618
|
+
me
|
|
1619
|
+
] },
|
|
1620
|
+
errorSchemas: D,
|
|
1621
|
+
...M === void 0 ? {} : {
|
|
1622
|
+
interceptMutation: M,
|
|
1623
|
+
interceptAction: M
|
|
1194
1624
|
},
|
|
1195
|
-
...
|
|
1196
|
-
...
|
|
1625
|
+
...A === void 0 ? {} : { interceptQuery: A },
|
|
1626
|
+
...j ? { onChangeEvent: st(_, k) } : {},
|
|
1197
1627
|
bindDataStore: (e) => {
|
|
1198
|
-
|
|
1628
|
+
v.stores = Ke(e), se = e;
|
|
1199
1629
|
},
|
|
1200
|
-
onActivate: (e) =>
|
|
1630
|
+
onActivate: (e) => p.sync(() => {
|
|
1201
1631
|
e.logger.info("billing active", {
|
|
1202
|
-
provider:
|
|
1203
|
-
plans:
|
|
1204
|
-
enforce:
|
|
1205
|
-
metering:
|
|
1206
|
-
webhook:
|
|
1632
|
+
provider: h.name,
|
|
1633
|
+
plans: m.planIds().length,
|
|
1634
|
+
enforce: O ? Object.keys(O).length : 0,
|
|
1635
|
+
metering: k ? Object.keys(k).length : 0,
|
|
1636
|
+
webhook: $
|
|
1207
1637
|
});
|
|
1208
|
-
let n = t.flushIntervalMs ?? 6e4, r =
|
|
1638
|
+
let n = t.flushIntervalMs ?? 6e4, r = k, i = r !== void 0 && Object.keys(r).length > 0;
|
|
1209
1639
|
if (n > 0) {
|
|
1210
1640
|
let e = process.env.HOSTNAME ?? `pid-${process.pid}`;
|
|
1211
|
-
|
|
1212
|
-
let t =
|
|
1641
|
+
N = setInterval(() => {
|
|
1642
|
+
let t = se;
|
|
1213
1643
|
if (t === void 0) return;
|
|
1214
1644
|
let a = Date.now();
|
|
1215
1645
|
(async () => {
|
|
1216
|
-
await
|
|
1646
|
+
await _t(t, gt(a, n), e, a) && i && await p.runPromise(ut(_, t, r).pipe(p.catchAll(() => p.void)));
|
|
1217
1647
|
})();
|
|
1218
|
-
}, n),
|
|
1648
|
+
}, n), N.unref?.();
|
|
1219
1649
|
}
|
|
1220
1650
|
}),
|
|
1221
|
-
onDeactivate: () =>
|
|
1222
|
-
|
|
1651
|
+
onDeactivate: () => p.sync(() => {
|
|
1652
|
+
N !== void 0 && (clearInterval(N), N = void 0);
|
|
1223
1653
|
})
|
|
1224
1654
|
});
|
|
1225
1655
|
};
|
|
1226
1656
|
//#endregion
|
|
1227
|
-
export { P as BILLING_CUSTOMERS_TABLE,
|
|
1657
|
+
export { P as BILLING_CUSTOMERS_TABLE, R as BILLING_DUNNING_NOTICES_TABLE, Y as BILLING_FLUSH_CLAIMS_TABLE, I as BILLING_INVOICES_TABLE, F as BILLING_SUBSCRIPTIONS_TABLE, L as BILLING_USAGE_TABLE, Lt as BILLING_WEBHOOK_ID, $ as BILLING_WEBHOOK_PATH, e as BillingError, t as BillingService, ge as DEFAULT_DUNNING_STEPS, _e as DEFAULT_GRACE_HOURS, ve as DEFAULT_LOCKOUT, n as DEFAULT_PLAN, he as DUNNING_LOCK_STEP_ID, r as EntitlementExceeded, i as SubscriptionLocked, le as billingCustomersTable, me as billingDunningNoticesTable, ht as billingFlushClaimsTable, de as billingInvoicesTable, Ut as billingPlugin, Ye as billingServiceLayer, ue as billingSubscriptionsTable, pe as billingTables, fe as billingUsageTable, Rt as billingWebhookDescriptor, Je as buildBillingService, ce as buildEnforceInterceptor, pt as buildPlanRegistry, _t as claimFlushWindow, je as dataStoreDunningNoticeStore, Ke as dataStoreStores, Ee as defaultDunningEmail, we as dueSteps, De as dunningMailNotifier, mt as emptyPlanRegistry, Se as episodeKeyOf, z as evaluateBillingEntitlement, j as evaluateEntitlement, gt as flushWindowKey, Ce as graceEndOf, U as isStaleStatusEvent, ke as memoryDunningNoticeStore, Ge as memoryStores, Pt as mockProvider, zt as mountBillingWebhook, kt as normalizeStripeEvent, q as periodKey, N as requireEntitled, se as requireEntitlement, be as resolveDunningConfig, Ft as resolveProvider, jt as stripeProvider };
|