Files
Hotel-Booking/Frontend/src/components/payments/StripePaymentWrapper.tsx
Iliyan Angelov 6f85b8cf17 updates
2025-11-21 01:20:51 +02:00

197 lines
5.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { loadStripe, StripeElementsOptions } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import StripePaymentForm from './StripePaymentForm';
import { createStripePaymentIntent, confirmStripePayment } from '../../services/api/paymentService';
import { Loader2, AlertCircle } from 'lucide-react';
interface StripePaymentWrapperProps {
bookingId: number;
amount: number;
currency?: string;
onSuccess: () => void;
onError?: (error: string) => void;
}
const StripePaymentWrapper: React.FC<StripePaymentWrapperProps> = ({
bookingId,
amount,
currency = 'usd',
onSuccess,
onError,
}) => {
const [stripePromise, setStripePromise] = useState<Promise<any> | null>(null);
const [clientSecret, setClientSecret] = useState<string | null>(null);
const [, setPublishableKey] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [paymentCompleted, setPaymentCompleted] = useState(false);
useEffect(() => {
if (paymentCompleted) {
return;
}
const initializeStripe = async () => {
try {
setLoading(true);
setError(null);
const response = await createStripePaymentIntent(
bookingId,
amount,
currency
);
if (response.success && response.data) {
const { publishable_key, client_secret } = response.data;
console.log('Payment intent response:', { publishable_key: publishable_key ? 'present' : 'missing', client_secret: client_secret ? 'present' : 'missing' });
if (!client_secret) {
throw new Error('Client secret not received from server');
}
if (!publishable_key) {
throw new Error('Publishable key not configured. Please configure Stripe settings in Admin Panel.');
}
setPublishableKey(publishable_key);
setClientSecret(client_secret);
const stripePromise = loadStripe(publishable_key);
setStripePromise(stripePromise);
const stripe = await stripePromise;
if (!stripe) {
throw new Error('Failed to load Stripe');
}
} else {
throw new Error(response.message || 'Failed to initialize payment');
}
} catch (err: any) {
console.error('Error initializing Stripe:', err);
const errorMessage = err.response?.data?.message || err.message || 'Failed to initialize payment';
setError(errorMessage);
if (onError) {
onError(errorMessage);
}
} finally {
setLoading(false);
}
};
initializeStripe();
}, [bookingId, amount, currency, onError, paymentCompleted]);
useEffect(() => {
if (clientSecret && stripePromise) {
console.log('Stripe initialized successfully', { hasClientSecret: !!clientSecret, hasStripePromise: !!stripePromise });
} else {
console.log('Stripe not ready', { hasClientSecret: !!clientSecret, hasStripePromise: !!stripePromise, error });
}
}, [clientSecret, stripePromise, error]);
const handlePaymentSuccess = async (paymentIntentId: string) => {
try {
setPaymentCompleted(true);
const response = await confirmStripePayment(paymentIntentId, bookingId);
if (response.success) {
onSuccess();
} else {
setPaymentCompleted(false);
throw new Error(response.message || 'Payment confirmation failed');
}
} catch (err: any) {
console.error('Error confirming payment:', err);
setPaymentCompleted(false);
const errorMessage = err.response?.data?.message || err.message || 'Payment confirmation failed';
setError(errorMessage);
if (onError) {
onError(errorMessage);
}
}
};
const handlePaymentError = (errorMessage: string) => {
setError(errorMessage);
if (onError) {
onError(errorMessage);
}
};
if (paymentCompleted) {
return null;
}
if (loading) {
return (
<div className="flex items-center justify-center p-8">
<Loader2 className="w-8 h-8 animate-spin text-indigo-600" />
<span className="ml-2 text-gray-600">Initializing payment...</span>
</div>
);
}
if (error) {
return (
<div className="bg-red-50 border border-red-200 rounded-lg p-6">
<div className="flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-red-600 mt-0.5" />
<div>
<h3 className="text-lg font-semibold text-red-900 mb-1">
Payment Initialization Failed
</h3>
<p className="text-sm text-red-800">
{error || 'Unable to initialize payment. Please try again.'}
</p>
</div>
</div>
</div>
);
}
if (!clientSecret || !stripePromise) {
return (
<div className="flex items-center justify-center p-8">
<Loader2 className="w-8 h-8 animate-spin text-indigo-600" />
<span className="ml-2 text-gray-600">Loading payment form...</span>
</div>
);
}
const options: StripeElementsOptions = {
clientSecret,
appearance: {
theme: 'stripe',
},
};
return (
<Elements stripe={stripePromise} options={options}>
<StripePaymentForm
clientSecret={clientSecret}
amount={amount}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}
/>
</Elements>
);
};
export default StripePaymentWrapper;