From 6524ea415e34beac53174bde11a570734a1fb992 Mon Sep 17 00:00:00 2001 From: shialoth Date: Wed, 30 Apr 2025 23:08:35 +0530 Subject: [PATCH] payment system fix --- src/app/dashboard/rent/Boxes.tsx | 17 ++-- src/components/GpuPaymentModal.tsx | 155 +++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 50 deletions(-) diff --git a/src/app/dashboard/rent/Boxes.tsx b/src/app/dashboard/rent/Boxes.tsx index 567778e..453a15b 100644 --- a/src/app/dashboard/rent/Boxes.tsx +++ b/src/app/dashboard/rent/Boxes.tsx @@ -171,14 +171,15 @@ export default function Boxes() { {selectedGpu && ( setSelectedGpu(null)} - gpu={{ - id: selectedGpu.id, - title: selectedGpu.title, - price_usd: selectedGpu.price_usd, - }} - /> + isOpen={true} + onClose={() => setSelectedGpu(null)} + gpu={{ + id: selectedGpu.id, + title: selectedGpu.title, + price_usd: selectedGpu.price_usd, + price_per_hour: selectedGpu.price, // pass it here + }} + /> )} ); diff --git a/src/components/GpuPaymentModal.tsx b/src/components/GpuPaymentModal.tsx index f9683c5..8c12827 100644 --- a/src/components/GpuPaymentModal.tsx +++ b/src/components/GpuPaymentModal.tsx @@ -11,8 +11,8 @@ import axios from 'axios'; import { usePrivy } from '@privy-io/react-auth'; import { useSolanaWallets, - useSignTransaction, } from '@privy-io/react-auth/solana'; +import { createClient } from '@supabase/supabase-js'; interface GpuPaymentModalProps { isOpen: boolean; @@ -21,23 +21,35 @@ interface GpuPaymentModalProps { id: string; title: string; price_usd: number; + price_per_hour: string; // new field }; } -const SOLANA_RPC = 'https://api.mainnet-beta.solana.com'; -const BUSINESS_WALLET = 'Y93ednSpre2XRjPTBafHU1BXPXKMPhujcYAEshS5pXm8K'; // <-- Replace this + +const SOLANA_RPC = process.env.NEXT_PUBLIC_SOLANA_RPC!; +const BUSINESS_WALLET = process.env.NEXT_PUBLIC_BUSINESS_WALLET!; +const EMAIL_API_URL = process.env.NEXT_PUBLIC_EMAIL_API_URL!; export const GpuPaymentModal = ({ isOpen, onClose, gpu }: GpuPaymentModalProps) => { - const { user } = usePrivy(); - const { wallets } = useSolanaWallets(); - const { signTransaction } = useSignTransaction(); + const { user, connectWallet } = usePrivy(); + const { wallets: privyWallets } = useSolanaWallets(); const [solPrice, setSolPrice] = useState(null); + const [emailInput, setEmailInput] = useState(''); const [loading, setLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const connection = new Connection(SOLANA_RPC); + const userEmail = user?.email?.address || emailInput || null; + + const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + const [emailTouched, setEmailTouched] = useState(false); + + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + ); useEffect(() => { const fetchSolPrice = async () => { @@ -64,13 +76,23 @@ export const GpuPaymentModal = ({ isOpen, onClose, gpu }: GpuPaymentModalProps) setSuccessMsg(null); try { - const solWallet = wallets[0]; + if (!userEmail || !isValidEmail(userEmail)) { + throw new Error('Please enter a valid email address for your receipt.'); + } + + const solWallet = privyWallets[0]; + if (!solWallet || !solWallet.address) { - throw new Error('No connected Solana wallet found.'); + throw new Error('No connected Solana wallet found. Please connect a wallet via Privy.'); + } + + if (!solWallet.signTransaction) { + throw new Error('Connected wallet does not support transaction signing.'); } const fromPubKey = new PublicKey(solWallet.address); const toPubKey = new PublicKey(BUSINESS_WALLET); + const solAmount = (gpu.price_usd / (solPrice || 1)).toFixed(6); const transaction = new Transaction().add( @@ -82,34 +104,41 @@ export const GpuPaymentModal = ({ isOpen, onClose, gpu }: GpuPaymentModalProps) ); transaction.feePayer = fromPubKey; - const { blockhash } = await connection.getRecentBlockhash(); + const { blockhash } = await connection.getLatestBlockhash(); transaction.recentBlockhash = blockhash; - const signedTx = await signTransaction({ - transaction, - connection, - address: solWallet.address, - }); + const signedTx = await solWallet.signTransaction(transaction); const txId = await connection.sendRawTransaction(signedTx.serialize()); await connection.confirmTransaction(txId, 'confirmed'); - await axios.post('/api/orders', { - gpuId: gpu.id, - userEmail: user?.email?.address, - txSignature: txId, - status: 'success', - }); + // Send confirmation email + await axios.post(EMAIL_API_URL, { + email: userEmail, + product: gpu.title, + price_hour: gpu.price_per_hour, + price: gpu.price_usd.toFixed(2), + }); + + // Store order in Supabase + const { error } = await supabase.from('orders').insert([ + { + gpu_id: gpu.id, + user_email: userEmail, + amount_sol: parseFloat(solAmount), + sol_tx_signature: txId, + status: 'success', + }, + ]); + + if (error) { + throw new Error(`Failed to save order: ${error.message}`); + } + setSuccessMsg(`Payment successful! Transaction ID: ${txId}`); } catch (err: any) { setErrorMsg(err.message || 'Payment failed.'); - await axios.post('/api/orders', { - gpuId: gpu.id, - userEmail: user?.email?.address, - txSignature: null, - status: 'failed', - }); } finally { setLoading(false); } @@ -139,22 +168,64 @@ export const GpuPaymentModal = ({ isOpen, onClose, gpu }: GpuPaymentModalProps) )} -
- - -
+ {/* Wallet not connected */} + {privyWallets.length === 0 ? ( +
+

+ To proceed with payment, please connect a Solana wallet. +

+ +
+ ) : ( + <> + {/* Show email prompt if not logged in with email */} + {!user?.email?.address && ( +
+ + setEmailInput(e.target.value)} + onBlur={() => setEmailTouched(true)} + className="w-full px-3 py-2 border rounded-md text-sm" + /> + {emailTouched && emailInput && !isValidEmail(emailInput) && ( +

Please enter a valid email address.

+ )} + +
+ )} + +
+ + +
+ + )} );