# Appstle-native integration (no custom backend)

Decision: the portal writes changes **directly into the customer's Appstle subscription**
via Appstle's **storefront APIs**, which run over Appstle's own Shopify App Proxy and
authenticate by the **logged-in customer session** (no API key, no token, no server of
ours). Confirmed from Appstle docs: storefront APIs support swap products, add/remove
items, view/modify contract, skip/next-billing — and "must be called from your shop's
domain with a logged-in customer session; will not work with API key authentication."

This removes our Cloudflare Worker / Vercel backend for the customer flow.

## How the portal maps to Appstle

```
On load:
  - Liquid gives tier/cycle/pool from metaobjects (default box + swap pool + live prices)
  - AppstleWC.getContract()  → the customer's active subscription + current line items
  - Portal shows current box (from contract) + swap UI (from pool)

On "Lock in":
  - diff(composedBox, contractLines):
      for each changed slot → AppstleWC.swapLine(contractId, oldVariant, newVariant)
      for each add-on       → AppstleWC.addOneTime(contractId, variant, qty=1)
  - Appstle bills the one-time extras and its renewal ships the swapped box.

On "Switch tier":
  - AppstleWC.changePlan(contractId, targetSellingPlanId)

Fulfillment: Appstle's renewal order already contains the swapped bottles + one-times.
Shopify Flow only adds packing tags (tier:*, cycle:*). No fulfillment-generation code.
Fallback: a customer who doesn't customize renews with their existing contract = the
default box (assuming the cycle's default box was set on the contract — see "Defaults").
```

## What still needs confirming — capture the real endpoints (2 min)

Appstle's exact storefront paths depend on the store's Appstle app-proxy subpath. Get
them from Appstle's OWN customer portal on the live store:

1. Log into the store as a **subscriber**, open Appstle's **Manage Subscription** portal
   (usually `/account` or `/pages/manage-subscription`).
2. Open browser **DevTools → Network** (filter: Fetch/XHR).
3. Perform each action once and record the request that fires:
   - **Swap a product** → note the request URL, method, and JSON body.
   - **Add a one-time product** → note URL/method/body.
   - **Change/swap plan** (if available) → note URL/method/body.
   - On portal load, note the **GET** that lists the subscription contract(s).
4. Paste those 4 requests (URL + method + body) back — I'll drop them into
   `assets/appstle-adapter.js` CONFIG and the integration is done.

The paths look roughly like `/apps/<appstle-subpath>/api/external/v2/subscription-...`,
but the exact subpath + version + body shape must come from the capture above.

## Adapter

`theme/assets/appstle-adapter.js` exposes `window.AppstleWC`:
- `getContract()` → `{ contractId, sellingPlanId, lines:[{variantId, quantity}] }`
- `swapLine(contractId, fromVariantId, toVariantId)`
- `addOneTime(contractId, variantId, qty)`
- `changePlan(contractId, sellingPlanId)`
- `commit(selection, contract)` → diffs and applies swaps + one-times

The orchestration/diff logic is complete; only the CONFIG endpoint strings are pending
the capture above.

## Trade-offs (accepted)
- **Pool restriction is client-side only** — the JS shows only approved wines, but a
  determined user could call Appstle directly with an off-pool variant. Low risk for a
  wine club; monitor via Flow. A tiny validation endpoint could be added later if needed.
- **Per-cycle default box (Defaults):** to make each new cycle's default box the fallback,
  the admin must set those line items on subscriptions at cycle start — via Appstle's
  ADMIN API (needs the Appstle API key) or a Flow. This is the one place the Appstle API
  key is still useful. Out of the customer flow.
