'use client'; import { useState } from 'react'; import Image from 'next/image'; import { Copy, Loader2, Zap, KeyRound } from 'lucide-react'; import { toast } from 'sonner'; import QRCode from 'qrcode'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; interface RoutstrCreateKeySectionProps { baseUrl: string; onApiKeyCreated: (apiKey: string) => void; } async function generateQR(text: string): Promise { try { return await QRCode.toDataURL(text, { type: 'image/png', width: 200, margin: 1, color: { dark: '#000000', light: '#FFFFFF' }, }); } catch { return ''; } } export function RoutstrCreateKeySection({ baseUrl, onApiKeyCreated, }: RoutstrCreateKeySectionProps) { // Lightning state const [lnAmount, setLnAmount] = useState(''); const [lnInvoice, setLnInvoice] = useState<{ bolt11: string; invoice_id: string; } | null>(null); const [lnQrCode, setLnQrCode] = useState(''); const [isCreatingLn, setIsCreatingLn] = useState(false); const [isWaitingLn, setIsWaitingLn] = useState(false); // Cashu state const [cashuToken, setCashuToken] = useState(''); const [isCreatingCashu, setIsCreatingCashu] = useState(false); if (!baseUrl) { return (

Enter the upstream node Base URL above to enable key creation.

); } const cleanUrl = baseUrl.replace(/\/+$/, ''); const handleCopy = async (text: string) => { try { await navigator.clipboard.writeText(text); toast.success('Copied to clipboard'); } catch { toast.error('Failed to copy'); } }; const pollInvoiceStatus = (invoiceId: string) => { let attempts = 0; const maxAttempts = 60; const poll = async () => { try { const resp = await fetch( `${cleanUrl}/v1/balance/lightning/invoice/${invoiceId}/status` ); if (!resp.ok) throw new Error('Failed to check status'); const status = await resp.json(); if (status.status === 'paid' && status.api_key) { onApiKeyCreated(status.api_key); setLnInvoice(null); setLnQrCode(''); setIsWaitingLn(false); setLnAmount(''); toast.success('Payment received! API key created.'); return; } if (status.status === 'expired' || status.status === 'cancelled') { toast.error('Invoice expired or cancelled'); setIsWaitingLn(false); return; } attempts++; if (attempts < maxAttempts) { setTimeout(poll, 5000); } else { toast.error('Payment timeout'); setIsWaitingLn(false); } } catch { attempts++; if (attempts < maxAttempts) { setTimeout(poll, 5000); } else { setIsWaitingLn(false); } } }; poll(); }; const handleCreateLightning = async () => { const amount = parseInt(lnAmount); if (!amount || amount <= 0) { toast.error('Enter a valid amount in sats'); return; } setIsCreatingLn(true); try { const resp = await fetch(`${cleanUrl}/v1/balance/lightning/invoice`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount_sats: amount, purpose: 'create' }), }); if (!resp.ok) { const errorText = await resp.text(); throw new Error(errorText || 'Failed to create invoice'); } const data = await resp.json(); setLnInvoice({ bolt11: data.bolt11, invoice_id: data.invoice_id }); const qr = await generateQR(data.bolt11); setLnQrCode(qr); setIsWaitingLn(true); pollInvoiceStatus(data.invoice_id); } catch (e) { toast.error(e instanceof Error ? e.message : 'Failed to create invoice'); } finally { setIsCreatingLn(false); } }; const handleCreateCashu = async () => { if (!cashuToken.trim()) { toast.error('Paste a Cashu token'); return; } setIsCreatingCashu(true); try { const resp = await fetch(`${cleanUrl}/v1/balance/create`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ initial_balance_token: cashuToken.trim() }), }); if (!resp.ok) { const errorText = await resp.text(); throw new Error(errorText || 'Failed to create API key'); } const data = await resp.json(); onApiKeyCreated(data.api_key); setCashuToken(''); toast.success('API key created'); } catch (e) { toast.error(e instanceof Error ? e.message : 'Failed to create key'); } finally { setIsCreatingCashu(false); } }; return (
External Node

Create an API key on the upstream Routstr node by paying with Lightning or Cashu.

Lightning Cashu
setLnAmount(e.target.value)} className='h-9' disabled={isWaitingLn} />
{lnInvoice && (
Pay this invoice to create your key
{lnQrCode && (
Lightning Invoice QR Code
)}
{lnInvoice.bolt11}
{isWaitingLn && (
Waiting for payment...
)}
)}