@voltro/plugin-billing 0.85.0 → 0.86.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.
@@ -0,0 +1,352 @@
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";
4
+ //#region src/stripeProvider.ts
5
+ var c = {
6
+ active: "active",
7
+ trialing: "trialing",
8
+ past_due: "pastDue",
9
+ unpaid: "pastDue",
10
+ canceled: "canceled",
11
+ incomplete: "incomplete",
12
+ incomplete_expired: "canceled",
13
+ paused: "pastDue"
14
+ }, l = (e) => {
15
+ let t = e.metadata?.tenantId;
16
+ if (typeof t == "string" && t.length > 0) return t;
17
+ let n = e.client_reference_id;
18
+ return typeof n == "string" && n.length > 0 ? n : null;
19
+ }, u = (e) => {
20
+ let t = e.items?.data ?? [];
21
+ return t.find((e) => e.price?.recurring?.usage_type !== "metered") ?? t[0];
22
+ }, d = (e) => {
23
+ let t = u(e)?.price;
24
+ 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;
27
+ if (r !== null) return r;
28
+ let i = e.metadata?.plan;
29
+ return typeof i == "string" && i.length > 0 ? i : null;
30
+ }, m = [
31
+ "checkout.session.completed",
32
+ "customer.created",
33
+ "customer.deleted",
34
+ "customer.subscription.created",
35
+ "customer.subscription.deleted",
36
+ "customer.subscription.paused",
37
+ "customer.subscription.resumed",
38
+ "customer.subscription.updated",
39
+ "invoice.paid",
40
+ "invoice.payment_failed"
41
+ ], h = (e, t) => {
42
+ let n = o(e.created);
43
+ switch (e.type) {
44
+ case "customer.subscription.created":
45
+ case "customer.subscription.updated":
46
+ case "customer.subscription.resumed":
47
+ 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);
51
+ return {
52
+ _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
62
+ };
63
+ }
64
+ case "customer.subscription.deleted": {
65
+ let t = e.data.object, r = l(t);
66
+ return r === null ? null : {
67
+ _tag: "subscriptionCanceled",
68
+ tenantId: r,
69
+ providerSubscriptionId: t.id,
70
+ occurredAt: n
71
+ };
72
+ }
73
+ case "invoice.paid":
74
+ 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
83
+ };
84
+ }
85
+ case "checkout.session.completed": {
86
+ let t = e.data.object, r = l(t), i = f(t.customer);
87
+ return r === null || i === null ? null : {
88
+ _tag: "customerLinked",
89
+ tenantId: r,
90
+ providerCustomerId: i,
91
+ occurredAt: n
92
+ };
93
+ }
94
+ case "customer.deleted": {
95
+ let t = e.data.object;
96
+ return {
97
+ _tag: "customerUnlinked",
98
+ tenantId: l(t),
99
+ providerCustomerId: t.id,
100
+ occurredAt: n
101
+ };
102
+ }
103
+ case "customer.created": {
104
+ let t = e.data.object, r = l(t);
105
+ return r === null ? null : {
106
+ _tag: "customerLinked",
107
+ tenantId: r,
108
+ providerCustomerId: t.id,
109
+ occurredAt: n
110
+ };
111
+ }
112
+ default: return null;
113
+ }
114
+ }, g = {
115
+ prorate: "create_prorations",
116
+ none: "none",
117
+ always_invoice: "always_invoice"
118
+ }, _ = (d) => {
119
+ let _ = n(d), S = d.planForPriceId, C = (e) => {
120
+ let t = r(e);
121
+ return {
122
+ status: c[e.status] ?? "incomplete",
123
+ plan: p(e, S),
124
+ quantity: i(e),
125
+ currentPeriodStart: t.start,
126
+ currentPeriodEnd: t.end,
127
+ cancelAt: o(e.cancel_at)
128
+ };
129
+ }, w = (e) => ({
130
+ ...C(e),
131
+ providerSubscriptionId: e.id,
132
+ providerCustomerId: f(e.customer),
133
+ tenantId: l(e),
134
+ 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, {
137
+ cancel_at_period_end: !0,
138
+ expand: ["items.data.price"]
139
+ }),
140
+ close: (e) => _.subscriptions.update(e, {
141
+ cancel_at: Math.ceil(Date.now() / 1e3) + T,
142
+ proration_behavior: "create_prorations",
143
+ 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;
147
+ return {
148
+ plan: p(e, S) ?? "free",
149
+ quantity: i(e),
150
+ prorationMinor: t,
151
+ currency: (a?.currency ?? "usd").toLowerCase(),
152
+ currentPeriodStart: n.start,
153
+ currentPeriodEnd: n.end
154
+ };
155
+ }, k = (e, t) => {
156
+ let n = u(e)?.id;
157
+ return {
158
+ items: [{
159
+ ...n === void 0 ? {} : { id: n },
160
+ ...t.priceId === void 0 ? {} : { price: t.priceId },
161
+ ...t.quantity === void 0 ? {} : { quantity: t.quantity }
162
+ }],
163
+ proration_behavior: g[t.proration ?? "prorate"],
164
+ ...t.priceId !== void 0 && S ? { metadata: {
165
+ ...e.metadata,
166
+ plan: S(t.priceId) ?? ""
167
+ } } : {}
168
+ };
169
+ };
170
+ return {
171
+ name: "stripe",
172
+ supportsMeteredUsage: d.supportsMeteredUsage ?? !0,
173
+ 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,
189
+ 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 };
205
+ }),
206
+ createPortalSession: (e) => a("stripe.billingPortal.sessions.create", async () => ({ url: (await _.billingPortal.sessions.create({
207
+ customer: e.providerCustomerId,
208
+ return_url: e.returnUrl
209
+ })).url })),
210
+ reportUsage: (t) => {
211
+ let n = t.providerCustomerId;
212
+ return n === void 0 ? s.fail(new e({
213
+ source: "stripe.billing.meterEvents.create",
214
+ message: `no Stripe customer linked for tenant '${t.tenantId}'`,
215
+ transient: !1
216
+ })) : a("stripe.billing.meterEvents.create", async () => {
217
+ try {
218
+ await _.billing.meterEvents.create({
219
+ event_name: t.entitlementKey,
220
+ identifier: t.idempotencyKey ?? `${t.tenantId}:${t.entitlementKey}:${t.periodEnd?.toISOString() ?? "current"}`,
221
+ payload: {
222
+ value: String(t.quantity),
223
+ stripe_customer_id: n
224
+ }
225
+ });
226
+ } catch (e) {
227
+ if (v(e)) return;
228
+ throw e;
229
+ }
230
+ });
231
+ },
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);
235
+ }),
236
+ previewSubscriptionChange: (e) => a("stripe.invoices.createPreview", async () => {
237
+ let t = await D(e.providerSubscriptionId), n = k(t, e), r = (await _.invoices.createPreview({
238
+ subscription: e.providerSubscriptionId,
239
+ subscription_details: {
240
+ items: n.items,
241
+ ...n.proration_behavior === void 0 ? {} : { proration_behavior: n.proration_behavior }
242
+ }
243
+ })).lines.data.filter((e) => e.parent?.subscription_item_details?.proration === !0).reduce((e, t) => e + t.amount, 0);
244
+ return {
245
+ ...O(t, r),
246
+ ...e.quantity === void 0 ? {} : { quantity: e.quantity },
247
+ ...e.priceId !== void 0 && S ? { plan: S(e.priceId) ?? "free" } : {}
248
+ };
249
+ }),
250
+ fetchSubscription: (e) => a("stripe.subscriptions.retrieve", async () => {
251
+ try {
252
+ return C(await D(e));
253
+ } catch (e) {
254
+ if (e instanceof t.errors.StripeInvalidRequestError && e.statusCode === 404) return null;
255
+ throw e;
256
+ }
257
+ }),
258
+ listSubscriptions: (e) => a("stripe.subscriptions.list", async () => {
259
+ let t = [];
260
+ for await (let n of _.subscriptions.list({
261
+ limit: 100,
262
+ expand: ["data.items.data.price"],
263
+ ...e.providerCustomerId === void 0 ? {} : { customer: e.providerCustomerId }
264
+ })) t.push(w(n));
265
+ return t;
266
+ }),
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, {
269
+ cancel_at_period_end: !1,
270
+ expand: ["items.data.price"]
271
+ }))),
272
+ ensureCustomer: (e) => a("stripe.customers.create", async () => {
273
+ let t = (await _.customers.search({
274
+ query: `metadata['tenantId']:'${e.tenantId.replace(/['\\]/g, "")}'`,
275
+ limit: 2
276
+ })).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;
278
+ }),
279
+ expireOpenCheckouts: (e) => a("stripe.checkout.sessions.expire", async () => {
280
+ let t = 0;
281
+ for await (let n of _.checkout.sessions.list({
282
+ customer: e.providerCustomerId,
283
+ status: "open",
284
+ limit: 100
285
+ })) await _.checkout.sessions.expire(n.id), t += 1;
286
+ return t;
287
+ }),
288
+ addSubscriptionPrices: (e) => a("stripe.subscriptionItems.create", async () => {
289
+ for (let t of e.priceIds) await _.subscriptionItems.create({
290
+ subscription: e.providerSubscriptionId,
291
+ price: t,
292
+ proration_behavior: "none"
293
+ });
294
+ }),
295
+ customerEmail: (e) => a("stripe.customers.retrieve", async () => {
296
+ let t = await _.customers.retrieve(e);
297
+ return t.deleted === !0 ? null : typeof t.email == "string" && t.email.length > 0 ? t.email : null;
298
+ }),
299
+ listInvoices: (e) => a("stripe.invoices.list", async () => (await _.invoices.list({
300
+ customer: e.providerCustomerId,
301
+ 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)
322
+ } : {
323
+ providerCustomerId: null,
324
+ createdAt: null
325
+ },
326
+ listUndeliveredEvents: () => a("stripe.events.list", async () => {
327
+ let e = [];
328
+ for await (let t of _.events.list({
329
+ 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)
336
+ });
337
+ return e;
338
+ }),
339
+ normalizeEvent: (e) => s.succeed(b(e) ? h(e, S) : null)
340
+ };
341
+ }, v = (e) => e instanceof t.errors.StripeInvalidRequestError && typeof e.message == "string" && e.message.startsWith("An event already exists with identifier"), y = (e) => {
342
+ 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) => {
345
+ try {
346
+ 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
+ } catch {
348
+ return 0;
349
+ }
350
+ };
351
+ //#endregion
352
+ export { m as STRIPE_WEBHOOK_EVENTS, v as isDuplicateMeterEvent, h as normalizeStripeEvent, _ as stripeProvider };
@@ -0,0 +1,52 @@
1
+ import { STRIPE_API_VERSION as e } from "./catalog.js";
2
+ //#region src/stripeRest.ts
3
+ var t = (e) => {
4
+ let t = [], n = (e, r) => {
5
+ if (r !== void 0) {
6
+ if (r === null) t.push(`${encodeURIComponent(e)}=`);
7
+ else if (Array.isArray(r)) r.forEach((t, r) => n(`${e}[${r}]`, t));
8
+ else if (typeof r == "object") for (let [t, i] of Object.entries(r)) n(`${e}[${t}]`, i);
9
+ else t.push(`${encodeURIComponent(e)}=${encodeURIComponent(String(r))}`);
10
+ }
11
+ };
12
+ for (let [t, r] of Object.entries(e)) n(t, r);
13
+ return t.join("&");
14
+ }, n = (e) => e.replace(/\b(sk|rk|rkcs|pk)_(test|live)_[A-Za-z0-9*]+/g, "$1_$2_[REDACTED]").replace(/\bwhsec_[A-Za-z0-9]+/g, "whsec_[REDACTED]"), r = class extends Error {
15
+ status;
16
+ constructor(e, t) {
17
+ super(t), this.status = e;
18
+ }
19
+ }, i = (i, a = fetch) => {
20
+ let o = async (o, s, c = {}) => {
21
+ if (!/^\/v[12]\/[A-Za-z0-9_/.-]+$/.test(s)) throw new r(0, `refusing request path ${JSON.stringify(s)}`);
22
+ let l = t(c), u = await a(`https://api.stripe.com${s}${o === "GET" && l.length > 0 ? `?${l}` : ""}`, {
23
+ method: o,
24
+ headers: {
25
+ authorization: `Bearer ${i}`,
26
+ "stripe-version": e,
27
+ ...o === "POST" ? { "content-type": "application/x-www-form-urlencoded" } : {}
28
+ },
29
+ ...o === "POST" ? { body: l } : {}
30
+ }), d = await u.json();
31
+ if (!u.ok) throw new r(u.status, n(d.error?.message ?? `HTTP ${u.status}`));
32
+ return d;
33
+ };
34
+ return {
35
+ get: (e, t) => o("GET", e, t),
36
+ post: (e, t) => o("POST", e, t),
37
+ list: async (e, t = {}) => {
38
+ let n = [], r;
39
+ for (;;) {
40
+ let i = await o("GET", e, {
41
+ limit: 100,
42
+ ...t,
43
+ ...r === void 0 ? {} : { starting_after: r }
44
+ });
45
+ if (n.push(...i.data), !i.has_more || i.data.length === 0) return n;
46
+ r = i.data[i.data.length - 1].id;
47
+ }
48
+ }
49
+ };
50
+ };
51
+ //#endregion
52
+ export { r as StripeRestError, t as formEncode, n as redactStripeKeys, i as stripeRest };
package/dist/table.js ADDED
@@ -0,0 +1,54 @@
1
+ import { id as e, integer as t, table as n, text as r, timestamp as i } from "@voltro/database";
2
+ //#region src/table.ts
3
+ var a = "_voltro_billing_customers", o = "_voltro_billing_subscriptions", s = "_voltro_billing_invoices", c = "_voltro_billing_usage", l = n(a, {
4
+ id: e({ prefix: "bcust" }),
5
+ tenantId: r(),
6
+ provider: r(),
7
+ providerCustomerId: r(),
8
+ createdAt: i(),
9
+ updatedAt: i()
10
+ }).index("byBillingCustomerTenant", ["tenantId"]).index("byBillingProviderCustomer", ["providerCustomerId"]), u = n(o, {
11
+ id: e({ prefix: "bsub" }),
12
+ tenantId: r(),
13
+ provider: r(),
14
+ providerSubscriptionId: r(),
15
+ plan: r(),
16
+ status: r(),
17
+ quantity: t().default(1),
18
+ currentPeriodStart: i().nullable(),
19
+ currentPeriodEnd: i().nullable(),
20
+ cancelAt: i().nullable(),
21
+ pastDueSince: i().nullable(),
22
+ statusEventAt: i().nullable(),
23
+ createdAt: i(),
24
+ updatedAt: i()
25
+ }).index("byBillingSubscriptionTenant", ["tenantId"]).index("byBillingProviderSubscription", ["providerSubscriptionId"]).index("byBillingSubscriptionStatus", ["status"]), d = n(s, {
26
+ id: e({ prefix: "binv" }),
27
+ tenantId: r(),
28
+ provider: r(),
29
+ providerInvoiceId: r(),
30
+ amountMinor: t(),
31
+ currency: r(),
32
+ status: r(),
33
+ statusEventAt: i().nullable(),
34
+ createdAt: i()
35
+ }).index("byBillingInvoiceTenant", ["tenantId"]).index("byBillingProviderInvoice", ["providerInvoiceId"]), f = n(c, {
36
+ id: e({ prefix: "buse" }),
37
+ tenantId: r().maxLength(191),
38
+ entitlementKey: r().maxLength(191),
39
+ period: r().maxLength(16),
40
+ used: t().default(0),
41
+ reportedToProvider: t().default(0),
42
+ updatedAt: i()
43
+ }).unique("byBillingUsageWindow", [
44
+ "tenantId",
45
+ "entitlementKey",
46
+ "period"
47
+ ]), p = () => [
48
+ l,
49
+ u,
50
+ d,
51
+ f
52
+ ];
53
+ //#endregion
54
+ export { a as BILLING_CUSTOMERS_TABLE, s as BILLING_INVOICES_TABLE, o as BILLING_SUBSCRIPTIONS_TABLE, c as BILLING_USAGE_TABLE, l as billingCustomersTable, d as billingInvoicesTable, u as billingSubscriptionsTable, p as billingTables, f as billingUsageTable };
@@ -0,0 +1,45 @@
1
+ import { Effect as e, Schema as t } from "effect";
2
+ import { createLogger as n } from "@voltro/logger";
3
+ import { defineIncomingWebhook as r, mountIncomingWebhook as i } from "@voltro/plugin-webhooks";
4
+ import { stripeProvider as a } from "@voltro/plugin-webhooks/providers";
5
+ //#region src/webhook.ts
6
+ var o = n({ scope: "@voltro/plugin-billing" }), s = "billing", c = "/billing/webhook", l = 3e5, u = (n) => r({
7
+ id: s,
8
+ path: c,
9
+ provider: a(),
10
+ payload: t.Unknown,
11
+ handler: async (t) => {
12
+ let r = n.provider.verifyWebhook !== void 0 && n.webhookSecret !== null && n.webhookSecret !== void 0 ? await e.runPromise(n.provider.verifyWebhook({
13
+ rawBody: t.rawBody,
14
+ signatureHeader: t.headers["stripe-signature"] ?? "",
15
+ secret: n.webhookSecret ?? ""
16
+ })) : t.body, i = await e.runPromise(n.provider.normalizeEvent(r)), a = n.provider.eventContext?.(r) ?? {
17
+ providerCustomerId: null,
18
+ createdAt: null
19
+ };
20
+ if (i === null && a.providerCustomerId === null) return;
21
+ let s = i?._tag === "subscriptionUpserted" || i?._tag === "subscriptionCanceled", c = a.providerCustomerId !== null && i?._tag !== "customerUnlinked", l = [];
22
+ if (i !== null && !(s && c) && (await e.runPromise(n.service.applyEvent(i)), l.push(i)), c) {
23
+ let t = await e.runPromise(n.service.syncCustomer(a.providerCustomerId, i !== null && "tenantId" in i && typeof i.tenantId == "string" ? i.tenantId : void 0));
24
+ l.push(...t.events);
25
+ }
26
+ a.createdAt !== null && Date.now() - a.createdAt.getTime() > 3e5 && n.onLateDelivery?.();
27
+ for (let t of l) {
28
+ let r = n.onEvent?.[t._tag];
29
+ r !== void 0 && await e.runPromise(r(t).pipe(e.tapCause((n) => e.sync(() => {
30
+ o.error("billing onEvent side effect failed — answering 500 so the provider redelivers", {
31
+ event: t._tag,
32
+ cause: String(n)
33
+ });
34
+ }))));
35
+ }
36
+ }
37
+ }), d = (e) => i(u(e), {
38
+ resolveSecret: () => Promise.resolve(e.webhookSecret),
39
+ ...e.idempotencyCache === void 0 ? {} : { idempotencyCache: e.idempotencyCache },
40
+ log: (e) => {
41
+ e.status >= 400 && o.warn("billing webhook rejected", { ...e });
42
+ }
43
+ });
44
+ //#endregion
45
+ export { s as BILLING_WEBHOOK_ID, c as BILLING_WEBHOOK_PATH, l as REDELIVERY_AGE_MS, u as billingWebhookDescriptor, d as mountBillingWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-billing",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "Billing on the official Stripe SDK — subscriptions, plans, entitlements and usage metering. Proration, failed-payment retries, tax, promotion codes, trials and the checkout seat stepper are Stripe's own; the plugin adds the entitlement engine, the enforce interceptor, typed BillingError/EntitlementExceeded, the /billing/webhook mount with Stripe's signature verification, the BillingService Tag, requireEntitlement(), and a browser-safe useStartCheckout() hook. Money is integer minor units end-to-end.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -48,11 +48,11 @@
48
48
  "node": ">=24.0.0"
49
49
  },
50
50
  "dependencies": {
51
- "@voltro/database": "0.85.0",
52
- "@voltro/env": "0.85.0",
53
- "@voltro/logger": "0.85.0",
54
- "@voltro/plugin-webhooks": "0.85.0",
55
- "@voltro/protocol": "0.85.0"
51
+ "@voltro/database": "0.86.0",
52
+ "@voltro/env": "0.86.0",
53
+ "@voltro/logger": "0.86.0",
54
+ "@voltro/plugin-webhooks": "0.86.0",
55
+ "@voltro/protocol": "0.86.0"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "effect": "4.0.1",