diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js index a58d45240..169e27fd2 100644 --- a/server/intellipay/intellipay.js +++ b/server/intellipay/intellipay.js @@ -8,6 +8,7 @@ const { sendTaskEmail } = require("../email/sendemail"); const generateEmailTemplate = require("../email/generateTemplate"); const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager"); const { InstanceRegion, InstanceEndpoints } = require("../utils/instanceMgr"); +const { isEmpty, isNumber } = require("lodash"); const domain = process.env.NODE_ENV ? "secure" : "test"; @@ -57,7 +58,6 @@ const getShopCredentials = async (bodyshop) => { const decodeComment = (comment) => { try { return comment ? JSON.parse(Buffer.from(comment, "base64").toString()) : null; - // eslint-disable-next-line no-unused-vars } catch (error) { return null; // Handle malformed base64 string gracefully } @@ -85,17 +85,17 @@ const lightboxCredentials = async (req, res) => { const shopCredentials = await getShopCredentials(req.body.bodyshop); - if (shopCredentials.error) { + if (shopCredentials?.error) { logger.log("intellipay-credentials-error", "ERROR", req.user?.email, null, { message: shopCredentials.error?.message, ...logMeta }); - res.json({ + + return res.json({ message: shopCredentials.error?.message, type: "intellipay-credentials-error", ...logMeta }); - return; } try { @@ -116,13 +116,14 @@ const lightboxCredentials = async (req, res) => { ...logMeta }); - res.send(response.data); + return res.send(response.data); } catch (error) { logger.log("intellipay-lightbox-error", "ERROR", req.user?.email, null, { message: error?.message, ...logMeta }); - res.json({ + + return res.json({ message: error?.message, type: "intellipay-lightbox-error", ...logMeta @@ -154,18 +155,17 @@ const paymentRefund = async (req, res) => { const shopCredentials = await getShopCredentials(req.body.bodyshop); - if (shopCredentials.error) { + if (shopCredentials?.error) { logger.log("intellipay-refund-credentials-error", "ERROR", req.user?.email, null, { credentialsError: shopCredentials.error, ...logResponseMeta }); - res.status(400).json({ + return res.status(400).json({ credentialsError: shopCredentials.error, type: "intellipay-refund-credentials-error", ...logResponseMeta }); - return; } try { @@ -193,13 +193,14 @@ const paymentRefund = async (req, res) => { ...logResponseMeta }); - res.send(response.data); + return res.send(response.data); } catch (error) { logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, { message: error?.message, ...logResponseMeta }); - res.status(500).json({ + + return res.status(500).json({ message: error?.message, type: "intellipay-refund-error", ...logResponseMeta @@ -233,17 +234,17 @@ const generatePaymentUrl = async (req, res) => { const shopCredentials = await getShopCredentials(req.body.bodyshop); - if (shopCredentials.error) { + if (shopCredentials?.error) { logger.log("intellipay-generate-payment-url-credentials-error", "ERROR", req.user?.email, null, { message: shopCredentials.error?.message, ...logResponseMeta }); - res.status(400).json({ + + return res.status(400).json({ message: shopCredentials.error?.message, type: "intellipay-generate-payment-url-credentials-error", ...logResponseMeta }); - return; } try { @@ -274,13 +275,14 @@ const generatePaymentUrl = async (req, res) => { ...logResponseMeta }); - res.send(response.data); + return res.send(response.data); } catch (error) { logger.log("intellipay-generate-payment-url-error", "ERROR", req.user?.email, null, { message: error?.message, ...logResponseMeta }); - res.status(500).json({ message: error?.message, ...logResponseMeta }); + + return res.status(500).json({ message: error?.message, ...logResponseMeta }); } }; @@ -304,24 +306,24 @@ const checkFee = async (req, res) => { logger.log("intellipay-checkfee-request-received", "DEBUG", req.user?.email, null, logResponseMeta); - if (!req.body.amount || req.body.amount <= 0) { + if (!isNumber(req.body?.amount) || req.body?.amount <= 0) { logger.log("intellipay-checkfee-skip", "DEBUG", req.user?.email, null, { message: "Amount is zero or undefined, skipping fee check.", ...logResponseMeta }); - res.json({ fee: 0 }); - return; + + return res.json({ fee: 0 }); } const shopCredentials = await getShopCredentials(req.body.bodyshop); - if (shopCredentials.error) { + if (shopCredentials?.error) { logger.log("intellipay-checkfee-credentials-error", "ERROR", req.user?.email, null, { message: shopCredentials.error?.message, ...logResponseMeta }); - res.status(400).json({ error: shopCredentials.error?.message, ...logResponseMeta }); - return; + + return res.status(400).json({ error: shopCredentials.error?.message, ...logResponseMeta }); } try { @@ -357,34 +359,40 @@ const checkFee = async (req, res) => { message: response.data?.error, ...logResponseMeta }); - res.status(400).json({ + + return res.status(400).json({ error: response.data?.error, type: "intellipay-checkfee-api-error", ...logResponseMeta }); - } else if (response.data < 0) { + } + + if (response.data < 0) { logger.log("intellipay-checkfee-negative-fee", "ERROR", req.user?.email, null, { message: "Fee amount returned is negative.", ...logResponseMeta }); - res.json({ + + return res.json({ error: "Fee amount negative. Check API credentials & account configuration.", ...logResponseMeta, type: "intellipay-checkfee-negative-fee" }); - } else { - logger.log("intellipay-checkfee-success", "DEBUG", req.user?.email, null, { - fee: response.data, - ...logResponseMeta - }); - res.json({ fee: response.data, ...logResponseMeta }); } + + logger.log("intellipay-checkfee-success", "DEBUG", req.user?.email, null, { + fee: response.data, + ...logResponseMeta + }); + + return res.json({ fee: response.data, ...logResponseMeta }); } catch (error) { logger.log("intellipay-checkfee-error", "ERROR", req.user?.email, null, { message: error?.message, ...logResponseMeta }); - res.status(500).json({ error: error?.message, logResponseMeta }); + + return res.status(500).json({ error: error?.message, logResponseMeta }); } }; @@ -406,19 +414,15 @@ const postBack = async (req, res) => { logger.log("intellipay-postback-received", "DEBUG", "api", null, logResponseMeta); try { - if ((!values.invoice || values.invoice === "") && !decodedComment) { - //invoice is specified through the pay link. Comment by IO. + if (isEmpty(values?.invoice) && !decodedComment) { logger.log("intellipay-postback-ignored", "DEBUG", "api", null, { message: "No invoice or comment provided", ...logResponseMeta }); - res.sendStatus(200); - return; + return res.sendStatus(200); } if (decodedComment) { - //Shifted the order to have this first to retain backwards compatibility for the old style of short link. - //This has been triggered by IO and may have multiple jobs. const parsedComment = decodedComment; logger.log("intellipay-postback-parsed-comment", "DEBUG", "api", null, { @@ -426,18 +430,16 @@ const postBack = async (req, res) => { ...logResponseMeta }); - //Adding in the user email to the short pay email. - //Need to check this to ensure backwards compatibility for clients that don't update. - const partialPayments = Array.isArray(parsedComment) ? parsedComment : parsedComment.payments; - // Fetch jobs by job IDs const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, { ids: partialPayments.map((p) => p.jobid) }); + const bodyshop = await gqlClient.request(queries.GET_BODYSHOP_BY_ID, { id: jobs.jobs[0].shopid }); + const ipMapping = bodyshop.bodyshops_by_pk.intellipay_config?.payment_map; logger.log("intellipay-postback-jobs-fetched", "DEBUG", "api", null, { @@ -446,7 +448,6 @@ const postBack = async (req, res) => { ...logResponseMeta }); - // Insert new payments const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, { paymentInput: partialPayments.map((p) => ({ amount: p.amount, @@ -476,7 +477,7 @@ const postBack = async (req, res) => { ...logResponseMeta }); - if (values.origin === "OneLink" && parsedComment.userEmail) { + if (values?.origin === "OneLink" && parsedComment?.userEmail) { sendTaskEmail({ to: parsedComment.userEmail, subject: `New Payment(s) Received - RO ${jobs.jobs.map((j) => j.ro_number).join(", ")}`, @@ -500,8 +501,11 @@ const postBack = async (req, res) => { }); }); } - res.sendStatus(200); - } else if (values.invoice) { + + return res.sendStatus(200); + } + + if (values?.invoice) { const job = await gqlClient.request(queries.GET_JOB_BY_PK, { id: values.invoice }); @@ -551,14 +555,24 @@ const postBack = async (req, res) => { responseResults, ...logResponseMeta }); - res.sendStatus(200); + + return res.sendStatus(200); } + + // Default case: no valid conditions met + logger.log("intellipay-postback-invalid", "WARN", "api", null, { + message: "No valid invoice or comment provided", + ...logResponseMeta + }); + + return res.status(400).send("Bad Request: No valid invoice or comment provided"); } catch (error) { logger.log("intellipay-postback-error", "ERROR", "api", null, { message: error?.message, ...logResponseMeta }); - res.status(400).json({ successful: false, error: error.message, ...logResponseMeta }); + + return res.status(400).json({ successful: false, error: error.message, ...logResponseMeta }); } };