This commit is contained in:
Hemant Vishwakarma
2026-04-22 17:50:55 +05:30
3 changed files with 109 additions and 39 deletions

View File

@@ -27,6 +27,7 @@ export function PaymentCancelPage({
useEffect(() => {
localStorage.removeItem('pendingBookingId');
sessionStorage.removeItem('pendingBookingId');
document.cookie = 'pendingBookingId=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
}, []);
return (

View File

@@ -289,6 +289,13 @@ export function PaymentDetailsPage({
const { checkoutPageUrl } = payResponse;
const setCookie = (name: string, value: string, days = 1) => {
const expires = new Date();
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
};
setCookie('pendingBookingId', bookingId);
localStorage.setItem('pendingBookingId', bookingId);
sessionStorage.setItem('pendingBookingId', bookingId);
@@ -317,7 +324,7 @@ export function PaymentDetailsPage({
<div className="min-h-screen bg-[#fafafa] font-poppins">
<Navbar
activeCity="Melbourne"
onCityChange={() => {}}
onCityChange={() => { }}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onPassesClick={onPassesClick}
@@ -373,22 +380,20 @@ export function PaymentDetailsPage({
<div className="flex gap-2 mb-6">
<button
onClick={() => setSelectedTab('myself')}
className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-poppins text-sm font-medium transition-all duration-200 ${
selectedTab === 'myself'
className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-poppins text-sm font-medium transition-all duration-200 ${selectedTab === 'myself'
? 'bg-[#F95F62] text-white shadow-md shadow-[#F95F62]/20'
: 'bg-gray-100 text-[#555] hover:bg-gray-200'
}`}
}`}
>
<User className="w-4 h-4" />
<span>For myself</span>
</button>
<button
onClick={() => setSelectedTab('gift')}
className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-poppins text-sm font-medium transition-all duration-200 ${
selectedTab === 'gift'
className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-poppins text-sm font-medium transition-all duration-200 ${selectedTab === 'gift'
? 'bg-[#F95F62] text-white shadow-md shadow-[#F95F62]/20'
: 'bg-gray-100 text-[#555] hover:bg-gray-200'
}`}
}`}
>
<Gift className="w-4 h-4" />
<span>To gift Someone</span>
@@ -423,12 +428,12 @@ export function PaymentDetailsPage({
<h2 className="font-poppins text-xl leading-snug font-semibold text-[#2a2a2a]">Personal Information</h2>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Field label="First Name" value={formData.firstName} onChange={() => {}} prefilled disabled />
<Field label="Last Name" value={formData.lastName} onChange={() => {}} prefilled disabled />
<Field label="First Name" value={formData.firstName} onChange={() => { }} prefilled disabled />
<Field label="Last Name" value={formData.lastName} onChange={() => { }} prefilled disabled />
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Field label="Email Address" value={formData.email} onChange={() => {}} type="email" prefilled disabled />
<Field label="Phone Number" value={formData.phone} onChange={() => {}} type="tel" prefilled disabled />
<Field label="Email Address" value={formData.email} onChange={() => { }} type="email" prefilled disabled />
<Field label="Phone Number" value={formData.phone} onChange={() => { }} type="tel" prefilled disabled />
</div>
</motion.div>
@@ -524,15 +529,15 @@ export function PaymentDetailsPage({
<h2 className="font-poppins text-xl leading-snug font-semibold text-[#2a2a2a]">Billing Address</h2>
</div>
<div className="space-y-4">
<Field label="Address 1" value={formData.address1} onChange={() => {}} prefilled disabled />
<Field label="Address 2" value={formData.address2} onChange={() => {}} prefilled disabled />
<Field label="Address 1" value={formData.address1} onChange={() => { }} prefilled disabled />
<Field label="Address 2" value={formData.address2} onChange={() => { }} prefilled disabled />
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Field label="City / Suburb" value={formData.city} onChange={() => {}} prefilled disabled />
<Field label="State" value="Victoria" onChange={() => {}} prefilled disabled />
<Field label="City / Suburb" value={formData.city} onChange={() => { }} prefilled disabled />
<Field label="State" value="Victoria" onChange={() => { }} prefilled disabled />
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Field label="Postcode" value={formData.postalCode} onChange={() => {}} inputMode="numeric" prefilled disabled />
<Field label="Country" value={formData.country} onChange={() => {}} prefilled disabled />
<Field label="Postcode" value={formData.postalCode} onChange={() => { }} inputMode="numeric" prefilled disabled />
<Field label="Country" value={formData.country} onChange={() => { }} prefilled disabled />
</div>
</div>
</motion.div>
@@ -551,11 +556,10 @@ export function PaymentDetailsPage({
<div className="px-6 py-5 border-b border-gray-100">
<div className="flex items-start gap-4">
<div
className={`w-16 h-10 rounded-lg flex-shrink-0 flex items-center justify-center ${
bookingDetails?.cardMode?.toLowerCase() === 'flexi'
className={`w-16 h-10 rounded-lg flex-shrink-0 flex items-center justify-center ${bookingDetails?.cardMode?.toLowerCase() === 'flexi'
? 'bg-gradient-to-br from-[#f95faf] to-[#F95F62]'
: 'bg-gradient-to-br from-[#F95F62] to-[#c94245]'
}`}
}`}
>
<span className="font-poppins text-[10px] font-semibold text-white">{bookingDetails?.cardMode}</span>
</div>

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { CheckCircle, XCircle, Loader2 } from 'lucide-react';
import { CheckCircle, XCircle, Loader2, AlertCircle } from 'lucide-react';
import { useConfirmCardPaymentMutation } from '../Redux/services/cards.service';
import { toast } from 'sonner';
import Navbar from '../components/Navbar';
@@ -15,6 +15,14 @@ interface PaymentSuccessPageProps {
user?: { email: string; name: string } | null;
}
// Helper to get cookie value
const getCookie = (name: string): string | null => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
return null;
};
export function PaymentSuccessPage({
onHomeClick,
onPassesClick,
@@ -25,34 +33,38 @@ export function PaymentSuccessPage({
}: PaymentSuccessPageProps) {
const [searchParams] = useSearchParams();
const [confirmPayment, { isLoading }] = useConfirmCardPaymentMutation();
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
const [status, setStatus] = useState<'loading' | 'success' | 'error' | 'manual'>('loading');
const [errorMsg, setErrorMsg] = useState<string>('');
const [manualBookingId, setManualBookingId] = useState('');
const navigate = useNavigate();
useEffect(() => {
const confirm = async () => {
// Try multiple sources to get bookingId
let bookingId = localStorage.getItem('pendingBookingId');
const retrieveBookingId = async () => {
// Try all possible sources
let bookingId = getCookie('pendingBookingId');
if (!bookingId) bookingId = localStorage.getItem('pendingBookingId');
if (!bookingId) bookingId = sessionStorage.getItem('pendingBookingId');
if (!bookingId) bookingId = searchParams.get('bookingId'); // URL query param
console.log('Retrieved bookingId:', bookingId);
if (!bookingId) bookingId = searchParams.get('bookingId');
console.log('Retrieved bookingId from sources:', {
cookie: getCookie('pendingBookingId'),
localStorage: localStorage.getItem('pendingBookingId'),
sessionStorage: sessionStorage.getItem('pendingBookingId'),
queryParam: searchParams.get('bookingId'),
final: bookingId,
});
if (!bookingId) {
setStatus('error');
setErrorMsg(
'Booking ID not found. Please contact support with your order details. ' +
'If you just completed payment, your order may still be processing.'
);
setStatus('manual'); // show manual entry form
return;
}
try {
// ✅ Call confirm API with only bookingId
await confirmPayment(bookingId).unwrap();
setStatus('success');
toast.success('Payment confirmed! Your order is complete.');
// Clean up storage
// Clean up
document.cookie = 'pendingBookingId=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
localStorage.removeItem('pendingBookingId');
sessionStorage.removeItem('pendingBookingId');
} catch (err: any) {
@@ -60,15 +72,34 @@ export function PaymentSuccessPage({
setStatus('error');
setErrorMsg(err?.data?.message || 'Failed to confirm payment. Please contact support.');
toast.error('Confirmation failed');
// Clean up to avoid retry loops
localStorage.removeItem('pendingBookingId');
sessionStorage.removeItem('pendingBookingId');
}
};
confirm();
retrieveBookingId();
}, [confirmPayment, searchParams]);
const handleManualConfirm = async () => {
if (!manualBookingId.trim()) {
toast.error('Please enter your Booking ID');
return;
}
setStatus('loading');
try {
await confirmPayment(manualBookingId).unwrap();
setStatus('success');
toast.success('Payment confirmed! Your order is complete.');
// Clean up
document.cookie = 'pendingBookingId=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
localStorage.removeItem('pendingBookingId');
sessionStorage.removeItem('pendingBookingId');
} catch (err: any) {
console.error('Manual confirmation error:', err);
setStatus('error');
setErrorMsg(err?.data?.message || 'Failed to confirm payment. Please contact support.');
toast.error('Confirmation failed');
}
};
return (
<div className="min-h-screen bg-[#fafafa] font-poppins">
<Navbar
@@ -135,6 +166,40 @@ export function PaymentSuccessPage({
</button>
</>
)}
{status === 'manual' && (
<>
<AlertCircle className="w-16 h-16 text-yellow-500 mx-auto mb-4" />
<h2 className="text-2xl font-semibold text-[#2a2a2a]">Booking ID Not Found</h2>
<p className="text-[#555] mt-2">
We couldn't automatically retrieve your booking ID. Please enter it below to confirm your payment.
<br />
<span className="text-sm text-gray-400">(Check your email or order history for the Booking ID)</span>
</p>
<div className="mt-4">
<input
type="text"
value={manualBookingId}
onChange={(e) => setManualBookingId(e.target.value)}
placeholder="Enter Booking ID"
className="w-full border rounded-xl px-4 py-3 font-poppins text-base outline-none focus:border-[#F95F62]"
/>
<button
onClick={handleManualConfirm}
disabled={isLoading}
className="mt-4 w-full px-6 py-3 bg-[#F95F62] text-white rounded-xl font-medium hover:bg-[#e8545a] transition disabled:opacity-50"
>
{isLoading ? 'Confirming...' : 'Confirm Payment'}
</button>
</div>
<button
onClick={onHomeClick}
className="mt-3 text-sm text-gray-500 hover:text-gray-700"
>
Return to Home
</button>
</>
)}
</div>
</div>