'use client'; import { useCallback, useMemo, useState, useEffect } from 'react'; import assert from 'assert'; import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js'; import { useWallet } from '@solana/wallet-adapter-react'; import { sendSolanaPayment, getRecipientAddress } from '@/services/solana'; import { getRequiredTokenInfo } from '@/services/jupiter-price'; import { PaymentModalProps, PaymentRequest } from '@/types'; import { PAYMENT_METHOD_LABELS, SOL_PAYMENT_AMOUNT_LAMPORTS } from '@/constants/payments'; import { usePaymentMethod } from '@/contexts/PaymentMethodContext'; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL; const SOLANA_SOL_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL; export default function PaymentModal({ isOpen, onClose, url, onPaymentComplete, }: PaymentModalProps) { const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const { selectedPaymentMethod } = usePaymentMethod(); const paymentMethod = selectedPaymentMethod; const [tokenAmount, setTokenAmount] = useState(0); const [tokenDecimals, setTokenDecimals] = useState(6); // Default fallback const [loadingPrice, setLoadingPrice] = useState(false); const { wallet, publicKey } = useWallet(); const directConnection = useMemo(() => new Connection(SOLANA_RPC_URL), []); const solConnection = useMemo(() => SOLANA_SOL_RPC_URL ? new Connection(SOLANA_SOL_RPC_URL) : directConnection, [directConnection] ); // Get configuration from environment variables const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!); const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL || 'TOKEN'; // Fetch token amount based on USD price when SPL token method is selected useEffect(() => { if (!isOpen || paymentMethod !== 'spl-token') { setLoadingPrice(false); return; } const fetchTokenAmount = async () => { setLoadingPrice(true); setError(''); try { const {requiredAmountInBaseUnits, decimals} = await getRequiredTokenInfo(targetUsdAmount, mintAddress); setTokenAmount(requiredAmountInBaseUnits); setTokenDecimals(decimals); } catch (error) { console.error('Error fetching token price:', error); setError('Unable to fetch current token price. Please try again.'); } finally { setLoadingPrice(false); } }; fetchTokenAmount(); }, [isOpen, paymentMethod, targetUsdAmount, mintAddress]); // Initialize payment method when modal opens useEffect(() => { if (isOpen) { setError(''); setLoadingPrice(false); // Set tokenAmount for native GOR payments to maintain consistency if (paymentMethod === 'nat-gor') { setTokenAmount(SOL_PAYMENT_AMOUNT_LAMPORTS); } else { setTokenAmount(0); } } }, [isOpen, paymentMethod]); const handlePayment = useCallback(async () => { if (!paymentMethod) { return; } if (tokenAmount === 0 || (paymentMethod === 'spl-token' && loadingPrice)) { setError('Payment amount not ready. Please wait.'); return; } if (!wallet?.adapter) { setError('Wallet not connected.'); return; } setLoading(true); setError(''); try { const paymentRequest: PaymentRequest = { paymentMethod: paymentMethod, amount: tokenAmount, recipientAddress: getRecipientAddress(paymentMethod) }; // Use different RPC connection based on payment method const connectionToUse = paymentMethod === 'nat-gor' ? solConnection : directConnection; const result = await sendSolanaPayment( wallet.adapter, connectionToUse, publicKey!.toBase58(), paymentRequest ); if (result.success && result.transactionSignature) { onPaymentComplete(result.transactionSignature, paymentMethod); } else { setError(result.error || 'Payment failed. Please try again.'); } } catch (error) { setError(error instanceof Error ? error.message : 'Payment failed. Please try again.'); } finally { setLoading(false); } }, [paymentMethod, tokenAmount, loadingPrice, wallet, directConnection, solConnection, publicKey, onPaymentComplete]); const getPaymentAmountDisplay = () => { switch (paymentMethod) { case 'nat-gor': return `${SOL_PAYMENT_AMOUNT_LAMPORTS / LAMPORTS_PER_SOL} GOR`; case 'spl-token': if (loadingPrice) return 'Loading...'; return tokenAmount > 0 ? `${(tokenAmount / Math.pow(10, tokenDecimals)).toFixed(6)} ${tokenSymbol}` : '0'; default: return ''; } }; if (!isOpen || !paymentMethod) return null; return (

Complete Payment

URL to be deployed:

{url}
{/* Payment Method Display */}

{PAYMENT_METHOD_LABELS[paymentMethod]}

{/* Payment Details */}

Recipient Address:

{getRecipientAddress(paymentMethod)}
USD
{(paymentMethod === 'spl-token' && loadingPrice) ? (
Fetching token amount...
) : ( )} {!(paymentMethod === 'spl-token' && loadingPrice) && (
{paymentMethod === 'nat-gor' ? 'GOR (native)' : tokenSymbol}
)}
{paymentMethod === 'spl-token' && (

Token information fetched from Jupiter

)}
{error && (
{error}
)}
); }