@fonderie/billing 5.3.1 → 6.0.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.
@@ -1,10 +1,19 @@
1
1
  import { Middleware, IFonderieContext } from '@fonderie/core';
2
2
  import { IStoreAdapter } from '@fonderie/store';
3
- import { SubscriberType, PolicyEntry } from './types.cjs';
3
+ import { SubscriberType, BillingInterval, PolicyEntry, IWalletRate } from './types.cjs';
4
4
 
5
5
  interface IBillingEvent {
6
6
  type: string;
7
7
  subscription: INormalizedSubscription | null;
8
+ payment?: INormalizedPayment | null;
9
+ }
10
+ interface INormalizedPayment {
11
+ sessionId: string;
12
+ providerTxId: string | null;
13
+ amountTotal: bigint | null;
14
+ currency: string | null;
15
+ paymentStatus: string | null;
16
+ metadata: Record<string, string>;
8
17
  }
9
18
  interface INormalizedSubscription {
10
19
  subscriberType: SubscriberType;
@@ -21,14 +30,14 @@ interface INormalizedSubscription {
21
30
  currentPeriodEnd: Date;
22
31
  cancelAtPeriodEnd: boolean;
23
32
  trialEndsAt: Date | null;
24
- interval: 'month' | 'year';
33
+ interval: BillingInterval;
25
34
  }
26
35
  interface IResolvedPrice {
27
36
  priceId: string;
28
37
  lookupKey: string | null;
29
- unitAmount: number;
38
+ unitAmount: bigint;
30
39
  currency: string;
31
- interval: 'month' | 'year';
40
+ interval: BillingInterval;
32
41
  nickname: string | null;
33
42
  productId: string;
34
43
  active: boolean;
@@ -54,6 +63,20 @@ interface IBillingProvider {
54
63
  }): Promise<{
55
64
  url: string;
56
65
  }>;
66
+ createPaymentCheckoutSession?(opts: {
67
+ customerId: string;
68
+ amount: bigint;
69
+ currency: string;
70
+ name: string;
71
+ quantity?: number;
72
+ priceId?: string;
73
+ metadata: Record<string, string>;
74
+ successUrl: string;
75
+ cancelUrl: string;
76
+ }): Promise<{
77
+ url: string;
78
+ sessionId: string;
79
+ }>;
57
80
  resolvePriceById(priceId: string): Promise<IResolvedPrice | null>;
58
81
  resolvePricesByLookupKey(lookupKeys: string[]): Promise<Map<string, IResolvedPrice>>;
59
82
  updateSubscription(opts: {
@@ -88,11 +111,12 @@ interface IBillingPlanPrice {
88
111
  /** Stripe price id. Used for hydration and as a lookup_key fallback. */
89
112
  priceId?: string;
90
113
  /**
91
- * Display amount in cents the seed value written to fonderie_plans and the
92
- * fallback shown (flagged pricingStale) when hydration is off or Stripe is
93
- * unreachable. When hydration resolves a live price, the live amount wins.
114
+ * Display amount in the smallest currency unit (bigint, e.g. 1999n = $19.99)
115
+ * the seed value written to fonderie_plans and the fallback shown (flagged
116
+ * pricingStale) when hydration is off or Stripe is unreachable. When
117
+ * hydration resolves a live price, the live amount wins.
94
118
  */
95
- amount?: number;
119
+ amount?: bigint;
96
120
  }
97
121
  /**
98
122
  * Read-through pricing: amount/currency come from Stripe (source of truth) rather
@@ -113,6 +137,28 @@ interface IBillingPlanDefaults {
113
137
  warnAt?: number;
114
138
  buffer?: number;
115
139
  }
140
+ /**
141
+ * Per-plan wallet economics. Requires config.wallet to be set — a plan-level
142
+ * wallet without the global opt-in is ignored (with a boot warning).
143
+ */
144
+ interface IBillingPlanWallet {
145
+ /** Overrides the global wallet currency for this plan's grants and rates. */
146
+ currency?: string;
147
+ /** Display precision override. */
148
+ precision?: number;
149
+ /**
150
+ * Credits auto-granted once per grantPeriod, applied lazily by withBilling
151
+ * on the subscriber's first request of the period. Only granted while the
152
+ * subscription is active or trialing (no new credit while payment fails).
153
+ */
154
+ grantAmount?: bigint;
155
+ /** Grant cadence for grantAmount. Default 'month'. */
156
+ grantPeriod?: 'month' | 'week' | 'day';
157
+ /** How far below zero rate debits may take the balance. Default 0n (block at zero). */
158
+ overdraftLimit?: bigint;
159
+ /** Per-metric unit costs, e.g. { 'sms:send': { cost: 75n, unit: 'msg' } }. */
160
+ rates?: Record<string, IWalletRate>;
161
+ }
116
162
  interface IBillingPlan {
117
163
  name: string;
118
164
  description?: string;
@@ -122,6 +168,7 @@ interface IBillingPlan {
122
168
  yearly?: IBillingPlanPrice;
123
169
  defaults?: IBillingPlanDefaults;
124
170
  policy?: Record<string, PolicyEntry>;
171
+ wallet?: IBillingPlanWallet;
125
172
  metadata?: Record<string, unknown>;
126
173
  }
127
174
  type RateLimitBackendConfig = 'memory' | 'db' | ICounterBackend;
@@ -129,6 +176,56 @@ interface IBillingNotificationsConfig {
129
176
  warnAt?: boolean;
130
177
  softHit?: boolean;
131
178
  }
179
+ /**
180
+ * A purchasable credit top-up, synced to fonderie_credit_packs at boot (same
181
+ * pattern as plans). Purchases go through the provider's one-time checkout;
182
+ * the payment webhook credits `credits` to the buyer's wallet.
183
+ */
184
+ interface IBillingCreditPack {
185
+ /** Stable identifier used by POST /billing/wallet/checkout, e.g. 'small'. */
186
+ id: string;
187
+ name: string;
188
+ /** Wallet credits granted on purchase, in the smallest wallet unit. */
189
+ credits: bigint;
190
+ /** Purchase price in the provider's smallest currency unit. */
191
+ priceAmount: bigint;
192
+ /**
193
+ * ISO 4217 PAYMENT currency for the provider charge; defaults to the
194
+ * buyer's wallet currency. Credits always land in the buyer's wallet
195
+ * currency regardless of what the charge was priced in.
196
+ */
197
+ currency?: string;
198
+ /** Existing provider Price id — used instead of the ad-hoc priceAmount. */
199
+ priceId?: string;
200
+ /** Inactive packs stay in the DB but can no longer be checked out. */
201
+ active?: boolean;
202
+ metadata?: Record<string, unknown>;
203
+ }
204
+ /**
205
+ * Opt-in stored-value wallet. Presence of this object activates the wallet
206
+ * subsystem (routes, credit packs, per-plan grants and rates); leaving it out
207
+ * changes nothing for existing subscription-only consumers.
208
+ */
209
+ interface IBillingWalletConfig {
210
+ /** Default wallet currency when a plan doesn't override it. Default 'USD'. */
211
+ currency?: string;
212
+ /** Display precision — decimal places of the smallest unit. Default 2. */
213
+ precision?: number;
214
+ /**
215
+ * Bearer token guarding POST /billing/wallet/grant (manual support/ops
216
+ * grants). The route is only registered when a token is configured.
217
+ */
218
+ adminToken?: string;
219
+ /**
220
+ * Signing secret for POST /billing/webhook/payment. REQUIRED for pack
221
+ * purchases: the route answers 500 until it is set, and it deliberately
222
+ * does NOT fall back to the subscription webhook's secret — per-endpoint
223
+ * secrets keep a delivery captured for one endpoint from replaying
224
+ * against the other.
225
+ */
226
+ webhookSecret?: string;
227
+ creditPacks?: IBillingCreditPack[];
228
+ }
132
229
  interface IBillingConfig {
133
230
  provider: IBillingProvider;
134
231
  plans: IBillingPlan[];
@@ -140,6 +237,7 @@ interface IBillingConfig {
140
237
  };
141
238
  notifications?: IBillingNotificationsConfig;
142
239
  pricing?: IBillingPricingConfig;
240
+ wallet?: IBillingWalletConfig;
143
241
  }
144
242
  declare const MESSAGE_KEYS: {
145
243
  readonly limitWarning: "billing.limit-warning";
@@ -153,4 +251,4 @@ declare function requirePlan(plans: string | string[], store: IStoreAdapter, ctx
153
251
 
154
252
  declare function withBilling(store: IStoreAdapter, config: IBillingConfig, backend: ICounterBackend): Middleware;
155
253
 
156
- export { type BillingMessageKey as B, type IBillingConfig as I, MESSAGE_KEYS as M, type RateLimitBackendConfig as R, type IBillingProvider as a, type IResolvedPrice as b, type IBillingEvent as c, type ICounterBackend as d, type IBillingPlan as e, type IBillingNotificationsConfig as f, type IBillingPlanDefaults as g, type IBillingPlanPrice as h, type IBillingPricingConfig as i, requirePlan as r, withBilling as w };
254
+ export { type BillingMessageKey as B, type IBillingConfig as I, MESSAGE_KEYS as M, type RateLimitBackendConfig as R, type IBillingProvider as a, type IResolvedPrice as b, type IBillingEvent as c, type IBillingPlan as d, type ICounterBackend as e, type IBillingCreditPack as f, type IBillingNotificationsConfig as g, type IBillingPlanDefaults as h, type IBillingPlanPrice as i, type IBillingPlanWallet as j, type IBillingPricingConfig as k, type IBillingWalletConfig as l, type INormalizedPayment as m, requirePlan as r, withBilling as w };