@classytic/revenue 0.1.0 → 0.2.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 +53 -5
- package/core/builder.js +12 -4
- package/dist/types/core/builder.d.ts +13 -5
- package/dist/types/services/subscription.service.d.ts +3 -2
- package/enums/payment.enums.js +26 -5
- package/index.js +2 -2
- package/package.json +1 -1
- package/services/payment.service.js +10 -0
- package/services/subscription.service.js +21 -7
package/README.md
CHANGED
|
@@ -494,24 +494,71 @@ const transactions = await Transaction.find({ ... })
|
|
|
494
494
|
const revenue = createRevenue({
|
|
495
495
|
models: { Transaction },
|
|
496
496
|
hooks: {
|
|
497
|
-
|
|
498
|
-
|
|
497
|
+
// Monetization lifecycle (specific)
|
|
498
|
+
'purchase.created': async ({ transaction, isFree }) => {
|
|
499
|
+
console.log('One-time purchase:', transaction._id);
|
|
499
500
|
},
|
|
501
|
+
'subscription.created': async ({ transaction, isFree }) => {
|
|
502
|
+
console.log('Recurring subscription:', transaction._id);
|
|
503
|
+
},
|
|
504
|
+
'free.created': async ({ transaction }) => {
|
|
505
|
+
console.log('Free access granted:', transaction._id);
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
// Generic event (fires for all types)
|
|
509
|
+
'monetization.created': async ({ transaction, monetizationType }) => {
|
|
510
|
+
console.log(`${monetizationType} created:`, transaction._id);
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
// Payment lifecycle
|
|
500
514
|
'payment.verified': async ({ transaction }) => {
|
|
501
515
|
// Send confirmation email
|
|
502
516
|
},
|
|
517
|
+
'payment.failed': async ({ transaction, error, provider }) => {
|
|
518
|
+
// Alert admin or send customer notification
|
|
519
|
+
console.error('Payment failed:', error);
|
|
520
|
+
},
|
|
503
521
|
'payment.refunded': async ({ refundTransaction }) => {
|
|
504
522
|
// Process refund notification
|
|
505
523
|
},
|
|
524
|
+
|
|
525
|
+
// Subscription management (requires Subscription model)
|
|
526
|
+
'subscription.activated': async ({ subscription }) => {
|
|
527
|
+
// Subscription activated after payment
|
|
528
|
+
},
|
|
529
|
+
'subscription.renewed': async ({ subscription, renewalCount }) => {
|
|
530
|
+
// Subscription renewed
|
|
531
|
+
},
|
|
532
|
+
'subscription.paused': async ({ subscription }) => {
|
|
533
|
+
// Subscription paused
|
|
534
|
+
},
|
|
535
|
+
'subscription.resumed': async ({ subscription }) => {
|
|
536
|
+
// Subscription resumed
|
|
537
|
+
},
|
|
538
|
+
'subscription.cancelled': async ({ subscription }) => {
|
|
539
|
+
// Subscription cancelled
|
|
540
|
+
},
|
|
506
541
|
},
|
|
507
542
|
});
|
|
508
543
|
```
|
|
509
544
|
|
|
510
545
|
**Available hooks:**
|
|
511
|
-
|
|
546
|
+
|
|
547
|
+
**Monetization Events (specific):**
|
|
548
|
+
- `purchase.created` - One-time purchase
|
|
549
|
+
- `subscription.created` - Recurring subscription
|
|
550
|
+
- `free.created` - Free access granted
|
|
551
|
+
- `monetization.created` - Generic event (fires for all types)
|
|
552
|
+
|
|
553
|
+
**Payment Events:**
|
|
554
|
+
- `payment.verified` - Payment confirmed
|
|
555
|
+
- `payment.failed` - Payment verification failed
|
|
556
|
+
- `payment.refunded` - Refund processed
|
|
557
|
+
- `payment.webhook.{type}` - Webhook events from providers
|
|
558
|
+
|
|
559
|
+
**Subscription Management Events (requires Subscription model):**
|
|
560
|
+
- `subscription.activated`, `subscription.renewed`
|
|
512
561
|
- `subscription.paused`, `subscription.resumed`, `subscription.cancelled`
|
|
513
|
-
- `payment.verified`, `payment.refunded`
|
|
514
|
-
- `payment.webhook.{type}` (for webhook events)
|
|
515
562
|
|
|
516
563
|
## Provider Patterns
|
|
517
564
|
|
|
@@ -599,6 +646,7 @@ const subscription = await revenue.subscriptions.create({ ... });
|
|
|
599
646
|
- [`examples/transaction.model.js`](examples/transaction.model.js) - Complete model setup
|
|
600
647
|
- [`examples/complete-flow.js`](examples/complete-flow.js) - Full lifecycle (types, refs, state)
|
|
601
648
|
- [`examples/commission-tracking.js`](examples/commission-tracking.js) - Commission calculation
|
|
649
|
+
- [`examples/hooks-v0.2.0.js`](examples/hooks-v0.2.0.js) - v0.2.0 semantic hooks (NEW)
|
|
602
650
|
|
|
603
651
|
## Error Handling
|
|
604
652
|
|
package/core/builder.js
CHANGED
|
@@ -18,7 +18,7 @@ import { ConfigurationError } from './errors.js';
|
|
|
18
18
|
*
|
|
19
19
|
* @param {Object} options - Configuration options
|
|
20
20
|
* @param {Object} options.models - Mongoose models { Transaction, Subscription, etc. }
|
|
21
|
-
* @param {
|
|
21
|
+
* @param {Record<string, import('../providers/base.js').PaymentProvider>} options.providers - Payment providers - Register ANY custom gateway by name
|
|
22
22
|
* @param {Object} options.hooks - Event hooks
|
|
23
23
|
* @param {Object} options.config - Additional configuration
|
|
24
24
|
* @param {Object} options.logger - Logger instance
|
|
@@ -26,7 +26,8 @@ import { ConfigurationError } from './errors.js';
|
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```javascript
|
|
29
|
-
* import { createRevenue
|
|
29
|
+
* import { createRevenue } from '@classytic/revenue';
|
|
30
|
+
* import { ManualProvider } from '@classytic/revenue-manual';
|
|
30
31
|
*
|
|
31
32
|
* const revenue = createRevenue({
|
|
32
33
|
* models: {
|
|
@@ -35,6 +36,10 @@ import { ConfigurationError } from './errors.js';
|
|
|
35
36
|
* },
|
|
36
37
|
* providers: {
|
|
37
38
|
* manual: new ManualProvider(),
|
|
39
|
+
* bkash: new BkashProvider(), // Custom gateway
|
|
40
|
+
* nagad: new NagadProvider(), // Custom gateway
|
|
41
|
+
* stripe: new StripeProvider(), // Custom gateway
|
|
42
|
+
* // ... register any gateway you want
|
|
38
43
|
* },
|
|
39
44
|
* config: {
|
|
40
45
|
* targetModels: ['Subscription', 'Membership'],
|
|
@@ -45,8 +50,11 @@ import { ConfigurationError } from './errors.js';
|
|
|
45
50
|
* },
|
|
46
51
|
* });
|
|
47
52
|
*
|
|
48
|
-
* // Use
|
|
49
|
-
* const subscription = await revenue.subscriptions.create({
|
|
53
|
+
* // Use any registered gateway by name
|
|
54
|
+
* const subscription = await revenue.subscriptions.create({
|
|
55
|
+
* gateway: 'bkash', // Use your custom gateway
|
|
56
|
+
* // ...
|
|
57
|
+
* });
|
|
50
58
|
* await revenue.payments.verify(txnId);
|
|
51
59
|
* ```
|
|
52
60
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @param {Object} options - Configuration options
|
|
5
5
|
* @param {Object} options.models - Mongoose models { Transaction, Subscription, etc. }
|
|
6
|
-
* @param {
|
|
6
|
+
* @param {Record<string, import('../providers/base.js').PaymentProvider>} options.providers - Payment providers - Register ANY custom gateway by name
|
|
7
7
|
* @param {Object} options.hooks - Event hooks
|
|
8
8
|
* @param {Object} options.config - Additional configuration
|
|
9
9
|
* @param {Object} options.logger - Logger instance
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```javascript
|
|
14
|
-
* import { createRevenue
|
|
14
|
+
* import { createRevenue } from '@classytic/revenue';
|
|
15
|
+
* import { ManualProvider } from '@classytic/revenue-manual';
|
|
15
16
|
*
|
|
16
17
|
* const revenue = createRevenue({
|
|
17
18
|
* models: {
|
|
@@ -20,6 +21,10 @@
|
|
|
20
21
|
* },
|
|
21
22
|
* providers: {
|
|
22
23
|
* manual: new ManualProvider(),
|
|
24
|
+
* bkash: new BkashProvider(), // Custom gateway
|
|
25
|
+
* nagad: new NagadProvider(), // Custom gateway
|
|
26
|
+
* stripe: new StripeProvider(), // Custom gateway
|
|
27
|
+
* // ... register any gateway you want
|
|
23
28
|
* },
|
|
24
29
|
* config: {
|
|
25
30
|
* targetModels: ['Subscription', 'Membership'],
|
|
@@ -30,14 +35,17 @@
|
|
|
30
35
|
* },
|
|
31
36
|
* });
|
|
32
37
|
*
|
|
33
|
-
* // Use
|
|
34
|
-
* const subscription = await revenue.subscriptions.create({
|
|
38
|
+
* // Use any registered gateway by name
|
|
39
|
+
* const subscription = await revenue.subscriptions.create({
|
|
40
|
+
* gateway: 'bkash', // Use your custom gateway
|
|
41
|
+
* // ...
|
|
42
|
+
* });
|
|
35
43
|
* await revenue.payments.verify(txnId);
|
|
36
44
|
* ```
|
|
37
45
|
*/
|
|
38
46
|
export function createRevenue(options?: {
|
|
39
47
|
models: any;
|
|
40
|
-
providers:
|
|
48
|
+
providers: Record<string, import("../providers/base.js").PaymentProvider>;
|
|
41
49
|
hooks: any;
|
|
42
50
|
config: any;
|
|
43
51
|
logger: any;
|
|
@@ -18,7 +18,7 @@ export class SubscriptionService {
|
|
|
18
18
|
* @param {String} params.planKey - Plan key ('monthly', 'quarterly', 'yearly')
|
|
19
19
|
* @param {Number} params.amount - Subscription amount
|
|
20
20
|
* @param {String} params.currency - Currency code (default: 'BDT')
|
|
21
|
-
* @param {String} params.gateway - Payment gateway
|
|
21
|
+
* @param {String} params.gateway - Payment gateway name (default: 'manual') - Use ANY registered provider name: 'manual', 'bkash', 'nagad', 'stripe', etc.
|
|
22
22
|
* @param {String} params.entity - Logical entity identifier (e.g., 'Order', 'PlatformSubscription', 'Membership')
|
|
23
23
|
* NOTE: This is NOT a database model name - it's just a logical identifier for categoryMappings
|
|
24
24
|
* @param {String} params.monetizationType - Monetization type ('free', 'subscription', 'purchase')
|
|
@@ -35,6 +35,7 @@ export class SubscriptionService {
|
|
|
35
35
|
* referenceId: subscription._id, // Links to entity
|
|
36
36
|
* referenceModel: 'Subscription', // Model name
|
|
37
37
|
* },
|
|
38
|
+
* gateway: 'bkash', // Any registered provider
|
|
38
39
|
* amount: 1500,
|
|
39
40
|
* // ...
|
|
40
41
|
* });
|
|
@@ -66,7 +67,7 @@ export class SubscriptionService {
|
|
|
66
67
|
*
|
|
67
68
|
* @param {String} subscriptionId - Subscription ID
|
|
68
69
|
* @param {Object} params - Renewal parameters
|
|
69
|
-
* @param {String} params.gateway - Payment gateway
|
|
70
|
+
* @param {String} params.gateway - Payment gateway name (default: 'manual') - Use ANY registered provider name: 'manual', 'bkash', 'nagad', 'stripe', etc.
|
|
70
71
|
* @param {String} params.entity - Logical entity identifier (optional, inherits from subscription)
|
|
71
72
|
* @param {Object} params.paymentData - Payment method details
|
|
72
73
|
* @param {Object} params.metadata - Additional metadata
|
package/enums/payment.enums.js
CHANGED
|
@@ -22,13 +22,34 @@ export const PAYMENT_STATUS_VALUES = Object.values(PAYMENT_STATUS);
|
|
|
22
22
|
|
|
23
23
|
// ============ PAYMENT GATEWAY TYPES ============
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Common gateway type constants for convenience
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
* STRIPE: Stripe provider (build with @classytic/revenue-stripe)
|
|
29
|
-
* SSLCOMMERZ: SSLCommerz provider (build with @classytic/revenue-sslcommerz)
|
|
27
|
+
* ⚠️ IMPORTANT: These are NOT restrictions - just common reference values
|
|
30
28
|
*
|
|
31
|
-
*
|
|
29
|
+
* You can register ANY custom gateway provider by passing it to createRevenue():
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```javascript
|
|
33
|
+
* const revenue = createRevenue({
|
|
34
|
+
* providers: {
|
|
35
|
+
* manual: new ManualProvider(),
|
|
36
|
+
* bkash: new BkashProvider(), // ✅ Custom gateway
|
|
37
|
+
* nagad: new NagadProvider(), // ✅ Custom gateway
|
|
38
|
+
* stripe: new StripeProvider(), // ✅ Custom gateway
|
|
39
|
+
* paypal: new PaypalProvider(), // ✅ Any gateway you want
|
|
40
|
+
* }
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Use by name
|
|
44
|
+
* await revenue.subscriptions.create({ gateway: 'bkash', ... });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Reference values:
|
|
48
|
+
* - MANUAL: Built-in manual provider (@classytic/revenue-manual)
|
|
49
|
+
* - STRIPE: Stripe provider (build with @classytic/revenue-stripe)
|
|
50
|
+
* - SSLCOMMERZ: SSLCommerz provider (build with @classytic/revenue-sslcommerz)
|
|
51
|
+
*
|
|
52
|
+
* Add your own: bkash, nagad, rocket, paypal, razorpay, flutterwave, etc.
|
|
32
53
|
*/
|
|
33
54
|
export const PAYMENT_GATEWAY_TYPE = {
|
|
34
55
|
MANUAL: 'manual',
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @classytic/revenue
|
|
3
|
-
*
|
|
3
|
+
* Revenue Management System
|
|
4
4
|
*
|
|
5
5
|
* A unified, enterprise-grade revenue management system combining
|
|
6
6
|
* monetization (subscriptions, purchases, proration) and payment processing
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Thin, focused, production-ready library with smart defaults.
|
|
10
10
|
*
|
|
11
|
-
* @version
|
|
11
|
+
* @version 0.2.0
|
|
12
12
|
* @author Classytic (Classytic)
|
|
13
13
|
* @license MIT
|
|
14
14
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/revenue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Enterprise revenue management system with subscriptions, purchases, proration, payment processing, escrow, and multi-party splits",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -83,12 +83,22 @@ export class PaymentService {
|
|
|
83
83
|
|
|
84
84
|
// Update transaction as failed
|
|
85
85
|
transaction.status = 'failed';
|
|
86
|
+
transaction.failureReason = error.message;
|
|
86
87
|
transaction.metadata = {
|
|
87
88
|
...transaction.metadata,
|
|
88
89
|
verificationError: error.message,
|
|
90
|
+
failedAt: new Date().toISOString(),
|
|
89
91
|
};
|
|
90
92
|
await transaction.save();
|
|
91
93
|
|
|
94
|
+
// Trigger payment.failed hook
|
|
95
|
+
this._triggerHook('payment.failed', {
|
|
96
|
+
transaction,
|
|
97
|
+
error: error.message,
|
|
98
|
+
provider: gatewayType,
|
|
99
|
+
paymentIntentId,
|
|
100
|
+
});
|
|
101
|
+
|
|
92
102
|
throw new PaymentVerificationError(paymentIntentId, error.message);
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -45,14 +45,14 @@ export class SubscriptionService {
|
|
|
45
45
|
* @param {String} params.planKey - Plan key ('monthly', 'quarterly', 'yearly')
|
|
46
46
|
* @param {Number} params.amount - Subscription amount
|
|
47
47
|
* @param {String} params.currency - Currency code (default: 'BDT')
|
|
48
|
-
* @param {String} params.gateway - Payment gateway
|
|
48
|
+
* @param {String} params.gateway - Payment gateway name (default: 'manual') - Use ANY registered provider name: 'manual', 'bkash', 'nagad', 'stripe', etc.
|
|
49
49
|
* @param {String} params.entity - Logical entity identifier (e.g., 'Order', 'PlatformSubscription', 'Membership')
|
|
50
50
|
* NOTE: This is NOT a database model name - it's just a logical identifier for categoryMappings
|
|
51
51
|
* @param {String} params.monetizationType - Monetization type ('free', 'subscription', 'purchase')
|
|
52
52
|
* @param {Object} params.paymentData - Payment method details
|
|
53
53
|
* @param {Object} params.metadata - Additional metadata
|
|
54
54
|
* @param {String} params.idempotencyKey - Idempotency key for duplicate prevention
|
|
55
|
-
*
|
|
55
|
+
*
|
|
56
56
|
* @example
|
|
57
57
|
* // With polymorphic reference (recommended)
|
|
58
58
|
* await revenue.subscriptions.create({
|
|
@@ -62,10 +62,11 @@ export class SubscriptionService {
|
|
|
62
62
|
* referenceId: subscription._id, // Links to entity
|
|
63
63
|
* referenceModel: 'Subscription', // Model name
|
|
64
64
|
* },
|
|
65
|
+
* gateway: 'bkash', // Any registered provider
|
|
65
66
|
* amount: 1500,
|
|
66
67
|
* // ...
|
|
67
68
|
* });
|
|
68
|
-
*
|
|
69
|
+
*
|
|
69
70
|
* @returns {Promise<Object>} { subscription, transaction, paymentIntent }
|
|
70
71
|
*/
|
|
71
72
|
async create(params) {
|
|
@@ -204,13 +205,26 @@ export class SubscriptionService {
|
|
|
204
205
|
subscription = await SubscriptionModel.create(subscriptionData);
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
// Trigger
|
|
208
|
-
|
|
208
|
+
// Trigger hooks - emit specific event based on monetization type
|
|
209
|
+
const eventData = {
|
|
209
210
|
subscription,
|
|
210
211
|
transaction,
|
|
211
212
|
paymentIntent,
|
|
212
213
|
isFree,
|
|
213
|
-
|
|
214
|
+
monetizationType,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Emit specific monetization event
|
|
218
|
+
if (monetizationType === MONETIZATION_TYPES.PURCHASE) {
|
|
219
|
+
this._triggerHook('purchase.created', eventData);
|
|
220
|
+
} else if (monetizationType === MONETIZATION_TYPES.SUBSCRIPTION) {
|
|
221
|
+
this._triggerHook('subscription.created', eventData);
|
|
222
|
+
} else if (monetizationType === MONETIZATION_TYPES.FREE) {
|
|
223
|
+
this._triggerHook('free.created', eventData);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Also emit generic event for backward compatibility
|
|
227
|
+
this._triggerHook('monetization.created', eventData);
|
|
214
228
|
|
|
215
229
|
return {
|
|
216
230
|
subscription,
|
|
@@ -271,7 +285,7 @@ export class SubscriptionService {
|
|
|
271
285
|
*
|
|
272
286
|
* @param {String} subscriptionId - Subscription ID
|
|
273
287
|
* @param {Object} params - Renewal parameters
|
|
274
|
-
* @param {String} params.gateway - Payment gateway
|
|
288
|
+
* @param {String} params.gateway - Payment gateway name (default: 'manual') - Use ANY registered provider name: 'manual', 'bkash', 'nagad', 'stripe', etc.
|
|
275
289
|
* @param {String} params.entity - Logical entity identifier (optional, inherits from subscription)
|
|
276
290
|
* @param {Object} params.paymentData - Payment method details
|
|
277
291
|
* @param {Object} params.metadata - Additional metadata
|