diff --git a/apps/pay/src/routes.ts b/apps/pay/src/routes.ts index 3acc289..5430bb2 100644 --- a/apps/pay/src/routes.ts +++ b/apps/pay/src/routes.ts @@ -74,7 +74,14 @@ export const routes = new Elysia() // Calculate amount const planDef = PLANS[plan]; if (!planDef) { set.status = 400; return { error: `Unknown plan: ${plan}` }; } - const amountUsd = planDef.priceUsd ?? (planDef.monthlyUsd! * (months ?? 1)); + let amountUsd = planDef.priceUsd ?? (planDef.monthlyUsd! * (months ?? 1)); + + // Lifetime discount: credit up to 50% of lifetime price from previous payments + if (plan === "lifetime" && planDef.priceUsd) { + const [{ total }] = await sql`SELECT COALESCE(SUM(amount_usd), 0)::numeric as total FROM payments WHERE account_id = ${accountId} AND status = 'paid'`; + const credit = Math.min(Number(total), planDef.priceUsd * 0.5); + amountUsd = Math.max(amountUsd - credit, 1); + } const rates = await getExchangeRates(); const rate = rates[coin]; if (!rate) { set.status = 500; return { error: "Could not fetch exchange rate" }; } diff --git a/apps/web/src/routes/dashboard.ts b/apps/web/src/routes/dashboard.ts index 3f0237b..90a2ca3 100644 --- a/apps/web/src/routes/dashboard.ts +++ b/apps/web/src/routes/dashboard.ts @@ -273,6 +273,9 @@ export const dashboard = new Elysia() const [acc] = await sql`SELECT plan, plan_expires_at FROM accounts WHERE id = ${resolved.accountId}`; if (acc.plan === "lifetime") return redirect("/dashboard/settings"); + // Total spent on paid invoices (for lifetime discount) + const [{ total_spent }] = await sql`SELECT COALESCE(SUM(amount_usd), 0)::numeric as total_spent FROM payments WHERE account_id = ${resolved.accountId} AND status = 'paid'`; + // Fetch coins server-side for no-JS rendering const payApi = process.env.PAY_API || "https://pay.pingql.com"; let coins: any[] = []; @@ -282,7 +285,7 @@ export const dashboard = new Elysia() coins = data.coins || []; } catch {} - return html("checkout", { nav: "settings", account: acc, payApi, invoiceId: null, coins, invoice: null }); + return html("checkout", { nav: "settings", account: acc, payApi, invoiceId: null, coins, invoice: null, totalSpent: Number(total_spent) }); }) // Existing invoice by ID — SSR the payment status diff --git a/apps/web/src/views/checkout.ejs b/apps/web/src/views/checkout.ejs index 569aa14..f5c48fa 100644 --- a/apps/web/src/views/checkout.ejs +++ b/apps/web/src/views/checkout.ejs @@ -6,6 +6,10 @@ const coins = it.coins || []; const invoice = it.invoice; const payApi = it.payApi || ''; + const totalSpent = it.totalSpent || 0; + const lifetimeBase = 140; + const lifetimeDiscount = Math.min(totalSpent, lifetimeBase * 0.5); + const lifetimePrice = lifetimeBase - lifetimeDiscount; %>