SubSovereign
All guides

SubSovereign auf Android — Integrationsleitfaden

Dieser Leitfaden führt Ihre Android-App (Smartphone, Tablet, Android TV oder Amazon Fire TV) von „Ich habe keine Ahnung, wer mich bezahlt hat“ zu „Meine App schaltet die richtigen Funktionen für den richtigen Nutzer frei, verifiziert auf meinem eigenen Server.“ Er ist so geschrieben, dass Sie ihn von Anfang bis Ende durcharbeiten können — ohne Vorkenntnisse im Bereich Abonnement-Tools. Jedes Kotlin-Codebeispiel wird bei jedem Testlauf gegen das SDK kompiliert, sodass das, was Sie hier lesen, genau das ist, was der Code tut.

Was SubSovereign für Sie leistet

Ihre Nutzer abonnieren über den Google Play-Store (oder Amazon auf Fire TV). SubSovereign beantwortet eine Frage für Ihre App zuverlässig: Wofür hat dieser Nutzer tatsächlich bezahlt?

  • Ihre App fragt SubSovereign nach der Berechtigung des Nutzers — dem Zugang, den er freigeschaltet hat.
  • Die Belegvalidierung erfolgt auf dem Server, direkt mit dem Store, sodass eine modifizierte App kein Abonnement vortäuschen kann.
  • Die Paywall — der Bildschirm, der Ihre Pläne anbietet — wird remote konfiguriert und vom SDK gezeichnet, sodass Sie Preise, Testversionen und Formulierungen ohne Veröffentlichung einer neuen App-Version ändern können.
  • Die EU-Rücktrittsfunktion — der gesetzlich vorgeschriebene Kündigungsbutton — wird ebenfalls vom SDK gezeichnet, in der Sprache des Kunden, sodass ein rechtlicher Hinweis nicht in Marketing übergeht.
  • Es ist selbstgehostet: SubSovereign läuft auf Ihrer Infrastruktur, die Daten Ihrer Nutzer bleiben bei Ihnen, und es gibt keinen Umsatzanteil — Sie behalten 100 % dessen, was Ihre Nutzer zahlen.

Sie vertrauen niemals dem Gerät. Das Gerät fragt; der Server entscheidet.

Bevor Sie beginnen

Sie benötigen:

  1. Einen laufenden SubSovereign-Server (Ihre selbstgehostete Bereitstellung). Sie richten das SDK auf dessen URL aus. Falls Sie noch keinen haben, stellen Sie ihn zuerst bereit — der Rest dieses Leitfadens geht davon aus, dass er live ist.
  2. Eine in der Dashboard registrierte App. Erstellen Sie im SubSovereign-Dashboard eine App. Sie erhalten zwei Dinge:
    • eine appId (identifiziert Ihre App) und
    • einen API-Schlüssel (die Anmeldedaten, mit denen Ihre App mit dem Server kommuniziert). Registrieren Sie sie für die Oberfläche, auf der sie veröffentlicht wird — Smartphone, Android TV oder Fire TV. Ein Codebase, der auf mehreren Oberflächen läuft, ist in Ordnung; siehe Smartphones und Fernseher unter Schritt 2.
  3. Zugangsstufen erstellt. Definieren Sie im Dashboard die Zugangsstufen (auch Tiers genannt), die Ihre App gewährt — zum Beispiel pro oder premium — und verknüpfen Sie jede mit den Store-Produkt-IDs, die Ihre Nutzer kaufen.
  4. Store-Billing bereits eingerichtet. SubSovereign validiert und verfolgt Käufe; es ersetzt nicht Google Play Billing (oder Amazon IAP). Richten Sie diese wie gewohnt in Ihrer App ein — SubSovereign setzt direkt nach dem Kauf an, um ihn zu verifizieren und zu erfassen.

Auf der Code-Seite benötigen Sie Kotlin mit Coroutinen (die SDK-Aufrufe sind suspend-Funktionen), Jetpack Compose (die Paywall und der Kündigungsbutton sind Composables) und minSdk 24 oder höher.

Schritt 1 — SDK hinzufügen

Das SDK wird derzeit als Quellcode ausgeliefert: Es gibt noch nichts zum Herunterladen von Maven Central. Nehmen Sie den Ordner sdk-android/src/main/java aus dem SubSovereign-Repository in Ihr Projekt auf und kompilieren Sie ihn als Teil Ihrer App — genau wie die Beispiel-App im selben Repository. Nachfolgend finden Sie, was das SDK selbst benötigt, zusätzlich zu dem, was Ihre App bereits hat: die beiden Plugins, die der Code verwendet, und seine eigenen Abhängigkeiten, die auf die Versionen festgelegt sind, mit denen es gebaut und getestet wurde (die letzte Zeile zeigt, wie die Beispiel-App ein Composable auf dem Bildschirm platziert). Bei AGP 8 wenden Sie auch org.jetbrains.kotlin.android an; bei AGP 9 ist Kotlin integriert. Deklarieren Sie die Plugin-Versionen in Ihrer Root-Build-Datei, wie es die Beispiel-App tut.

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.plugin.compose")
    id("org.jetbrains.kotlin.plugin.serialization")   // the SDK's models are @Serializable
}

android {
    // ... your existing settings; minSdk 24 or higher
    buildFeatures { compose = true }
    // Compile the SDK's source folder as part of your app — wherever you put it.
    sourceSets["main"].kotlin.srcDir("../subsovereign-sdk/src/main/java")
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
    implementation("io.coil-kt:coil-compose:2.7.0")     // hero images — the SDK's one UI dependency
    implementation(platform("androidx.compose:compose-bom:2024.09.02"))
    implementation("androidx.compose.foundation:foundation")
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.runtime:runtime")
    implementation("androidx.activity:activity-compose:1.9.3")   // setContent, as in the sample
}

Stellen Sie sicher, dass Ihre App die Internet-Berechtigung in AndroidManifest.xml hat:

<uses-permission android:name="android.permission.INTERNET" />

Da das SDK als Ihr eigener Quellcode kompiliert wird, gibt es keine Version zum Aktualisieren: Um es zu aktualisieren, ersetzen Sie den Ordner. Sobald ein veröffentlichtes Artefakt existiert, wird dieser Schritt zu einer einzigen Abhängigkeitszeile, und dieser Leitfaden wird dies entsprechend anpassen.

Schritt 2 — Einmalig beim Start der App konfigurieren

Konfigurieren Sie das SDK einmalig — der beste Ort dafür ist Ihre Application.onCreate() oder direkt nach der Anmeldung des Nutzers. Sie übergeben ihm Ihren API-Schlüssel, Ihre App-ID, einen stabilen Identifikator für diesen Nutzer, seine Sprache und auf welcher Oberfläche die App läuft.

import com.subsovereign.sdk.SubSovereign
import com.subsovereign.sdk.SubSovereignConfig
import com.subsovereign.sdk.detectPlatform

SubSovereign.configure(
    SubSovereignConfig(
        apiKey   = "YOUR_APP_API_KEY",                    // from the dashboard
        appId    = "your-app-id",                         // from the dashboard
        baseUrl  = "https://subs.yourdomain.com/api/v1",  // YOUR self-hosted server
        userId   = currentUser.id,                        // your own stable user id
        locale   = customerLanguage,                      // the CUSTOMER's language, e.g. "fr" — decides the words they get
        platform = detectPlatform(this),                  // "android", "androidtv" or "firetv"
    )
)

Einige Dinge, die es wert sind, erwähnt zu werden:

  • userId stammt von Ihnen. Verwenden Sie die stabile ID, die Sie bereits für einen angemeldeten Nutzer haben. Verwenden Sie denselben Wert jedes Mal, damit der Zugang des Nutzers ihm über Geräte hinweg folgt. Wenn Sie Abmeldung unterstützen und ein anderer Nutzer sich anmeldet, rufen Sie einfach configure erneut mit der neuen userId auf.
  • baseUrl zeigt auf Ihren Server. Der Standardwert im SDK ist ein Platzhalter — setzen Sie diesen auf Ihre eigene SubSovereign-Bereitstellung.
  • locale ist die Sprache des Kunden, nicht die Ihrer Konsole, und sie wird HIER festgelegt: Der Server gibt die Paywall und die Worte des gesetzlichen Hinweises in der Sprache zurück, die Sie konfigurieren, und das SDK formatiert die Daten dafür. Nehmen Sie sie aus dem Profil des Kunden oder vom Gerät, nicht als Literal.

Smartphones und Fernseher

Ein Play-Codebase kann auf Smartphones, Tablets und Android TV veröffentlicht werden; ein Fire-TV-Build wird über Amazon ausgeliefert. platform teilt dem SDK mit, auf welcher Oberfläche es läuft — "android", "androidtv" oder "firetv" — und das entscheidet drei Dinge: welche Paywall der Server sendet (eine Fernseh-Paywall ist für einen Raum und nicht für eine Hand ausgelegt), unter welcher Oberfläche eine Kündigung eingereicht wird und wo Ihre Kunden als Herkunft erfasst werden. detectPlatform(context) beantwortet dies zur Laufzeit: Ein Fire TV anhand von Amazons eigener Gerätefunktion, ein Android TV anhand seiner Konfiguration oder Leanback, alles andere als Smartphone. Kodieren Sie "android" nicht hart in einem Build, der auch auf Fernsehern läuft — das würde die Smartphone-Paywall auf dem TV abrufen.

Auf Fire TV registrieren Sie die App als Fire TV im Dashboard oder veröffentlichen eine Fire-TV-Paywall dafür. Ein Fire-TV-Build fragt den Server nach der Fire-TV-Paywall, und ein Server, der keine hat, antwortet mit einem Error, anstatt die Smartphone-Paywall zu liefern (der Server greift von Android TV auf die Smartphone-Paywall zurück, von Fire TV auf nichts). Das SDK rät niemals eine Oberfläche: Ein platform-Wert, den es nicht erkennt, ist ein Error, der ihn benennt, und es wird nichts gesendet.

Schritt 3 — Prüfen, auf was der Nutzer zugreifen kann

Die schnelle Ja/Nein-Abfrage ist hasAccess(). Sie fällt sicher aus (fail-closed): Bei einem Netzwerkproblem, einem Serverfehler oder einem nicht konfigurierten SDK antwortet sie mit false, sodass ein kurzzeitiger Ausfall niemals eine kostenpflichtige Funktion freischalten kann. Verwenden Sie sie am Punkt der Zugangssteuerung:

lifecycleScope.launch {
    if (SubSovereign.hasAccess("pro")) unlockProFeatures() else showPaywall()
}

hasAccess("pro") ist nur dann true, wenn eine aktive Berechtigung diesen Namen der Zugangsstufe trägt; hasAccess() ohne Namen ist das Gesamturteil des Servers.

Wenn Sie die Details benötigen — was genau aktiv ist, wann es verlängert wird oder um einen „Nein“ von einem „Konnte nicht prüfen“ zu unterscheiden — rufen Sie checkEntitlements() auf. Es gibt ein Ergebnis zurück, über das Sie entscheiden, und die richtige Entscheidung bei einem Fehler ist normalerweise, den Kunden dort zu belassen, wo er war:

import com.subsovereign.sdk.SubSovereignResult
import kotlinx.coroutines.launch

lifecycleScope.launch {
    when (val result = SubSovereign.checkEntitlements()) {
        is SubSovereignResult.Success -> {
            val ent = result.data
            if (ent.hasAccess) unlockProFeatures() else showPaywall()
        }
        is SubSovereignResult.Error -> {
            // A network hiccup or a server error. Keep the customer on whatever access they
            // last had and try again later — never lock a paying customer out over a blip.
            Log.w("MyApp", "Entitlement check failed: ${result.message}")
        }
    }
}

Wenn Ihre App mehr als eine Stufe hat, sehen Sie in result.data.entitlements nach — jede einzelne sagt Ihnen genau, welche Zugangsstufe aktiv ist. Vergleichen Sie mit dem Namen, den Sie der Stufe im Dashboard gegeben haben; accessLevelId ist die interne ID des Servers, nicht dieser Name:

val isPro = result.data.entitlements.any { it.isActive && it.accessLevelName.equals("pro", ignoreCase = true) }

Jede Berechtigung enthält außerdem expiresAt, willRenew und den store, von dem sie stammt — nützlich für Nachrichten wie „Ihr Abonnement verlängert sich am…“. fromCache bedeutet, dass der Server aus seinem eigenen kurzlebigen Cache geantwortet hat, anstatt aus seiner Datenbank; es ist niemals eine Offline-Antwort — ein nicht erreichbarer Server ist ein Error, und dieses SDK behält keine Kopie.

Schritt 4 — Ein Abonnement verkaufen

Paywall abrufen

Holen Sie sich die Paywall vom Server, anstatt Preise hart in Ihrer App zu kodieren. Dadurch können Sie einen Sale durchführen oder die Länge einer Testversion ändern, ohne eine neue Version zu veröffentlichen:

lifecycleScope.launch {
    when (val result = SubSovereign.getPaywallConfig()) {
        is SubSovereignResult.Success -> showPaywall(result.data)   // the paywall published for THIS surface
        is SubSovereignResult.Error   -> showFallbackPaywall()      // your own built-in default
    }
}

PaywallConfig enthält die Überschrift, die Funktionen, die angebotenen Produkte (jedes mit einem angezeigten Preis, Zeitraum, Testversion und einem optionalen Badge), den Call-to-Action, die Fußzeile, die Farben und die Vorlage — alles, was der Bildschirm benötigt.

Zeichnen der Paywall

Das SDK zeichnet die Paywall. PaywallView ist ein Composable: Geben Sie ihm die Konfiguration und sagen Sie ihm, was zu tun ist, wenn der Kunde ein Produkt auswählt.

import androidx.compose.runtime.Composable

@Composable
fun PaywallScreen(config: PaywallConfig, onBuy: (String) -> Unit) {
    PaywallView(
        config = config,
        onDismiss = { /* the customer closed it — only reachable when you allow closing in the console */ },
    ) { productId -> onBuy(productId) }   // start Google Play Billing (or Amazon IAP) for this product
}

Es übernimmt das Layout, die Farben, die Sie in der Konsole gewählt haben (und stellt sicher, dass der Text lesbar bleibt, selbst wenn ein Verlauf ihn blass gemacht hätte), den Schließen-Button, falls Sie einen zugelassen haben, und — auf einem Fernseher — den Ring, der anzeigt, welche Steuerung die Fernbedienung ausgewählt hat. Sie starten den Kaufvorgang des Stores für das Produkt, das es Ihnen übergibt. Das Anzeigen auf dem Bildschirm folgt dem üblichen Compose-Muster:

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent

class PaywallActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { PaywallScreen(config = paywall) { productId -> startPurchase(productId) } }   // `paywall`: the PaywallConfig you fetched above
    }
}

Kauf abschließen und dann verifizieren

Führen Sie den Kauf durch Google Play Billing genau so durch, wie Sie es normalerweise tun würden. Wenn Google Ihnen ein purchaseToken zurückgibt, übergeben Sie es an SubSovereign, damit der Server den Beleg direkt mit dem Store validieren und die Zugangsstufe gewähren kann. Die Stufe stammt aus der Produkt-zu-Stufe-Zuordnung in Ihrem Dashboard, niemals aus der App — eine modifizierte App kann sich nicht selbst hochstufen — daher wird accessLevelId aus Kompatibilitätsgründen akzeptiert und ignoriert:

lifecycleScope.launch {
    val ok = SubSovereign.validateGooglePurchase(
        purchaseToken = purchase.purchaseToken,     // from Google Play Billing
        productId     = purchase.products.first(),
        accessLevelId = "pro",                      // required by the SDK; the server grants the tier YOUR DASHBOARD maps this product to
    )
    when (ok) {
        is SubSovereignResult.Success ->
            // Confirmed by the server: re-check (fail-closed) and unlock on the answer, not on hope.
            if (ok.data && SubSovereign.hasAccess("pro")) unlockProFeatures() else showTryAgain("Not confirmed yet")
        is SubSovereignResult.Error   -> showTryAgain(ok.message)
    }
}

Das ist das gesamte Vertrauensmodell: Der Kauf ist erst real, wenn der Server ihn bestätigt hat.

Auf Amazon Fire TV

Fire-TV-Apps verwenden Amazons In-App-Kaufsystem anstelle von Google Play. Es folgt demselben Muster mit anderen Eingaben: Amazon gibt Ihnen einen Beleg und ein Nutzerdaten-Objekt, und der Beleg ist nur gültig zusammen mit Amazons eigener Nutzer-ID — die nicht Ihre userId ist. Der Server ordnet die SKU der Zugangsstufe zu, daher gibt es kein accessLevelId, das übergeben werden muss:

SubSovereign.validateAmazonPurchase(
    receiptId    = receipt.receiptId,   // from Amazon's PurchaseResponse
    amazonUserId = userData.userId,     // from Amazon's UserData — NOT your own userId
    productId    = receipt.sku,
)

Schritt 5 — Die EU-Rücktrittsfunktion (Compliance Passport)

Wenn Sie Abonnements an EU-Verbraucher verkaufen, verlangt die Richtlinie (EU) 2023/2673 eine klar gekennzeichnete Rücktrittsfunktion — den Kündigungsbutton. Das SDK liefert sie fertig, und sie wird in zwei Teilen gezeichnet: Holen Sie sich die Einstellungen für Ihre App, dann zeichnen Sie die Steuerung mit ihnen.

import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

@Composable
fun CancelSubscription(subscriptionId: String, customerLocale: String) {
    var settings by remember { mutableStateOf<WithdrawalConfig?>(null) }
    LaunchedEffect(Unit) {
        when (val r = SubSovereign.getWithdrawalConfig()) {
            is SubSovereignResult.Success -> settings = r.data
            // Log it: a silent failure here is how you ship English to German customers and never find out.
            is SubSovereignResult.Error -> Log.w("MyApp", "Withdrawal settings unavailable: ${r.message}")
        }
    }
    CancelButton(subscriptionId, settings, customerLocale)
}
@Composable
fun CancelButton(subscriptionId: String, settings: WithdrawalConfig?, customerLocale: String) {
    if (settings?.enabled == false) return       // switched off for this app in the console
    // The WORDS are not passed in and cannot be: getWithdrawalConfig() fetched them in your
    // customer's language and the SDK kept them, and this control reads them from there.
    // Fetch FIRST — without it the notice falls back to English for every customer.
    WithdrawalView(
        subscriptionId = subscriptionId,
        labelKey   = settings?.labelKey,         // ← the label chosen for this app
        locale     = customerLocale,             // ← without this the DATE is formatted for the device
        appearance = Appearance.AUTO,            // LIGHT, DARK or AUTO — and nothing else
    )
}

Sie können die Formulierung nicht ändern. Es gibt zwei Beschriftungen für die Rücktrittsfunktion — „Hier vom Vertrag zurücktreten“ und „Hier kündigen“ — und labelKey enthält diejenige, die für Ihre App festgelegt ist; die Wörter selbst kommen vom Server in der Sprache des Kunden. Es gibt keine Möglichkeit, eigenen Text zu übergeben: Ein label-Parameter akzeptierte früher freien Text und wird jetzt mit einer protokollierten Warnung ignoriert. Ein gesetzlicher Hinweis, der umformuliert werden kann, ist einer, der zu Marketing abgeschwächt werden kann, was derselbe Grund ist, warum Sie keine Farbe übergeben können.

🚩 Übergeben Sie strings, sonst ist der gesetzliche Hinweis für alle auf Englisch. Die Wörter werden von Ihrem SubSovereign-Server in den Sprachen bereitgestellt, die er unterstützt; eine Sprache, die er nicht hat, kommt auf Englisch zurück. Welche Sprache Sie erhalten, wird durch die locale entschieden, die Sie in Schritt 2 konfiguriert haben — der Einstellungsaufruf sendet sie mit — daher muss customerLocale hier denselben Wert haben; es formatiert das Datum, es wählt nicht die Wörter aus. Die Steuerung rendert, was ihr übergeben wird, und holt sich die Wörter nicht selbst, weil derselbe Vertrag auf jeder Plattform gelten muss, einschließlich einer, auf der eine UI-Komponente überhaupt kein Netzwerk nutzen kann. Wenn der Abruf fehlschlägt, erscheint die Steuerung trotzdem, auf Englisch, anstatt nicht zu erscheinen: Das Fehlen einer auffindbaren Rücktrittsfunktion vor dem Verbraucher ist Ihr Verstoß; sie in der falschen Sprache anzuzeigen, ist es nicht. Protokollieren Sie den Fehler; zeigen Sie ihn niemals dem Kunden an.

🚩 Übergeben Sie auch locale. Es formatiert den Zeitstempel auf der Bestätigung. Ohne es greift das SDK auf die von Ihnen konfigurierte Sprache zurück; ohne beides wird das Datum in einer Form gedruckt, die kein Leser falsch verstehen kann. Ein deutscher Kunde auf einem Gerät mit US-Spracheinstellung darf nicht 9/3/2026 sehen, was er als 9. März lesen würde.

Erscheinungsbild — LIGHT, DARK oder AUTO (Standard) und nichts anderes. Die Steuerung zeichnet ihre eigene Karte und ihren eigenen Text, sodass sie auf jedem Bildschirm lesbar ist, hell oder dunkel. AUTO folgt der Dunkelmodus-Einstellung des Geräts auf einem Smartphone oder Tablet und ist auf einem Fernseher immer dunkel. Es gibt absichtlich keine Möglichkeit, eine Farbe zu übergeben.

Sie zeigt den beschrifteten Button, bestätigt einmal, sendet idempotent (ein Netzwerk-Wiederholungsversuch kann niemals zwei Rücktritte erzeugen) und zeigt die Bestätigung mit ihrem Datum an. Die Steuerung der Anzeige über enabled liegt bei Ihnen, wie oben; die Steuerung selbst führt kein Netzwerk aus, außer dem Absenden.

Schritt 6 — Einwilligung und Datenschutz

Alles, was eine Datenschutzanfrage benötigt, ist jeweils ein Aufruf, und alle gehen über Ihren eigenen Server:

SubSovereign.recordConsent(purpose = "analytics", granted = true)
SubSovereign.setDoNotSell(enabled = true)        // CCPA "do not sell" / Global Privacy Control
SubSovereign.requestErasure()                    // GDPR Art. 17 — the customer's right to be forgotten
val export = SubSovereign.exportMyData()         // GDPR Art. 20 — their data, as JSON
val flags  = SubSovereign.getFeatureFlags()      // remote feature flags for this app
SubSovereign.recordAttributionTouch(utmSource = "newsletter")   // where this customer first came from

recordConsent akzeptiert als purpose einen der Werte analytics, marketing, personalisation oder consumption_data_sharing — alles andere führt zu einem Error; jurisdiction ist standardmäßig "GDPR" und policyVersion "1.0", übergeben Sie Ihre eigenen Werte, falls sie abweichen. Jeder Aufruf gibt ein SubSovereignResult zurück, wie jeder andere Aufruf auch.

Umgang mit Ergebnissen

Jeder SDK-Aufruf außer hasAccess() gibt ein SubSovereignResult zurück, das entweder Success (mit den Daten) oder Error (mit einer Nachricht und, falls relevant, einem HTTP-Status code) ist. Das ist Absicht: Netzwerkaufrufe können fehlschlagen, und Ihre App sollte entscheiden, was zu tun ist, anstatt abzustürzen. Eine einfache Vorgehensweise:

  • Bei Success — Nutzen Sie die Daten.
  • Bei Error — Protokollieren Sie ihn, behalten Sie den Nutzer bei seinem zuletzt bekannten Zugang und versuchen Sie es später erneut. Sperren Sie niemals einen zahlenden Nutzer wegen eines kurzzeitigen Netzwerkproblems aus.
  • hasAccess() ist die Ausnahme: Es ist ein einfaches Boolean und antwortet bei jedem Fehler mit false, sodass Sie es zur Zugangssteuerung verwenden und checkEntitlements() zur Erklärung nutzen können.

Best Practices

  • Prüfen Sie beim Kaltstart. Rufen Sie checkEntitlements() beim Start der App auf, damit die Zugangssteuerung korrekt ist, bevor der Nutzer eine gesperrte Funktion erreicht.
  • Prüfen Sie nach dem Kauf erneut. Direkt nach einem erfolgreichen validate…-Aufruf führen Sie checkEntitlements() erneut aus, damit die UI den neuen Zugang sofort widerspiegelt.
  • Vertrauen Sie niemals dem Client. Speichern Sie nicht „ist pro“ in der App und behandeln Sie es als Wahrheit. Fragen Sie den Server; der Server hat den Beleg verifiziert.
  • Eine userId pro echten Nutzer. Halten Sie sie stabil, damit der Zugang dem Nutzer über Geräte hinweg folgt, und konfigurieren Sie sie neu, wenn sich der angemeldete Nutzer ändert.
  • Lassen Sie das SDK die Oberfläche erkennen. detectPlatform(context) zur Konfigurationszeit, niemals ein Literal in einem Build, der auf mehr als einer Geräteart läuft.
  • Halten Sie Coroutinen sauber. Dies sind suspend-Funktionen — rufen Sie sie aus einem lifecycleScope oder viewModelScope auf, damit sie mit dem Bildschirm abgebrochen werden.

Schnellreferenz

Sie möchten… Aufruf
SDK einrichten SubSovereign.configure(config) mit platform = detectPlatform(context)
Funktion sperren, sicher ausfallend (fail-closed) hasAccess(accessLevelName?)Boolean
Sehen, was der Nutzer freigeschaltet hat checkEntitlements()EntitlementResult
Paywall für diese Oberfläche abrufen getPaywallConfig()PaywallConfig
Paywall zeichnen PaywallView(config, onDismiss) { productId -> … }
Google-Play-Kauf verifizieren validateGooglePurchase(purchaseToken, productId, accessLevelId)
Amazon- (Fire-TV-) Kauf verifizieren validateAmazonPurchase(receiptId, amazonUserId, productId)
Rücktrittseinstellungen und -texte abrufen getWithdrawalConfig()WithdrawalConfig
EU-Rücktrittsfunktion zeichnen WithdrawalView(subscriptionId, strings, labelKey, locale, appearance)
GDPR-Einwilligung erfassen recordConsent(purpose, granted)
CCPA „Do-not-sell“ · Löschung · Export · Flags · Attribution setDoNotSell · requestErasure · exportMyData · getFeatureFlags · recordAttributionTouch

Nächste Schritte