From c8ee9ca5a72cc9183eb25e4a9e0d24d71d7045d4 Mon Sep 17 00:00:00 2001 From: swtmply Date: Wed, 1 Mar 2023 00:31:44 +0800 Subject: [PATCH 01/26] initial intellipay implementation Co-authored-by: Patrick Fic --- .../_test/card-payment-modal.component..jsx | 43 +++++ .../src/components/_test/intellipay-test.jsx | 161 ++++++++++++++++++ .../components/_test/payment_response.json | 63 +++++++ .../_test/payment_response.queries.js | 29 ++++ .../src/components/_test/test.component.jsx | 9 - client/src/components/_test/test.page.jsx | 35 ++++ .../payment-modal/payment-modal.container.jsx | 4 +- .../pages/manage/manage.page.component.jsx | 7 +- client/src/redux/modals/modals.reducer.js | 1 + client/src/redux/modals/modals.selectors.js | 5 + server.js | 7 + server/intellipay/intellipay.js | 36 ++++ 12 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 client/src/components/_test/card-payment-modal.component..jsx create mode 100644 client/src/components/_test/intellipay-test.jsx create mode 100644 client/src/components/_test/payment_response.json create mode 100644 client/src/components/_test/payment_response.queries.js delete mode 100644 client/src/components/_test/test.component.jsx create mode 100644 client/src/components/_test/test.page.jsx create mode 100644 server/intellipay/intellipay.js diff --git a/client/src/components/_test/card-payment-modal.component..jsx b/client/src/components/_test/card-payment-modal.component..jsx new file mode 100644 index 000000000..5f0d9617b --- /dev/null +++ b/client/src/components/_test/card-payment-modal.component..jsx @@ -0,0 +1,43 @@ +import { Modal } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { toggleModalVisible } from "../../redux/modals/modals.actions"; +import { selectCardPayment } from "../../redux/modals/modals.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import IntellipayTestPage from "./intellipay-test"; + +const mapStateToProps = createStructuredSelector({ + cardPaymentModal: selectCardPayment, + bodyshop: selectBodyshop, +}); + +const mapDispatchToProps = (dispatch) => ({ + toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")), +}); + +function CardPaymentModalComponent({ + cardPaymentModal, + toggleModalVisible, + bodyshop, +}) { + const { context, visible } = cardPaymentModal; + + return ( + toggleModalVisible()} + onCancel={() => toggleModalVisible()} + cancelButtonProps={{ style: { display: "none" } }} + width="90%" + destroyOnClose + > + + + ); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(CardPaymentModalComponent); diff --git a/client/src/components/_test/intellipay-test.jsx b/client/src/components/_test/intellipay-test.jsx new file mode 100644 index 000000000..eb06d7651 --- /dev/null +++ b/client/src/components/_test/intellipay-test.jsx @@ -0,0 +1,161 @@ +import React, { useEffect } from "react"; +import axios from "axios"; +import { useTranslation } from "react-i18next"; +import { Button, Card, Form, Input, InputNumber, Select } from "antd"; +import moment from "moment"; +import { useMutation } from "@apollo/client"; +import LayoutFormRow from "../layout-form-row/layout-form-row.component"; +import JobSearchSelectComponent from "../job-search-select/job-search-select.component"; +import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries"; +import { INSERT_PAYMENT_RESPONSE } from "./payment_response.queries"; + +const IntellpayTestPage = ({ bodyshop, context }) => { + const [form] = Form.useForm(); + const amount = Form.useWatch("amount", form); + const [insertPayment] = useMutation(INSERT_NEW_PAYMENT); + const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE); + const { t } = useTranslation(); + + useEffect(() => { + axios.get("/intellipay/lightbox_credentials").then((response) => { + var rg = document.createRange(); + let node = rg.createContextualFragment(response.data); + + document.documentElement.appendChild(node); + window.intellipay.initialize(); + + console.log("Modal: Intellipay Initialized"); + + window.intellipay.runOnClose(() => { + // Apparently it automatically removes itself in the document when closed + // so we just need to reintialize it + window.intellipay.initialize(); + }); + + window.intellipay.runOnApproval(async function (response) { + console.log("Modal - Payment Response: ", response); + // TODO store the response + form.setFieldValue("paymentResponse", response); + }); + }); + }, []); + + const disabled = false; + + const handleFinish = async (values) => { + console.log("Modal: Handle Finish Values", values); + + const paymentResult = await insertPayment({ + variables: { + paymentInput: { + amount: values.amount, + transactionid: values.paymentResponse.receiptelements.transid, + payer: values.payer, + type: values.paymentResponse.cardType, + jobid: values.jobid, + date: moment(Date.now()), + }, + }, + }); + + const paymentResponseResult = await insertPaymentResponse({ + variables: { + paymentResponse: { + amount: values.amount, + bodyshopid: bodyshop.id, + paymentid: paymentResult.data.insert_payments.returning[0].id, + jobid: values.jobid, + declinereason: values.paymentResponse.declinereason, + ext_paymentid: values.paymentResponse.paymentid.toString(), + successful: true, + response: values.paymentResponse, + }, + }, + }); + + console.log("Modal - Insert Payment Result: ", paymentResult); + console.log( + "Modal - Insert Respose Payment Result: ", + paymentResponseResult + ); + }; + + return ( + +
+ + + + + + + {/* Lighbox Input amount needs to be hidden */} + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default IntellpayTestPage; diff --git a/client/src/components/_test/payment_response.json b/client/src/components/_test/payment_response.json new file mode 100644 index 000000000..2087d3e32 --- /dev/null +++ b/client/src/components/_test/payment_response.json @@ -0,0 +1,63 @@ +{ + "status": 24201299, + "custid": 19607899, + "paymentid": 24201299, + "response": "A", + "authcode": "498680", + "declinereason": "Approved", + "fee": 0, + "invoice": "", + "account": "john", + "amount": 1000, + "amountincludesfee": false, + "total": 1000, + "paymenttype": "C", + "methodhint": "VI ***1111", + "cardbrand": "Visa", + "cardnumdisplay": "***1111", + "receiptelements": { + "authcode": "498680", + "cust_srv_ph_num": "1-555-555-5555", + "rcpt_pg_ftr_txt": "Thank You\nPlease Come Again", + "rcpt_currency": "USD", + "responsecode": "A", + "rcpt_pay_mthd": "Visa", + "transid": "C00 915799", + "merch_disp_nm": "CP Devel Test", + "rcpt_input_mthd": "Keyed", + "rcpt_pg_hdr_txt": "Welcome!", + "rcpt_tran_time": "Thursday February 23 2023, 11:25:36 pm +08", + "rcpt_trans_type": "Normal Transaction (Sale)", + "message": "Approved", + "rcpt_dba_addr": "1234 Storefront Ave\nSome City, UT 84111", + "avsdata": "N", + "receiptrequirements": "S", + "rcpt_cardnum": "************1111", + "cv2result": "M", + "rfnd_policy_txt": "No Refunds\nStore Credit Only", + "labels": { + "tranref": "REF#", + "tid": "TID", + "validationcode": "ValCode", + "emvapplicationid": "AID", + "emvatc": "ATC", + "rcpt_pay_mthd": "Pay Method", + "transid": "TransID", + "rcpt_input_mthd": "IMode", + "emvtsi": "TSI", + "emvac": "AC", + "rcpt_trans_type": "TranType", + "emvapplicationname": "PApp", + "visarewards": "RewardsProg" + } + }, + "receipttoken": "H4sIAAAAAAAAACXMTQuCMBgA4P/ynh3tw_3dBI/ipQ8NOtRN53QiblpBRfTfCzo/8LwhxGAdZCCwFYoJJFQjI2kvHdGu74lVkgmrWyWNhASW5jW7cB87yHjKKePGJODnxrrnMl7dDTKmEJlSOqV/_N30XPpyj2Eddq57_KKZ8FLzmh_G1VQnVfhjiXGK1XYTc/h8AVOkf4qUAAAA", + "call": "card_payment", + "nonce": "488b5568-b5c1-4f38-8b2f-3b050f3abb11P", + "hmac": "JyPAJ9Yx0SlYBTtqns1OxAFRt+xF3l2UiLPO5zTDRBE=", + "paymentreferenceid": "C19607899P24201299", + "cardnum": "...1111", + "email": "", + "nameOnCard": "John Allen", + "cardType": "visa" +} diff --git a/client/src/components/_test/payment_response.queries.js b/client/src/components/_test/payment_response.queries.js new file mode 100644 index 000000000..4f266573a --- /dev/null +++ b/client/src/components/_test/payment_response.queries.js @@ -0,0 +1,29 @@ +import { gql } from "@apollo/client"; + +export const QUERY_ALL_PAYMENT_RESPONSE = gql` + query QUERY_ALL_PAYMENT_RESPONSE { + payment_response { + amount + bodyshopid + declinereason + ext_paymentid + id + jobid + paymentid + response + successful + } + } +`; + +export const INSERT_PAYMENT_RESPONSE = gql` + mutation INSERT_PAYMENT_RESPONSE( + $paymentResponse: [payment_response_insert_input!]! + ) { + insert_payment_response(objects: $paymentResponse) { + returning { + id + } + } + } +`; diff --git a/client/src/components/_test/test.component.jsx b/client/src/components/_test/test.component.jsx deleted file mode 100644 index b6b02b7c0..000000000 --- a/client/src/components/_test/test.component.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component"; -export default function Test() { - return ( -
- -
- ); -} diff --git a/client/src/components/_test/test.page.jsx b/client/src/components/_test/test.page.jsx new file mode 100644 index 000000000..b750e3873 --- /dev/null +++ b/client/src/components/_test/test.page.jsx @@ -0,0 +1,35 @@ +import { Button } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { setModalContext } from "../../redux/modals/modals.actions"; +import CardPaymentModalComponent from "./card-payment-modal.component."; + +const mapStateToProps = createStructuredSelector({}); + +const mapDispatchToProps = (dispatch) => ({ + setCardPaymentContext: (context) => + dispatch(setModalContext({ context: context, modal: "cardPayment" })), +}); + +function Test({ setCardPaymentContext }) { + return ( +
+ + + {/* */} +
+ ); +} + +export default connect(mapStateToProps, mapDispatchToProps)(Test); diff --git a/client/src/components/payment-modal/payment-modal.container.jsx b/client/src/components/payment-modal/payment-modal.container.jsx index 4b270bca5..9f31807e9 100644 --- a/client/src/components/payment-modal/payment-modal.container.jsx +++ b/client/src/components/payment-modal/payment-modal.container.jsx @@ -7,14 +7,14 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { INSERT_NEW_PAYMENT, - UPDATE_PAYMENT + UPDATE_PAYMENT, } from "../../graphql/payments.queries"; import { setEmailOptions } from "../../redux/email/email.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { selectPayment } from "../../redux/modals/modals.selectors"; import { selectBodyshop, - selectCurrentUser + selectCurrentUser, } from "../../redux/user/user.selectors"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 7a71f2cd1..46949077f 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -16,7 +16,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com import PartnerPingComponent from "../../components/partner-ping/partner-ping.component"; import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container"; import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component"; -import TestComponent from "../../components/_test/test.component"; +import TestComponent from "../../components/_test/test.page"; import { requestForToken } from "../../firebase/firebase.utils"; import { selectBodyshop, @@ -193,9 +193,8 @@ export function Manage({ match, conflict, bodyshop }) { } > - - - + + diff --git a/client/src/redux/modals/modals.reducer.js b/client/src/redux/modals/modals.reducer.js index 72859b08f..3209ae53d 100644 --- a/client/src/redux/modals/modals.reducer.js +++ b/client/src/redux/modals/modals.reducer.js @@ -25,6 +25,7 @@ const INITIAL_STATE = { contractFinder: { ...baseModal }, inventoryUpsert: { ...baseModal }, ca_bc_eftTableConvert: { ...baseModal }, + cardPayment: { ...baseModal }, }; const modalsReducer = (state = INITIAL_STATE, action) => { diff --git a/client/src/redux/modals/modals.selectors.js b/client/src/redux/modals/modals.selectors.js index 82d4f706d..8a4cfee77 100644 --- a/client/src/redux/modals/modals.selectors.js +++ b/client/src/redux/modals/modals.selectors.js @@ -79,3 +79,8 @@ export const selectCaBcEtfTableConvert = createSelector( [selectModals], (modals) => modals.ca_bc_eftTableConvert ); + +export const selectCardPayment = createSelector( + [selectModals], + (modals) => modals.cardPayment +); diff --git a/server.js b/server.js index 90d3da90f..12a90f3c8 100644 --- a/server.js +++ b/server.js @@ -225,6 +225,13 @@ app.post( mixdataUpload.mixdataUpload ); +var intellipay = require("./server/intellipay/intellipay"); +app.get( + "/intellipay/lightbox_credentials", + fb.validateFirebaseIdToken, + intellipay.lightbox_credentials +); + var ioevent = require("./server/ioevent/ioevent"); app.post("/ioevent", ioevent.default); app.post("/newlog", (req, res) => { diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js new file mode 100644 index 000000000..87aec5e2c --- /dev/null +++ b/server/intellipay/intellipay.js @@ -0,0 +1,36 @@ +const GraphQLClient = require("graphql-request").GraphQLClient; +const path = require("path"); +const queries = require("../graphql-client/queries"); +const Dinero = require("dinero.js"); +const qs = require("query-string"); +const axios = require("axios"); +require("dotenv").config({ + path: path.resolve( + process.cwd(), + `.env.${process.env.NODE_ENV || "development"}` + ), +}); + +exports.lightbox_credentials = async (req, res) => { + console.log("In API Lightbox Credential route."); + try { + const options = { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + //TODO: Move these to environment variables/database. + data: qs.stringify({ + merchantkey: "3B8068", + apikey: "Oepn2B.XqRgzAqHqvOOmYUxD2VW.vGSipi", + operatingenv: "businessattended", // add these for EMV Swipe + }), + url: "https://test.cpteller.com/api/custapi.cfc?method=autoterminal", //added .test + }; + + const response = await axios(options); + + res.send(response.data); + } catch (error) { + console.log(error); + res.json({ error }); + } +}; From aa5110ae136b7665a9b107bdc1ddb7aac131623c Mon Sep 17 00:00:00 2001 From: swtmply Date: Sat, 4 Mar 2023 04:05:28 +0800 Subject: [PATCH 02/26] transfered test files to separate component --- .../_test/card-payment-modal.component..jsx | 43 ------------- client/src/components/_test/test.page.jsx | 8 +-- .../card-payment-modal.component..jsx} | 49 +++++++++++---- .../card-payment-modal.container..jsx | 62 +++++++++++++++++++ .../job-payments/job-payments.component.jsx | 13 ++++ .../pages/manage/manage.page.component.jsx | 6 ++ server/intellipay/intellipay.js | 12 +++- 7 files changed, 130 insertions(+), 63 deletions(-) delete mode 100644 client/src/components/_test/card-payment-modal.component..jsx rename client/src/components/{_test/intellipay-test.jsx => card-payment-modal/card-payment-modal.component..jsx} (77%) create mode 100644 client/src/components/card-payment-modal/card-payment-modal.container..jsx diff --git a/client/src/components/_test/card-payment-modal.component..jsx b/client/src/components/_test/card-payment-modal.component..jsx deleted file mode 100644 index 5f0d9617b..000000000 --- a/client/src/components/_test/card-payment-modal.component..jsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Modal } from "antd"; -import React from "react"; -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { toggleModalVisible } from "../../redux/modals/modals.actions"; -import { selectCardPayment } from "../../redux/modals/modals.selectors"; -import { selectBodyshop } from "../../redux/user/user.selectors"; -import IntellipayTestPage from "./intellipay-test"; - -const mapStateToProps = createStructuredSelector({ - cardPaymentModal: selectCardPayment, - bodyshop: selectBodyshop, -}); - -const mapDispatchToProps = (dispatch) => ({ - toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")), -}); - -function CardPaymentModalComponent({ - cardPaymentModal, - toggleModalVisible, - bodyshop, -}) { - const { context, visible } = cardPaymentModal; - - return ( - toggleModalVisible()} - onCancel={() => toggleModalVisible()} - cancelButtonProps={{ style: { display: "none" } }} - width="90%" - destroyOnClose - > - - - ); -} - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CardPaymentModalComponent); diff --git a/client/src/components/_test/test.page.jsx b/client/src/components/_test/test.page.jsx index b750e3873..524a41a45 100644 --- a/client/src/components/_test/test.page.jsx +++ b/client/src/components/_test/test.page.jsx @@ -3,7 +3,7 @@ import React from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { setModalContext } from "../../redux/modals/modals.actions"; -import CardPaymentModalComponent from "./card-payment-modal.component."; +import CardPaymentModalContainer from "../card-payment-modal/card-payment-modal.container."; const mapStateToProps = createStructuredSelector({}); @@ -15,13 +15,11 @@ const mapDispatchToProps = (dispatch) => ({ function Test({ setCardPaymentContext }) { return (
- + - - + + + + {context.balance.toFormat()} + + + ); }; -export default IntellpayTestPage; +export default CardPaymentModalComponent; diff --git a/client/src/components/card-payment-modal/card-payment-modal.container..jsx b/client/src/components/card-payment-modal/card-payment-modal.container..jsx new file mode 100644 index 000000000..83142431d --- /dev/null +++ b/client/src/components/card-payment-modal/card-payment-modal.container..jsx @@ -0,0 +1,62 @@ +import { Button, Modal } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { toggleModalVisible } from "../../redux/modals/modals.actions"; +import { selectCardPayment } from "../../redux/modals/modals.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import CardPaymentModalComponent from "./card-payment-modal.component."; + +const mapStateToProps = createStructuredSelector({ + cardPaymentModal: selectCardPayment, + bodyshop: selectBodyshop, +}); + +const mapDispatchToProps = (dispatch) => ({ + toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")), +}); + +function CardPaymentModalContainer({ + cardPaymentModal, + toggleModalVisible, + bodyshop, +}) { + const { context, visible, actions } = cardPaymentModal; + + const handleCancel = () => { + toggleModalVisible(); + actions.refetch(); + }; + + const handleOK = () => { + toggleModalVisible(); + actions.refetch(); + }; + + const handleNewPayment = () => {}; + + return ( + + Go Back + , + , + ]} + width="50%" + destroyOnClose + > + + + ); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(CardPaymentModalContainer); diff --git a/client/src/components/job-payments/job-payments.component.jsx b/client/src/components/job-payments/job-payments.component.jsx index a67ed047b..f12b81bee 100644 --- a/client/src/components/job-payments/job-payments.component.jsx +++ b/client/src/components/job-payments/job-payments.component.jsx @@ -23,6 +23,8 @@ const mapStateToProps = createStructuredSelector({ const mapDispatchToProps = (dispatch) => ({ setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })), + setCardPaymentContext: (context) => + dispatch(setModalContext({ context: context, modal: "cardPayment" })), }); export function JobPayments({ @@ -30,6 +32,7 @@ export function JobPayments({ jobRO, bodyshop, setPaymentContext, + setCardPaymentContext, refetch, }) { const { t } = useTranslation(); @@ -160,6 +163,16 @@ export function JobPayments({ > {t("menus.header.enterpayment")} + ); const JobsPage = lazy(() => import("../jobs/jobs.page")); +const CardPaymentModalContainer = lazy(() => + import("../../components/card-payment-modal/card-payment-modal.container.") +); + const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container") ); @@ -195,6 +199,8 @@ export function Manage({ match, conflict, bodyshop }) { > + + diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js index 87aec5e2c..3eadfa0b1 100644 --- a/server/intellipay/intellipay.js +++ b/server/intellipay/intellipay.js @@ -11,8 +11,11 @@ require("dotenv").config({ ), }); +const url = process.env.NODE_ENV + ? "https://secure.cpteller.com/api/custapi.cfc?method=autoterminal" + : "https://test.cpteller.com/api/custapi.cfc?method=autoterminal"; + exports.lightbox_credentials = async (req, res) => { - console.log("In API Lightbox Credential route."); try { const options = { method: "POST", @@ -21,9 +24,12 @@ exports.lightbox_credentials = async (req, res) => { data: qs.stringify({ merchantkey: "3B8068", apikey: "Oepn2B.XqRgzAqHqvOOmYUxD2VW.vGSipi", - operatingenv: "businessattended", // add these for EMV Swipe + operatingenv: + process.env.NODE_ENV === undefined + ? process.env.NODE_ENV + : "businessattended", }), - url: "https://test.cpteller.com/api/custapi.cfc?method=autoterminal", //added .test + url, }; const response = await axios(options); From a44ed3c40637b4a2fac00b4d79d68a15e31572ab Mon Sep 17 00:00:00 2001 From: swtmply Date: Mon, 6 Mar 2023 21:49:14 +0800 Subject: [PATCH 03/26] removed console log and added the mutation for failed payment attempts --- .../card-payment-modal.component..jsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) 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 5769e21ad..f17ad4454 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 @@ -26,23 +26,30 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { document.documentElement.appendChild(node); window.intellipay.initialize(); - console.log("Modal: Intellipay Initialized"); - window.intellipay.runOnClose(() => { - // Apparently it automatically removes itself in the document when closed - // so we just need to reintialize it window.intellipay.initialize(); }); window.intellipay.runOnApproval(async function (response) { - console.log("Modal - Payment Response: ", response); - // TODO store the response form.setFieldValue("paymentResponse", response); form.submit(); }); - window.intellipay.runOnNonApproval(function (response) { - console.log("Modal: Non Payment: ", response); + window.intellipay.runOnNonApproval(async function (response) { + // Mutate unsuccessful payment + await insertPaymentResponse({ + variables: { + paymentResponse: { + amount: response.amount, + bodyshopid: bodyshop.id, + jobid: context.jobid, + declinereason: response.declinereason, + ext_paymentid: response.paymentid.toString(), + successful: false, + response, + }, + }, + }); }); }); @@ -54,8 +61,6 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { const disabled = false; const handleFinish = async (values) => { - console.log("Modal: Handle Finish Values", values); - const paymentResult = await insertPayment({ variables: { paymentInput: { @@ -69,7 +74,7 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { }, }); - const paymentResponseResult = await insertPaymentResponse({ + await insertPaymentResponse({ variables: { paymentResponse: { amount: values.amount, @@ -83,12 +88,6 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { }, }, }); - - console.log("Modal - Insert Payment Result: ", paymentResult); - console.log( - "Modal - Insert Respose Payment Result: ", - paymentResponseResult - ); }; return ( @@ -121,6 +120,7 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { value={amount} hidden /> + {/* Lightbox payment response when it is completed */} From a3cc5c23242538ae940116afb323ff5eecabf933 Mon Sep 17 00:00:00 2001 From: swtmply Date: Tue, 14 Mar 2023 02:35:56 +0800 Subject: [PATCH 04/26] restructured card payment modal and added audit trailing --- .../card-payment-modal.component..jsx | 124 +++++++++++++----- .../card-payment-modal.container..jsx | 7 - client/src/utils/AuditTrailMappings.js | 2 +- 3 files changed, 95 insertions(+), 38 deletions(-) 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 f17ad4454..696ef9097 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 @@ -3,21 +3,44 @@ import axios from "axios"; import { useTranslation } from "react-i18next"; import { Button, Card, Form, Input, InputNumber, Row, Select } from "antd"; import moment from "moment"; -import { useMutation } from "@apollo/client"; +import { useMutation, useQuery } from "@apollo/client"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import JobSearchSelectComponent from "../job-search-select/job-search-select.component"; import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries"; -import { INSERT_PAYMENT_RESPONSE } from "../_test/payment_response.queries"; +import { + INSERT_PAYMENT_RESPONSE, + QUERY_RO_AND_OWNER_BY_JOB_PK, +} from "../_test/payment_response.queries"; import DataLabel from "../data-label/data-label.component"; +import { insertAuditTrail } from "../../redux/application/application.actions"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; +import { connect } from "react-redux"; +import { toggleModalVisible } from "../../redux/modals/modals.actions"; -const CardPaymentModalComponent = ({ bodyshop, context }) => { +const mapDispatchToProps = (dispatch) => ({ + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), + toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")), +}); + +const CardPaymentModalComponent = ({ + bodyshop, + context, + toggleModalVisible, + insertAuditTrail, +}) => { const [form] = Form.useForm(); const amount = Form.useWatch("amount", form); const payer = Form.useWatch("payer", form); + const jobid = Form.useWatch("jobid", form); const [insertPayment] = useMutation(INSERT_NEW_PAYMENT); const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE); const { t } = useTranslation(); + const { data, refetch } = useQuery(QUERY_RO_AND_OWNER_BY_JOB_PK, { + variables: { jobid: context?.jobid ?? "" }, + }); + useEffect(() => { axios.get("/intellipay/lightbox_credentials").then((response) => { var rg = document.createRange(); @@ -26,6 +49,8 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { document.documentElement.appendChild(node); window.intellipay.initialize(); + // console.log("Declined Payments - Intellipay", window.intellipay); + window.intellipay.runOnClose(() => { window.intellipay.initialize(); }); @@ -33,32 +58,42 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { window.intellipay.runOnApproval(async function (response) { form.setFieldValue("paymentResponse", response); form.submit(); + + toggleModalVisible(); }); window.intellipay.runOnNonApproval(async function (response) { + console.log("Declined Payments", response); // Mutate unsuccessful payment - await insertPaymentResponse({ - variables: { - paymentResponse: { - amount: response.amount, - bodyshopid: bodyshop.id, - jobid: context.jobid, - declinereason: response.declinereason, - ext_paymentid: response.paymentid.toString(), - successful: false, - response, - }, - }, + + // 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(), }); }); }); - if (context.jobid) { + if (context?.jobid) { form.setFieldValue("jobid", context.jobid); } - }, []); - const disabled = false; + form.setFieldValue("payer", t("payments.labels.customer")); + }, []); const handleFinish = async (values) => { const paymentResult = await insertPayment({ @@ -72,6 +107,16 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { date: moment(Date.now()), }, }, + update(cache, { data }) { + cache.modify({ + id: cache.identify({ id: jobid, __typename: "jobs" }), + fields: { + payments(payments) { + return [...data.insert_payments.returning, ...payments]; + }, + }, + }); + }, }); await insertPaymentResponse({ @@ -105,9 +150,12 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { ]} > { + refetch({ jobid: e }); + }} /> @@ -120,6 +168,20 @@ const CardPaymentModalComponent = ({ bodyshop, context }) => { value={amount} hidden /> + + {/* Lightbox payment response when it is completed */}