Add proper deposit desk: multi-coin, clear custody model, DepositWidget

- /account/add-funds: full rebuild as Deposit Desk
  - 3-step how-it-works explainer: you send crypto → we hold it →
    you shop with USD credit → we pay vendors with your crypto
  - Coin selector: BTC (auto-verify), ETH (manual review), XMR (manual review)
  - Each coin shows deposit address from env var with copy button
  - BTC: immediate on-chain verify via /api/btc/verify (mempool.space)
  - ETH/XMR: submit txhash to /api/pending-deposit for manual review
  - Pending deposit history stored in localStorage with status
  - Important notes block explaining custody, no-withdrawal, confirmations
  - Back-links to checkout, wallets, dashboard

- /api/pending-deposit: new route to log ETH/XMR manual review requests
  (logs to console, ready to wire to DB/webhook)

- DepositWidget component: reusable compact + full variants
  - compact: balance + "Deposit crypto →" CTA (used in checkout)
  - full: balance + how-it-works summary (used in dashboard)

- Dashboard: replace balance section with DepositWidget + stats
- Checkout: add compact DepositWidget above CheckoutFlow, fix back link
  from "Sanctuary" → "Market", update feature cards to be accurate

- .env.example: document all three coin address env vars + optional
  payment processor URL

- .env.example: add NEXT_PUBLIC_ETH_ADDRESS, NEXT_PUBLIC_XMR_ADDRESS

Made-with: Cursor
This commit is contained in:
drjones
2026-04-16 01:03:14 -07:00
parent 2f928fbdc4
commit 348e55b63e
5 changed files with 596 additions and 143 deletions

View File

@@ -0,0 +1,85 @@
"use client";
import Link from "next/link";
import { useWallet } from "@/contexts/WalletContext";
import { useAccount } from "@/contexts/AccountContext";
import { isMerchantBtcConfigured } from "@/lib/merchantBtc";
type DepositWidgetProps = {
compact?: boolean;
};
export default function DepositWidget({ compact = false }: DepositWidgetProps) {
const { usdStoreCredit, luxCredits } = useWallet();
const { user } = useAccount();
const btcReady = isMerchantBtcConfigured();
if (compact) {
return (
<div className="glass rounded-2xl border border-neon-cyan/20 p-5">
<div className="flex items-center justify-between gap-4">
<div>
<div className="text-xs text-foreground/50">Your balance</div>
<div className="mt-0.5 font-orbitron text-2xl text-neon-cyan">
${usdStoreCredit.toFixed(2)}
<span className="ml-2 text-sm text-foreground/40">USD</span>
</div>
<div className="mt-0.5 text-xs text-neon-green">{luxCredits.toLocaleString()} LUX</div>
</div>
<Link
href="/account/add-funds"
className="shrink-0 rounded-xl bg-gradient-to-r from-neon-cyan to-neon-purple px-5 py-3 text-sm font-bold text-background hover:opacity-90"
>
Deposit crypto
</Link>
</div>
{!user && (
<p className="mt-3 text-xs text-amber-300/70">
<Link href="/sign-in?next=/account/add-funds" className="underline">Sign in</Link> to link deposits to your handle.
</p>
)}
{!btcReady && (
<p className="mt-2 text-[10px] text-amber-400/60">
Operator: set <code>NEXT_PUBLIC_MERCHANT_BTC_ADDRESS</code> in .env.local to enable deposits.
</p>
)}
</div>
);
}
return (
<div className="glass rounded-2xl border border-white/10 p-6">
<div className="mb-4 flex items-start justify-between gap-3">
<div>
<div className="text-xs text-foreground/50">Account balance</div>
<div className="mt-1 font-orbitron text-3xl text-neon-cyan">${usdStoreCredit.toFixed(2)}</div>
<div className="mt-0.5 text-sm text-neon-green">{luxCredits.toLocaleString()} LUX</div>
</div>
<Link
href="/account/add-funds"
className="rounded-xl bg-gradient-to-r from-neon-cyan to-neon-purple px-5 py-3 text-sm font-bold text-background hover:opacity-90"
>
Add crypto
</Link>
</div>
<div className="mt-4 rounded-xl border border-white/10 bg-black/30 p-4 text-sm">
<div className="mb-2 text-xs font-bold uppercase tracking-wider text-foreground/50">How it works</div>
<div className="space-y-2 text-foreground/70">
<div className="flex items-start gap-2">
<span className="mt-0.5 shrink-0 text-neon-cyan">1.</span>
<span>You send crypto to our receiving address.</span>
</div>
<div className="flex items-start gap-2">
<span className="mt-0.5 shrink-0 text-neon-cyan">2.</span>
<span>We verify on-chain and credit your account at market USD rate.</span>
</div>
<div className="flex items-start gap-2">
<span className="mt-0.5 shrink-0 text-neon-cyan">3.</span>
<span>You shop with USD credits. We hold the crypto and pay vendors on your behalf.</span>
</div>
</div>
</div>
</div>
);
}