From b5f1e5540b8711bcd8b7f875f4835a8f6922517b Mon Sep 17 00:00:00 2001 From: Mannu Date: Sat, 6 Jun 2026 13:39:57 +0530 Subject: [PATCH] feat(billing): brand checkout + auto-prefill user details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1 Checkout branding (within Razorpay's hosted-UI limits): - image: /icons/192.png — Tia logo in the checkout header - theme color #fb7185 (Tia rose, matches app theme-color) — was terracotta - Note: Razorpay Checkout is their hosted UI; logo + brand color + name are the only customisable bits. Fonts/layout cannot be changed (platform limit). #2 Auto-prefill name/email/phone: - UpgradeButton now fetches /api/auth/profile on mount and passes prefill {name, email, contact} to Razorpay. User can still edit in checkout. - Saves manual entry; uses the phone we now collect. (#3 "seller doesn't support recurring payments" is a Razorpay ACCOUNT setting, not code — needs subscriptions enabled on the account. Handled separately.) Co-Authored-By: Claude Opus 4.8 --- src/components/UpgradeButton.tsx | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/UpgradeButton.tsx b/src/components/UpgradeButton.tsx index 9015b25..d4df042 100644 --- a/src/components/UpgradeButton.tsx +++ b/src/components/UpgradeButton.tsx @@ -8,8 +8,10 @@ interface RazorpayOptions { subscription_id: string; name: string; description: string; + image?: string; // brand logo shown in the checkout header handler: (resp: RazorpayResponse) => void; - prefill?: { email?: string; contact?: string }; + prefill?: { name?: string; email?: string; contact?: string }; + notes?: Record; theme?: { color?: string }; modal?: { ondismiss?: () => void }; } @@ -73,9 +75,19 @@ export function UpgradeButton({ const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [done, setDone] = useState(false); + // Auto-prefill from the signed-in user's profile (editable in checkout). + const [profile, setProfile] = useState<{ name?: string; email?: string; phone?: string }>({}); - // Warm the checkout script so the first click is instant. - useEffect(() => { loadCheckout(); }, []); + // Warm the checkout script + load profile so the first click is instant. + useEffect(() => { + loadCheckout(); + fetch("/api/auth/profile", { credentials: "include" }) + .then((r) => r.json()) + .then((d) => { + if (d.user) setProfile({ name: d.user.name, email: d.user.email, phone: d.user.phone || undefined }); + }) + .catch(() => {}); + }, []); const handleUpgrade = async () => { setLoading(true); @@ -91,14 +103,23 @@ export function UpgradeButton({ const { subscriptionId, keyId } = createData as { subscriptionId: string; keyId: string }; + // Build prefill from the explicit prop (if any) + the loaded profile. + // Razorpay shows these pre-filled but the user can still edit them. + const prefill: { name?: string; email?: string; contact?: string } = {}; + const prefillEmail = email || profile.email; + if (profile.name) prefill.name = profile.name; + if (prefillEmail) prefill.email = prefillEmail; + if (profile.phone) prefill.contact = profile.phone; + // 2. Open Razorpay Checkout for the mandate. const rzp = new window.Razorpay({ key: keyId, // id only — never the secret subscription_id: subscriptionId, name: "Tia", description: "Tia Premium — ₹199/month", - prefill: email ? { email } : undefined, - theme: { color: "#C26B4E" }, // heirloom terracotta + image: `${window.location.origin}/icons/192.png`, // brand logo in checkout header + prefill: Object.keys(prefill).length ? prefill : undefined, + theme: { color: "#fb7185" }, // Tia rose (matches app theme-color) handler: async (resp) => { // 3. Verify for UX feedback only — entitlement comes via webhook. try {