Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
fix: api price edge case
  • Loading branch information
Shivank2200 committed Nov 13, 2025
commit f0eb5becbd9ebdb3526926a78984d98802e1911f
64 changes: 30 additions & 34 deletions app/controllers/api/apiController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -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 = {
Expand All @@ -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,
Expand Down
20 changes: 6 additions & 14 deletions app/controllers/apiProduct/aadhaarValidationController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
124 changes: 98 additions & 26 deletions app/services/apiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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
Expand Down Expand Up @@ -95,7 +102,6 @@ class APIService {
// API call failed - create failure transaction log without deducting wallet
await this.logFailedTransaction({
clientId,
price,
apiId,
vendorId,
documentData,
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -174,7 +178,6 @@ class APIService {

static async logFailedTransaction({
clientId,
price,
apiId,
vendorId,
documentData,
Expand All @@ -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) {
Expand Down Expand Up @@ -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()
}
}
}

Expand Down Expand Up @@ -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