diff --git a/components/checkout/PaymentForm.tsx b/components/checkout/PaymentForm.tsx
index fdd0a92..83f3c07 100644
--- a/components/checkout/PaymentForm.tsx
+++ b/components/checkout/PaymentForm.tsx
@@ -3,9 +3,10 @@
import { useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/navigation'
import Script from 'next/script'
-import { Lock, ShieldCheck } from '@phosphor-icons/react'
+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 } from '@/lib/api/billing'
+import { createEmbeddedCheckout, createCheckoutSession } from '@/lib/api/billing'
interface PaymentFormProps {
plan: string
@@ -15,12 +16,56 @@ interface PaymentFormProps {
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)
@@ -34,14 +79,14 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
expiryDate: null,
verificationCode: null,
})
+ const mollieInitialized = useRef(false)
const [scriptLoaded, setScriptLoaded] = useState(false)
- // Mount Mollie components AFTER both script loaded AND DOM elements exist
+ // Mount Mollie components AFTER script loaded
useEffect(() => {
- if (!scriptLoaded) return
+ if (!scriptLoaded || mollieInitialized.current) return
- // Small delay to ensure DOM elements are painted
const timer = setTimeout(() => {
const mollie = initMollie()
if (!mollie) {
@@ -79,6 +124,7 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
componentsRef.current[type] = component
}
+ mollieInitialized.current = true
setMollieReady(true)
} catch (err) {
console.error('Mollie mount error:', err)
@@ -99,41 +145,56 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
}, [])
const handleSubmit = async (e: React.FormEvent) => {
- setSubmitted(true)
e.preventDefault()
+ setSubmitted(true)
setFormError(null)
+
if (!country) {
setFormError('Please select your country')
return
}
- const mollie = getMollie()
- if (!mollie) {
- setFormError('Payment system not loaded. Please refresh.')
- return
- }
-
setSubmitting(true)
+
try {
- const { token, error } = await mollie.createToken()
- if (error || !token) {
- setFormError(error?.message || 'Invalid card details.')
- setSubmitting(false)
- return
+ 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
}
-
- const result = await createEmbeddedCheckout({
- plan_id: plan,
- interval: 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
} catch (err) {
setFormError((err as Error)?.message || 'Payment failed. Please try again.')
} finally {
@@ -141,6 +202,8 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
}
}
+ const isCard = selectedMethod === 'card'
+
return (
<>