import React from "react"; import { AddCardPropsInterface } from "./interfaces/AddCardPropsInterface"; import { AddCardStateInterface } from "./interfaces/AddCardStateInterface"; import { MessagesService } from "../MessagesService"; import { SimplyinPaymentMethod } from "../simplyinPayments"; declare global { interface Window { CCIframe?: any; SimplyinActivateNewOrderButton?: () => void; } } export class AddCard extends React.Component< AddCardPropsInterface, AddCardStateInterface > { private ccInstance: any = null; private containerRef: React.RefObject = React.createRef(); private hasTriedToLoad: boolean = false; constructor(props: AddCardPropsInterface) { super(props); this.state = { isLoading: false, isFormReady: false, cardName: "", tokenData: null, cardSaveChecked: false, error: "", successMessage: "", }; } componentDidMount() { this.setupMessageListener(); this.setupFormSubmitHandler(); this.checkAndLoadIframe(); } componentDidUpdate(prevProps: AddCardPropsInterface) { this.checkAndLoadIframe(); if ( !prevProps.paymentChannel.state.isSelected && this.props.paymentChannel.state.isSelected && this.state.isFormReady ) { this.enablePaymentButton(); } } private checkAndLoadIframe = () => { if ( this.props.paymentChannel.state.isSelected && !this.hasTriedToLoad && !this.state.isFormReady ) { this.hasTriedToLoad = true; this.loadCCIframeScript(); } }; componentWillUnmount() { window.removeEventListener("message", this.handleMessage); document.removeEventListener("click", this.handlePlaceOrderClick); } render() { const wasManuallyExpanded = ( this.props.paymentChannel.constructor as any ).globalAddCardWasManuallyExpanded; return (
{this.state.isLoading && (

{MessagesService.getMessage( "loading_iframe", "Loading form...", )}

)}
{(window as any).SimplyIn?.is_login && this.props.enableSaveCard && (
)}
{this.state.error && ( )} {(window as any).SimplyIn?.is_login && this.props.enableSaveCard && (
)}
); } private loadCCIframeScript = (): void => { if (window.CCIframe) { this.initializeCCIframeForm(); return; } this.setState({ isLoading: true }); const script = document.createElement("script"); script.src = this.props.iframeUrl; script.onload = () => { this.setState({ isLoading: false }); this.initializeCCIframeForm(); }; script.onerror = () => { this.setState({ isLoading: false, error: MessagesService.getMessage( "loading_error", "Iframe loading error", ), }); }; document.head.appendChild(script); }; private initializeCCIframeForm = (): void => { const cardNumber = document.querySelector("#card-number"); const expiryDate = document.querySelector("#expiry-date"); const verificationCode = document.querySelector("#verification-code"); if (!cardNumber || !expiryDate || !verificationCode) { setTimeout(() => this.initializeCCIframeForm(), 100); return; } try { this.ccInstance = window.CCIframe({ integratorId: this.props.integratorId, }); const options = { fonts: [ { family: "Inter", src: "url(https://fonts.gstatic.com/s/inter/v18/UcCo3FwrK3iLTcviYwYZ8UA3.woff2) format('woff2')", style: "normal", weight: "100 900", }, ], styles: { base: { color: "rgba(0, 0, 0, 0.8)", fontFamily: "Inter", fontSize: "14px", }, }, }; this.ccInstance.createComponent( "cardNumber", "#card-number", options, ); this.ccInstance.createComponent("cardExp", "#expiry-date", options); this.ccInstance.createComponent( "cardCvv", "#verification-code", options, ); this.setState({ isFormReady: true }); this.enablePaymentButton(); } catch (error) { console.error("Failed to initialize CCIframe:", error); this.setState({ error: MessagesService.getMessage( "iframe_error", "Iframe loading error", ), }); } }; private handlePlaceOrderClick = (event: Event): void => { const target = event.target as HTMLElement; if ( target.classList && target.closest(".wc-block-components-checkout-place-order-button") ) { if (!this.isAddCardSelected()) { return; } if ((target as any).dataset?.force === "true") { return; } if (this.state.tokenData && this.state.tokenData.oneTimeToken) { (target as any).dataset.force = "true"; target.click(); return; } event.preventDefault(); this.setState({ error: "", successMessage: "" }); this.createToken(); } }; private createToken = (): void => { if (!this.ccInstance || !this.state.isFormReady) { this.setState({ error: MessagesService.getMessage( "iframe_error", "Iframe not ready, please wait...", ), }); this.enablePaymentButton(); return; } this.disablePaymentButton(); this.ccInstance .createToken() .then((result: any) => { if (result && result.oneTimeToken) { const tokenData = { last4digits: result.last4digits || "", oneTimeToken: result.oneTimeToken, organization: result.organization || "", first6digits: result.first6digits || "", cardName: this.state.cardName, saveCard: this.state.cardSaveChecked, }; SimplyinPaymentMethod.setCardTokenData(tokenData); this.setHiddenFormFields(tokenData); window.postMessage( { type: "SIMPLYIN_CARD_TOKEN", itemId: this.props.itemId, tokenData: tokenData, }, window.location.origin, ); this.setState({ tokenData, successMessage: MessagesService.getMessage( "card_data_valid", "Card data is valid. You can place the order.", ), }); this.props.onCardTokenChange(tokenData); if ((window as any).SimplyinActivateNewOrderButton) { (window as any).SimplyinActivateNewOrderButton(); } const placeOrderButton = document.querySelector( ".wc-block-components-checkout-place-order-button", ) as HTMLButtonElement; if (placeOrderButton) { (placeOrderButton as any).dataset.force = "true"; const waitForCheckoutUpdate = () => { const observer = new MutationObserver( (mutations) => { mutations.forEach((mutation) => { if ( mutation.type === "childList" || mutation.type === "attributes" ) { observer.disconnect(); setTimeout(() => { placeOrderButton.click(); }, 100); } }); }, ); observer.observe( document.querySelector(".wc-block-checkout") || document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [ "class", "data-block-name", ], }, ); setTimeout(() => { observer.disconnect(); placeOrderButton.click(); }, 2000); }; waitForCheckoutUpdate(); } } else { console.error("Invalid token result:", result); SimplyinPaymentMethod.setCardTokenData(null); this.setState({ error: MessagesService.getMessage( "card_processing_error", "Card data processing error. Check entered data.", ), }); this.enablePaymentButton(); } }) .catch((error: any) => { console.error("CCIframe token creation failed:", error); let errorMessage = MessagesService.getMessage( "card_processing_error_retry", "Card data processing error. Try again.", ); if (error && error.message) { if (error.message.includes("400")) { errorMessage = MessagesService.getMessage( "invalid_card_data", "Invalid card data. Check entered information.", ); } else if (error.message.includes("network")) { errorMessage = MessagesService.getMessage( "connection_problem", "Connection problem. Check internet and try again.", ); } } this.setState({ error: errorMessage }); this.enablePaymentButton(); }); }; private setHiddenFormFields = (tokenData: any): void => { const inputOrganization = document.querySelector( 'input[name="simplyin_organization"]', ) as HTMLInputElement; if (inputOrganization) { inputOrganization.value = tokenData.organization; } const inputLastDigits = document.querySelector( 'input[name="simplyin_last_digits"]', ) as HTMLInputElement; if (inputLastDigits) { inputLastDigits.value = tokenData.last4digits; } const inputFirstDigits = document.querySelector( 'input[name="simplyin_first_digits"]', ) as HTMLInputElement; if (inputFirstDigits) { inputFirstDigits.value = tokenData.first6digits; } const inputOtt = document.querySelector( 'input[name="simplyin_ott"]', ) as HTMLInputElement; if (inputOtt) { inputOtt.value = tokenData.oneTimeToken; } const inputCardName = document.querySelector( 'input[name="simplyin_card_name"]', ) as HTMLInputElement; if (inputCardName) { const cardNameElement = document.getElementById( "card-name", ) as HTMLInputElement; inputCardName.value = cardNameElement ? cardNameElement.value : ""; } }; private handleCardNameChange = ( event: React.ChangeEvent, ): void => { this.setState({ cardName: event.target.value }); }; private handleCardSaveChange = ( event: React.ChangeEvent, ): void => { this.setState({ cardSaveChecked: event.target.checked }); if (this.state.cardSaveChecked) { this.hideCardNameGroup(); } else { this.showCardNameGroup(); } }; private showCardNameGroup = (): void => { const cardNameGroup = document.querySelector(".card-name-group"); if (cardNameGroup) { cardNameGroup.classList.remove("card-name-group-hidden"); } }; private hideCardNameGroup = (): void => { const cardNameGroup = document.querySelector(".card-name-group"); if (cardNameGroup) { cardNameGroup.classList.add("card-name-group-hidden"); } }; private isAddCardSelected = (): boolean => { return this.props.paymentChannel.state.isSelected; }; private enablePaymentButton = (): void => { const placeOrderButton = document.querySelector( ".wc-block-components-checkout-place-order-button", ) as HTMLButtonElement; if (placeOrderButton) { placeOrderButton.disabled = false; } }; private disablePaymentButton = (): void => { const placeOrderButton = document.querySelector( ".wc-block-components-checkout-place-order-button", ) as HTMLButtonElement; if (placeOrderButton) { placeOrderButton.disabled = true; } }; public getTokenData = (): any => { return this.state.tokenData; }; public isValid = (): boolean => { return this.state.tokenData !== null; }; private checkAndEnableButton = (): void => { let isPhoneValid = window.SimplyIn?.validatePhoneNumber?.() || false; const phoneCheckbox = document.querySelector( "#simply-save-checkbox", ) as HTMLInputElement; if (!phoneCheckbox || !phoneCheckbox.checked) { isPhoneValid = true; } const isAddCardSelected = this.isAddCardSelected(); const isTokenValid = this.state.tokenData !== null; if (isAddCardSelected && isTokenValid && isPhoneValid) { this.enablePaymentButton(); } else { this.disablePaymentButton(); } }; public validate = (): boolean => { if (!this.state.tokenData) { this.disablePaymentButton(); return false; } this.checkAndEnableButton(); return true; }; private setupMessageListener = (): void => { window.addEventListener("message", this.handleMessage); }; private handleMessage = (event: MessageEvent): void => { let iframeOrigin: string; try { iframeOrigin = new URL(this.props.iframeUrl).origin; } catch { return; } const allowedOrigins = [window.location.origin, iframeOrigin]; if (!allowedOrigins.includes(event.origin)) { return; } if (event.data && event.data.property === "fetchToken") { const tokenData = event.data.value; this.setHiddenFormFields(tokenData); SimplyinPaymentMethod.setCardTokenData(tokenData); if ((window as any).SimplyinActivateNewOrderButton) { (window as any).SimplyinActivateNewOrderButton(); } } if ( event.data && event.data.postmate === "emit" && event.data.value.name === "change" ) { const addCardRadio = document.querySelector( "li.simplyin-podaj_karte_patnicza > label", ) as HTMLInputElement; if (addCardRadio) { addCardRadio.click(); } if (window.SimplyIn?.validatePhoneNumber?.() === true) { console.log("activate new order button"); window.SimplyinActivateNewOrderButton?.(); } } }; private setupFormSubmitHandler = (): void => { document.addEventListener("click", this.handlePlaceOrderClick); }; }