# Maison Fayard — Wine Club Customization System

A cycle-based customization layer on top of **Appstle Subscriptions** for the Maison Fayard
wine club. Appstle already handles recurring billing; this system adds per-shipment
customization (swap / add / upgrade), one-time charges for extras, automated reminder
email, and cycle-close fulfillment generation.

Live club page: https://www.maisonfayard.com/pages/wine-club

## Membership tiers

| Tier          | Price/shipment | Cadence          | Bottles |
|---------------|----------------|------------------|---------|
| Classic 3btl  | $150           | Quarterly (4x)   | 3       |
| Classic 6btl  | $300           | Quarterly (4x)   | 6       |
| Reserve 3btl  | $400           | Quarterly (4x)   | 3       |
| Reserve 6btl  | $800           | Quarterly (4x)   | 6       |
| Magnum        | $1,300         | Bi-annual (2x)   | 3 magnums |

## Locked design decisions

1. **Backend** — one thin serverless function behind a Shopify **App Proxy**
   (Cloudflare Worker). Handles the 3 operations that cannot run client-side:
   save selection, create one-time charge, tier-swap in Appstle. Everything else is
   Metaobjects + Liquid + JS + Shopify Flow.
2. **"Add bottles"** — **one-time, this shipment only**. A one-time charge for the
   current cycle; the subscription tier/renewal is unchanged. (Permanent tier change is a
   separate "Switch tier" action.)
3. **Pricing** — read **live Shopify variant prices** at render time. Admin maintains
   price once, in Shopify. No per-cycle price double-entry.
4. **Swap pool** — **curated per-tier pool per cycle**. Admin assigns an approved wine
   list to each tier for each cycle ("assign a product pool per membership tier").

## Architecture

```
Metaobjects          cycle config, per-tier pools, defaults, dates
Liquid + JS section  the customization portal (theme, storefront)
App Proxy Worker      save selection · create charge · swap Appstle plan  (authenticated writes)
Shopify Flow          reminder email @ (close − 10d) · fulfillment @ close · fallback
Customer metafield    saved per-cycle selection + fulfillment status
```

See [docs/architecture.md](docs/architecture.md) and [docs/data-model.md](docs/data-model.md).

## Repository layout

```
maison-fayard-wineclub/
├── README.md
├── DEPLOYMENT.md                go-live checklist + what's needed from the client
├── docs/
│   ├── architecture.md          system design + data flow
│   ├── data-model.md            metaobject + metafield reference
│   └── conventions.md           tags, handles, tier keys, order tags, lifecycle
├── metaobjects/
│   ├── definitions.graphql      metaobjectDefinitionCreate mutations
│   └── seed-q2-2026.md          example cycle + pool seed data
├── theme/
│   ├── sections/
│   │   └── wine-club-customize.liquid
│   ├── snippets/
│   │   └── wine-club-customize-data.liquid   server→client data contract
│   ├── assets/
│   │   ├── wine-club-customize.js
│   │   └── wine-club-customize.css
│   └── preview.html             open in a browser for a local visual check
├── app-proxy/
│   ├── worker.js                Cloudflare Worker: App Proxy routes + cron scheduler
│   ├── wrangler.toml            secrets + daily cron trigger
│   └── README.md
├── emails/
│   ├── README.md
│   ├── reminder.preview.html
│   └── receipt.preview.html
└── flows/
    ├── 01-reminder-email.md
    ├── 02-fulfillment-generation.md
    └── 03-fallback.md
```

## Build phases

- **Phase 1** — Metaobject config + customization portal wired to real per-tier data. ✅ built
- **Phase 2** — App Proxy: persist selection, one-time charge for extras, Appstle tier swap. ✅ built (Appstle swap endpoint stubbed pending API details)
- **Phase 3** — Reminder email, fulfillment generation, fallback via Worker cron. ✅ built

## Status

All three phases built and tested locally (portal 23/23 + Worker 15/15 assertions).
Remaining work is live-store wiring and two product decisions — see
[DEPLOYMENT.md](DEPLOYMENT.md).
