Add payment method context to choose between native gorbagana and solana chain tokens

This commit is contained in:
Shreerang Kale 2025-07-23 17:32:12 +05:30
parent 9995e76e87
commit b47103bafa
7 changed files with 332 additions and 178 deletions

View File

@ -1,8 +1,8 @@
# Client-side environment variables (must be prefixed with NEXT_PUBLIC_)
# Solana Token Payment Configuration
# TODO: Use different RPC URL or use browser wallet
# Solana Payment Configuration
NEXT_PUBLIC_SOLANA_RPC_URL=https://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158
NEXT_PUBLIC_SOLANA_SOL_RPC_URL=https://rpc.gorbagana.wtf
NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS=71Jvq4Epe2FCJ7JFSF7jLXdNk1Wy4Bhqd9iL6bEFELvg
# Multisig address

View File

@ -15,7 +15,10 @@ import { PaymentMethod, SOL_PAYMENT_AMOUNT_LAMPORTS } from '@/constants/payments
import { getRecipientAddress } from '@/services/solana';
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
assert(process.env.NEXT_PUBLIC_SOLANA_SOL_RPC_URL, 'SOLANA_SOL_RPC_URL is required');
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
const SOLANA_SOL_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_SOL_RPC_URL;
// Allow 20% slippage due to price fluctuations
const ALLOWED_SLIPPAGE_FACTOR = 0.2
@ -136,16 +139,19 @@ export const registryTransactionWithRetry = async (
throw lastError;
};
let connection: Connection;
// Helper function to get the appropriate connection based on payment method
const getConnection = (paymentMethod: PaymentMethod): Connection => {
if (paymentMethod === 'sol' && SOLANA_SOL_RPC_URL) {
return new Connection(SOLANA_SOL_RPC_URL);
}
return new Connection(SOLANA_RPC_URL);
};
export async function POST(request: NextRequest) {
try {
if (!connection) {
connection = new Connection(SOLANA_RPC_URL);
}
// First check if the request body is valid JSON
let url, txHash, senderPublicKey, paymentMethod;
let connection: Connection;
try {
const body = await request.json();
@ -153,6 +159,9 @@ export async function POST(request: NextRequest) {
txHash = body.txHash;
paymentMethod = body.paymentMethod as PaymentMethod;
// Get the appropriate connection based on payment method
connection = getConnection(paymentMethod);
const tx = await connection.getParsedTransaction(txHash, 'confirmed');
if (!tx) {
console.error("Transaction not found.");

View File

@ -4,6 +4,7 @@ import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import ErrorBoundaryWrapper from "../components/ErrorBoundaryWrapper";
import WalletProviders from "../components/WalletProviders";
import { PaymentMethodProvider } from "../contexts/PaymentMethodContext";
const geistSans = Geist({
@ -32,9 +33,11 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<ErrorBoundaryWrapper />
<PaymentMethodProvider>
<WalletProviders>
{children}
</WalletProviders>
</PaymentMethodProvider>
</body>
</html>
);

View File

@ -7,14 +7,17 @@ import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import URLForm from '@/components/URLForm';
import StatusDisplay from '@/components/StatusDisplay';
import { createApplicationDeploymentRequest } from '@/services/registry';
import { PaymentMethod } from '@/constants/payments';
import { PaymentMethod, PAYMENT_METHOD_LABELS } from '@/constants/payments';
import { useWallet } from '@solana/wallet-adapter-react';
import { usePaymentMethod } from '@/contexts/PaymentMethodContext';
// Dynamically import components to avoid SSR issues with browser APIs
const PaymentModal = dynamic(() => import('@/components/PaymentModal'), { ssr: false });
export default function Home() {
const { wallet, connected, publicKey } = useWallet();
const { selectedPaymentMethod, setSelectedPaymentMethod } = usePaymentMethod();
const [url, setUrl] = useState<string | null>(null);
const [showPaymentModal, setShowPaymentModal] = useState(false);
const [status, setStatus] = useState<'idle' | 'creating' | 'success' | 'error'>('idle');
@ -32,6 +35,20 @@ export default function Home() {
setShowPaymentModal(true);
};
// Helper function to check if current wallet is compatible with selected payment method
const isWalletCompatible = () => {
if (!selectedPaymentMethod || !wallet) return false;
const walletName = wallet.adapter.name.toLowerCase();
const isBackpack = walletName.includes('backpack');
if (selectedPaymentMethod === 'sol') {
return isBackpack; // Only Backpack for SOL
} else {
return !isBackpack; // Only non-Backpack wallets for SPL tokens
}
};
const handlePaymentComplete = useCallback(async (hash: string, paymentMethod: PaymentMethod) => {
if (!publicKey || !url) {
return
@ -85,26 +102,97 @@ export default function Home() {
Deploy Frontends with {process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} + Laconic
</h1>
{/* Step 1: Payment Method Selection */}
<div className="mb-10 p-6 rounded-lg" style={{ background: 'var(--muted-light)', borderLeft: '4px solid var(--primary)' }}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
<span className="inline-flex items-center justify-center mr-3 w-7 h-7 rounded-full text-sm font-bold"
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>1</span>
Connect Your Wallet
Choose Payment Method
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<button
onClick={() => setSelectedPaymentMethod('sol')}
className={`p-4 rounded-lg border-2 transition-colors ${
selectedPaymentMethod === 'sol' ? 'border-blue-500 bg-blue-50' : 'border-gray-300 hover:border-gray-400'
}`}
style={{
backgroundColor: selectedPaymentMethod === 'sol' ? 'var(--accent)' : 'var(--card-bg)',
borderColor: selectedPaymentMethod === 'sol' ? 'var(--primary)' : 'var(--card-border)'
}}
>
<div className="text-left">
<h3 className="font-semibold text-lg mb-2">Native SOL</h3>
<p className="text-sm" style={{ color: 'var(--muted-foreground)' }}>
Pay 0.01 SOL (~$2-5 USD)
</p>
<p className="text-xs mt-1" style={{ color: 'var(--muted-foreground)' }}>
Compatible with: Backpack
</p>
</div>
</button>
<button
onClick={() => setSelectedPaymentMethod('spl-token')}
className={`p-4 rounded-lg border-2 transition-colors ${
selectedPaymentMethod === 'spl-token' ? 'border-blue-500 bg-blue-50' : 'border-gray-300 hover:border-gray-400'
}`}
style={{
backgroundColor: selectedPaymentMethod === 'spl-token' ? 'var(--accent)' : 'var(--card-bg)',
borderColor: selectedPaymentMethod === 'spl-token' ? 'var(--primary)' : 'var(--card-border)'
}}
>
<div className="text-left">
<h3 className="font-semibold text-lg mb-2">{process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} Token</h3>
<p className="text-sm" style={{ color: 'var(--muted-foreground)' }}>
Pay ${process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD} worth
</p>
<p className="text-xs mt-1" style={{ color: 'var(--muted-foreground)' }}>
Compatible with: Phantom, Solflare
</p>
</div>
</button>
</div>
</div>
{/* Step 2: Wallet Connection */}
<div className="mb-10 p-6 rounded-lg" style={{
background: 'var(--muted-light)',
borderLeft: '4px solid var(--primary)',
opacity: selectedPaymentMethod ? '1' : '0.6'
}}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
<span className="inline-flex items-center justify-center mr-3 w-7 h-7 rounded-full text-sm font-bold"
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>2</span>
Connect Compatible Wallet
</h2>
{!selectedPaymentMethod ? (
<p className="text-center" style={{ color: 'var(--muted-foreground)' }}>
Please select a payment method first
</p>
) : (
<div className="text-center">
<p className="mb-4" style={{ color: 'var(--muted-foreground)' }}>
Payment methods: <span className="font-semibold" style={{ color: 'var(--foreground)' }}>
SOL or {process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} (Solana)
Selected: <span className="font-semibold" style={{ color: 'var(--foreground)' }}>
{PAYMENT_METHOD_LABELS[selectedPaymentMethod]}
</span>
</p>
{connected && publicKey ? (
<div className="flex flex-col items-center space-y-3">
<div className="flex items-center">
<span className="w-3 h-3 rounded-full mr-2" style={{ backgroundColor: 'var(--success)' }}></span>
<p className="font-medium" style={{ color: 'var(--success)' }}>
Connected ({wallet?.adapter.name})
<span className="w-3 h-3 rounded-full mr-2" style={{
backgroundColor: isWalletCompatible() ? 'var(--success)' : 'var(--destructive)'
}}></span>
<p className="font-medium" style={{
color: isWalletCompatible() ? 'var(--success)' : 'var(--destructive)'
}}>
{isWalletCompatible() ? 'Compatible' : 'Incompatible'} Wallet ({wallet?.adapter.name})
</p>
</div>
{!isWalletCompatible() && (
<p className="text-sm" style={{ color: 'var(--destructive)' }}>
This wallet is not compatible with {PAYMENT_METHOD_LABELS[selectedPaymentMethod]} payments.
Please select a different wallet or payment method.
</p>
)}
<div className="w-full p-3 rounded-md" style={{ background: 'var(--card-bg)', border: '1px solid var(--card-border)' }}>
<p className="text-sm font-mono break-all text-center">{publicKey.toBase58()}</p>
</div>
@ -118,6 +206,13 @@ export default function Home() {
/>
</div>
) : (
<div>
<p className="mb-4 text-sm" style={{ color: 'var(--muted-foreground)' }}>
{selectedPaymentMethod === 'sol'
? 'Only Backpack wallet supports SOL payments'
: 'Phantom and Solflare wallets support SPL token payments'
}
</p>
<WalletMultiButton
className="!px-6 !py-3 !rounded-md !w-full !transition-colors"
style={{
@ -125,22 +220,25 @@ export default function Home() {
color: 'var(--primary-foreground)',
}}
/>
</div>
)}
</div>
)}
</div>
{/* Step 3: URL Input */}
<div className="mb-8 p-6 rounded-lg" style={{
background: 'var(--muted-light)',
borderLeft: '4px solid var(--primary)',
opacity: connected ? '1' : '0.6'
opacity: (connected && isWalletCompatible()) ? '1' : '0.6'
}}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
<span className="inline-flex items-center justify-center mr-3 w-7 h-7 rounded-full text-sm font-bold"
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>2</span>
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>3</span>
Enter URL to Deploy
</h2>
<URLForm
onSubmit={handleUrlSubmit}
disabled={!connected || status === 'creating'}
disabled={!connected || !isWalletCompatible() || status === 'creating'}
/>
</div>
@ -148,7 +246,7 @@ export default function Home() {
<div className="p-6 rounded-lg" style={{ background: 'var(--muted-light)', borderLeft: '4px solid var(--primary)' }}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
<span className="inline-flex items-center justify-center mr-3 w-7 h-7 rounded-full text-sm font-bold"
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>3</span>
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}>4</span>
Deployment Status
</h2>
<StatusDisplay
@ -166,7 +264,7 @@ export default function Home() {
)}
</div>
{showPaymentModal && url && connected && publicKey && (
{showPaymentModal && url && connected && publicKey && selectedPaymentMethod && (
<PaymentModal
isOpen={showPaymentModal}
onClose={handleClosePaymentModal}

View File

@ -9,10 +9,12 @@ 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 { PaymentMethod, PAYMENT_METHOD_LABELS, SOL_PAYMENT_AMOUNT_LAMPORTS } from '@/constants/payments';
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_SOLANA_SOL_RPC_URL;
export default function PaymentModal({
isOpen,
@ -22,7 +24,8 @@ export default function PaymentModal({
}: PaymentModalProps) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod | null>(null);
const { selectedPaymentMethod } = usePaymentMethod();
const paymentMethod = selectedPaymentMethod;
const [tokenAmount, setTokenAmount] = useState<number>(0);
const [tokenDecimals, setTokenDecimals] = useState<number>(6); // Default fallback
const [loadingPrice, setLoadingPrice] = useState(false);
@ -30,6 +33,10 @@ export default function PaymentModal({
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!);
@ -38,7 +45,7 @@ export default function PaymentModal({
// Fetch token amount based on USD price when SPL token method is selected
useEffect(() => {
if (!isOpen || selectedPaymentMethod !== 'spl-token') {
if (!isOpen || paymentMethod !== 'spl-token') {
setLoadingPrice(false);
return;
}
@ -60,35 +67,30 @@ export default function PaymentModal({
};
fetchTokenAmount();
}, [isOpen, selectedPaymentMethod, targetUsdAmount, mintAddress]);
}, [isOpen, paymentMethod, targetUsdAmount, mintAddress]);
// Reset state when modal opens
// Initialize payment method when modal opens
useEffect(() => {
if (isOpen) {
setSelectedPaymentMethod(null);
setError('');
setTokenAmount(0);
setLoadingPrice(false);
}
}, [isOpen]);
const handlePaymentMethodChange = (method: PaymentMethod) => {
setSelectedPaymentMethod(method);
setError('');
// Set tokenAmount for SOL payments to maintain consistency
if (method === 'sol') {
if (paymentMethod === 'sol') {
setTokenAmount(SOL_PAYMENT_AMOUNT_LAMPORTS);
} else {
setTokenAmount(0);
}
};
}
}, [isOpen, paymentMethod]);
const handlePayment = useCallback(async () => {
if (!selectedPaymentMethod) {
setError('Please select a payment method.');
if (!paymentMethod) {
return;
}
if (tokenAmount === 0 || (selectedPaymentMethod === 'spl-token' && loadingPrice)) {
if (tokenAmount === 0 || (paymentMethod === 'spl-token' && loadingPrice)) {
setError('Payment amount not ready. Please wait.');
return;
}
@ -103,20 +105,23 @@ export default function PaymentModal({
try {
const paymentRequest: PaymentRequest = {
paymentMethod: selectedPaymentMethod,
paymentMethod: paymentMethod,
amount: tokenAmount,
recipientAddress: getRecipientAddress(selectedPaymentMethod)
recipientAddress: getRecipientAddress(paymentMethod)
};
// Use different RPC connection based on payment method
const connectionToUse = paymentMethod === 'sol' ? solConnection : directConnection;
const result = await sendSolanaPayment(
wallet.adapter,
directConnection,
connectionToUse,
publicKey!.toBase58(),
paymentRequest
);
if (result.success && result.transactionSignature) {
onPaymentComplete(result.transactionSignature, selectedPaymentMethod);
onPaymentComplete(result.transactionSignature, paymentMethod);
} else {
setError(result.error || 'Payment failed. Please try again.');
}
@ -125,12 +130,10 @@ export default function PaymentModal({
} finally {
setLoading(false);
}
}, [selectedPaymentMethod, tokenAmount, loadingPrice, wallet, directConnection, publicKey, onPaymentComplete]);
}, [paymentMethod, tokenAmount, loadingPrice, wallet, directConnection, solConnection, publicKey, onPaymentComplete]);
const getPaymentAmountDisplay = () => {
if (!selectedPaymentMethod) return '';
switch (selectedPaymentMethod) {
switch (paymentMethod) {
case 'sol':
return `${SOL_PAYMENT_AMOUNT_LAMPORTS / LAMPORTS_PER_SOL} SOL`;
case 'spl-token':
@ -141,7 +144,7 @@ export default function PaymentModal({
}
};
if (!isOpen) return null;
if (!isOpen || !paymentMethod) return null;
return (
<div className="fixed inset-0 flex items-center justify-center p-4 z-50" style={{ background: 'rgba(15, 23, 42, 0.75)' }}>
@ -161,39 +164,23 @@ export default function PaymentModal({
</div>
</div>
{/* Payment Method Selection */}
{/* Payment Method Display */}
<div>
<label className="block text-sm font-medium mb-3" style={{ color: 'var(--foreground)' }}>
Select Payment Method *
Payment Method
</label>
<div className="space-y-2">
{Object.entries(PAYMENT_METHOD_LABELS).map(([method, label]) => (
<label key={method} className="flex items-center p-3 rounded-md cursor-pointer border"
style={{
borderColor: selectedPaymentMethod === method ? 'var(--primary)' : 'var(--input-border)',
backgroundColor: selectedPaymentMethod === method ? 'var(--primary-light)' : 'var(--card-bg)'
}}>
<input
type="radio"
name="paymentMethod"
value={method}
checked={selectedPaymentMethod === method}
onChange={() => handlePaymentMethodChange(method as PaymentMethod)}
className="mr-3"
/>
<span style={{ color: 'var(--foreground)' }}>{label}</span>
</label>
))}
<div className="p-3 rounded-md" style={{ background: 'var(--muted-light)', color: 'var(--foreground)' }}>
<p className="text-sm font-semibold">
{PAYMENT_METHOD_LABELS[paymentMethod]}
</p>
</div>
</div>
{/* Payment Details - Only show when method is selected */}
{selectedPaymentMethod && (
<>
{/* Payment Details */}
<div>
<p className="text-sm mb-2 font-medium" style={{ color: 'var(--muted)' }}>Recipient Address:</p>
<div className="p-3 rounded-md overflow-hidden" style={{ background: 'var(--muted-light)', color: 'var(--foreground)' }}>
<code className="text-sm font-mono break-all block">{getRecipientAddress(selectedPaymentMethod)}</code>
<code className="text-sm font-mono break-all block">{getRecipientAddress(paymentMethod)}</code>
</div>
</div>
@ -222,7 +209,7 @@ export default function PaymentModal({
</div>
<div className="relative">
{(selectedPaymentMethod === 'spl-token' && loadingPrice) ? (
{(paymentMethod === 'spl-token' && loadingPrice) ? (
<div className="w-full p-3 rounded-md flex items-center" style={{
background: 'var(--muted-light)',
border: '1px solid var(--input-border)',
@ -249,23 +236,21 @@ export default function PaymentModal({
readOnly
/>
)}
{!(selectedPaymentMethod === 'spl-token' && loadingPrice) && (
{!(paymentMethod === 'spl-token' && loadingPrice) && (
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<span className="text-sm font-medium" style={{ color: 'var(--muted)' }}>
{selectedPaymentMethod === 'sol' ? 'SOL' : tokenSymbol}
{paymentMethod === 'sol' ? 'SOL' : tokenSymbol}
</span>
</div>
)}
</div>
</div>
{selectedPaymentMethod === 'spl-token' && (
{paymentMethod === 'spl-token' && (
<p className="text-xs mt-1" style={{ color: 'var(--muted)' }}>
Token information fetched from <a className='text-blue-400 underline' href={`https://jup.ag/tokens/${mintAddress}`} target="_blank" rel="noopener noreferrer">Jupiter</a>
</p>
)}
</div>
</>
)}
{error && (
<div className="p-3 rounded-md text-sm" style={{ backgroundColor: 'var(--error-light)', color: 'var(--error)' }}>
@ -291,11 +276,11 @@ export default function PaymentModal({
onClick={handlePayment}
className="px-5 py-2 rounded-md flex items-center transition-colors"
style={{
backgroundColor: (loading || loadingPrice || !selectedPaymentMethod) ? 'var(--muted)' : 'var(--primary)',
backgroundColor: (loading || loadingPrice) ? 'var(--muted)' : 'var(--primary)',
color: 'var(--primary-foreground)',
opacity: (loading || loadingPrice || !selectedPaymentMethod) ? '0.8' : '1'
opacity: (loading || loadingPrice) ? '0.8' : '1'
}}
disabled={loading || loadingPrice || !selectedPaymentMethod}
disabled={loading || loadingPrice}
>
{(loading || loadingPrice) && (
<svg className="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
@ -303,8 +288,7 @@ export default function PaymentModal({
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
{!selectedPaymentMethod ? 'Select Payment Method' :
loadingPrice ? 'Loading Price...' :
{loadingPrice ? 'Loading Price...' :
loading ? 'Processing...' :
'Pay with Solana Wallet'}
</button>

View File

@ -10,6 +10,7 @@ import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare';
// Default styles that can be overridden by your app
import '@solana/wallet-adapter-react-ui/styles.css';
import assert from 'assert';
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;
@ -19,12 +20,15 @@ interface WalletProvidersProps {
}
export default function WalletProviders({ children }: WalletProvidersProps) {
const { selectedPaymentMethod } = usePaymentMethod();
// Configure the Solana network endpoint
const endpoint = useMemo(() => {
return SOLANA_RPC_URL;
}, []);
const wallets = useMemo(
// All available wallet adapters
const allWallets = useMemo(
() => [
new PhantomWalletAdapter(),
new SolflareWalletAdapter(),
@ -33,6 +37,24 @@ export default function WalletProviders({ children }: WalletProvidersProps) {
[]
);
// Filter wallets based on selected payment method
const wallets = useMemo(() => {
if (!selectedPaymentMethod) {
// If no payment method selected, show all wallets
return allWallets;
}
return allWallets.filter(wallet => {
const isBackpack = wallet.name.toLowerCase().includes('backpack');
if (selectedPaymentMethod === 'sol') {
return isBackpack; // Only Backpack for SOL
} else {
return !isBackpack; // Only non-Backpack wallets for SPL tokens
}
});
}, [allWallets, selectedPaymentMethod]);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>

View File

@ -0,0 +1,38 @@
'use client';
import React, { createContext, useContext, useState, ReactNode } from 'react';
import { PaymentMethod } from '@/constants/payments';
interface PaymentMethodContextType {
selectedPaymentMethod: PaymentMethod | null;
setSelectedPaymentMethod: (method: PaymentMethod | null) => void;
}
const PaymentMethodContext = createContext<PaymentMethodContextType | undefined>(undefined);
interface PaymentMethodProviderProps {
children: ReactNode;
}
export function PaymentMethodProvider({ children }: PaymentMethodProviderProps) {
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod | null>(null);
return (
<PaymentMethodContext.Provider
value={{
selectedPaymentMethod,
setSelectedPaymentMethod
}}
>
{children}
</PaymentMethodContext.Provider>
);
}
export function usePaymentMethod() {
const context = useContext(PaymentMethodContext);
if (context === undefined) {
throw new Error('usePaymentMethod must be used within a PaymentMethodProvider');
}
return context;
}