Report: State of in-app subscriptions in the US 2023 Get a report

Was ist neu an der StoreKit 2 API und wie Apple die Integration von In-App-Käufen vereinfacht hat

Kirill Potekhin

Updated: März 20, 2023

Content

62fdf0da8b35ff3ba074dbce jp android tutorial 1 configuration 5

Apple hat letztens während der WWDC 2021 eine neue Version von StoreKit 2 präsentiert. Dabei handelt es sich um ein Framework, das für Käufe in iOS verantwortlich ist. Der Anteil an Apps mit In-App-Käufen und Abonnements wächst stetig und Apple hat mit der Veröffentlichung von StoreKit 2 die Integration von In-App-Käufen in die App deutlich vereinfacht. In dem heutigen Artikel beleuchten wir die Arbeit mit StoreKit 2 auf der Serverseite (sprich mit Hilfe der App Store Server API).

Anforderung der Authentifizierung

In der aktuellen API-Version benötigen Sie das gemeinsame Geheimnis (shared secret), um eine Anfrage zu senden. Dies ist eine geheime feste Zeichenfolge, die Sie in App Store Connect erhalten. Die neue Version der API verwendet den JSON Web Token (JWT)-Standard für die Anforderung der Authentifizierung.  

Generierung des Keys

Zunächst müssen Sie einen privaten Key erstellen, der zur Autorisierung der Anfragen verwendet wird. Öffnen Sie App Store Connect und gehen Sie zum Abschnitt Nnutzer und Zugriff und anschließend zur Registerkarte Key. Wählen Sie den Key-Typ für den In-App-Kauf aus und laden Sie einen neuen Key herunter. Sie benötigen auch seine ID. Diese können Sie auf derselben Seite wie die Problem-ID kopieren, die auf der Registerkarte App Store Connect API zu finden ist.

Creating a private key to work with StoreKit 2
Erstellung eines privaten Keys für die Arbeit mit der App Store Server API

Erstellung eines Tokens

Als nächstes erstellen Sie einen Token, der zum Autorisieren der Anforderungen verwendet wird. Dieser Vorgang wird in der Dokumentation ausführlich beschrieben, weswegen wir an dieser Stelle nicht weiter darauf eingehen. Stattdessen zeigen wir Ihnen ein Beispiel für eine fertige Implementierung für Python. Es ergibt keinen Sinn, für jede neue Anfrage einen neuen Token zu generieren. Beim Erstellen eines Tokens legen Sie dessen Lebensdauer auf bis zu 60 Minuten fest und verwenden in dieser Zeit denselben Token. 

import time, uuid
from authlib.jose import jwt

BUNDLE_ID = 'com.adapty.sample_app'
ISSUER_ID = '4336a124-f214-4d40-883b-6db275b5e4aa'
KEY_ID = 'J65UYBDA74'
PRIVATE_KEY = '''
-----BEGIN PRIVATE KEY-----
MIGTAgMGByqGSMBHkAQQgR/fR+3Lkg4...
-----END PRIVATE KEY-----
'''

issue_time = round(time.time())
expiration_time = issue_time + 60 * 60 # 1 hour expiration


header = {
 'alg': 'ES256',
 'kid': KEY_ID,
 'typ': 'JWT'
}

payload = {
 'iss': ISSUER_ID,
 'iat': issue_time,
 'exp': expiration_time,
 'aud': 'appstoreconnect-v1',
 'nonce': str(uuid.uuid4()),
 'bid': BUNDLE_ID
}

token_encoded = jwt.encode(header, payload, PRIVATE_KEY)
token_decoded = token_encoded.decode()

authorization_header = {
 'Authorization': f'Bearer {token_decoded}'
}

Unterzeichnete Transaktionen

In der neuen API-Version werden alle Transaktionen im JSON Web Signature (JWS)-Standard zurückgegeben. Dies ist eine Zeichenfolge, die aus drei Teilen besteht, die durch Punkte getrennt sind.  

  1. Base64 Header.
  2. Base64 Transaction Payload.
  3. Transaction Signature.
Base64(header) + "." + Base64(payload) + "." + sign(Base64(header) + "." + Base64(payload))

Transaction Header

Der Header ist wichtig, damit Sie sicher sein können, dass die Transaktion echt ist. Der Alg-Key enthält einen Verschlüsselungsalgorithmus, der x5c-Key enthält eine Zertifikatskette.

{
  "kid": "AMP/DEV",
  "alg": "ES256",
  "x5c": [
    "MIIEO...",
    "MIIDK..."
  ]
}

Transaction Payload

{
  "transactionId": "1000000831360853",
  "originalTransactionId": "1000000806937552",
  "webOrderLineItemId": "1000000063561721",
  "bundleId": "com.adapty.sample_app",
  "productId": "basic_subscription_1_month",
  "subscriptionGroupIdentifier": "27636320",
  "purchaseDate": 1624446341000,
  "originalPurchaseDate": 1619686337000,
  "expiresDate": 1624446641000,
  "quantity": 1,
  "type": "Auto-Renewable Subscription",
  "appAccountToken": "fd12746f-2d3a-46c8-bff8-55b75ed06aca",
  "inAppOwnershipType": "PURCHASED",
  "signedDate": 1624446484882,
  "offerType": 2,
  "offerIdentifier": "basic_subscription_1_month.pay_as_you_go.3_months"
}

Apple hat das Transaktionsformat sowohl geändert als auch erweitert. Für mich ist es jetzt einfacher zu verwenden. Einzelheiten zum neuen Format finden Sie in der Dokumentation. Im Folgenden beschreibe ich die wichtigsten Änderungen.

  • Apple hat das Feld appAccountToken hinzugefügt, das die Nutzer-ID Ihres Systems enthält. Diese ID muss im UUID-Format vorliegen. Sie wird in der App festgelegt, wenn ein Kauf initialisiert wird. Wenn sie gesetzt ist, wird sie bei allen Transaktionen in dieser Kette zurückgegeben (Verlängerung, Abrechnungsprobleme usw.) und Sie können leicht nachvollziehen, welcher Nutzer einen Kauf getätigt hat.    
  • Apple hat außerdem die Felder offerType und offerIdentifier hinzugefügt, welche die Informationen über ein genutztes Angebot (falls vorhanden) enthalten. Hier sind Werte für das Feld offerType:
  • 1 — Einführungsangebot (nur für Nutzer ohne aktive oder abgelaufene Abonnements verfügbar)
  • 2 — Aktionsangebot (nur für aktuelle und abgelaufene Abonnements verfügbar)
  • 3 — Promo-Code

Wurde ein Aktionsangebot oder ein Promo-Code verwendet, enthält der offerIdentifier-Key die ID des verwendeten Angebots. Früher war es unmöglich, die Nutzung des Angebots serverseitig nachzuvollziehen, was Analysen unmöglich machte. Jetzt können Sie Promo-Codes für Analysen verwenden.   

  • Apple hat das Feld inAppOwnershipType hinzugefügt. Dies zeigt an, ob ein Nutzer ein Produkt gekauft oder dank eines Familienabonnements darauf zugegriffen hat. Mögliche Werte:
  • PURCHASED
  • FAMILY_SHARED
  • Ein weiteres neues Feld – Type – beinhaltet den Transaktionstyp. Mögliche Werte:
  • Abonnement mit automatischer Verlängerung
  • Nicht-verbrauchbar
  • Verbrauchbar
  • Nicht verlängertes Abonnement
  • Die Felder cancellation_date und cancellation_reason haben jetzt neue Namen: recovationDate und revocationReason. Sie enthalten ein Datum und einen Grund für den Widerruf des Abonnements aufgrund einer Rückerstattung, sodass der neue Name logischer erscheint.
  • Alle Keys werden im CamelCase-Format zurückgegeben (genau wie bei allen App Store Server-API-Anforderungen).
  • Alle Daten werden im Unix-Zeitstempelformat in Millisekunden angezeigt.

Subscribe to Adapty newsletter

Get fresh paywall ideas, subscription insights, and mobile app news every month!

Status des Abonnements

Um den Abonnementstatus des Nutzers zu prüfen, senden Sie eine GET-Anfrage an https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}, wobei {originalTransactionId} die ID einer beliebigen Transaktionskette des Nutzers ist. Sie erhalten nun Transaktionen samt Status für jede Abonnement-Gruppe.

{
  "environment": "Sandbox",
  "bundleId": "com.adapty.sample_app",
  "data": [
    {
      "subscriptionGroupIdentifier": "39636320",
      "lastTransactions": [
        {
          "originalTransactionId": "1000000819078552",
          "status": 2,
          "signedTransactionInfo": "eyJraWQiOi...",
          "signedRenewalInfo": "eyJraWQiOi..."
        }
      ]
    }
  ]
}

Der Status-Key zeigt den aktuellen Abonnementstatus an. Darauf aufbauend können Sie entscheiden, ob Sie einem Nutzer Zugriff auf die kostenpflichtigen Funktionen der App gewähren möchten. Mögliche Werte:

  • 1 — Das Abonnement ist aktiv. Der Nutzer muss auf kostenpflichtige Funktionen zugreifen können.
  • 2 — Das Abonnement ist abgelaufen. Der Nutzer darf nicht mehr auf kostenpflichtige Funktionen zugreifen können. 
  • 3 — Der Status des Abonnements lautet Abrechnungswiederholung. Dies bedeutet, dass der Nutzer Probleme mit der Zahlung hat. Apple wird 60 Tage lang versuchen, die Karte zu belasten. Der Nutzer darf nicht auf kostenpflichtige Funktionen zugreifen können.  
  • 4 — Der Status des Abonnements ist Gnadenfrist. Das bedeutet, dass der Nutzer Zahlungsprobleme hat. Die Gnadenfrist ist in App Store Connect aktiviert und der Nutzer darf weiterhin auf kostenpflichtige Funktionen zugreifen.  
  • 5 — Das Abonnement wurde aufgrund einer Rückerstattung gekündigt. Der Nutzer darf nicht mehr auf kostenpflichtige Funktionen zugreifen können. 

Der SignedTransactionInfo-Key enthält Informationen über die letzte Transaktion in der Kette. Einzelheiten zum Format finden Sie oben.  

Informationen zur Abonnementverlängerung 

Der SignedRenewalInfo-Key enthält Informationen zur Abonnementverlängerung.

{
  "expirationIntent": 1,
  "originalTransactionId": "1000000819078552",
  "autoRenewProductId": "basic_subscription_1_month",
  "productId": "basic_subscription_1_month",
  "autoRenewStatus": 0,
  "isInBillingRetryPeriod": false,
  "signedDate": 1624520884048
}

Dank dieser Informationen können wir nachvollziehen, was mit dem Abonnement während der nächsten Zahlungsperiode geschehen wird. Sehen Sie beispielsweise, dass ein Nutzer die automatische Verlängerung gekündigt hat, können Sie ihm anbieten, zu einem anderen Abonnementplan zu wechseln oder ihm ein Werbeangebot machen. Sie können diese Art von Ereignissen mit Hilfe von Serverbenachrichtigungen ganz einfach verfolgen.  

Transaktionsverlauf des Nutzers

Um den Transaktionsverlauf des Nutzers abzurufen, senden Sie eine GET-Anforderung an https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}, wobei {originalTransactionId} die ID einer beliebigen Transaktionskette des Nutzers ist. Dafür erhalten Sie eine Reihe von Transaktionen, die chronologisch sortiert sind.  

{
	"revision": "1625872984000_1000000212854038",
	"bundleId": "com.adapty.sample_app",
	"environment": "Sandbox",
	"hasMore": true,
	"signedTransactions": [
		"eyJraWQiOiJ...",
		"joiRVMyNeyX...",
		"5MnkvOTlOZl...",
		...
	]
}

Eine Anfrage darf maximal 20 Transaktionen enthalten. Wenn ein Nutzer mehr Transaktionen hat, ist der Wert des hasMore-Flags wahr. Benötigen Sie die nächste Transaktionsseite, senden Sie die Anfrage erneut mit dem GET-Parameter. Sie enthält den Wert aus demselben Key. 

Benachrichtigungen zu Server-Transaktionen

Serverbenachrichtigungen helfen Ihnen, Informationen über neue Käufe, Verlängerungen, Abrechnungsprobleme usw. zu erhalten. Damit können Sie zudem genauere Analysen erstellen und die Verwaltung des Abonnementstatus vereinfachen.  

Die existierenden Serverbenachrichtigungen (V1) können die meisten Probleme lösen, sind aber oft unpraktisch. Meistens erhalten Sie für nur eine Aktion eines Nutzers mehrere Benachrichtigungen. Wenn ein Nutzer beispielsweise ein Abonnement aktualisiert, sendet Apple zwei Benachrichtigungen: DID_CHANGE_RENEWAL_STATUS und INTERACTIVE_RENEWAL. Um diesen Fall zu bearbeiten, müssen Sie den Status irgendwie speichern und prüfen, ob die zweite Benachrichtigung gesendet wurde. In einer neuen Version von Serverbenachrichtigungen (V2) gibt es nur eine Benachrichtigung für eine Aktion eines Nutzers. Das ist viel bequemer.   

Die zweite Version der Serverbenachrichtigungen enthält neue Ereignisse – OFFER_REDEEMED, EXPIRED und GRACE_PERIOD_EXPIRED. Sie erleichtern die Verwaltung des Abonnentenstatus erheblich. SUBSCRIBED und PRICE_INCREASE Ereignisse sind verbesserte Ereignisse aus der ersten Version.

Transaction notifications in StoreKit 2 feature new events

Benachrichtigungstypen

Die Benachrichtigungen haben jetzt Typen, sodass eine Benachrichtigung für jede Aktion eines Nutzers ausreicht, um zu verstehen, was passiert ist. 

Notifications in StoreKit 2 have types

Benachrichtigungstypen

{
  "notificationType": "SUBSCRIBED",
  "subtype": "INITIAL_BUY",
  "version": 2,
  "data": {
    "environment": "Sandbox",
    "bundleId": "com.adapty.sample_app",
    "appAppleId": 739104078,
    "bundleVersion": 1,
    "signedTransactionInfo": "eyJraWQiOi...",
    "signedRenewalInfo": "eyJraWQiOi..."
  }
}

Serverbenachrichtigungen enthalten wie bereits beschrieben Informationen im JWS-Format über Transaktionen und Verlängerungen.

Der Arbeit in einer Sandbox-Umgebung

Verwenden Sie die URL der Sandbox-Umgebung, um Einkäufe zu testen: https://api.storekit-sandbox.itunes.apple.com.

Die neue Version der Serverbenachrichtigungen steht noch nicht zum Testen zur Verfügung. Sobald sie verfügbar ist, können unterschiedliche URLs für Produktions- und Sandbox-Benachrichtigungen angegeben werden. Sie können die V2 für die Sandbox-Umgebung und die V1 für die Produktion und das Testen nutzen.

Mit App Store Connect können Sie jetzt Folgendes tun:

  • Den Kaufverlauf für den Sandbox-Nutzer löschen, was bedeutet, dass Sie dafür kein neues Konto mehr erstellen müssen.   
  • Das Land des Stores für den Sandbox-Nutzer ändern.
  • Den Verlängerungszeitraum des Sandbox-Abonnements ändern. Zum Beispiel können Sie einen monatlichen Kauf tätigen, der 1 Stunde statt 5 Minuten dauert.     

Fazit

Apple hat die Arbeit mit In-App-Käufen und Abonnements auf der Serverseite erheblich verbessert. Dies sind aus meiner Sicht die nützlichsten neuen Funktionen:

  • Vollwertiges Promo-Angebot und die Unterstützung von Promo-Codes; 
  • Einfachere und informativere Serverbenachrichtigungen;  
  • Die Chance, sich mit einem einfachen API-Aufruf über den Status des aktuellen Abonnements zu informieren;
  • Den Kaufverlauf der Sandbox des Nutzers zu löschen.   

Der Wechsel zu einer neuen API ist nicht schwierig. Es reicht aus, die originalTransactionId für jeden Kauf zu erhalten. Vermutlich sind diese bereits in Ihrer Basis enthalten.   

Das Schwierigste bei der Integration von Abonnements in eine App ist der Aufbau eines Analysesystems und die Optimierung der Wirtschaftlichkeit. Adapty kann Ihnen dabei helfen:

  • Integrierte Analysen bieten Erkenntnisse bezüglich der Metriken der App.
  • Die Kohortenanalyse zeigt, ob es Probleme mit der Wirtschaftlichkeit gibt.
  • A/B-Tests erhöhen die Rentabilität der App.   
  • Integrationen mit externen Systemen ermöglichen das Senden von Transaktionen an Attributions- und Produktanalysedienste.    
  • Werbekampagnen reduzieren Nutzerabwanderung.   
  • Open-Source-SDK ermöglicht es, Abonnements innerhalb weniger Stunden in eine App zu integrieren.  

Erfahren Sie mehr über diese Features, um Abonnements schneller in Ihre App zu implementieren und Ihre App schneller und besser zu monetarisieren.