feature/IO-2885-IntelliPay-App-Postback
- Finish ticket
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "intellipay_merchant_id" text
|
||||||
|
-- null unique;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "intellipay_merchant_id" text
|
||||||
|
null unique;
|
||||||
@@ -2832,3 +2832,15 @@ exports.GET_DOCUMENTS_BY_IDS = `
|
|||||||
takenat
|
takenat
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
exports.GET_JOBID_BY_MERCHANTID_RONUMBER = `
|
||||||
|
query GET_JOBID_BY_MERCHANTID_RONUMBER($merchantID: String!, $roNumber: String!) {
|
||||||
|
jobs(where: {ro_number: {_eq: $roNumber}, bodyshop: {intellipay_merchant_id: {_eq: $merchantID}}}) {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
bodyshop {
|
||||||
|
id
|
||||||
|
intellipay_config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|||||||
@@ -18,21 +18,52 @@ const client = new SecretsManagerClient({
|
|||||||
|
|
||||||
const gqlClient = require("../graphql-client/graphql-client").client;
|
const gqlClient = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a properly formatted Cpteller API URL
|
||||||
|
* @param {Object} options - URL configuration options
|
||||||
|
* @param {string} options.apiType - 'webapi' or 'custapi'
|
||||||
|
* @param {string} [options.version] - API version (e.g., '26' for webapi)
|
||||||
|
* @param {Object} [options.params] - URL query parameters
|
||||||
|
* @returns {string} - The formatted Cpteller URL
|
||||||
|
*/
|
||||||
|
const getCptellerUrl = (options) => {
|
||||||
|
const { apiType = "webapi", version, params = {} } = options;
|
||||||
|
|
||||||
|
// Base URL construction
|
||||||
|
let url = `https://${domain}.cpteller.com/api/`;
|
||||||
|
|
||||||
|
// Add version if specified for webapi
|
||||||
|
if (apiType === "webapi" && version) {
|
||||||
|
url += `${version}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the API endpoint
|
||||||
|
url += `${apiType}.cfc`;
|
||||||
|
|
||||||
|
// Add query parameters if any exist
|
||||||
|
const queryParams = new URLSearchParams(params).toString();
|
||||||
|
if (queryParams) {
|
||||||
|
url += `?${queryParams}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Get shop credentials from AWS Secrets Manager
|
* @description Get shop credentials from AWS Secrets Manager
|
||||||
* @param bodyshop
|
* @param bodyshop
|
||||||
* @returns {Promise<{error}|{merchantkey: *, apikey: *}|any>}
|
* @returns {Promise<{error}|{merchantkey: *, apikey: *}|any>}
|
||||||
*/
|
*/
|
||||||
const getShopCredentials = async (bodyshop) => {
|
const getShopCredentials = async (bodyshop) => {
|
||||||
// Development only
|
// In Dev/Testing we will use the environment variables
|
||||||
if (process.env.NODE_ENV === undefined) {
|
if (process.env?.NODE_ENV !== "production") {
|
||||||
return {
|
return {
|
||||||
merchantkey: process.env.INTELLIPAY_MERCHANTKEY,
|
merchantkey: process.env.INTELLIPAY_MERCHANTKEY,
|
||||||
apikey: process.env.INTELLIPAY_APIKEY
|
apikey: process.env.INTELLIPAY_APIKEY
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Production code
|
// In Production we will use the AWS Secrets Manager
|
||||||
if (bodyshop?.imexshopid) {
|
if (bodyshop?.imexshopid) {
|
||||||
try {
|
try {
|
||||||
const secret = await client.send(
|
const secret = await client.send(
|
||||||
@@ -106,7 +137,10 @@ const lightboxCredentials = async (req, res) => {
|
|||||||
...shopCredentials,
|
...shopCredentials,
|
||||||
operatingenv: "businessattended"
|
operatingenv: "businessattended"
|
||||||
}),
|
}),
|
||||||
url: `https://${domain}.cpteller.com/api/custapi.cfc?method=autoterminal${req.body.refresh ? "_refresh" : ""}` //autoterminal_refresh
|
url: getCptellerUrl({
|
||||||
|
apiType: "custapi",
|
||||||
|
params: { method: `autoterminal${req.body.refresh ? "_refresh" : ""}` }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios(options);
|
const response = await axios(options);
|
||||||
@@ -178,7 +212,11 @@ const paymentRefund = async (req, res) => {
|
|||||||
paymentid: req.body.paymentid,
|
paymentid: req.body.paymentid,
|
||||||
amount: req.body.amount
|
amount: req.body.amount
|
||||||
}),
|
}),
|
||||||
url: `https://${domain}.cpteller.com/api/26/webapi.cfc?method=payment_refund`
|
url: getCptellerUrl({
|
||||||
|
apiType: "webapi",
|
||||||
|
version: "26",
|
||||||
|
params: { method: "payment_refund" }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.log("intellipay-refund-options-prepared", "DEBUG", req.user?.email, null, {
|
logger.log("intellipay-refund-options-prepared", "DEBUG", req.user?.email, null, {
|
||||||
@@ -259,7 +297,10 @@ const generatePaymentUrl = async (req, res) => {
|
|||||||
invoice: req.body.invoice,
|
invoice: req.body.invoice,
|
||||||
createshorturl: true
|
createshorturl: true
|
||||||
}),
|
}),
|
||||||
url: `https://${domain}.cpteller.com/api/custapi.cfc?method=generate_lightbox_url`
|
url: getCptellerUrl({
|
||||||
|
apiType: "custapi",
|
||||||
|
params: { method: "generate_lightbox_url" }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.log("intellipay-generate-payment-url-options-prepared", "DEBUG", req.user?.email, null, {
|
logger.log("intellipay-generate-payment-url-options-prepared", "DEBUG", req.user?.email, null, {
|
||||||
@@ -344,7 +385,7 @@ const checkFee = async (req, res) => {
|
|||||||
},
|
},
|
||||||
{ sort: false } // Ensure query string order is preserved
|
{ sort: false } // Ensure query string order is preserved
|
||||||
),
|
),
|
||||||
url: `https://${domain}.cpteller.com/api/26/webapi.cfc`
|
url: getCptellerUrl({ apiType: "webapi", version: "26" })
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.log("intellipay-checkfee-options-prepared", "DEBUG", req.user?.email, null, {
|
logger.log("intellipay-checkfee-options-prepared", "DEBUG", req.user?.email, null, {
|
||||||
@@ -506,19 +547,49 @@ const postBack = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (values?.invoice) {
|
if (values?.invoice) {
|
||||||
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
// Early Bail on Merchant ID
|
||||||
id: values.invoice
|
if (!values?.merchantid) {
|
||||||
|
logger.log("intellipay-postback-no-merchantid", "ERROR", "api", null, {
|
||||||
|
message: "Merchant ID is missing",
|
||||||
|
...logResponseMeta
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(400).send("Bad Request: Merchant ID is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await gqlClient.request(queries.GET_JOBID_BY_MERCHANTID_RONUMBER, {
|
||||||
|
merchantID: values.merchantid,
|
||||||
|
roNumber: values.invoice
|
||||||
});
|
});
|
||||||
|
|
||||||
const bodyshop = await gqlClient.request(queries.GET_BODYSHOP_BY_ID, {
|
// Early Bail on No Jobs Found
|
||||||
id: job.jobs_by_pk.shopid
|
if (!result?.jobs?.length) {
|
||||||
});
|
logger.log("intellipay-postback-job-not-found", "ERROR", "api", null, {
|
||||||
|
message: "Job not found",
|
||||||
|
...logResponseMeta
|
||||||
|
});
|
||||||
|
|
||||||
const ipMapping = bodyshop.bodyshops_by_pk.intellipay_config?.payment_map;
|
return res.status(400).send("Bad Request: Job not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const job = result?.jobs?.[0];
|
||||||
|
|
||||||
|
const bodyshop = job?.bodyshop;
|
||||||
|
|
||||||
|
// Early Bail on no Bodyshop Found
|
||||||
|
if (!bodyshop) {
|
||||||
|
logger.log("intellipay-postback-bodyshop-not-found", "ERROR", "api", null, {
|
||||||
|
message: "Bodyshop not found",
|
||||||
|
...logResponseMeta
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(400).send("Bad Request: Bodyshop not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipMapping = bodyshop?.intellipay_config?.payment_map;
|
||||||
|
|
||||||
logger.log("intellipay-postback-invoice-job-fetched", "DEBUG", "api", null, {
|
logger.log("intellipay-postback-invoice-job-fetched", "DEBUG", "api", null, {
|
||||||
job,
|
job,
|
||||||
bodyshop,
|
|
||||||
...logResponseMeta
|
...logResponseMeta
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -528,7 +599,7 @@ const postBack = async (req, res) => {
|
|||||||
transactionid: values.authcode,
|
transactionid: values.authcode,
|
||||||
payer: "Customer",
|
payer: "Customer",
|
||||||
type: ipMapping ? ipMapping[(values.cardtype || "").toLowerCase()] || values.cardtype : values.cardtype,
|
type: ipMapping ? ipMapping[(values.cardtype || "").toLowerCase()] || values.cardtype : values.cardtype,
|
||||||
jobid: values.invoice,
|
jobid: job.id,
|
||||||
date: moment(Date.now())
|
date: moment(Date.now())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -541,9 +612,9 @@ const postBack = async (req, res) => {
|
|||||||
const responseResults = await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
|
const responseResults = await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
|
||||||
paymentResponse: {
|
paymentResponse: {
|
||||||
amount: values.total,
|
amount: values.total,
|
||||||
bodyshopid: bodyshop.bodyshops_by_pk.id,
|
bodyshopid: bodyshop.id,
|
||||||
paymentid: paymentResult.id,
|
paymentid: paymentResult.id,
|
||||||
jobid: values.invoice,
|
jobid: job.id,
|
||||||
declinereason: "Approved",
|
declinereason: "Approved",
|
||||||
ext_paymentid: values.paymentid,
|
ext_paymentid: values.paymentid,
|
||||||
successful: true,
|
successful: true,
|
||||||
@@ -576,13 +647,10 @@ const postBack = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const postBackCallBack = async (req, res) => {};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
lightboxCredentials,
|
lightboxCredentials,
|
||||||
paymentRefund,
|
paymentRefund,
|
||||||
generatePaymentUrl,
|
generatePaymentUrl,
|
||||||
checkFee,
|
checkFee,
|
||||||
postBack,
|
postBack
|
||||||
postBackCallBack
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ const {
|
|||||||
paymentRefund,
|
paymentRefund,
|
||||||
generatePaymentUrl,
|
generatePaymentUrl,
|
||||||
postBack,
|
postBack,
|
||||||
checkFee,
|
checkFee
|
||||||
postBackCallBack
|
|
||||||
} = require("../intellipay/intellipay");
|
} = require("../intellipay/intellipay");
|
||||||
|
|
||||||
router.post("/lightbox_credentials", validateFirebaseIdTokenMiddleware, lightboxCredentials);
|
router.post("/lightbox_credentials", validateFirebaseIdTokenMiddleware, lightboxCredentials);
|
||||||
@@ -15,6 +14,5 @@ router.post("/payment_refund", validateFirebaseIdTokenMiddleware, paymentRefund)
|
|||||||
router.post("/generate_payment_url", validateFirebaseIdTokenMiddleware, generatePaymentUrl);
|
router.post("/generate_payment_url", validateFirebaseIdTokenMiddleware, generatePaymentUrl);
|
||||||
router.post("/checkfee", validateFirebaseIdTokenMiddleware, checkFee);
|
router.post("/checkfee", validateFirebaseIdTokenMiddleware, checkFee);
|
||||||
router.post("/postback", postBack);
|
router.post("/postback", postBack);
|
||||||
router.post("/postback-callback", postBackCallBack);
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user