diff --git a/app/controllers/api/apiController.js b/app/controllers/api/apiController.js index 10143bc..cba8adf 100644 --- a/app/controllers/api/apiController.js +++ b/app/controllers/api/apiController.js @@ -34,15 +34,12 @@ class APIController { static async getAllActiveApis(req, res) { try { const user = await BCA.findById(req.user.bcaId) - console.log(user) + console.log('LOGGEDUSER>>', user?.configurations?.apiCatalog?.allowedApis) const isUserAllowedToAccess = user?.configurations?.apiCatalog?.enabled const isUserHavingAccessToAllApis = user?.configurations?.apiCatalog?.all const allowedApis = user?.configurations?.apiCatalog?.allowedApis || [] console.log(allowedApis.length) - // Check if 'all' query param is set to 'true' - const fetchAll = req.query.all === 'true' - // Ensure limit is a number and between 1-100 let limit = parseInt(req.query.limit) || 10 limit = Math.min(Math.max(limit, 1), 100) @@ -57,7 +54,7 @@ class APIController { } // Build the query object - let query = {} + const query = {} // Add category filter if provided in query params if (req.query.category) { @@ -73,36 +70,30 @@ class APIController { 'No APIs are configured for access' ) } - query._id = { $in: allowedApis } + // Extract apiId values from the allowedApis array of objects + const apiIds = allowedApis.map((item) => item.apiId || item) // Handle both new format (object with apiId) and old format (direct ObjectId) + query._id = { $in: apiIds } } - if (fetchAll) { - // Fetch all matching APIs without pagination - const apis = await API.find(query).populate('vendor').lean() - - // Add id field to each API document - const apisWithId = apis.map((api) => ({ - ...api, - id: api._id - })) - - const totalDocs = apisWithId.length - const paginatedResult = { - docs: apisWithId, - totalDocs, - page: 1, - limit: totalDocs, - totalPages: 1, - nextPage: null, - prevPage: null, - hasNextPage: false, - hasPrevPage: false + // Create a map of apiId to price from allowedApis for quick lookup + const priceMap = new Map() + allowedApis.forEach((item) => { + const apiId = item.apiId || item + if (apiId && item.price !== undefined) { + priceMap.set(apiId.toString(), item.price) } - return ResponseHelper.success( - res, - paginatedResult, - 'All matching APIs retrieved successfully' - ) + }) + + // Helper function to overwrite price from allowedApis if available + const overwritePrice = (api) => { + const apiIdStr = api._id?.toString() + if (apiIdStr && priceMap.has(apiIdStr)) { + return { + ...api, + price: priceMap.get(apiIdStr) + } + } + return api } const options = { @@ -114,11 +105,16 @@ class APIController { const result = await API.paginate(query, options) + // Overwrite price from allowedApis if available + const docsWithOverwrittenPrice = result.docs.map((api) => + overwritePrice(api) + ) + const paginatedResult = { - docs: result.docs, + docs: docsWithOverwrittenPrice, totalDocs: result.totalDocs, page: result.page, - limit: limit, + limit, totalPages: result.totalPages, nextPage: result.hasNextPage ? result.nextPage : null, prevPage: result.hasPrevPage ? result.prevPage : null, diff --git a/app/controllers/apiProduct/aadhaarValidationController.js b/app/controllers/apiProduct/aadhaarValidationController.js index e88008d..61e2188 100644 --- a/app/controllers/apiProduct/aadhaarValidationController.js +++ b/app/controllers/apiProduct/aadhaarValidationController.js @@ -16,20 +16,12 @@ class AadhaarController { console.log('api details:', apiDetails) documentType = apiDetails.documentType - const { - statusCode, - apiResponse, - isSuccess, - responseMessage, - remark, - referenceId, - message, - messageCode - } = await APIService.processDocumentAndUpdateBalance({ - apiDetails, - documentData, - clientId - }) + const { statusCode, apiResponse, responseMessage } = + await APIService.processDocumentAndUpdateBalance({ + apiDetails, + documentData, + clientId + }) console.log('apiResponse controller:', apiResponse) diff --git a/app/models/BCA.js b/app/models/BCA.js index 3d768e9..3cbe43b 100644 --- a/app/models/BCA.js +++ b/app/models/BCA.js @@ -17,12 +17,20 @@ const apiCatalogSchema = new mongoose.Schema({ type: Boolean, default: false }, - all: { - type: Boolean, - default: false - }, allowedApis: { - type: [mongoose.Schema.Types.ObjectId], + type: [ + { + apiId: { + type: mongoose.Schema.Types.ObjectId, + required: true + }, + price: { + type: Number, + required: true, + min: 0 + } + } + ], default: [] } }) diff --git a/app/services/apiService.js b/app/services/apiService.js index 7c53b0a..dde7e3d 100644 --- a/app/services/apiService.js +++ b/app/services/apiService.js @@ -9,6 +9,7 @@ const API = require('../models/api') const APIError = require('../utils/error/apiError.js') const { STATUS_CODES } = require('../constants/statusCodes.js') const DocumentService = require('./documentService.js') +const BCA = require('../models/BCA.js') class APIService { static async processDocumentAndUpdateBalance({ @@ -28,18 +29,24 @@ class APIService { // Extract API details const apiId = apiDetails._id - const price = apiDetails.price const documentType = apiDetails.documentType const vendorId = apiDetails.vendorId._id const vendorName = apiDetails.vendorId.name + // Get configured price from BCA apiCatalog configuration + const configuredPrice = await this.getConfiguredPriceFromApiCatalog( + clientId, + apiId + ) + const price = configuredPrice + // Check if the user has sufficient balance (without deducting) // Skip this check if skipWalletDeduction is true let wallet = null if (!skipWalletDeduction) { wallet = await WalletService.getWalletAndCheckBalance(clientId, price) console.log( - `Wallet balance before API call: ${wallet.balance}, API price: ${price}` + `Wallet balance before API call: ${wallet.balance}, Configured price: ${price}` ) } else { // Just get the wallet info without checking balance @@ -95,7 +102,6 @@ class APIService { // API call failed - create failure transaction log without deducting wallet await this.logFailedTransaction({ clientId, - price, apiId, vendorId, documentData, @@ -125,21 +131,19 @@ class APIService { : TRANSACTION_STATUS_TYPES.FAILURE // API call requires deduction - proceed to deduct wallet and log transaction - const transactionLog = await this.deductWalletAndCreateSuccessTransaction( - { - clientId, - price, - apiId, - vendorId, - documentData, - statusCode, - apiResponse, - initiatedBy, - initiatedByRoleId, - remark, - transactionStatus - } - ) + await this.deductWalletAndCreateSuccessTransaction({ + clientId, + price, + apiId, + vendorId, + documentData, + statusCode, + apiResponse, + initiatedBy, + initiatedByRoleId, + remark, + transactionStatus + }) // Determine response message based on transaction status let responseMessage = `${documentType} verification successful` @@ -174,7 +178,6 @@ class APIService { static async logFailedTransaction({ clientId, - price, apiId, vendorId, documentData, @@ -184,12 +187,20 @@ class APIService { initiatedBy, initiatedByRoleId, remark, - afterBalance + afterBalance, + message, + messageCode, + session = null }) { console.log('Logging failed transaction') - const session = await mongoose.startSession() + const useExternalSession = session !== null + let transactionSession = session + try { - session.startTransaction() + if (!useExternalSession) { + transactionSession = await mongoose.startSession() + transactionSession.startTransaction() + } // If afterBalance wasn't provided, get current wallet balance if (afterBalance === undefined) { @@ -218,18 +229,24 @@ class APIService { messageCode, completedAt: Date.now() }, - session + transactionSession ) console.log('Failed transaction logged:', transaction.id) - await session.commitTransaction() + if (!useExternalSession) { + await transactionSession.commitTransaction() + } return transaction } catch (error) { console.error('Error logging failed transaction:', error) - await session.abortTransaction() + if (!useExternalSession && transactionSession) { + await transactionSession.abortTransaction() + } // We don't throw here as the original API error is more important } finally { - session.endSession() + if (!useExternalSession && transactionSession) { + transactionSession.endSession() + } } } @@ -330,6 +347,61 @@ class APIService { return api } + + /** + * Get configured price from BCA apiCatalog for a single API + * @param {String} clientId - BCA ID + * @param {String} apiId - API ID + * @returns {Number} - Configured price from apiCatalog + */ + static async getConfiguredPriceFromApiCatalog(clientId, apiId) { + const bca = await BCA.findById(clientId).select( + 'configurations.apiCatalog.allowedApis' + ) + + if (!bca) { + throw new APIError(STATUS_CODES.NOT_FOUND, 'BCA not found') + } + + const allowedApis = bca.configurations?.apiCatalog?.allowedApis || [] + + if (!allowedApis || allowedApis.length === 0) { + throw new APIError( + STATUS_CODES.FORBIDDEN, + 'No APIs configured in apiCatalog for this client' + ) + } + + // Find the configured price for this API + const apiIdStr = apiId.toString() + const apiConfig = allowedApis.find( + (item) => item.apiId.toString() === apiIdStr + ) + + if (!apiConfig) { + throw new APIError( + STATUS_CODES.FORBIDDEN, + `API ${apiIdStr} is not configured in apiCatalog for this client` + ) + } + + // If price is missing or 0, fetch from API collection + if ( + apiConfig.price === undefined || + apiConfig.price === null || + apiConfig.price === 0 + ) { + const api = await API.findById(apiId).select('price') + + if (!api) { + throw new APIError(STATUS_CODES.NOT_FOUND, 'API not found') + } + + return api.price + } + + return apiConfig.price + } } module.exports = APIService