tango-app-api-payment-subscription 3.0.62-dev → 3.0.63-dev

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-payment-subscription",
3
- "version": "3.0.62-dev",
3
+ "version": "3.0.63-dev",
4
4
  "description": "paymentSubscription",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -2,6 +2,7 @@ import { download, logger } from 'tango-app-api-middleware';
2
2
  import { aggregate } from '../services/store.service.js';
3
3
  import * as invoice from '../services/invoice.service.js';
4
4
  import { aggregatebilling, countDocuments, create, deleteOne, find, findOne, updateMany, updateOne } from '../services/billing.service.js';
5
+ import * as invoiceService from '../services/invoice.service.js';
5
6
  import mongoose from 'mongoose';
6
7
  import dayjs from 'dayjs';
7
8
 
@@ -470,7 +471,7 @@ export const getInvoices = async ( req, res ) => {
470
471
  filterEndDate = new Date( dayjs().subtract( 1, 'month' ).endOf( 'month' ).format( 'YYYY-MM-DD' ) );
471
472
  }
472
473
  if ( req.body?.filter && req.body.filter == 'last' ) {
473
- filterStartDate = new Date( dayjs().subtract( 2, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
474
+ filterStartDate = new Date( dayjs().subtract( 3, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
474
475
  filterEndDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
475
476
  }
476
477
 
@@ -613,8 +614,8 @@ export const getInvoices = async ( req, res ) => {
613
614
 
614
615
  const billingDate = invoiceData.billingDate;
615
616
 
616
- if ( !groups[billingDate] ) {
617
- groups[billingDate] = {
617
+ if ( !groups[dayjs( billingDate ).format( 'MMMM' )] ) {
618
+ groups[dayjs( billingDate ).format( 'MMMM' )] = {
618
619
  summary: `Summary - ${dayjs( billingDate ).format( 'MMM YYYY' )}`,
619
620
  products: new Set(),
620
621
  stores: 0,
@@ -626,23 +627,23 @@ export const getInvoices = async ( req, res ) => {
626
627
  };
627
628
  }
628
629
 
629
- groups[billingDate].invoices.push( invoiceData );
630
- groups[billingDate].stores += invoiceData.stores;
631
- groups[billingDate].totalAmount += invoiceData.totalAmount;
630
+ groups[dayjs( billingDate ).format( 'MMMM' )].invoices.push( invoiceData );
631
+ groups[dayjs( billingDate ).format( 'MMMM' )].stores += invoiceData.stores;
632
+ groups[dayjs( billingDate ).format( 'MMMM' )].totalAmount += invoiceData.totalAmount;
632
633
 
633
634
  if ( invoiceData.paymentStatus === 'unpaid' ) {
634
635
  const currentDate = dayjs();
635
636
  const givenDate = dayjs( invoiceData.billingDate );
636
637
  const daysDifference = currentDate.diff( givenDate, 'day' );
637
638
  if ( daysDifference <= invoiceData.paymentTerm && daysDifference >= 0 ) {
638
- groups[billingDate].paymentStatus = 'due';
639
+ groups[dayjs( billingDate ).format( 'MMMM' )].paymentStatus = 'due';
639
640
  } else {
640
- groups[billingDate].paymentStatus = 'unpaid';
641
+ groups[dayjs( billingDate ).format( 'MMMM' )].paymentStatus = 'unpaid';
641
642
  }
642
643
  }
643
644
 
644
645
  invoiceData.products.forEach( ( product ) => {
645
- groups[billingDate].products.add( product );
646
+ groups[dayjs( billingDate ).format( 'MMMM' )].products.add( product );
646
647
  } );
647
648
  } );
648
649
 
@@ -669,4 +670,53 @@ export const getInvoices = async ( req, res ) => {
669
670
  }
670
671
  };
671
672
 
673
+ export const onetimePayment = async ( req, res ) => {
674
+ try {
675
+ const invoice = await invoiceService.findOne( { invoice: req.params.invoice } );
676
+ if ( !invoice ) {
677
+ return res.sendError( 'Invoice not found', 404 );
678
+ }
679
+
680
+ const onetimefeeObj = {
681
+ amount: req.body.oneTimeFee,
682
+ productName: 'installationFee',
683
+ description: 'One time setup & installation',
684
+ month: dayjs( invoice.billingDate ).format( 'MMM YYYY' ),
685
+ HsnNumber: '998314',
686
+ price: 0,
687
+ };
688
+
689
+ await invoiceService.invoiceUpdateOne( { invoice: invoice.invoice }, { $push: { products: onetimefeeObj } } );
690
+
691
+
692
+ if ( invoice.currency === 'inr' ) {
693
+ let totalTaxRate = 0;
694
+
695
+ if ( invoice.tax.length ) {
696
+ for ( let i = 0; i<invoice._doc.tax.length; i++ ) {
697
+ totalTaxRate += invoice._doc.tax[i].value;
698
+ invoice._doc.tax[i].taxAmount = Number( invoice._doc.tax[i].taxAmount ) + ( req.body.oneTimeFee * invoice._doc.tax[i].value ) / 100;
699
+ }
700
+ }
701
+
702
+
703
+ invoice._doc.amount += req.body.oneTimeFee;
704
+
705
+ invoice._doc.totalAmount = ( ( invoice._doc.amount * totalTaxRate ) / 100 ) + invoice._doc.amount;
706
+ } else {
707
+ invoice._doc.amount += req.body.oneTimeFee;
708
+ invoice._doc.totalAmount += req.body.oneTimeFee;
709
+ }
710
+
711
+
712
+ await invoiceService.invoiceUpdateOne( { invoice: invoice.invoice }, { $set: { 'tax': invoice._doc.tax, 'amount': invoice._doc.amount, 'totalAmount': invoice._doc.totalAmount } } );
713
+
714
+
715
+ return res.sendSuccess( 'Fee added successfully to invoice' );
716
+ } catch ( error ) {
717
+ logger.error( { error: error, function: 'onetimePayment' } );
718
+ return res.sendError( error, 500 );
719
+ }
720
+ };
721
+
672
722
 
@@ -1,17 +1,115 @@
1
1
 
2
- import { createOrder } from 'tango-app-api-middleware';
3
- import { findOneAccount } from '../services/paymentAccount.service.js';
2
+ import { createOrder, download, logger } from 'tango-app-api-middleware';
3
+ import { findOneAccount, updateOneAccount } from '../services/paymentAccount.service.js';
4
4
  import * as invoiceService from '../services/invoice.service.js';
5
+ import { updateOrder, verifySignature, verifyWebhook } from 'tango-app-api-middleware/src/utils/razorPay.js';
6
+ import { aggregateTransaction, createTransaction } from '../services/transaction.service.js';
7
+ import dayjs from 'dayjs';
5
8
 
6
9
 
10
+ export const getVirtualAccount = async ( req, res ) => {
11
+ try {
12
+ const account = await findOneAccount( { clientId: req.query.clientId }, { accountNumber: 1, ifsc: 1, branch: 1, paymentType: 1, credit: 1, currency: 1, _id: 0 } );
13
+
14
+ if ( !account ) {
15
+ return res.sendError( 'No data found', 204 );
16
+ }
17
+
18
+ return res.sendSuccess( account );
19
+ } catch ( error ) {
20
+ logger.error( { error: error, function: 'getVirtualAccount' } );
21
+ return res.sendError( error, 500 );
22
+ }
23
+ };
24
+
25
+ export const payUsingVallet = async ( req, res ) => {
26
+ try {
27
+ const invoice = await invoiceService.findOne( { invoice: req.params.invoice } );
28
+ if ( !invoice ) {
29
+ return res.sendError( 'Invoice not found', 404 );
30
+ }
31
+
32
+ const account = await findOneAccount( { clientId: invoice.clientId } );
33
+
34
+ if ( !account ) {
35
+ return res.sendError( 'Wallet not found', 404 );
36
+ }
37
+
38
+ if ( account.credit < invoice.totalAmount ) {
39
+ return res.sendError( 'Insufficient balance for payment', 409 );
40
+ }
41
+
42
+ const reducedCredit = Math.round( account.credit - invoice.totalAmount );
43
+
44
+ const deductFromVallet = await updateOneAccount( { clientId: invoice.clientId }, { credit: reducedCredit } );
45
+
46
+ let statusChange;
47
+
48
+ if ( deductFromVallet.modifiedCount ) {
49
+ statusChange = await invoiceService.updateOne( { invoice: req.params.invoice }, { paymentStatus: 'paid' } );
50
+
51
+ const transaction = {
52
+ clientId: account.clientId,
53
+ debtType: 'wallet',
54
+ billingDate: invoice.billingDate,
55
+ invoice: invoice.invoice,
56
+ groupName: invoice.groupName,
57
+ groupId: invoice.groupId,
58
+ amount: invoice.totalAmount,
59
+ currency: invoice.currency,
60
+ transactionType: 'debt',
61
+ balanceCredit: Math.round( account.credit - invoice.totalAmount ),
62
+ balanceCreditCurrency: account.currency,
63
+ };
64
+
65
+ await createTransaction( transaction );
66
+ }
67
+
68
+ if ( !statusChange?.modifiedCount ) {
69
+ logger.error( { error: { ...account, ...invoice }, function: 'payUsingVallet' } );
70
+ }
71
+
72
+ return res.sendSuccess( 'Payment successful !' );
73
+ } catch ( error ) {
74
+ logger.error( { error: error, function: 'payUsingVallet' } );
75
+ return res.sendError( error, 500 );
76
+ }
77
+ };
78
+
7
79
  export const generateOrder = async ( req, res ) => {
8
80
  try {
9
- const order = await createOrder( { amount: req.body.amount, currency: req.body.currency } );
81
+ const invoice = await invoiceService.findOne( { invoice: req.params.invoice } );
82
+ if ( !invoice ) {
83
+ return res.sendError( 'Invoice not found', 404 );
84
+ }
85
+ const orderData = {
86
+ amount: Number( Math.round( invoice.totalAmount ) + '00' ),
87
+ currency: invoice.currency === 'inr' ? 'INR' : 'USD',
88
+ receipt: invoice.invoice,
89
+ };
10
90
 
11
- if ( !order ) {
12
- return res.sendError( 'Failed to generate order id', 500 );
91
+ const createdOrder = await createOrder( orderData );
92
+
93
+ if ( !createdOrder ) {
94
+ return res.sendError( 'Payment order generation failed', 500 );
13
95
  }
14
96
 
97
+ const updatedOrder = await updateOrder( { orderId: createdOrder.id, notes: { oid: createdOrder.id } } );
98
+
99
+ if ( !updatedOrder ) {
100
+ return res.sendError( 'Failed to generate order', 500 );
101
+ }
102
+
103
+ await invoiceService.updateOne( { invoice: invoice.invoice }, { orderId: createdOrder.id } );
104
+
105
+ const order = {
106
+ ...createdOrder,
107
+ apiKey: process.env.RAZORPAY_KEY_ID,
108
+ name: req.user.userName,
109
+ email: req.user.email,
110
+ contact: req.user.mobileNumber,
111
+ };
112
+
15
113
  return res.sendSuccess( order );
16
114
  } catch ( error ) {
17
115
  logger.error( { error: error, function: 'generateOrder' } );
@@ -19,29 +117,223 @@ export const generateOrder = async ( req, res ) => {
19
117
  }
20
118
  };
21
119
 
22
- export const getVirtualAccount = async ( req, res ) => {
120
+ export const verifyPayment = async ( req, res ) => {
23
121
  try {
24
- const account = await findOneAccount( { clientId: req.query.clientId }, { accountNumber: 1, ifsc: 1, branch: 1, paymentType: 1, credit: 1, _id: 0 } );
122
+ const invoice = await invoiceService.findOne( { invoice: req.params.invoice } );
123
+ if ( !invoice ) {
124
+ return res.sendError( 'Invoice not found', 404 );
125
+ }
25
126
 
26
- if ( !account ) {
27
- return res.sendError( 'No data found', 204 );
127
+ const verify = await verifySignature( { razorpayOrderId: invoice.orderId, razorpayPaymentId: req.body.razorpay_payment_id, signature: req.body.razorpay_signature } );
128
+
129
+ if ( !verify ) {
130
+ return res.sendError( 'Invalid signature', 409 );
28
131
  }
29
132
 
30
- return res.sendSuccess( account );
133
+ return res.sendSuccess( 'Payment successful !' );
31
134
  } catch ( error ) {
32
- logger.error( { error: error, function: 'getVirtualAccount' } );
135
+ logger.error( { error: error, function: 'verifyPayment' } );
33
136
  return res.sendError( error, 500 );
34
137
  }
35
138
  };
36
139
 
37
- export const payUsingVallet = async ( req, res ) => {
140
+ export const paymentHook = async ( req, res ) => {
38
141
  try {
39
- const invoice = await invoiceService.findOne( { ivoice: req.params.invoice } );
40
- if ( !invoice ) {
142
+ const signature = req.headers['x-razorpay-signature'];
143
+ const isValid = await verifyWebhook( { body: req.body, signature: signature } );
144
+ if ( isValid ) {
145
+ const { event, payload } = req.body;
146
+
147
+ switch ( event ) {
148
+ case 'payment.captured':
149
+ await handlePaymentCapture( payload );
150
+ break;
151
+ default:
152
+ logger.error( { error: payload, function: `Unhandled event: ${event}` } );
153
+ break;
154
+ }
155
+ }
156
+ res.status( 200 ).send();
157
+ } catch ( error ) {
158
+ logger.error( { error: error, function: 'paymentHook' } );
159
+ return res.sendError( error, 500 );
160
+ }
161
+ };
162
+
163
+ async function handlePaymentCapture( responseBody ) {
164
+ const paidInvoice = await invoiceService.findOne( { orderId: responseBody?.payment?.entity?.notes?.oid } );
165
+
166
+ if ( !paidInvoice ) {
167
+ return;
168
+ }
169
+
170
+ const account = await findOneAccount( { clientId: paidInvoice.clientId } );
171
+
172
+ await invoiceService.updateOne( { invoice: paidInvoice.invoice }, { paymentStatus: 'paid', paymentReferenceId: responseBody?.payment?.entity?.notes?.oid } );
173
+
174
+ const transaction = {
175
+ debtType: 'razor',
176
+ clientId: account.clientId,
177
+ billingDate: paidInvoice.billingDate,
178
+ invoice: paidInvoice.invoice,
179
+ groupName: paidInvoice.groupName,
180
+ groupId: paidInvoice.groupId,
181
+ amount: paidInvoice.totalAmount,
182
+ currency: paidInvoice.currency,
183
+ transactionType: 'debt',
184
+ balanceCredit: Math.round( account.credit ),
185
+ balanceCreditCurrency: account.currency,
186
+ };
187
+
188
+ await createTransaction( transaction );
189
+
190
+ return;
191
+ }
192
+
193
+ export const transactionList = async ( req, res ) => {
194
+ try {
195
+ const matchStage = {
196
+ $match: {
197
+ clientId: req.body.clientId,
198
+ },
199
+ };
200
+
201
+ let filterStartDate = '';
202
+ let filterEndDate = '';
203
+
204
+ if ( req.body?.filter && req.body.filter == 'current' ) {
205
+ filterStartDate = new Date( dayjs().startOf( 'month' ).format( 'YYYY-MM-DD' ) );
206
+ filterEndDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
207
+ }
208
+ if ( req.body?.filter && req.body.filter == 'prev' ) {
209
+ filterStartDate = new Date( dayjs().subtract( 1, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
210
+ filterEndDate = new Date( dayjs().subtract( 1, 'month' ).endOf( 'month' ).format( 'YYYY-MM-DD' ) );
211
+ }
212
+ if ( req.body?.filter && req.body.filter == 'last' ) {
213
+ filterStartDate = new Date( dayjs().subtract( 3, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
214
+ filterEndDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
215
+ }
216
+
217
+ if ( req.body?.filter && !req.body?.searchValue ) {
218
+ matchStage.$match['$and'] = [
219
+ { billingDate: { $gte: filterStartDate } },
220
+ { billingDate: { $lte: filterEndDate } },
221
+ ];
222
+ }
223
+
224
+ if ( req.body.searchValue ) {
225
+ matchStage.$match.$or = [
226
+ {
227
+ groupName: {
228
+ $regex: req.body.searchValue,
229
+ $options: 'i',
230
+ },
231
+ },
232
+ ];
233
+ }
234
+
235
+
236
+ const pipeline = [
237
+ matchStage,
238
+ {
239
+ $project: {
240
+ '_id': 1,
241
+ 'groupName': 1,
242
+ 'billingDate': 1,
243
+ 'amount': 1,
244
+ 'currency': 1,
245
+ 'transactionType': 1,
246
+ 'balanceCredit': 1,
247
+ 'balanceCreditCurrency': 1,
248
+ },
249
+ },
250
+ ];
251
+
252
+ if ( req.body?.sortColumn && req.body?.sortBy ) {
253
+ pipeline.push(
254
+ {
255
+ $addFields: {
256
+ sortField: {
257
+ $toLower: `$${req.body.sortColumn}`,
258
+ },
259
+ },
260
+ },
261
+ {
262
+ $sort: {
263
+ [req.body.sortColumn]: req.body.sortBy,
264
+ },
265
+ },
266
+ );
267
+ }
268
+
269
+ pipeline.push(
270
+ {
271
+ $project: {
272
+ sortField: 0,
273
+ },
274
+ },
275
+
276
+ );
277
+
278
+ const facetStage = {
279
+ $facet: {
280
+ data: [
281
+ {
282
+ $skip: ( ( req.body.offset - 1 ) * req.body.limit ),
283
+ },
284
+ {
285
+ $limit: ( req.body.limit ),
286
+ },
287
+ ],
288
+ pageInfo: [
289
+ {
290
+ $count: 'count',
291
+ },
292
+ ],
293
+ },
294
+ };
295
+
296
+ if ( req.body?.isExport ) {
297
+ facetStage.$facet.data = [];
298
+ }
299
+
300
+ pipeline.push( facetStage );
301
+
302
+ pipeline.push( {
303
+ $unwind: {
304
+ path: '$pageInfo',
305
+ },
306
+ } );
307
+
308
+ const transactionList = await aggregateTransaction( pipeline );
309
+
310
+ if ( !transactionList[0] ) {
41
311
  return res.sendError( 'No data found', 204 );
42
312
  }
313
+
314
+ if ( req.body.isExport ) {
315
+ const exportResult = [];
316
+ for ( let transaction of transactionList[0].data ) {
317
+ exportResult.push( {
318
+ 'Billing date': transaction?.billingDate ? dayjs( transaction.billingDate ).format( 'DD MMM, YYYY' ) : '',
319
+ 'Transaction': transaction?.transactionType === 'debt' ? `Consumption for ${dayjs( transaction?.billingDate ).format( 'MMM YYYY' )}- ${transaction?.groupName}` : 'Paid Credit',
320
+ 'Debit': transaction?.transactionType === 'debt' ? `${transaction?.currency === 'inr' ? '₹' : '$'} ${transaction.amount}` : '',
321
+ 'Credit': transaction?.transactionType === 'credit' ? `${transaction?.currency === 'inr' ? '₹' : '$'} ${transaction.amount}` : '',
322
+ 'Balance credit': `${transaction?.balanceCreditCurrency === 'inr' ? '₹' : '$'} ${transaction.balanceCredit}` || '',
323
+
324
+ } );
325
+ }
326
+
327
+ await download( exportResult, res );
328
+ return;
329
+ }
330
+
331
+
332
+ return res.sendSuccess( transactionList[0] );
43
333
  } catch ( error ) {
44
- logger.error( { error: error, function: 'payUsingVallet' } );
334
+ logger.error( { error: error, function: 'transactionList' } );
45
335
  return res.sendError( error, 500 );
46
336
  }
47
337
  };
338
+
339
+
@@ -0,0 +1,16 @@
1
+
2
+ import { logger } from 'tango-app-api-middleware';
3
+
4
+ const processedEventIds = new Set();
5
+
6
+ export const checkDuplicateEvent = ( req, res, next ) => {
7
+ const eventId = req.headers['x-razorpay-event-id'];
8
+
9
+ if ( processedEventIds.has( eventId ) ) {
10
+ logger.error( { error: `Duplicate event with ID ${eventId}. Skipping processing.`, function: 'checkDuplicateEvent' } );
11
+ res.status( 200 ).send();
12
+ } else {
13
+ processedEventIds.add( eventId );
14
+ next();
15
+ }
16
+ };
@@ -298,14 +298,14 @@ export const createBillingGroupBody = joi.object(
298
298
  groupName: joi.string().required(),
299
299
  groupTag: joi.string().required(),
300
300
  registeredCompanyName: joi.string().required(),
301
- gst: joi.string().required(),
301
+ gst: joi.string().optional(),
302
302
  addressLineOne: joi.string().optional(),
303
303
  addressLineTwo: joi.string().optional(),
304
304
  city: joi.string().optional(),
305
305
  state: joi.string().optional(),
306
306
  country: joi.string().optional(),
307
307
  pinCode: joi.string().optional(),
308
- placeOfSupply: joi.string().required(),
308
+ placeOfSupply: joi.string().optional(),
309
309
  po: joi.string().optional(),
310
310
  stores: joi.array().optional(),
311
311
  proRata: joi.string().optional(),
@@ -409,3 +409,31 @@ export const valletPayParam = joi.object().keys( {
409
409
  export const valletPayValid = {
410
410
  params: valletPayParam,
411
411
  };
412
+
413
+ export const verifyPaymentParam = joi.object().keys( {
414
+ invoice: joi.string().required(),
415
+ } );
416
+
417
+ export const verifyPaymentBody = joi.object().keys( {
418
+ razorpay_payment_id: joi.string().required(),
419
+ razorpay_order_id: joi.string().required(),
420
+ razorpay_signature: joi.string().required(),
421
+ } );
422
+
423
+ export const verifyPaymentValid = {
424
+ params: verifyPaymentParam,
425
+ body: verifyPaymentBody,
426
+ };
427
+
428
+ export const onetimeFeeParam = joi.object().keys( {
429
+ invoice: joi.string().required(),
430
+ } );
431
+
432
+ export const onetimeFeeBody = joi.object().keys( {
433
+ oneTimeFee: joi.number().required(),
434
+ } );
435
+
436
+ export const onetimeFeeValid = {
437
+ params: onetimeFeeParam,
438
+ body: onetimeFeeBody,
439
+ };
@@ -1,8 +1,8 @@
1
1
  import express from 'express';
2
2
  export const billingRouter = express.Router();
3
3
  import { authorize, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
4
- import { createBillingGroup, deleteBillingGroup, getAllBillingGroups, getBillingGroups, getInvoices, subscribedStoreList, updateBillingGroup } from '../controllers/billing.controllers.js';
5
- import { billingGroupSchema, createBillingGroupsSchema, deleteBillingGroupsSchema, getBillingGroupsSchema, getInvoiceSchema, subscribedStoreListSchema, updateBillingGroupsSchema } from '../dtos/validation.dtos.js';
4
+ import { createBillingGroup, deleteBillingGroup, getAllBillingGroups, getBillingGroups, getInvoices, onetimePayment, subscribedStoreList, updateBillingGroup } from '../controllers/billing.controllers.js';
5
+ import { billingGroupSchema, createBillingGroupsSchema, deleteBillingGroupsSchema, getBillingGroupsSchema, getInvoiceSchema, onetimeFeeValid, subscribedStoreListSchema, updateBillingGroupsSchema } from '../dtos/validation.dtos.js';
6
6
 
7
7
 
8
8
  billingRouter.post( '/get-subscribed-store-list', isAllowedSessionHandler, authorize( {
@@ -48,3 +48,9 @@ billingRouter.post( '/get-invoices', isAllowedSessionHandler, authorize( {
48
48
  ],
49
49
  } ), validate( getInvoiceSchema ), getInvoices );
50
50
 
51
+ billingRouter.post( '/onetime-payment/:invoice', isAllowedSessionHandler, authorize( {
52
+ userType: [ 'tango' ], access: [
53
+ { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
54
+ ],
55
+ } ), validate( onetimeFeeValid ), onetimePayment );
56
+
@@ -1,9 +1,10 @@
1
1
  import express from 'express';
2
2
  export const paymentRouter = express.Router();
3
3
  import { authorize, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
4
- import { generateOrder, payUsingVallet } from '../controllers/payment.controller.js';
4
+ import { generateOrder, paymentHook, payUsingVallet, transactionList, verifyPayment } from '../controllers/payment.controller.js';
5
5
  import { getVirtualAccount } from '../controllers/payment.controller.js';
6
- import { getVirtualAccountSchema, valletPayValid } from '../dtos/validation.dtos.js';
6
+ import { getInvoiceSchema, getVirtualAccountSchema, valletPayValid, verifyPaymentValid } from '../dtos/validation.dtos.js';
7
+ import { checkDuplicateEvent } from '../dtos/isUniqueEvent.js';
7
8
 
8
9
 
9
10
  paymentRouter.get( '/get-virtual-account', isAllowedSessionHandler, authorize( {
@@ -12,15 +13,29 @@ paymentRouter.get( '/get-virtual-account', isAllowedSessionHandler, authorize( {
12
13
  ],
13
14
  } ), validate( getVirtualAccountSchema ), getVirtualAccount );
14
15
 
15
- paymentRouter.post( '/generate-order', isAllowedSessionHandler, authorize( {
16
+ paymentRouter.get( '/wallet-pay/:invoice', isAllowedSessionHandler, authorize( {
16
17
  userType: [ 'tango' ], access: [
17
18
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
18
19
  ],
19
- } ), generateOrder );
20
+ } ), validate( valletPayValid ), payUsingVallet );
20
21
 
21
- paymentRouter.get( '/wallet-pay/:invoice', isAllowedSessionHandler, authorize( {
22
+ paymentRouter.get( '/generate-order/:invoice', isAllowedSessionHandler, authorize( {
22
23
  userType: [ 'tango' ], access: [
23
24
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
24
25
  ],
25
- } ), validate( valletPayValid ), payUsingVallet );
26
+ } ), validate( valletPayValid ), generateOrder );
27
+
28
+ paymentRouter.post( '/verify-payment/:invoice', isAllowedSessionHandler, authorize( {
29
+ userType: [ 'tango' ], access: [
30
+ { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
31
+ ],
32
+ } ), validate( verifyPaymentValid ), verifyPayment );
33
+
34
+ paymentRouter.post( '/get-transactions', isAllowedSessionHandler, authorize( {
35
+ userType: [ 'tango' ], access: [
36
+ { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isView' ] },
37
+ ],
38
+ } ), validate( getInvoiceSchema ), transactionList );
39
+
40
+ paymentRouter.post( '/payment-hook', checkDuplicateEvent, paymentHook );
26
41
 
@@ -26,6 +26,10 @@ export const count = async ( query ) => {
26
26
  return await model.invoiceModel.count( query );
27
27
  };
28
28
 
29
+ export const invoiceUpdateOne = async ( query ={}, record={} ) => {
30
+ return await model.invoiceModel.updateOne( query, record );
31
+ };
32
+
29
33
  export const deleteRecord = async ( query ) => {
30
34
  return await model.invoiceModel.deleteOne( query );
31
35
  };
@@ -6,6 +6,9 @@ export const findOneAccount = ( query = {}, record = {} ) => {
6
6
  return model.paymentAccountModel.findOne( query, record );
7
7
  };
8
8
 
9
+ export const updateOneAccount = async ( query ={}, record={} ) => {
10
+ return await model.paymentAccountModel.updateOne( query, { $set: record } );
11
+ };
9
12
  export const aggregate = ( query = [] ) => {
10
13
  return model.paymentAccountModel.aggregate( query );
11
14
  };
@@ -0,0 +1,10 @@
1
+ import model from 'tango-api-schema';
2
+
3
+
4
+ export const createTransaction = async ( data ) => {
5
+ return await model.transactionModel.create( data );
6
+ };
7
+
8
+ export const aggregateTransaction = async ( query ={} ) => {
9
+ return await model.transactionModel.aggregate( query );
10
+ };