Initial commit (unpacked platform)
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# Del 13 – Subscriptions
|
||||
|
||||
Planer (spec §45): Household 79 kr (3 pers), Family 129 kr (6), Large Household 169 kr (12).
|
||||
Fair use-kvoter för AI: 300/600/1000 skanningar/mån. Gratis: 10. Trial: 7 dagar full
|
||||
tillgång (Family-nivå) utan kort, startar vid registrering (spec §46).
|
||||
|
||||
## Arkitekturprincip
|
||||
|
||||
**Backend är source of truth (spec §47, §61.14).** Klientens kvitto är ett påstående;
|
||||
`subscriptions`-tabellen efter serververifiering är sanningen. Appen läser
|
||||
`GET /v1/me/entitlements` och cachar den signerade offline-token.
|
||||
|
||||
## Flöden
|
||||
|
||||
**Köp:** StoreKit 2 / Play Billing i appen (fas 7: `react-native-purchases` eller
|
||||
expo-iap – beslut D-021 tas då) → kvitto till `POST /v1/subscriptions/verify` →
|
||||
`StoreVerifier` verifierar mot butiken → upsert på `original_transaction_id`
|
||||
(idempotent; kopplat-till-annat-konto ger 409) → entitlements + ny token i svaret.
|
||||
|
||||
**Verifiering:**
|
||||
|
||||
- Apple: App Store Server API v2 – JWS-signaturkedja mot Apples rotcertifikat,
|
||||
bundleId + environment kontrolleras. Kräver APPLE_ISSUER_ID/KEY_ID/PRIVATE_KEY.
|
||||
- Google: Play Developer API `purchases.subscriptionsv2.get` med service account.
|
||||
- Dev/CI: `APP_STORE_MODE=sandbox` accepterar JSON-payloads så att hela flödet
|
||||
(köp → entitlements → grace → expiry) E2E-testas utan butikskonton. Produktion
|
||||
kräver `production`-läge.
|
||||
|
||||
**Livscykel via webhooks (byggt):** App Store Server Notifications V2 och Play RTDN
|
||||
tas emot råa på `/v1/subscriptions/webhooks/*`, sparas i `store_notifications`, och
|
||||
processas asynkront (`PROCESS_STORE_NOTIFICATION`). Mappning när butiksnycklar finns:
|
||||
DID_RENEW/RECOVERED→active · GRACE_PERIOD→in_grace (+grace_period_expires_at) ·
|
||||
ON_HOLD→on_hold · CANCELED/EXPIRED→canceled/expired · REFUND→expired + audit.
|
||||
Nattligt svep (`VERIFY_SUBSCRIPTION`) markerar passerade prenumerationer som utgångna
|
||||
även om webhooks missats.
|
||||
|
||||
**Trial → betald/free:** trial-raden styr entitlements t.o.m. endsAt; därefter free
|
||||
automatiskt (ingen åtgärd krävs). En trial per användare (PK på user).
|
||||
|
||||
**Restore:** klienten hämtar aktuellt kvitto från butiken → samma verify-endpoint.
|
||||
|
||||
**Grace & offline (spec §44):** butiks-grace speglas i status `in_grace` med full
|
||||
åtkomst. Offline litar appen på den signerade token: TTL 24 h + grace 7 dagar,
|
||||
HMAC-verifierad, aldrig en permanent boolean. Efter graceExp krävs server.
|
||||
|
||||
**Hushållsdelning:** ägarens plan sätter maxHouseholdMembers; join-endpointen
|
||||
blockerar över gränsen med uppgraderingsuppmaning (402).
|
||||
|
||||
## Tabeller
|
||||
|
||||
`subscriptions` (status/plan/expires/grace/last_verified) · `subscription_events`
|
||||
(logg) · `store_transactions` (råa, unika per provider+transactionId) ·
|
||||
`store_notifications` (webhook-inbox) · `trials` · `ai_usage_counters` (fair use).
|
||||
|
||||
## Kvarstår till fas 7
|
||||
|
||||
Riktiga butiksnycklar + JWS/PDA-verifiering i `StoreVerifier` (interfacet är fryst),
|
||||
klientköp via vald IAP-modul, produkt-id:n registrerade i båda konsolerna
|
||||
(`<iosBundleId>.<plan>_monthly` för Apple / `<plan>_monthly` för Google – härleds ur `brand.config.json`),
|
||||
sandbox-testkonton, kvittotest av alla livscykelhändelser (Del 16).
|
||||
Reference in New Issue
Block a user