/** * Pending Payment Banner * Shows alert banner when account status is 'pending_payment' * Displays invoice details and provides link to payment confirmation */ import { useState, useEffect } from 'react'; import { AlertCircleIcon, CreditCardIcon, XIcon } from '../../icons'; import { Link } from 'react-router-dom'; import Button from '../ui/button/Button'; import { useAuthStore } from '../../store/authStore'; import { API_BASE_URL } from '../../services/api'; import PayInvoiceModal from './PayInvoiceModal'; interface Invoice { id: number; invoice_number: string; total?: string; // For backward compatibility total_amount?: string; // Backend returns 'total_amount' currency: string; status: string; due_date?: string; created_at: string; payment_method?: string; // For checking bank_transfer subscription?: { id?: number; plan?: { id: number; name: string; slug?: string; }; }; } interface PendingPaymentBannerProps { className?: string; } export default function PendingPaymentBanner({ className = '' }: PendingPaymentBannerProps) { const [invoice, setInvoice] = useState(null); const [paymentMethod, setPaymentMethod] = useState(null); const [availableGateways, setAvailableGateways] = useState<{ stripe: boolean; paypal: boolean; manual: boolean }>({ stripe: false, paypal: false, manual: true, }); const [loading, setLoading] = useState(true); const [dismissed, setDismissed] = useState(false); const [showPaymentModal, setShowPaymentModal] = useState(false); const { user, refreshUser } = useAuthStore(); const accountStatus = user?.account?.status; const isPendingPayment = accountStatus === 'pending_payment'; // Clear dismissed state when account is no longer pending payment // This ensures the banner shows again if account reverts to pending useEffect(() => { if (!isPendingPayment) { sessionStorage.removeItem('payment-banner-dismissed'); } }, [isPendingPayment]); useEffect(() => { if (isPendingPayment && !dismissed) { loadPendingInvoice(); } else { setLoading(false); } }, [isPendingPayment, dismissed]); const loadPendingInvoice = async () => { try { setLoading(true); const token = useAuthStore.getState().token; // Fetch pending invoices for this account const response = await fetch(`${API_BASE_URL}/v1/billing/invoices/?status=pending&limit=1`, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, credentials: 'include', }); const data = await response.json(); if (response.ok && data.success && data.results?.length > 0) { setInvoice(data.results[0]); // Load user's account payment methods try { const apmResponse = await fetch(`${API_BASE_URL}/v1/billing/payment-methods/`, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, credentials: 'include', }); const apmData = await apmResponse.json(); if (apmResponse.ok && apmData.success && apmData.results?.length > 0) { const defaultMethod = apmData.results.find((m: any) => m.is_default && m.is_verified) || apmData.results.find((m: any) => m.is_verified) || apmData.results[0]; if (defaultMethod) { setPaymentMethod(defaultMethod); } } } catch (err) { console.error('Failed to load account payment methods:', err); } // Load available payment gateways by checking their config endpoints try { const [stripeRes, paypalRes] = await Promise.all([ fetch(`${API_BASE_URL}/v1/billing/stripe/config/`, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, credentials: 'include', }), fetch(`${API_BASE_URL}/v1/billing/paypal/config/`, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, credentials: 'include', }), ]); const stripeData = await stripeRes.json().catch(() => ({})); const paypalData = await paypalRes.json().catch(() => ({})); setAvailableGateways({ stripe: stripeRes.ok && stripeData.success && !!stripeData.publishable_key, paypal: paypalRes.ok && paypalData.success && !!paypalData.client_id, manual: true, }); } catch (err) { console.error('Failed to load payment gateways:', err); } } } catch (err) { console.error('Failed to load pending invoice:', err); } finally { setLoading(false); } }; const handleDismiss = () => { setDismissed(true); // Store dismissal in sessionStorage to persist during session sessionStorage.setItem('payment-banner-dismissed', 'true'); }; const handlePaymentSuccess = async () => { setShowPaymentModal(false); // Refresh user data to update account status await refreshUser(); }; // Don't show if not pending payment, loading, or dismissed if (!isPendingPayment || loading || dismissed) { return null; } // Check if already dismissed in this session if (sessionStorage.getItem('payment-banner-dismissed') === 'true') { return null; } // If no invoice found, show simplified banner if (!invoice) { return (

Payment Required

Your account is pending payment. Please complete your payment to activate your subscription.

); } // Format due date const formatDate = (dateString?: string) => { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }; const isDueSoon = invoice.due_date && new Date(invoice.due_date) <= new Date(Date.now() + 3 * 24 * 60 * 60 * 1000); const isOverdue = invoice.due_date && new Date(invoice.due_date) < new Date(); return ( <>

{isOverdue ? 'Payment Overdue' : 'Payment Required'}

{isDueSoon && !isOverdue && ( Due Soon )}

Your subscription is pending payment confirmation. Complete your payment to activate your account and unlock all features.

{/* Invoice Details */}
Invoice #{invoice.invoice_number}
Amount {invoice.currency} {invoice.total_amount}
Status {invoice.status}
{isOverdue ? 'Was Due' : 'Due Date'} {formatDate(invoice.due_date)}
{/* Action Buttons */}
{/* Only show Bank Transfer Details for PK users with bank_transfer payment method */} {(user?.account as any)?.billing_country?.toUpperCase() === 'PK' && (paymentMethod?.type === 'bank_transfer' || invoice.payment_method === 'bank_transfer') && ( )}
{/* Dismiss Button */}
{/* Payment Modal with All Options */} {showPaymentModal && invoice && ( setShowPaymentModal(false)} onSuccess={handlePaymentSuccess} invoice={invoice} userCountry={(user?.account as any)?.billing_country || 'US'} defaultPaymentMethod={paymentMethod?.type} /> )} ); }