251 lines
6.9 KiB
TypeScript
251 lines
6.9 KiB
TypeScript
// app/page.tsx
|
||
// This page doubles as:
|
||
// 1. A landing page
|
||
// 2. A product spec
|
||
// 3. A reminder to future-me what the hell I was building
|
||
//
|
||
// If you’re reading this months later: hi 👋
|
||
// The product is the automation, not the UI.
|
||
|
||
export default function Home() {
|
||
return (
|
||
<main className="max-w-3xl mx-auto p-8 space-y-16">
|
||
|
||
{/* --------------------------------------------------
|
||
Intro
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h1 className="text-2xl font-semibold">
|
||
Stripe → Xero automation
|
||
</h1>
|
||
|
||
<p className="mt-2 text-gray-600">
|
||
Automatically create and mark Xero invoices as paid when a Stripe payment succeeds.
|
||
<br />
|
||
Built for people who value time more than pressing buttons.
|
||
</p>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
High-level flow (human readable)
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h2 className="text-xl font-medium">How it works (high level)</h2>
|
||
|
||
<ol className="mt-4 space-y-3 list-decimal list-inside text-gray-700">
|
||
<li>Log in via magic link (passwordless)</li>
|
||
<li>Connect your Stripe account</li>
|
||
<li>Connect your Xero organisation</li>
|
||
<li>A Stripe payment succeeds</li>
|
||
<li>An invoice appears in Xero as paid</li>
|
||
</ol>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
Magic link auth – detailed flow
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h2 className="text-xl font-medium">Login flow (magic link)</h2>
|
||
|
||
<p className="mt-2 text-gray-600">
|
||
Authentication is passwordless. We only store intent and proof of login.
|
||
</p>
|
||
|
||
{/* Text-based flow diagram (easy to read + copy) */}
|
||
<pre className="mt-4 p-4 bg-gray-50 border rounded text-sm overflow-x-auto">
|
||
{`Browser
|
||
|
|
||
| POST /auth/login (email)
|
||
v
|
||
Backend
|
||
- find or create user
|
||
- generate token
|
||
- hash token
|
||
- store login_tokens row
|
||
- send email (SES)
|
||
|
|
||
v
|
||
Email (magic link)
|
||
|
|
||
| GET /auth/callback?token=XYZ
|
||
v
|
||
Backend
|
||
- hash token
|
||
- validate token (unused + not expired)
|
||
- mark token as used
|
||
- create session
|
||
|
|
||
v
|
||
Set session cookie
|
||
`}
|
||
</pre>
|
||
|
||
{/* Step-by-step breakdown */}
|
||
<ol className="mt-6 space-y-4 list-decimal list-inside text-gray-700">
|
||
<li>
|
||
User enters their email address.
|
||
</li>
|
||
|
||
<li>
|
||
Backend creates (or finds) a user record and stores a one-time login token
|
||
in <code className="px-1 bg-gray-100 rounded">login_tokens</code>.
|
||
</li>
|
||
|
||
<li>
|
||
An email is sent containing a short-lived magic link.
|
||
</li>
|
||
|
||
<li>
|
||
When the link is clicked, the token is validated, marked as used,
|
||
and a session is created.
|
||
</li>
|
||
|
||
<li>
|
||
A secure session cookie is set. No passwords. No OAuth popups.
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
Stripe → Xero automation flow
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h2 className="text-xl font-medium">Stripe → Xero automation flow</h2>
|
||
|
||
<pre className="mt-4 p-4 bg-gray-50 border rounded text-sm overflow-x-auto">
|
||
{`Stripe payment succeeds
|
||
|
|
||
| Webhook
|
||
v
|
||
Backend
|
||
- verify Stripe event
|
||
- map payment to customer
|
||
- create Xero invoice
|
||
- mark invoice as paid
|
||
|
|
||
v
|
||
Xero (reconciled automatically)
|
||
`}
|
||
</pre>
|
||
|
||
<p className="mt-4 text-gray-600">
|
||
Once connected, everything runs automatically.
|
||
No manual reconciliation. No “awaiting payment” state.
|
||
</p>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
Proof
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h2 className="text-xl font-medium">Proof, not promises</h2>
|
||
|
||
<p className="mt-2 text-gray-600">
|
||
Your next Stripe payment will automatically reconcile in Xero.
|
||
<br />
|
||
No manual matching. No bookkeeping busywork.
|
||
</p>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
Pricing
|
||
-------------------------------------------------- */}
|
||
<section>
|
||
<h2 className="text-xl font-medium">Pricing</h2>
|
||
|
||
<p className="mt-2 text-gray-700">
|
||
£200 / month — unlimited invoices.
|
||
</p>
|
||
</section>
|
||
|
||
{/* --------------------------------------------------
|
||
Footer / reminder
|
||
-------------------------------------------------- */}
|
||
<section className="pt-8 border-t">
|
||
<p className="text-gray-500 text-sm">
|
||
This page is intentionally simple.
|
||
<br />
|
||
The product is the automation, not the UI.
|
||
</p>
|
||
</section>
|
||
|
||
|
||
<section>
|
||
<h2 className="text-xl font-medium">Implementation notes (for future me)</h2>
|
||
|
||
<p className="mt-2 text-gray-600">
|
||
These are the only docs needed to implement magic-link auth with Next.js + AWS SES.
|
||
</p>
|
||
|
||
<ul className="mt-4 space-y-2 list-disc list-inside text-gray-700">
|
||
<li>
|
||
Next.js Route Handlers (auth endpoints):{" "}
|
||
<a
|
||
href="https://nextjs.org/docs/app/building-your-application/routing/route-handlers"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
nextjs.org/docs/app/.../route-handlers
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
Next.js Server Actions (optional):{" "}
|
||
<a
|
||
href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
nextjs.org/docs/app/.../server-actions
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
AWS SES – sending email (Node.js):{" "}
|
||
<a
|
||
href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/sesv2/"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
AWS SDK SESv2
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
AWS SES sandbox → production access:{" "}
|
||
<a
|
||
href="https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
Request production access
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
Node.js crypto (token generation + hashing):{" "}
|
||
<a
|
||
href="https://nodejs.org/api/crypto.html"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
nodejs.org/api/crypto
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
Cookies & sessions:{" "}
|
||
<a
|
||
href="https://nextjs.org/docs/app/api-reference/functions/cookies"
|
||
className="text-blue-600 underline"
|
||
target="_blank"
|
||
>
|
||
Next.js cookies API
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
</main>
|
||
)
|
||
}
|