@classytic/revenue 0.2.4 → 1.0.1

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.
Files changed (111) hide show
  1. package/README.md +498 -501
  2. package/dist/actions-CwG-b7fR.d.ts +519 -0
  3. package/dist/core/index.d.ts +884 -0
  4. package/dist/core/index.js +2941 -0
  5. package/dist/core/index.js.map +1 -0
  6. package/dist/enums/index.d.ts +130 -0
  7. package/dist/enums/index.js +167 -0
  8. package/dist/enums/index.js.map +1 -0
  9. package/dist/index-BnJWVXuw.d.ts +378 -0
  10. package/dist/index-ChVD3P9k.d.ts +504 -0
  11. package/dist/index.d.ts +42 -0
  12. package/dist/index.js +4362 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/providers/index.d.ts +132 -0
  15. package/dist/providers/index.js +122 -0
  16. package/dist/providers/index.js.map +1 -0
  17. package/dist/retry-80lBCmSe.d.ts +234 -0
  18. package/dist/schemas/index.d.ts +906 -0
  19. package/dist/schemas/index.js +533 -0
  20. package/dist/schemas/index.js.map +1 -0
  21. package/dist/schemas/validation.d.ts +309 -0
  22. package/dist/schemas/validation.js +249 -0
  23. package/dist/schemas/validation.js.map +1 -0
  24. package/dist/services/index.d.ts +3 -0
  25. package/dist/services/index.js +1632 -0
  26. package/dist/services/index.js.map +1 -0
  27. package/dist/split.enums-DHdM1YAV.d.ts +255 -0
  28. package/dist/split.schema-CETjPq10.d.ts +976 -0
  29. package/dist/utils/index.d.ts +24 -0
  30. package/dist/utils/index.js +1067 -0
  31. package/dist/utils/index.js.map +1 -0
  32. package/package.json +48 -32
  33. package/core/builder.js +0 -219
  34. package/core/container.js +0 -119
  35. package/core/errors.js +0 -262
  36. package/dist/types/core/builder.d.ts +0 -97
  37. package/dist/types/core/container.d.ts +0 -57
  38. package/dist/types/core/errors.d.ts +0 -122
  39. package/dist/types/enums/escrow.enums.d.ts +0 -24
  40. package/dist/types/enums/index.d.ts +0 -69
  41. package/dist/types/enums/monetization.enums.d.ts +0 -6
  42. package/dist/types/enums/payment.enums.d.ts +0 -16
  43. package/dist/types/enums/split.enums.d.ts +0 -25
  44. package/dist/types/enums/subscription.enums.d.ts +0 -15
  45. package/dist/types/enums/transaction.enums.d.ts +0 -24
  46. package/dist/types/index.d.ts +0 -22
  47. package/dist/types/providers/base.d.ts +0 -128
  48. package/dist/types/schemas/escrow/hold.schema.d.ts +0 -54
  49. package/dist/types/schemas/escrow/index.d.ts +0 -6
  50. package/dist/types/schemas/index.d.ts +0 -506
  51. package/dist/types/schemas/split/index.d.ts +0 -8
  52. package/dist/types/schemas/split/split.schema.d.ts +0 -142
  53. package/dist/types/schemas/subscription/index.d.ts +0 -152
  54. package/dist/types/schemas/subscription/info.schema.d.ts +0 -128
  55. package/dist/types/schemas/subscription/plan.schema.d.ts +0 -39
  56. package/dist/types/schemas/transaction/common.schema.d.ts +0 -12
  57. package/dist/types/schemas/transaction/gateway.schema.d.ts +0 -86
  58. package/dist/types/schemas/transaction/index.d.ts +0 -202
  59. package/dist/types/schemas/transaction/payment.schema.d.ts +0 -145
  60. package/dist/types/services/escrow.service.d.ts +0 -51
  61. package/dist/types/services/monetization.service.d.ts +0 -193
  62. package/dist/types/services/payment.service.d.ts +0 -117
  63. package/dist/types/services/transaction.service.d.ts +0 -40
  64. package/dist/types/utils/category-resolver.d.ts +0 -46
  65. package/dist/types/utils/commission-split.d.ts +0 -56
  66. package/dist/types/utils/commission.d.ts +0 -29
  67. package/dist/types/utils/hooks.d.ts +0 -17
  68. package/dist/types/utils/index.d.ts +0 -6
  69. package/dist/types/utils/logger.d.ts +0 -12
  70. package/dist/types/utils/subscription/actions.d.ts +0 -28
  71. package/dist/types/utils/subscription/index.d.ts +0 -2
  72. package/dist/types/utils/subscription/period.d.ts +0 -47
  73. package/dist/types/utils/transaction-type.d.ts +0 -102
  74. package/enums/escrow.enums.js +0 -36
  75. package/enums/index.d.ts +0 -116
  76. package/enums/index.js +0 -110
  77. package/enums/monetization.enums.js +0 -15
  78. package/enums/payment.enums.js +0 -64
  79. package/enums/split.enums.js +0 -37
  80. package/enums/subscription.enums.js +0 -33
  81. package/enums/transaction.enums.js +0 -69
  82. package/index.js +0 -76
  83. package/providers/base.js +0 -162
  84. package/schemas/escrow/hold.schema.js +0 -62
  85. package/schemas/escrow/index.js +0 -15
  86. package/schemas/index.d.ts +0 -33
  87. package/schemas/index.js +0 -27
  88. package/schemas/split/index.js +0 -16
  89. package/schemas/split/split.schema.js +0 -86
  90. package/schemas/subscription/index.js +0 -17
  91. package/schemas/subscription/info.schema.js +0 -115
  92. package/schemas/subscription/plan.schema.js +0 -48
  93. package/schemas/transaction/common.schema.js +0 -22
  94. package/schemas/transaction/gateway.schema.js +0 -69
  95. package/schemas/transaction/index.js +0 -20
  96. package/schemas/transaction/payment.schema.js +0 -110
  97. package/services/escrow.service.js +0 -353
  98. package/services/monetization.service.js +0 -675
  99. package/services/payment.service.js +0 -535
  100. package/services/transaction.service.js +0 -142
  101. package/utils/category-resolver.js +0 -74
  102. package/utils/commission-split.js +0 -180
  103. package/utils/commission.js +0 -83
  104. package/utils/hooks.js +0 -44
  105. package/utils/index.d.ts +0 -164
  106. package/utils/index.js +0 -16
  107. package/utils/logger.js +0 -36
  108. package/utils/subscription/actions.js +0 -68
  109. package/utils/subscription/index.js +0 -20
  110. package/utils/subscription/period.js +0 -123
  111. package/utils/transaction-type.js +0 -254
@@ -1,69 +0,0 @@
1
- /**
2
- * Gateway and Commission Schemas
3
- * @classytic/revenue
4
- *
5
- * Schemas for payment gateway and commission tracking
6
- */
7
-
8
- import { Schema } from 'mongoose';
9
- import { PAYMENT_GATEWAY_TYPE_VALUES } from '../../enums/index.js';
10
-
11
- /**
12
- * Gateway Schema
13
- * For payment gateway integration details
14
- */
15
- export const gatewaySchema = new Schema({
16
- type: {
17
- type: String,
18
- enum: PAYMENT_GATEWAY_TYPE_VALUES,
19
- default: 'manual'
20
- },
21
- paymentIntentId: { type: String },
22
- sessionId: { type: String },
23
- paymentUrl: { type: String },
24
- expiresAt: { type: Date },
25
- metadata: { type: Schema.Types.Mixed },
26
- }, { _id: false });
27
-
28
- /**
29
- * Commission Schema
30
- * Commission tracking for marketplace transactions
31
- */
32
- export const commissionSchema = new Schema({
33
- rate: {
34
- type: Number,
35
- min: 0,
36
- max: 1
37
- },
38
- grossAmount: {
39
- type: Number,
40
- min: 0
41
- },
42
- gatewayFeeRate: {
43
- type: Number,
44
- min: 0,
45
- max: 1
46
- },
47
- gatewayFeeAmount: {
48
- type: Number,
49
- min: 0
50
- },
51
- netAmount: {
52
- type: Number,
53
- min: 0
54
- },
55
- status: {
56
- type: String,
57
- enum: ['pending', 'due', 'paid', 'waived'],
58
- default: 'pending'
59
- },
60
- dueDate: { type: Date },
61
- paidDate: { type: Date },
62
- paidBy: { type: Schema.Types.ObjectId, ref: 'User' },
63
- notes: { type: String },
64
- }, { _id: false });
65
-
66
- export default {
67
- gatewaySchema,
68
- commissionSchema,
69
- };
@@ -1,20 +0,0 @@
1
- /**
2
- * Transaction Schemas
3
- * @classytic/revenue
4
- *
5
- * Re-exports all transaction-related schemas
6
- */
7
-
8
- export * from './payment.schema.js';
9
- export * from './gateway.schema.js';
10
- export * from './common.schema.js';
11
-
12
- import paymentSchemas from './payment.schema.js';
13
- import gatewaySchemas from './gateway.schema.js';
14
- import commonSchemas from './common.schema.js';
15
-
16
- export default {
17
- ...paymentSchemas,
18
- ...gatewaySchemas,
19
- ...commonSchemas,
20
- };
@@ -1,110 +0,0 @@
1
- /**
2
- * Payment Schemas for Transaction Model
3
- * @classytic/revenue
4
- *
5
- * Schemas for payment tracking in transactions
6
- */
7
-
8
- import { Schema } from 'mongoose';
9
- import {
10
- PAYMENT_STATUS,
11
- PAYMENT_STATUS_VALUES,
12
- } from '../../enums/index.js';
13
-
14
- /**
15
- * Current Payment Schema
16
- * Use this in your model: currentPayment: { type: currentPaymentSchema }
17
- *
18
- * Tracks the latest payment transaction for an entity
19
- */
20
- export const currentPaymentSchema = new Schema({
21
- transactionId: {
22
- type: Schema.Types.ObjectId,
23
- ref: 'Transaction',
24
- index: true
25
- },
26
- amount: {
27
- type: Number,
28
- min: 0
29
- },
30
- status: {
31
- type: String,
32
- enum: PAYMENT_STATUS_VALUES,
33
- default: 'pending',
34
- index: true
35
- },
36
- method: {
37
- type: String,
38
- // Users define payment methods in their transaction model
39
- },
40
- reference: {
41
- type: String,
42
- trim: true
43
- },
44
- verifiedAt: {
45
- type: Date
46
- },
47
- verifiedBy: {
48
- type: Schema.Types.ObjectId,
49
- ref: 'User'
50
- },
51
- }, { _id: false });
52
-
53
- /**
54
- * Payment Summary Schema
55
- * Use this in your model: paymentSummary: { type: paymentSummarySchema }
56
- *
57
- * Tracks payment history and totals
58
- */
59
- export const paymentSummarySchema = new Schema({
60
- totalPayments: {
61
- type: Number,
62
- default: 0,
63
- min: 0
64
- },
65
- totalAmountPaid: {
66
- type: Number,
67
- default: 0,
68
- min: 0
69
- },
70
- lastPaymentDate: {
71
- type: Date
72
- },
73
- lastPaymentAmount: {
74
- type: Number,
75
- min: 0
76
- },
77
- }, { _id: false });
78
-
79
- /**
80
- * Payment Details Schema (for manual payments)
81
- * Embedded in Transaction model
82
- */
83
- export const paymentDetailsSchema = new Schema({
84
- provider: { type: String },
85
- walletNumber: { type: String },
86
- walletType: { type: String },
87
- trxId: { type: String },
88
- bankName: { type: String },
89
- accountNumber: { type: String },
90
- accountName: { type: String },
91
- proofUrl: { type: String },
92
- }, { _id: false });
93
-
94
- /**
95
- * Tenant Snapshot Schema
96
- * Captures organization payment details at transaction time (audit trail)
97
- */
98
- export const tenantSnapshotSchema = new Schema({
99
- paymentInstructions: { type: String },
100
- bkashNumber: { type: String },
101
- nagadNumber: { type: String },
102
- bankAccount: { type: String },
103
- }, { _id: false });
104
-
105
- export default {
106
- currentPaymentSchema,
107
- paymentSummarySchema,
108
- paymentDetailsSchema,
109
- tenantSnapshotSchema,
110
- };
@@ -1,353 +0,0 @@
1
- /**
2
- * Escrow Service
3
- * @classytic/revenue
4
- *
5
- * Platform-as-intermediary payment flow
6
- * Hold funds → Verify → Split/Deduct → Release to organization
7
- */
8
-
9
- import { TransactionNotFoundError } from '../core/errors.js';
10
- import { HOLD_STATUS, RELEASE_REASON, HOLD_REASON } from '../enums/escrow.enums.js';
11
- import { TRANSACTION_TYPE, TRANSACTION_STATUS } from '../enums/transaction.enums.js';
12
- import { SPLIT_STATUS } from '../enums/split.enums.js';
13
- import { triggerHook } from '../utils/hooks.js';
14
- import { calculateSplits, calculateOrganizationPayout } from '../utils/commission-split.js';
15
-
16
- export class EscrowService {
17
- constructor(container) {
18
- this.container = container;
19
- this.models = container.get('models');
20
- this.providers = container.get('providers');
21
- this.config = container.get('config');
22
- this.hooks = container.get('hooks');
23
- this.logger = container.get('logger');
24
- }
25
-
26
- /**
27
- * Hold funds in escrow
28
- *
29
- * @param {String} transactionId - Transaction to hold
30
- * @param {Object} options - Hold options
31
- * @returns {Promise<Object>} Updated transaction
32
- */
33
- async hold(transactionId, options = {}) {
34
- const {
35
- reason = HOLD_REASON.PAYMENT_VERIFICATION,
36
- holdUntil = null,
37
- metadata = {},
38
- } = options;
39
-
40
- const TransactionModel = this.models.Transaction;
41
- const transaction = await TransactionModel.findById(transactionId);
42
-
43
- if (!transaction) {
44
- throw new TransactionNotFoundError(transactionId);
45
- }
46
-
47
- if (transaction.status !== TRANSACTION_STATUS.VERIFIED) {
48
- throw new Error(`Cannot hold transaction with status: ${transaction.status}. Must be verified.`);
49
- }
50
-
51
- transaction.hold = {
52
- status: HOLD_STATUS.HELD,
53
- heldAmount: transaction.amount,
54
- releasedAmount: 0,
55
- reason,
56
- heldAt: new Date(),
57
- ...(holdUntil && { holdUntil }),
58
- releases: [],
59
- metadata,
60
- };
61
-
62
- await transaction.save();
63
-
64
- this._triggerHook('escrow.held', {
65
- transaction,
66
- heldAmount: transaction.amount,
67
- reason,
68
- });
69
-
70
- return transaction;
71
- }
72
-
73
- /**
74
- * Release funds from escrow to recipient
75
- *
76
- * @param {String} transactionId - Transaction to release
77
- * @param {Object} options - Release options
78
- * @returns {Promise<Object>} { transaction, releaseTransaction }
79
- */
80
- async release(transactionId, options = {}) {
81
- const {
82
- amount = null,
83
- recipientId,
84
- recipientType = 'organization',
85
- reason = RELEASE_REASON.PAYMENT_VERIFIED,
86
- releasedBy = null,
87
- createTransaction = true,
88
- metadata = {},
89
- } = options;
90
-
91
- const TransactionModel = this.models.Transaction;
92
- const transaction = await TransactionModel.findById(transactionId);
93
-
94
- if (!transaction) {
95
- throw new TransactionNotFoundError(transactionId);
96
- }
97
-
98
- if (!transaction.hold || transaction.hold.status !== HOLD_STATUS.HELD) {
99
- throw new Error(`Transaction is not in held status. Current: ${transaction.hold?.status || 'none'}`);
100
- }
101
-
102
- if (!recipientId) {
103
- throw new Error('recipientId is required for release');
104
- }
105
-
106
- const releaseAmount = amount || (transaction.hold.heldAmount - transaction.hold.releasedAmount);
107
- const availableAmount = transaction.hold.heldAmount - transaction.hold.releasedAmount;
108
-
109
- if (releaseAmount > availableAmount) {
110
- throw new Error(`Release amount (${releaseAmount}) exceeds available held amount (${availableAmount})`);
111
- }
112
-
113
- const releaseRecord = {
114
- amount: releaseAmount,
115
- recipientId,
116
- recipientType,
117
- releasedAt: new Date(),
118
- releasedBy,
119
- reason,
120
- metadata,
121
- };
122
-
123
- transaction.hold.releases.push(releaseRecord);
124
- transaction.hold.releasedAmount += releaseAmount;
125
-
126
- const isFullRelease = transaction.hold.releasedAmount >= transaction.hold.heldAmount;
127
- const isPartialRelease = transaction.hold.releasedAmount > 0 && transaction.hold.releasedAmount < transaction.hold.heldAmount;
128
-
129
- if (isFullRelease) {
130
- transaction.hold.status = HOLD_STATUS.RELEASED;
131
- transaction.hold.releasedAt = new Date();
132
- transaction.status = TRANSACTION_STATUS.COMPLETED;
133
- } else if (isPartialRelease) {
134
- transaction.hold.status = HOLD_STATUS.PARTIALLY_RELEASED;
135
- }
136
-
137
- await transaction.save();
138
-
139
- let releaseTransaction = null;
140
- if (createTransaction) {
141
- releaseTransaction = await TransactionModel.create({
142
- organizationId: transaction.organizationId,
143
- customerId: recipientId,
144
- amount: releaseAmount,
145
- currency: transaction.currency,
146
- category: transaction.category,
147
- type: TRANSACTION_TYPE.INCOME,
148
- method: transaction.method,
149
- status: TRANSACTION_STATUS.COMPLETED,
150
- gateway: transaction.gateway,
151
- referenceId: transaction.referenceId,
152
- referenceModel: transaction.referenceModel,
153
- metadata: {
154
- ...metadata,
155
- isRelease: true,
156
- heldTransactionId: transaction._id.toString(),
157
- releaseReason: reason,
158
- recipientType,
159
- },
160
- idempotencyKey: `release_${transaction._id}_${Date.now()}`,
161
- });
162
- }
163
-
164
- this._triggerHook('escrow.released', {
165
- transaction,
166
- releaseTransaction,
167
- releaseAmount,
168
- recipientId,
169
- recipientType,
170
- reason,
171
- isFullRelease,
172
- isPartialRelease,
173
- });
174
-
175
- return {
176
- transaction,
177
- releaseTransaction,
178
- releaseAmount,
179
- isFullRelease,
180
- isPartialRelease,
181
- };
182
- }
183
-
184
- /**
185
- * Cancel hold and release back to customer
186
- *
187
- * @param {String} transactionId - Transaction to cancel hold
188
- * @param {Object} options - Cancel options
189
- * @returns {Promise<Object>} Updated transaction
190
- */
191
- async cancel(transactionId, options = {}) {
192
- const { reason = 'Hold cancelled', metadata = {} } = options;
193
-
194
- const TransactionModel = this.models.Transaction;
195
- const transaction = await TransactionModel.findById(transactionId);
196
-
197
- if (!transaction) {
198
- throw new TransactionNotFoundError(transactionId);
199
- }
200
-
201
- if (!transaction.hold || transaction.hold.status !== HOLD_STATUS.HELD) {
202
- throw new Error(`Transaction is not in held status. Current: ${transaction.hold?.status || 'none'}`);
203
- }
204
-
205
- transaction.hold.status = HOLD_STATUS.CANCELLED;
206
- transaction.hold.cancelledAt = new Date();
207
- transaction.hold.metadata = {
208
- ...transaction.hold.metadata,
209
- ...metadata,
210
- cancelReason: reason,
211
- };
212
-
213
- transaction.status = TRANSACTION_STATUS.CANCELLED;
214
-
215
- await transaction.save();
216
-
217
- this._triggerHook('escrow.cancelled', {
218
- transaction,
219
- reason,
220
- });
221
-
222
- return transaction;
223
- }
224
-
225
- /**
226
- * Split payment to multiple recipients
227
- * Deducts splits from held amount and releases remainder to organization
228
- *
229
- * @param {String} transactionId - Transaction to split
230
- * @param {Array} splitRules - Split configuration
231
- * @returns {Promise<Object>} { transaction, splitTransactions, organizationTransaction }
232
- */
233
- async split(transactionId, splitRules = []) {
234
- const TransactionModel = this.models.Transaction;
235
- const transaction = await TransactionModel.findById(transactionId);
236
-
237
- if (!transaction) {
238
- throw new TransactionNotFoundError(transactionId);
239
- }
240
-
241
- if (!transaction.hold || transaction.hold.status !== HOLD_STATUS.HELD) {
242
- throw new Error(`Transaction must be held before splitting. Current: ${transaction.hold?.status || 'none'}`);
243
- }
244
-
245
- if (!splitRules || splitRules.length === 0) {
246
- throw new Error('splitRules cannot be empty');
247
- }
248
-
249
- const splits = calculateSplits(
250
- transaction.amount,
251
- splitRules,
252
- transaction.commission?.gatewayFeeRate || 0
253
- );
254
-
255
- transaction.splits = splits;
256
- await transaction.save();
257
-
258
- const splitTransactions = [];
259
-
260
- for (const split of splits) {
261
- const splitTransaction = await TransactionModel.create({
262
- organizationId: transaction.organizationId,
263
- customerId: split.recipientId,
264
- amount: split.netAmount,
265
- currency: transaction.currency,
266
- category: split.type,
267
- type: TRANSACTION_TYPE.EXPENSE,
268
- method: transaction.method,
269
- status: TRANSACTION_STATUS.COMPLETED,
270
- gateway: transaction.gateway,
271
- referenceId: transaction.referenceId,
272
- referenceModel: transaction.referenceModel,
273
- metadata: {
274
- isSplit: true,
275
- splitType: split.type,
276
- recipientType: split.recipientType,
277
- originalTransactionId: transaction._id.toString(),
278
- grossAmount: split.grossAmount,
279
- gatewayFeeAmount: split.gatewayFeeAmount,
280
- },
281
- idempotencyKey: `split_${transaction._id}_${split.recipientId}_${Date.now()}`,
282
- });
283
-
284
- split.payoutTransactionId = splitTransaction._id.toString();
285
- split.status = SPLIT_STATUS.PAID;
286
- split.paidDate = new Date();
287
-
288
- splitTransactions.push(splitTransaction);
289
- }
290
-
291
- await transaction.save();
292
-
293
- const organizationPayout = calculateOrganizationPayout(transaction.amount, splits);
294
-
295
- const organizationTransaction = await this.release(transactionId, {
296
- amount: organizationPayout,
297
- recipientId: transaction.organizationId,
298
- recipientType: 'organization',
299
- reason: RELEASE_REASON.PAYMENT_VERIFIED,
300
- createTransaction: true,
301
- metadata: {
302
- afterSplits: true,
303
- totalSplits: splits.length,
304
- totalSplitAmount: transaction.amount - organizationPayout,
305
- },
306
- });
307
-
308
- this._triggerHook('escrow.split', {
309
- transaction,
310
- splits,
311
- splitTransactions,
312
- organizationTransaction: organizationTransaction.releaseTransaction,
313
- organizationPayout,
314
- });
315
-
316
- return {
317
- transaction,
318
- splits,
319
- splitTransactions,
320
- organizationTransaction: organizationTransaction.releaseTransaction,
321
- organizationPayout,
322
- };
323
- }
324
-
325
- /**
326
- * Get escrow status
327
- *
328
- * @param {String} transactionId - Transaction ID
329
- * @returns {Promise<Object>} Escrow status
330
- */
331
- async getStatus(transactionId) {
332
- const TransactionModel = this.models.Transaction;
333
- const transaction = await TransactionModel.findById(transactionId);
334
-
335
- if (!transaction) {
336
- throw new TransactionNotFoundError(transactionId);
337
- }
338
-
339
- return {
340
- transaction,
341
- hold: transaction.hold || null,
342
- splits: transaction.splits || [],
343
- hasHold: !!transaction.hold,
344
- hasSplits: transaction.splits && transaction.splits.length > 0,
345
- };
346
- }
347
-
348
- _triggerHook(event, data) {
349
- triggerHook(this.hooks, event, data, this.logger);
350
- }
351
- }
352
-
353
- export default EscrowService;