'use client'; import { useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { FaTimes, FaCreditCard, FaLock, FaUniversity } from 'react-icons/fa'; import { Album } from '@/lib/types'; import { formatPrice } from '@/lib/utils'; type PaymentMethod = 'ipg' | 'card-to-card'; interface PaymentModalProps { album: Album | null; onClose: () => void; onSuccess: (albumId: string, transactionId: string) => void; } export default function PaymentModal({ album, onClose, onSuccess }: PaymentModalProps) { const [paymentMethod, setPaymentMethod] = useState('ipg'); const [cardNumber, setCardNumber] = useState(''); const [cardName, setCardName] = useState(''); const [phoneNumber, setPhoneNumber] = useState(''); const [email, setEmail] = useState(''); const [txReceipt, setTxReceipt] = useState(''); const [processing, setProcessing] = useState(false); const [error, setError] = useState(''); const formatCardNumber = (value: string) => { const numbers = value.replace(/\s/g, ''); const groups = numbers.match(/.{1,4}/g); return groups ? groups.join(' ') : numbers; }; const formatPhoneNumber = (value: string) => { // Remove all non-digits let numbers = value.replace(/\D/g, ''); // Remove leading 98 if user types it (we'll add +98 prefix) if (numbers.startsWith('98')) { numbers = numbers.slice(2); } // Limit to 10 digits (Iranian mobile number length) numbers = numbers.slice(0, 10); // Format as +98 XXXX XXX XXXX if (numbers.length === 0) return ''; if (numbers.length <= 4) return `+98 ${numbers}`; if (numbers.length <= 7) return `+98 ${numbers.slice(0, 4)} ${numbers.slice(4)}`; return `+98 ${numbers.slice(0, 4)} ${numbers.slice(4, 7)} ${numbers.slice(7)}`; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); // Basic validation if (!cardName.trim()) { setError('Name is required'); return; } // Validate Iranian phone number (should have 10 digits after +98) const phoneDigits = phoneNumber.replace(/\D/g, ''); // Remove the country code (98) if present to get local number const localNumber = phoneDigits.startsWith('98') ? phoneDigits.slice(2) : phoneDigits; if (localNumber.length !== 10 || !localNumber.startsWith('9')) { setError('Invalid phone number. Must start with 9 and have 10 digits'); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError('Invalid email address'); return; } // Validate based on payment method if (paymentMethod === 'card-to-card') { if (cardNumber.replace(/\s/g, '').length !== 16) { setError('Invalid card number'); return; } if (!txReceipt.trim()) { setError('Transaction receipt is required'); return; } } if (!album) return; setProcessing(true); try { // Handle IPG payment if (paymentMethod === 'ipg') { const response = await fetch('/api/payment/initiate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ albumId: album.id, amount: album.price, customerName: cardName.trim(), email: email.trim(), phoneNumber: phoneNumber, }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to initiate payment'); } // Redirect to ZarinPal payment gateway window.location.href = data.paymentUrl; return; } // Handle card-to-card payment const transactionId = txReceipt.trim() || 'TXN-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9).toUpperCase(); // Create purchase via API (pending approval for card-to-card) const response = await fetch('/api/purchases', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ albumId: album.id, transactionId, customerName: cardName.trim(), email: email.trim(), phoneNumber: phoneNumber, txReceipt: txReceipt.trim(), purchaseDate: new Date().getTime(), approvalStatus: 'pending', paymentMethod: 'card-to-card', }), }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || 'Failed to process purchase'); } setProcessing(false); onSuccess(album.id, transactionId); } catch (err: any) { setProcessing(false); setError(err.message || 'Failed to process payment'); } }; if (!album) return null; return ( {album && ( e.stopPropagation()} className="paper-card max-w-md w-full p-6 border-4 border-paper-dark shadow-paper-lg" > {/* Header */}

Purchase Album

{/* Album Info */}

{album.title}

{album.songs.length} tracks • {album.genre}

{formatPrice(album.price)}

{/* Payment Method Selector */}
{/* Payment Form */}
{/* Show Card Number only for card-to-card */} {paymentMethod === 'card-to-card' && (
{ const formatted = formatCardNumber(e.target.value.replace(/\D/g, '').slice(0, 16)); setCardNumber(formatted); }} placeholder="1234 5678 9012 3456" className="w-full px-3 py-2 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper text-sm" required />
)} {/* Full Name */}
setCardName(e.target.value)} placeholder="John Doe" className="w-full px-3 py-2 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper text-sm" required />
{/* Phone Number */}
{ const formatted = formatPhoneNumber(e.target.value); setPhoneNumber(formatted); }} placeholder="+98 9390 084 053" className="w-full px-3 py-2 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper text-sm" required />
{/* Email */}
setEmail(e.target.value)} placeholder="john@example.com" className="w-full px-3 py-2 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper text-sm" required />
{/* Transaction Receipt - only for card-to-card */} {paymentMethod === 'card-to-card' && (
setTxReceipt(e.target.value)} placeholder="TXN-123456789" className="w-full px-3 py-2 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper text-sm" required />
)} {/* Error Message */} {error && ( {error} )} {/* Submit Button */} {/* Security Notice */}

This is a demo payment form. No real charges will be made.

)}
); }