@voltro/plugin-billing 0.85.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.
package/dist/stores.js ADDED
@@ -0,0 +1,260 @@
1
+ import { BillingError as e } from "./types.js";
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
+ //#region 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({
18
+ try: t,
19
+ catch: (t) => (x.warn("billing store operation failed", {
20
+ source: n,
21
+ detail: String(t?.message ?? t)
22
+ }), new e({
23
+ source: n,
24
+ message: `${n} failed`,
25
+ transient: !0
26
+ }))
27
+ }), C = (e) => ({
28
+ table: e.table,
29
+ predicate: e.predicate,
30
+ order: e.order ?? [],
31
+ take: e.take,
32
+ skip: void 0,
33
+ projection: void 0
34
+ }), w = (e) => e === "canceled", T = /* @__PURE__ */ new Set([
35
+ "active",
36
+ "trialing",
37
+ "pastDue",
38
+ "incomplete"
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) => ({
40
+ tenantId: String(e.tenantId),
41
+ provider: String(e.provider),
42
+ providerSubscriptionId: String(e.providerSubscriptionId),
43
+ plan: String(e.plan),
44
+ status: String(e.status),
45
+ quantity: e.quantity == null ? 1 : Number(e.quantity),
46
+ currentPeriodStart: e.currentPeriodStart == null ? null : new Date(e.currentPeriodStart),
47
+ currentPeriodEnd: e.currentPeriodEnd == null ? null : new Date(e.currentPeriodEnd),
48
+ cancelAt: e.cancelAt == null ? null : new Date(e.cancelAt),
49
+ pastDueSince: e.pastDueSince == null ? null : new Date(e.pastDueSince),
50
+ statusEventAt: e.statusEventAt == null ? null : new Date(e.statusEventAt)
51
+ }), A = async (e, t) => (await e.query(C({
52
+ table: "_voltro_billing_subscriptions",
53
+ predicate: _("providerSubscriptionId", t),
54
+ take: 1
55
+ })))[0] ?? null, j = async (e, t) => (await e.query(C({
56
+ table: u,
57
+ predicate: _("tenantId", t),
58
+ order: [{
59
+ column: "updatedAt",
60
+ direction: "desc"
61
+ }, {
62
+ column: "id",
63
+ direction: "desc"
64
+ }]
65
+ }))).map(k), M = (e) => ({
66
+ upsert: (t) => S(async () => {
67
+ let n = await A(e, t.providerSubscriptionId), r = /* @__PURE__ */ new Date();
68
+ if (n) {
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), {
71
+ tenantId: t.tenantId,
72
+ provider: t.provider,
73
+ plan: t.plan,
74
+ status: t.status,
75
+ quantity: t.quantity,
76
+ currentPeriodStart: t.currentPeriodStart,
77
+ currentPeriodEnd: t.currentPeriodEnd,
78
+ cancelAt: t.cancelAt,
79
+ statusEventAt: t.statusEventAt,
80
+ updatedAt: r
81
+ });
82
+ } else await e.insert(u, {
83
+ id: b("bsub"),
84
+ tenantId: t.tenantId,
85
+ provider: t.provider,
86
+ providerSubscriptionId: t.providerSubscriptionId,
87
+ plan: t.plan,
88
+ status: t.status,
89
+ quantity: t.quantity,
90
+ currentPeriodStart: t.currentPeriodStart,
91
+ currentPeriodEnd: t.currentPeriodEnd,
92
+ cancelAt: t.cancelAt,
93
+ pastDueSince: null,
94
+ statusEventAt: t.statusEventAt,
95
+ createdAt: r,
96
+ updatedAt: r
97
+ });
98
+ }, "subscriptionStore"),
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),
104
+ order: [{
105
+ column: "updatedAt",
106
+ direction: "asc"
107
+ }]
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), {
112
+ status: "canceled",
113
+ updatedAt: /* @__PURE__ */ new Date()
114
+ });
115
+ }, "subscriptionStore"),
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), {
119
+ ...n.plan === void 0 ? {} : { plan: n.plan },
120
+ ...n.quantity === void 0 ? {} : { quantity: n.quantity },
121
+ ...n.status === void 0 ? {} : { status: n.status },
122
+ ...n.currentPeriodStart === void 0 ? {} : { currentPeriodStart: n.currentPeriodStart },
123
+ ...n.currentPeriodEnd === void 0 ? {} : { currentPeriodEnd: n.currentPeriodEnd },
124
+ ...n.pastDueSince === void 0 ? {} : { pastDueSince: n.pastDueSince },
125
+ ...n.statusEventAt === void 0 ? {} : { statusEventAt: n.statusEventAt },
126
+ updatedAt: /* @__PURE__ */ new Date()
127
+ });
128
+ }, "subscriptionStore"),
129
+ setStatus: (t, n) => S(async () => {
130
+ let r = await A(e, t);
131
+ r && await e.update(u, String(r.id), {
132
+ status: n,
133
+ updatedAt: /* @__PURE__ */ new Date()
134
+ });
135
+ }, "subscriptionStore")
136
+ }), N = (e) => ({
137
+ tenantId: String(e.tenantId),
138
+ entitlementKey: String(e.entitlementKey),
139
+ period: String(e.period),
140
+ used: Number(e.used ?? 0),
141
+ reportedToProvider: Number(e.reportedToProvider ?? 0)
142
+ }), P = async (e, t, n, r) => (await e.query(C({
143
+ table: "_voltro_billing_usage",
144
+ predicate: h(_("tenantId", t), _("entitlementKey", n), _("period", r)),
145
+ take: 1
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);
158
+ return i ? Number(i.used ?? 0) : 0;
159
+ }, "usageStore"),
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 {
165
+ allowed: !1,
166
+ entitlement: n,
167
+ limit: a,
168
+ used: c,
169
+ cost: i
170
+ };
171
+ if (s) {
172
+ if (await t.updateMany("_voltro_billing_usage", {
173
+ used: c + i,
174
+ updatedAt: /* @__PURE__ */ new Date()
175
+ }, { where: h(_("id", String(s.id)), _("used", c)) }) === 1) return {
176
+ allowed: !0,
177
+ entitlement: n,
178
+ limit: a,
179
+ used: c,
180
+ cost: i
181
+ };
182
+ } else {
183
+ let o = b("buse"), s = await t.insertIgnore(d, {
184
+ id: o,
185
+ tenantId: e,
186
+ entitlementKey: n,
187
+ period: r,
188
+ used: i,
189
+ reportedToProvider: 0,
190
+ updatedAt: /* @__PURE__ */ new Date()
191
+ }, { conflictColumns: [
192
+ "tenantId",
193
+ "entitlementKey",
194
+ "period"
195
+ ] });
196
+ if (String(s.id) === o) return {
197
+ allowed: !0,
198
+ entitlement: n,
199
+ limit: a,
200
+ used: 0,
201
+ cost: i
202
+ };
203
+ }
204
+ }
205
+ throw Error(`billing usage consume: contention on (${e}, ${n}, ${r}) exceeded 32 attempts`);
206
+ }, "usageStore"),
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,
215
+ updatedAt: /* @__PURE__ */ new Date()
216
+ }, { where: h(_("id", String(o.id)), _("used", e)) }) === 1) return e + i;
217
+ } else {
218
+ let a = b("buse"), o = await t.insertIgnore(d, {
219
+ id: a,
220
+ tenantId: e,
221
+ entitlementKey: n,
222
+ period: r,
223
+ used: i,
224
+ reportedToProvider: 0,
225
+ updatedAt: /* @__PURE__ */ new Date()
226
+ }, { conflictColumns: [
227
+ "tenantId",
228
+ "entitlementKey",
229
+ "period"
230
+ ] });
231
+ if (String(o.id) === a) return i;
232
+ }
233
+ }
234
+ throw Error(`billing usage increment: contention on (${e}, ${n}, ${r}) exceeded 32 attempts`);
235
+ }, "usageStore"),
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,
241
+ updatedAt: /* @__PURE__ */ new Date()
242
+ });
243
+ }, "usageStore")
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),
256
+ usage: F(e),
257
+ dunningNotices: l(e)
258
+ });
259
+ //#endregion
260
+ 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 };
@@ -0,0 +1,53 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { redactStripeKeys as t } from "./stripeRest.js";
3
+ import { Effect as n } from "effect";
4
+ import { createLogger as r } from "@voltro/logger";
5
+ import i from "stripe";
6
+ //#region src/stripeClient.ts
7
+ var a = r({ scope: "@voltro/plugin-billing" }), o = 3, s = 2e4, c = (e) => e.client ?? new i(e.apiKey, {
8
+ maxNetworkRetries: o,
9
+ timeout: s,
10
+ appInfo: {
11
+ name: "voltro",
12
+ url: "https://voltro.dev"
13
+ }
14
+ }), l = (e) => e instanceof i.errors.StripeConnectionError || e instanceof i.errors.StripeAPIError || e instanceof i.errors.StripeRateLimitError, u = (e) => e instanceof i.errors.StripeError ? e.code ?? e.type : void 0, d = (r, i) => n.tryPromise({
15
+ try: i,
16
+ catch: (n) => {
17
+ let i = l(n);
18
+ return a.warn("stripe call failed", {
19
+ source: r,
20
+ transient: i,
21
+ detail: t(f(n))
22
+ }), new e({
23
+ source: r,
24
+ message: p(n),
25
+ transient: i
26
+ });
27
+ }
28
+ }), f = (e) => {
29
+ if (e instanceof i.errors.StripeError) {
30
+ let t = u(e);
31
+ return t === void 0 ? e.message : `${e.message} (${t})`;
32
+ }
33
+ return String(e?.message ?? e);
34
+ }, p = (e) => {
35
+ if (e instanceof i.errors.StripeError) {
36
+ let t = [e.code, e.decline_code].filter((e) => typeof e == "string" && e.length > 0);
37
+ return t.length > 0 ? `${e.type} (${t.join(", ")})` : e.type;
38
+ }
39
+ return "provider call failed";
40
+ }, m = (e) => {
41
+ let t = e.items?.data?.[0];
42
+ return {
43
+ start: h(t?.current_period_start),
44
+ end: h(t?.current_period_end)
45
+ };
46
+ }, h = (e) => typeof e == "number" && Number.isFinite(e) ? /* @__PURE__ */ new Date(e * 1e3) : null, g = (e) => {
47
+ let t = e.items?.data ?? [];
48
+ if (t.length === 0) return 1;
49
+ let n = t.reduce((e, t) => e + (t.quantity ?? 0), 0);
50
+ return n > 0 ? n : 1;
51
+ };
52
+ //#endregion
53
+ export { i as Stripe, c as makeStripeClient, m as periodOf, g as quantityOf, d as stripeCall, h as unixToDate };
@@ -0,0 +1,56 @@
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
+ //#region 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
+ //#endregion
56
+ export { c as stripeAccountIdentity, l as stripeCustomerSnapshot };
@@ -0,0 +1,67 @@
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
+ //#region 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
+ //#endregion
67
+ export { c as stripeInvoiceHistory };
@@ -0,0 +1,86 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { assertInvoiceSnapshot as t, invoiceSnapshotError as n } from "./invoiceSnapshot.js";
3
+ import { stripeCall as r } from "./stripeClient.js";
4
+ import { Effect as i } from "effect";
5
+ //#region src/stripeInvoiceSnapshot.ts
6
+ var a = (e) => typeof e == "string" ? e : e?.id ?? null, o = (e) => {
7
+ if (e === null || !Number.isSafeInteger(e) || !Number.isSafeInteger(e * 1e3)) throw n("The invoice observation contains an invalid timestamp.");
8
+ let t = /* @__PURE__ */ new Date(e * 1e3);
9
+ if (!Number.isFinite(t.getTime())) throw n("The invoice timestamp is outside the supported range.");
10
+ return t;
11
+ }, s = (t) => i.try({
12
+ try: t,
13
+ catch: (t) => t instanceof e ? t : n("The provider invoice observation could not be confirmed.")
14
+ }), c = (e, t) => {
15
+ if (e.id !== t.providerInvoiceId || a(e.customer) !== t.providerCustomerId) throw n("The invoice does not belong to the linked billing customer.");
16
+ let r = e.status;
17
+ if (r !== "open" && r !== "paid" && r !== "uncollectible" && r !== "void") throw n("Detailed invoice evidence requires a finalized invoice.");
18
+ return {
19
+ providerInvoiceId: e.id,
20
+ providerCustomerId: t.providerCustomerId,
21
+ providerSubscriptionId: a(e.parent?.subscription_details?.subscription ?? null),
22
+ providerRevisionId: a(e.latest_revision),
23
+ status: r === "open" ? "open" : r === "paid" ? "paid" : r === "void" ? "void" : "uncollectible",
24
+ livemode: e.livemode,
25
+ currency: e.currency,
26
+ amountDueMinor: e.amount_due,
27
+ amountPaidMinor: e.amount_paid,
28
+ amountRemainingMinor: e.amount_remaining,
29
+ finalizedAt: o(e.status_transitions.finalized_at),
30
+ paidAt: e.status_transitions.paid_at === null ? null : o(e.status_transitions.paid_at)
31
+ };
32
+ }, l = (e, t) => {
33
+ if (e.invoice !== t) throw n("An invoice line has a different invoice identity.");
34
+ let r = e.parent, i = r?.type === "subscription_item_details" ? r.subscription_item_details : null, s = r?.type === "invoice_item_details" ? r.invoice_item_details : null;
35
+ if (r?.type === "subscription_item_details" && i === null || r?.type === "invoice_item_details" && s === null) throw n("The invoice line origin could not be confirmed.");
36
+ let c = a(e.subscription), l = i?.subscription ?? s?.subscription ?? null;
37
+ if (c !== null && l !== null && c !== l) throw n("The invoice line contains conflicting subscription identities.");
38
+ return {
39
+ providerLineId: e.id,
40
+ providerSubscriptionId: l ?? c,
41
+ providerSubscriptionItemId: i?.subscription_item ?? null,
42
+ providerPriceId: e.pricing?.type === "price_details" ? a(e.pricing.price_details?.price ?? null) : null,
43
+ amountMinor: e.amount,
44
+ currency: e.currency,
45
+ quantityDecimal: e.quantity_decimal?.toString() ?? null,
46
+ periodStart: o(e.period.start),
47
+ periodEnd: o(e.period.end),
48
+ proration: i?.proration ?? s?.proration ?? null
49
+ };
50
+ }, u = (e, a) => {
51
+ let o = Object.freeze({
52
+ providerCustomerId: a.providerCustomerId,
53
+ providerInvoiceId: a.providerInvoiceId
54
+ });
55
+ return [o.providerCustomerId, o.providerInvoiceId].every((e) => typeof e == "string" && e.length > 0 && e.trim() === e) ? i.gen(function* () {
56
+ let a = yield* r("stripe.invoices.retrieve", () => e.invoices.retrieve(o.providerInvoiceId)), u = yield* s(() => c(a, o)), d = [], f = /* @__PURE__ */ new Set(), p, m = !1;
57
+ for (let t = 0; t < 100; t++) {
58
+ let t = yield* r("stripe.invoices.listLineItems", () => e.invoices.listLineItems(o.providerInvoiceId, {
59
+ limit: 100,
60
+ ...p === void 0 ? {} : { starting_after: p }
61
+ }));
62
+ if (yield* s(() => {
63
+ if (!Array.isArray(t.data) || t.data.length > 100 || typeof t.has_more != "boolean" || (t.has_more || p !== void 0) && t.data.length === 0) throw n("The invoice line page is incomplete.");
64
+ for (let e of t.data) {
65
+ if (typeof e.id != "string" || e.id.length === 0 || f.has(e.id)) throw n("The invoice line cursor repeated an identity.", !0);
66
+ f.add(e.id), d.push(l(e, o.providerInvoiceId));
67
+ }
68
+ }), !t.has_more) {
69
+ m = !0;
70
+ break;
71
+ }
72
+ p = t.data[t.data.length - 1]?.id;
73
+ }
74
+ if (!m) return yield* i.fail(n("The invoice exceeds the complete observation bound."));
75
+ let h = yield* r("stripe.invoices.retrieve", () => e.invoices.retrieve(o.providerInvoiceId)), g = yield* s(() => c(h, o));
76
+ if (JSON.stringify(u) !== JSON.stringify(g)) return yield* i.fail(n("The invoice changed during observation; retry the complete read.", !0));
77
+ let _ = {
78
+ ...g,
79
+ lines: d,
80
+ observedAt: /* @__PURE__ */ new Date()
81
+ };
82
+ return yield* s(() => t(_)), _;
83
+ }) : i.fail(n("An exact billing customer and invoice identity are required."));
84
+ };
85
+ //#endregion
86
+ export { u as stripeInvoiceSnapshot };