From 4bb8e394ef0cb74bac497d66a9375ea315a37160 Mon Sep 17 00:00:00 2001 From: nate Date: Sat, 11 Apr 2026 03:18:12 +0400 Subject: [PATCH] improve checkout --- apps/web/src/routes/dashboard.ts | 13 +++++++++++++ apps/web/src/views/checkout.ejs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/web/src/routes/dashboard.ts b/apps/web/src/routes/dashboard.ts index c217d53..cacda2f 100644 --- a/apps/web/src/routes/dashboard.ts +++ b/apps/web/src/routes/dashboard.ts @@ -359,6 +359,19 @@ export const dashboard = new Elysia() return html("checkout", { nav: "settings", account: acc, payApi, invoiceId: params.id, coins, invoice }); }) + .get("/dashboard/checkout/:id/status", async ({ cookie, headers, params, set }) => { + const resolved = await getAccountId(cookie, headers); + if (!resolved?.accountId) { set.status = 401; return { error: "Unauthorized" }; } + const payApi = process.env.PAY_API || "https://pay.pingql.com"; + const key = cookie?.pingql_key?.value; + try { + const res = await fetch(`${payApi}/checkout/${params.id}`, { headers: { "Authorization": `Bearer ${key}` } }); + if (!res.ok) { set.status = res.status; return { error: "Not found" }; } + const data = await res.json(); + return { status: data.status, amount_received: data.amount_received, amount_crypto: data.amount_crypto }; + } catch { set.status = 500; return { error: "Could not fetch status" }; } + }) + .get("/dashboard/checkout/:id/receipt", async ({ cookie, headers, params, set }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); diff --git a/apps/web/src/views/checkout.ejs b/apps/web/src/views/checkout.ejs index 2f22a67..52b3084 100644 --- a/apps/web/src/views/checkout.ejs +++ b/apps/web/src/views/checkout.ejs @@ -186,10 +186,10 @@ %> <% if (isPending) { %> - + <% } %> -
+
@@ -229,7 +229,7 @@
-
+
<%= received.toFixed(8) %> received @@ -323,6 +323,32 @@ if (btn) { btn.textContent = 'Copied'; setTimeout(() => btn.textContent = 'Copy', 1500); } } + // Poll payment status instead of meta refresh + (function() { + const m = location.pathname.match(/\/dashboard\/checkout\/([^/]+)$/); + if (!m) return; + const id = m[1]; + let lastStatus = document.querySelector('[data-pay-status]')?.dataset.payStatus; + if (!lastStatus || lastStatus === 'paid' || lastStatus === 'expired') return; + + async function poll() { + try { + const res = await fetch('/dashboard/checkout/' + id + '/status', { credentials: 'same-origin' }); + if (!res.ok) return; + const data = await res.json(); + if (data.status !== lastStatus) { location.reload(); return; } + // Update progress bar in-place for partial payments + const received = parseFloat(data.amount_received || '0'); + const total = parseFloat(data.amount_crypto || '1'); + const pct = Math.min(100, Math.round((received / total) * 100)); + const bar = document.querySelector('[data-progress-bar]'); + if (bar) bar.style.width = pct + '%'; + } catch {} + setTimeout(poll, 1500); + } + setTimeout(poll, 1500); + })(); + const multipliers = { pro: 1, pro2x: 2, pro4x: 4 }; const lifetimePlans = { lifetime: 'lifetime', lifetime2x: 'lifetime2x', lifetime4x: 'lifetime4x' }; const planValue = document.getElementById('plan-value');