@classytic/revenue 0.0.2 → 0.0.22
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 +248 -322
- package/enums/index.d.ts +9 -0
- package/enums/index.js +4 -0
- package/enums/transaction.enums.js +16 -0
- package/package.json +1 -1
- package/revenue.d.ts +51 -21
- package/schemas/index.d.ts +0 -21
- package/services/payment.service.js +41 -10
- package/services/subscription.service.js +66 -19
- package/utils/category-resolver.js +74 -0
- package/utils/logger.js +1 -1
- package/providers/manual.js +0 -171
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Category Resolver Utility
|
|
3
|
+
* @classytic/revenue
|
|
4
|
+
*
|
|
5
|
+
* Resolves transaction category based on referenceModel and categoryMappings
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { LIBRARY_CATEGORIES } from '../enums/transaction.enums.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve category for a transaction based on entity and monetizationType
|
|
12
|
+
*
|
|
13
|
+
* Resolution Logic:
|
|
14
|
+
* 1. If categoryMappings[entity] exists → use it
|
|
15
|
+
* 2. Otherwise → fall back to default library category
|
|
16
|
+
*
|
|
17
|
+
* @param {String} entity - The logical entity/identifier (e.g., 'Order', 'PlatformSubscription', 'Membership')
|
|
18
|
+
* NOTE: This is NOT a database model name - it's just a logical identifier
|
|
19
|
+
* @param {String} monetizationType - The monetization type ('subscription', 'purchase', 'free')
|
|
20
|
+
* @param {Object} categoryMappings - User-defined category mappings from config
|
|
21
|
+
* @returns {String} Category name for the transaction
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // With mapping defined
|
|
25
|
+
* resolveCategory('Order', 'subscription', { Order: 'order_subscription' })
|
|
26
|
+
* // Returns: 'order_subscription'
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Without mapping, falls back to library default
|
|
30
|
+
* resolveCategory('Order', 'subscription', {})
|
|
31
|
+
* // Returns: 'subscription'
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Different entities with different mappings
|
|
35
|
+
* const mappings = {
|
|
36
|
+
* Order: 'order_subscription',
|
|
37
|
+
* PlatformSubscription: 'platform_subscription',
|
|
38
|
+
* TenantUpgrade: 'tenant_upgrade',
|
|
39
|
+
* Membership: 'gym_membership',
|
|
40
|
+
* Enrollment: 'course_enrollment',
|
|
41
|
+
* };
|
|
42
|
+
* resolveCategory('PlatformSubscription', 'subscription', mappings)
|
|
43
|
+
* // Returns: 'platform_subscription'
|
|
44
|
+
*/
|
|
45
|
+
export function resolveCategory(entity, monetizationType, categoryMappings = {}) {
|
|
46
|
+
// If user has defined a custom mapping for this entity, use it
|
|
47
|
+
if (entity && categoryMappings[entity]) {
|
|
48
|
+
return categoryMappings[entity];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Otherwise, fall back to library default based on monetization type
|
|
52
|
+
switch (monetizationType) {
|
|
53
|
+
case 'subscription':
|
|
54
|
+
return LIBRARY_CATEGORIES.SUBSCRIPTION; // 'subscription'
|
|
55
|
+
case 'purchase':
|
|
56
|
+
return LIBRARY_CATEGORIES.PURCHASE; // 'purchase'
|
|
57
|
+
default:
|
|
58
|
+
return LIBRARY_CATEGORIES.SUBSCRIPTION; // Default to subscription
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Validate that a category is defined in user's Transaction model enum
|
|
64
|
+
* This is informational - actual validation happens at Mongoose schema level
|
|
65
|
+
*
|
|
66
|
+
* @param {String} category - Category to validate
|
|
67
|
+
* @param {Array<String>} allowedCategories - List of allowed categories
|
|
68
|
+
* @returns {Boolean} Whether category is valid
|
|
69
|
+
*/
|
|
70
|
+
export function isCategoryValid(category, allowedCategories = []) {
|
|
71
|
+
return allowedCategories.includes(category);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default resolveCategory;
|
package/utils/logger.js
CHANGED
package/providers/manual.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manual Payment Provider
|
|
3
|
-
* @classytic/revenue
|
|
4
|
-
*
|
|
5
|
-
* Built-in provider for manual payment verification
|
|
6
|
-
* Perfect for: Cash, bank transfers, mobile money without API
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { PaymentProvider, PaymentIntent, PaymentResult, RefundResult } from './base.js';
|
|
10
|
-
import { nanoid } from 'nanoid';
|
|
11
|
-
|
|
12
|
-
export class ManualProvider extends PaymentProvider {
|
|
13
|
-
constructor(config = {}) {
|
|
14
|
-
super(config);
|
|
15
|
-
this.name = 'manual';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create manual payment intent
|
|
20
|
-
* Returns instructions for manual payment
|
|
21
|
-
*/
|
|
22
|
-
async createIntent(params) {
|
|
23
|
-
const intentId = `manual_${nanoid(16)}`;
|
|
24
|
-
|
|
25
|
-
return new PaymentIntent({
|
|
26
|
-
id: intentId,
|
|
27
|
-
provider: 'manual',
|
|
28
|
-
status: 'pending',
|
|
29
|
-
amount: params.amount,
|
|
30
|
-
currency: params.currency || 'BDT',
|
|
31
|
-
metadata: params.metadata || {},
|
|
32
|
-
instructions: this._getPaymentInstructions(params),
|
|
33
|
-
raw: params,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Verify manual payment
|
|
39
|
-
* Note: This is called by admin after checking payment proof
|
|
40
|
-
*/
|
|
41
|
-
async verifyPayment(intentId) {
|
|
42
|
-
// Manual verification doesn't auto-verify
|
|
43
|
-
// Admin must explicitly call payment verification endpoint
|
|
44
|
-
return new PaymentResult({
|
|
45
|
-
id: intentId,
|
|
46
|
-
provider: 'manual',
|
|
47
|
-
status: 'requires_manual_approval',
|
|
48
|
-
amount: 0, // Amount will be filled by transaction
|
|
49
|
-
currency: 'BDT',
|
|
50
|
-
metadata: {
|
|
51
|
-
message: 'Manual payment requires admin verification',
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get payment status
|
|
58
|
-
*/
|
|
59
|
-
async getStatus(intentId) {
|
|
60
|
-
return this.verifyPayment(intentId);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Refund manual payment
|
|
65
|
-
*/
|
|
66
|
-
async refund(paymentId, amount, options = {}) {
|
|
67
|
-
const refundId = `refund_${nanoid(16)}`;
|
|
68
|
-
|
|
69
|
-
return new RefundResult({
|
|
70
|
-
id: refundId,
|
|
71
|
-
provider: 'manual',
|
|
72
|
-
status: 'succeeded', // Manual refunds are immediately marked as succeeded
|
|
73
|
-
amount: amount,
|
|
74
|
-
currency: options.currency || 'BDT',
|
|
75
|
-
refundedAt: new Date(),
|
|
76
|
-
reason: options.reason || 'Manual refund',
|
|
77
|
-
metadata: options.metadata || {},
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Manual provider doesn't support webhooks
|
|
83
|
-
*/
|
|
84
|
-
async handleWebhook(payload, headers) {
|
|
85
|
-
throw new Error('Manual provider does not support webhooks');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get provider capabilities
|
|
90
|
-
*/
|
|
91
|
-
getCapabilities() {
|
|
92
|
-
return {
|
|
93
|
-
supportsWebhooks: false,
|
|
94
|
-
supportsRefunds: true,
|
|
95
|
-
supportsPartialRefunds: true,
|
|
96
|
-
requiresManualVerification: true,
|
|
97
|
-
supportedMethods: ['cash', 'bank', 'bkash', 'nagad', 'rocket', 'manual'],
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Generate payment instructions for customer
|
|
103
|
-
* @private
|
|
104
|
-
*/
|
|
105
|
-
_getPaymentInstructions(params) {
|
|
106
|
-
const { organizationPaymentInfo, method } = params.metadata || {};
|
|
107
|
-
|
|
108
|
-
if (!organizationPaymentInfo) {
|
|
109
|
-
return 'Please contact the organization for payment details.';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const instructions = [];
|
|
113
|
-
|
|
114
|
-
// Add method-specific instructions
|
|
115
|
-
switch (method) {
|
|
116
|
-
case 'bkash':
|
|
117
|
-
case 'nagad':
|
|
118
|
-
case 'rocket':
|
|
119
|
-
if (organizationPaymentInfo[`${method}Number`]) {
|
|
120
|
-
instructions.push(
|
|
121
|
-
`Send money via ${method.toUpperCase()}:`,
|
|
122
|
-
`Number: ${organizationPaymentInfo[`${method}Number`]}`,
|
|
123
|
-
`Amount: ${params.amount} ${params.currency || 'BDT'}`,
|
|
124
|
-
``,
|
|
125
|
-
`After payment, provide the transaction ID/reference number.`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
|
|
130
|
-
case 'bank':
|
|
131
|
-
if (organizationPaymentInfo.bankAccount) {
|
|
132
|
-
const bank = organizationPaymentInfo.bankAccount;
|
|
133
|
-
instructions.push(
|
|
134
|
-
`Bank Transfer Details:`,
|
|
135
|
-
`Bank: ${bank.bankName || 'N/A'}`,
|
|
136
|
-
`Account: ${bank.accountNumber || 'N/A'}`,
|
|
137
|
-
`Account Name: ${bank.accountName || 'N/A'}`,
|
|
138
|
-
`Amount: ${params.amount} ${params.currency || 'BDT'}`,
|
|
139
|
-
``,
|
|
140
|
-
`After payment, upload proof and provide reference.`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
break;
|
|
144
|
-
|
|
145
|
-
case 'cash':
|
|
146
|
-
instructions.push(
|
|
147
|
-
`Cash Payment:`,
|
|
148
|
-
`Amount: ${params.amount} ${params.currency || 'BDT'}`,
|
|
149
|
-
``,
|
|
150
|
-
`Pay at the organization's office and get a receipt.`
|
|
151
|
-
);
|
|
152
|
-
break;
|
|
153
|
-
|
|
154
|
-
default:
|
|
155
|
-
instructions.push(
|
|
156
|
-
`Payment Amount: ${params.amount} ${params.currency || 'BDT'}`,
|
|
157
|
-
``,
|
|
158
|
-
`Contact the organization for payment details.`
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Add custom instructions if provided
|
|
163
|
-
if (organizationPaymentInfo.paymentInstructions) {
|
|
164
|
-
instructions.push(``, `Additional Instructions:`, organizationPaymentInfo.paymentInstructions);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return instructions.join('\n');
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export default ManualProvider;
|