From ece1cd75e3515b83bf4176feaf37ccd578e7773b Mon Sep 17 00:00:00 2001 From: Hazem Krimi Date: Fri, 18 Jun 2021 01:41:39 +0100 Subject: [PATCH] Complete payments logic --- src/pages/Payments/index.tsx | 501 ++++++++++++++++++++++++++++++----- src/pages/Project/index.tsx | 41 ++- 2 files changed, 469 insertions(+), 73 deletions(-) diff --git a/src/pages/Payments/index.tsx b/src/pages/Payments/index.tsx index 4f97bb3..cec3fca 100644 --- a/src/pages/Payments/index.tsx +++ b/src/pages/Payments/index.tsx @@ -1,101 +1,460 @@ import * as Yup from 'yup'; -import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; import { useFormik } from 'formik'; -import { useReactToPrint } from 'react-to-print'; import { useHistory, useParams } from 'react-router-dom'; import { useEffect, useState } from 'react'; -import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; +import { useLazyQuery, useReactiveVar } from '@apollo/client'; import { Redirect } from 'react-router'; import { roleVar } from '../../graphql/state'; import { Wrapper } from './styles'; -import { Box, Button, Text } from '../../components'; +import { Alert, Box, Button, Input, Spinner, Text } from '../../components'; import { ArrowLeft } from '../../assets'; +import { + GetProjectByIdQuery, + GetProjectByIdQueryVariables, + ProjectOutput, +} from '../../graphql/types'; +import { GET_PROJECT_BY_ID } from '../../graphql/project.api'; + +type Transaction = { + amount: number; + created: boolean; + selectedOption: number; + _id: string; +}; + +type TransactionData = { + transactions: Array; + remaining_amount: number; + amount: number; + project_id: string; + status: boolean; + total_amount: number; + _id: string; +}; const Payments = () => { const role = useReactiveVar(roleVar); const history = useHistory(); - const stripe = useStripe(); - const elements = useElements(); const { id } = useParams<{ id: string }>(); + const [project, setProject] = useState(); + const [success, setSuccess] = useState(false); const [error, setError] = useState(''); + const [transactionsData, setTransactionsData] = useState(); + const [selectedChunk, setSelectedChunk] = useState(); + const [paymentLoading, setPaymentLoading] = useState(false); - const handleSubmit = async (event: React.FormEvent) => { - // Block native form submission. - event.preventDefault(); + const [getProject, { loading: projectLoading }] = useLazyQuery< + GetProjectByIdQuery, + GetProjectByIdQueryVariables + >(GET_PROJECT_BY_ID, { + onCompleted({ getProjectById }) { + setProject(getProjectById); + }, + fetchPolicy: 'network-only', + }); - if (!stripe || !elements) { - // Stripe.js has not loaded yet. Make sure to disable - // form submission until Stripe.js has loaded. - return; - } + useEffect(() => { + (async () => { + if (id) { + getProject({ variables: { id } }); - // Get a reference to a mounted CardElement. Elements knows how - // to find your CardElement because there can only ever be one of - // each type of element. - const cardElement = elements.getElement(CardElement); + try { + const transactionsResult = await ( + await fetch(`${process.env.REACT_APP_PAYMENT_API}/transactions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ project_id: id }), + }) + ).json(); + if (transactionsResult) setTransactionsData(transactionsResult); + } catch (err) { + console.error(err); + } + } + })(); - // Use your card Element with other Stripe.js APIs - const { - error: paymentError, - paymentMethod, - } = await stripe.createPaymentMethod({ - type: 'card', - card: cardElement!, - }); + // eslint-disable-next-line + }, [id]); - if (paymentError) { - console.log('[error]', paymentError); - } else { - console.log('[PaymentMethod]', paymentMethod); + const paymentForm = useFormik({ + initialValues: { + number: '', + expMonth: '', + expYear: '', + cvc: '', + }, + validationSchema: Yup.object().shape({ + number: Yup.number() + .typeError('Card Number must be a number') + .required('Card Number is required'), - const paymentRequest = stripe.paymentRequest({ - country: 'US', - currency: 'usd', - total: { - label: 'Demo total', - amount: 1000, - }, - requestPayerName: true, - requestPayerEmail: false, - }); + expMonth: Yup.number() + .typeError('Expiary Month must be a number') + .required('Expiary Month is required'), - console.log(paymentRequest); + expYear: Yup.number() + .typeError('Expiary Year must be a number') + .required('Expiary Year is required'), - const result = await paymentRequest.canMakePayment(); + cvc: Yup.number() + .typeError('CVC must be a number') + .required('CVC is required'), + }), + onSubmit: async ({ number, expMonth, expYear, cvc }) => { + try { + setPaymentLoading(true); + let amount = 0; - console.log(result); + switch (selectedChunk) { + case 0: { + amount = + (project?.paymentOption.optOne! * project?.totalPrice!) / 100; + break; + } + case 1: { + amount = + (project?.paymentOption.optTwo! * project?.totalPrice!) / 100; + break; + } + case 2: { + amount = + (project?.paymentOption.optThree! * project?.totalPrice!) / 100; + break; + } + default: + break; + } - paymentRequest.show(); - } - }; + const transactionsResult = await ( + await fetch(`${process.env.REACT_APP_PAYMENT_API}/charge`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + project_id: project?.id, + total_amount: project?.totalPrice, + selectedOption: selectedChunk, + amount, + card: { + number, + exp_month: expMonth, + exp_year: expYear, + cvc, + }, + }), + }) + ).json(); - useEffect(() => {}, []); + if (transactionsResult) { + setPaymentLoading(false); + setTransactionsData(transactionsResult); + setSuccess(true); + setTimeout(() => setSuccess(false), 3000); + } + } catch (err) { + setPaymentLoading(false); + setError(err); + setTimeout(() => setError(''), 3000); + } + }, + }); return role === 'client' ? ( - - - - - - - + <> + {!projectLoading ? ( + + + + +