lokicms-plugin-stripe 1.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.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/package.json +44 -0
- package/src/index.ts +1092 -0
- package/src/stripe-service.ts +552 -0
- package/src/types.ts +214 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe Service
|
|
3
|
+
* Handles all Stripe API interactions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Stripe from 'stripe';
|
|
7
|
+
import type {
|
|
8
|
+
StripeConfig,
|
|
9
|
+
StripeProduct,
|
|
10
|
+
StripePrice,
|
|
11
|
+
StripeCustomer,
|
|
12
|
+
StripeSubscription,
|
|
13
|
+
StripeInvoice,
|
|
14
|
+
CheckoutSessionParams,
|
|
15
|
+
PortalSessionParams,
|
|
16
|
+
WebhookEvent,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
export class StripeService {
|
|
20
|
+
private stripe: Stripe;
|
|
21
|
+
private config: StripeConfig;
|
|
22
|
+
|
|
23
|
+
constructor(config: StripeConfig) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.stripe = new Stripe(config.secretKey, {
|
|
26
|
+
apiVersion: '2025-02-24.acacia',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Products
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
async createProduct(params: {
|
|
35
|
+
name: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
metadata?: Record<string, string>;
|
|
38
|
+
}): Promise<StripeProduct> {
|
|
39
|
+
const product = await this.stripe.products.create({
|
|
40
|
+
name: params.name,
|
|
41
|
+
description: params.description,
|
|
42
|
+
metadata: params.metadata,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return this.mapProduct(product);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getProduct(id: string): Promise<StripeProduct | null> {
|
|
49
|
+
try {
|
|
50
|
+
const product = await this.stripe.products.retrieve(id);
|
|
51
|
+
return this.mapProduct(product);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async listProducts(params?: {
|
|
58
|
+
active?: boolean;
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): Promise<StripeProduct[]> {
|
|
61
|
+
const products = await this.stripe.products.list({
|
|
62
|
+
active: params?.active,
|
|
63
|
+
limit: params?.limit || 100,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return products.data.map((p) => this.mapProduct(p));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async updateProduct(
|
|
70
|
+
id: string,
|
|
71
|
+
params: {
|
|
72
|
+
name?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
active?: boolean;
|
|
75
|
+
metadata?: Record<string, string>;
|
|
76
|
+
}
|
|
77
|
+
): Promise<StripeProduct> {
|
|
78
|
+
const product = await this.stripe.products.update(id, params);
|
|
79
|
+
return this.mapProduct(product);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async archiveProduct(id: string): Promise<StripeProduct> {
|
|
83
|
+
const product = await this.stripe.products.update(id, { active: false });
|
|
84
|
+
return this.mapProduct(product);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private mapProduct(product: Stripe.Product): StripeProduct {
|
|
88
|
+
return {
|
|
89
|
+
id: product.id,
|
|
90
|
+
name: product.name,
|
|
91
|
+
description: product.description || undefined,
|
|
92
|
+
active: product.active,
|
|
93
|
+
metadata: product.metadata,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// Prices
|
|
99
|
+
// ============================================================================
|
|
100
|
+
|
|
101
|
+
async createPrice(params: {
|
|
102
|
+
productId: string;
|
|
103
|
+
unitAmount: number;
|
|
104
|
+
currency?: string;
|
|
105
|
+
recurring?: {
|
|
106
|
+
interval: 'day' | 'week' | 'month' | 'year';
|
|
107
|
+
intervalCount?: number;
|
|
108
|
+
};
|
|
109
|
+
trialPeriodDays?: number;
|
|
110
|
+
metadata?: Record<string, string>;
|
|
111
|
+
}): Promise<StripePrice> {
|
|
112
|
+
const priceParams: Stripe.PriceCreateParams = {
|
|
113
|
+
product: params.productId,
|
|
114
|
+
unit_amount: params.unitAmount,
|
|
115
|
+
currency: params.currency || this.config.currency,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (params.recurring) {
|
|
119
|
+
priceParams.recurring = {
|
|
120
|
+
interval: params.recurring.interval,
|
|
121
|
+
interval_count: params.recurring.intervalCount,
|
|
122
|
+
trial_period_days: params.trialPeriodDays,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (params.metadata) {
|
|
127
|
+
priceParams.metadata = params.metadata;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const price = await this.stripe.prices.create(priceParams);
|
|
131
|
+
return this.mapPrice(price);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async getPrice(id: string): Promise<StripePrice | null> {
|
|
135
|
+
try {
|
|
136
|
+
const price = await this.stripe.prices.retrieve(id);
|
|
137
|
+
return this.mapPrice(price);
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async listPrices(params?: {
|
|
144
|
+
productId?: string;
|
|
145
|
+
active?: boolean;
|
|
146
|
+
type?: 'one_time' | 'recurring';
|
|
147
|
+
limit?: number;
|
|
148
|
+
}): Promise<StripePrice[]> {
|
|
149
|
+
const prices = await this.stripe.prices.list({
|
|
150
|
+
product: params?.productId,
|
|
151
|
+
active: params?.active,
|
|
152
|
+
type: params?.type,
|
|
153
|
+
limit: params?.limit || 100,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return prices.data.map((p) => this.mapPrice(p));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async updatePrice(
|
|
160
|
+
id: string,
|
|
161
|
+
params: {
|
|
162
|
+
active?: boolean;
|
|
163
|
+
metadata?: Record<string, string>;
|
|
164
|
+
}
|
|
165
|
+
): Promise<StripePrice> {
|
|
166
|
+
const price = await this.stripe.prices.update(id, params);
|
|
167
|
+
return this.mapPrice(price);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private mapPrice(price: Stripe.Price): StripePrice {
|
|
171
|
+
return {
|
|
172
|
+
id: price.id,
|
|
173
|
+
productId: typeof price.product === 'string' ? price.product : price.product.id,
|
|
174
|
+
active: price.active,
|
|
175
|
+
currency: price.currency,
|
|
176
|
+
unitAmount: price.unit_amount || 0,
|
|
177
|
+
type: price.type,
|
|
178
|
+
interval: price.recurring?.interval,
|
|
179
|
+
intervalCount: price.recurring?.interval_count,
|
|
180
|
+
trialPeriodDays: price.recurring?.trial_period_days || undefined,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Customers
|
|
186
|
+
// ============================================================================
|
|
187
|
+
|
|
188
|
+
async createCustomer(params: {
|
|
189
|
+
email: string;
|
|
190
|
+
name?: string;
|
|
191
|
+
metadata?: Record<string, string>;
|
|
192
|
+
}): Promise<StripeCustomer> {
|
|
193
|
+
const customer = await this.stripe.customers.create(params);
|
|
194
|
+
return this.mapCustomer(customer);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getCustomer(id: string): Promise<StripeCustomer | null> {
|
|
198
|
+
try {
|
|
199
|
+
const customer = await this.stripe.customers.retrieve(id);
|
|
200
|
+
if (customer.deleted) return null;
|
|
201
|
+
return this.mapCustomer(customer as Stripe.Customer);
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getCustomerByEmail(email: string): Promise<StripeCustomer | null> {
|
|
208
|
+
const customers = await this.stripe.customers.list({ email, limit: 1 });
|
|
209
|
+
if (customers.data.length === 0) return null;
|
|
210
|
+
return this.mapCustomer(customers.data[0]);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async updateCustomer(
|
|
214
|
+
id: string,
|
|
215
|
+
params: {
|
|
216
|
+
email?: string;
|
|
217
|
+
name?: string;
|
|
218
|
+
metadata?: Record<string, string>;
|
|
219
|
+
}
|
|
220
|
+
): Promise<StripeCustomer> {
|
|
221
|
+
const customer = await this.stripe.customers.update(id, params);
|
|
222
|
+
return this.mapCustomer(customer);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async deleteCustomer(id: string): Promise<void> {
|
|
226
|
+
await this.stripe.customers.del(id);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private mapCustomer(customer: Stripe.Customer): StripeCustomer {
|
|
230
|
+
return {
|
|
231
|
+
id: customer.id,
|
|
232
|
+
email: customer.email || '',
|
|
233
|
+
name: customer.name || undefined,
|
|
234
|
+
metadata: customer.metadata,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ============================================================================
|
|
239
|
+
// Subscriptions
|
|
240
|
+
// ============================================================================
|
|
241
|
+
|
|
242
|
+
async createSubscription(params: {
|
|
243
|
+
customerId: string;
|
|
244
|
+
priceId: string;
|
|
245
|
+
trialPeriodDays?: number;
|
|
246
|
+
metadata?: Record<string, string>;
|
|
247
|
+
}): Promise<StripeSubscription> {
|
|
248
|
+
const subscriptionParams: Stripe.SubscriptionCreateParams = {
|
|
249
|
+
customer: params.customerId,
|
|
250
|
+
items: [{ price: params.priceId }],
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (params.trialPeriodDays) {
|
|
254
|
+
subscriptionParams.trial_period_days = params.trialPeriodDays;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (params.metadata) {
|
|
258
|
+
subscriptionParams.metadata = params.metadata;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const subscription = await this.stripe.subscriptions.create(subscriptionParams);
|
|
262
|
+
return this.mapSubscription(subscription);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async getSubscription(id: string): Promise<StripeSubscription | null> {
|
|
266
|
+
try {
|
|
267
|
+
const subscription = await this.stripe.subscriptions.retrieve(id);
|
|
268
|
+
return this.mapSubscription(subscription);
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async listSubscriptions(params?: {
|
|
275
|
+
customerId?: string;
|
|
276
|
+
priceId?: string;
|
|
277
|
+
status?: string;
|
|
278
|
+
limit?: number;
|
|
279
|
+
}): Promise<StripeSubscription[]> {
|
|
280
|
+
const subscriptions = await this.stripe.subscriptions.list({
|
|
281
|
+
customer: params?.customerId,
|
|
282
|
+
price: params?.priceId,
|
|
283
|
+
status: params?.status as Stripe.SubscriptionListParams.Status,
|
|
284
|
+
limit: params?.limit || 100,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return subscriptions.data.map((s) => this.mapSubscription(s));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async cancelSubscription(
|
|
291
|
+
id: string,
|
|
292
|
+
params?: { atPeriodEnd?: boolean }
|
|
293
|
+
): Promise<StripeSubscription> {
|
|
294
|
+
let subscription: Stripe.Subscription;
|
|
295
|
+
|
|
296
|
+
if (params?.atPeriodEnd) {
|
|
297
|
+
subscription = await this.stripe.subscriptions.update(id, {
|
|
298
|
+
cancel_at_period_end: true,
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
subscription = await this.stripe.subscriptions.cancel(id);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return this.mapSubscription(subscription);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async resumeSubscription(id: string): Promise<StripeSubscription> {
|
|
308
|
+
const subscription = await this.stripe.subscriptions.update(id, {
|
|
309
|
+
cancel_at_period_end: false,
|
|
310
|
+
});
|
|
311
|
+
return this.mapSubscription(subscription);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async updateSubscription(
|
|
315
|
+
id: string,
|
|
316
|
+
params: {
|
|
317
|
+
priceId?: string;
|
|
318
|
+
metadata?: Record<string, string>;
|
|
319
|
+
}
|
|
320
|
+
): Promise<StripeSubscription> {
|
|
321
|
+
const updateParams: Stripe.SubscriptionUpdateParams = {};
|
|
322
|
+
|
|
323
|
+
if (params.priceId) {
|
|
324
|
+
const subscription = await this.stripe.subscriptions.retrieve(id);
|
|
325
|
+
updateParams.items = [
|
|
326
|
+
{
|
|
327
|
+
id: subscription.items.data[0].id,
|
|
328
|
+
price: params.priceId,
|
|
329
|
+
},
|
|
330
|
+
];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (params.metadata) {
|
|
334
|
+
updateParams.metadata = params.metadata;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const subscription = await this.stripe.subscriptions.update(id, updateParams);
|
|
338
|
+
return this.mapSubscription(subscription);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private mapSubscription(subscription: Stripe.Subscription): StripeSubscription {
|
|
342
|
+
const item = subscription.items.data[0];
|
|
343
|
+
return {
|
|
344
|
+
id: subscription.id,
|
|
345
|
+
customerId:
|
|
346
|
+
typeof subscription.customer === 'string'
|
|
347
|
+
? subscription.customer
|
|
348
|
+
: subscription.customer.id,
|
|
349
|
+
priceId: item.price.id,
|
|
350
|
+
status: subscription.status,
|
|
351
|
+
currentPeriodStart: subscription.current_period_start,
|
|
352
|
+
currentPeriodEnd: subscription.current_period_end,
|
|
353
|
+
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
|
354
|
+
canceledAt: subscription.canceled_at || undefined,
|
|
355
|
+
trialStart: subscription.trial_start || undefined,
|
|
356
|
+
trialEnd: subscription.trial_end || undefined,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Invoices
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
async getInvoice(id: string): Promise<StripeInvoice | null> {
|
|
365
|
+
try {
|
|
366
|
+
const invoice = await this.stripe.invoices.retrieve(id);
|
|
367
|
+
return this.mapInvoice(invoice);
|
|
368
|
+
} catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async listInvoices(params?: {
|
|
374
|
+
customerId?: string;
|
|
375
|
+
subscriptionId?: string;
|
|
376
|
+
status?: string;
|
|
377
|
+
limit?: number;
|
|
378
|
+
}): Promise<StripeInvoice[]> {
|
|
379
|
+
const invoices = await this.stripe.invoices.list({
|
|
380
|
+
customer: params?.customerId,
|
|
381
|
+
subscription: params?.subscriptionId,
|
|
382
|
+
status: params?.status as Stripe.InvoiceListParams.Status,
|
|
383
|
+
limit: params?.limit || 100,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
return invoices.data.map((i) => this.mapInvoice(i));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async payInvoice(id: string): Promise<StripeInvoice> {
|
|
390
|
+
const invoice = await this.stripe.invoices.pay(id);
|
|
391
|
+
return this.mapInvoice(invoice);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async voidInvoice(id: string): Promise<StripeInvoice> {
|
|
395
|
+
const invoice = await this.stripe.invoices.voidInvoice(id);
|
|
396
|
+
return this.mapInvoice(invoice);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private mapInvoice(invoice: Stripe.Invoice): StripeInvoice {
|
|
400
|
+
return {
|
|
401
|
+
id: invoice.id,
|
|
402
|
+
customerId:
|
|
403
|
+
typeof invoice.customer === 'string'
|
|
404
|
+
? invoice.customer
|
|
405
|
+
: invoice.customer?.id || '',
|
|
406
|
+
subscriptionId:
|
|
407
|
+
typeof invoice.subscription === 'string'
|
|
408
|
+
? invoice.subscription
|
|
409
|
+
: invoice.subscription?.id,
|
|
410
|
+
status: invoice.status || 'draft',
|
|
411
|
+
amountDue: invoice.amount_due,
|
|
412
|
+
amountPaid: invoice.amount_paid,
|
|
413
|
+
currency: invoice.currency,
|
|
414
|
+
hostedInvoiceUrl: invoice.hosted_invoice_url || undefined,
|
|
415
|
+
invoicePdf: invoice.invoice_pdf || undefined,
|
|
416
|
+
paidAt: invoice.status_transitions?.paid_at || undefined,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ============================================================================
|
|
421
|
+
// Checkout Sessions
|
|
422
|
+
// ============================================================================
|
|
423
|
+
|
|
424
|
+
async createCheckoutSession(params: CheckoutSessionParams): Promise<{
|
|
425
|
+
id: string;
|
|
426
|
+
url: string;
|
|
427
|
+
}> {
|
|
428
|
+
const sessionParams: Stripe.Checkout.SessionCreateParams = {
|
|
429
|
+
mode: params.mode,
|
|
430
|
+
line_items: [
|
|
431
|
+
{
|
|
432
|
+
price: params.priceId,
|
|
433
|
+
quantity: params.quantity || 1,
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
success_url: this.config.successUrl,
|
|
437
|
+
cancel_url: this.config.cancelUrl,
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
if (params.customerId) {
|
|
441
|
+
sessionParams.customer = params.customerId;
|
|
442
|
+
} else if (params.customerEmail) {
|
|
443
|
+
sessionParams.customer_email = params.customerEmail;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (params.mode === 'subscription' && params.trialPeriodDays) {
|
|
447
|
+
sessionParams.subscription_data = {
|
|
448
|
+
trial_period_days: params.trialPeriodDays,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (params.metadata) {
|
|
453
|
+
sessionParams.metadata = params.metadata;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (params.userId) {
|
|
457
|
+
sessionParams.client_reference_id = params.userId;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const session = await this.stripe.checkout.sessions.create(sessionParams);
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
id: session.id,
|
|
464
|
+
url: session.url || '',
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async getCheckoutSession(id: string): Promise<Stripe.Checkout.Session | null> {
|
|
469
|
+
try {
|
|
470
|
+
return await this.stripe.checkout.sessions.retrieve(id, {
|
|
471
|
+
expand: ['customer', 'subscription', 'payment_intent'],
|
|
472
|
+
});
|
|
473
|
+
} catch {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ============================================================================
|
|
479
|
+
// Customer Portal
|
|
480
|
+
// ============================================================================
|
|
481
|
+
|
|
482
|
+
async createPortalSession(params: PortalSessionParams): Promise<{
|
|
483
|
+
id: string;
|
|
484
|
+
url: string;
|
|
485
|
+
}> {
|
|
486
|
+
const session = await this.stripe.billingPortal.sessions.create({
|
|
487
|
+
customer: params.customerId,
|
|
488
|
+
return_url: params.returnUrl || this.config.portalReturnUrl,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
id: session.id,
|
|
493
|
+
url: session.url,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ============================================================================
|
|
498
|
+
// Webhooks
|
|
499
|
+
// ============================================================================
|
|
500
|
+
|
|
501
|
+
verifyWebhookSignature(
|
|
502
|
+
payload: string | Buffer,
|
|
503
|
+
signature: string
|
|
504
|
+
): WebhookEvent {
|
|
505
|
+
const event = this.stripe.webhooks.constructEvent(
|
|
506
|
+
payload,
|
|
507
|
+
signature,
|
|
508
|
+
this.config.webhookSecret
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
id: event.id,
|
|
513
|
+
type: event.type,
|
|
514
|
+
data: event.data,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// ============================================================================
|
|
519
|
+
// Payment Methods
|
|
520
|
+
// ============================================================================
|
|
521
|
+
|
|
522
|
+
async listPaymentMethods(customerId: string): Promise<Stripe.PaymentMethod[]> {
|
|
523
|
+
const methods = await this.stripe.paymentMethods.list({
|
|
524
|
+
customer: customerId,
|
|
525
|
+
type: 'card',
|
|
526
|
+
});
|
|
527
|
+
return methods.data;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async detachPaymentMethod(paymentMethodId: string): Promise<void> {
|
|
531
|
+
await this.stripe.paymentMethods.detach(paymentMethodId);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ============================================================================
|
|
535
|
+
// Usage Records (for metered billing)
|
|
536
|
+
// ============================================================================
|
|
537
|
+
|
|
538
|
+
async createUsageRecord(
|
|
539
|
+
subscriptionItemId: string,
|
|
540
|
+
quantity: number,
|
|
541
|
+
timestamp?: number
|
|
542
|
+
): Promise<Stripe.UsageRecord> {
|
|
543
|
+
return await this.stripe.subscriptionItems.createUsageRecord(
|
|
544
|
+
subscriptionItemId,
|
|
545
|
+
{
|
|
546
|
+
quantity,
|
|
547
|
+
timestamp: timestamp || Math.floor(Date.now() / 1000),
|
|
548
|
+
action: 'increment',
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
}
|