@qazuor/qzpay-core 1.0.1 → 1.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 +309 -19
- package/dist/index.cjs +1527 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1785 -217
- package/dist/index.d.ts +1785 -217
- package/dist/index.js +1496 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,8 +80,8 @@ await billing.subscriptions.cancel('sub_123');
|
|
|
80
80
|
### Payment Operations
|
|
81
81
|
|
|
82
82
|
```typescript
|
|
83
|
-
//
|
|
84
|
-
const payment = await billing.payments.
|
|
83
|
+
// Process a payment
|
|
84
|
+
const payment = await billing.payments.process({
|
|
85
85
|
customerId: 'cus_123',
|
|
86
86
|
amount: 2999, // $29.99 in cents
|
|
87
87
|
currency: 'USD',
|
|
@@ -91,8 +91,26 @@ const payment = await billing.payments.create({
|
|
|
91
91
|
// Retrieve payment
|
|
92
92
|
const status = await billing.payments.get('pay_123');
|
|
93
93
|
|
|
94
|
+
// Get payments by customer
|
|
95
|
+
const payments = await billing.payments.getByCustomerId('cus_123');
|
|
96
|
+
|
|
97
|
+
// Record an external payment (already processed by provider)
|
|
98
|
+
const recorded = await billing.payments.record({
|
|
99
|
+
id: 'pay_external_123',
|
|
100
|
+
customerId: 'cus_123',
|
|
101
|
+
amount: 2999,
|
|
102
|
+
currency: 'USD',
|
|
103
|
+
status: 'succeeded',
|
|
104
|
+
providerPaymentId: 'pi_xxx',
|
|
105
|
+
provider: 'stripe'
|
|
106
|
+
});
|
|
107
|
+
|
|
94
108
|
// Refund payment
|
|
95
|
-
await billing.payments.refund(
|
|
109
|
+
await billing.payments.refund({
|
|
110
|
+
paymentId: 'pay_123',
|
|
111
|
+
amount: 1500, // Optional: partial refund
|
|
112
|
+
reason: 'requested_by_customer'
|
|
113
|
+
});
|
|
96
114
|
```
|
|
97
115
|
|
|
98
116
|
### Events
|
|
@@ -113,27 +131,44 @@ billing.on('payment.succeeded', (event) => {
|
|
|
113
131
|
|
|
114
132
|
### Metrics
|
|
115
133
|
|
|
116
|
-
|
|
117
|
-
import { createMetricsService } from '@qazuor/qzpay-core';
|
|
118
|
-
|
|
119
|
-
const metrics = createMetricsService({ storage });
|
|
134
|
+
The `billing.metrics` service provides business intelligence and analytics:
|
|
120
135
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
```typescript
|
|
137
|
+
// Get Monthly Recurring Revenue
|
|
138
|
+
const mrr = await billing.metrics.getMrr({ currency: 'USD' });
|
|
139
|
+
console.log('Current MRR:', mrr.currentMrr);
|
|
140
|
+
console.log('Previous MRR:', mrr.previousMrr);
|
|
141
|
+
console.log('Net New MRR:', mrr.netNewMrr);
|
|
142
|
+
|
|
143
|
+
// Get subscription metrics by status
|
|
144
|
+
const subMetrics = await billing.metrics.getSubscriptionMetrics();
|
|
145
|
+
console.log('Active:', subMetrics.active);
|
|
146
|
+
console.log('Trialing:', subMetrics.trialing);
|
|
147
|
+
console.log('Canceled:', subMetrics.canceled);
|
|
148
|
+
console.log('Past Due:', subMetrics.pastDue);
|
|
149
|
+
|
|
150
|
+
// Get revenue metrics for a period
|
|
151
|
+
const revenue = await billing.metrics.getRevenueMetrics({
|
|
152
|
+
startDate: new Date('2024-01-01'),
|
|
153
|
+
endDate: new Date('2024-12-31'),
|
|
154
|
+
currency: 'USD'
|
|
155
|
+
});
|
|
156
|
+
console.log('Total Revenue:', revenue.totalRevenue);
|
|
157
|
+
console.log('Refunds:', revenue.totalRefunds);
|
|
124
158
|
|
|
125
|
-
// Get churn
|
|
126
|
-
const churn = await metrics.
|
|
127
|
-
|
|
128
|
-
|
|
159
|
+
// Get churn metrics
|
|
160
|
+
const churn = await billing.metrics.getChurnMetrics({
|
|
161
|
+
startDate: new Date('2024-01-01'),
|
|
162
|
+
endDate: new Date('2024-12-31')
|
|
129
163
|
});
|
|
164
|
+
console.log('Churn Rate:', churn.churnRate);
|
|
165
|
+
console.log('Churned MRR:', churn.churnedMrr);
|
|
130
166
|
|
|
131
|
-
// Get
|
|
132
|
-
const
|
|
133
|
-
from: new Date('2024-01-01'),
|
|
134
|
-
to: new Date('2024-12-31'),
|
|
167
|
+
// Get all dashboard metrics aggregated
|
|
168
|
+
const dashboard = await billing.metrics.getDashboard({
|
|
135
169
|
currency: 'USD'
|
|
136
170
|
});
|
|
171
|
+
console.log('Dashboard:', dashboard);
|
|
137
172
|
```
|
|
138
173
|
|
|
139
174
|
### Health Checks
|
|
@@ -203,11 +238,266 @@ const billing = new QZPayBilling({
|
|
|
203
238
|
| `billing.payments` | Payment processing |
|
|
204
239
|
| `billing.invoices` | Invoice management |
|
|
205
240
|
| `billing.plans` | Plan configuration |
|
|
206
|
-
| `billing.prices` | Price management |
|
|
207
241
|
| `billing.promoCodes` | Promotional codes |
|
|
208
242
|
| `billing.entitlements` | Feature entitlements |
|
|
209
243
|
| `billing.limits` | Usage limits |
|
|
210
244
|
| `billing.addons` | Add-on management |
|
|
245
|
+
| `billing.paymentMethods` | Payment method management |
|
|
246
|
+
| `billing.metrics` | Business metrics and analytics |
|
|
247
|
+
|
|
248
|
+
### Entitlements Service
|
|
249
|
+
|
|
250
|
+
Manage feature access based on subscriptions:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Check if customer has an entitlement
|
|
254
|
+
const hasAccess = await billing.entitlements.check('cus_123', 'advanced_analytics');
|
|
255
|
+
if (hasAccess) {
|
|
256
|
+
// Show advanced analytics
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Get all entitlements for a customer
|
|
260
|
+
const entitlements = await billing.entitlements.getByCustomerId('cus_123');
|
|
261
|
+
|
|
262
|
+
// Grant an entitlement manually
|
|
263
|
+
await billing.entitlements.grant('cus_123', 'beta_features', 'manual', 'admin_grant');
|
|
264
|
+
|
|
265
|
+
// Revoke an entitlement
|
|
266
|
+
await billing.entitlements.revoke('cus_123', 'beta_features');
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Limits Service
|
|
270
|
+
|
|
271
|
+
Track and enforce usage limits:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Check if customer is within limit
|
|
275
|
+
const result = await billing.limits.check('cus_123', 'api_calls');
|
|
276
|
+
console.log('Allowed:', result.allowed);
|
|
277
|
+
console.log('Current:', result.currentValue);
|
|
278
|
+
console.log('Max:', result.maxValue);
|
|
279
|
+
console.log('Remaining:', result.remaining);
|
|
280
|
+
|
|
281
|
+
// Get all limits for a customer
|
|
282
|
+
const limits = await billing.limits.getByCustomerId('cus_123');
|
|
283
|
+
|
|
284
|
+
// Increment usage
|
|
285
|
+
await billing.limits.increment('cus_123', 'api_calls', 1);
|
|
286
|
+
|
|
287
|
+
// Set a limit value
|
|
288
|
+
await billing.limits.set('cus_123', 'api_calls', 10000);
|
|
289
|
+
|
|
290
|
+
// Record usage (for audit/history)
|
|
291
|
+
await billing.limits.recordUsage('cus_123', 'api_calls', 5, 'increment');
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Add-ons Service
|
|
295
|
+
|
|
296
|
+
Manage subscription add-ons:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// Create an add-on definition
|
|
300
|
+
const addon = await billing.addons.create({
|
|
301
|
+
name: 'Extra Storage',
|
|
302
|
+
unitAmount: 500, // $5.00 in cents
|
|
303
|
+
currency: 'USD',
|
|
304
|
+
billingInterval: 'month',
|
|
305
|
+
compatiblePlanIds: ['plan_pro', 'plan_enterprise']
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Get add-ons compatible with a plan
|
|
309
|
+
const addons = await billing.addons.getByPlanId('plan_pro');
|
|
310
|
+
|
|
311
|
+
// Add an add-on to a subscription
|
|
312
|
+
const result = await billing.addons.addToSubscription({
|
|
313
|
+
subscriptionId: 'sub_123',
|
|
314
|
+
addOnId: 'addon_extra_storage',
|
|
315
|
+
quantity: 2
|
|
316
|
+
});
|
|
317
|
+
console.log('Proration amount:', result.prorationAmount);
|
|
318
|
+
|
|
319
|
+
// Get add-ons attached to a subscription
|
|
320
|
+
const subscriptionAddons = await billing.addons.getBySubscriptionId('sub_123');
|
|
321
|
+
|
|
322
|
+
// Update add-on quantity
|
|
323
|
+
await billing.addons.updateSubscriptionAddOn('sub_123', 'addon_extra_storage', {
|
|
324
|
+
quantity: 5
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Remove add-on from subscription
|
|
328
|
+
await billing.addons.removeFromSubscription('sub_123', 'addon_extra_storage');
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Payment Methods Service
|
|
332
|
+
|
|
333
|
+
Manage customer payment methods:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// Create a payment method
|
|
337
|
+
const paymentMethod = await billing.paymentMethods.create({
|
|
338
|
+
customerId: 'cus_123',
|
|
339
|
+
type: 'card',
|
|
340
|
+
providerPaymentMethodId: 'pm_xxx',
|
|
341
|
+
provider: 'stripe',
|
|
342
|
+
card: {
|
|
343
|
+
brand: 'visa',
|
|
344
|
+
last4: '4242',
|
|
345
|
+
expMonth: 12,
|
|
346
|
+
expYear: 2025
|
|
347
|
+
},
|
|
348
|
+
setAsDefault: true
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Get payment methods for a customer
|
|
352
|
+
const methods = await billing.paymentMethods.getByCustomerId('cus_123');
|
|
353
|
+
|
|
354
|
+
// Get the default payment method
|
|
355
|
+
const defaultMethod = await billing.paymentMethods.getDefault('cus_123');
|
|
356
|
+
|
|
357
|
+
// Set a payment method as default
|
|
358
|
+
await billing.paymentMethods.setDefault('cus_123', 'pm_456');
|
|
359
|
+
|
|
360
|
+
// Update a payment method
|
|
361
|
+
await billing.paymentMethods.update('pm_123', {
|
|
362
|
+
card: { expMonth: 1, expYear: 2026 }
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Delete a payment method
|
|
366
|
+
await billing.paymentMethods.delete('pm_123');
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Saved Card Service
|
|
370
|
+
|
|
371
|
+
Unified interface for saving and managing payment cards across providers.
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { createSavedCardService } from '@qazuor/qzpay-stripe'; // or '@qazuor/qzpay-mercadopago'
|
|
375
|
+
|
|
376
|
+
const cardService = createSavedCardService({
|
|
377
|
+
provider: 'stripe',
|
|
378
|
+
stripeSecretKey: 'sk_xxx',
|
|
379
|
+
getProviderCustomerId: async (customerId) => {
|
|
380
|
+
const customer = await db.customers.findById(customerId);
|
|
381
|
+
return customer.stripeCustomerId;
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Save a card
|
|
386
|
+
const card = await cardService.save({
|
|
387
|
+
customerId: 'local_cus_123',
|
|
388
|
+
paymentMethodId: 'pm_xxx', // From Stripe.js
|
|
389
|
+
setAsDefault: true,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// List all cards
|
|
393
|
+
const cards = await cardService.list('local_cus_123');
|
|
394
|
+
|
|
395
|
+
// Set card as default (Stripe only)
|
|
396
|
+
await cardService.setDefault('local_cus_123', 'pm_xxx');
|
|
397
|
+
|
|
398
|
+
// Remove a card
|
|
399
|
+
await cardService.remove('local_cus_123', 'pm_xxx');
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Provider Differences:**
|
|
403
|
+
|
|
404
|
+
| Feature | Stripe | MercadoPago |
|
|
405
|
+
|---------|--------|-------------|
|
|
406
|
+
| Save card | `paymentMethodId` | `token` |
|
|
407
|
+
| List cards | ✅ | ✅ |
|
|
408
|
+
| Remove card | ✅ | ✅ |
|
|
409
|
+
| Set default | ✅ Native | ❌ Track in your DB |
|
|
410
|
+
|
|
411
|
+
**Note:** MercadoPago doesn't support default payment methods natively. Your application must track the default card ID in its database.
|
|
412
|
+
|
|
413
|
+
### Subscription Lifecycle Service
|
|
414
|
+
|
|
415
|
+
Automates subscription renewals, trial conversions, and payment retries.
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import { createSubscriptionLifecycle } from '@qazuor/qzpay-core';
|
|
419
|
+
|
|
420
|
+
const lifecycle = createSubscriptionLifecycle(billing, storage, {
|
|
421
|
+
gracePeriodDays: 7,
|
|
422
|
+
retryIntervals: [1, 3, 5], // Retry after 1, 3, and 5 days
|
|
423
|
+
trialConversionDays: 0, // Convert immediately when trial ends
|
|
424
|
+
|
|
425
|
+
// Process payment callback
|
|
426
|
+
processPayment: async (input) => {
|
|
427
|
+
const result = await stripe.paymentIntents.create({
|
|
428
|
+
amount: input.amount,
|
|
429
|
+
currency: input.currency,
|
|
430
|
+
customer: await getStripeCustomerId(input.customerId),
|
|
431
|
+
payment_method: input.paymentMethodId,
|
|
432
|
+
confirm: true,
|
|
433
|
+
off_session: true,
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
success: result.status === 'succeeded',
|
|
437
|
+
paymentId: result.id,
|
|
438
|
+
error: result.last_payment_error?.message,
|
|
439
|
+
};
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
// Get default payment method callback
|
|
443
|
+
getDefaultPaymentMethod: async (customerId) => {
|
|
444
|
+
const pm = await db.paymentMethods.findDefault(customerId);
|
|
445
|
+
return pm ? {
|
|
446
|
+
id: pm.id,
|
|
447
|
+
providerPaymentMethodId: pm.stripePaymentMethodId
|
|
448
|
+
} : null;
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
// Optional: event callback for logging/notifications
|
|
452
|
+
onEvent: async (event) => {
|
|
453
|
+
console.log(`[${event.type}] Subscription: ${event.subscriptionId}`);
|
|
454
|
+
|
|
455
|
+
// Send notifications
|
|
456
|
+
if (event.type === 'subscription.renewal_failed') {
|
|
457
|
+
await sendEmail({
|
|
458
|
+
to: customer.email,
|
|
459
|
+
subject: 'Payment Failed',
|
|
460
|
+
body: 'Your subscription payment failed. Please update your payment method.'
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Run from a cron job
|
|
467
|
+
const results = await lifecycle.processAll();
|
|
468
|
+
console.log('Renewals:', results.renewals);
|
|
469
|
+
console.log('Trial conversions:', results.trialConversions);
|
|
470
|
+
console.log('Retries:', results.retries);
|
|
471
|
+
console.log('Cancellations:', results.cancellations);
|
|
472
|
+
|
|
473
|
+
// Or run individual operations
|
|
474
|
+
await lifecycle.processRenewals();
|
|
475
|
+
await lifecycle.processTrialConversions();
|
|
476
|
+
await lifecycle.processRetries();
|
|
477
|
+
await lifecycle.processCancellations();
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Lifecycle Events:**
|
|
481
|
+
|
|
482
|
+
- `subscription.renewed` - Subscription successfully renewed
|
|
483
|
+
- `subscription.renewal_failed` - Renewal payment failed
|
|
484
|
+
- `subscription.trial_converted` - Trial converted to paid
|
|
485
|
+
- `subscription.trial_conversion_failed` - Trial conversion failed
|
|
486
|
+
- `subscription.entered_grace_period` - Entered grace period after failed payment
|
|
487
|
+
- `subscription.retry_scheduled` - Payment retry scheduled
|
|
488
|
+
- `subscription.retry_succeeded` - Retry payment succeeded
|
|
489
|
+
- `subscription.retry_failed` - All retries exhausted
|
|
490
|
+
- `subscription.canceled_nonpayment` - Canceled due to non-payment
|
|
491
|
+
|
|
492
|
+
**Cron Setup Example:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// Run every hour
|
|
496
|
+
cron.schedule('0 * * * *', async () => {
|
|
497
|
+
const results = await lifecycle.processAll();
|
|
498
|
+
console.log(`Processed ${results.renewals.processed} renewals`);
|
|
499
|
+
});
|
|
500
|
+
```
|
|
211
501
|
|
|
212
502
|
## Utility Functions
|
|
213
503
|
|