UNPKG

@paybyrd/card-collect

Version:

Paybyrd's tool to aid in the creation of credit card info collect forms

232 lines (199 loc) 5.84 kB
import { getTokensAPIURL } from '../service/api'; import { post } from '../service/api'; import { CardCollectProps, CardCollectResponse, GenerateIFrameFieldProps, IFrameValuesPostMessageResponse } from '../types/types'; import { validateFields, generateError } from '../utils/validations'; const handleCardCollectV2 = ({ onCardCollectFrameLoaded, onFieldChange, onDCCData, pciFieldsBasePath, dccUrl, validateOnFrame, i18nMessages, displayErrors, css, env = 'production' }: CardCollectProps = {}): CardCollectResponse => { const PAYBYRD_API_TOKEN_URL = getTokensAPIURL(env); const cHolder = document.getElementById('cc-holder'); const cNumber = document.getElementById('cc-number'); const cExpDate = document.getElementById('cc-expiration-date'); const cCVV = document.getElementById('cc-cvc'); const handleMessage = (event: MessageEvent) => { if (event.data.type === 'PB_PCI_FIELD_CHANGE') { onFieldChange?.(event.data); } if (event.data.type === 'PB_PCI_DCC_DATA') { onDCCData?.(event.data); } }; window.addEventListener('message', handleMessage); const destroy = () => { window.removeEventListener('message', handleMessage); }; const fieldsToLoad = [ cHolder ? 'cHolder' : null, cNumber ? 'cNumber' : null, cExpDate ? 'cExpDate' : null, cCVV ? 'cCVV' : null ].filter(Boolean).length; let loadedFields = 0; const generateIFrameField = ({ src, placeholder, wrapper, id, css }: GenerateIFrameFieldProps) => { const iframeField = document.createElement('iframe'); iframeField.src = src; iframeField.id = id || ''; iframeField.classList.add('pb-secure-field'); iframeField.style.border = '0'; iframeField.style.width = '100%'; iframeField.style.height = '100%'; wrapper.append(iframeField); iframeField.onload = () => { loadedFields++; iframeField.contentWindow?.postMessage( { type: 'PB_PCI_METADATA', data: { placeholder, css, dccUrl } }, '*' ); if (loadedFields === fieldsToLoad) { onCardCollectFrameLoaded?.(); } }; }; // Generate fields in DOM if (cHolder) { generateIFrameField({ id: 'cc-holder', src: `${pciFieldsBasePath}/pci-card-holder.html`, placeholder: i18nMessages?.holderName || 'Card Holder', wrapper: cHolder, css }); } if (cNumber) { generateIFrameField({ id: 'cc-number', src: `${pciFieldsBasePath}/pci-card-number.html`, placeholder: i18nMessages?.cardNumber || 'Card Number', wrapper: cNumber, css }); } if (cExpDate) { generateIFrameField({ id: 'cc-expiration-date', src: `${pciFieldsBasePath}/pci-card-exp-date.html`, placeholder: i18nMessages?.expDate || 'MM/YY', wrapper: cExpDate, css }); } if (cCVV) { generateIFrameField({ id: 'cc-cvc', src: `${pciFieldsBasePath}/pci-card-cvv.html`, placeholder: i18nMessages?.cvv || 'CVV', wrapper: cCVV, css }); } const getIFrameValues = async (): Promise<IFrameValuesPostMessageResponse> => { const iframes = document.querySelectorAll('.pb-secure-field'); const fieldData = {} as IFrameValuesPostMessageResponse; // Get All Values from each iFrame field before sending to Paybyrd's Payments API async function getFieldData(iframe: HTMLIFrameElement) { return new Promise((resolve) => { const messageListener = (event: MessageEvent) => { if (event.data.type === 'PB_PCI_FIELD_VALUE') { window.removeEventListener('message', messageListener); resolve({ [event.data.field as string]: event.data.value }); } }; window.addEventListener('message', messageListener); iframe.contentWindow?.postMessage( { type: 'PB_PCI_GET_VALUES', data: { id: iframe.id } }, '*' ); }); } for (const iframe of Array.from(iframes)) { Object.assign(fieldData, await getFieldData(iframe as HTMLIFrameElement)); } return fieldData; }; const clearIFrameErrors = () => { const iframes = document.querySelectorAll('.pb-secure-field'); for (const iframe of Array.from(iframes)) { (iframe as HTMLIFrameElement).contentWindow?.postMessage( { type: 'PB_PCI_CLEAR_ERROR' }, '*' ); } }; const generateIFrameErrors = ( field: HTMLElement, errorData: Record<string, string>, validateOnFrame?: boolean ) => { field .querySelector('iframe') ?.contentWindow?.postMessage( { type: 'PB_PCI_FIELD_ERROR', data: { errorData, validateOnFrame } }, '*' ); }; const submit = async () => { clearIFrameErrors(); const fields = await getIFrameValues(); let normalizedExpDate = fields['cc-expiration-date']; if (normalizedExpDate && /^\d{3,4}$/.test(normalizedExpDate)) { const formattedDate = normalizedExpDate.padStart(4, '0'); normalizedExpDate = `${formattedDate.slice(0, 2)}/${formattedDate.slice(2)}`; } const { isValid, errors } = validateFields({ holderValue: fields['cc-holder'], cardValue: fields['cc-number'], dateValue: normalizedExpDate, cvvValue: fields['cc-cvc'], i18nMessages }); if (!isValid) { Object.entries(errors).map((error) => { const field = document.getElementById(error[0]); const errorData = error[1]; if (field) { generateError({ field, displayErrors, errorData }); generateIFrameErrors(field, errorData, validateOnFrame); } }); return Promise.reject(errors); } // Returns tokenized card data to fetch /payment return post(`${PAYBYRD_API_TOKEN_URL}/api/v1/tokens`, { holder: fields['cc-holder'] || '', number: fields['cc-number'] ? fields['cc-number'].replace(/ /g, '') : '', expiration: fields['cc-expiration-date'] || '', cvv: fields['cc-cvc'] || '' }).then((response) => { return { status: 200, data: response }; }); }; return { cardCollect_submit: submit, destroy }; }; export default handleCardCollectV2;