import { useState, useEffect } from "react"; import { useTranslation } from 'react-i18next'; import { useStripe, useElements, PaymentElement, Elements } from '@stripe/react-stripe-js'; import { loadStripe } from '@stripe/stripe-js'; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { useCheckoutWizard } from "../WizardContext"; interface PaymentStepProps { stripePublishableKey: string; } // Komponente für kostenpflichtige Zahlungen (immer innerhalb Elements Provider) const PaymentForm: React.FC = () => { const stripe = useStripe(); const elements = useElements(); const { selectedPackage, resetPaymentState, nextStep } = useCheckoutWizard(); const { t } = useTranslation('marketing'); const [isProcessing, setIsProcessing] = useState(false); const [error, setError] = useState(''); const [paymentStatus, setPaymentStatus] = useState<'idle' | 'processing' | 'succeeded' | 'failed'>('idle'); useEffect(() => { resetPaymentState(); }, [selectedPackage?.id, resetPaymentState]); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); if (!stripe || !elements) { setError(t('checkout.payment_step.stripe_not_loaded')); return; } setIsProcessing(true); setError(''); setPaymentStatus('processing'); try { const { error: stripeError, paymentIntent } = await stripe.confirmPayment({ elements, confirmParams: { return_url: `${window.location.origin}/checkout/success`, }, redirect: 'if_required', // Wichtig für SCA }); if (stripeError) { console.error('Stripe Payment Error:', stripeError); let errorMessage = t('checkout.payment_step.payment_failed'); switch (stripeError.type) { case 'card_error': errorMessage += stripeError.message || t('checkout.payment_step.error_card'); break; case 'validation_error': errorMessage += t('checkout.payment_step.error_validation'); break; case 'api_connection_error': errorMessage += t('checkout.payment_step.error_connection'); break; case 'api_error': errorMessage += t('checkout.payment_step.error_server'); break; case 'authentication_error': errorMessage += t('checkout.payment_step.error_auth'); break; default: errorMessage += stripeError.message || t('checkout.payment_step.error_unknown'); } setError(errorMessage); setPaymentStatus('failed'); } else if (paymentIntent) { switch (paymentIntent.status) { case 'succeeded': setPaymentStatus('succeeded'); // Kleiner Delay für bessere UX setTimeout(() => nextStep(), 1000); break; case 'processing': setError(t('checkout.payment_step.processing')); setPaymentStatus('processing'); break; case 'requires_payment_method': setError(t('checkout.payment_step.needs_method')); setPaymentStatus('failed'); break; case 'requires_confirmation': setError(t('checkout.payment_step.needs_confirm')); setPaymentStatus('failed'); break; default: setError(t('checkout.payment_step.unexpected_status', { status: paymentIntent.status })); setPaymentStatus('failed'); } } } catch (err) { console.error('Unexpected payment error:', err); setError(t('checkout.payment_step.error_unknown')); setPaymentStatus('failed'); } finally { setIsProcessing(false); } }; return (
{error && ( {error} )}

{t('checkout.payment_step.secure_payment_desc')}

); }; // Wrapper-Komponente mit eigenem Elements Provider export const PaymentStep: React.FC = ({ stripePublishableKey }) => { const { t } = useTranslation('marketing'); const { selectedPackage, authUser, nextStep } = useCheckoutWizard(); const [clientSecret, setClientSecret] = useState(''); const [error, setError] = useState(''); const isFree = selectedPackage ? selectedPackage.price <= 0 : false; // Payment Intent für kostenpflichtige Pakete laden useEffect(() => { if (isFree || !authUser || !selectedPackage) return; const loadPaymentIntent = async () => { try { const response = await fetch('/stripe/create-payment-intent', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', }, body: JSON.stringify({ package_id: selectedPackage.id, }), }); const data = await response.json(); console.log('Payment Intent Response:', { ok: response.ok, status: response.status, data: data }); if (response.ok && data.client_secret) { setClientSecret(data.client_secret); setError(''); } else { const errorMsg = data.error || t('checkout.payment_step.payment_intent_error'); console.error('Payment Intent Error:', errorMsg); setError(errorMsg); } } catch (err) { setError(t('checkout.payment_step.network_error')); } }; loadPaymentIntent(); }, [selectedPackage?.id, authUser, isFree, t]); // Für kostenlose Pakete: Direkte Aktivierung ohne Stripe if (isFree) { return (
{t('checkout.payment_step.free_package_title')} {t('checkout.payment_step.free_package_desc')}
); } // Für kostenpflichtige Pakete: Warten auf clientSecret if (!clientSecret) { return (
{error && ( {error} )}

{t('checkout.payment_step.loading_payment')}

); } // Eigener Elements Provider mit clientSecret für kostenpflichtige Pakete const stripePromise = loadStripe(stripePublishableKey); return ( ); };