@classytic/revenue 0.2.3 → 0.2.4
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
CHANGED
|
@@ -47,8 +47,8 @@ const { transaction } = await revenue.monetization.create({
|
|
|
47
47
|
paymentData: { method: 'card' },
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
// Verify → Refund
|
|
51
|
-
await revenue.payments.verify(transaction.
|
|
50
|
+
// Verify → Refund (use transaction._id - works for all providers)
|
|
51
|
+
await revenue.payments.verify(transaction._id);
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
### Multi-Tenant (Marketplace/Platform)
|
|
@@ -130,7 +130,7 @@ export default mongoose.model('Transaction', transactionSchema);
|
|
|
130
130
|
|
|
131
131
|
| Schema | Purpose | Key Fields |
|
|
132
132
|
|--------|---------|------------|
|
|
133
|
-
| `gatewaySchema` | Payment gateway integration | `type`, `
|
|
133
|
+
| `gatewaySchema` | Payment gateway integration | `type`, `sessionId`, `paymentIntentId` |
|
|
134
134
|
| `paymentDetailsSchema` | Payment method info | `walletNumber`, `trxId`, `bankName` |
|
|
135
135
|
| `commissionSchema` | Commission tracking (marketplace) | `rate`, `grossAmount`, `gatewayFeeAmount`, `netAmount` |
|
|
136
136
|
| `currentPaymentSchema` | Latest payment (for Order/Subscription models) | `transactionId`, `status`, `verifiedAt` |
|
|
@@ -164,7 +164,7 @@ const { subscription, transaction, paymentIntent } =
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
// Verify and activate
|
|
167
|
-
await revenue.payments.verify(transaction.
|
|
167
|
+
await revenue.payments.verify(transaction._id);
|
|
168
168
|
await revenue.monetization.activate(subscription._id);
|
|
169
169
|
|
|
170
170
|
// Renew subscription
|
|
@@ -599,6 +599,8 @@ export class StripeProvider extends PaymentProvider {
|
|
|
599
599
|
|
|
600
600
|
return new PaymentIntent({
|
|
601
601
|
id: intent.id,
|
|
602
|
+
sessionId: null, // No session for direct intents
|
|
603
|
+
paymentIntentId: intent.id, // Available immediately
|
|
602
604
|
provider: 'stripe',
|
|
603
605
|
status: intent.status,
|
|
604
606
|
amount: intent.amount,
|
|
@@ -13,7 +13,7 @@ export class PaymentService {
|
|
|
13
13
|
/**
|
|
14
14
|
* Verify a payment
|
|
15
15
|
*
|
|
16
|
-
* @param {String} paymentIntentId - Payment intent ID or transaction ID
|
|
16
|
+
* @param {String} paymentIntentId - Payment intent ID, session ID, or transaction ID
|
|
17
17
|
* @param {Object} options - Verification options
|
|
18
18
|
* @param {String} options.verifiedBy - User ID who verified (for manual verification)
|
|
19
19
|
* @returns {Promise<Object>} { transaction, status }
|
|
@@ -24,14 +24,14 @@ export class PaymentService {
|
|
|
24
24
|
/**
|
|
25
25
|
* Get payment status
|
|
26
26
|
*
|
|
27
|
-
* @param {String} paymentIntentId - Payment intent ID or transaction ID
|
|
27
|
+
* @param {String} paymentIntentId - Payment intent ID, session ID, or transaction ID
|
|
28
28
|
* @returns {Promise<Object>} { transaction, status }
|
|
29
29
|
*/
|
|
30
30
|
getStatus(paymentIntentId: string): Promise<any>;
|
|
31
31
|
/**
|
|
32
32
|
* Refund a payment
|
|
33
33
|
*
|
|
34
|
-
* @param {String} paymentId - Payment intent ID or transaction ID
|
|
34
|
+
* @param {String} paymentId - Payment intent ID, session ID, or transaction ID
|
|
35
35
|
* @param {Number} amount - Amount to refund (optional, full refund if not provided)
|
|
36
36
|
* @param {Object} options - Refund options
|
|
37
37
|
* @param {String} options.reason - Refund reason
|
|
@@ -76,6 +76,11 @@ export class PaymentService {
|
|
|
76
76
|
* @private
|
|
77
77
|
*/
|
|
78
78
|
private _triggerHook;
|
|
79
|
+
/**
|
|
80
|
+
* Find transaction by sessionId, paymentIntentId, or transaction ID
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
private _findTransaction;
|
|
79
84
|
}
|
|
80
85
|
export default PaymentService;
|
|
81
86
|
export type PaymentVerifyResult = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/revenue",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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",
|
package/providers/base.js
CHANGED
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
export class PaymentIntent {
|
|
13
13
|
constructor(data) {
|
|
14
14
|
this.id = data.id;
|
|
15
|
+
this.sessionId = data.sessionId || null;
|
|
16
|
+
this.paymentIntentId = data.paymentIntentId || null;
|
|
15
17
|
this.provider = data.provider;
|
|
16
|
-
this.status = data.status;
|
|
18
|
+
this.status = data.status;
|
|
17
19
|
this.amount = data.amount;
|
|
18
20
|
this.currency = data.currency || 'BDT';
|
|
19
21
|
this.metadata = data.metadata || {};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.instructions = data.instructions; // For manual
|
|
25
|
-
this.raw = data.raw; // Raw provider response
|
|
22
|
+
this.clientSecret = data.clientSecret;
|
|
23
|
+
this.paymentUrl = data.paymentUrl;
|
|
24
|
+
this.instructions = data.instructions;
|
|
25
|
+
this.raw = data.raw;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -177,8 +177,10 @@ export class MonetizationService {
|
|
|
177
177
|
status: paymentIntent.status === 'succeeded' ? 'verified' : 'pending',
|
|
178
178
|
gateway: {
|
|
179
179
|
type: gateway,
|
|
180
|
-
|
|
180
|
+
sessionId: paymentIntent.sessionId,
|
|
181
|
+
paymentIntentId: paymentIntent.paymentIntentId,
|
|
181
182
|
provider: paymentIntent.provider,
|
|
183
|
+
metadata: paymentIntent.metadata,
|
|
182
184
|
},
|
|
183
185
|
paymentDetails: {
|
|
184
186
|
provider: gateway,
|
|
@@ -395,8 +397,10 @@ export class MonetizationService {
|
|
|
395
397
|
status: paymentIntent.status === 'succeeded' ? 'verified' : 'pending',
|
|
396
398
|
gateway: {
|
|
397
399
|
type: gateway,
|
|
398
|
-
|
|
400
|
+
sessionId: paymentIntent.sessionId,
|
|
401
|
+
paymentIntentId: paymentIntent.paymentIntentId,
|
|
399
402
|
provider: paymentIntent.provider,
|
|
403
|
+
metadata: paymentIntent.metadata,
|
|
400
404
|
},
|
|
401
405
|
paymentDetails: {
|
|
402
406
|
provider: gateway,
|
|
@@ -53,7 +53,7 @@ export class PaymentService {
|
|
|
53
53
|
/**
|
|
54
54
|
* Verify a payment
|
|
55
55
|
*
|
|
56
|
-
* @param {String} paymentIntentId - Payment intent ID or transaction ID
|
|
56
|
+
* @param {String} paymentIntentId - Payment intent ID, session ID, or transaction ID
|
|
57
57
|
* @param {Object} options - Verification options
|
|
58
58
|
* @param {String} options.verifiedBy - User ID who verified (for manual verification)
|
|
59
59
|
* @returns {Promise<Object>} { transaction, status }
|
|
@@ -62,16 +62,7 @@ export class PaymentService {
|
|
|
62
62
|
const { verifiedBy = null } = options;
|
|
63
63
|
|
|
64
64
|
const TransactionModel = this.models.Transaction;
|
|
65
|
-
|
|
66
|
-
// Find transaction by payment intent ID or transaction ID
|
|
67
|
-
let transaction = await TransactionModel.findOne({
|
|
68
|
-
'gateway.paymentIntentId': paymentIntentId,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
if (!transaction) {
|
|
72
|
-
// Try finding by transaction ID directly
|
|
73
|
-
transaction = await TransactionModel.findById(paymentIntentId);
|
|
74
|
-
}
|
|
65
|
+
let transaction = await this._findTransaction(TransactionModel, paymentIntentId);
|
|
75
66
|
|
|
76
67
|
if (!transaction) {
|
|
77
68
|
throw new TransactionNotFoundError(paymentIntentId);
|
|
@@ -160,20 +151,12 @@ export class PaymentService {
|
|
|
160
151
|
/**
|
|
161
152
|
* Get payment status
|
|
162
153
|
*
|
|
163
|
-
* @param {String} paymentIntentId - Payment intent ID or transaction ID
|
|
154
|
+
* @param {String} paymentIntentId - Payment intent ID, session ID, or transaction ID
|
|
164
155
|
* @returns {Promise<Object>} { transaction, status }
|
|
165
156
|
*/
|
|
166
157
|
async getStatus(paymentIntentId) {
|
|
167
158
|
const TransactionModel = this.models.Transaction;
|
|
168
|
-
|
|
169
|
-
// Find transaction
|
|
170
|
-
let transaction = await TransactionModel.findOne({
|
|
171
|
-
'gateway.paymentIntentId': paymentIntentId,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
if (!transaction) {
|
|
175
|
-
transaction = await TransactionModel.findById(paymentIntentId);
|
|
176
|
-
}
|
|
159
|
+
let transaction = await this._findTransaction(TransactionModel, paymentIntentId);
|
|
177
160
|
|
|
178
161
|
if (!transaction) {
|
|
179
162
|
throw new TransactionNotFoundError(paymentIntentId);
|
|
@@ -212,7 +195,7 @@ export class PaymentService {
|
|
|
212
195
|
/**
|
|
213
196
|
* Refund a payment
|
|
214
197
|
*
|
|
215
|
-
* @param {String} paymentId - Payment intent ID or transaction ID
|
|
198
|
+
* @param {String} paymentId - Payment intent ID, session ID, or transaction ID
|
|
216
199
|
* @param {Number} amount - Amount to refund (optional, full refund if not provided)
|
|
217
200
|
* @param {Object} options - Refund options
|
|
218
201
|
* @param {String} options.reason - Refund reason
|
|
@@ -222,15 +205,7 @@ export class PaymentService {
|
|
|
222
205
|
const { reason = null } = options;
|
|
223
206
|
|
|
224
207
|
const TransactionModel = this.models.Transaction;
|
|
225
|
-
|
|
226
|
-
// Find transaction
|
|
227
|
-
let transaction = await TransactionModel.findOne({
|
|
228
|
-
'gateway.paymentIntentId': paymentId,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
if (!transaction) {
|
|
232
|
-
transaction = await TransactionModel.findById(paymentId);
|
|
233
|
-
}
|
|
208
|
+
let transaction = await this._findTransaction(TransactionModel, paymentId);
|
|
234
209
|
|
|
235
210
|
if (!transaction) {
|
|
236
211
|
throw new TransactionNotFoundError(paymentId);
|
|
@@ -384,26 +359,47 @@ export class PaymentService {
|
|
|
384
359
|
}
|
|
385
360
|
|
|
386
361
|
// Validate webhook event structure
|
|
387
|
-
if (!webhookEvent?.data?.paymentIntentId) {
|
|
362
|
+
if (!webhookEvent?.data?.sessionId && !webhookEvent?.data?.paymentIntentId) {
|
|
388
363
|
throw new ValidationError(
|
|
389
|
-
`Invalid webhook event structure from ${providerName}: missing paymentIntentId`,
|
|
364
|
+
`Invalid webhook event structure from ${providerName}: missing sessionId or paymentIntentId`,
|
|
390
365
|
{ provider: providerName, eventType: webhookEvent?.type }
|
|
391
366
|
);
|
|
392
367
|
}
|
|
393
368
|
|
|
394
|
-
// Find transaction by
|
|
369
|
+
// Find transaction by sessionId first (for checkout flows), then paymentIntentId
|
|
395
370
|
const TransactionModel = this.models.Transaction;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
371
|
+
let transaction = null;
|
|
372
|
+
|
|
373
|
+
if (webhookEvent.data.sessionId) {
|
|
374
|
+
transaction = await TransactionModel.findOne({
|
|
375
|
+
'gateway.sessionId': webhookEvent.data.sessionId,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!transaction && webhookEvent.data.paymentIntentId) {
|
|
380
|
+
transaction = await TransactionModel.findOne({
|
|
381
|
+
'gateway.paymentIntentId': webhookEvent.data.paymentIntentId,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
399
384
|
|
|
400
385
|
if (!transaction) {
|
|
401
386
|
this.logger.warn('Transaction not found for webhook event', {
|
|
402
387
|
provider: providerName,
|
|
403
388
|
eventId: webhookEvent.id,
|
|
389
|
+
sessionId: webhookEvent.data.sessionId,
|
|
404
390
|
paymentIntentId: webhookEvent.data.paymentIntentId,
|
|
405
391
|
});
|
|
406
|
-
throw new TransactionNotFoundError(
|
|
392
|
+
throw new TransactionNotFoundError(
|
|
393
|
+
webhookEvent.data.sessionId || webhookEvent.data.paymentIntentId
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Update gateway with complete information from webhook
|
|
398
|
+
if (webhookEvent.data.sessionId && !transaction.gateway.sessionId) {
|
|
399
|
+
transaction.gateway.sessionId = webhookEvent.data.sessionId;
|
|
400
|
+
}
|
|
401
|
+
if (webhookEvent.data.paymentIntentId && !transaction.gateway.paymentIntentId) {
|
|
402
|
+
transaction.gateway.paymentIntentId = webhookEvent.data.paymentIntentId;
|
|
407
403
|
}
|
|
408
404
|
|
|
409
405
|
// Check for duplicate webhook processing (idempotency)
|
|
@@ -512,6 +508,28 @@ export class PaymentService {
|
|
|
512
508
|
_triggerHook(event, data) {
|
|
513
509
|
triggerHook(this.hooks, event, data, this.logger);
|
|
514
510
|
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Find transaction by sessionId, paymentIntentId, or transaction ID
|
|
514
|
+
* @private
|
|
515
|
+
*/
|
|
516
|
+
async _findTransaction(TransactionModel, identifier) {
|
|
517
|
+
let transaction = await TransactionModel.findOne({
|
|
518
|
+
'gateway.sessionId': identifier,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
if (!transaction) {
|
|
522
|
+
transaction = await TransactionModel.findOne({
|
|
523
|
+
'gateway.paymentIntentId': identifier,
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (!transaction) {
|
|
528
|
+
transaction = await TransactionModel.findById(identifier);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return transaction;
|
|
532
|
+
}
|
|
515
533
|
}
|
|
516
534
|
|
|
517
535
|
export default PaymentService;
|