Created
July 27, 2025 09:25
-
-
Save Comurule/08228c3e34ef2a4744f3cc6728ec5a91 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| async intiate(userId: Types.ObjectId, payload: InitiateCrossBorderTrxDTO) { | |
| const session = await this.connection.startSession(); | |
| try { | |
| await session.withTransaction( | |
| async (session: ClientSession) => { | |
| const isVerified = await this.pinService.verifyUser({ | |
| userId, | |
| pin: payload.pin, | |
| biometric: payload.biometric, | |
| }); | |
| if (!isVerified) { | |
| throw new BadRequestException({ | |
| statusCode: HttpStatus.BAD_REQUEST, | |
| message: 'User pin/biometrics is not verified.', | |
| }); | |
| } | |
| const paymentProviders = this.getPaymentProviders(payload); | |
| const payerWalletProfile = await this.walletService.getWalletProfile( | |
| { userId, currency: payload.from }, | |
| session, | |
| ); | |
| const { user: payerUserInfo, ...payerWallet } = payerWalletProfile; | |
| if (!payerWallet) { | |
| throw new BadRequestException({ | |
| statusCode: HttpStatus.BAD_REQUEST, | |
| message: 'Insufficient Balance', | |
| }); | |
| } | |
| const chargedAmount = await this.chargeService.verifyChargedAmount( | |
| payload.amount, | |
| payerUserInfo.phoneNumber, | |
| TransactionType.CROSS_BORDER, | |
| 0, | |
| false, | |
| ); | |
| await this.walletService.checkTransactionLimit( | |
| chargedAmount.totalAmount, | |
| userId.toString(), | |
| ); | |
| const deductUserAvailableBalance = await this.walletService.updateBalance( | |
| { | |
| $expr: { | |
| $gte: [ | |
| { | |
| $toDouble: '$availableBalance', | |
| }, | |
| chargedAmount.totalAmount, | |
| ], | |
| }, | |
| userId, | |
| currency: payerWalletProfile.currency, | |
| }, | |
| -NumberToDecimal128(chargedAmount.totalAmount), | |
| session, | |
| ); | |
| if (!deductUserAvailableBalance) { | |
| throw new BadRequestException({ | |
| statusCode: HttpStatus.BAD_REQUEST, | |
| message: 'Insufficient Balance', | |
| }); | |
| } | |
| const transactionRef = randomUUID(); | |
| const transactionPayload: Partial<Transaction> = { | |
| transactionRef, | |
| externalReference: `PAYRIT-${transactionRef}`, | |
| transactionType: TransactionType.CROSS_BORDER, | |
| initialAmount: NumberToDecimal128(chargedAmount.totalAmount), | |
| amount: NumberToDecimal128(chargedAmount.totalAmount - chargedAmount.charge), | |
| status: TransactionStatus.PENDING, | |
| walletId: payerWallet, | |
| payerId: payerWallet, | |
| charges: NumberToDecimal128(chargedAmount.charge), | |
| accountingType: AccountingType.DEBIT, | |
| balanceHistory: { | |
| initialBalance: payerWallet.availableBalance, | |
| currentBalance: deductUserAvailableBalance.availableBalance, | |
| }, | |
| metaData: { | |
| sourceCurrency: payload.from, | |
| targetCurrency: payload.to, | |
| }, | |
| description: `Payrit payment from ${payerUserInfo.firstName} ${payerUserInfo.lastName}`, | |
| category: await this.categoryModel.findOne({ slug: 'Transfers' }, '_id'), | |
| history: [ | |
| { | |
| status: TransactionStatus.PENDING, | |
| date: new Date().toISOString(), | |
| }, | |
| ], | |
| latitude: payload.latitude, | |
| longitude: payload.longitude, | |
| }; | |
| await Promise.all([ | |
| this.transactionService.create([transactionPayload], session), | |
| this.crossBorderRepo.create( | |
| { | |
| transactionRef, | |
| amount: NumberToDecimal128(payload.amount), | |
| sourceCurrency: payload.from, | |
| targetCurrency: payload.to, | |
| onRampProvider: paymentProviders.onRamp, | |
| offRampProvider: paymentProviders.offRamp, | |
| charges: NumberToDecimal128(chargedAmount.charge), | |
| amountReceived: null, | |
| usdcAmount: null, | |
| paymentChannel: payload.paymentChannel, | |
| recipientAccount: payload.paymentChannelInput, | |
| providerReceipts: [], | |
| }, | |
| session, | |
| ), | |
| ]); | |
| await session.commitTransaction(); | |
| // Send to a queue for handling | |
| this.handleCrossBorderTransactionByStatus(transactionRef); | |
| }, | |
| { readConcern: 'majority', writeConcern: { w: 'majority', j: true }, retryWrites: true }, | |
| ); | |
| } catch (error) { | |
| await session.abortTransaction(); | |
| this.logger.error(error); | |
| errorHandler(error); | |
| } finally { | |
| session.endSession(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| async verifyUserPin({ user, ...payload }: PinDto & Pick<TokenData, 'user'>) { | |
| let pin = await this.getCachedPin(user.toString()); | |
| if (!pin) { | |
| const { pin: existingPin = null } = | |
| (await this.pinModel.findOne({ user: user }).exec()) || {}; | |
| if (!existingPin) { | |
| return false; | |
| } | |
| pin = existingPin; | |
| await this.cachePin(user.toString(), pin); | |
| } | |
| await this.decryptPin(payload.pin, pin); | |
| return true; | |
| } | |
| async verifyPin({ user, ...payload }: PinDto & Pick<TokenData, 'user'>): Promise<IResponse> { | |
| try { | |
| const verified = await this.verifyUserPin({ user, pin: payload.pin }); | |
| if (!verified) { | |
| throw new BadRequestException({ | |
| statusCode: HttpStatus.BAD_REQUEST, | |
| message: 'User pin does not exist', | |
| }); | |
| } | |
| return { | |
| status: 'success', | |
| statusCode: HttpStatus.OK, | |
| message: 'Pin verified successfully', | |
| data: null, | |
| error: null, | |
| }; | |
| } catch (error) { | |
| this.logger.error(error); | |
| errorHandler(error); | |
| } | |
| } | |
| async verifyUser({ userId, pin, biometric }) { | |
| if (!pin) { | |
| return biometric === this.configService.get(BIOMETRICSKEY.BIOMETRIC_ENCRYPTION_KEY); | |
| } | |
| return this.verifyUserPin({ user: userId, pin }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment