import React, { useEffect, useState } from 'react'; interface SquarePaymentNewProps { amount: number; onPaymentSuccess: (paymentResult: any) => void; onPaymentError: (error: string) => void; tierId: number; } const SquarePaymentNew: React.FC = ({ amount, onPaymentSuccess, onPaymentError, tierId }) => { const [isLoading, setIsLoading] = useState(true); const [card, setCard] = useState(null); const [payments, setPayments] = useState(null); const [squareConfig, setSquareConfig] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const [cardholderName, setCardholderName] = useState(''); const [addressLine1, setAddressLine1] = useState(''); const [addressLine2, setAddressLine2] = useState(''); const [city, setCity] = useState(''); const [postalCode, setPostalCode] = useState(''); useEffect(() => { loadSquareConfig(); }, []); useEffect(() => { if (squareConfig && !payments && !isLoading) { // Only initialize after component is fully rendered (not loading) setTimeout(() => { initializeSquare(); }, 100); } }, [squareConfig, isLoading]); const loadSquareSDK = (environment: string): Promise => { return new Promise((resolve, reject) => { if (window.Square) { resolve(); return; } const script = document.createElement('script'); script.type = 'text/javascript'; if (environment?.toLowerCase() === 'sandbox') { script.src = 'https://sandbox.web.squarecdn.com/v1/square.js'; } else { script.src = 'https://web.squarecdn.com/v1/square.js'; } script.onload = () => resolve(); script.onerror = () => reject(new Error('Failed to load Square SDK')); document.head.appendChild(script); }); }; const loadSquareConfig = async () => { try { const response = await fetch('/api/v1/payments/config/square', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const config = await response.json(); console.log('Square config received:', config); await loadSquareSDK(config.environment); console.log('Square SDK loaded for environment:', config.environment); setSquareConfig(config); setIsLoading(false); // Set loading to false here so DOM renders } catch (error) { console.error('Failed to load Square config:', error); onPaymentError('Failed to load payment configuration'); setIsLoading(false); } }; const initializeSquare = async () => { if (!window.Square) { console.error('Square.js failed to load'); onPaymentError('Payment system failed to load. Please refresh the page.'); setIsLoading(false); return; } try { const environment = squareConfig.environment?.toLowerCase() === 'sandbox' ? 'sandbox' : 'production'; console.log('Initializing Square with environment:', environment); const paymentsInstance = window.Square.payments( squareConfig.application_id, squareConfig.location_id, { environment: environment } ); setPayments(paymentsInstance); // Initialize card with minimal styling - let Square handle address collection const cardInstance = await paymentsInstance.card({ style: { '.input-container': { borderColor: '#E0E0E0', borderRadius: '4px' }, '.input-container.is-focus': { borderColor: '#4CAF50' }, '.message-text': { color: '#666' }, '.message-icon': { color: '#666' }, 'input': { color: '#333' } } }); // Wait for element to be available in DOM const waitForElement = (selector: string, maxAttempts = 20): Promise => { return new Promise((resolve, reject) => { let attempts = 0; console.log(`Looking for element: ${selector}`); const checkElement = () => { const element = document.querySelector(selector); console.log(`Attempt ${attempts + 1}: Element ${selector} found:`, !!element); if (element) { console.log('Element found, resolving'); resolve(element); } else if (attempts < maxAttempts) { attempts++; setTimeout(checkElement, 200); } else { console.error(`Element ${selector} not found after ${maxAttempts} attempts`); console.log('Available elements:', document.querySelectorAll('div[id*="square"]')); reject(new Error(`Element ${selector} not found after ${maxAttempts} attempts`)); } }; checkElement(); }); }; console.log('About to wait for square-card-element'); await waitForElement('#square-card-element'); console.log('Element found, attaching card instance'); await cardInstance.attach('#square-card-element'); setCard(cardInstance); } catch (error) { console.error('Failed to initialize Square:', error); onPaymentError('Failed to initialize payment form'); } }; const handlePayment = async () => { if (!card || !payments) { onPaymentError('Payment form not initialized'); return; } if (!cardholderName.trim()) { onPaymentError('Please enter cardholder name'); return; } if (!addressLine1.trim()) { onPaymentError('Please enter address line 1'); return; } if (!city.trim()) { onPaymentError('Please enter city'); return; } if (!postalCode.trim()) { onPaymentError('Please enter postal code'); return; } setIsProcessing(true); try { const [givenName, ...familyNameParts] = cardholderName.trim().split(/\s+/); const verificationDetails = { amount: amount.toFixed(2), currencyCode: 'GBP', intent: 'CHARGE', billingContact: { givenName: givenName || cardholderName.trim(), familyName: familyNameParts.join(' '), addressLines: [addressLine1.trim(), addressLine2.trim()].filter(Boolean), city: city.trim(), postalCode: postalCode.trim(), countryCode: 'GB' }, customerInitiated: true, sellerKeyedIn: false }; const result = await card.tokenize(verificationDetails); if (result.status === 'OK') { const response = await fetch('/api/v1/payments/square/process', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ source_id: result.token, amount: amount, tier_id: tierId, note: `Membership payment - £${amount.toFixed(2)}`, billing_details: { cardholder_name: cardholderName.trim(), address_line_1: addressLine1.trim(), address_line_2: addressLine2.trim() || undefined, city: city.trim(), postal_code: postalCode.trim(), country: 'GB' } }) }); const paymentResult = await response.json(); if (response.ok && paymentResult.success) { onPaymentSuccess(paymentResult); } else { let errorMessage = 'Payment failed. Please try again.'; if (paymentResult.errors && Array.isArray(paymentResult.errors) && paymentResult.errors.length > 0) { errorMessage = paymentResult.errors[0]; } else if (paymentResult.detail) { errorMessage = paymentResult.detail; } onPaymentError(errorMessage); } } else { const errors = result.errors?.map((e: any) => e.message).join(', ') || 'Card validation failed'; onPaymentError(errors); } } catch (error: any) { console.error('Payment error:', error); onPaymentError(error.message || 'Payment processing failed'); } finally { setIsProcessing(false); } }; if (isLoading) { return (
Loading secure payment form...
); } return (

Secure Payment

£{amount.toFixed(2)}

Enter billing details to verify your card securely.

setCardholderName(e.target.value)} placeholder="Name as shown on card" style={{ width: '100%', padding: '10px 12px', fontSize: '14px', border: '1px solid #E0E0E0', borderRadius: '4px', boxSizing: 'border-box' }} disabled={isProcessing} />
setAddressLine1(e.target.value)} placeholder="Street address" style={{ width: '100%', padding: '10px 12px', fontSize: '14px', border: '1px solid #E0E0E0', borderRadius: '4px', boxSizing: 'border-box' }} disabled={isProcessing} />
setAddressLine2(e.target.value)} placeholder="Apartment, suite, etc. (optional)" style={{ width: '100%', padding: '10px 12px', fontSize: '14px', border: '1px solid #E0E0E0', borderRadius: '4px', boxSizing: 'border-box' }} disabled={isProcessing} />
setCity(e.target.value)} placeholder="City" style={{ width: '100%', padding: '10px 12px', fontSize: '14px', border: '1px solid #E0E0E0', borderRadius: '4px', boxSizing: 'border-box' }} disabled={isProcessing} />
setPostalCode(e.target.value)} placeholder="Postcode" style={{ width: '100%', padding: '10px 12px', fontSize: '14px', border: '1px solid #E0E0E0', borderRadius: '4px', boxSizing: 'border-box' }} disabled={isProcessing} />

🔒 Secure payment powered by Square

Your payment information is encrypted and secure

{squareConfig?.environment === 'sandbox' && (

TEST MODE: Use test card 4111 1111 1111 1111 with any future expiry date

)}
); }; // Extend Window interface for TypeScript declare global { interface Window { Square: any; } } export default SquarePaymentNew;