# PayBridge — Developer Handoff

A multi-tenant payment-terminal & invoicing dashboard: smart gateway routing, gateway
auto-detection, card-on-file subscriptions, a custom invoice generator, and email
transaction alerts. This document is the single source of truth for a developer taking
the project forward.

> **Status:** working, clickable prototype. All business logic runs **client-side** against
> a seeded in-memory store persisted to `localStorage`. Payment capture and provider
> connections are **mocked** behind clean seams designed to be swapped for real SDKs.
> Email alerts are **real** (Gmail SMTP). See [What's real vs mock](#whats-real-vs-mock).

---

## 1. Stack

| | |
|---|---|
| Framework | **Next.js 16.4** (App Router, `cacheComponents: true`, Turbopack) |
| UI | **React 19.3**, **Tailwind CSS v4**, custom components, **lucide-react** icons |
| Language | **TypeScript** (strict) |
| State | React Context + `useReducer`-style `setState`, persisted to `localStorage` |
| Email | **nodemailer** (Gmail SMTP) with a Resend HTTP fallback |
| Payments (demo) | `@stripe/stripe-js`, PayPal JS SDK wired on the checkout; gateway charge is mocked |
| Node | 24.x (Node 20+ required) |

---

## 2. Run / build

```bash
npm install
npm run dev        # http://localhost:3000 (Turbopack)
npm run build      # production build — currently green, 24 routes
npm run start      # serve the production build
npm run lint       # eslint (see Known issues for the advisory warnings)
```

No database or external service is required to run locally. On first load the app
seeds demo data (`src/lib/seed.ts`) into `localStorage` under key `paybridge.v1`.
**Reset demo data:** call `resetDemo()` from the store, or clear that localStorage key.

---

## 3. Project map

```
src/
├ app/
│  ├ (app)/                 # authenticated dashboard (shares AppShell layout)
│  │  ├ page.tsx            # Dashboard (volume, donut, recent, mobile alert mock)
│  │  ├ payments/           # Invoices list (+ client filter) & /new create flow
│  │  ├ subscriptions/      # Cards on file, subscriptions, "Bill now", manual charge
│  │  ├ quotes/ expenses/ reports/        # Zoho-style invoicing extras
│  │  ├ brands/ gateways/ roles/ users/   # workspace config (+ /new, /connect)
│  │  └ alerts/            # Email alerts settings + delivery log
│  ├ api/
│  │  ├ notify/route.ts     # ← EMAIL SENDING (Gmail SMTP / Resend). REAL.
│  │  ├ connect/route.ts    # provider connect server seam (validate/oauth). MOCK-ish.
│  │  └ paypal/verify/route.ts
│  ├ pay/[linkId]/page.tsx  # hosted customer checkout (branded)
│  ├ invoice/[id]/page.tsx  # printable / PDF invoice (also the email CTA target)
│  ├ layout.tsx  globals.css
├ components/
│  ├ AppShell.tsx           # sidebar, topbar, notifications, toasts, providers
│  ├ ui.tsx                 # design system: Card, Button, Input, Badge, Toggle, …
│  ├ icons.tsx              # lucide wrapper — keeps the `Icon.Name` API
│  ├ BillNowDialog.tsx      # subscription charge popup (amount + merchant routing)
│  ├ InvoiceGenerator.tsx   # global slide-over: custom invoice w/ logo + CSV import
│  ├ CardBrandLogo.tsx      # inline SVG card-network marks (Visa/MC/Amex/…)
│  ├ PaymentBits.tsx  Donut.tsx  MobilePreview.tsx
└ lib/
   ├ store.tsx              # THE app state + all actions (single context)
   ├ types.ts               # all domain types
   ├ seed.ts                # demo data
   ├ cards.ts               # card tokenization (mock) + brand detection
   ├ currency.ts            # country→currency, formatMoney, currency lists
   ├ gateways/
   │  ├ routing.ts          # smart routing engine (scores eligible gateways)
   │  ├ adapters.ts         # ← MOCK charge(). Swap for real SDK calls.
   │  └ detect.ts           # detect provider from a pasted key/token
   └ providers/
      ├ registry.ts         # per-provider connect metadata (OAuth/api_key/keypair)
      └ connect.ts
```

---

## 4. Data model (`src/lib/types.ts`)

Core entities: **Brand** (a tenant's business identity), **Gateway** (a connected
processor account — called "merchant" in the UI), **Payment** (invoice/charge, with
optional `extras` for custom invoices and `recurring`), **PaymentMethod** (tokenized
card on file — token + brand + last4 + expiry only), **Subscription**, **Estimate**
(quote), **Expense**, **User**, **Role**, **AppNotification**, plus email types
(`EmailAlertSettings`, `EmailLogEntry`).

Everything lives in one store (`src/lib/store.tsx`) exposed via `useStore()`. State is
persisted to `localStorage` on every change and rehydrated on load.

---

## 5. What's real vs mock

| Area | Status | Where | To make real |
|---|---|---|---|
| **Gateway charge** | **MOCK** — approves/declines by `successRate` | `src/lib/gateways/adapters.ts` → `charge()` | Replace body with real Stripe/PayPal/Adyen/etc SDK calls per `gateway.type`. Signature returns `{status, processorRef, …}` — keep it. |
| **Smart routing** | Real logic (fees/success/latency), but scores mock health numbers | `src/lib/gateways/routing.ts` | Feed real gateway health/fees; logic stays. |
| **Provider connect** | Server seam exists; validation/oauth mostly simulated | `src/app/api/connect/route.ts`, `src/lib/providers/registry.ts` | Implement real OAuth exchange / key validation; registry already documents each provider's method. Needs platform app creds. |
| **Card tokenization** | **MOCK** — derives brand/last4, mints fake token, **discards PAN/CVV** | `src/lib/cards.ts` | Use processor hosted fields (Stripe.js etc). Never store PAN/CVV — current contract already enforces this. |
| **Email alerts** | **REAL** (Gmail SMTP; Resend fallback) | `src/app/api/notify/route.ts` | Production-ready. Just set env (below). |
| **Auth / users** | **MOCK** — seeded users, no real login | `src/lib/seed.ts`, `(app)/users`,`roles` | Add real auth (e.g. Clerk/Auth.js) + route protection. There is currently **no auth gate** on `(app)/*`. |
| **Persistence** | `localStorage` only (per-browser) | `src/lib/store.tsx` | Move to a real DB + API; store actions map 1:1 to endpoints. |

---

## 6. Environment variables

Local values live in `.env.local` (gitignored). For production, set the same keys in
the host (e.g. `vercel env add`).

```bash
# --- Email alerts (REQUIRED for email to send) ---
GMAIL_USER=alexparkerds@gmail.com        # sending Gmail account
GMAIL_APP_PASSWORD=                       # 16-char Google App Password (2-Step must be ON)
NOTIFY_DEFAULT_TO=alexparkerds@gmail.com  # default alert recipient
NOTIFY_FROM_NAME=PayBridge Alerts         # sender display name
# Alternative email backend (used only if GMAIL_* absent):
# RESEND_API_KEY=...        RESEND_FROM_EMAIL="PayBridge <you@yourdomain.com>"

# --- PayPal (checkout demo; use SANDBOX while testing) ---
PAYPAL_ENV=sandbox
PAYPAL_CLIENT_ID=...
PAYPAL_SECRET=...
NEXT_PUBLIC_PAYPAL_CLIENT_ID=...          # exposed to the browser SDK

# VERCEL_OIDC_TOKEN is auto-managed by `vercel link`; do not set by hand.
```

**Email backend selection** (`/api/notify`): Gmail if `GMAIL_USER` + `GMAIL_APP_PASSWORD`
are set → else Resend if `RESEND_API_KEY` → else "not configured" (logged, not sent).
`GET /api/notify` returns `{configured, provider, from, defaultTo, pendingGmail, connectedAccount}`.

---

## 7. Email alerts — how it works

- Every `pushNotification()` in the store also emails the alert recipient (respecting the
  per-event toggles on `/alerts`: success / failed / activity).
- `buildMeta(state, paymentId)` turns the related payment into the email's structured
  detail (amount, status pill, Invoice/Customer/Email/Brand/Merchant/Status/Date rows,
  and a **View invoice** CTA → `/invoice/{id}`).
- The CTA uses `window.location.origin`, so links resolve to the deployed URL automatically.
- Recipient defaults to the server-connected account if the admin hasn't set one.

**Gmail setup recap:** Google Account → Security → enable **2-Step Verification** →
**App passwords** → create 16-char code → put in `GMAIL_APP_PASSWORD` → restart.

---

## 8. Deployment (Vercel)

Project is already linked (`paybridge`, scope `asharibrashid-9611`). To deploy:

```bash
vercel                      # preview
vercel --prod               # production
```

Before/after first prod deploy, set env in the dashboard or CLI:

```bash
vercel env add GMAIL_USER production
vercel env add GMAIL_APP_PASSWORD production
vercel env add NOTIFY_DEFAULT_TO production
vercel env add NOTIFY_FROM_NAME production
# + PAYPAL_* if the checkout demo is used
```

**Custom domain:** add it in Vercel → Project → Domains, then create the CNAME/A records
it prints at your registrar. Use an identity that is genuinely yours (your own business
domain/subdomain) — do not deploy under a name that impersonates another organization.

---

## 9. Known issues / follow-ups

- **Lint:** a few advisory `react-hooks` warnings remain (hydration `setState` in
  `store.tsx`, `Date.now()` read during render in a couple of pages) plus two
  intentionally-unused `_`-prefixed params in `adapters.ts`. Non-behavioral; `next build`
  passes. Clean up if you want a zero-warning tree.
- **No auth gate** on `(app)/*` routes yet — add before any real deployment with real data.
- **`cacheComponents: true`** forbids `export const runtime` in route handlers (Node is the
  default runtime anyway). Keep that in mind when adding API routes.
- **Card row in emails:** emails show card brand/last4 in the summary sentence but not as a
  dedicated row (Payment records don't store the card). Add if desired.
- **PDF invoice** uses the browser print dialog (`window.print()`), not a server PDF renderer.

---

## 10. Key flows to know

- **Create invoice:** `(app)/payments/new` or the global **Invoice generator** slide-over
  (`InvoiceGenerator.tsx`) → `createInvoice()` → appears in Invoices with a pay link &
  printable invoice.
- **Hosted checkout:** `/pay/[linkId]` → tokenizes card (`cards.ts`) → `processPayLink()`
  routes & "charges" (mock) → if recurring, starts a subscription.
- **Subscriptions / Bill now:** `subscriptions/page.tsx` + `BillNowDialog.tsx` →
  `billSubscriptionNow(id, amount?, gatewayId?)`. Smart routing with failover, or pick a
  merchant manually (no failover).
- **Smart routing:** `routePayment()` ranks eligible gateways by success rate, fee, speed.
