@voltro/plugin-billing 0.86.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/README.md +428 -0
  3. package/THIRD-PARTY-NOTICES.md +1511 -276
  4. package/dist/catalog.js +1 -2
  5. package/dist/checkoutStore.js +164 -0
  6. package/dist/closeStore.js +124 -0
  7. package/dist/custodyInventory.js +154 -0
  8. package/dist/customerStore.js +258 -0
  9. package/dist/dunning.js +1 -2
  10. package/dist/economy.d.ts +30 -0
  11. package/dist/economy.js +265 -0
  12. package/dist/entitlement.js +1 -2
  13. package/dist/eventDelivery.js +71 -0
  14. package/dist/flushClaim.js +1 -2
  15. package/dist/index.d.ts +669 -65
  16. package/dist/index.js +230 -173
  17. package/dist/invoiceHistory.js +90 -0
  18. package/dist/invoiceHistoryJournal.js +475 -0
  19. package/dist/invoiceHistoryProgress.js +234 -0
  20. package/dist/invoiceHistoryWork.js +362 -0
  21. package/dist/invoiceSnapshot.js +65 -0
  22. package/dist/invoiceStore.js +132 -0
  23. package/dist/metering.js +1 -2
  24. package/dist/plans.js +1 -2
  25. package/dist/providerIdentity.js +23 -0
  26. package/dist/providers.js +271 -67
  27. package/dist/provision.js +1 -2
  28. package/dist/rpc.js +1 -2
  29. package/dist/service.js +892 -392
  30. package/dist/stores.js +125 -291
  31. package/dist/stripeClient.js +1 -2
  32. package/dist/stripeIdentity.js +55 -0
  33. package/dist/stripeInvoiceHistory.js +66 -0
  34. package/dist/stripeInvoiceSnapshot.js +85 -0
  35. package/dist/stripeProvider.js +305 -179
  36. package/dist/stripeRest.js +1 -2
  37. package/dist/stripeVerifiedEvent.js +28 -0
  38. package/dist/table.js +70 -48
  39. package/dist/types.d.ts +298 -27
  40. package/dist/types.js +1 -2
  41. package/dist/usageEvents.js +64 -0
  42. package/dist/web/index.js +1 -2
  43. package/dist/webhook.js +32 -40
  44. package/package.json +18 -8
package/dist/stores.js CHANGED
@@ -1,13 +1,22 @@
1
1
  import { BillingError as e } from "./types.js";
2
- import { BILLING_CUSTOMERS_TABLE as t, BILLING_INVOICES_TABLE as n, BILLING_SUBSCRIPTIONS_TABLE as r, BILLING_USAGE_TABLE as i } from "./table.js";
3
- import { dataStoreDunningNoticeStore as a, memoryDunningNoticeStore as o } from "./dunning.js";
4
- import { Effect as s } from "effect";
5
- import { and as c, eq as l } from "@voltro/database";
6
- import { createLogger as u } from "@voltro/logger";
7
- //#region src/stores.ts
8
- var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`, f = u({ scope: "@voltro/plugin-billing" }), p = (t, n) => s.tryPromise({
2
+ import { dataStoreCustomerStore as t } from "./customerStore.js";
3
+ import { dataStoreInvoiceHistoryJournal as n } from "./invoiceHistoryJournal.js";
4
+ import { dataStoreInvoiceStore as r } from "./invoiceStore.js";
5
+ import { dataStoreInvoiceHistoryWorkStore as i } from "./invoiceHistoryWork.js";
6
+ import { dataStoreInvoiceHistoryProgressStore as a } from "./invoiceHistoryProgress.js";
7
+ import { recordDataStoreUsageEvent as o } from "./usageEvents.js";
8
+ import { dataStoreCheckoutOperationStore as s } from "./checkoutStore.js";
9
+ import { dataStoreBillingCloseStore as c } from "./closeStore.js";
10
+ import { dataStoreDunningNoticeStore as l } from "./dunning.js";
11
+ import { BILLING_SUBSCRIPTIONS_TABLE as u, BILLING_USAGE_TABLE as d, billingTables as f } from "./table.js";
12
+ import { Effect as p } from "effect";
13
+ import { _voltroIdempotencyTable as m, and as h, ensureTableRegistered as g, eq as _ } from "@voltro/database";
14
+ import { InMemoryDataStore as v } from "@voltro/runtime";
15
+ import { createLogger as y } from "@voltro/logger";
16
+ // src/stores.ts
17
+ var b = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`, x = y({ scope: "@voltro/plugin-billing" }), S = (t, n) => p.tryPromise({
9
18
  try: t,
10
- catch: (t) => (f.warn("billing store operation failed", {
19
+ catch: (t) => (x.warn("billing store operation failed", {
11
20
  source: n,
12
21
  detail: String(t?.message ?? t)
13
22
  }), new e({
@@ -15,122 +24,19 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
15
24
  message: `${n} failed`,
16
25
  transient: !0
17
26
  }))
18
- }), m = (e) => ({
27
+ }), C = (e) => ({
19
28
  table: e.table,
20
29
  predicate: e.predicate,
21
30
  order: e.order ?? [],
22
31
  take: e.take,
23
32
  skip: void 0,
24
33
  projection: void 0
25
- }), h = () => {
26
- let e = /* @__PURE__ */ new Map();
27
- return {
28
- upsert: (t) => s.sync(() => {
29
- e.set(t.tenantId, t);
30
- }),
31
- getByTenant: (t) => s.sync(() => e.get(t) ?? null),
32
- getByProviderCustomerId: (t) => s.sync(() => [...e.values()].find((e) => e.providerCustomerId === t) ?? null),
33
- list: () => s.sync(() => [...e.values()]),
34
- removeByProviderCustomerId: (t) => s.sync(() => {
35
- for (let [n, r] of e) if (r.providerCustomerId === t) return e.delete(n), n;
36
- return null;
37
- })
38
- };
39
- }, g = (e) => ({
40
- tenantId: String(e.tenantId),
41
- provider: String(e.provider),
42
- providerCustomerId: String(e.providerCustomerId)
43
- }), _ = (e) => ({
44
- upsert: (n) => p(async () => {
45
- let r = await e.query(m({
46
- table: t,
47
- predicate: l("tenantId", n.tenantId),
48
- take: 1
49
- })), i = /* @__PURE__ */ new Date();
50
- r[0] ? await e.update(t, String(r[0].id), {
51
- provider: n.provider,
52
- providerCustomerId: n.providerCustomerId,
53
- updatedAt: i
54
- }) : await e.insert(t, {
55
- id: d("bcust"),
56
- ...n,
57
- createdAt: i,
58
- updatedAt: i
59
- });
60
- }, "customerStore"),
61
- getByTenant: (n) => p(async () => {
62
- let r = await e.query(m({
63
- table: t,
64
- predicate: l("tenantId", n),
65
- take: 1
66
- }));
67
- return r[0] ? g(r[0]) : null;
68
- }, "customerStore"),
69
- getByProviderCustomerId: (n) => p(async () => {
70
- let r = await e.query(m({
71
- table: t,
72
- predicate: l("providerCustomerId", n),
73
- take: 1
74
- }));
75
- return r[0] ? g(r[0]) : null;
76
- }, "customerStore"),
77
- list: () => p(async () => (await e.query(m({ table: t }))).map(g), "customerStore"),
78
- removeByProviderCustomerId: (n) => p(async () => {
79
- let r = await e.query(m({
80
- table: t,
81
- predicate: l("providerCustomerId", n)
82
- })), i = null;
83
- for (let n of r) i = String(n.tenantId), await e.delete(t, String(n.id));
84
- return i;
85
- }, "customerStore")
86
- }), v = (e) => e === "canceled", y = /* @__PURE__ */ new Set([
34
+ }), w = (e) => e === "canceled", T = /* @__PURE__ */ new Set([
87
35
  "active",
88
36
  "trialing",
89
37
  "pastDue",
90
38
  "incomplete"
91
- ]), b = (e) => y.has(e), x = (e) => e.find((e) => b(e.status)) ?? e[0] ?? null, S = (e, t) => e === null ? !1 : t === null || t.getTime() < e.getTime(), C = () => {
92
- let e = /* @__PURE__ */ new Map(), t = 0, n = (t) => [...e.values()].filter((e) => e.tenantId === t).sort((e, t) => t.touched - e.touched), r = ({ touched: e, ...t }) => t;
93
- return {
94
- upsert: (n) => s.sync(() => {
95
- let r = e.get(n.providerSubscriptionId);
96
- r && S(r.statusEventAt, n.statusEventAt) || r && v(r.status) && !v(n.status) || e.set(n.providerSubscriptionId, {
97
- ...n,
98
- pastDueSince: r ? r.pastDueSince : n.pastDueSince,
99
- touched: ++t
100
- });
101
- }),
102
- getByTenant: (e) => s.sync(() => {
103
- let t = x(n(e));
104
- return t ? r(t) : null;
105
- }),
106
- listByTenant: (e) => s.sync(() => n(e).map(r)),
107
- listByStatus: (t) => s.sync(() => [...e.values()].filter((e) => e.status === t).map(r)),
108
- markCanceled: (n) => s.sync(() => {
109
- let r = e.get(n);
110
- r && e.set(n, {
111
- ...r,
112
- status: "canceled",
113
- touched: ++t
114
- });
115
- }),
116
- patchByTenant: (r, i) => s.sync(() => {
117
- let a = x(n(r));
118
- a && e.set(a.providerSubscriptionId, {
119
- ...a,
120
- ...i,
121
- touched: ++t
122
- });
123
- }),
124
- setStatus: (n, r) => s.sync(() => {
125
- let i = e.get(n);
126
- i && e.set(n, {
127
- ...i,
128
- status: r,
129
- touched: ++t
130
- });
131
- })
132
- };
133
- }, w = (e) => ({
39
+ ]), E = (e) => T.has(e), D = (e) => e.find((e) => E(e.status)) ?? e[0] ?? null, O = (e, t) => e === null ? !1 : t === null || t.getTime() < e.getTime(), k = (e) => ({
134
40
  tenantId: String(e.tenantId),
135
41
  provider: String(e.provider),
136
42
  providerSubscriptionId: String(e.providerSubscriptionId),
@@ -142,23 +48,26 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
142
48
  cancelAt: e.cancelAt == null ? null : new Date(e.cancelAt),
143
49
  pastDueSince: e.pastDueSince == null ? null : new Date(e.pastDueSince),
144
50
  statusEventAt: e.statusEventAt == null ? null : new Date(e.statusEventAt)
145
- }), T = async (e, t) => (await e.query(m({
51
+ }), A = async (e, t) => (await e.query(C({
146
52
  table: "_voltro_billing_subscriptions",
147
- predicate: l("providerSubscriptionId", t),
53
+ predicate: _("providerSubscriptionId", t),
148
54
  take: 1
149
- })))[0] ?? null, E = async (e, t) => (await e.query(m({
150
- table: r,
151
- predicate: l("tenantId", t),
55
+ })))[0] ?? null, j = async (e, t) => (await e.query(C({
56
+ table: u,
57
+ predicate: _("tenantId", t),
152
58
  order: [{
153
59
  column: "updatedAt",
154
60
  direction: "desc"
61
+ }, {
62
+ column: "id",
63
+ direction: "desc"
155
64
  }]
156
- }))).map(w), D = (e) => ({
157
- upsert: (t) => p(async () => {
158
- let n = await T(e, t.providerSubscriptionId), i = /* @__PURE__ */ new Date();
65
+ }))).map(k), M = (e) => ({
66
+ upsert: (t) => S(async () => {
67
+ let n = await A(e, t.providerSubscriptionId), r = /* @__PURE__ */ new Date();
159
68
  if (n) {
160
- if (S(n.statusEventAt == null ? null : new Date(n.statusEventAt), t.statusEventAt) || v(String(n.status)) && !v(t.status)) return;
161
- await e.update(r, String(n.id), {
69
+ if (O(n.statusEventAt == null ? null : new Date(n.statusEventAt), t.statusEventAt) || w(String(n.status)) && !w(t.status)) return;
70
+ await e.update(u, String(n.id), {
162
71
  tenantId: t.tenantId,
163
72
  provider: t.provider,
164
73
  plan: t.plan,
@@ -168,10 +77,10 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
168
77
  currentPeriodEnd: t.currentPeriodEnd,
169
78
  cancelAt: t.cancelAt,
170
79
  statusEventAt: t.statusEventAt,
171
- updatedAt: i
80
+ updatedAt: r
172
81
  });
173
- } else await e.insert(r, {
174
- id: d("bsub"),
82
+ } else await e.insert(u, {
83
+ id: b("bsub"),
175
84
  tenantId: t.tenantId,
176
85
  provider: t.provider,
177
86
  providerSubscriptionId: t.providerSubscriptionId,
@@ -183,30 +92,30 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
183
92
  cancelAt: t.cancelAt,
184
93
  pastDueSince: null,
185
94
  statusEventAt: t.statusEventAt,
186
- createdAt: i,
187
- updatedAt: i
95
+ createdAt: r,
96
+ updatedAt: r
188
97
  });
189
98
  }, "subscriptionStore"),
190
- getByTenant: (t) => p(async () => x(await E(e, t)), "subscriptionStore"),
191
- listByTenant: (t) => p(() => E(e, t), "subscriptionStore"),
192
- listByStatus: (t) => p(async () => (await e.query(m({
193
- table: r,
194
- predicate: l("status", t),
99
+ getByTenant: (t) => S(async () => D(await j(e, t)), "subscriptionStore"),
100
+ listByTenant: (t) => S(() => j(e, t), "subscriptionStore"),
101
+ listByStatus: (t) => S(async () => (await e.query(C({
102
+ table: u,
103
+ predicate: _("status", t),
195
104
  order: [{
196
105
  column: "updatedAt",
197
106
  direction: "asc"
198
107
  }]
199
- }))).map(w), "subscriptionStore"),
200
- markCanceled: (t) => p(async () => {
201
- let n = await T(e, t);
202
- n && await e.update(r, String(n.id), {
108
+ }))).map(k), "subscriptionStore"),
109
+ markCanceled: (t) => S(async () => {
110
+ let n = await A(e, t);
111
+ n && await e.update(u, String(n.id), {
203
112
  status: "canceled",
204
113
  updatedAt: /* @__PURE__ */ new Date()
205
114
  });
206
115
  }, "subscriptionStore"),
207
- patchByTenant: (t, n) => p(async () => {
208
- let i = x(await E(e, t)), a = i === null ? null : await T(e, i.providerSubscriptionId);
209
- a && await e.update(r, String(a.id), {
116
+ patchByTenant: (t, n) => S(async () => {
117
+ let r = D(await j(e, t)), i = r === null ? null : await A(e, r.providerSubscriptionId);
118
+ i && await e.update(u, String(i.id), {
210
119
  ...n.plan === void 0 ? {} : { plan: n.plan },
211
120
  ...n.quantity === void 0 ? {} : { quantity: n.quantity },
212
121
  ...n.status === void 0 ? {} : { status: n.status },
@@ -217,142 +126,66 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
217
126
  updatedAt: /* @__PURE__ */ new Date()
218
127
  });
219
128
  }, "subscriptionStore"),
220
- setStatus: (t, n) => p(async () => {
221
- let i = await T(e, t);
222
- i && await e.update(r, String(i.id), {
129
+ setStatus: (t, n) => S(async () => {
130
+ let r = await A(e, t);
131
+ r && await e.update(u, String(r.id), {
223
132
  status: n,
224
133
  updatedAt: /* @__PURE__ */ new Date()
225
134
  });
226
135
  }, "subscriptionStore")
227
- }), O = () => {
228
- let e = /* @__PURE__ */ new Map();
229
- return {
230
- upsert: (t) => s.sync(() => {
231
- let n = e.get(t.providerInvoiceId);
232
- n && S(n.statusEventAt, t.statusEventAt) || e.set(t.providerInvoiceId, t);
233
- }),
234
- listByTenant: (t) => s.sync(() => [...e.values()].filter((e) => e.tenantId === t))
235
- };
236
- }, k = (e) => ({
237
- tenantId: String(e.tenantId),
238
- provider: String(e.provider),
239
- providerInvoiceId: String(e.providerInvoiceId),
240
- amountMinor: Number(e.amountMinor),
241
- currency: String(e.currency),
242
- status: String(e.status),
243
- statusEventAt: e.statusEventAt == null ? null : new Date(e.statusEventAt)
244
- }), A = (e) => ({
245
- upsert: (t) => p(async () => {
246
- let r = (await e.query(m({
247
- table: n,
248
- predicate: l("providerInvoiceId", t.providerInvoiceId),
249
- take: 1
250
- })))[0];
251
- if (r) {
252
- if (S(r.statusEventAt == null ? null : new Date(r.statusEventAt), t.statusEventAt)) return;
253
- await e.update(n, String(r.id), {
254
- amountMinor: t.amountMinor,
255
- currency: t.currency,
256
- status: t.status,
257
- statusEventAt: t.statusEventAt
258
- });
259
- } else await e.insert(n, {
260
- id: d("binv"),
261
- ...t,
262
- createdAt: /* @__PURE__ */ new Date()
263
- });
264
- }, "invoiceStore"),
265
- listByTenant: (t) => p(async () => (await e.query(m({
266
- table: n,
267
- predicate: l("tenantId", t),
268
- order: [{
269
- column: "createdAt",
270
- direction: "desc"
271
- }]
272
- }))).map(k), "invoiceStore")
273
- }), j = (e, t, n) => `${e}\u0000${t}\u0000${n}`, M = () => {
274
- let e = /* @__PURE__ */ new Map();
275
- return {
276
- used: (t, n, r) => s.sync(() => e.get(j(t, n, r))?.used ?? 0),
277
- consume: (t, n, r, i, a) => s.sync(() => {
278
- let o = j(t, n, r), s = e.get(o), c = s?.used ?? 0, l = c + i <= a;
279
- return l && e.set(o, {
280
- tenantId: t,
281
- entitlementKey: n,
282
- period: r,
283
- used: c + i,
284
- reportedToProvider: s?.reportedToProvider ?? 0
285
- }), {
286
- allowed: l,
287
- entitlement: n,
288
- limit: a,
289
- used: c,
290
- cost: i
291
- };
292
- }),
293
- increment: (t, n, r, i) => s.sync(() => {
294
- let a = j(t, n, r), o = e.get(a), s = (o?.used ?? 0) + i;
295
- return e.set(a, {
296
- tenantId: t,
297
- entitlementKey: n,
298
- period: r,
299
- used: s,
300
- reportedToProvider: o?.reportedToProvider ?? 0
301
- }), s;
302
- }),
303
- pending: () => s.sync(() => [...e.values()].filter((e) => e.used > e.reportedToProvider)),
304
- markReported: (t, n, r, i) => s.sync(() => {
305
- let a = j(t, n, r), o = e.get(a);
306
- o && e.set(a, {
307
- ...o,
308
- reportedToProvider: i
309
- });
310
- })
311
- };
312
- }, N = (e) => ({
136
+ }), N = (e) => ({
313
137
  tenantId: String(e.tenantId),
314
138
  entitlementKey: String(e.entitlementKey),
315
139
  period: String(e.period),
316
140
  used: Number(e.used ?? 0),
317
141
  reportedToProvider: Number(e.reportedToProvider ?? 0)
318
- }), P = async (e, t, n, r) => (await e.query(m({
142
+ }), P = async (e, t, n, r) => (await e.query(C({
319
143
  table: "_voltro_billing_usage",
320
- predicate: c(l("tenantId", t), l("entitlementKey", n), l("period", r)),
144
+ predicate: h(_("tenantId", t), _("entitlementKey", n), _("period", r)),
321
145
  take: 1
322
- })))[0] ?? null, F = (e) => ({
323
- used: (t, n, r) => p(async () => {
324
- let i = await P(e, t, n, r);
146
+ })))[0] ?? null, F = (t) => ({
147
+ recordEvent: (n) => o(t, n, async (t, n) => {
148
+ let r = F(t), i = await p.runPromise(r.used(n.tenantId, n.entitlementKey, n.period));
149
+ if (!Number.isSafeInteger(i + n.quantity)) throw new e({
150
+ source: "usageEvent",
151
+ message: "Usage total exceeds exact integer storage.",
152
+ transient: !1
153
+ });
154
+ return p.runPromise(r.increment(n.tenantId, n.entitlementKey, n.period, n.quantity));
155
+ }),
156
+ used: (e, n, r) => S(async () => {
157
+ let i = await P(t, e, n, r);
325
158
  return i ? Number(i.used ?? 0) : 0;
326
159
  }, "usageStore"),
327
- consume: (t, n, r, a, o) => p(async () => {
328
- for (let s = 0; s < 32; s++) {
329
- s > 0 && await new Promise((e) => setTimeout(e, s));
330
- let u = await P(e, t, n, r), f = u ? Number(u.used ?? 0) : 0;
331
- if (f + a > o) return {
160
+ consume: (e, n, r, i, a) => S(async () => {
161
+ for (let o = 0; o < 32; o++) {
162
+ o > 0 && await new Promise((e) => setTimeout(e, o));
163
+ let s = await P(t, e, n, r), c = s ? Number(s.used ?? 0) : 0;
164
+ if (c + i > a) return {
332
165
  allowed: !1,
333
166
  entitlement: n,
334
- limit: o,
335
- used: f,
336
- cost: a
167
+ limit: a,
168
+ used: c,
169
+ cost: i
337
170
  };
338
- if (u) {
339
- if (await e.updateMany("_voltro_billing_usage", {
340
- used: f + a,
171
+ if (s) {
172
+ if (await t.updateMany("_voltro_billing_usage", {
173
+ used: c + i,
341
174
  updatedAt: /* @__PURE__ */ new Date()
342
- }, { where: c(l("id", String(u.id)), l("used", f)) }) === 1) return {
175
+ }, { where: h(_("id", String(s.id)), _("used", c)) }) === 1) return {
343
176
  allowed: !0,
344
177
  entitlement: n,
345
- limit: o,
346
- used: f,
347
- cost: a
178
+ limit: a,
179
+ used: c,
180
+ cost: i
348
181
  };
349
182
  } else {
350
- let s = d("buse"), c = await e.insertIgnore(i, {
351
- id: s,
352
- tenantId: t,
183
+ let o = b("buse"), s = await t.insertIgnore(d, {
184
+ id: o,
185
+ tenantId: e,
353
186
  entitlementKey: n,
354
187
  period: r,
355
- used: a,
188
+ used: i,
356
189
  reportedToProvider: 0,
357
190
  updatedAt: /* @__PURE__ */ new Date()
358
191
  }, { conflictColumns: [
@@ -360,34 +193,34 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
360
193
  "entitlementKey",
361
194
  "period"
362
195
  ] });
363
- if (String(c.id) === s) return {
196
+ if (String(s.id) === o) return {
364
197
  allowed: !0,
365
198
  entitlement: n,
366
- limit: o,
199
+ limit: a,
367
200
  used: 0,
368
- cost: a
201
+ cost: i
369
202
  };
370
203
  }
371
204
  }
372
- throw Error(`billing usage consume: contention on (${t}, ${n}, ${r}) exceeded 32 attempts`);
205
+ throw Error(`billing usage consume: contention on (${e}, ${n}, ${r}) exceeded 32 attempts`);
373
206
  }, "usageStore"),
374
- increment: (t, n, r, a) => p(async () => {
375
- for (let o = 0; o < 32; o++) {
376
- o > 0 && await new Promise((e) => setTimeout(e, o));
377
- let s = await P(e, t, n, r);
378
- if (s) {
379
- let t = Number(s.used ?? 0);
380
- if (await e.updateMany("_voltro_billing_usage", {
381
- used: t + a,
207
+ increment: (e, n, r, i) => S(async () => {
208
+ for (let a = 0; a < 32; a++) {
209
+ a > 0 && await new Promise((e) => setTimeout(e, a));
210
+ let o = await P(t, e, n, r);
211
+ if (o) {
212
+ let e = Number(o.used ?? 0);
213
+ if (await t.updateMany("_voltro_billing_usage", {
214
+ used: e + i,
382
215
  updatedAt: /* @__PURE__ */ new Date()
383
- }, { where: c(l("id", String(s.id)), l("used", t)) }) === 1) return t + a;
216
+ }, { where: h(_("id", String(o.id)), _("used", e)) }) === 1) return e + i;
384
217
  } else {
385
- let o = d("buse"), s = await e.insertIgnore(i, {
386
- id: o,
387
- tenantId: t,
218
+ let a = b("buse"), o = await t.insertIgnore(d, {
219
+ id: a,
220
+ tenantId: e,
388
221
  entitlementKey: n,
389
222
  period: r,
390
- used: a,
223
+ used: i,
391
224
  reportedToProvider: 0,
392
225
  updatedAt: /* @__PURE__ */ new Date()
393
226
  }, { conflictColumns: [
@@ -395,31 +228,32 @@ var d = (e) => `${e}_${Math.random().toString(36).slice(2)}${Date.now().toString
395
228
  "entitlementKey",
396
229
  "period"
397
230
  ] });
398
- if (String(s.id) === o) return a;
231
+ if (String(o.id) === a) return i;
399
232
  }
400
233
  }
401
- throw Error(`billing usage increment: contention on (${t}, ${n}, ${r}) exceeded 32 attempts`);
234
+ throw Error(`billing usage increment: contention on (${e}, ${n}, ${r}) exceeded 32 attempts`);
402
235
  }, "usageStore"),
403
- pending: () => p(async () => (await e.query(m({ table: i }))).map(N).filter((e) => e.used > e.reportedToProvider), "usageStore"),
404
- markReported: (t, n, r, a) => p(async () => {
405
- let o = await P(e, t, n, r);
406
- o && await e.update(i, String(o.id), {
407
- reportedToProvider: a,
236
+ pending: () => S(async () => (await t.query(C({ table: d }))).map(N).filter((e) => e.used > e.reportedToProvider), "usageStore"),
237
+ markReported: (e, n, r, i) => S(async () => {
238
+ let a = await P(t, e, n, r);
239
+ a && await t.update(d, String(a.id), {
240
+ reportedToProvider: i,
408
241
  updatedAt: /* @__PURE__ */ new Date()
409
242
  });
410
243
  }, "usageStore")
411
- }), I = () => ({
412
- customers: h(),
413
- subscriptions: C(),
414
- invoices: O(),
415
- usage: M(),
416
- dunningNotices: o()
417
- }), L = (e) => ({
418
- customers: _(e),
419
- subscriptions: D(e),
420
- invoices: A(e),
244
+ }), I = () => {
245
+ for (let e of [...f(), m]) g(e);
246
+ return L(new v());
247
+ }, L = (e) => ({
248
+ closures: c(e),
249
+ checkouts: s(e),
250
+ customers: t(e),
251
+ subscriptions: M(e),
252
+ invoices: r(e),
253
+ historyJournal: n(e),
254
+ historyWork: i(e),
255
+ historyProgress: a(e),
421
256
  usage: F(e),
422
- dunningNotices: a(e)
257
+ dunningNotices: l(e)
423
258
  });
424
- //#endregion
425
- export { _ as dataStoreCustomerStore, A as dataStoreInvoiceStore, L as dataStoreStores, D as dataStoreSubscriptionStore, F as dataStoreUsageStore, b as isLive, S as isStaleStatusEvent, v as isTerminal, h as memoryCustomerStore, O as memoryInvoiceStore, I as memoryStores, C as memorySubscriptionStore, M as memoryUsageStore };
259
+ export { L as dataStoreStores, M as dataStoreSubscriptionStore, F as dataStoreUsageStore, E as isLive, O as isStaleStatusEvent, w as isTerminal, I as memoryStores, D as selectCurrentSubscription };
@@ -3,7 +3,7 @@ import { redactStripeKeys as t } from "./stripeRest.js";
3
3
  import { Effect as n } from "effect";
4
4
  import { createLogger as r } from "@voltro/logger";
5
5
  import i from "stripe";
6
- //#region src/stripeClient.ts
6
+ // src/stripeClient.ts
7
7
  var a = r({ scope: "@voltro/plugin-billing" }), o = 3, s = 2e4, c = (e) => e.client ?? new i(e.apiKey, {
8
8
  maxNetworkRetries: o,
9
9
  timeout: s,
@@ -49,5 +49,4 @@ var a = r({ scope: "@voltro/plugin-billing" }), o = 3, s = 2e4, c = (e) => e.cli
49
49
  let n = t.reduce((e, t) => e + (t.quantity ?? 0), 0);
50
50
  return n > 0 ? n : 1;
51
51
  };
52
- //#endregion
53
52
  export { i as Stripe, c as makeStripeClient, m as periodOf, g as quantityOf, d as stripeCall, h as unixToDate };
@@ -0,0 +1,55 @@
1
+ import { captureProviderAccount as e, captureProviderCustomer as t, exactProviderIdentifier as n, providerIdentityError as r, sameProviderAccount as i } from "./providerIdentity.js";
2
+ import { stripeCall as a } from "./stripeClient.js";
3
+ import { Effect as o } from "effect";
4
+ // src/stripeIdentity.ts
5
+ var s = (e) => o.try({
6
+ try: e,
7
+ catch: () => r()
8
+ }), c = (t) => o.gen(function* () {
9
+ let i = yield* a("stripe.accounts.retrieve", () => t.accounts.retrieve(null)), o = yield* s(() => {
10
+ if (!n(i.id)) throw r();
11
+ return i.id;
12
+ }), c = yield* a("stripe.balance.retrieve", () => t.balance.retrieve()), l = yield* s(() => e({
13
+ providerAccountId: o,
14
+ livemode: c.livemode
15
+ })), u = yield* a("stripe.accounts.retrieve", () => t.accounts.retrieve(null));
16
+ return yield* s(() => {
17
+ if (u.id !== o) throw r();
18
+ return l;
19
+ });
20
+ }), l = (l, u) => {
21
+ let d = {
22
+ account: {
23
+ providerAccountId: u?.account?.providerAccountId,
24
+ livemode: u?.account?.livemode
25
+ },
26
+ providerCustomerId: u?.providerCustomerId
27
+ };
28
+ return o.gen(function* () {
29
+ let o = yield* s(() => {
30
+ if (!n(d.providerCustomerId)) throw r();
31
+ return Object.freeze({
32
+ account: e(d.account),
33
+ providerCustomerId: d.providerCustomerId
34
+ });
35
+ }), u = yield* c(l);
36
+ yield* s(() => {
37
+ if (!i(u, o.account)) throw r();
38
+ });
39
+ let f = yield* a("stripe.customers.retrieve", () => l.customers.retrieve(o.providerCustomerId)), p = yield* s(() => {
40
+ let e = f.deleted;
41
+ if (e !== void 0 && typeof e != "boolean" || f.id !== o.providerCustomerId || f.deleted !== !0 && f.livemode !== o.account.livemode) throw r();
42
+ return f.deleted === !0;
43
+ }), m = yield* c(l);
44
+ return yield* s(() => {
45
+ if (!i(m, o.account)) throw r();
46
+ return t({
47
+ account: o.account,
48
+ providerCustomerId: o.providerCustomerId,
49
+ deleted: p,
50
+ observedAt: /* @__PURE__ */ new Date()
51
+ });
52
+ });
53
+ });
54
+ };
55
+ export { c as stripeAccountIdentity, l as stripeCustomerSnapshot };
@@ -0,0 +1,66 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { sameProviderAccount as t } from "./providerIdentity.js";
3
+ import { captureInvoiceHistoryInput as n, captureInvoiceHistoryPage as r, historyError as i } from "./invoiceHistory.js";
4
+ import { stripeCall as a } from "./stripeClient.js";
5
+ import { stripeAccountIdentity as o } from "./stripeIdentity.js";
6
+ import { Effect as s } from "effect";
7
+ // src/stripeInvoiceHistory.ts
8
+ var c = (e, c) => s.gen(function* () {
9
+ let f = yield* d(() => n(c)), p = yield* o(e);
10
+ if (!t(p, f.account)) return yield* s.fail(i("The provider account differs before invoice history access."));
11
+ let m = f.cursor;
12
+ if (m !== void 0) {
13
+ let t = yield* a("stripe.invoices.retrieve", () => e.invoices.retrieve(m.afterInvoiceId));
14
+ yield* d(() => {
15
+ if (t.id !== f.cursor?.afterInvoiceId || l(t.customer) !== f.providerCustomerId || t.livemode !== f.account.livemode) throw i("The invoice cursor belongs to another customer/account.");
16
+ });
17
+ }
18
+ let h = yield* a("stripe.invoices.list", () => e.invoices.list({
19
+ customer: f.providerCustomerId,
20
+ limit: f.limit,
21
+ ...f.cursor === void 0 ? {} : { starting_after: f.cursor.afterInvoiceId }
22
+ })), g = yield* d(() => {
23
+ if (!Array.isArray(h.data) || h.data.length > f.limit || typeof h.has_more != "boolean" || h.has_more && h.data.length === 0) throw i("The provider invoice page is incomplete.");
24
+ let e = h.data.map((e) => {
25
+ let t = u(e.status);
26
+ if (l(e.customer) !== f.providerCustomerId || e.livemode !== f.account.livemode || !Number.isSafeInteger(e.created) || !Number.isSafeInteger(e.created * 1e3)) throw i();
27
+ return {
28
+ providerInvoiceId: e.id,
29
+ providerCustomerId: f.providerCustomerId,
30
+ livemode: e.livemode,
31
+ amountMinor: e.amount_due,
32
+ currency: e.currency,
33
+ status: t,
34
+ createdAt: /* @__PURE__ */ new Date(e.created * 1e3),
35
+ hostedUrl: e.hosted_invoice_url ?? null,
36
+ pdfUrl: e.invoice_pdf ?? null
37
+ };
38
+ }), t = e[e.length - 1];
39
+ if (h.has_more && t === void 0) throw i();
40
+ return r(f, {
41
+ account: f.account,
42
+ providerCustomerId: f.providerCustomerId,
43
+ invoices: e,
44
+ nextCursor: h.has_more && t !== void 0 ? {
45
+ ...f.account,
46
+ providerCustomerId: f.providerCustomerId,
47
+ afterInvoiceId: t.providerInvoiceId
48
+ } : null,
49
+ observedAt: /* @__PURE__ */ new Date()
50
+ });
51
+ }), _ = yield* o(e);
52
+ return t(_, f.account) ? g : yield* s.fail(i("The provider account changed during invoice history access.", !0));
53
+ }), l = (e) => typeof e == "string" ? e : e?.id ?? null, u = (e) => {
54
+ switch (e) {
55
+ case "draft": return "draft";
56
+ case "open": return "open";
57
+ case "paid": return "paid";
58
+ case "uncollectible": return "uncollectible";
59
+ case "void": return "void";
60
+ default: throw i("The invoice history status is unsupported.");
61
+ }
62
+ }, d = (t) => s.try({
63
+ try: t,
64
+ catch: (t) => t instanceof e ? t : i()
65
+ });
66
+ export { c as stripeInvoiceHistory };