@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.gateway.paymentIntentId);
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`, `paymentIntentId`, `sessionId` |
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.gateway.paymentIntentId);
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,
@@ -11,6 +11,8 @@
11
11
  export class PaymentIntent {
12
12
  constructor(data: any);
13
13
  id: any;
14
+ sessionId: any;
15
+ paymentIntentId: any;
14
16
  provider: any;
15
17
  status: any;
16
18
  amount: any;
@@ -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",
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; // 'pending', 'processing', 'requires_action', 'succeeded', 'failed'
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
- // Provider-specific data
22
- this.clientSecret = data.clientSecret; // For frontend (Stripe)
23
- this.paymentUrl = data.paymentUrl; // For redirect (SSLCommerz)
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
- paymentIntentId: paymentIntent.id,
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
- paymentIntentId: paymentIntent.id,
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 payment intent ID from webhook
369
+ // Find transaction by sessionId first (for checkout flows), then paymentIntentId
395
370
  const TransactionModel = this.models.Transaction;
396
- const transaction = await TransactionModel.findOne({
397
- 'gateway.paymentIntentId': webhookEvent.data.paymentIntentId,
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(webhookEvent.data.paymentIntentId);
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;