Untitled

mail@pastecode.io avatar
unknown
plain_text
a month ago
13 kB
1
Indexable
Never
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Button from 'components/common/Button';
import { useSearchParams, withRouter } from 'react-router';
import PaymentOptionCard from './components/PaymentOptionCard';
import useBankPayment from './hooks/useBankPayment';
import useCardProcessor from './hooks/useCardProcessor';
import useInvoice from './hooks/useInvoice';
import { useMobile } from 'services/hooks';
import LoadingSpinner from 'components/common/LoadingSpinner';
import PaymentResult from './components/PaymentResult';
import Header from './components/Header';
import FuseModal from 'components/common/FuseModal';
import TermsContent from './components/TermsContent';
import BankInfoContent from './components/BankInfoContent';
import { formatCurrency } from 'services/currency';
import ReviewInvoice from 'components/Invoices/components/CreateInvoice/ReviewInvoice/ReviewInvoice';
import useEtransfer from './hooks/useEtransfer';
import EtransferContent from './components/EtransferContent';
import './styles/payment.scss';
import Modal from 'components/common/Modal';
import { updateInvoiceStatus, executePayment } from 'services/webApi';
import RejectMessageModalBody from './components/RejectMessageModalBody';
import ClearIcon from '@mui/icons-material/Clear';
import RecurringProfile from '../Invoices/components/CreateInvoice/components/RecurringProfile';
import CreditCard from '@mui/icons-material/CreditCard';
import ComponentAccessControl from 'components/AccessControl/ComponentAccessControl';
import initializePaymentModule from '../PaymentWidget/payment-widget';
import { v4 as uuidv4 } from 'uuid';
import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function Payment({ location, router }) {
  const invoiceToken = location.query.t || '';
  const [view, setView] = useState('paymentPending');
  const [isRejectModalOpen, setIsRejectModalOpen] = useState(false);
  const [isSaveRecurringPaymentInfoModal, setIsSaveRecurringPaymentInfoModal] = useState(false);
  const [isThankYouModalOpen, setIsThankYouModalOpen] = useState(false);
  const [isRejectionLoading, setIsRejectionLoading] = useState(false);
  const [uuid, setUuid] = useState(uuidv4());
  const { invoice, isLoading: isInvoiceLoading, error } = useInvoice(invoiceToken, setView);
  const phase = invoice?.tenant?.tenantPhase || 1;
  const isRecurring = !!invoice?.salesInvoiceDetail?.recurringInvoice;

  const { error: cardProcessorError, openPayModal, isModalLoading } = useCardProcessor(setView, invoiceToken, isRecurring, invoice?.salesInvoiceDetail?.recurringInvoice?.Id);
  const {
    isEtransferModalOpen,
    setIsEtransferModalOpen,
    closeEtransferModal,
    sendEtransferRequest,
    isEtransferLoading,
    etransferError,
    email,
    emailName,
    isEtransferDisabled
  } = useEtransfer(invoice?.billToBranchInfo?.email, invoice?.billToBranchInfo?.partnerName, setView, invoiceToken);
  const {
    isPadAgreementOpen,
    setIsPadAgreementOpen,
    proceedToAccountFields,
    isBankInfoOpen,
    setIsBankInfoOpen,
    bankInfoFields,
    setBankInfoField,
    isBankPaymentLoading,
    isBankPaymentDisabled,
    payWithBankAccount,
    bankPaymentError
  } = useBankPayment(setView, invoiceToken, invoice);

  const isMobile = useMobile();
  const [message, setMessage] = useState('');

  useEffect(() => {
    if (!invoiceToken) {
      router.push('/');
    }
  }, [invoiceToken, router]);

  useEffect(() => {
    if (invoice && invoice.payableAmount) {
      const checkDOMReady = () => {
        if (document.getElementById('divContainer')) {
          initializePaymentModule(invoice.payableAmount);
        } else {
          setTimeout(checkDOMReady, 100);
        }
      };
      checkDOMReady();
    }
  }, [invoice]);

  const rejectInvoice = () => {
    setIsRejectionLoading(true);
    updateInvoiceStatus(invoiceToken, true, '', message).then(() => {
      setView('invoiceRejected');
      setIsThankYouModalOpen(true);
      setIsRejectionLoading(false);
    }).catch(() => {
      setView('noInvoice');
      setIsRejectionLoading(false);
    });
    setIsRejectModalOpen(false);
  };

  const generateNewOrderId = () => {
    setUuid(uuidv4());
  };

  const handlePayment = async () => {
    generateNewOrderId();
  
    const cardNo = localStorage.getItem('creditCard');
    const cvv2 = localStorage.getItem('cvc');
    const expiry = localStorage.getItem('expDate');
    const orderId = uuid;
    const amount = Math.round(invoice.payableAmount * 100);
    const currency = 124;
    const lang = 'EN';
    const ecommerceTxnType = 0;
  
    if (!cardNo || !cvv2 || !expiry || !amount) {
      return;
    }
  
    const expiryParts = expiry.split('/');
    const formattedExpiry = expiryParts[1] + expiryParts[0];
  
    const paymentRequest = {
      cardNo: cardNo,
      cvv2: cvv2,
      expiry: formattedExpiry,
      orderId: orderId,
      amount: amount,
      currency: currency,
      lang: lang,
      ecommerceTxnType: ecommerceTxnType
    };
  
    try {
      const response = await executePayment(paymentRequest);
      console.log('Full Payment Response:', response.data);  // Log the entire response
  
      // Safely access the result object
      const result = response.data?.result;
      if (!result) {
        throw new Error('Invalid response structure: No result object found');
      }
  
      console.log('Result:', result);  // Log the result object
  
      const resultMessage = result?.resultMessage;
      console.log('Result Message:', resultMessage);  // Log the resultMessage
  
      if (!resultMessage) {
        throw new Error('Invalid response structure: No resultMessage found');
      }
  
      switch (resultMessage) {
        case 'Approved':
          toast.success('Invoice paid', {
            position: toast.POSITION.TOP_RIGHT,
          });
          break;
        case 'Invalid card number.':
          toast.error('Invalid card number.', {
            position: toast.POSITION.TOP_RIGHT,
          });
          break;
        case 'Not Approved':
          toast.error('Expiry date or CVV is wrong', {
            position: toast.POSITION.TOP_RIGHT,
          });
          break;
        default:
          toast.error(resultMessage, {
            position: toast.POSITION.TOP_RIGHT,
          });
          break;
      }
    } catch (error) {
      console.error('Payment failed:', error);
      toast.error(`Payment failed: ${error.message}`, {
        position: toast.POSITION.TOP_RIGHT,
      });
    }
  };

  if (isInvoiceLoading) {
    return <div className='payment--loading'><LoadingSpinner /></div>;
  }

  const invoiceStatus = invoice?.salesInvoiceDetail?.trnStatusDefinition?.parameterValue;
  const isPaymentPending = invoiceStatus === 'Sent' || invoiceStatus === 'Overdue';

  const isPaymentOptionsVisible =
    (!isRecurring && isPaymentPending) ||
    (isRecurring && invoice?.salesInvoiceDetail?.recurringInvoice?.status !== 'Canceled' && isPaymentPending);

  return (
    <div className='payment'>
      <ToastContainer />
      <Modal
        isOpen={isRejectModalOpen}
        headerText="Reject Invoice"
        headerIcon={<ClearIcon />}
        flavourText={`Please tell ${invoice?.tenant?.companyName} why you are rejecting the invoice`}
        onPrimaryClick={rejectInvoice}
        isPrimaryDelete
        isPrimaryDisabled={message === ''}
        primaryLabel="Submit"
        onClose={() => setIsRejectModalOpen(false)}>
        <RejectMessageModalBody message={message} setMessage={setMessage} />
      </Modal>
      <Modal
        isOpen={isSaveRecurringPaymentInfoModal}
        headerText={<div>
          <CreditCard style={{ fontSize: '2.5rem' }} />
          <div> Save Recurring Payment Information</div>
        </div>}
        flavourText={<div>
          By continuing, your payment information  will be saved securely and used for future instances of this recurring invoice.
          <br /><br />
          You can revoke this permission at any time by clicking ‘Reject Invoice’ on the invoice preview page or by contacting the Fuse Financial customer who sent you the invoice.
          <br /><br />
          You can read the full terms and conditions here.
        </div>}
        onPrimaryClick={async () => {
          await sendEtransferRequest();
          setIsSaveRecurringPaymentInfoModal(false);
        }}
        primaryLabel="I Understand"
        onClose={() => setIsSaveRecurringPaymentInfoModal(false)}
      />
      <Modal
        isOpen={isThankYouModalOpen}
        headerText="Thank You!"
        headerIcon={<ClearIcon />}
        flavourText={`The Invoice has been rejected and ${invoice?.tenant?.companyName} has been notified.`}
        onPrimaryClick={() => setIsThankYouModalOpen(false)}
        isPrimaryDelete
        primaryLabel="Close"
        onClose={() => setIsThankYouModalOpen(false)}
      />
      <Header invoice={invoice} isMobile={isMobile} />
      <div className='payment__summary'>
        {view === 'paymentPending' && invoice && <div className='payment__summary__invoice'>
          <ReviewInvoice
            companyLogo={invoice.tenant?.companyLogo}
            invoiceDetails={invoice?.salesInvoiceDetail}
          />
        </div>}
        <div className='payment__summary__content'>
          {view === 'paymentPending' ? (
            <React.Fragment>
              <FuseModal
                shouldShowHeader
                isForTermsBlocking
                isOpen={isPadAgreementOpen}
                onClose={() => setIsPadAgreementOpen(false)}
                onPrimaryClick={proceedToAccountFields}
              >
                <TermsContent />
              </FuseModal>
              <FuseModal
                shouldShowHeader
                isOpen={isBankInfoOpen}
                onClose={() => setIsBankInfoOpen(false)}
                primaryLabel='Pay From Account'
                isPrimaryLoading={isBankPaymentLoading}
                isPrimaryDisabled={isBankPaymentDisabled}
                onPrimaryClick={payWithBankAccount}
                error={bankPaymentError}
              >
                <BankInfoContent
                  bankInfoObject={bankInfoFields}
                  onInputChange={setBankInfoField}
                  invoiceAmount={formatCurrency(invoice.payableAmount)}
                  invoiceName={invoice.invoiceNumber.toString()}
              />
              </FuseModal>
              <FuseModal
                shouldShowHeader
                isOpen={isEtransferModalOpen}
                onClose={() => closeEtransferModal()}
                primaryLabel='Send Request'
                isPrimaryLoading={isEtransferLoading}
                isPrimaryDisabled={isEtransferDisabled}
                onPrimaryClick={() => {
                  if (invoice?.salesInvoiceDetail?.recurringInvoice) {
                    setIsSaveRecurringPaymentInfoModal(true);
                  } else {
                    sendEtransferRequest();
                  }
                }}
                error={etransferError}
              >
                <EtransferContent
                  invoiceAmount={formatCurrency(invoice.payableAmount)}
                  invoiceName={invoice.invoiceNumber.toString()}
                  fieldsObject={{ email, emailName }}
                />
              </FuseModal>
              {isRecurring && isPaymentOptionsVisible && <RecurringProfile
                isPaymentPage
                isRecurring={isRecurring}
                recurringInvoice={invoice.salesInvoiceDetail.recurringInvoice}
                reviewInvoice />}
              {isPaymentOptionsVisible && <React.Fragment>
                <div id='divContainer' />
                <div id='hiddenDiv' style={{ display:`none` }} />

                <div className='payment__summary__content__reject_pay'>
                  <Button className="pay-btn" variant='text' onClick={() => handlePayment()} isLoading={isRejectionLoading}>
                    Pay Invoice
                  </Button>
                  <Button className="reject-btn" variant='text' onClick={() => setIsRejectModalOpen(true)} isLoading={isRejectionLoading}>
                    Reject Invoice
                  </Button>
                </div>
              </React.Fragment>}
              {cardProcessorError && <div className='payment__summary__content__error'>{cardProcessorError}</div>}
            </React.Fragment>
          ) : (
            <PaymentResult
              variant={view}
              companyName={invoice?.tenant.companyName}
              invoiceName={invoice?.invoiceNumber.toString()}
            />
          )}
        </div>
      </div>
    </div>
  );
}

Payment.propTypes = {
  location: PropTypes.object.isRequired,
  router: PropTypes.object.isRequired
};

export default withRouter(Payment);
Leave a Comment