Files
Cibello-app/docs/13-subscriptions.md
T
Claude 88876a24b7 fix(subscriptions): trial 14 dagar konsekvent i UI/docs/legal/butik
TRIAL_DAYS var redan 14 i koden men auth.trialNote (12 sprak), butiksmetadata, juridik-utkast, API-ref, exec-summary och UX-doc sa 7. Nu 14 overallt. Rorde EJ icke-trial 7-dagar (inbjudningslank, backup, S3-lifecycle, grace, retention). Beslut: Johan (battre onboarding).
2026-08-17 17:04:00 +00:00

61 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: 14 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).