'use client' import { useEffect, useRef, useState } from 'react' import { useRouter } from 'next/navigation' import Script from 'next/script' import { motion, AnimatePresence } from 'framer-motion' import { CreditCard, Lock, ShieldCheck, Bank } from '@phosphor-icons/react' import { initMollie, getMollie, MOLLIE_FIELD_STYLES, type MollieComponent } from '@/lib/mollie' import { createEmbeddedCheckout, createCheckoutSession } from '@/lib/api/billing' interface PaymentFormProps { plan: string interval: string limit: number country: string vatId: string } const PAYMENT_METHODS: Array<{ id: string; label: string; description?: string; icon: string }> = [ { id: 'card', label: 'Card', description: 'Visa, Mastercard, Amex', icon: 'card' }, { id: 'bancontact', label: 'Bancontact', icon: 'bancontact' }, { id: 'ideal', label: 'iDEAL', icon: 'ideal' }, { id: 'applepay', label: 'Apple Pay', icon: 'applepay' }, { id: 'directdebit', label: 'SEPA Direct Debit', icon: 'sepa' }, { id: 'banktransfer', label: 'Bank Transfer', icon: 'banktransfer' }, ] function MethodIcon({ type, className }: { type: string; className?: string }) { const cn = className || 'h-6 w-6' switch (type) { case 'card': return case 'bancontact': return ( ) case 'ideal': return ( ) case 'applepay': return ( ) case 'sepa': case 'banktransfer': return default: return } } const mollieFieldBase = 'w-full rounded-lg border border-neutral-700 bg-neutral-800/50 px-3 py-3 h-[48px] transition-all focus-within:ring-1 focus-within:ring-brand-orange focus-within:border-brand-orange' export default function PaymentForm({ plan, interval, limit, country, vatId }: PaymentFormProps) { const router = useRouter() const [selectedMethod, setSelectedMethod] = useState('card') const [mollieReady, setMollieReady] = useState(false) const [mollieError, setMollieError] = useState(false) const [formError, setFormError] = useState(null) const [cardErrors, setCardErrors] = useState>({}) const [submitted, setSubmitted] = useState(false) const [submitting, setSubmitting] = useState(false) const componentsRef = useRef>({ cardHolder: null, cardNumber: null, expiryDate: null, verificationCode: null, }) const mollieInitialized = useRef(false) const [scriptLoaded, setScriptLoaded] = useState(false) // Mount Mollie components AFTER script loaded useEffect(() => { if (!scriptLoaded || mollieInitialized.current) return const timer = setTimeout(() => { const mollie = initMollie() if (!mollie) { setMollieError(true) return } try { const fields: Array<{ type: string; selector: string; placeholder?: string }> = [ { type: 'cardHolder', selector: '#mollie-card-holder', placeholder: 'John Doe' }, { type: 'cardNumber', selector: '#mollie-card-number', placeholder: '1234 5678 9012 3456' }, { type: 'expiryDate', selector: '#mollie-card-expiry', placeholder: 'MM / YY' }, { type: 'verificationCode', selector: '#mollie-card-cvc', placeholder: 'CVC' }, ] for (const { type, selector, placeholder } of fields) { const el = document.querySelector(selector) as HTMLElement | null if (!el) { setMollieError(true) return } const opts: Record = { styles: MOLLIE_FIELD_STYLES } if (placeholder) opts.placeholder = placeholder const component = mollie.createComponent(type, opts) component.mount(el) component.addEventListener('change', (event: unknown) => { const e = event as { error?: string } setCardErrors((prev) => { const next = { ...prev } if (e.error) next[type] = e.error else delete next[type] return next }) }) componentsRef.current[type] = component } mollieInitialized.current = true setMollieReady(true) } catch (err) { console.error('Mollie mount error:', err) setMollieError(true) } }, 100) return () => clearTimeout(timer) }, [scriptLoaded]) // Cleanup Mollie components on unmount useEffect(() => { return () => { Object.values(componentsRef.current).forEach((c) => { try { c?.unmount() } catch { /* DOM already removed */ } }) } }, []) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setSubmitted(true) setFormError(null) if (!country) { setFormError('Please select your country') return } setSubmitting(true) try { if (selectedMethod === 'card') { const mollie = getMollie() if (!mollie) { setFormError('Payment system not loaded. Please refresh.') setSubmitting(false) return } const { token, error } = await mollie.createToken() if (error || !token) { setFormError(error?.message || 'Invalid card details.') setSubmitting(false) return } const result = await createEmbeddedCheckout({ plan_id: plan, interval, limit, country, vat_id: vatId || undefined, card_token: token, }) if (result.status === 'success') router.push('/checkout?status=success') else if (result.status === 'pending' && result.redirect_url) window.location.href = result.redirect_url } else { const result = await createCheckoutSession({ plan_id: plan, interval, limit, country, vat_id: vatId || undefined, method: selectedMethod, }) window.location.href = result.url } } catch (err) { setFormError((err as Error)?.message || 'Payment failed. Please try again.') } finally { setSubmitting(false) } } const isCard = selectedMethod === 'card' return ( <>