UNPKG

gapp-payment-method-flow

Version:

Mobile Gapp flow for Payment Method

571 lines (505 loc) 18.5 kB
import { useCallback, useEffect, useState } from 'react'; import { httpRequest } from '../shared/httpRequest'; import type { TDataLoadType, IModel, TValueOut } from './module.type'; import axios from 'axios'; /** Initial value for payment method */ const paymentMethodInitialValues = { id: 0, text: '', value: '', }; /** view model of payment method gapp */ function useViewModel({ dataLoad, dataIn, dataOut }: IModel) { const [paymentMethods, setPaymentMethods] = useState([]); const [newDataLoad, setNewDataLoad] = useState([]); const [tempSelectedPaymentMethod, setTempSelectedPaymentMethod] = useState< TValueOut['paymentMethod'] >(paymentMethodInitialValues); const [paymentMethodDataOut, setPaymentMethodDataOut] = useState<TValueOut>({ loading: false, paymentMethod: {}, cardDetails: {}, errorResponse: {}, savedCard: {}, defaultCardId: 0, savedCards: dataLoad?.savedCards || [], }); const { data: dataLoadData, axios: axiosInstance, cardValidationToken, endpoints, token, baseUrl, validateCardUrl, ...resDataLoad } = dataLoad || {}; const dataLoadType: TDataLoadType = dataIn.dataLoadType || 'json-stab'; const data = dataLoadType === 'json-stab' ? dataLoadData : [...paymentMethods]; const isCardValidate: boolean = dataIn?.isCardValidate || false; const cardFormTrigger: string = dataIn?.cardFormTrigger || ''; const cardFormTriggerKey: string = dataIn?.cardFormTriggerKey || 'text'; /** dataIn function to reconstruct the list of payment method */ const constructedData = () => { const newData = data.map((i: any) => ({ id: i?.id, text: i?.name, value: i?.name, subText: i.description, descriptionText: '', isExpanded: i.expanded || true, child: (i.merchant_processors || []).length > 0 ? i.merchant_processors?.map((d: any) => ({ id: d?.id, text: d.processors?.name, value: d.processors?.name, subText: d.processors?.description, descriptionText: '', imageUri: d.processors?.icon_url || undefined, })) : undefined, })); const isNewDataLoad = Array.isArray(newDataLoad) && newDataLoad.length > 0; const dLoad = isNewDataLoad ? newDataLoad : newData; if (dataIn.constructData) { return dataIn.constructData(dLoad, data); } else { return dLoad || []; } }; /** dataOut function of Payment Method MiniApp * like MethodSelection, MethodSelectionAccordion */ const handleSelectPaymentMethod = (values: any) => { const newPaymentMethod: TValueOut['paymentMethod'] = values?.type === 'Selected' && values?.value ? values?.value : values; const newPaymentMethods = values?.type === 'NewDataLoad' && values?.value ? values?.value : newDataLoad; setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, paymentMethod: newPaymentMethod, }); setNewDataLoad(newPaymentMethods); }; /** dataOut function of Payment Method MiniApp * (MethodSelectionAccordionScreen) */ const handleSubmitPaymentMethodAccordion = async (values: any) => { const newPaymentMethod: TValueOut['paymentMethod'] = values?.selectedMethod; const newCardDetails: TValueOut['cardDetails'] = { ...values.selectedSavedCard?.value, isSavedCard: true, }; const newSavedCard: TValueOut['savedCard'] = values?.selectedSavedCard; const newPaymentMethods = values?.newData || newDataLoad; const hasTokenId = cardFormTrigger === newPaymentMethod[cardFormTriggerKey] && newSavedCard?.id && paymentMethodDataOut.cardDetails?.paymentTokenId ? true : false; const validatedCard: any = hasTokenId ? {} : values?.selectedSavedCard?.value?.cardNumber && (await validateCreditDebitCard(values?.selectedSavedCard?.value)); setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, cardDetails: Object.assign(newCardDetails, validatedCard), paymentMethod: newPaymentMethod, savedCard: newSavedCard, }); setNewDataLoad(newPaymentMethods); }; const handleCardFormTrigger = (methodValues: any) => { setTempSelectedPaymentMethod(methodValues); if ( dataIn?.actions?.handleCardFormTrigger && methodValues[cardFormTriggerKey] === cardFormTrigger ) { dataIn?.actions?.handleCardFormTrigger(methodValues); } }; const handleAddNewCard = (methodValues: any) => { setTempSelectedPaymentMethod(methodValues); if (dataIn?.actions?.handleAddNewCard) { dataIn?.actions?.handleAddNewCard(methodValues); } }; const validateCreditDebitCard = async (values: any) => { try { let validatedCard: any = {}; const cardPayload = { card: { number: values?.cardNumber?.replace(/-/g, ''), expMonth: values.mm, expYear: values.yy, cvc: values.cvv, }, }; setPaymentMethodDataOut({ ...paymentMethodDataOut, loading: true, }); const networkServiceCondition1 = dataLoadType === 'network-service-config' && validateCardUrl && cardValidationToken; const networkServiceCondition2 = dataLoadType === 'network-service' && cardValidationToken && validateCardUrl; if (isCardValidate) { if (networkServiceCondition1) { const validateRes = await httpRequest({ tokenType: 'Basic', ...resDataLoad, url: `${validateCardUrl}`, method: 'POST', bearerToken: cardValidationToken, requestPostData: cardPayload, }); const res = validateRes?.data || validateRes; validatedCard = { paymentTokenId: res?.paymentTokenId, state: res?.state, }; } else if (networkServiceCondition2) { const validateRes: any = await axios.post( validateCardUrl, cardPayload, { headers: { 'Accept': 'application/json', 'Content-type': 'application/json;charset=utf-8', 'Authorization': `${ resDataLoad?.tokenType || 'Basic' } ${cardValidationToken}`, }, } ); const res = validateRes?.data || validateRes; validatedCard = { paymentTokenId: res?.paymentTokenId, state: res?.state, }; } } return validatedCard; } catch (err: any) { const error = err?.response?.data || err?.data || err; console.error( 'error at validating card form located on payment method gapp', error ); setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: err?.response || err, loading: false, }); } }; // @ts-ignore const verifyCardNumberType = (numberCard: string) => { const cardTypes: any = { 'Electron': /^(4026|417500|4405|4508|4844|4913|4917)\d+$/, 'Maestro': /^(5018|5020|5038|5612|5893|6304|6759|6761|6762|6763|0604|6390)\d+$/, 'Dankort': /^(5019)\d+$/, 'Interpayment': /^(636)\d+$/, 'Union Pay': /^(62|88)\d+$/, // Visa card numbers start with a 4. 'Visa': /^4[0-9]{6,}$/g, // MasterCard numbers start with the numbers 51 through 55, // but this will only detect MasterCard credit cards; // there are other cards issued using the MasterCard system that // do not fall into this IIN range. In 2016, they will add numbers // in the range (222100-272099). 'Master Card': /^5[1-5][0-9]{5,}|222[1-9][0-9]{3,}|22[3-9][0-9]{4,}|2[3-6][0-9]{5,}|27[01][0-9]{4,}|2720[0-9]{3,}$/g, // American Express card numbers start with 34 or 37. 'American Express': /^3[47][0-9]{5,}$/g, // Diners Club card numbers begin with 300 through 305, 36 or 38. There are Diners Club cards that begin with 5 and have 16 digits. These are a joint venture between Diners Club and MasterCard and should be processed like a MasterCard. 'Diner Club': /^3(?:0[0-5]|[68][0-9])[0-9]{4,}$/g, // Discover card numbers begin with 6011 or 65. 'Discover': /^6(?:011|5[0-9]{2})[0-9]{3,}$/g, // JCB cards begin with 2131, 1800 or 35. 'JCB': /^(?:2131|1800|35[0-9]{3})[0-9]{3,}$/g, }; for (const nType in cardTypes) { if (cardTypes[nType].test(numberCard)) { return nType; } } }; /** handle dataOut of CardForm */ const handleCardFormDataOut = async (values: any) => { try { const validatedCard: any = await validateCreditDebitCard(values); // if (typeof values?.isSavedCard === 'boolean') { setPaymentMethodDataOut({ ...paymentMethodDataOut, loading: false, errorResponse: {}, paymentMethod: tempSelectedPaymentMethod, cardDetails: Object.assign(values, validatedCard), }); // } else { // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // loading: false, // errorResponse: {}, // cardDetails: Object.assign(values, validatedCard), // }); // } } catch (err: any) { const error = err?.response?.data || err?.data || err; console.error( 'error at submitting card details located on payment method gapp', error ); setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: err?.response || err, loading: false, }); } }; /** handle dataOut of CardForm */ // const handleCardFormDataOut2 = async (values: any) => { // try { // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // loading: true, // }); // const validatedCard: any = await validateCreditDebitCard(values); // const numberCard: string = (values?.cardNumber || '')?.replace( // /(-)*/g, // '' // ); // const cardDetailsValue = { // cardNumber: numberCard, // '5123-4567-8901-2346', // cvv: values?.cvv, // mm: values?.mm, // name: values?.name, // yy: values?.yy, // }; // if (typeof values?.isSavedCard === 'boolean') { // const privacyCardNumber = numberCard?.replace(/.{12}/g, '**********'); // const savedCardsLength: number = ( // paymentMethodDataOut?.savedCards || [] // )?.length; // // const hasSavedCards: boolean = savedCardsLength > 0; // const savedCardNewId: number = savedCardsLength + 1; // const savedCardDetails = { // id: savedCardNewId, // name: verifyCardNumberType(numberCard) || '', // card_number: privacyCardNumber, // '**********2346', // promo: '', // value: cardDetailsValue, // }; // const cards = paymentMethodDataOut?.savedCards?.concat([ // savedCardDetails, // ]); // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // loading: false, // errorResponse: {}, // paymentMethod: tempSelectedPaymentMethod, // cardDetails: Object.assign(values, cardDetailsValue, validatedCard), // defaultCardId: values?.isSavedCard // ? values.id // : paymentMethodDataOut?.defaultCardId, // savedCards: values?.isSavedCard // ? cards // : paymentMethodDataOut?.savedCards, // }); // } else { // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // loading: false, // errorResponse: {}, // cardDetails: Object.assign(values, cardDetailsValue, validatedCard), // }); // } // } catch (err: any) { // const error = err?.response?.data || err?.data || err; // console.error( // 'error at submitting card details located on payment method gapp', // error // ); // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // errorResponse: err?.response || err, // loading: false, // }); // } // }; // const handleSavedCardScreenDataOut = async ({ // defaultId, // newData, // newSelected, // }: { // defaultId?: number | string; // newData: any; // newSelected: any; // }) => { // const cardDefaultID = newData.filter((n: any) => n.id === defaultId); // const cardNotDefaultID = newData.filter((n: any) => n.id !== defaultId); // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // defaultCardId: cardDefaultID?.length === 1 ? defaultId : 0, // savedCards: cardDefaultID.concat(cardNotDefaultID), // savedCard: newSelected, // }); // }; // api request and set to state action (case 1 - network-service-config) const fetchItems = useCallback(async () => { try { const networkServiceCondition1 = dataLoadType === 'network-service-config' && endpoints?.getAll; const networkServiceCondition2 = dataLoadType === 'network-service' && endpoints?.getAll && axiosInstance; if (networkServiceCondition1) { setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, loading: true, }); const res = await httpRequest({ url: `${baseUrl}${endpoints?.getAll}`, method: 'GET', bearerToken: token, tokenType: 'Bearer', ...resDataLoad, }); setPaymentMethods(res?.data?.data || res?.data || []); setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, loading: false, }); } else if (networkServiceCondition2) { setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, loading: true, }); const res = await axiosInstance.get(endpoints?.getAll); setPaymentMethods(res?.data?.data || res?.data || []); setPaymentMethodDataOut({ ...paymentMethodDataOut, errorResponse: {}, loading: false, }); } } catch (err: any) { const error = err?.response || err; console.error( 'error at fetching payment method list located on payment method gapp', error ); setPaymentMethodDataOut({ ...paymentMethodDataOut, loading: false, errorResponse: error, }); } }, []); // life-cycle method to run api request useEffect(() => { fetchItems(); }, [endpoints?.getAll]); // life-cycle method to dataOut (your GApp) useEffect(() => { dataOut(paymentMethodDataOut); }, [paymentMethodDataOut]); // useEffect(() => { // if (Array.isArray(dataLoad?.savedCards)) { // setPaymentMethodDataOut({ // ...paymentMethodDataOut, // savedCards: dataLoad?.savedCards, // }); // console.log('render dataLoad Saved cards', dataLoad?.savedCards); // } // }, [JSON.stringify(dataLoad?.savedCards)]); return { /** mini-app/screen key (compatible to: MethodSelection) */ 'payment-method': { dataLoad: constructedData(), }, /** mini-app/screen key (compatible to: MethodSelectionAccordion) */ 'payment-method-accordion-list': { dataLoad: constructedData(), dataIn: { selectedPaymentMethod: paymentMethodDataOut.paymentMethod[cardFormTriggerKey] === cardFormTrigger ? paymentMethodInitialValues : paymentMethodDataOut.paymentMethod, }, dataOut: handleSelectPaymentMethod, }, /** mini-app/screen key (compatible to: MethodSelectionAccordionScreen) */ 'payment-method-accordion-screen': { dataLoad: constructedData(), dataIn: { selectedPaymentMethod: paymentMethodDataOut.paymentMethod[cardFormTriggerKey] === cardFormTrigger ? paymentMethodInitialValues : paymentMethodDataOut.paymentMethod, cardFormTrigger, handleCardFormTrigger, savedCards: dataLoad?.savedCards, onPressNewCard: handleAddNewCard, onPressManageCard: dataIn?.actions?.handleManageCard, }, dataOut: handleSubmitPaymentMethodAccordion, }, /** mini-app/screen key (compatible to: MethodSelectionAccordionScreen) */ // 'payment-method-accordion-screen2': { // dataLoad: constructedData(), // dataIn: { // selectedPaymentMethod: // paymentMethodDataOut.paymentMethod?.text === cardFormTrigger // ? paymentMethodInitialValues // : paymentMethodDataOut.paymentMethod, // cardFormTrigger, // handleCardFormTrigger, // savedCards: paymentMethodDataOut?.savedCards, // onPressNewCard: handleAddNewCard, // onPressManageCard: dataIn?.actions?.handleManageCard, // }, // dataOut: handleSubmitPaymentMethodAccordion, // }, /** mini-app/screen key (compatible to: CardForm) */ 'card-form-details': { dataOut: handleCardFormDataOut, }, /** mini-app/screen key (compatible to: CardForm) */ // 'card-form-details2': { // dataOut: handleCardFormDataOut2, // }, /** mini-app/screen key (compatible to: SavedCardScreen) */ 'saved-card-screen': { dataLoad: { data: dataLoad?.savedCards, }, }, // 'saved-card-screen-2': { // dataLoad: { // data: paymentMethodDataOut?.savedCards, // }, // dataOut: handleSavedCardScreenDataOut, // }, }; } export default useViewModel;