@billing-saas/usage-sdk 0.1.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/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # @billing-saas/usage-sdk
2
+
3
+ SDK TypeScript pour intégrer le **PU Billing SaaS** dans votre application Next.js.
4
+
5
+ Gérez vos plans, abonnements, checkout Stripe et feature gates en quelques lignes.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @billing-saas/usage-sdk
11
+ ```
12
+
13
+ ## Démarrage rapide
14
+
15
+ ### 1. Créez votre compte tenant
16
+
17
+ Rendez-vous sur [dev.billingsaas.187.124.95.146.sslip.io/tenant/register](http://dev.billingsaas.187.124.95.146.sslip.io/tenant/register), créez votre espace et copiez votre clé API depuis l'onboarding.
18
+
19
+ ### 2. Configurez `.env.local`
20
+
21
+ ```env
22
+ BILLING_API_URL=http://dev.billingsaas.187.124.95.146.sslip.io/api
23
+ BILLING_API_KEY=bsk_live_...
24
+ NEXT_PUBLIC_APP_URL=https://votre-app.com
25
+ ```
26
+
27
+ > ⚠️ **Ne jamais exposer `BILLING_API_KEY` côté client.** Pas de `NEXT_PUBLIC_`.
28
+
29
+ ### 3. Initialisez le client (serveur uniquement)
30
+
31
+ ```ts
32
+ // src/lib/billing.ts
33
+ import { BillingClient } from '@billing-saas/usage-sdk'
34
+
35
+ export const billing = new BillingClient({
36
+ apiKey: process.env.BILLING_API_KEY!,
37
+ baseUrl: process.env.BILLING_API_URL!,
38
+ })
39
+ ```
40
+
41
+ ### 4. Route API — plans
42
+
43
+ ```ts
44
+ // src/app/api/billing/plans/route.ts
45
+ import { NextResponse } from 'next/server'
46
+ import { billing } from '@/lib/billing'
47
+
48
+ export async function GET() {
49
+ const plans = await billing.plans.list()
50
+ return NextResponse.json({ plans })
51
+ }
52
+ ```
53
+
54
+ ### 5. Route API — checkout Stripe
55
+
56
+ ```ts
57
+ // src/app/api/billing/checkout/route.ts
58
+ import { NextRequest, NextResponse } from 'next/server'
59
+ import { billing } from '@/lib/billing'
60
+
61
+ export async function POST(req: NextRequest) {
62
+ const { planId, period = 'monthly' } = await req.json()
63
+ const user = await getSession() // votre système d'auth
64
+
65
+ const result = await billing.customers.checkout(user.id, {
66
+ plan_id: planId,
67
+ period,
68
+ success_url: `${process.env.NEXT_PUBLIC_APP_URL}/billing/success`,
69
+ cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
70
+ })
71
+
72
+ return NextResponse.json(result)
73
+ }
74
+ ```
75
+
76
+ ### 6. Abonnement actif
77
+
78
+ ```ts
79
+ // src/app/api/billing/subscription/route.ts
80
+ import { NextResponse } from 'next/server'
81
+ import { billing } from '@/lib/billing'
82
+
83
+ export async function GET() {
84
+ const user = await getSession()
85
+
86
+ // Synchronise le customer (idempotent)
87
+ await billing.customers.upsert({
88
+ external_id: user.id,
89
+ email: user.email,
90
+ name: user.name,
91
+ })
92
+
93
+ const { subscription } = await billing.customers.subscription(user.id)
94
+ return NextResponse.json({ subscription })
95
+ }
96
+ ```
97
+
98
+ ## API
99
+
100
+ ### `BillingClient`
101
+
102
+ ```ts
103
+ const billing = new BillingClient({ apiKey: string, baseUrl?: string })
104
+ ```
105
+
106
+ #### Plans
107
+
108
+ | Méthode | Description |
109
+ |---|---|
110
+ | `billing.plans.list()` | Liste tous les plans disponibles |
111
+ | `billing.plans.get(id)` | Récupère un plan par ID |
112
+
113
+ #### Customers
114
+
115
+ | Méthode | Description |
116
+ |---|---|
117
+ | `billing.customers.upsert(input)` | Crée ou met à jour un customer (idempotent) |
118
+ | `billing.customers.get(externalId)` | Récupère un customer |
119
+ | `billing.customers.subscription(externalId)` | Abonnement actif (null si aucun) |
120
+ | `billing.customers.checkout(externalId, input)` | Crée une Stripe Checkout Session |
121
+ | `billing.customers.portal(externalId, returnUrl)` | URL vers le Stripe Billing Portal |
122
+ | `billing.customers.invoices(externalId)` | Historique des factures |
123
+
124
+ ## Types
125
+
126
+ ```ts
127
+ interface SdkPlan {
128
+ id: number
129
+ name: string
130
+ price_monthly: string
131
+ price_yearly: string
132
+ features: string[]
133
+ }
134
+
135
+ interface SdkSubscription {
136
+ id: number
137
+ status: 'active' | 'trialing' | 'past_due' | 'canceled'
138
+ plan: SdkPlan
139
+ current_period_end: string
140
+ }
141
+
142
+ interface CheckoutInput {
143
+ plan_id: number
144
+ period?: 'monthly' | 'yearly'
145
+ success_url: string
146
+ cancel_url: string
147
+ }
148
+ ```
149
+
150
+ ## Licence
151
+
152
+ MIT
@@ -0,0 +1,281 @@
1
+ interface UsageEventInput {
2
+ metric: string;
3
+ quantity: number;
4
+ /** Clé unique pour déduplication. Générée automatiquement si absente. */
5
+ idempotencyKey?: string;
6
+ recordedAt?: Date | string;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ interface UsageEventResponse {
10
+ id: number;
11
+ user_id: number;
12
+ subscription_id: number | null;
13
+ org_id: string | null;
14
+ metric: string;
15
+ quantity: string;
16
+ idempotency_key: string;
17
+ recorded_at: string;
18
+ billed_at: string | null;
19
+ metadata: Record<string, unknown> | null;
20
+ created_at: string;
21
+ updated_at: string;
22
+ }
23
+ interface BatchResult {
24
+ created: number;
25
+ duplicate: number;
26
+ keys: {
27
+ created: string[];
28
+ duplicate: string[];
29
+ };
30
+ }
31
+ interface UsageSummary {
32
+ period_start: string;
33
+ period_end: string;
34
+ usage: Record<string, {
35
+ metric: string;
36
+ total: string;
37
+ events: number;
38
+ }>;
39
+ }
40
+ interface QuotaMetric {
41
+ used: number;
42
+ limit: number | null;
43
+ pct: number | null;
44
+ status: 'ok' | 'warning' | 'critical' | 'exceeded' | 'unlimited';
45
+ }
46
+ type QuotaStatus = Record<string, QuotaMetric>;
47
+ interface UsageClientOptions {
48
+ baseUrl: string;
49
+ apiToken: string;
50
+ /** Timeout en ms (défaut : 10 000) */
51
+ timeout?: number;
52
+ }
53
+
54
+ declare class UsageError extends Error {
55
+ readonly statusCode: number;
56
+ readonly body: unknown;
57
+ constructor(message: string, statusCode: number, body: unknown);
58
+ }
59
+ interface UsageClientAdvancedOptions extends UsageClientOptions {
60
+ /** Active le buffering automatique (défaut: false) */
61
+ autoQueue?: boolean;
62
+ /** Taille max du buffer avant flush forcé (défaut: 50) */
63
+ queueMaxSize?: number;
64
+ /** Intervalle de flush en ms (défaut: 5000) */
65
+ flushInterval?: number;
66
+ /** Nombre de tentatives en cas d'erreur 5xx (défaut: 3) */
67
+ maxRetries?: number;
68
+ /** Délai initial entre retries en ms (défaut: 300) */
69
+ retryDelay?: number;
70
+ /** Callback appelé avant chaque envoi (enrichissement, logging) */
71
+ beforeSend?: (event: UsageEventInput) => UsageEventInput | null;
72
+ }
73
+ /**
74
+ * Client TypeScript pour l'API Usage de Billing SaaS.
75
+ *
76
+ * @example
77
+ * // Mode direct
78
+ * const client = new UsageClient({ baseUrl: '...', apiToken: '...' })
79
+ * await client.record({ metric: 'api_calls', quantity: 1 })
80
+ *
81
+ * @example
82
+ * // Mode queue (batch automatique)
83
+ * const client = new UsageClient({ baseUrl: '...', apiToken: '...', autoQueue: true })
84
+ * client.track({ metric: 'page_view', quantity: 1 }) // non-bloquant
85
+ */
86
+ declare class UsageClient {
87
+ private readonly baseUrl;
88
+ private readonly apiToken;
89
+ private readonly timeout;
90
+ private readonly maxRetries;
91
+ private readonly retryDelay;
92
+ private readonly beforeSend?;
93
+ private queue;
94
+ constructor(opts: UsageClientAdvancedOptions);
95
+ /**
96
+ * Enregistre un événement de façon synchrone (direct API call).
97
+ */
98
+ record(event: UsageEventInput): Promise<UsageEventResponse>;
99
+ /**
100
+ * Ajoute un événement dans le buffer (non-bloquant).
101
+ * Nécessite `autoQueue: true` à la construction.
102
+ */
103
+ track(event: UsageEventInput): void;
104
+ /**
105
+ * Vide le buffer immédiatement (utile avant navigations SPA).
106
+ */
107
+ flush(): Promise<void>;
108
+ /**
109
+ * Enregistre jusqu'à 100 événements en une seule requête.
110
+ */
111
+ batch(events: UsageEventInput[]): Promise<BatchResult>;
112
+ summary(): Promise<UsageSummary>;
113
+ quota(): Promise<QuotaStatus>;
114
+ /** Libère les ressources (timer de la queue). */
115
+ destroy(): void;
116
+ private applyBeforeSend;
117
+ private normalizeEvent;
118
+ private generateKey;
119
+ private post;
120
+ private get;
121
+ private request;
122
+ }
123
+
124
+ interface QueueOptions {
125
+ maxSize?: number;
126
+ flushInterval?: number;
127
+ onFlush: (events: UsageEventInput[]) => Promise<void>;
128
+ onError?: (err: unknown, events: UsageEventInput[]) => void;
129
+ }
130
+ /**
131
+ * Buffer d'événements avec flush automatique par intervalle ou par taille.
132
+ * Permet d'envoyer les événements en lot sans bloquer l'appelant.
133
+ */
134
+ declare class EventQueue {
135
+ private queue;
136
+ private timer;
137
+ private readonly maxSize;
138
+ private readonly onFlush;
139
+ private readonly onError;
140
+ constructor({ maxSize, flushInterval, onFlush, onError }: QueueOptions);
141
+ push(event: UsageEventInput): void;
142
+ flush(): Promise<void>;
143
+ destroy(): void;
144
+ }
145
+
146
+ interface BillingClientOptions {
147
+ apiKey: string;
148
+ baseUrl?: string;
149
+ }
150
+ interface SdkCustomer {
151
+ id: number;
152
+ external_id: string;
153
+ email: string | null;
154
+ name: string | null;
155
+ stripe_customer_id: string | null;
156
+ metadata: Record<string, unknown> | null;
157
+ tenant_id: number;
158
+ active_subscription: SdkSubscription | null;
159
+ created_at: string;
160
+ updated_at: string;
161
+ }
162
+ interface SdkSubscription {
163
+ id: number;
164
+ status: 'active' | 'trialing' | 'past_due' | 'canceled' | 'paused';
165
+ billing_period: 'monthly' | 'yearly';
166
+ current_period_start: string | null;
167
+ current_period_end: string | null;
168
+ trial_ends_at: string | null;
169
+ canceled_at: string | null;
170
+ ends_at: string | null;
171
+ plan: SdkPlan;
172
+ }
173
+ interface SdkPlan {
174
+ id: number;
175
+ name: string;
176
+ slug: string;
177
+ type: string;
178
+ price_monthly: string;
179
+ price_yearly: string;
180
+ currency: string;
181
+ trial_days: number;
182
+ features: Record<string, boolean>;
183
+ max_users: number | null;
184
+ max_api_calls_per_month: number | null;
185
+ max_storage_bytes: number | null;
186
+ }
187
+ interface CheckoutResult {
188
+ checkout_url: string;
189
+ session_id: string;
190
+ }
191
+ interface PortalResult {
192
+ portal_url: string;
193
+ }
194
+ interface UpsertCustomerInput {
195
+ external_id: string;
196
+ email?: string;
197
+ name?: string;
198
+ metadata?: Record<string, unknown>;
199
+ }
200
+ interface CheckoutInput {
201
+ plan_id: number;
202
+ period?: 'monthly' | 'yearly';
203
+ success_url: string;
204
+ cancel_url: string;
205
+ coupon?: string;
206
+ }
207
+
208
+ declare class BillingError extends Error {
209
+ readonly status: number;
210
+ readonly body: unknown;
211
+ constructor(message: string, status: number, body: unknown);
212
+ }
213
+ /**
214
+ * Client SDK pour intégrer Billing SaaS dans votre application.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * import { BillingClient } from '@billing-saas/js'
219
+ *
220
+ * const billing = new BillingClient({ apiKey: 'bsk_live_...' })
221
+ *
222
+ * // Crée ou retrouve un customer
223
+ * const { customer } = await billing.customers.upsert({
224
+ * external_id: 'user_123',
225
+ * email: 'alice@example.com',
226
+ * })
227
+ *
228
+ * // Lance le checkout Stripe
229
+ * const { checkout_url } = await billing.customers.checkout('user_123', {
230
+ * plan_id: 2,
231
+ * success_url: 'https://yourapp.com/success',
232
+ * cancel_url: 'https://yourapp.com/cancel',
233
+ * })
234
+ *
235
+ * window.location.href = checkout_url
236
+ * ```
237
+ */
238
+ declare class BillingClient {
239
+ private readonly baseUrl;
240
+ private readonly headers;
241
+ constructor(options: BillingClientOptions);
242
+ readonly plans: {
243
+ list: () => Promise<SdkPlan[]>;
244
+ get: (id: number) => Promise<SdkPlan>;
245
+ };
246
+ readonly customers: {
247
+ /**
248
+ * Crée ou met à jour un customer (idempotent sur external_id).
249
+ */
250
+ upsert: (input: UpsertCustomerInput) => Promise<{
251
+ customer: SdkCustomer;
252
+ }>;
253
+ get: (externalId: string) => Promise<{
254
+ customer: SdkCustomer;
255
+ }>;
256
+ delete: (externalId: string) => Promise<{
257
+ message: string;
258
+ }>;
259
+ /**
260
+ * Subscription active du customer (null si aucune).
261
+ */
262
+ subscription: (externalId: string) => Promise<{
263
+ subscription: SdkSubscription | null;
264
+ }>;
265
+ /**
266
+ * Crée une Stripe Checkout Session — redirigez l'utilisateur vers checkout_url.
267
+ */
268
+ checkout: (externalId: string, input: CheckoutInput) => Promise<CheckoutResult>;
269
+ /**
270
+ * URL vers le Stripe Billing Portal (gestion CB, annulation, etc.).
271
+ */
272
+ portal: (externalId: string, returnUrl: string) => Promise<PortalResult>;
273
+ invoices: (externalId: string) => Promise<unknown>;
274
+ };
275
+ private get;
276
+ private post;
277
+ private delete;
278
+ private request;
279
+ }
280
+
281
+ export { type BatchResult, BillingClient, type BillingClientOptions, BillingError, type CheckoutInput, type CheckoutResult, EventQueue, type PortalResult, type QuotaMetric, type QuotaStatus, type SdkCustomer, type SdkPlan, type SdkSubscription, type UpsertCustomerInput, UsageClient, type UsageClientOptions, UsageError, type UsageEventInput, type UsageEventResponse, type UsageSummary };
@@ -0,0 +1,281 @@
1
+ interface UsageEventInput {
2
+ metric: string;
3
+ quantity: number;
4
+ /** Clé unique pour déduplication. Générée automatiquement si absente. */
5
+ idempotencyKey?: string;
6
+ recordedAt?: Date | string;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ interface UsageEventResponse {
10
+ id: number;
11
+ user_id: number;
12
+ subscription_id: number | null;
13
+ org_id: string | null;
14
+ metric: string;
15
+ quantity: string;
16
+ idempotency_key: string;
17
+ recorded_at: string;
18
+ billed_at: string | null;
19
+ metadata: Record<string, unknown> | null;
20
+ created_at: string;
21
+ updated_at: string;
22
+ }
23
+ interface BatchResult {
24
+ created: number;
25
+ duplicate: number;
26
+ keys: {
27
+ created: string[];
28
+ duplicate: string[];
29
+ };
30
+ }
31
+ interface UsageSummary {
32
+ period_start: string;
33
+ period_end: string;
34
+ usage: Record<string, {
35
+ metric: string;
36
+ total: string;
37
+ events: number;
38
+ }>;
39
+ }
40
+ interface QuotaMetric {
41
+ used: number;
42
+ limit: number | null;
43
+ pct: number | null;
44
+ status: 'ok' | 'warning' | 'critical' | 'exceeded' | 'unlimited';
45
+ }
46
+ type QuotaStatus = Record<string, QuotaMetric>;
47
+ interface UsageClientOptions {
48
+ baseUrl: string;
49
+ apiToken: string;
50
+ /** Timeout en ms (défaut : 10 000) */
51
+ timeout?: number;
52
+ }
53
+
54
+ declare class UsageError extends Error {
55
+ readonly statusCode: number;
56
+ readonly body: unknown;
57
+ constructor(message: string, statusCode: number, body: unknown);
58
+ }
59
+ interface UsageClientAdvancedOptions extends UsageClientOptions {
60
+ /** Active le buffering automatique (défaut: false) */
61
+ autoQueue?: boolean;
62
+ /** Taille max du buffer avant flush forcé (défaut: 50) */
63
+ queueMaxSize?: number;
64
+ /** Intervalle de flush en ms (défaut: 5000) */
65
+ flushInterval?: number;
66
+ /** Nombre de tentatives en cas d'erreur 5xx (défaut: 3) */
67
+ maxRetries?: number;
68
+ /** Délai initial entre retries en ms (défaut: 300) */
69
+ retryDelay?: number;
70
+ /** Callback appelé avant chaque envoi (enrichissement, logging) */
71
+ beforeSend?: (event: UsageEventInput) => UsageEventInput | null;
72
+ }
73
+ /**
74
+ * Client TypeScript pour l'API Usage de Billing SaaS.
75
+ *
76
+ * @example
77
+ * // Mode direct
78
+ * const client = new UsageClient({ baseUrl: '...', apiToken: '...' })
79
+ * await client.record({ metric: 'api_calls', quantity: 1 })
80
+ *
81
+ * @example
82
+ * // Mode queue (batch automatique)
83
+ * const client = new UsageClient({ baseUrl: '...', apiToken: '...', autoQueue: true })
84
+ * client.track({ metric: 'page_view', quantity: 1 }) // non-bloquant
85
+ */
86
+ declare class UsageClient {
87
+ private readonly baseUrl;
88
+ private readonly apiToken;
89
+ private readonly timeout;
90
+ private readonly maxRetries;
91
+ private readonly retryDelay;
92
+ private readonly beforeSend?;
93
+ private queue;
94
+ constructor(opts: UsageClientAdvancedOptions);
95
+ /**
96
+ * Enregistre un événement de façon synchrone (direct API call).
97
+ */
98
+ record(event: UsageEventInput): Promise<UsageEventResponse>;
99
+ /**
100
+ * Ajoute un événement dans le buffer (non-bloquant).
101
+ * Nécessite `autoQueue: true` à la construction.
102
+ */
103
+ track(event: UsageEventInput): void;
104
+ /**
105
+ * Vide le buffer immédiatement (utile avant navigations SPA).
106
+ */
107
+ flush(): Promise<void>;
108
+ /**
109
+ * Enregistre jusqu'à 100 événements en une seule requête.
110
+ */
111
+ batch(events: UsageEventInput[]): Promise<BatchResult>;
112
+ summary(): Promise<UsageSummary>;
113
+ quota(): Promise<QuotaStatus>;
114
+ /** Libère les ressources (timer de la queue). */
115
+ destroy(): void;
116
+ private applyBeforeSend;
117
+ private normalizeEvent;
118
+ private generateKey;
119
+ private post;
120
+ private get;
121
+ private request;
122
+ }
123
+
124
+ interface QueueOptions {
125
+ maxSize?: number;
126
+ flushInterval?: number;
127
+ onFlush: (events: UsageEventInput[]) => Promise<void>;
128
+ onError?: (err: unknown, events: UsageEventInput[]) => void;
129
+ }
130
+ /**
131
+ * Buffer d'événements avec flush automatique par intervalle ou par taille.
132
+ * Permet d'envoyer les événements en lot sans bloquer l'appelant.
133
+ */
134
+ declare class EventQueue {
135
+ private queue;
136
+ private timer;
137
+ private readonly maxSize;
138
+ private readonly onFlush;
139
+ private readonly onError;
140
+ constructor({ maxSize, flushInterval, onFlush, onError }: QueueOptions);
141
+ push(event: UsageEventInput): void;
142
+ flush(): Promise<void>;
143
+ destroy(): void;
144
+ }
145
+
146
+ interface BillingClientOptions {
147
+ apiKey: string;
148
+ baseUrl?: string;
149
+ }
150
+ interface SdkCustomer {
151
+ id: number;
152
+ external_id: string;
153
+ email: string | null;
154
+ name: string | null;
155
+ stripe_customer_id: string | null;
156
+ metadata: Record<string, unknown> | null;
157
+ tenant_id: number;
158
+ active_subscription: SdkSubscription | null;
159
+ created_at: string;
160
+ updated_at: string;
161
+ }
162
+ interface SdkSubscription {
163
+ id: number;
164
+ status: 'active' | 'trialing' | 'past_due' | 'canceled' | 'paused';
165
+ billing_period: 'monthly' | 'yearly';
166
+ current_period_start: string | null;
167
+ current_period_end: string | null;
168
+ trial_ends_at: string | null;
169
+ canceled_at: string | null;
170
+ ends_at: string | null;
171
+ plan: SdkPlan;
172
+ }
173
+ interface SdkPlan {
174
+ id: number;
175
+ name: string;
176
+ slug: string;
177
+ type: string;
178
+ price_monthly: string;
179
+ price_yearly: string;
180
+ currency: string;
181
+ trial_days: number;
182
+ features: Record<string, boolean>;
183
+ max_users: number | null;
184
+ max_api_calls_per_month: number | null;
185
+ max_storage_bytes: number | null;
186
+ }
187
+ interface CheckoutResult {
188
+ checkout_url: string;
189
+ session_id: string;
190
+ }
191
+ interface PortalResult {
192
+ portal_url: string;
193
+ }
194
+ interface UpsertCustomerInput {
195
+ external_id: string;
196
+ email?: string;
197
+ name?: string;
198
+ metadata?: Record<string, unknown>;
199
+ }
200
+ interface CheckoutInput {
201
+ plan_id: number;
202
+ period?: 'monthly' | 'yearly';
203
+ success_url: string;
204
+ cancel_url: string;
205
+ coupon?: string;
206
+ }
207
+
208
+ declare class BillingError extends Error {
209
+ readonly status: number;
210
+ readonly body: unknown;
211
+ constructor(message: string, status: number, body: unknown);
212
+ }
213
+ /**
214
+ * Client SDK pour intégrer Billing SaaS dans votre application.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * import { BillingClient } from '@billing-saas/js'
219
+ *
220
+ * const billing = new BillingClient({ apiKey: 'bsk_live_...' })
221
+ *
222
+ * // Crée ou retrouve un customer
223
+ * const { customer } = await billing.customers.upsert({
224
+ * external_id: 'user_123',
225
+ * email: 'alice@example.com',
226
+ * })
227
+ *
228
+ * // Lance le checkout Stripe
229
+ * const { checkout_url } = await billing.customers.checkout('user_123', {
230
+ * plan_id: 2,
231
+ * success_url: 'https://yourapp.com/success',
232
+ * cancel_url: 'https://yourapp.com/cancel',
233
+ * })
234
+ *
235
+ * window.location.href = checkout_url
236
+ * ```
237
+ */
238
+ declare class BillingClient {
239
+ private readonly baseUrl;
240
+ private readonly headers;
241
+ constructor(options: BillingClientOptions);
242
+ readonly plans: {
243
+ list: () => Promise<SdkPlan[]>;
244
+ get: (id: number) => Promise<SdkPlan>;
245
+ };
246
+ readonly customers: {
247
+ /**
248
+ * Crée ou met à jour un customer (idempotent sur external_id).
249
+ */
250
+ upsert: (input: UpsertCustomerInput) => Promise<{
251
+ customer: SdkCustomer;
252
+ }>;
253
+ get: (externalId: string) => Promise<{
254
+ customer: SdkCustomer;
255
+ }>;
256
+ delete: (externalId: string) => Promise<{
257
+ message: string;
258
+ }>;
259
+ /**
260
+ * Subscription active du customer (null si aucune).
261
+ */
262
+ subscription: (externalId: string) => Promise<{
263
+ subscription: SdkSubscription | null;
264
+ }>;
265
+ /**
266
+ * Crée une Stripe Checkout Session — redirigez l'utilisateur vers checkout_url.
267
+ */
268
+ checkout: (externalId: string, input: CheckoutInput) => Promise<CheckoutResult>;
269
+ /**
270
+ * URL vers le Stripe Billing Portal (gestion CB, annulation, etc.).
271
+ */
272
+ portal: (externalId: string, returnUrl: string) => Promise<PortalResult>;
273
+ invoices: (externalId: string) => Promise<unknown>;
274
+ };
275
+ private get;
276
+ private post;
277
+ private delete;
278
+ private request;
279
+ }
280
+
281
+ export { type BatchResult, BillingClient, type BillingClientOptions, BillingError, type CheckoutInput, type CheckoutResult, EventQueue, type PortalResult, type QuotaMetric, type QuotaStatus, type SdkCustomer, type SdkPlan, type SdkSubscription, type UpsertCustomerInput, UsageClient, type UsageClientOptions, UsageError, type UsageEventInput, type UsageEventResponse, type UsageSummary };
package/dist/index.js ADDED
@@ -0,0 +1,308 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ BillingClient: () => BillingClient,
23
+ BillingError: () => BillingError,
24
+ EventQueue: () => EventQueue,
25
+ UsageClient: () => UsageClient,
26
+ UsageError: () => UsageError
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/EventQueue.ts
31
+ var EventQueue = class {
32
+ queue = [];
33
+ timer = null;
34
+ maxSize;
35
+ onFlush;
36
+ onError;
37
+ constructor({ maxSize = 50, flushInterval = 5e3, onFlush, onError }) {
38
+ this.maxSize = maxSize;
39
+ this.onFlush = onFlush;
40
+ this.onError = onError ?? ((err) => console.error("[UsageQueue]", err));
41
+ if (flushInterval > 0 && typeof setInterval !== "undefined") {
42
+ this.timer = setInterval(() => void this.flush(), flushInterval);
43
+ }
44
+ if (typeof window !== "undefined") {
45
+ window.addEventListener("visibilitychange", () => {
46
+ if (document.visibilityState === "hidden") void this.flush();
47
+ });
48
+ }
49
+ }
50
+ push(event) {
51
+ this.queue.push(event);
52
+ if (this.queue.length >= this.maxSize) {
53
+ void this.flush();
54
+ }
55
+ }
56
+ async flush() {
57
+ if (this.queue.length === 0) return;
58
+ const batch = this.queue.splice(0, this.queue.length);
59
+ try {
60
+ await this.onFlush(batch);
61
+ } catch (err) {
62
+ this.onError(err, batch);
63
+ }
64
+ }
65
+ destroy() {
66
+ if (this.timer !== null) {
67
+ clearInterval(this.timer);
68
+ this.timer = null;
69
+ }
70
+ }
71
+ };
72
+
73
+ // src/UsageClient.ts
74
+ var UsageError = class extends Error {
75
+ constructor(message, statusCode, body) {
76
+ super(message);
77
+ this.statusCode = statusCode;
78
+ this.body = body;
79
+ this.name = "UsageError";
80
+ }
81
+ statusCode;
82
+ body;
83
+ };
84
+ var UsageClient = class {
85
+ baseUrl;
86
+ apiToken;
87
+ timeout;
88
+ maxRetries;
89
+ retryDelay;
90
+ beforeSend;
91
+ queue = null;
92
+ constructor(opts) {
93
+ this.baseUrl = opts.baseUrl.replace(/\/$/, "");
94
+ this.apiToken = opts.apiToken;
95
+ this.timeout = opts.timeout ?? 1e4;
96
+ this.maxRetries = opts.maxRetries ?? 3;
97
+ this.retryDelay = opts.retryDelay ?? 300;
98
+ this.beforeSend = opts.beforeSend;
99
+ if (opts.autoQueue) {
100
+ this.queue = new EventQueue({
101
+ maxSize: opts.queueMaxSize ?? 50,
102
+ flushInterval: opts.flushInterval ?? 5e3,
103
+ onFlush: (events) => this.batch(events).then(() => void 0),
104
+ onError: (err) => console.error("[UsageClient:queue]", err)
105
+ });
106
+ }
107
+ }
108
+ /**
109
+ * Enregistre un événement de façon synchrone (direct API call).
110
+ */
111
+ async record(event) {
112
+ const payload = this.applyBeforeSend(this.normalizeEvent(event));
113
+ if (!payload) return Promise.reject(new Error("beforeSend filtered event"));
114
+ return this.post("/v1/usage/events", payload);
115
+ }
116
+ /**
117
+ * Ajoute un événement dans le buffer (non-bloquant).
118
+ * Nécessite `autoQueue: true` à la construction.
119
+ */
120
+ track(event) {
121
+ if (!this.queue) {
122
+ throw new Error("autoQueue est d\xE9sactiv\xE9. Passez autoQueue:true ou utilisez record().");
123
+ }
124
+ const normalized = this.applyBeforeSend(this.normalizeEvent(event));
125
+ if (normalized) this.queue.push(normalized);
126
+ }
127
+ /**
128
+ * Vide le buffer immédiatement (utile avant navigations SPA).
129
+ */
130
+ async flush() {
131
+ var _a;
132
+ await ((_a = this.queue) == null ? void 0 : _a.flush());
133
+ }
134
+ /**
135
+ * Enregistre jusqu'à 100 événements en une seule requête.
136
+ */
137
+ async batch(events) {
138
+ if (events.length === 0) {
139
+ return { created: 0, duplicate: 0, keys: { created: [], duplicate: [] } };
140
+ }
141
+ if (events.length > 100) {
142
+ throw new RangeError("Le lot ne peut pas d\xE9passer 100 \xE9v\xE9nements.");
143
+ }
144
+ return this.post("/v1/usage/batch", {
145
+ events: events.map((e) => this.normalizeEvent(e))
146
+ });
147
+ }
148
+ async summary() {
149
+ return this.get("/v1/usage/summary");
150
+ }
151
+ async quota() {
152
+ return this.get("/v1/usage/quota");
153
+ }
154
+ /** Libère les ressources (timer de la queue). */
155
+ destroy() {
156
+ var _a;
157
+ (_a = this.queue) == null ? void 0 : _a.destroy();
158
+ }
159
+ // ── Helpers ────────────────────────────────────────────────────────────────
160
+ applyBeforeSend(event) {
161
+ return this.beforeSend ? this.beforeSend(event) : event;
162
+ }
163
+ normalizeEvent(event) {
164
+ return {
165
+ ...event,
166
+ idempotencyKey: event.idempotencyKey ?? this.generateKey(event.metric)
167
+ };
168
+ }
169
+ generateKey(metric) {
170
+ const rand = Math.random().toString(36).slice(2);
171
+ return `${metric}:${Date.now()}:${rand}`;
172
+ }
173
+ async post(path, body) {
174
+ return this.request("POST", path, body);
175
+ }
176
+ async get(path, params) {
177
+ const url = params ? `${path}?${new URLSearchParams(params)}` : path;
178
+ return this.request("GET", url);
179
+ }
180
+ async request(method, path, body) {
181
+ let lastError;
182
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
183
+ try {
184
+ const controller = new AbortController();
185
+ const timer = setTimeout(() => controller.abort(), this.timeout);
186
+ try {
187
+ const resp = await fetch(`${this.baseUrl}${path}`, {
188
+ method,
189
+ headers: {
190
+ Authorization: `Bearer ${this.apiToken}`,
191
+ "Content-Type": "application/json",
192
+ Accept: "application/json"
193
+ },
194
+ body: body != null ? JSON.stringify(body) : void 0,
195
+ signal: controller.signal
196
+ });
197
+ const data = await resp.json().catch(() => ({}));
198
+ if (!resp.ok) {
199
+ const message = data["message"] ?? `HTTP ${resp.status}`;
200
+ const err = new UsageError(message, resp.status, data);
201
+ if (resp.status >= 400 && resp.status < 500) throw err;
202
+ lastError = err;
203
+ } else {
204
+ return data;
205
+ }
206
+ } finally {
207
+ clearTimeout(timer);
208
+ }
209
+ } catch (err) {
210
+ if (err instanceof UsageError && err.statusCode >= 400 && err.statusCode < 500) {
211
+ throw err;
212
+ }
213
+ lastError = err;
214
+ }
215
+ if (attempt < this.maxRetries - 1) {
216
+ await sleep(this.retryDelay * 2 ** attempt);
217
+ }
218
+ }
219
+ throw lastError;
220
+ }
221
+ };
222
+ function sleep(ms) {
223
+ return new Promise((resolve) => setTimeout(resolve, ms));
224
+ }
225
+
226
+ // src/BillingClient.ts
227
+ var BillingError = class extends Error {
228
+ constructor(message, status, body) {
229
+ super(message);
230
+ this.status = status;
231
+ this.body = body;
232
+ this.name = "BillingError";
233
+ }
234
+ status;
235
+ body;
236
+ };
237
+ var BillingClient = class {
238
+ baseUrl;
239
+ headers;
240
+ constructor(options) {
241
+ this.baseUrl = (options.baseUrl ?? "http://localhost:8000/api").replace(/\/$/, "");
242
+ this.headers = {
243
+ "Content-Type": "application/json",
244
+ "Authorization": `Bearer ${options.apiKey}`
245
+ };
246
+ }
247
+ // ─── Plans ────────────────────────────────────────────────────────────────
248
+ plans = {
249
+ list: () => this.get("/v1/sdk/plans"),
250
+ get: (id) => this.get(`/v1/sdk/plans/${id}`)
251
+ };
252
+ // ─── Customers ────────────────────────────────────────────────────────────
253
+ customers = {
254
+ /**
255
+ * Crée ou met à jour un customer (idempotent sur external_id).
256
+ */
257
+ upsert: (input) => this.post("/v1/sdk/customers", input),
258
+ get: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}`),
259
+ delete: (externalId) => this.delete(`/v1/sdk/customers/${encodeURIComponent(externalId)}`),
260
+ /**
261
+ * Subscription active du customer (null si aucune).
262
+ */
263
+ subscription: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}/subscription`),
264
+ /**
265
+ * Crée une Stripe Checkout Session — redirigez l'utilisateur vers checkout_url.
266
+ */
267
+ checkout: (externalId, input) => this.post(`/v1/sdk/customers/${encodeURIComponent(externalId)}/checkout`, input),
268
+ /**
269
+ * URL vers le Stripe Billing Portal (gestion CB, annulation, etc.).
270
+ */
271
+ portal: (externalId, returnUrl) => this.post(`/v1/sdk/customers/${encodeURIComponent(externalId)}/portal`, { return_url: returnUrl }),
272
+ invoices: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}/invoices`)
273
+ };
274
+ // ─── HTTP helpers ─────────────────────────────────────────────────────────
275
+ async get(path) {
276
+ return this.request("GET", path);
277
+ }
278
+ async post(path, body) {
279
+ return this.request("POST", path, body);
280
+ }
281
+ async delete(path) {
282
+ return this.request("DELETE", path);
283
+ }
284
+ async request(method, path, body) {
285
+ const res = await fetch(`${this.baseUrl}${path}`, {
286
+ method,
287
+ headers: this.headers,
288
+ body: body !== void 0 ? JSON.stringify(body) : void 0
289
+ });
290
+ const data = await res.json().catch(() => null);
291
+ if (!res.ok) {
292
+ throw new BillingError(
293
+ (data == null ? void 0 : data.message) ?? `HTTP ${res.status}`,
294
+ res.status,
295
+ data
296
+ );
297
+ }
298
+ return data;
299
+ }
300
+ };
301
+ // Annotate the CommonJS export names for ESM import in node:
302
+ 0 && (module.exports = {
303
+ BillingClient,
304
+ BillingError,
305
+ EventQueue,
306
+ UsageClient,
307
+ UsageError
308
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,278 @@
1
+ // src/EventQueue.ts
2
+ var EventQueue = class {
3
+ queue = [];
4
+ timer = null;
5
+ maxSize;
6
+ onFlush;
7
+ onError;
8
+ constructor({ maxSize = 50, flushInterval = 5e3, onFlush, onError }) {
9
+ this.maxSize = maxSize;
10
+ this.onFlush = onFlush;
11
+ this.onError = onError ?? ((err) => console.error("[UsageQueue]", err));
12
+ if (flushInterval > 0 && typeof setInterval !== "undefined") {
13
+ this.timer = setInterval(() => void this.flush(), flushInterval);
14
+ }
15
+ if (typeof window !== "undefined") {
16
+ window.addEventListener("visibilitychange", () => {
17
+ if (document.visibilityState === "hidden") void this.flush();
18
+ });
19
+ }
20
+ }
21
+ push(event) {
22
+ this.queue.push(event);
23
+ if (this.queue.length >= this.maxSize) {
24
+ void this.flush();
25
+ }
26
+ }
27
+ async flush() {
28
+ if (this.queue.length === 0) return;
29
+ const batch = this.queue.splice(0, this.queue.length);
30
+ try {
31
+ await this.onFlush(batch);
32
+ } catch (err) {
33
+ this.onError(err, batch);
34
+ }
35
+ }
36
+ destroy() {
37
+ if (this.timer !== null) {
38
+ clearInterval(this.timer);
39
+ this.timer = null;
40
+ }
41
+ }
42
+ };
43
+
44
+ // src/UsageClient.ts
45
+ var UsageError = class extends Error {
46
+ constructor(message, statusCode, body) {
47
+ super(message);
48
+ this.statusCode = statusCode;
49
+ this.body = body;
50
+ this.name = "UsageError";
51
+ }
52
+ statusCode;
53
+ body;
54
+ };
55
+ var UsageClient = class {
56
+ baseUrl;
57
+ apiToken;
58
+ timeout;
59
+ maxRetries;
60
+ retryDelay;
61
+ beforeSend;
62
+ queue = null;
63
+ constructor(opts) {
64
+ this.baseUrl = opts.baseUrl.replace(/\/$/, "");
65
+ this.apiToken = opts.apiToken;
66
+ this.timeout = opts.timeout ?? 1e4;
67
+ this.maxRetries = opts.maxRetries ?? 3;
68
+ this.retryDelay = opts.retryDelay ?? 300;
69
+ this.beforeSend = opts.beforeSend;
70
+ if (opts.autoQueue) {
71
+ this.queue = new EventQueue({
72
+ maxSize: opts.queueMaxSize ?? 50,
73
+ flushInterval: opts.flushInterval ?? 5e3,
74
+ onFlush: (events) => this.batch(events).then(() => void 0),
75
+ onError: (err) => console.error("[UsageClient:queue]", err)
76
+ });
77
+ }
78
+ }
79
+ /**
80
+ * Enregistre un événement de façon synchrone (direct API call).
81
+ */
82
+ async record(event) {
83
+ const payload = this.applyBeforeSend(this.normalizeEvent(event));
84
+ if (!payload) return Promise.reject(new Error("beforeSend filtered event"));
85
+ return this.post("/v1/usage/events", payload);
86
+ }
87
+ /**
88
+ * Ajoute un événement dans le buffer (non-bloquant).
89
+ * Nécessite `autoQueue: true` à la construction.
90
+ */
91
+ track(event) {
92
+ if (!this.queue) {
93
+ throw new Error("autoQueue est d\xE9sactiv\xE9. Passez autoQueue:true ou utilisez record().");
94
+ }
95
+ const normalized = this.applyBeforeSend(this.normalizeEvent(event));
96
+ if (normalized) this.queue.push(normalized);
97
+ }
98
+ /**
99
+ * Vide le buffer immédiatement (utile avant navigations SPA).
100
+ */
101
+ async flush() {
102
+ var _a;
103
+ await ((_a = this.queue) == null ? void 0 : _a.flush());
104
+ }
105
+ /**
106
+ * Enregistre jusqu'à 100 événements en une seule requête.
107
+ */
108
+ async batch(events) {
109
+ if (events.length === 0) {
110
+ return { created: 0, duplicate: 0, keys: { created: [], duplicate: [] } };
111
+ }
112
+ if (events.length > 100) {
113
+ throw new RangeError("Le lot ne peut pas d\xE9passer 100 \xE9v\xE9nements.");
114
+ }
115
+ return this.post("/v1/usage/batch", {
116
+ events: events.map((e) => this.normalizeEvent(e))
117
+ });
118
+ }
119
+ async summary() {
120
+ return this.get("/v1/usage/summary");
121
+ }
122
+ async quota() {
123
+ return this.get("/v1/usage/quota");
124
+ }
125
+ /** Libère les ressources (timer de la queue). */
126
+ destroy() {
127
+ var _a;
128
+ (_a = this.queue) == null ? void 0 : _a.destroy();
129
+ }
130
+ // ── Helpers ────────────────────────────────────────────────────────────────
131
+ applyBeforeSend(event) {
132
+ return this.beforeSend ? this.beforeSend(event) : event;
133
+ }
134
+ normalizeEvent(event) {
135
+ return {
136
+ ...event,
137
+ idempotencyKey: event.idempotencyKey ?? this.generateKey(event.metric)
138
+ };
139
+ }
140
+ generateKey(metric) {
141
+ const rand = Math.random().toString(36).slice(2);
142
+ return `${metric}:${Date.now()}:${rand}`;
143
+ }
144
+ async post(path, body) {
145
+ return this.request("POST", path, body);
146
+ }
147
+ async get(path, params) {
148
+ const url = params ? `${path}?${new URLSearchParams(params)}` : path;
149
+ return this.request("GET", url);
150
+ }
151
+ async request(method, path, body) {
152
+ let lastError;
153
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
154
+ try {
155
+ const controller = new AbortController();
156
+ const timer = setTimeout(() => controller.abort(), this.timeout);
157
+ try {
158
+ const resp = await fetch(`${this.baseUrl}${path}`, {
159
+ method,
160
+ headers: {
161
+ Authorization: `Bearer ${this.apiToken}`,
162
+ "Content-Type": "application/json",
163
+ Accept: "application/json"
164
+ },
165
+ body: body != null ? JSON.stringify(body) : void 0,
166
+ signal: controller.signal
167
+ });
168
+ const data = await resp.json().catch(() => ({}));
169
+ if (!resp.ok) {
170
+ const message = data["message"] ?? `HTTP ${resp.status}`;
171
+ const err = new UsageError(message, resp.status, data);
172
+ if (resp.status >= 400 && resp.status < 500) throw err;
173
+ lastError = err;
174
+ } else {
175
+ return data;
176
+ }
177
+ } finally {
178
+ clearTimeout(timer);
179
+ }
180
+ } catch (err) {
181
+ if (err instanceof UsageError && err.statusCode >= 400 && err.statusCode < 500) {
182
+ throw err;
183
+ }
184
+ lastError = err;
185
+ }
186
+ if (attempt < this.maxRetries - 1) {
187
+ await sleep(this.retryDelay * 2 ** attempt);
188
+ }
189
+ }
190
+ throw lastError;
191
+ }
192
+ };
193
+ function sleep(ms) {
194
+ return new Promise((resolve) => setTimeout(resolve, ms));
195
+ }
196
+
197
+ // src/BillingClient.ts
198
+ var BillingError = class extends Error {
199
+ constructor(message, status, body) {
200
+ super(message);
201
+ this.status = status;
202
+ this.body = body;
203
+ this.name = "BillingError";
204
+ }
205
+ status;
206
+ body;
207
+ };
208
+ var BillingClient = class {
209
+ baseUrl;
210
+ headers;
211
+ constructor(options) {
212
+ this.baseUrl = (options.baseUrl ?? "http://localhost:8000/api").replace(/\/$/, "");
213
+ this.headers = {
214
+ "Content-Type": "application/json",
215
+ "Authorization": `Bearer ${options.apiKey}`
216
+ };
217
+ }
218
+ // ─── Plans ────────────────────────────────────────────────────────────────
219
+ plans = {
220
+ list: () => this.get("/v1/sdk/plans"),
221
+ get: (id) => this.get(`/v1/sdk/plans/${id}`)
222
+ };
223
+ // ─── Customers ────────────────────────────────────────────────────────────
224
+ customers = {
225
+ /**
226
+ * Crée ou met à jour un customer (idempotent sur external_id).
227
+ */
228
+ upsert: (input) => this.post("/v1/sdk/customers", input),
229
+ get: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}`),
230
+ delete: (externalId) => this.delete(`/v1/sdk/customers/${encodeURIComponent(externalId)}`),
231
+ /**
232
+ * Subscription active du customer (null si aucune).
233
+ */
234
+ subscription: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}/subscription`),
235
+ /**
236
+ * Crée une Stripe Checkout Session — redirigez l'utilisateur vers checkout_url.
237
+ */
238
+ checkout: (externalId, input) => this.post(`/v1/sdk/customers/${encodeURIComponent(externalId)}/checkout`, input),
239
+ /**
240
+ * URL vers le Stripe Billing Portal (gestion CB, annulation, etc.).
241
+ */
242
+ portal: (externalId, returnUrl) => this.post(`/v1/sdk/customers/${encodeURIComponent(externalId)}/portal`, { return_url: returnUrl }),
243
+ invoices: (externalId) => this.get(`/v1/sdk/customers/${encodeURIComponent(externalId)}/invoices`)
244
+ };
245
+ // ─── HTTP helpers ─────────────────────────────────────────────────────────
246
+ async get(path) {
247
+ return this.request("GET", path);
248
+ }
249
+ async post(path, body) {
250
+ return this.request("POST", path, body);
251
+ }
252
+ async delete(path) {
253
+ return this.request("DELETE", path);
254
+ }
255
+ async request(method, path, body) {
256
+ const res = await fetch(`${this.baseUrl}${path}`, {
257
+ method,
258
+ headers: this.headers,
259
+ body: body !== void 0 ? JSON.stringify(body) : void 0
260
+ });
261
+ const data = await res.json().catch(() => null);
262
+ if (!res.ok) {
263
+ throw new BillingError(
264
+ (data == null ? void 0 : data.message) ?? `HTTP ${res.status}`,
265
+ res.status,
266
+ data
267
+ );
268
+ }
269
+ return data;
270
+ }
271
+ };
272
+ export {
273
+ BillingClient,
274
+ BillingError,
275
+ EventQueue,
276
+ UsageClient,
277
+ UsageError
278
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@billing-saas/usage-sdk",
3
+ "version": "0.1.0",
4
+ "description": "SDK TypeScript pour intégrer le PU Billing SaaS dans votre application Next.js — gestion des plans, checkout Stripe, abonnements et feature gates.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts",
21
+ "test": "vitest run",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "billing",
26
+ "saas",
27
+ "subscription",
28
+ "stripe",
29
+ "payments",
30
+ "nextjs",
31
+ "sdk"
32
+ ],
33
+ "author": "Billing SaaS",
34
+ "license": "MIT",
35
+ "devDependencies": {
36
+ "tsup": "^8.0.0",
37
+ "typescript": "^5.0.0",
38
+ "vitest": "^1.0.0"
39
+ }
40
+ }