Files
Cibello-app/docs/13-subscriptions.md
Claude 75591bb294 fix(subscriptions): free-niva 5 AI-skanningar (beslut Johan)
Free 10->5 i kod, test, paywall-copy (12 sprak) och docs. Full trappa: free 5 / trial 14d 25 skann / premium 100/200/400.
2026-08-17 17:34:55 +00:00

3.1 KiB
Raw Permalink Blame History

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: 100/200/400 skanningar/mån. Gratis: 5. 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/verifyStoreVerifier 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).