# App Proxy backend — Vercel

Serverless functions behind a Shopify App Proxy. Persists per-bottle selections to the
customer metafield (`wineclub.selection`), creates one-time extras charges, and runs the
daily reminder + fulfillment cron. Same verified logic as the Cloudflare version; ported
to Vercel/Node.

```
api/
  save.js     POST /apps/wineclub/save     persist selection (validate + reprice)
  charge.js   POST /apps/wineclub/charge   one-time draft-order invoice for extras
  upgrade.js  POST /apps/wineclub/upgrade  tier/preference bundle swap (Appstle)
  cron.js     GET  /api/cron               daily reminders + fulfillment (Vercel Cron)
lib/
  shopify.js  Admin API helpers
  verify.js   App Proxy HMAC verification
  email.js    reminder + receipt emails (Resend)
vercel.json   cron schedule (08:00 UTC daily)
```

## Deploy

```bash
cd app-proxy-vercel
npm i -g vercel
vercel login
vercel --prod          # first run links/creates the project, then deploys
```

Set environment variables (Vercel dashboard → Project → Settings → Environment Variables,
or `vercel env add`):

| Variable | Value |
|----------|-------|
| `SHOPIFY_SHOP` | `grs60a-y0.myshopify.com` |
| `SHOPIFY_ADMIN_TOKEN` | the offline Admin API token (`shpat_…`) |
| `SHOPIFY_APP_PROXY_SECRET` | the app's client secret (`shpss_…`) — must match the app whose App Proxy you configure |
| `CRON_SECRET` | any random string (protects `/api/cron`) |
| `STORE_URL` | `https://www.maisonfayard.com` |
| `RESEND_API_KEY` | (optional) transactional email |
| `FROM_EMAIL` | (optional) e.g. `Maison Fayard <club@maisonfayard.com>` |
| `APPSTLE_API_KEY` | (optional) for the tier-swap route |

## Shopify App Proxy config (in the app settings)

- Subpath prefix: `apps`
- Subpath: `wineclub`
- Proxy URL: `https://<your-vercel-app>.vercel.app/api`

Shopify then forwards `/apps/wineclub/save` → `…/api/save` (signed). The theme section's
**App Proxy base path** setting must be `/apps/wineclub` (the default).

## Security
- Every request's App Proxy HMAC is verified (`lib/verify.js`).
- Customer identity comes from Shopify's signed `logged_in_customer_id`, never the body.
- Selections are re-validated against the tier pool and re-priced from live variants —
  a tampered client payload can't inject an off-pool wine or a fake price.
