The product needs trial, purchase, restore, cancel, and allowance states before any billing vendor is approved (ADR-0003, ADR-0005 forbid real vendors, credentials, and charges in this repository). The UX guidance requires server-verified entitlement, honest states during outages, and a manual logging path that never depends on billing.
BillingProvider seam (describePlan, restorePurchases, cancel, manageUrl) sits
beside the existing EntitlementProvider. The only adapter is DeterministicBillingProvider:
the plan is described from configuration (display-only price string, 14-day trial, monthly
allowance), restore always finds one 30-day mock purchase, cancel always succeeds, and nothing
is ever charged. MOCK_PROVIDERS=true is required to construct it.subscriptions (one row per adult): trial, active,
or cancelled with trial and period timestamps. Status shown to the adult is derived on read:
trial/active grant access, cancelled grants it until the period ends, otherwise expired.
A lapsed active period rolls forward one month on read (mock renewal, no charge).GET /subscription or a new
photo upload) and idempotently through POST /subscription/trial/start, so existing photo flows
keep working and the client can still explain the trial before showing it.POST /photo/uploads calls the allowance guard
before authorizing a new upload; replays of an existing authorization are never re-counted.
Usage is the number of meal_analysis_jobs rows the adult created in the current UTC calendar
month, regardless of outcome. When usage reaches the plan allowance, or access has ended, the API
answers 403 with code: analysis_allowance_exhausted, details.reason, the allowance, the
usage, and the subscription status, and says the adult can still type meals in.code. Exceptions whose body contains
a lowercase snake-case code override the status-derived default; everything else is unchanged.subscription.trial_start, subscription.restore,
subscription.cancel) with the subscription public ID as the resource; no price or payment data
exists to leak.BillingProvider against a store or vendor and mapping its
entitlement into subscriptions; the allowance guard and the API contract do not change.