@voltro/plugin-billing 0.83.1 → 0.85.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 +1191 -0
- package/THIRD-PARTY-NOTICES.md +71 -525
- package/dist/index.d.ts +19 -24
- package/dist/index.js +18 -18
- package/dist/rpc.d.ts +72 -73
- package/dist/types.d.ts +19 -24
- package/dist/types.js +1 -1
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { Subject } from '@voltro/protocol';
|
|
|
16
16
|
import { TableIndex } from '@voltro/database';
|
|
17
17
|
import { TableLike } from '@voltro/database';
|
|
18
18
|
import { VoltroPlugin } from '@voltro/protocol';
|
|
19
|
+
import { YieldableError } from 'effect/Cause';
|
|
19
20
|
|
|
20
21
|
export declare const assertBillingProductionReady: (input: {
|
|
21
22
|
readonly env: NodeJS.ProcessEnv;
|
|
@@ -93,15 +94,13 @@ export declare const billingEgressOf: (provider: Pick<BillingProvider, "name">)
|
|
|
93
94
|
export declare class BillingError extends BillingError_base {
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
declare const BillingError_base: Schema.
|
|
97
|
-
readonly _tag: Schema.tag<"BillingError">;
|
|
98
|
-
} & {
|
|
97
|
+
declare const BillingError_base: Schema.Class<BillingError, Schema.TaggedStruct<"BillingError", {
|
|
99
98
|
/** Where the failure originated — provider name or an internal stage. */
|
|
100
|
-
source:
|
|
101
|
-
message:
|
|
99
|
+
readonly source: Schema.String;
|
|
100
|
+
readonly message: Schema.String;
|
|
102
101
|
/** Retryable? The service retries `transient` failures on a Schedule. */
|
|
103
|
-
transient:
|
|
104
|
-
}>;
|
|
102
|
+
readonly transient: Schema.Boolean;
|
|
103
|
+
}>, YieldableError>;
|
|
105
104
|
|
|
106
105
|
/**
|
|
107
106
|
* The normalized event union a `BillingProvider.normalizeEvent` produces
|
|
@@ -420,7 +419,7 @@ export declare type BillingProviderName = 'stripe' | 'mock';
|
|
|
420
419
|
export declare class BillingService extends BillingService_base {
|
|
421
420
|
}
|
|
422
421
|
|
|
423
|
-
declare const BillingService_base: Context.
|
|
422
|
+
declare const BillingService_base: Context.ServiceClass<BillingService, "@voltro/plugin-billing/BillingService", BillingServiceShape>;
|
|
424
423
|
|
|
425
424
|
/** Wrap a built service in a `Layer` for the plugin's `services` field. */
|
|
426
425
|
export declare const billingServiceLayer: (service: BillingServiceShape) => Layer.Layer<BillingService, never, never>;
|
|
@@ -1012,18 +1011,16 @@ export declare interface EntitlementDecision {
|
|
|
1012
1011
|
export declare class EntitlementExceeded extends EntitlementExceeded_base {
|
|
1013
1012
|
}
|
|
1014
1013
|
|
|
1015
|
-
declare const EntitlementExceeded_base: Schema.
|
|
1016
|
-
readonly _tag: Schema.tag<"EntitlementExceeded">;
|
|
1017
|
-
} & {
|
|
1014
|
+
declare const EntitlementExceeded_base: Schema.Class<EntitlementExceeded, Schema.TaggedStruct<"EntitlementExceeded", {
|
|
1018
1015
|
/** The entitlement key that was exhausted (`'aiCalls'`). */
|
|
1019
|
-
entitlement:
|
|
1016
|
+
readonly entitlement: Schema.String;
|
|
1020
1017
|
/** The static limit for the caller's plan. */
|
|
1021
|
-
limit:
|
|
1018
|
+
readonly limit: Schema.Number;
|
|
1022
1019
|
/** How much was already used in the current window. */
|
|
1023
|
-
used:
|
|
1020
|
+
readonly used: Schema.Number;
|
|
1024
1021
|
/** The cost the rejected call would have charged. */
|
|
1025
|
-
cost:
|
|
1026
|
-
}>;
|
|
1022
|
+
readonly cost: Schema.Number;
|
|
1023
|
+
}>, YieldableError>;
|
|
1027
1024
|
|
|
1028
1025
|
/** An entitlement limit: a finite quota or the unbounded `'unlimited'`. */
|
|
1029
1026
|
export declare type EntitlementLimit = number | 'unlimited';
|
|
@@ -1839,17 +1836,15 @@ declare interface SubscriptionChangeResult {
|
|
|
1839
1836
|
export declare class SubscriptionLocked extends SubscriptionLocked_base {
|
|
1840
1837
|
}
|
|
1841
1838
|
|
|
1842
|
-
declare const SubscriptionLocked_base: Schema.
|
|
1843
|
-
readonly
|
|
1844
|
-
} & {
|
|
1845
|
-
tenantId: typeof Schema.String;
|
|
1839
|
+
declare const SubscriptionLocked_base: Schema.Class<SubscriptionLocked, Schema.TaggedStruct<"SubscriptionLocked", {
|
|
1840
|
+
readonly tenantId: Schema.String;
|
|
1846
1841
|
/** The subscription status at the time of the refusal (`'pastDue'`). */
|
|
1847
|
-
status:
|
|
1842
|
+
readonly status: Schema.String;
|
|
1848
1843
|
/** ISO timestamp at which the grace period expired. */
|
|
1849
|
-
lockedSince:
|
|
1844
|
+
readonly lockedSince: Schema.String;
|
|
1850
1845
|
/** `'hard'` (limits fell to the free plan) or `'soft'` (report only). */
|
|
1851
|
-
lockout:
|
|
1852
|
-
}>;
|
|
1846
|
+
readonly lockout: Schema.String;
|
|
1847
|
+
}>, YieldableError>;
|
|
1853
1848
|
|
|
1854
1849
|
/** Fields a mid-cycle / reconcile patch may set on the tenant's row. */
|
|
1855
1850
|
export declare interface SubscriptionPatch {
|
package/dist/index.js
CHANGED
|
@@ -863,9 +863,9 @@ var fe = (e, t, n, r) => ({
|
|
|
863
863
|
return t === null ? null : yield* n.customerEmail(t.providerCustomerId);
|
|
864
864
|
}), E = (e) => {
|
|
865
865
|
let t = f.portalReturnUrl;
|
|
866
|
-
return t === void 0 ? v.succeed(null) : w(e, t).pipe(v.map((e) => e.url), v.
|
|
866
|
+
return t === void 0 ? v.succeed(null) : w(e, t).pipe(v.map((e) => e.url), v.catch(() => v.succeed(null)));
|
|
867
867
|
}, te = (e, t, n, r, i) => v.gen(function* () {
|
|
868
|
-
let a = yield* T(e.tenantId).pipe(v.
|
|
868
|
+
let a = yield* T(e.tenantId).pipe(v.catch(() => v.succeed(null))), o = yield* E(e.tenantId), s = t.lockedSince !== null, c = Ie({
|
|
869
869
|
stepId: n,
|
|
870
870
|
locked: s,
|
|
871
871
|
graceEndsAt: i,
|
|
@@ -891,7 +891,7 @@ var fe = (e, t, n, r) => ({
|
|
|
891
891
|
delivered: f.notify !== void 0 && a !== null
|
|
892
892
|
});
|
|
893
893
|
let u = f.notify;
|
|
894
|
-
u !== void 0 && (yield* u(l).pipe(v.
|
|
894
|
+
u !== void 0 && (yield* u(l).pipe(v.catchCause((t) => v.sync(() => {
|
|
895
895
|
J.warn("billing dunning notify failed", {
|
|
896
896
|
tenant: e.tenantId,
|
|
897
897
|
step: n,
|
|
@@ -901,7 +901,7 @@ var fe = (e, t, n, r) => ({
|
|
|
901
901
|
}), D = (e) => v.gen(function* () {
|
|
902
902
|
let t = yield* d.stores.subscriptions.getByTenant(e);
|
|
903
903
|
if (t === null) return Pe(null, f, s(), e);
|
|
904
|
-
let r = yield* n.fetchSubscription(t.providerSubscriptionId).pipe(v.
|
|
904
|
+
let r = yield* n.fetchSubscription(t.providerSubscriptionId).pipe(v.catch((t) => v.sync(() => (J.warn("billing dunning reconcile: provider unreachable, not escalating", {
|
|
905
905
|
tenant: e,
|
|
906
906
|
source: t.source,
|
|
907
907
|
message: t.message
|
|
@@ -935,7 +935,7 @@ var fe = (e, t, n, r) => ({
|
|
|
935
935
|
return a;
|
|
936
936
|
}), O = () => v.gen(function* () {
|
|
937
937
|
let e = yield* d.stores.subscriptions.listByStatus("pastDue"), t = 0;
|
|
938
|
-
for (let n of e) yield* D(n.tenantId).pipe(v.
|
|
938
|
+
for (let n of e) yield* D(n.tenantId).pipe(v.catch((e) => v.sync(() => {
|
|
939
939
|
J.warn("billing dunning sweep: tenant failed", {
|
|
940
940
|
tenant: n.tenantId,
|
|
941
941
|
message: e.message
|
|
@@ -989,7 +989,7 @@ var fe = (e, t, n, r) => ({
|
|
|
989
989
|
});
|
|
990
990
|
case "customerUnlinked": return d.stores.customers.removeByProviderCustomerId(e.providerCustomerId).pipe(v.asVoid);
|
|
991
991
|
}
|
|
992
|
-
}, A = (e) => k(e).pipe(v.
|
|
992
|
+
}, A = (e) => k(e).pipe(v.andThen(ne(e))), re = (t, r) => v.gen(function* () {
|
|
993
993
|
yield* c(t, "change"), yield* R(t);
|
|
994
994
|
let o = yield* d.stores.subscriptions.getByTenant(t);
|
|
995
995
|
if (o === null) return yield* v.fail(new e({
|
|
@@ -1118,7 +1118,7 @@ var fe = (e, t, n, r) => ({
|
|
|
1118
1118
|
};
|
|
1119
1119
|
}), I = (e) => {
|
|
1120
1120
|
let n = t.isTenantClosed;
|
|
1121
|
-
return n === void 0 ? v.succeed(!1) : n(e).pipe(v.
|
|
1121
|
+
return n === void 0 ? v.succeed(!1) : n(e).pipe(v.catch((t) => v.sync(() => (J.warn("billing: could not tell whether a tenant is closed — leaving it", {
|
|
1122
1122
|
tenant: e,
|
|
1123
1123
|
message: t.message
|
|
1124
1124
|
}), !1))));
|
|
@@ -1166,7 +1166,7 @@ var fe = (e, t, n, r) => ({
|
|
|
1166
1166
|
};
|
|
1167
1167
|
}), se = (e) => v.forEach(e, (e) => {
|
|
1168
1168
|
let n = t.onEvent?.[e._tag];
|
|
1169
|
-
return n === void 0 ? v.void : n(e).pipe(v.
|
|
1169
|
+
return n === void 0 ? v.void : n(e).pipe(v.catchCause((t) => v.sync(() => {
|
|
1170
1170
|
J.error("billing: onEvent failed for a correction — the next sync of this tenant retries it", {
|
|
1171
1171
|
event: e._tag,
|
|
1172
1172
|
cause: String(t)
|
|
@@ -1235,7 +1235,7 @@ var fe = (e, t, n, r) => ({
|
|
|
1235
1235
|
J.error("billing reconcile: a closed tenant still has a paying subscription — closing it", {
|
|
1236
1236
|
tenant: t,
|
|
1237
1237
|
subscription: r.providerSubscriptionId
|
|
1238
|
-
}), (yield* F(t).pipe(v.map(() => !0), v.
|
|
1238
|
+
}), (yield* F(t).pipe(v.map(() => !0), v.catch((e) => v.sync(() => (J.error("billing reconcile: closing a closed tenant failed — retried next run", {
|
|
1239
1239
|
tenant: t,
|
|
1240
1240
|
message: e.message
|
|
1241
1241
|
}), !1))))) && p.add(t), c.add(t);
|
|
@@ -1335,7 +1335,7 @@ var fe = (e, t, n, r) => ({
|
|
|
1335
1335
|
if (!e.global && !e.sticky) return e;
|
|
1336
1336
|
let t = St.get(e);
|
|
1337
1337
|
return t === void 0 && (t = new RegExp(e.source, e.flags.replace(/[gy]/g, "")), St.set(e, t)), t;
|
|
1338
|
-
}, wt = (e, t) => typeof e == "string" ? t === e : Ct(e).test(t), Tt = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "rpc" && (e.kind === void 0 || e.kind === n) && wt(e.match, t)).map(([e]) => e), Et = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "cdc" && e.table === t && (e.op ?? "insert") === n).map(([e]) => e), Dt = (e) => Object.entries(e).filter(([, e]) => e.from === "ai").map(([e, t]) => [e, t.metric ?? "tokens"]), Ot = (e) => e.tenantId, kt = (e, t, n, r) => v.forEach(n, (n) => e.reportUsage(t, n, r).pipe(v.
|
|
1338
|
+
}, wt = (e, t) => typeof e == "string" ? t === e : Ct(e).test(t), Tt = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "rpc" && (e.kind === void 0 || e.kind === n) && wt(e.match, t)).map(([e]) => e), Et = (e, t, n) => Object.entries(e).filter(([, e]) => e.from === "cdc" && e.table === t && (e.op ?? "insert") === n).map(([e]) => e), Dt = (e) => Object.entries(e).filter(([, e]) => e.from === "ai").map(([e, t]) => [e, t.metric ?? "tokens"]), Ot = (e) => e.tenantId, kt = (e, t, n, r) => v.forEach(n, (n) => e.reportUsage(t, n, r).pipe(v.catch(() => v.void)), { discard: !0 }), At = (e, t) => (n, r) => {
|
|
1339
1339
|
let i = Tt(t, r.tag, r.kind);
|
|
1340
1340
|
if (i.length === 0) return n;
|
|
1341
1341
|
let a = Ot(r.subject);
|
|
@@ -1386,10 +1386,10 @@ var fe = (e, t, n, r) => ({
|
|
|
1386
1386
|
for (let [t, n] of i) for (let [r, i] of c) {
|
|
1387
1387
|
let a = n === "costMicroUsd" ? i.cost : i.tokens;
|
|
1388
1388
|
if (a <= 0) continue;
|
|
1389
|
-
let o = a - (yield* e.checkEntitlement(r, t, 0).pipe(v.map((e) => e.used), v.
|
|
1390
|
-
o > 0 && (yield* e.reportUsage(r, t, o).pipe(v.
|
|
1389
|
+
let o = a - (yield* e.checkEntitlement(r, t, 0).pipe(v.map((e) => e.used), v.catch(() => v.succeed(0))));
|
|
1390
|
+
o > 0 && (yield* e.reportUsage(r, t, o).pipe(v.catch(() => v.void)));
|
|
1391
1391
|
}
|
|
1392
|
-
}), It = (e, t, n, r) => Ft(e, t, n, r).pipe(v.andThen(e.flushUsage().pipe(v.
|
|
1392
|
+
}), It = (e, t, n, r) => Ft(e, t, n, r).pipe(v.andThen(e.flushUsage().pipe(v.catch(() => v.void)))), Lt = (e) => Object.values(e).some((e) => e.from === "cdc"), Rt = (e) => e === void 0 ? 0 : e === "unlimited" ? Infinity : e, zt = (e) => {
|
|
1393
1393
|
let t = /* @__PURE__ */ new Map();
|
|
1394
1394
|
for (let [n, r] of Object.entries(e)) r.priceId !== void 0 && t.set(r.priceId, n);
|
|
1395
1395
|
return {
|
|
@@ -2483,7 +2483,7 @@ var fe = (e, t, n, r) => ({
|
|
|
2483
2483
|
i.createdAt !== null && Date.now() - i.createdAt.getTime() > 3e5 && e.onLateDelivery?.();
|
|
2484
2484
|
for (let t of s) {
|
|
2485
2485
|
let n = e.onEvent?.[t._tag];
|
|
2486
|
-
n !== void 0 && await v.runPromise(n(t).pipe(v.
|
|
2486
|
+
n !== void 0 && await v.runPromise(n(t).pipe(v.tapCause((e) => v.sync(() => {
|
|
2487
2487
|
Hn.error("billing onEvent side effect failed — answering 500 so the provider redelivers", {
|
|
2488
2488
|
event: t._tag,
|
|
2489
2489
|
cause: String(e)
|
|
@@ -2868,7 +2868,7 @@ var fe = (e, t, n, r) => ({
|
|
|
2868
2868
|
handler: () => E.stores.usage.pending().pipe(v.map((e) => ({
|
|
2869
2869
|
kind: "json",
|
|
2870
2870
|
data: { usage: e }
|
|
2871
|
-
})), v.
|
|
2871
|
+
})), v.catch((e) => v.succeed(A(500, `${e.source} failed`))))
|
|
2872
2872
|
}], j = [
|
|
2873
2873
|
{
|
|
2874
2874
|
schema: e,
|
|
@@ -2915,7 +2915,7 @@ var fe = (e, t, n, r) => ({
|
|
|
2915
2915
|
let o = 0;
|
|
2916
2916
|
for (let e of a) await v.runPromise(T.syncCustomer(e).pipe(v.flatMap((e) => v.forEach(e.events, (e) => {
|
|
2917
2917
|
let n = t.onEvent?.[e._tag];
|
|
2918
|
-
return n === void 0 ? v.void : n(e).pipe(v.
|
|
2918
|
+
return n === void 0 ? v.void : n(e).pipe(v.catchCause((t) => v.sync(() => {
|
|
2919
2919
|
V.error("billing catch-up: onEvent failed for a correction", {
|
|
2920
2920
|
event: e._tag,
|
|
2921
2921
|
cause: String(t)
|
|
@@ -2923,7 +2923,7 @@ var fe = (e, t, n, r) => ({
|
|
|
2923
2923
|
})));
|
|
2924
2924
|
}, { discard: !0 })), v.tap(() => v.sync(() => {
|
|
2925
2925
|
o += 1;
|
|
2926
|
-
})), v.
|
|
2926
|
+
})), v.catch((e) => v.sync(() => {
|
|
2927
2927
|
V.error("billing catch-up: syncing a customer failed — the provider redelivers its events", { message: e.message });
|
|
2928
2928
|
}))));
|
|
2929
2929
|
V.info("billing catch-up", {
|
|
@@ -3001,7 +3001,7 @@ var fe = (e, t, n, r) => ({
|
|
|
3001
3001
|
if (t === void 0) return;
|
|
3002
3002
|
let a = Date.now();
|
|
3003
3003
|
(async () => {
|
|
3004
|
-
await Wt(t, Ut(a, n), e, a) && await v.runPromise((i ? It(T, t, r) : T.flushUsage()).pipe(v.
|
|
3004
|
+
await Wt(t, Ut(a, n), e, a) && await v.runPromise((i ? It(T, t, r) : T.flushUsage()).pipe(v.catch(() => v.void)));
|
|
3005
3005
|
})();
|
|
3006
3006
|
}, n), B.unref?.();
|
|
3007
3007
|
}
|
package/dist/rpc.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ActionProcedureDescriptor } from '@voltro/protocol';
|
|
|
2
2
|
import { PluginRpcClientDescriptor } from '@voltro/protocol';
|
|
3
3
|
import { QueryProcedureDescriptor } from '@voltro/protocol';
|
|
4
4
|
import { Schema } from 'effect';
|
|
5
|
+
import { YieldableError } from 'effect/Cause';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A billing failure. `transient: true` marks failures the service retries
|
|
@@ -13,15 +14,13 @@ import { Schema } from 'effect';
|
|
|
13
14
|
declare class BillingError extends BillingError_base {
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
declare const BillingError_base: Schema.
|
|
17
|
-
readonly _tag: Schema.tag<"BillingError">;
|
|
18
|
-
} & {
|
|
17
|
+
declare const BillingError_base: Schema.Class<BillingError, Schema.TaggedStruct<"BillingError", {
|
|
19
18
|
/** Where the failure originated — provider name or an internal stage. */
|
|
20
|
-
source:
|
|
21
|
-
message:
|
|
19
|
+
readonly source: Schema.String;
|
|
20
|
+
readonly message: Schema.String;
|
|
22
21
|
/** Retryable? The service retries `transient` failures on a Schedule. */
|
|
23
|
-
transient:
|
|
24
|
-
}>;
|
|
22
|
+
readonly transient: Schema.Boolean;
|
|
23
|
+
}>, YieldableError>;
|
|
25
24
|
|
|
26
25
|
/** Declared for the codegen (`VoltroPlugin.rpcClientDescriptors`) so each tag is
|
|
27
26
|
* emitted into the generated client group. Kept in lockstep with the exports. */
|
|
@@ -30,33 +29,33 @@ export declare const billingRpcClientImports: ReadonlyArray<PluginRpcClientDescr
|
|
|
30
29
|
/** The customer's own cancellation: active until the paid period ends, then
|
|
31
30
|
* ended. `cancelAt` on the answer is when. */
|
|
32
31
|
export declare const cancelSubscriptionDescriptor: ActionProcedureDescriptor<"billing.cancel", Schema.Struct<{}>, Schema.Struct<{
|
|
33
|
-
subscription: Schema.Struct<{
|
|
34
|
-
plan:
|
|
35
|
-
status:
|
|
36
|
-
quantity:
|
|
37
|
-
currentPeriodEnd: Schema.NullOr<
|
|
38
|
-
cancelAt: Schema.NullOr<
|
|
32
|
+
readonly subscription: Schema.Struct<{
|
|
33
|
+
readonly plan: Schema.String;
|
|
34
|
+
readonly status: Schema.String;
|
|
35
|
+
readonly quantity: Schema.Number;
|
|
36
|
+
readonly currentPeriodEnd: Schema.NullOr<Schema.String>;
|
|
37
|
+
readonly cancelAt: Schema.NullOr<Schema.String>;
|
|
39
38
|
}>;
|
|
40
39
|
}>, typeof BillingError>;
|
|
41
40
|
|
|
42
41
|
export declare const changePlanDescriptor: ActionProcedureDescriptor<"billing.changePlan", Schema.Struct<{
|
|
43
|
-
plan:
|
|
42
|
+
readonly plan: Schema.String;
|
|
44
43
|
}>, Schema.Struct<{
|
|
45
|
-
plan:
|
|
46
|
-
quantity:
|
|
44
|
+
readonly plan: Schema.String;
|
|
45
|
+
readonly quantity: Schema.Number;
|
|
47
46
|
/** Integer minor-unit settlement: > 0 charge, < 0 credit, 0 at boundary. */
|
|
48
|
-
prorationMinor:
|
|
49
|
-
currency:
|
|
47
|
+
readonly prorationMinor: Schema.Number;
|
|
48
|
+
readonly currency: Schema.String;
|
|
50
49
|
}>, typeof BillingError>;
|
|
51
50
|
|
|
52
51
|
export declare const changeSeatsDescriptor: ActionProcedureDescriptor<"billing.changeSeats", Schema.Struct<{
|
|
53
|
-
quantity:
|
|
52
|
+
readonly quantity: Schema.Number;
|
|
54
53
|
}>, Schema.Struct<{
|
|
55
|
-
plan:
|
|
56
|
-
quantity:
|
|
54
|
+
readonly plan: Schema.String;
|
|
55
|
+
readonly quantity: Schema.Number;
|
|
57
56
|
/** Integer minor-unit settlement: > 0 charge, < 0 credit, 0 at boundary. */
|
|
58
|
-
prorationMinor:
|
|
59
|
-
currency:
|
|
57
|
+
readonly prorationMinor: Schema.Number;
|
|
58
|
+
readonly currency: Schema.String;
|
|
60
59
|
}>, typeof BillingError>;
|
|
61
60
|
|
|
62
61
|
/**
|
|
@@ -67,85 +66,85 @@ export declare const changeSeatsDescriptor: ActionProcedureDescriptor<"billing.c
|
|
|
67
66
|
* would fail encode at the boundary).
|
|
68
67
|
*/
|
|
69
68
|
export declare const entitlementStatusDescriptor: QueryProcedureDescriptor<"billing.entitlementStatus", Schema.Struct<{}>, Schema.Struct<{
|
|
70
|
-
plan:
|
|
71
|
-
billedPlan:
|
|
72
|
-
status:
|
|
73
|
-
entitled:
|
|
74
|
-
inGrace:
|
|
75
|
-
graceEndsAt: Schema.NullOr<
|
|
76
|
-
lockedSince: Schema.NullOr<
|
|
77
|
-
lockout:
|
|
69
|
+
readonly plan: Schema.String;
|
|
70
|
+
readonly billedPlan: Schema.String;
|
|
71
|
+
readonly status: Schema.String;
|
|
72
|
+
readonly entitled: Schema.Boolean;
|
|
73
|
+
readonly inGrace: Schema.Boolean;
|
|
74
|
+
readonly graceEndsAt: Schema.NullOr<Schema.String>;
|
|
75
|
+
readonly lockedSince: Schema.NullOr<Schema.String>;
|
|
76
|
+
readonly lockout: Schema.String;
|
|
78
77
|
}>, typeof BillingError>;
|
|
79
78
|
|
|
80
79
|
/** The provider's invoice history. `hostedUrl` / `pdfUrl` are the provider's
|
|
81
80
|
* own pages — we never render an invoice ourselves. */
|
|
82
81
|
export declare const invoicesDescriptor: QueryProcedureDescriptor<"billing.invoices", Schema.Struct<{}>, Schema.Struct<{
|
|
83
|
-
invoices: Schema
|
|
84
|
-
providerInvoiceId:
|
|
85
|
-
amountMinor:
|
|
86
|
-
currency:
|
|
87
|
-
status:
|
|
88
|
-
createdAt: Schema.NullOr<
|
|
89
|
-
hostedUrl: Schema.NullOr<
|
|
90
|
-
pdfUrl: Schema.NullOr<
|
|
82
|
+
readonly invoices: Schema.$Array<Schema.Struct<{
|
|
83
|
+
readonly providerInvoiceId: Schema.String;
|
|
84
|
+
readonly amountMinor: Schema.Number;
|
|
85
|
+
readonly currency: Schema.String;
|
|
86
|
+
readonly status: Schema.String;
|
|
87
|
+
readonly createdAt: Schema.NullOr<Schema.String>;
|
|
88
|
+
readonly hostedUrl: Schema.NullOr<Schema.String>;
|
|
89
|
+
readonly pdfUrl: Schema.NullOr<Schema.String>;
|
|
91
90
|
}>>;
|
|
92
91
|
}>, typeof BillingError>;
|
|
93
92
|
|
|
94
93
|
export declare const portalUrlDescriptor: ActionProcedureDescriptor<"billing.portalUrl", Schema.Struct<{
|
|
95
|
-
returnUrl:
|
|
94
|
+
readonly returnUrl: Schema.String;
|
|
96
95
|
}>, Schema.Struct<{
|
|
97
|
-
url:
|
|
96
|
+
readonly url: Schema.String;
|
|
98
97
|
}>, typeof BillingError>;
|
|
99
98
|
|
|
100
99
|
/** What a plan/seat change would cost, from the provider's invoice preview.
|
|
101
100
|
* Quote THIS in a confirmation dialog — a locally-estimated figure differs
|
|
102
101
|
* from the charge by the provider's rounding, tax and credit balance. */
|
|
103
102
|
export declare const previewChangeDescriptor: ActionProcedureDescriptor<"billing.previewChange", Schema.Struct<{
|
|
104
|
-
plan: Schema.optional<
|
|
105
|
-
quantity: Schema.optional<
|
|
103
|
+
readonly plan: Schema.optional<Schema.String>;
|
|
104
|
+
readonly quantity: Schema.optional<Schema.Number>;
|
|
106
105
|
}>, Schema.Struct<{
|
|
107
|
-
plan:
|
|
108
|
-
quantity:
|
|
106
|
+
readonly plan: Schema.String;
|
|
107
|
+
readonly quantity: Schema.Number;
|
|
109
108
|
/** Integer minor-unit settlement: > 0 charge, < 0 credit, 0 at boundary. */
|
|
110
|
-
prorationMinor:
|
|
111
|
-
currency:
|
|
109
|
+
readonly prorationMinor: Schema.Number;
|
|
110
|
+
readonly currency: Schema.String;
|
|
112
111
|
}>, typeof BillingError>;
|
|
113
112
|
|
|
114
113
|
export declare const reportUsageDescriptor: ActionProcedureDescriptor<"billing.reportUsage", Schema.Struct<{
|
|
115
|
-
key:
|
|
116
|
-
quantity:
|
|
114
|
+
readonly key: Schema.String;
|
|
115
|
+
readonly quantity: Schema.Number;
|
|
117
116
|
}>, Schema.Struct<{
|
|
118
|
-
ok:
|
|
117
|
+
readonly ok: Schema.Boolean;
|
|
119
118
|
}>, typeof BillingError>;
|
|
120
119
|
|
|
121
120
|
/** Take back a cancellation before the period ends. */
|
|
122
121
|
export declare const resumeSubscriptionDescriptor: ActionProcedureDescriptor<"billing.resume", Schema.Struct<{}>, Schema.Struct<{
|
|
123
|
-
subscription: Schema.Struct<{
|
|
124
|
-
plan:
|
|
125
|
-
status:
|
|
126
|
-
quantity:
|
|
127
|
-
currentPeriodEnd: Schema.NullOr<
|
|
128
|
-
cancelAt: Schema.NullOr<
|
|
122
|
+
readonly subscription: Schema.Struct<{
|
|
123
|
+
readonly plan: Schema.String;
|
|
124
|
+
readonly status: Schema.String;
|
|
125
|
+
readonly quantity: Schema.Number;
|
|
126
|
+
readonly currentPeriodEnd: Schema.NullOr<Schema.String>;
|
|
127
|
+
readonly cancelAt: Schema.NullOr<Schema.String>;
|
|
129
128
|
}>;
|
|
130
129
|
}>, typeof BillingError>;
|
|
131
130
|
|
|
132
131
|
export declare const startCheckoutDescriptor: ActionProcedureDescriptor<"billing.startCheckout", Schema.Struct<{
|
|
133
|
-
plan:
|
|
134
|
-
successUrl:
|
|
135
|
-
cancelUrl:
|
|
132
|
+
readonly plan: Schema.String;
|
|
133
|
+
readonly successUrl: Schema.String;
|
|
134
|
+
readonly cancelUrl: Schema.String;
|
|
136
135
|
/** Seats to buy up front. Omit for 1. */
|
|
137
|
-
quantity: Schema.optional<
|
|
136
|
+
readonly quantity: Schema.optional<Schema.Number>;
|
|
138
137
|
}>, Schema.Struct<{
|
|
139
|
-
url:
|
|
138
|
+
readonly url: Schema.String;
|
|
140
139
|
}>, typeof BillingError>;
|
|
141
140
|
|
|
142
141
|
export declare const subscriptionDescriptor: QueryProcedureDescriptor<"billing.subscription", Schema.Struct<{}>, Schema.Struct<{
|
|
143
|
-
subscription: Schema.NullOr<Schema.Struct<{
|
|
144
|
-
plan:
|
|
145
|
-
status:
|
|
146
|
-
quantity:
|
|
147
|
-
currentPeriodEnd: Schema.NullOr<
|
|
148
|
-
cancelAt: Schema.NullOr<
|
|
142
|
+
readonly subscription: Schema.NullOr<Schema.Struct<{
|
|
143
|
+
readonly plan: Schema.String;
|
|
144
|
+
readonly status: Schema.String;
|
|
145
|
+
readonly quantity: Schema.Number;
|
|
146
|
+
readonly currentPeriodEnd: Schema.NullOr<Schema.String>;
|
|
147
|
+
readonly cancelAt: Schema.NullOr<Schema.String>;
|
|
149
148
|
}>>;
|
|
150
149
|
}>, typeof BillingError>;
|
|
151
150
|
|
|
@@ -153,12 +152,12 @@ export declare const subscriptionDescriptor: QueryProcedureDescriptor<"billing.s
|
|
|
153
152
|
* its current subscription. Call it on the return from checkout or the
|
|
154
153
|
* portal, and when a billing page loads. */
|
|
155
154
|
export declare const syncDescriptor: ActionProcedureDescriptor<"billing.sync", Schema.Struct<{}>, Schema.Struct<{
|
|
156
|
-
subscription: Schema.NullOr<Schema.Struct<{
|
|
157
|
-
plan:
|
|
158
|
-
status:
|
|
159
|
-
quantity:
|
|
160
|
-
currentPeriodEnd: Schema.NullOr<
|
|
161
|
-
cancelAt: Schema.NullOr<
|
|
155
|
+
readonly subscription: Schema.NullOr<Schema.Struct<{
|
|
156
|
+
readonly plan: Schema.String;
|
|
157
|
+
readonly status: Schema.String;
|
|
158
|
+
readonly quantity: Schema.Number;
|
|
159
|
+
readonly currentPeriodEnd: Schema.NullOr<Schema.String>;
|
|
160
|
+
readonly cancelAt: Schema.NullOr<Schema.String>;
|
|
162
161
|
}>>;
|
|
163
162
|
}>, typeof BillingError>;
|
|
164
163
|
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context } from 'effect';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
3
|
import { Schema } from 'effect';
|
|
4
|
+
import { YieldableError } from 'effect/Cause';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* What a billing call would do to what the tenant pays — the unit an app's
|
|
@@ -20,15 +21,13 @@ export declare type BillingAction = 'checkout' | 'change' | 'resume' | 'portal'
|
|
|
20
21
|
export declare class BillingError extends BillingError_base {
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
declare const BillingError_base: Schema.
|
|
24
|
-
readonly _tag: Schema.tag<"BillingError">;
|
|
25
|
-
} & {
|
|
24
|
+
declare const BillingError_base: Schema.Class<BillingError, Schema.TaggedStruct<"BillingError", {
|
|
26
25
|
/** Where the failure originated — provider name or an internal stage. */
|
|
27
|
-
source:
|
|
28
|
-
message:
|
|
26
|
+
readonly source: Schema.String;
|
|
27
|
+
readonly message: Schema.String;
|
|
29
28
|
/** Retryable? The service retries `transient` failures on a Schedule. */
|
|
30
|
-
transient:
|
|
31
|
-
}>;
|
|
29
|
+
readonly transient: Schema.Boolean;
|
|
30
|
+
}>, YieldableError>;
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* The normalized event union a `BillingProvider.normalizeEvent` produces
|
|
@@ -229,7 +228,7 @@ export declare interface BillingProvider {
|
|
|
229
228
|
export declare class BillingService extends BillingService_base {
|
|
230
229
|
}
|
|
231
230
|
|
|
232
|
-
declare const BillingService_base: Context.
|
|
231
|
+
declare const BillingService_base: Context.ServiceClass<BillingService, "@voltro/plugin-billing/BillingService", BillingServiceShape>;
|
|
233
232
|
|
|
234
233
|
export declare interface BillingServiceShape {
|
|
235
234
|
/** Read the tenant's subscription row; null when none exists. */
|
|
@@ -428,18 +427,16 @@ export declare interface EntitlementDecision {
|
|
|
428
427
|
export declare class EntitlementExceeded extends EntitlementExceeded_base {
|
|
429
428
|
}
|
|
430
429
|
|
|
431
|
-
declare const EntitlementExceeded_base: Schema.
|
|
432
|
-
readonly _tag: Schema.tag<"EntitlementExceeded">;
|
|
433
|
-
} & {
|
|
430
|
+
declare const EntitlementExceeded_base: Schema.Class<EntitlementExceeded, Schema.TaggedStruct<"EntitlementExceeded", {
|
|
434
431
|
/** The entitlement key that was exhausted (`'aiCalls'`). */
|
|
435
|
-
entitlement:
|
|
432
|
+
readonly entitlement: Schema.String;
|
|
436
433
|
/** The static limit for the caller's plan. */
|
|
437
|
-
limit:
|
|
434
|
+
readonly limit: Schema.Number;
|
|
438
435
|
/** How much was already used in the current window. */
|
|
439
|
-
used:
|
|
436
|
+
readonly used: Schema.Number;
|
|
440
437
|
/** The cost the rejected call would have charged. */
|
|
441
|
-
cost:
|
|
442
|
-
}>;
|
|
438
|
+
readonly cost: Schema.Number;
|
|
439
|
+
}>, YieldableError>;
|
|
443
440
|
|
|
444
441
|
/** An entitlement limit: a finite quota or the unbounded `'unlimited'`. */
|
|
445
442
|
export declare type EntitlementLimit = number | 'unlimited';
|
|
@@ -660,17 +657,15 @@ export declare interface SubscriptionChangeResult {
|
|
|
660
657
|
export declare class SubscriptionLocked extends SubscriptionLocked_base {
|
|
661
658
|
}
|
|
662
659
|
|
|
663
|
-
declare const SubscriptionLocked_base: Schema.
|
|
664
|
-
readonly
|
|
665
|
-
} & {
|
|
666
|
-
tenantId: typeof Schema.String;
|
|
660
|
+
declare const SubscriptionLocked_base: Schema.Class<SubscriptionLocked, Schema.TaggedStruct<"SubscriptionLocked", {
|
|
661
|
+
readonly tenantId: Schema.String;
|
|
667
662
|
/** The subscription status at the time of the refusal (`'pastDue'`). */
|
|
668
|
-
status:
|
|
663
|
+
readonly status: Schema.String;
|
|
669
664
|
/** ISO timestamp at which the grace period expired. */
|
|
670
|
-
lockedSince:
|
|
665
|
+
readonly lockedSince: Schema.String;
|
|
671
666
|
/** `'hard'` (limits fell to the free plan) or `'soft'` (report only). */
|
|
672
|
-
lockout:
|
|
673
|
-
}>;
|
|
667
|
+
readonly lockout: Schema.String;
|
|
668
|
+
}>, YieldableError>;
|
|
674
669
|
|
|
675
670
|
export declare type SubscriptionStatus = 'active' | 'trialing' | 'pastDue' | 'canceled' | 'incomplete';
|
|
676
671
|
|
package/dist/types.js
CHANGED
|
@@ -14,6 +14,6 @@ var n = class extends t.TaggedError()("BillingError", {
|
|
|
14
14
|
limit: t.Number,
|
|
15
15
|
used: t.Number,
|
|
16
16
|
cost: t.Number
|
|
17
|
-
}) {}, a = "free", o = class extends e.
|
|
17
|
+
}) {}, a = "free", o = class extends e.Service()("@voltro/plugin-billing/BillingService") {};
|
|
18
18
|
//#endregion
|
|
19
19
|
export { n as BillingError, o as BillingService, a as DEFAULT_PLAN, i as EntitlementExceeded, r as SubscriptionLocked };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/plugin-billing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.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,14 +48,14 @@
|
|
|
48
48
|
"node": ">=24.0.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@voltro/database": "0.
|
|
52
|
-
"@voltro/env": "0.
|
|
53
|
-
"@voltro/logger": "0.
|
|
54
|
-
"@voltro/plugin-webhooks": "0.
|
|
55
|
-
"@voltro/protocol": "0.
|
|
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"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"effect": "
|
|
58
|
+
"effect": "4.0.1",
|
|
59
59
|
"react": "^19.0.0",
|
|
60
60
|
"stripe": "^22.6.1"
|
|
61
61
|
},
|