diff --git a/client/src/components/card-payment-modal/card-payment-modal.component..jsx b/client/src/components/card-payment-modal/card-payment-modal.component..jsx
index 3c3b397c1..8e19c2a61 100644
--- a/client/src/components/card-payment-modal/card-payment-modal.component..jsx
+++ b/client/src/components/card-payment-modal/card-payment-modal.component..jsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React, { useCallback, useEffect } from "react";
import axios from "axios";
import { useTranslation } from "react-i18next";
import { Button, Card, Form, Input, InputNumber, Row, Select } from "antd";
@@ -41,13 +41,35 @@ const CardPaymentModalComponent = ({
variables: { jobid: context?.jobid ?? "" },
});
- useEffect(() => {
- axios.get("/intellipay/lightbox_credentials").then((response) => {
- var rg = document.createRange();
- let node = rg.createContextualFragment(response.data);
+ const nonApproval = useCallback(
+ async (response) => {
+ // Mutate unsuccessful payment
+ await insertPaymentResponse({
+ variables: {
+ paymentResponse: {
+ amount: response.amount,
+ bodyshopid: bodyshop.id,
+ jobid: jobid || context.jobid,
+ declinereason: response.declinereason,
+ ext_paymentid: response.paymentid.toString(),
+ successful: false,
+ response,
+ },
+ },
+ });
- document.documentElement.appendChild(node);
- window.intellipay.initialize();
+ // Insert failed payment to audit trail
+ insertAuditTrail({
+ jobid: jobid || context?.jobid,
+ operation: AuditTrailMapping.failedpayment(),
+ });
+ },
+ [bodyshop, context, insertAuditTrail, insertPaymentResponse, jobid]
+ );
+
+ const initIntellipayFunctions = useCallback(() => {
+ if (window.intellipay !== undefined && typeof jobid !== "undefined") {
+ console.log("intellipay init functions");
window.intellipay.runOnClose(() => {
window.intellipay.initialize();
@@ -60,35 +82,32 @@ const CardPaymentModalComponent = ({
toggleModalVisible();
});
- window.intellipay.runOnNonApproval(async function (response) {
- // Mutate unsuccessful payment
- await insertPaymentResponse({
- variables: {
- paymentResponse: {
- amount: response.amount,
- bodyshopid: bodyshop.id,
- jobid: jobid || context.jobid,
- declinereason: response.declinereason,
- ext_paymentid: response.paymentid.toString(),
- successful: false,
- response,
- },
- },
- });
-
- // Insert failed payment to audit trail
- insertAuditTrail({
- jobid: jobid || context?.jobid,
- operation: AuditTrailMapping.failedpayment(),
- });
- });
- });
+ window.intellipay.runOnNonApproval(nonApproval);
+ }
+ }, [form, jobid, nonApproval, toggleModalVisible]);
+ const initJobId = useCallback(() => {
if (context?.jobid) {
form.setFieldValue("jobid", context.jobid);
}
form.setFieldValue("payer", t("payments.labels.customer"));
+ }, [context?.jobid, form, t]);
+
+ useEffect(() => {
+ initJobId();
+
+ axios
+ .post("/intellipay/lightbox_credentials", { bodyshop })
+ .then((response) => {
+ var rg = document.createRange();
+ let node = rg.createContextualFragment(response.data);
+
+ document.documentElement.appendChild(node);
+ window.intellipay.initialize();
+
+ initIntellipayFunctions();
+ });
function handleEvents(...props) {
const operation = props[0].data.operation;
@@ -101,7 +120,7 @@ const CardPaymentModalComponent = ({
window.addEventListener("message", handleEvents, false);
return () => window.removeEventListener("message", handleEvents, false);
- }, []);
+ }, [bodyshop, initJobId, initIntellipayFunctions]);
const handleFinish = async (values) => {
const paymentResult = await insertPayment({
diff --git a/client/src/components/job-payments/job-payments.component.jsx b/client/src/components/job-payments/job-payments.component.jsx
index 196ef0baa..ad56ebc61 100644
--- a/client/src/components/job-payments/job-payments.component.jsx
+++ b/client/src/components/job-payments/job-payments.component.jsx
@@ -174,12 +174,15 @@ export function JobPayments({
const response = await axios.post(
"/intellipay/generate_payment_url",
{
+ bodyshop,
amount: balance.getAmount(),
account: job.ro_number,
}
);
setGeneratingtURL(false);
+ console.log("SMS", response);
+
openChatByPhone({
phone_num: p.formatInternational(),
jobid: job.id,
@@ -236,7 +239,7 @@ export function JobPayments({
}}
expandable={{
expandedRowRender: (record) => (
-
+
),
}}
summary={() => (
diff --git a/client/src/components/payment-expanded-row/payment-expanded-row.component.jsx b/client/src/components/payment-expanded-row/payment-expanded-row.component.jsx
index da3e79e20..6266457a7 100644
--- a/client/src/components/payment-expanded-row/payment-expanded-row.component.jsx
+++ b/client/src/components/payment-expanded-row/payment-expanded-row.component.jsx
@@ -20,7 +20,7 @@ const openNotificationWithIcon = (type, t) => {
});
};
-const PaymentExpandedRowComponent = ({ record }) => {
+const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
const [refundAmount, setRefundAmount] = useState(0);
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
@@ -96,6 +96,7 @@ const PaymentExpandedRowComponent = ({ record }) => {
"The payment will be refunded. Click OK to confirm and Cancel to dismiss.",
async onOk() {
const refundResponse = await axios.post("/intellipay/payment_refund", {
+ bodyshop,
amount: refundAmount,
paymentid: payment_response.ext_paymentid,
});
diff --git a/server.js b/server.js
index 78c140670..5067be835 100644
--- a/server.js
+++ b/server.js
@@ -233,7 +233,7 @@ app.post(
);
var intellipay = require("./server/intellipay/intellipay");
-app.get(
+app.post(
"/intellipay/lightbox_credentials",
fb.validateFirebaseIdToken,
intellipay.lightbox_credentials
diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js
index 650d6d76a..63a0b8f91 100644
--- a/server/graphql-client/queries.js
+++ b/server/graphql-client/queries.js
@@ -12,6 +12,39 @@ query FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID(
}
`;
+exports.GET_JOB_BY_RO_NUMBER = `
+ query GET_JOB_BY_RO_NUMBER($ro_number: String!) {
+ jobs(where:{ro_number:{_eq:$ro_number}}) {
+ id
+ bodyshop {
+ id
+ }
+ }
+ }
+`;
+
+exports.INSERT_NEW_PAYMENT = `
+ mutation INSERT_NEW_PAYMENT($paymentInput: [payments_insert_input!]!) {
+ insert_payments(objects: $paymentInput) {
+ returning {
+ id
+ }
+ }
+ }
+`;
+
+exports.INSERT_PAYMENT_RESPONSE = `
+ mutation INSERT_PAYMENT_RESPONSE(
+ $paymentResponse: [payment_response_insert_input!]!
+ ) {
+ insert_payment_response(objects: $paymentResponse) {
+ returning {
+ id
+ }
+ }
+ }
+`;
+
exports.UNARCHIVE_CONVERSATION = `
mutation UNARCHIVE_CONVERSATION($id: uuid!) {
update_conversations_by_pk(pk_columns: {id: $id}, _set: {archived: false}) {
diff --git a/server/intellipay/aws-secrets-manager.js b/server/intellipay/aws-secrets-manager.js
new file mode 100644
index 000000000..598fc9090
--- /dev/null
+++ b/server/intellipay/aws-secrets-manager.js
@@ -0,0 +1,46 @@
+"use strict";
+
+const AWS = require("aws-sdk");
+
+class SecretsManager {
+ /**
+ * Uses AWS Secrets Manager to retrieve a secret
+ */
+ static async getSecret(secretName, region) {
+ const config = { region: region };
+ let secretsManager = new AWS.SecretsManager(config);
+ try {
+ let secretValue = await secretsManager
+ .getSecretValue({ SecretId: secretName })
+ .promise();
+ if ("SecretString" in secretValue) {
+ return secretValue.SecretString;
+ } else {
+ let buff = new Buffer(secretValue.SecretBinary, "base64");
+ return buff.toString("ascii");
+ }
+ } catch (err) {
+ if (err.code === "DecryptionFailureException")
+ // Secrets Manager can't decrypt the protected secret text using the provided KMS key.
+ // Deal with the exception here, and/or rethrow at your discretion.
+ throw err;
+ else if (err.code === "InternalServiceErrorException")
+ // An error occurred on the server side.
+ // Deal with the exception here, and/or rethrow at your discretion.
+ throw err;
+ else if (err.code === "InvalidParameterException")
+ // You provided an invalid value for a parameter.
+ // Deal with the exception here, and/or rethrow at your discretion.
+ throw err;
+ else if (err.code === "InvalidRequestException")
+ // You provided a parameter value that is not valid for the current state of the resource.
+ // Deal with the exception here, and/or rethrow at your discretion.
+ throw err;
+ else if (err.code === "ResourceNotFoundException")
+ // We can't find the resource that you asked for.
+ // Deal with the exception here, and/or rethrow at your discretion.
+ throw err;
+ }
+ }
+}
+module.exports = SecretsManager;
diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js
index 97d9771af..367303926 100644
--- a/server/intellipay/intellipay.js
+++ b/server/intellipay/intellipay.js
@@ -12,23 +12,32 @@ require("dotenv").config({
});
const domain = process.env.NODE_ENV ? "secure" : "test";
+const SecretsManager = require("./aws-secrets-manager");
-const getShopCredentials = () => {
- // add parametes for the request
- // TODO: Implement retrieval logic later.
+const gqlClient = require("../graphql-client/graphql-client").client;
- return {
- merchantkey: "3B8068", //This should be dynamic
- apikey: "Oepn2B.XqRgzAqHqvOOmYUxD2VW.vGSipi", //This should be dynamic
- };
+const getShopCredentials = async (bodyshop) => {
+ // Development only
+ if (process.env.NODE_ENV === undefined) {
+ return {
+ merchantkey: process.env.DEV_INTELLIPAY_MERCHANTKEY,
+ apikey: process.env.DEV_INTELLIPAY_APIKEY,
+ };
+ }
+
+ // Production code
+ if (bodyshop?.imexshopid) {
+ const secret = await SecretsManager.getSecret(
+ `intellipay-credentials-${bodyshop.imexshopid}`,
+ process.env.REGION
+ );
+
+ return JSON.parse(secret);
+ }
};
exports.lightbox_credentials = async (req, res) => {
- //req.user contains firebase decoded credentials
- // can add bodyshopid to req.body
- //Server side query to get API credentials for that shop and generatae link
-
- const shopCredentials = getShopCredentials();
+ const shopCredentials = await getShopCredentials(req.body.bodyshop);
try {
const options = {
@@ -55,7 +64,7 @@ exports.lightbox_credentials = async (req, res) => {
};
exports.payment_refund = async (req, res) => {
- const shopCredentials = getShopCredentials();
+ const shopCredentials = await getShopCredentials(req.body.bodyshop);
try {
const options = {
@@ -81,8 +90,7 @@ exports.payment_refund = async (req, res) => {
};
exports.generate_payment_url = async (req, res) => {
- const shopCredentials = getShopCredentials();
-
+ const shopCredentials = await getShopCredentials(req.body.bodyshop);
try {
const options = {
method: "POST",
@@ -108,5 +116,69 @@ exports.generate_payment_url = async (req, res) => {
exports.postback = async (req, res) => {
console.log("postback as", req.body);
- res.send({ message: "postback" });
+ const { body: values } = req;
+
+ // TODO query job by account name
+ const job = await gqlClient.request(queries.GET_JOB_BY_RO_NUMBER, {
+ ro_number: values.account,
+ });
+ // TODO add mutation to database
+
+ const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
+ paymentInput: {
+ amount: values.total,
+ transactionid: `C00 ${values.authcode}`,
+ payer: "Customer",
+ type: values.cardtype,
+ jobid: job.jobs[0].id,
+ date: moment(Date.now()),
+ },
+ });
+
+ await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
+ paymentResponse: {
+ amount: values.total,
+ bodyshopid: job.jobs[0].bodyshop.id,
+ paymentid: paymentResult.id,
+ jobid: job.jobs[0].id,
+ declinereason: "Approved",
+ ext_paymentid: values.paymentid,
+ successful: true,
+ response: values,
+ },
+ });
+
+ res.send({ message: "Postback Successful" });
};
+
+`{
+ ipaddress: '136.158.34.242',
+ firstname: 'JC',
+ notes: '',
+ city: '',
+ fee: ' 0.00',
+ origin: 'OneLink',
+ total: '5061.36',
+ avsdata: 'N',
+ arglist: '""',
+ state: ' ',
+ cardtype: 'Visa',
+ department: '',
+ email: '',
+ timestamp: "{ts '2023-03-23 09:52:23'}",
+ op: 'Kh6Pa6AT9keg',
+ amount: '5061.36',
+ method: 'CARD',
+ address2: '',
+ address1: '',
+ lastname: 'Tolentino',
+ zipcode: '1742 ',
+ authcode: '367885',
+ phone: '',
+ merchantid: '7114',
+ paymentid: '24205435',
+ customerid: '19610104',
+ comment: '',
+ invoice: '',
+ account: 'QBD241'
+}`;