No MacBook. No desk setup. No IDE.
Every line of code in PasteCheck was written on an Android phone using SPCK Editor, committed via GitHub mobile, and deployed to Vercel. That includes the free tier launch, the linting engine, and everything I'm about to describe.
This week I shipped Pro tier. Here's exactly how I built it.
What PasteCheck is (quick context)
PasteCheck is a mobile-optimised code linter. You paste code, it detects the language, highlights errors and warnings, and gives you tappable explanations with actionable fix hints. No setup. No signup required for the free tier.
It supports JavaScript, TypeScript, Python, HTML, and CSS. The linting runs entirely client-side no server round trips, no latency.
Pro tier adds three things: multi-file mode (up to 5 files at once), shareable check links (permanent URLs anyone can view), and saved collections (cross-device sync via Supabase).
The auth flow: magic links vs passwords
The first decision was authentication method. I initially set up Supabase magic links so user enters email, gets a link, clicks it, they're in.
It broke the user flow immediately. Every session meant going to your email, finding the link, clicking back to the app. For a tool people use repeatedly that's friction that compounds fast. I scrapped it and rebuilt with email and password.
The Supabase email + password flow in React looks straightforward on paper. In practice the callback routing caught me, the /auth/callback page needs to handle two completely different scenarios: email confirmation on signup (verifyOtp) and session restoration on login (getSession). Getting those two paths clean without one interfering with the other took a few iterations.
The confirmation email routes through Resend on a custom domain (noreply@pastecheck.co.uk). 3,000 emails a month free. That was a zero-cost decision that took about 20 minutes to configure.
Pro gating: keeping free users out of paid features
This was the most important piece to get right. The Pro gate connects three things: Stripe payment confirmation, a Supabase is_pro boolean on the user record, and localStorage for session persistence.
The flow on successful payment:
- Stripe redirects to
/success -
/successwrites the Pro licence to localStorage immediately (so the UI updates without a round trip) -
/successalso writesis_pro: trueto Supabase via a serverless function - On every subsequent mount, login, and page load the app re-syncs
is_profrom Supabase
The reason for both localStorage and Supabase is speed vs reliability. localStorage gives you instant UI response. Supabase gives you truth that persists across devices and survives a cleared browser. Neither alone is sufficient.
The gating itself is a check against both the local state and the synced Supabase value before rendering any Pro component. If either is missing or false, the upgrade prompt shows instead.
Stripe: test mode to live mode
I built the entire payment flow in Stripe test mode first. Fake card numbers, test webhooks, the full checkout session. Once it was working reliably I switched to live keys, hit the upgrade button myself, and watched the checkout load with a real cs_live_ session URL, no test banner, real payment form.
I didn't pay for my own subscription to verify it. I trusted that test mode behaviour maps cleanly to live mode, which it does, by design. The architecture doesn't change between modes. Only the keys change.
The Stripe integration runs through a Vercel serverless function (api/create-checkout.ts). It creates the checkout session server-side so the secret key never touches the client. Standard practice but worth stating explicitly if you're building this for the first time.
Multi-file mode
This was the most satisfying feature to build. The UI lets you add up to 5 named files, switch between them, and see per-file linting results alongside a summary bar showing total errors and warnings across all files.
The architecture is straightforward. each file is an entry in a React state array with its own name, content, and result set. The linter runs on each independently. The summary bar aggregates.
What makes it feel polished is the tab system. Each file gets a tab. The active tab highlights. Errors on inactive tabs show a red indicator so you can see at a glance which files need attention without switching to them.
Shareable check links
User hits share, a serverless function writes the current code and results to a Supabase shared_checks table, returns a unique ID, and the app constructs a /s/[id] URL. The share page is read-only — anyone with the link can see the code and results without an account.
The share page includes a CTA to the free tool for users who arrive without an account. Every shared link is a passive acquisition touchpoint.
What the stack looks like end to end
- React / TypeScript / Vite / Tailwind — client
- Vercel — hosting and serverless functions
- Supabase — database and auth
- Stripe — payments (£4/month)
- Resend — transactional email
- SPCK Editor on Android — where every line was written
Total infrastructure cost: £0 until revenue arrives. Vercel free tier, Supabase free tier, Resend free tier, Stripe takes a cut only on successful payments.
The Android constraint
I want to be direct about this because it changes how you think about the whole build.
Working from Android means no terminal. No local dev server. No hot reload in a browser you control. Every change is: edit in SPCK → commit via GitHub mobile → wait for Vercel to deploy → test in mobile Chrome.
The feedback loop is longer. But it forces discipline. You don't make speculative changes because every change costs a deploy cycle. You think the edit through before you make it. You read the code more carefully because you can't run it locally to check.
I'm not claiming it's better. I'm saying it's a real constraint that produces a specific way of working and that way of working built a production app with live payments in a few weeks from nothing.
Where it is now
PasteCheck is live at pastecheck.co.uk. Free tier is fully functional with no account required. Pro is £4/month.
If you paste code regularly for debugging, for learning, for checking before committing give it a try. The mobile experience in particular is something most linters don't prioritise.
If you're building something solo under constraints, I'd genuinely like to hear what your setup looks like. The Android-only workflow is uncommon enough that I rarely find other people doing it.
Built solo. Deployed from a phone. Open for business.
Top comments (1)
The line that jumped out: "every shared link is a passive acquisition touchpoint." That's the whole growth engine hiding inside a feature list — your Pro users are doing your distribution for you, for free, every time they share a check.
Which raises the one tension I'd poke at: shareable links are simultaneously a Pro feature AND your acquisition loop. Gating them to Pro protects revenue but throttles the exact mechanism that brings in new free users. Multi-file and cross-device sync are clean "power-user pays" value — but I'd at least test leaving share free (or partially free), because a share that converts a stranger is worth more than the £4 it's locked behind.
Genuinely impressed it's all from an Android phone — the "every change costs a deploy cycle, so you think before you edit" discipline is real. What's actually driving the £4 conversions so far: multi-file, sync, or the share links?