@voltro/plugin-billing 0.86.0 → 0.87.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.
@@ -1,8 +1,31 @@
1
1
  import { BillingError as e } from "./types.js";
2
- import { Stripe as t, makeStripeClient as n, periodOf as r, quantityOf as i, stripeCall as a, unixToDate as o } from "./stripeClient.js";
3
- import { Effect as s } from "effect";
2
+ import { invoiceHistoryPreview as t } from "./invoiceHistory.js";
3
+ import { Stripe as n, makeStripeClient as r, periodOf as i, quantityOf as a, stripeCall as o, unixToDate as s } from "./stripeClient.js";
4
+ import { stripeInvoiceSnapshot as c } from "./stripeInvoiceSnapshot.js";
5
+ import { stripeAccountIdentity as l, stripeCustomerSnapshot as u } from "./stripeIdentity.js";
6
+ import { stripeInvoiceHistory as d } from "./stripeInvoiceHistory.js";
7
+ import { verifyStripeBillingEvent as f } from "./stripeVerifiedEvent.js";
8
+ import { Effect as p } from "effect";
4
9
  //#region src/stripeProvider.ts
5
- var c = {
10
+ var m = async (e) => {
11
+ let t = e ?? globalThis.crypto.randomUUID();
12
+ return "voltro-billing-" + [...new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(t))).slice(0, 8)].map((e) => String.fromCharCode(97 + e % 26)).join("");
13
+ }, h = (e) => {
14
+ let t = e.status === "open" ? "open" : e.status === "complete" ? "complete" : e.status === "expired" ? "expired" : null;
15
+ if (t === null) throw Error("Checkout provider status is unavailable.");
16
+ let n = /* @__PURE__ */ new Date(e.expires_at * 1e3);
17
+ if (!Number.isFinite(n.getTime())) throw Error("Checkout provider deadline is invalid.");
18
+ return {
19
+ id: e.id,
20
+ operationId: e.metadata?.voltroBillingOperation ?? null,
21
+ tenantId: e.client_reference_id,
22
+ providerCustomerId: typeof e.customer == "string" ? e.customer : e.customer?.id ?? null,
23
+ providerSubscriptionId: typeof e.subscription == "string" ? e.subscription : e.subscription?.id ?? null,
24
+ status: t,
25
+ url: e.url,
26
+ expiresAt: n
27
+ };
28
+ }, g = {
6
29
  active: "active",
7
30
  trialing: "trialing",
8
31
  past_due: "pastDue",
@@ -11,23 +34,23 @@ var c = {
11
34
  incomplete: "incomplete",
12
35
  incomplete_expired: "canceled",
13
36
  paused: "pastDue"
14
- }, l = (e) => {
37
+ }, _ = (e) => {
15
38
  let t = e.metadata?.tenantId;
16
39
  if (typeof t == "string" && t.length > 0) return t;
17
40
  let n = e.client_reference_id;
18
41
  return typeof n == "string" && n.length > 0 ? n : null;
19
- }, u = (e) => {
42
+ }, v = (e) => {
20
43
  let t = e.items?.data ?? [];
21
44
  return t.find((e) => e.price?.recurring?.usage_type !== "metered") ?? t[0];
22
- }, d = (e) => {
23
- let t = u(e)?.price;
45
+ }, y = (e) => {
46
+ let t = v(e)?.price;
24
47
  return typeof t?.id == "string" ? t.id : null;
25
- }, f = (e) => typeof e == "string" ? e : typeof e?.id == "string" ? e.id : null, p = (e, t) => {
26
- let n = d(e), r = n !== null && t ? t(n) : null;
48
+ }, b = (e) => typeof e == "string" ? e : typeof e?.id == "string" ? e.id : null, x = (e, t) => {
49
+ let n = y(e), r = n !== null && t ? t(n) : null;
27
50
  if (r !== null) return r;
28
51
  let i = e.metadata?.plan;
29
52
  return typeof i == "string" && i.length > 0 ? i : null;
30
- }, m = [
53
+ }, S = [
31
54
  "checkout.session.completed",
32
55
  "customer.created",
33
56
  "customer.deleted",
@@ -38,184 +61,237 @@ var c = {
38
61
  "customer.subscription.updated",
39
62
  "invoice.paid",
40
63
  "invoice.payment_failed"
41
- ], h = (e, t) => {
42
- let n = o(e.created);
43
- switch (e.type) {
64
+ ], C = (t, n) => {
65
+ let r = s(t.created);
66
+ switch (t.type) {
44
67
  case "customer.subscription.created":
45
68
  case "customer.subscription.updated":
46
69
  case "customer.subscription.resumed":
47
70
  case "customer.subscription.paused": {
48
- let a = e.data.object, s = l(a), u = p(a, t), d = c[a.status];
49
- if (s === null || u === null || d === void 0) return null;
50
- let f = r(a);
71
+ let e = t.data.object, o = _(e), c = x(e, n), l = g[e.status];
72
+ if (o === null || c === null || l === void 0) return null;
73
+ let u = i(e);
51
74
  return {
52
75
  _tag: "subscriptionUpserted",
53
- tenantId: s,
54
- providerSubscriptionId: a.id,
55
- plan: u,
56
- status: d,
57
- quantity: i(a),
58
- currentPeriodStart: f.start,
59
- currentPeriodEnd: f.end,
60
- cancelAt: o(a.cancel_at),
61
- occurredAt: n
76
+ tenantId: o,
77
+ providerSubscriptionId: e.id,
78
+ plan: c,
79
+ status: l,
80
+ quantity: a(e),
81
+ currentPeriodStart: u.start,
82
+ currentPeriodEnd: u.end,
83
+ cancelAt: s(e.cancel_at),
84
+ occurredAt: r
62
85
  };
63
86
  }
64
87
  case "customer.subscription.deleted": {
65
- let t = e.data.object, r = l(t);
66
- return r === null ? null : {
88
+ let e = t.data.object, n = _(e);
89
+ return n === null ? null : {
67
90
  _tag: "subscriptionCanceled",
68
- tenantId: r,
69
- providerSubscriptionId: t.id,
70
- occurredAt: n
91
+ tenantId: n,
92
+ providerSubscriptionId: e.id,
93
+ occurredAt: r
71
94
  };
72
95
  }
73
96
  case "invoice.paid":
74
97
  case "invoice.payment_failed": {
75
- let t = e.data.object, r = l(t) ?? l({ metadata: t.parent?.subscription_details?.metadata ?? null });
76
- return r === null || typeof t.id != "string" ? null : {
77
- _tag: e.type === "invoice.paid" ? "invoicePaid" : "invoicePaymentFailed",
78
- tenantId: r,
79
- providerInvoiceId: t.id,
80
- amountMinor: t.amount_due,
81
- currency: t.currency,
82
- occurredAt: n
98
+ let n = t.data.object, i = D(n);
99
+ if (i === null || i.length === 0 || typeof n.id != "string" || n.id.length === 0) throw new e({
100
+ source: "stripe.normalizeEvent",
101
+ message: "The invoice trigger requires exact customer and invoice identities.",
102
+ transient: !1
103
+ });
104
+ return {
105
+ _tag: t.type === "invoice.paid" ? "invoicePaid" : "invoicePaymentFailed",
106
+ providerCustomerId: i,
107
+ providerInvoiceId: n.id,
108
+ amountMinor: n.amount_due,
109
+ currency: n.currency,
110
+ occurredAt: r
83
111
  };
84
112
  }
85
113
  case "checkout.session.completed": {
86
- let t = e.data.object, r = l(t), i = f(t.customer);
87
- return r === null || i === null ? null : {
114
+ let e = t.data.object, n = _(e), i = b(e.customer);
115
+ return n === null || i === null ? null : {
88
116
  _tag: "customerLinked",
89
- tenantId: r,
117
+ tenantId: n,
90
118
  providerCustomerId: i,
91
- occurredAt: n
119
+ occurredAt: r
92
120
  };
93
121
  }
94
122
  case "customer.deleted": {
95
- let t = e.data.object;
123
+ let e = t.data.object;
96
124
  return {
97
125
  _tag: "customerUnlinked",
98
- tenantId: l(t),
99
- providerCustomerId: t.id,
100
- occurredAt: n
126
+ tenantId: _(e),
127
+ providerCustomerId: e.id,
128
+ occurredAt: r
101
129
  };
102
130
  }
103
131
  case "customer.created": {
104
- let t = e.data.object, r = l(t);
105
- return r === null ? null : {
132
+ let e = t.data.object, n = _(e);
133
+ return n === null ? null : {
106
134
  _tag: "customerLinked",
107
- tenantId: r,
108
- providerCustomerId: t.id,
109
- occurredAt: n
135
+ tenantId: n,
136
+ providerCustomerId: e.id,
137
+ occurredAt: r
110
138
  };
111
139
  }
112
140
  default: return null;
113
141
  }
114
- }, g = {
142
+ }, w = {
115
143
  prorate: "create_prorations",
116
144
  none: "none",
117
145
  always_invoice: "always_invoice"
118
- }, _ = (d) => {
119
- let _ = n(d), S = d.planForPriceId, C = (e) => {
120
- let t = r(e);
146
+ }, T = (y) => {
147
+ let T = r(y), A = y.planForPriceId, j = (e) => {
148
+ let t = i(e);
121
149
  return {
122
- status: c[e.status] ?? "incomplete",
123
- plan: p(e, S),
124
- quantity: i(e),
150
+ status: g[e.status] ?? "incomplete",
151
+ plan: x(e, A),
152
+ quantity: a(e),
125
153
  currentPeriodStart: t.start,
126
154
  currentPeriodEnd: t.end,
127
- cancelAt: o(e.cancel_at)
155
+ cancelAt: s(e.cancel_at)
128
156
  };
129
- }, w = (e) => ({
130
- ...C(e),
157
+ }, M = (e) => ({
158
+ ...j(e),
131
159
  providerSubscriptionId: e.id,
132
- providerCustomerId: f(e.customer),
133
- tenantId: l(e),
160
+ providerCustomerId: b(e.customer),
161
+ tenantId: _(e),
134
162
  priceIds: (e.items?.data ?? []).map((e) => e.price?.id).filter((e) => typeof e == "string")
135
- }), T = d.closeSettleSeconds ?? 600, E = {
136
- periodEnd: (e) => _.subscriptions.update(e, {
163
+ }), N = y.closeSettleSeconds ?? 600;
164
+ if (!Number.isSafeInteger(N) || N < 0) throw TypeError("closeSettleSeconds must be a nonnegative integer.");
165
+ let P = (e) => {
166
+ let t = e.lastResponse.headers.date, n = typeof t == "string" ? Date.parse(t) : NaN;
167
+ if (!Number.isFinite(n)) throw Error("Provider clock evidence is required for billing closure.");
168
+ return n;
169
+ }, F = async (e) => {
170
+ let t = e.providerSubscriptionId;
171
+ if (e.mode === "periodEnd") return T.subscriptions.update(t, {
137
172
  cancel_at_period_end: !0,
138
173
  expand: ["items.data.price"]
139
- }),
140
- close: (e) => _.subscriptions.update(e, {
141
- cancel_at: Math.ceil(Date.now() / 1e3) + T,
174
+ });
175
+ let n = e.closeOperation;
176
+ if (!n.id || !Number.isFinite(n.cancelAt.getTime()) || n.cancelAt.getTime() % 1e3 != 0) throw Error("A retained close operation and provider deadline are required.");
177
+ let r = await T.subscriptions.retrieve(t, { expand: ["items.data.price"] });
178
+ if (r.status === "canceled") return r;
179
+ let i = n.cancelAt.getTime() / 1e3;
180
+ if (r.cancel_at !== null && r.cancel_at <= i) return r;
181
+ let a = "voltro-close:" + n.id;
182
+ return P(r) >= n.cancelAt.getTime() ? T.subscriptions.cancel(t, {
183
+ invoice_now: !0,
184
+ prorate: !0
185
+ }, { idempotencyKey: a + ":cancel" }) : T.subscriptions.update(t, {
186
+ cancel_at: i,
142
187
  proration_behavior: "create_prorations",
143
188
  expand: ["items.data.price"]
144
- })
145
- }, D = (e) => _.subscriptions.retrieve(e, { expand: ["items.data.price"] }), O = (e, t) => {
146
- let n = r(e), a = u(e)?.price;
189
+ }, { idempotencyKey: a + ":schedule" });
190
+ }, I = (e) => T.subscriptions.retrieve(e, { expand: ["items.data.price"] }), L = (e, t) => {
191
+ let n = i(e), r = v(e)?.price;
147
192
  return {
148
- plan: p(e, S) ?? "free",
149
- quantity: i(e),
193
+ plan: x(e, A) ?? "free",
194
+ quantity: a(e),
150
195
  prorationMinor: t,
151
- currency: (a?.currency ?? "usd").toLowerCase(),
196
+ currency: (r?.currency ?? "usd").toLowerCase(),
152
197
  currentPeriodStart: n.start,
153
198
  currentPeriodEnd: n.end
154
199
  };
155
- }, k = (e, t) => {
156
- let n = u(e)?.id;
200
+ }, R = (e, t) => {
201
+ let n = v(e)?.id;
157
202
  return {
158
203
  items: [{
159
204
  ...n === void 0 ? {} : { id: n },
160
205
  ...t.priceId === void 0 ? {} : { price: t.priceId },
161
206
  ...t.quantity === void 0 ? {} : { quantity: t.quantity }
162
207
  }],
163
- proration_behavior: g[t.proration ?? "prorate"],
164
- ...t.priceId !== void 0 && S ? { metadata: {
208
+ proration_behavior: w[t.proration ?? "prorate"],
209
+ ...t.priceId !== void 0 && A ? { metadata: {
165
210
  ...e.metadata,
166
- plan: S(t.priceId) ?? ""
211
+ plan: A(t.priceId) ?? ""
167
212
  } } : {}
168
213
  };
169
214
  };
170
215
  return {
171
216
  name: "stripe",
172
- supportsMeteredUsage: d.supportsMeteredUsage ?? !0,
217
+ fetchAccountIdentity: () => l(T),
218
+ fetchCustomerSnapshot: (e) => u(T, e),
219
+ supportsMeteredUsage: y.supportsMeteredUsage ?? !0,
173
220
  usageRequiresCustomer: !0,
174
- createCheckoutSession: (e) => a("stripe.checkout.sessions.create", async () => {
175
- let t = await _.checkout.sessions.create({
176
- mode: "subscription",
177
- line_items: [{
178
- price: e.priceId,
179
- quantity: e.quantity ?? 1,
180
- ...e.adjustableQuantity ? { adjustable_quantity: {
181
- enabled: !0,
182
- minimum: e.adjustableQuantity.min,
183
- maximum: e.adjustableQuantity.max
184
- } } : {}
185
- }, ...(e.meteredPriceIds ?? []).map((e) => ({ price: e }))],
186
- success_url: e.successUrl,
187
- cancel_url: e.cancelUrl,
188
- client_reference_id: e.tenantId,
221
+ createCheckoutSession: (e) => o("stripe.checkout.sessions.create", async () => h(await T.checkout.sessions.create({
222
+ mode: "subscription",
223
+ integration_identifier: await m(e.idempotencyKey),
224
+ line_items: [{
225
+ price: e.priceId,
226
+ quantity: e.quantity ?? 1,
227
+ ...e.adjustableQuantity ? { adjustable_quantity: {
228
+ enabled: !0,
229
+ minimum: e.adjustableQuantity.min,
230
+ maximum: e.adjustableQuantity.max
231
+ } } : {}
232
+ }, ...(e.meteredPriceIds ?? []).map((e) => ({ price: e }))],
233
+ success_url: e.successUrl,
234
+ cancel_url: e.cancelUrl,
235
+ client_reference_id: e.tenantId,
236
+ metadata: {
237
+ tenantId: e.tenantId,
238
+ ...e.idempotencyKey === void 0 ? {} : { voltroBillingOperation: e.idempotencyKey }
239
+ },
240
+ subscription_data: {
189
241
  metadata: { tenantId: e.tenantId },
190
- subscription_data: {
191
- metadata: { tenantId: e.tenantId },
192
- ...e.trialDays === void 0 ? {} : { trial_period_days: e.trialDays }
193
- },
194
- ...e.providerCustomerId === void 0 ? {} : { customer: e.providerCustomerId },
195
- automatic_tax: { enabled: !0 },
196
- tax_id_collection: { enabled: !0 },
197
- ...e.providerCustomerId === void 0 ? {} : { customer_update: {
198
- name: "auto",
199
- address: "auto"
200
- } },
201
- allow_promotion_codes: !0
202
- });
203
- if (typeof t.url != "string") throw Error("checkout session has no url");
204
- return { url: t.url };
242
+ ...e.trialDays === void 0 ? {} : { trial_period_days: e.trialDays }
243
+ },
244
+ ...e.providerCustomerId === void 0 ? {} : { customer: e.providerCustomerId },
245
+ ...e.expiresAt === void 0 ? {} : { expires_at: Math.floor(e.expiresAt.getTime() / 1e3) },
246
+ automatic_tax: { enabled: !0 },
247
+ tax_id_collection: { enabled: !0 },
248
+ ...e.providerCustomerId === void 0 ? {} : { customer_update: {
249
+ name: "auto",
250
+ address: "auto"
251
+ } },
252
+ allow_promotion_codes: !0
253
+ }, e.idempotencyKey === void 0 ? void 0 : { idempotencyKey: e.idempotencyKey }))),
254
+ findCheckoutSession: (e) => o("stripe.checkout.sessions.find", async () => {
255
+ let t = null, n = 0, r, i = null;
256
+ do {
257
+ let a = await T.checkout.sessions.list({
258
+ ...e.providerCustomerId === void 0 ? {} : { customer: e.providerCustomerId },
259
+ limit: 100,
260
+ ...r === void 0 ? {} : { starting_after: r }
261
+ });
262
+ if (r === void 0) {
263
+ let e = a.lastResponse.headers.date, t = typeof e == "string" ? new Date(e) : null;
264
+ i = t !== null && Number.isFinite(t.getTime()) ? t : null;
265
+ }
266
+ for (let r of a.data) {
267
+ if (++n > 1e4) throw Error("Checkout history is too large to establish an unambiguous operation receipt.");
268
+ if (r.metadata?.voltroBillingOperation !== e.operationId) continue;
269
+ let i = h(r);
270
+ if (i.tenantId !== e.tenantId || e.providerCustomerId !== void 0 && i.providerCustomerId !== e.providerCustomerId || t !== null) throw Error("Checkout receipt does not match a unique tenant operation.");
271
+ t = i;
272
+ }
273
+ if (!a.has_more) return {
274
+ receipt: t,
275
+ observedAt: i
276
+ };
277
+ let o = a.data.at(-1)?.id;
278
+ if (!o || o === r) throw Error("Checkout history did not advance.");
279
+ r = o;
280
+ } while (1);
205
281
  }),
206
- createPortalSession: (e) => a("stripe.billingPortal.sessions.create", async () => ({ url: (await _.billingPortal.sessions.create({
282
+ createPortalSession: (e) => o("stripe.billingPortal.sessions.create", async () => ({ url: (await T.billingPortal.sessions.create({
207
283
  customer: e.providerCustomerId,
208
284
  return_url: e.returnUrl
209
285
  })).url })),
210
286
  reportUsage: (t) => {
211
287
  let n = t.providerCustomerId;
212
- return n === void 0 ? s.fail(new e({
288
+ return n === void 0 ? p.fail(new e({
213
289
  source: "stripe.billing.meterEvents.create",
214
290
  message: `no Stripe customer linked for tenant '${t.tenantId}'`,
215
291
  transient: !1
216
- })) : a("stripe.billing.meterEvents.create", async () => {
292
+ })) : o("stripe.billing.meterEvents.create", async () => {
217
293
  try {
218
- await _.billing.meterEvents.create({
294
+ await T.billing.meterEvents.create({
219
295
  event_name: t.entitlementKey,
220
296
  identifier: t.idempotencyKey ?? `${t.tenantId}:${t.entitlementKey}:${t.periodEnd?.toISOString() ?? "current"}`,
221
297
  payload: {
@@ -224,17 +300,17 @@ var c = {
224
300
  }
225
301
  });
226
302
  } catch (e) {
227
- if (v(e)) return;
303
+ if (E(e)) return;
228
304
  throw e;
229
305
  }
230
306
  });
231
307
  },
232
- updateSubscription: (e) => a("stripe.subscriptions.update", async () => {
233
- let t = await D(e.providerSubscriptionId), n = await _.subscriptions.update(e.providerSubscriptionId, k(t, e)), r = await x(_, n);
234
- return O(n, r);
308
+ updateSubscription: (e) => o("stripe.subscriptions.update", async () => {
309
+ let t = await I(e.providerSubscriptionId), n = await T.subscriptions.update(e.providerSubscriptionId, R(t, e)), r = await k(T, n);
310
+ return L(n, r);
235
311
  }),
236
- previewSubscriptionChange: (e) => a("stripe.invoices.createPreview", async () => {
237
- let t = await D(e.providerSubscriptionId), n = k(t, e), r = (await _.invoices.createPreview({
312
+ previewSubscriptionChange: (e) => o("stripe.invoices.createPreview", async () => {
313
+ let t = await I(e.providerSubscriptionId), n = R(t, e), r = (await T.invoices.createPreview({
238
314
  subscription: e.providerSubscriptionId,
239
315
  subscription_details: {
240
316
  items: n.items,
@@ -242,106 +318,157 @@ var c = {
242
318
  }
243
319
  })).lines.data.filter((e) => e.parent?.subscription_item_details?.proration === !0).reduce((e, t) => e + t.amount, 0);
244
320
  return {
245
- ...O(t, r),
321
+ ...L(t, r),
246
322
  ...e.quantity === void 0 ? {} : { quantity: e.quantity },
247
- ...e.priceId !== void 0 && S ? { plan: S(e.priceId) ?? "free" } : {}
323
+ ...e.priceId !== void 0 && A ? { plan: A(e.priceId) ?? "free" } : {}
248
324
  };
249
325
  }),
250
- fetchSubscription: (e) => a("stripe.subscriptions.retrieve", async () => {
326
+ fetchSubscriptionSnapshot: (e) => o("stripe.subscriptions.retrieve", async () => {
251
327
  try {
252
- return C(await D(e));
328
+ return M(await I(e));
253
329
  } catch (e) {
254
- if (e instanceof t.errors.StripeInvalidRequestError && e.statusCode === 404) return null;
330
+ if (e instanceof n.errors.StripeInvalidRequestError && e.statusCode === 404) return null;
255
331
  throw e;
256
332
  }
257
333
  }),
258
- listSubscriptions: (e) => a("stripe.subscriptions.list", async () => {
334
+ fetchSubscription: (e) => o("stripe.subscriptions.retrieve", async () => {
335
+ try {
336
+ return j(await I(e));
337
+ } catch (e) {
338
+ if (e instanceof n.errors.StripeInvalidRequestError && e.statusCode === 404) return null;
339
+ throw e;
340
+ }
341
+ }),
342
+ listSubscriptions: (e) => o("stripe.subscriptions.list", async () => {
259
343
  let t = [];
260
- for await (let n of _.subscriptions.list({
344
+ for await (let n of T.subscriptions.list({
261
345
  limit: 100,
262
346
  expand: ["data.items.data.price"],
263
347
  ...e.providerCustomerId === void 0 ? {} : { customer: e.providerCustomerId }
264
- })) t.push(w(n));
348
+ })) t.push(M(n));
265
349
  return t;
266
350
  }),
267
- cancelSubscription: (e) => a("stripe.subscriptions.cancel", async () => C(await E[e.mode](e.providerSubscriptionId))),
268
- resumeSubscription: (e) => a("stripe.subscriptions.update", async () => C(await _.subscriptions.update(e, {
351
+ prepareSubscriptionClose: (e) => o("stripe.subscriptions.closeQuote", async () => {
352
+ let t = await T.subscriptions.retrieve(e, { expand: ["items.data.price"] });
353
+ return { cancelAt: new Date(P(t) + N * 1e3) };
354
+ }),
355
+ cancelSubscription: (e) => o("stripe.subscriptions.cancel", async () => j(await F(e))),
356
+ resumeSubscription: (e) => o("stripe.subscriptions.update", async () => j(await T.subscriptions.update(e, {
269
357
  cancel_at_period_end: !1,
270
358
  expand: ["items.data.price"]
271
359
  }))),
272
- ensureCustomer: (e) => a("stripe.customers.create", async () => {
273
- let t = (await _.customers.search({
360
+ ensureCustomer: (e) => o("stripe.customers.create", async () => {
361
+ let t = (await T.customers.search({
274
362
  query: `metadata['tenantId']:'${e.tenantId.replace(/['\\]/g, "")}'`,
275
363
  limit: 2
276
364
  })).data.find((t) => t.metadata?.tenantId === e.tenantId);
277
- return t === void 0 ? (await _.customers.create({ metadata: { tenantId: e.tenantId } }, { idempotencyKey: `voltro-billing-customer:${e.tenantId}` })).id : t.id;
365
+ return t === void 0 ? (await T.customers.create({ metadata: { tenantId: e.tenantId } }, { idempotencyKey: `voltro-billing-customer:${e.tenantId}` })).id : t.id;
278
366
  }),
279
- expireOpenCheckouts: (e) => a("stripe.checkout.sessions.expire", async () => {
367
+ expireOpenCheckouts: (e) => o("stripe.checkout.sessions.expire", async () => {
280
368
  let t = 0;
281
- for await (let n of _.checkout.sessions.list({
369
+ for await (let n of T.checkout.sessions.list({
282
370
  customer: e.providerCustomerId,
283
371
  status: "open",
284
372
  limit: 100
285
- })) await _.checkout.sessions.expire(n.id), t += 1;
373
+ })) (e.exceptOperationId === void 0 || n.metadata?.voltroBillingOperation !== e.exceptOperationId) && (await T.checkout.sessions.expire(n.id), t += 1);
286
374
  return t;
287
375
  }),
288
- addSubscriptionPrices: (e) => a("stripe.subscriptionItems.create", async () => {
289
- for (let t of e.priceIds) await _.subscriptionItems.create({
376
+ addSubscriptionPrices: (e) => o("stripe.subscriptionItems.create", async () => {
377
+ for (let t of e.priceIds) await T.subscriptionItems.create({
290
378
  subscription: e.providerSubscriptionId,
291
379
  price: t,
292
380
  proration_behavior: "none"
293
381
  });
294
382
  }),
295
- customerEmail: (e) => a("stripe.customers.retrieve", async () => {
296
- let t = await _.customers.retrieve(e);
383
+ customerEmail: (e) => o("stripe.customers.retrieve", async () => {
384
+ let t = await T.customers.retrieve(e);
297
385
  return t.deleted === !0 ? null : typeof t.email == "string" && t.email.length > 0 ? t.email : null;
298
386
  }),
299
- listInvoices: (e) => a("stripe.invoices.list", async () => (await _.invoices.list({
300
- customer: e.providerCustomerId,
387
+ fetchInvoiceSnapshot: (e) => c(T, e),
388
+ listInvoiceHistory: (e) => d(T, e),
389
+ listInvoices: (e) => l(T).pipe(p.flatMap((t) => d(T, {
390
+ account: t,
391
+ providerCustomerId: e.providerCustomerId,
301
392
  limit: e.limit ?? 20
302
- })).data.map((e) => ({
303
- providerInvoiceId: e.id ?? "",
304
- amountMinor: e.amount_due,
305
- currency: e.currency,
306
- status: e.status ?? "unknown",
307
- createdAt: o(e.created),
308
- hostedUrl: e.hosted_invoice_url ?? null,
309
- pdfUrl: e.invoice_pdf ?? null
310
- }))),
311
- verifyWebhook: (t) => s.try({
312
- try: () => _.webhooks.constructEvent(Buffer.from(t.rawBody), t.signatureHeader, t.secret),
313
- catch: (t) => new e({
314
- source: "stripe.webhooks.constructEvent",
315
- message: String(t?.message ?? t),
316
- transient: !1
317
- })
318
- }),
319
- eventContext: (e) => b(e) ? {
320
- providerCustomerId: y(e.data.object),
321
- createdAt: o(e.created)
393
+ })), p.map((e) => e.invoices.map(t))),
394
+ verifyWebhook: (e) => f(T, e),
395
+ eventContext: (e) => O(e) ? {
396
+ providerCustomerId: D(e.data.object),
397
+ createdAt: s(e.created)
322
398
  } : {
323
399
  providerCustomerId: null,
324
400
  createdAt: null
325
401
  },
326
- listUndeliveredEvents: () => a("stripe.events.list", async () => {
327
- let e = [];
328
- for await (let t of _.events.list({
402
+ listUndeliveredEvents: (t) => p.gen(function* () {
403
+ yield* p.try({
404
+ try: () => {
405
+ if (!Number.isInteger(t.limit) || t.limit < 1 || t.limit > 100 || t.cursor !== null && (!t.cursor || t.cursor.trim() !== t.cursor)) throw Error("Invalid event page input");
406
+ },
407
+ catch: () => new e({
408
+ source: "stripe.events.list",
409
+ message: "A bounded page and exact cursor are required.",
410
+ transient: !1
411
+ })
412
+ });
413
+ let n = yield* l(T), r = yield* o("stripe.events.list", () => T.events.list({
329
414
  delivery_success: !1,
330
- types: [...m],
331
- limit: 100
332
- })) e.push({
333
- id: t.id,
334
- providerCustomerId: y(t.data.object),
335
- createdAt: o(t.created)
415
+ types: [...S],
416
+ limit: t.limit,
417
+ ...t.cursor === null ? {} : { starting_after: t.cursor }
418
+ }));
419
+ return yield* p.try({
420
+ try: () => {
421
+ if (!Array.isArray(r.data) || r.data.length > t.limit || typeof r.has_more != "boolean" || r.has_more && r.data.length === 0) throw Error("Incomplete event page");
422
+ let e = r.data.map((e) => {
423
+ let t = {
424
+ id: e.id,
425
+ providerCustomerId: D(e.data.object),
426
+ createdAt: s(e.created)
427
+ };
428
+ if (e.livemode !== n.livemode || e.account !== void 0 && e.account !== null && e.account !== n.providerAccountId) return {
429
+ ...t,
430
+ event: null,
431
+ rejected: !0
432
+ };
433
+ try {
434
+ return {
435
+ ...t,
436
+ event: C(e, A)
437
+ };
438
+ } catch {
439
+ return {
440
+ ...t,
441
+ event: null,
442
+ rejected: !0
443
+ };
444
+ }
445
+ }), i = r.has_more ? e.at(-1)?.id ?? null : null;
446
+ if (r.has_more && (i === null || i === t.cursor)) throw Error("Nonadvancing event page");
447
+ return {
448
+ events: e,
449
+ nextCursor: i
450
+ };
451
+ },
452
+ catch: () => new e({
453
+ source: "stripe.events.list",
454
+ message: "The provider event page could not be confirmed.",
455
+ transient: !0
456
+ })
336
457
  });
337
- return e;
338
458
  }),
339
- normalizeEvent: (e) => s.succeed(b(e) ? h(e, S) : null)
459
+ normalizeEvent: (t) => p.try({
460
+ try: () => O(t) ? C(t, A) : null,
461
+ catch: (t) => t instanceof e ? t : new e({
462
+ source: "stripe.normalizeEvent",
463
+ message: "The provider event could not be normalized.",
464
+ transient: !1
465
+ })
466
+ })
340
467
  };
341
- }, v = (e) => e instanceof t.errors.StripeInvalidRequestError && typeof e.message == "string" && e.message.startsWith("An event already exists with identifier"), y = (e) => {
468
+ }, E = (e) => e instanceof n.errors.StripeInvalidRequestError && typeof e.message == "string" && e.message.startsWith("An event already exists with identifier"), D = (e) => {
342
469
  let t = e;
343
- return t.object === "customer" ? typeof t.id == "string" ? t.id : null : f(t.customer ?? null);
344
- }, b = (e) => typeof e == "object" && !!e && typeof e.type == "string" && typeof e.data == "object", x = async (e, t) => {
470
+ return t.object === "customer" ? typeof t.id == "string" ? t.id : null : b(t.customer ?? null);
471
+ }, O = (e) => typeof e == "object" && !!e && typeof e.type == "string" && typeof e.data == "object", k = async (e, t) => {
345
472
  try {
346
473
  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);
347
474
  } catch {
@@ -349,4 +476,4 @@ var c = {
349
476
  }
350
477
  };
351
478
  //#endregion
352
- export { m as STRIPE_WEBHOOK_EVENTS, v as isDuplicateMeterEvent, h as normalizeStripeEvent, _ as stripeProvider };
479
+ export { S as STRIPE_WEBHOOK_EVENTS, E as isDuplicateMeterEvent, C as normalizeStripeEvent, T as stripeProvider };