From b2bc8e35451b24d7f1863f14c6c2f576ffe90756 Mon Sep 17 00:00:00 2001 From: relayhop Date: Sun, 3 May 2026 18:01:17 +0800 Subject: [PATCH 1/2] feat(types): add optional sol_price_usd to Payment --- src/types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types.ts b/src/types.ts index 88dc42f..a3f2a94 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,6 +16,12 @@ export interface Payment { status: PaymentStatus expires_at: string created_at: string + /** + * Live SOL/USD price the backend used at the time this payment object + * was returned. Cached server-side (30s TTL). Optional for + * compatibility with older backends that don't return it. + */ + sol_price_usd?: number } export interface FluxPayConfig { From 082e2c06f6406be36a805d0e12fcf3d553778f0d Mon Sep 17 00:00:00 2001 From: relayhop Date: Sun, 3 May 2026 18:01:34 +0800 Subject: [PATCH 2/2] feat(checkout): read sol_price_usd from payment, fallback to /api/price/sol --- src/CheckoutModal.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/CheckoutModal.tsx b/src/CheckoutModal.tsx index d5be593..ad51ec1 100644 --- a/src/CheckoutModal.tsx +++ b/src/CheckoutModal.tsx @@ -379,7 +379,7 @@ function CheckoutModal({ }) { const [tab, setTab] = useState<'wallet' | 'qr'>('wallet') const [copied, setCopied] = useState(false) - const [solPrice, setSolPrice] = useState(165) + const [solPrice, setSolPrice] = useState(null) const [selectedToken, setSelectedToken] = useState('SOL') const { payment, error } = usePaymentStatus(paymentId, config) const { display: timerDisplay } = useCountdown(payment?.expires_at ?? null) @@ -388,15 +388,23 @@ function CheckoutModal({ const isProcessing = ['detected', 'swapping'].includes(payment?.status ?? '') const showActions = !isTerminal && !isProcessing + // Prefer the price the backend already attached to the payment object + // (no extra HTTP roundtrip, and matches the price used at payment creation). + // Fall back to GET /api/price/sol once for backends that don't yet return it. useEffect(() => { + if (payment?.sol_price_usd && payment.sol_price_usd > 0) { + setSolPrice(payment.sol_price_usd) + return + } + if (solPrice != null) return fetch(`${config.apiUrl}/api/price/sol`, { headers: { 'x-api-key': config.apiKey } }) .then(r => r.json()) .then(d => { if (d.sol_usd) setSolPrice(d.sol_usd) }) .catch(() => { }) - }, []) + }, [payment?.sol_price_usd]) const amountUsdc = payment ? Number(payment.amount_usdc) : 0 - const amountSol = amountUsdc / solPrice + const amountSol = solPrice && solPrice > 0 ? amountUsdc / solPrice : 0 const copyAddress = () => { if (!payment?.deposit_address) return