From 1891387b28dbb005cbc551215c12f5b0f6e1fee2 Mon Sep 17 00:00:00 2001 From: Tom Smith <142233216+tomsmith8@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:39:26 +0100 Subject: [PATCH] Fix L402 payment flow for non-admin users - Remove 402 auto-retry from api.ts that intercepted /buy_lsat responses and retried with stale tokens causing 500s - Attach L402 token upfront on every request if available, matching how nav-fiber handled authenticated requests - Fix Sphinx top-up to call payL402() instead of getL402() --- src/components/modals/budget-modal.tsx | 18 +++++++--------- src/lib/api.ts | 29 +++++++++----------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/components/modals/budget-modal.tsx b/src/components/modals/budget-modal.tsx index 5afa87b5..c82f2146 100644 --- a/src/components/modals/budget-modal.tsx +++ b/src/components/modals/budget-modal.tsx @@ -70,22 +70,20 @@ export function BudgetModal() { // Route "Top Up" to the right flow const handleTopUp = useCallback(async () => { if (sphinxConnected) { - // Sphinx: trigger directly + // Sphinx: buy L402 via setBudget → authorize → saveLsat flow setLoading(true) setError("") try { - localStorage.removeItem("l402") + await payL402(setBudget) const l402 = await getL402() - if (!l402) { - setError("Payment was not completed.") - return + if (l402) { + const balance = await api.get<{ balance: number }>("/balance", { + Authorization: l402, + }) + setBudget(balance.balance) } - const balance = await api.get<{ balance: number }>("/balance", { - Authorization: l402, - }) - setBudget(balance.balance) } catch { - setError("Failed to process payment. Try again.") + setError("Payment was cancelled or failed. Try again.") } finally { setLoading(false) } diff --git a/src/lib/api.ts b/src/lib/api.ts index b9ecd6ab..a57f7fb9 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -40,32 +40,23 @@ async function request( parsed.searchParams.append("msg", signed.message) } - const response = await fetch(parsed.toString(), { - ...config, - signal: signal ?? new AbortController().signal, - }) - - // Handle 402 Payment Required — get L402 token and retry - if (response.status === 402) { + // Attach L402 token upfront if available (unless caller set Authorization) + const existingHeaders = config?.headers as Record | undefined + if (!existingHeaders?.Authorization) { const l402 = await getL402() if (l402) { - const retryResponse = await fetch(parsed.toString(), { + config = { ...config, - headers: { - ...config?.headers, - Authorization: l402, - }, - signal: signal ?? new AbortController().signal, - }) - - if (!retryResponse.ok) { - throw retryResponse + headers: { ...existingHeaders, Authorization: l402 }, } - - return retryResponse.json() } } + const response = await fetch(parsed.toString(), { + ...config, + signal: signal ?? new AbortController().signal, + }) + if (!response.ok) { throw response }