@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.
- package/CHANGELOG.md +5484 -0
- package/README.md +428 -0
- package/THIRD-PARTY-NOTICES.md +1511 -276
- package/dist/catalog.js +1 -2
- package/dist/checkoutStore.js +164 -0
- package/dist/closeStore.js +124 -0
- package/dist/custodyInventory.js +154 -0
- package/dist/customerStore.js +258 -0
- package/dist/dunning.js +1 -2
- package/dist/economy.d.ts +30 -0
- package/dist/economy.js +265 -0
- package/dist/entitlement.js +1 -2
- package/dist/eventDelivery.js +71 -0
- package/dist/flushClaim.js +1 -2
- package/dist/index.d.ts +669 -65
- package/dist/index.js +230 -173
- package/dist/invoiceHistory.js +90 -0
- package/dist/invoiceHistoryJournal.js +475 -0
- package/dist/invoiceHistoryProgress.js +234 -0
- package/dist/invoiceHistoryWork.js +362 -0
- package/dist/invoiceSnapshot.js +65 -0
- package/dist/invoiceStore.js +132 -0
- package/dist/metering.js +1 -2
- package/dist/plans.js +1 -2
- package/dist/providerIdentity.js +23 -0
- package/dist/providers.js +271 -67
- package/dist/provision.js +1 -2
- package/dist/rpc.js +1 -2
- package/dist/service.js +892 -392
- package/dist/stores.js +125 -291
- package/dist/stripeClient.js +1 -2
- package/dist/stripeIdentity.js +55 -0
- package/dist/stripeInvoiceHistory.js +66 -0
- package/dist/stripeInvoiceSnapshot.js +85 -0
- package/dist/stripeProvider.js +305 -179
- package/dist/stripeRest.js +1 -2
- package/dist/stripeVerifiedEvent.js +28 -0
- package/dist/table.js +70 -48
- package/dist/types.d.ts +298 -27
- package/dist/types.js +1 -2
- package/dist/usageEvents.js +64 -0
- package/dist/web/index.js +1 -2
- package/dist/webhook.js +32 -40
- 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
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
+
idempotencyCache: e.idempotencyCache ?? new i(),
|
|
40
33
|
log: (e) => {
|
|
41
|
-
e.status >= 400 &&
|
|
34
|
+
e.status >= 400 && c.warn("billing webhook rejected", { ...e });
|
|
42
35
|
}
|
|
43
36
|
});
|
|
44
|
-
|
|
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.
|
|
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.
|
|
52
|
-
"@voltro/env": "0.
|
|
53
|
-
"@voltro/logger": "0.
|
|
54
|
-
"@voltro/plugin-webhooks": "0.
|
|
55
|
-
"@voltro/protocol": "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
|
|
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": {
|