# 🚀 Deploying ViVest on Render (Free Tier)

All 7 Render services (3 web + 4 cron) are defined in `render.yaml` and deploy with one click.

---

## How the free tier works

Render free web services **sleep after 15 minutes** of no traffic. This would break cron jobs if they ran inside the server. Instead:

- **Cron logic lives in HTTP endpoints** (`/cron/*`) on the API.
- **Render Cron Job services** (also free) hit those endpoints on schedule.
- A **keep-alive cron** pings `/health` every 14 minutes during business hours so the API stays warm while users are active.

```
Render Cron (free)          Render Web (free)
─────────────────           ─────────────────
dsl-keep-alive   ──ping──►  dsl-api  /health
dsl-cron-auto-debit ──────► dsl-api  /cron/auto-debit
dsl-cron-retry ───────────► dsl-api  /cron/retry-debits
dsl-cron-maturity ────────► dsl-api  /cron/maturity-check
```

---

## Step 1 — Set up Supabase

1. Create a project at [supabase.com](https://supabase.com) (free).
2. Go to **SQL Editor** → paste and run `backend/supabase/migrations/001_initial_schema.sql`.
3. Keep this tab open — you'll need the URL and keys in Step 4.

---

## Step 2 — Push to GitHub

```bash
cd december-savings
git init
git add .
git commit -m "Initial commit"

# Create a new repo on GitHub, then:
git remote add origin https://github.com/YOUR_USERNAME/december-savings.git
git branch -M main
git push -u origin main
```

---

## Step 3 — Deploy Blueprint on Render

1. Go to [render.com](https://render.com) → sign up/in (free).
2. Click **New → Blueprint**.
3. Connect GitHub and select your `december-savings` repo.
4. Render finds `render.yaml` and lists all 7 services.
5. Name it (e.g. `december-savings`) and click **Apply**.

First builds take 4–6 minutes each. The 3 web services build in parallel; cron services share the same build so they're fast.

---

## Step 4 — Add Secret Environment Variables

After the Blueprint applies, several env vars are marked `sync: false` — Render created the slot but left the value empty. Fill them in now.

### dsl-api → Environment tab

| Variable | Where to find it |
|----------|-----------------|
| `SUPABASE_URL` | Supabase → Project Settings → API → Project URL |
| `SUPABASE_ANON_KEY` | Supabase → Project Settings → API → anon public |
| `SUPABASE_SERVICE_ROLE_KEY` | Supabase → Project Settings → API → service_role (**keep secret**) |
| `PAYSTACK_SECRET_KEY` | Paystack Dashboard → Settings → API Keys & Webhooks |
| `PAYSTACK_PUBLIC_KEY` | Paystack Dashboard → Settings → API Keys & Webhooks |
| `PAYSTACK_WEBHOOK_SECRET` | Make up any long random string — you'll paste it into Paystack too |
| `TWILIO_ACCOUNT_SID` | Twilio Console → Account Info |
| `TWILIO_AUTH_TOKEN` | Twilio Console → Account Info |
| `TWILIO_PHONE_NUMBER` | Twilio Console → Phone Numbers (format: `+2348012345678`) |
| `SMTP_HOST` | Your email provider (e.g. `smtp.gmail.com`) |
| `SMTP_USER` | Your sending email address |
| `SMTP_PASS` | Your SMTP password or Gmail App Password |

> `JWT_SECRET` and `CRON_SECRET` are auto-generated by Render — you don't need to set those.

### dsl-api → Get the CRON_SECRET value

After saving the above vars, click into `CRON_SECRET` in `dsl-api` → **Reveal** and copy the value.

### dsl-cron-auto-debit, dsl-cron-retry-debits, dsl-cron-maturity → Environment tab

Paste the copied `CRON_SECRET` value into each of the 3 cron services.

### dsl-frontend → Environment tab

| Variable | Value |
|----------|-------|
| `NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY` | Your Paystack public key (`pk_live_...`) |
| `NEXT_PUBLIC_SUPABASE_URL` | Same as `SUPABASE_URL` |
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Same as `SUPABASE_ANON_KEY` |

> ⚠️ `NEXT_PUBLIC_*` variables are baked into the Next.js build. After adding them, go to **dsl-frontend → Manual Deploy → Deploy latest commit** to rebuild.

---

## Step 5 — Update CORS URLs

Your real Render URLs look like `https://dsl-api.onrender.com`. Update these in **dsl-api → Environment**:

| Variable | Value |
|----------|-------|
| `FRONTEND_URL` | `https://dsl-frontend.onrender.com` |
| `ADMIN_URL` | `https://dsl-admin.onrender.com` |

Then trigger a manual redeploy of `dsl-api`.

---

## Step 6 — Configure Paystack Webhook

1. Go to **Paystack Dashboard → Settings → API Keys & Webhooks**
2. Set **Webhook URL** to:
   ```
   https://dsl-api.onrender.com/api/v1/payments/webhook/paystack
   ```
3. The **Webhook Secret** must match the `PAYSTACK_WEBHOOK_SECRET` you set in Step 4.

---

## Step 7 — Create your Admin Account

1. Register through the frontend: `https://dsl-frontend.onrender.com/auth/register`
2. In Supabase → SQL Editor, run:
   ```sql
   UPDATE users SET role = 'super_admin' WHERE phone = '0801234xxxx';
   ```
3. Sign in at `https://dsl-admin.onrender.com`

---

## Step 8 — Verify Everything

| Check | URL | Expected |
|-------|-----|----------|
| API health | `https://dsl-api.onrender.com/health` | `{"status":"ok"}` |
| Frontend | `https://dsl-frontend.onrender.com` | Login page loads |
| Admin | `https://dsl-admin.onrender.com` | Admin login page loads |
| Cron logs | Render Dashboard → dsl-cron-auto-debit → Logs | Shows run history |

---

## Free Tier Limits

| Resource | Free Limit | Notes |
|----------|-----------|-------|
| Web services | 3 free | We use exactly 3 |
| Cron jobs | 4 free | We use exactly 4 |
| Build minutes | 500/month | ~6 min per deploy × 7 services = ~42 min per deploy |
| Bandwidth | 100 GB/month | Plenty for early stage |
| Sleep after inactivity | 15 minutes | Handled by keep-alive cron |

---

## Troubleshooting

**"CRON_SECRET not configured" error in logs**
You need to copy the auto-generated `CRON_SECRET` from `dsl-api` and paste it into each of the 3 cron job services.

**OTP not arriving**
Check `dsl-api` logs → Logs tab. In production Twilio must be configured. Verify `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_PHONE_NUMBER` are all set correctly and your Twilio account has credit.

**Frontend shows blank page / API errors**
The `NEXT_PUBLIC_*` vars weren't set before the build. Add them in `dsl-frontend → Environment` then do a **Manual Deploy**.

**Paystack webhook returns 401**
The `PAYSTACK_WEBHOOK_SECRET` in Render must exactly match the secret you entered in the Paystack dashboard.

**Cron job says "fetch is not defined"**
Your Node version may be below 18 (fetch is built-in from Node 18+). The `engines` field in `package.json` pins Node 20 — make sure Render is picking it up. In the service settings check the Node version shown in the build log.

**API takes 10–30 seconds to respond (cold start)**
This is normal on the free tier when the API has been sleeping. The keep-alive cron minimises this during business hours, but the first request after a long quiet period will be slow. This is the main trade-off of the free tier.

---

## Custom Domains (optional, free on Render)

In each service → **Settings → Custom Domains**:

- `api.yourdomain.ng` → `dsl-api`
- `app.yourdomain.ng` → `dsl-frontend`
- `admin.yourdomain.ng` → `dsl-admin`

Render provisions SSL automatically. After adding domains, update `FRONTEND_URL`, `ADMIN_URL`, and `NEXT_PUBLIC_API_URL` to use your custom domains, then redeploy.
