@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
@@ -0,0 +1,64 @@
1
+ import { BillingError as e } from "./types.js";
2
+ import { Effect as t } from "effect";
3
+ import { and as n, eq as r, id as i, integer as a, table as o, text as s, timestamp as c } from "@voltro/database";
4
+ // src/usageEvents.ts
5
+ var l = "_voltro_billing_usage_events", u = o(l, {
6
+ id: i({ prefix: "busage" }),
7
+ tenantId: s().maxLength(191),
8
+ idempotencyKey: s().maxLength(191),
9
+ entitlementKey: s().maxLength(100),
10
+ period: s().maxLength(7),
11
+ quantity: a(),
12
+ occurredAt: c(),
13
+ createdAt: c()
14
+ }).unique("byBillingUsageEvent", ["tenantId", "idempotencyKey"]).index("byBillingUsageEventPeriod", ["tenantId", "period"]), d = () => new e({
15
+ source: "usageEvent",
16
+ message: "Usage event identity or immutable payload requires reconciliation.",
17
+ transient: !1
18
+ }), f = (e) => {
19
+ if (e.tenantId.length === 0 || e.tenantId.length > 191 || e.idempotencyKey.trim().length === 0 || e.idempotencyKey.length > 191 || e.entitlementKey.length === 0 || e.entitlementKey.length > 100 || !Number.isSafeInteger(e.quantity) || e.quantity <= 0 || !(e.occurredAt instanceof Date) || !Number.isFinite(e.occurredAt.getTime())) throw d();
20
+ let t = new Date(e.occurredAt);
21
+ if (!/^[0-9]{4}-(0[1-9]|1[0-2])$/.test(e.period) || e.period !== `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, "0")}`) throw d();
22
+ return {
23
+ tenantId: e.tenantId,
24
+ idempotencyKey: e.idempotencyKey,
25
+ entitlementKey: e.entitlementKey,
26
+ quantity: e.quantity,
27
+ period: e.period,
28
+ occurredAt: t
29
+ };
30
+ }, p = (e, t) => e.tenantId === t.tenantId && e.idempotencyKey === t.idempotencyKey && e.entitlementKey === t.entitlementKey && e.period === t.period && e.quantity === t.quantity && new Date(e.occurredAt).getTime() === t.occurredAt.getTime(), m = (i, a, o) => t.tryPromise({
31
+ try: async () => {
32
+ let e = f(a);
33
+ await i.transactional(async (t) => {
34
+ let i = {
35
+ table: l,
36
+ predicate: n(r("tenantId", e.tenantId), r("idempotencyKey", e.idempotencyKey)),
37
+ order: [],
38
+ take: 2,
39
+ skip: void 0,
40
+ projection: void 0
41
+ }, a = await t.query(i);
42
+ if (a.length > 1) throw d();
43
+ if (a[0]) {
44
+ if (!p(a[0], e)) throw d();
45
+ return;
46
+ }
47
+ let s = "busage_" + globalThis.crypto.randomUUID();
48
+ await t.insertIgnore(l, {
49
+ ...e,
50
+ id: s,
51
+ createdAt: /* @__PURE__ */ new Date()
52
+ }, { conflictColumns: ["tenantId", "idempotencyKey"] });
53
+ let c = await t.query(i);
54
+ if (c.length !== 1 || !c[0] || !p(c[0], e)) throw d();
55
+ c[0].id === s && await o(t, e);
56
+ });
57
+ },
58
+ catch: (t) => t instanceof e ? t : new e({
59
+ source: "usageEvent",
60
+ message: "Usage event could not be durably recorded. Retry the same event identity.",
61
+ transient: !0
62
+ })
63
+ });
64
+ export { l as BILLING_USAGE_EVENTS_TABLE, u as billingUsageEventsTable, m as recordDataStoreUsageEvent };
package/dist/web/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useCallback as e, useState as t } from "react";
2
- //#region src/web/index.ts
2
+ // src/web/index.ts
3
3
  var n = () => globalThis.window, r = (r) => {
4
4
  let [i, a] = t(!1), [o, s] = t(null);
5
5
  return {
@@ -43,5 +43,4 @@ var n = () => globalThis.window, r = (r) => {
43
43
  error: s
44
44
  };
45
45
  };
46
- //#endregion
47
46
  export { r as useStartCheckout, i as useSubscriptionCancel };
package/dist/webhook.js CHANGED
@@ -1,45 +1,37 @@
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);
1
+ import { makeBillingEventDelivery as e } from "./eventDelivery.js";
2
+ import { Effect as t, Schema as n } from "effect";
3
+ import { createLogger as r } from "@voltro/logger";
4
+ import { IdempotencyCache as i, defineIncomingWebhook as a, mountIncomingWebhook as o } from "@voltro/plugin-webhooks";
5
+ import { stripeProvider as s } from "@voltro/plugin-webhooks/providers";
6
+ // src/webhook.ts
7
+ var c = r({ scope: "@voltro/plugin-billing" }), l = "billing", u = "/billing/webhook", d = 3e5, f = (r) => {
8
+ let i = e(r);
9
+ return a({
10
+ id: l,
11
+ path: u,
12
+ provider: s(),
13
+ payload: n.Unknown,
14
+ handler: async (e) => {
15
+ let n = r.provider.verifyWebhook !== void 0 && r.webhookSecret !== null && r.webhookSecret !== void 0 ? await t.runPromise(r.provider.verifyWebhook({
16
+ rawBody: e.rawBody,
17
+ signatureHeader: e.headers["stripe-signature"] ?? "",
18
+ secret: r.webhookSecret ?? ""
19
+ })) : e.body, a = await t.runPromise(r.provider.normalizeEvent(n)), o = r.provider.eventContext?.(n) ?? {
20
+ providerCustomerId: null,
21
+ createdAt: null
22
+ };
23
+ (a !== null || o.providerCustomerId !== null) && (o.createdAt !== null && Date.now() - o.createdAt.getTime() > 3e5 && r.onLateDelivery?.(), await t.runPromise(i({
24
+ id: e.idempotencyKey,
25
+ event: a,
26
+ ...o
27
+ })));
25
28
  }
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), {
29
+ });
30
+ }, p = (e) => o(f(e), {
38
31
  resolveSecret: () => Promise.resolve(e.webhookSecret),
39
- ...e.idempotencyCache === void 0 ? {} : { idempotencyCache: e.idempotencyCache },
32
+ idempotencyCache: e.idempotencyCache ?? new i(),
40
33
  log: (e) => {
41
- e.status >= 400 && o.warn("billing webhook rejected", { ...e });
34
+ e.status >= 400 && c.warn("billing webhook rejected", { ...e });
42
35
  }
43
36
  });
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 };
37
+ export { l as BILLING_WEBHOOK_ID, u as BILLING_WEBHOOK_PATH, d as REDELIVERY_AGE_MS, f as billingWebhookDescriptor, p as mountBillingWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-billing",
3
- "version": "0.86.0",
3
+ "version": "0.88.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",
@@ -38,6 +38,11 @@
38
38
  "import": "./dist/rpc.js",
39
39
  "default": "./dist/rpc.js"
40
40
  },
41
+ "./economy": {
42
+ "types": "./dist/economy.d.ts",
43
+ "import": "./dist/economy.js",
44
+ "default": "./dist/economy.js"
45
+ },
41
46
  "./package.json": "./package.json"
42
47
  },
43
48
  "main": "./dist/index.js",
@@ -48,20 +53,25 @@
48
53
  "node": ">=24.0.0"
49
54
  },
50
55
  "dependencies": {
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
+ "@voltro/database": "0.88.0",
57
+ "@voltro/env": "0.88.0",
58
+ "@voltro/logger": "0.88.0",
59
+ "@voltro/plugin-webhooks": "0.88.0",
60
+ "@voltro/protocol": "0.88.0",
61
+ "@voltro/runtime": "0.88.0"
56
62
  },
57
63
  "peerDependencies": {
58
- "effect": "4.0.1",
64
+ "effect": "4.1.0",
59
65
  "react": "^19.0.0",
60
- "stripe": "^22.6.1"
66
+ "stripe": "^22.6.1 || ^23.0.1",
67
+ "@voltro/plugin-economy": "0.88.0"
61
68
  },
62
69
  "peerDependenciesMeta": {
63
70
  "react": {
64
71
  "optional": true
72
+ },
73
+ "@voltro/plugin-economy": {
74
+ "optional": true
65
75
  }
66
76
  },
67
77
  "publishConfig": {