BlogRight arrowTutorialRight ArrowiOS In-App-Käufe, Teil 4: Serverseitige Kaufvalidierung
BlogRight arrowTutorialRight ArrowiOS In-App-Käufe, Teil 4: Serverseitige Kaufvalidierung

iOS In-App-Käufe, Teil 4: Serverseitige Kaufvalidierung

iOS In-App-Käufe, Teil 4: Serverseitige Kaufvalidierung
Listen to the episode
iOS In-App-Käufe, Teil 4: Serverseitige Kaufvalidierung

Was ist die Server-Kaufvalidierung?

Die Servervalidierung (serverseitige Empfangsvalidierung) ist eine Methode, um die Echtheit eines Kaufs zu bestätigen. Im Gegensatz zur gerätebasierten Validierung geschieht die Servervalidierung auf dem, Sie ahnen es, Server. Die Validierung umfasst nichts anderes als dass das Gerät oder der Server eine Anfrage an Apple schickt, um herauszufinden, ob der Kauf tatsächlich stattgefunden hat und gültig ist.

⭐️ Download our guide on in-app techniques which will make in-app purchases in your app perfect

Warum müssen wir Käufe validieren?

Wir sollten erwähnen, dass die Servervalidierung keine Pflicht ist. In-App-Käufe würden auch ohne sie funktionieren. Sie bietet Ihnen jedoch einige Vorteile:

  1. Tiefgreifende Analysemöglichkeiten: Dies ist besonders bei Abonnements von Vorteil, da alles, was nach der Aktivierung geschieht, nicht vom Gerät verarbeitet wird. Ohne die serverseitige Kaufvalidierung sind Sie nicht in der Lage, den aktuellen Status des Abonnements abzufragen und ob der Nutzer das Abonnement verlängert oder storniert hat. Auch Zahlungsprobleme usw. wären sonst nicht aufspürbar.
  2. Überprüfung der Echtheit des Kaufs: Sie können sichergehen, dass der Kauf wirklich stattgefunden hat und der Nutzer für Ihr Produkt bezahlt hat.
  3. Plattformübergreifende Abonnements: Wenn Sie den Status eines Abonnements in Echtzeit überprüfen können, haben Sie die Chance, dieses Abonnement über mehrere Plattformen hinweg zu synchronisieren. Ein Nutzer, der beispielsweise das Abonnement über ein iOS Gerät gekauft hat, könnte es so auch auf einem Android Mobilgerät, auf Ihrer Website oder anderen Plattformen nutzen. 
  4. Zugriff auf Inhalte über den Server: Dies schützt Sie vor Nutzern, die versuchen, ohne Abonnement auf Ihren Content zuzugreifen, indem sie einfach Anfragen an den Server durchführen. 

Allein der erste Vorteil reicht aus unserer Erfahrung aus, um eine Kaufabwicklung über den Server einzurichten.

Kaufvalidierung

In der Regel sieht der Vorgang der Kaufvalidierung auf iOS wie folgt aus:

Generieren des gemeinsamen Geheimnisses (shared secret)

Sie müssen das gemeinsame Geheimnis angeben, um eine Anforderung zur Zahlungsvalidierung zu autorisieren. Sie können es im App Store Connect generieren.

Das gemeinsame Geheimnis kann für eine bestimmte App (App-spezifisches Geheimnis) oder für alle Apps im Konto (primäres Geheimnis) erstellt werden.

Öffnen Sie die Seite der App in App Store Connect, gehen Sie zu In-App-Käufe → Verwalten und klicken Sie auf App-spezifisches gemeinsames Geheimnis, um ein App-spezifisches Geheimnis zu generieren. In dem sich nun öffnenden Fenster können Sie einen neuen Token generieren oder das vorhandene kopieren.

Generieren eines App-spezifischen gemeinsamen Geheimnisses

Öffnen Sie die Seite „Benutzer und Zugriff“ und wählen Sie die Registerkarte „Gemeinsames Geheimnis“ aus, um das Geheimnis für alle Apps in Ihrem Konto zu erhalten.

So finden Sie ein gemeinsames Geheimnis für alle Apps

Zahlungsvalidierung anfordern

Sobald Sie das gemeinsame Geheimnis erhalten, können Sie die Quittungen versenden, um diese auf den Apple Servern zu validieren. Das erfolgt über eine verifyReceipt Anfrage. Senden Sie eine POST Anfrage an https://buy.itunes.apple.com/verifyReceipt. Im JSON Body der Anfrage geben Sie das gemeinsame Geheimnis im Password Feld ein und die Quittung bei receipt-data. Optional gibt es auch den exclude-old-transactions Parameter. Ist dieser true, erhalten Sie für jedes automatisch verlängerte Abonnement die letzte Transaktion statt des kompletten Verlaufs.

Hier ist der Payload der Anfrage zur Kaufvalidierung:

{
  "password": "f4d35830e3...52aae",
  "receipt-data": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
  "exclude-old-transactions": false
}

Wenn Sie in einer Sandbox-Umgebung arbeiten (also zu Testzwecken), senden Sie die Validierungsanfragen an https://sandbox.itunes.apple.com/verifyReceipt. Das gemeinsame Geheimnis sowie der Payload und die Antwortformate bleiben gleich. 

Beachten Sie, dass Sie eine in der Sandbox-Umgebung erstellte Quittung nicht auf einem Produktionsserver validieren können und umgekehrt. Aus diesem Grund ist es in realen Systemen am besten, die erste Anfrage an den Produktionsserver zu leiten und sie an den Sandbox-Server umzuleiten, falls der Status Key den Fehlercode 21007 zurückgibt. Dieses Verhalten ist ein Muss während der App-Überprüfung. Nur so können Apple-Mitarbeiter Käufe testen, während auch echte Nutzern die Chance haben, Ihre App zu kaufen.

Unter den vielen zu beachtenden Fehlern gibt es den 21004 Fehlercode. Dieser bedeutet, dass wir das falsche Geheimnis verwenden. Behalten Sie immer den Überblick, da es sich sowohl auf die Benutzererfahrung als auch auf die Genauigkeit der Analyse auswirkt. Im schlimmsten Fall kann die App aus dem App Store entfernt werden, wenn der Nutzer nach der Bezahlung nie Zugriff auf die Premium-Features erhält.

War die Validierung erfolgreich (Status=0), enthält die Antwort die Details der Transaktionen des Nutzers.

Hier ist die Antwort auf die Anforderung zur Zahlungsvalidierung:

{
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 123,
    "app_item_id": 123,
    "bundle_id": "com.adapty.sample_app",
    "application_version": "1",
    "download_id": 123,
    "version_external_identifier": 123,
    "receipt_creation_date": "2021-04-28 19:42:01 Etc/GMT",
    "receipt_creation_date_ms": "1619638921000",
    "receipt_creation_date_pst": "2021-04-28 12:42:01 America/Los_Angeles",
    "request_date": "2021-08-09 18:26:02 Etc/GMT",
    "request_date_ms": "1628533562696",
    "request_date_pst": "2021-08-09 11:26:02 America/Los_Angeles",
    "original_purchase_date": "2017-04-09 21:18:41 Etc/GMT",
    "original_purchase_date_ms": "1491772721000",
    "original_purchase_date_pst": "2017-04-09 14:18:41 America/Los_Angeles",
    "original_application_version": "1",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "1000000831360853",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "purchase_date_ms": "1619638918000",
        "purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-05-05 19:41:58 Etc/GMT",
        "expires_date_ms": "1620243718000",
        "expires_date_pst": "2021-05-05 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000397200750",
        "is_trial_period": "true",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "basic_subscription_1_month",
      "transaction_id": "230001020690335",
      "original_transaction_id": "1000000831360853",
      "purchase_date": "2021-08-04 19:41:58 Etc/GMT",
      "purchase_date_ms": "1628106118000",
      "purchase_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
      "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
      "original_purchase_date_ms": "1619638918000",
      "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
      "expires_date": "2021-08-11 19:41:58 Etc/GMT",
      "expires_date_ms": "1628710918000",
      "expires_date_pst": "2021-08-11 12:41:58 America/Los_Angeles",
      "web_order_line_item_id": "230000438372383",
      "is_trial_period": "false",
      "is_in_intro_offer_period": "false",
      "in_app_ownership_type": "PURCHASED",
      "subscription_group_identifier": "272394410"
    },
    {
      "quantity": "1",
      "product_id": "basic_subscription_1_month",
      "transaction_id": "230001017218955",
      "original_transaction_id": "1000000831360853",
      "purchase_date": "2021-07-28 19:41:58 Etc/GMT",
      "purchase_date_ms": "1627501318000",
      "purchase_date_pst": "2021-07-28 12:41:58 America/Los_Angeles",
      "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
      "original_purchase_date_ms": "1619638918000",
      "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
      "expires_date": "2021-08-04 19:41:58 Etc/GMT",
      "expires_date_ms": "1628106118000",
      "expires_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
      "web_order_line_item_id": "230000849023623",
      "is_trial_period": "false",
      "is_in_intro_offer_period": "false",
      "in_app_ownership_type": "PURCHASED",
      "subscription_group_identifier": "272394410"
    }
  ],
  "latest_receipt": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
  "pending_renewal_info": [
    {
      "auto_renew_product_id": "basic_subscription_1_month",
      "product_id": "basic_subscription_1_month",
      "original_transaction_id": "1000000831360853",
      "auto_renew_status": "1"
    }
  ],
  "status": 0
}

Die Antwort ist ziemlich umständlich und wurde in der neuen Version der App Store Server API vereinfacht. Die aktuelle Implementierung ist jedoch nicht allzu umzusetzen.

Start for free

You don't need to write server code yourself,

because we did it for you. Try Adapty SDK!

Start for free

Abrufen des Abonnementstatus und des Transaktionsverlaufs

Sie benötigen eine Möglichkeit, den Abonnementstatus zu ermitteln, damit Sie wissen, ob der Nutzer Zugriff auf die Premium-Funktionen der App hat oder nicht. In der aktuellen Version der API gibt es keine spezielle Anfrage zum Abrufen des Abonnementstatus, sodass Sie in jedem Fall mit dem Transaktionsverlauf arbeiten müssen.

Der latest_receipt_info Array enthält standardmäßig alle In-App-Käufe des jeweiligen Nutzers, außer verbrauchbare Produkte, die direkt innerhalb der App erfolgen. So können Sie den gesamten Kaufverlauf des Nutzers abrufen. Das ist sowohl für Analysen als auch für die Bestimmung des aktuellen Abonnementstatus sehr nützlich. 

Die Transaktionen werden immer schon sortiert, sodass die neuesten zuerst angezeigt werden. Um sicherzugehen, empfehle ich dennoch, eine eigene Sortierung nach Datum für Transaktionen zu implementieren.

Der Transaktions-Payload:

{
  "quantity": "1",
  "product_id": "basic_subscription_1_month",
  "transaction_id": "1000000831360853",
  "original_transaction_id": "1000000831360853",
  "purchase_date": "2021-04-28 19:41:58 Etc/GMT",
  "purchase_date_ms": "1619638918000",
  "purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
  "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
  "original_purchase_date_ms": "1619638918000",
  "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
  "expires_date": "2021-05-05 19:41:58 Etc/GMT",
  "expires_date_ms": "1620243718000",
  "expires_date_pst": "2021-05-05 12:41:58 America/Los_Angeles",
  "web_order_line_item_id": "230000397200750",
  "is_trial_period": "true",
  "is_in_intro_offer_period": "false",
  "in_app_ownership_type": "PURCHASED",
  "subscription_group_identifier": "272394410"
}

Es reicht aus, die letzte Transaktion der Kette abzurufen und sich die Eigenschaft expires_date anzusehen, um den aktuellen Abonnementstatus zu überprüfen. Die Ausnahme davon wäre die Gnadenfrist, auf die wir später noch eingehen werden.

Zur Analyse empfehle ich, die folgenden Eigenschaften zu speichern:

  • product_id: die Textkennung des gekauften Produkts.
  • transaction_id: die eindeutige numerische Kennung der Transaktion. Jeder Kauf oder jede Verlängerung hat eine eigene Kennung, die verwendet werden kann, um nachzuvollziehen, ob die Transaktion zuvor verarbeitet wurde.
  • original_transaction_id: die eindeutige numerische Kennung der Transaktionskette. Sie stimmt mit transaction_id bei der Abonnement- oder Testaktivierung überein. Bei weiteren Abonnementverlängerungen ändert sich jedoch die transaction_id, während die original_transaction_id unverändert bleibt. Dies ist praktisch, um nachzuverfolgen, wie oft das Abonnement verlängert wurde.
  • purchase_date и original_purchase_date: das Transaktionsdatum und das ursprüngliche Transaktionsdatum. Dies funktioniert nach der gleichen Logik wie die vorherige Eigenschaft. 
  • expires_date: das Ablaufdatum des Abonnements.
  • cancellation_date: das Datum der Rückerstattung (und nicht die Kündigung des Abonnements wie der Name vermuten lässt). Wenn die Antwort dieses Feld enthält, bedeutet dies, dass Sie den Zugriff des Nutzers auf sein Abonnement beenden und die Kündigung in Ihren Analysen berücksichtigen sollten. Sie erhalten keine Zahlung aus dieser Transaktion.
  • is_in_intro_offer_period: dieses Flag zeigt an, ob das Einführungsangebot bei der Abonnementaktivierung verwendet wurde.
  • is_trial_period: dieses Flag zeigt an, ob es bei der Aktivierung des Abonnements einen Testzeitraum gab.
  • offer_code_ref_name: der Angebotscode, der bei der Aktivierung des Abonnements verwendet wurde.
  • promotional_offer_id: die Textkennung des Aktionsangebots, das bei der Eingabe des aktuellen Abrechnungszeitraums verwendet wurde.
  • in_app_ownership_type: der Eigentumstyp des Abonnements. So erfahren Sie, ob der Nutzer das Produkt selbst gekauft oder im Rahmen des Familienabos erhalten hat. Einige mögliche Werte sind:
  • PURCHASED: der Benutzer hat das Produkt selbst gekauft.
  • FAMILY_SHARED: der Benutzer hat das Produkt als Teil des Familienabonnements erhalten.

Apple hat auf der WWDC 2021 angekündigt, den Transaktionsinformationen ein appAccountToken Feld hinzuzufügen. Es enthält die Kennung des Nutzers in Ihrem System. Diese Kennung muss im UUID-Format vorliegen und wird auf der App-Seite definiert, wenn der Kauf initialisiert wird. Ist sie definiert, wird sie bei allen Transaktionen dieser Kette zurückgegeben (Abonnementverlängerungen, Abrechnungsprobleme usw.), sodass Sie leicht nachvollziehen können, welcher Nutzer den Kauf getätigt hat.

Behalten Sie auch den Parameter subscription_group_identifier im Auge. Wenn der Benutzer zuvor Transaktionen mit aktiven Test- oder Einführungsangeboten hatte, sollte er keinen Zugriff mehr auf diese Art Abonnements haben. Dies sollte serverseitig nachverfolgt werden.

Informationen zur Abonnementverlängerung, Kulanzzeitraum und Abrechnungsprobleme

Das Array 'pending_renewal_info' speichert die Daten zur Abonnement-Verlängerung. Sie können so nachvollziehen, was mit dem Abonnement in der nächsten Abrechnungsperiode geschehen soll. Wenn Sie beispielsweise festgestellt haben, dass sich der Nutzer gegen die automatische Verlängerung entschieden hat, können Sie ihm vorschlagen, zu einem anderen Plan zu wechseln, oder ihm ein Werbeangebot unterbreiten. Diese Ereignisse können bequem mit Serverbenachrichtigungen nachverfolgt werden, auf die ich in Kürze eingehen werde.

Daten zur Abonnementverlängerung:

{
  "auto_renew_product_id": "basic_subscription_1_month",
  "product_id": "basic_subscription_1_month",
  "original_transaction_id": "1000000831360853",
  "auto_renew_status": "1"
}
  • product_id: die Textkennung des gekauften Produkts.
  • auto_renew_product_id: die Textkennung des Produkts, das im nächsten Abrechnungszeitraum aktiviert wird. Wenn sie sich von der aktuellen product_id unterscheidet, bedeutet dies, dass der Benutzer seinen Abonnementtyp geändert hat.
  • auto_renew_status: dieses Flag zeigt an, ob das Abonnement in die nächste Abrechnungsperiode fortgesetzt werden soll.
  • expiration_intent: der Grund für das Ablaufen des Abonnements. Einige mögliche Werte sind:
  • 1 — der Nutzer hat das Abonnement selbst gekündigt,
  • 2 — das Abonnement wurde aufgrund von Abrechnungsproblemen gekündigt,
  • 3 — der Nutzer hat einer Preiserhöhung nicht zugestimmt,
  • 4 — das Abonnementprodukt war nicht zur Verlängerung verfügbar, z. B. weil es aus App Store Connect entfernt wurde.
  • 5 — unbekannt.
  • grace_period_expires_date: das Ablaufdatum des Kulanzzeitraums (sofern vorhanden). In diesem Fall sollte der Nutzer bis zu dem hier angegebenen Datum Zugriff auf die Premium-Funktionen haben, nicht aber auf das in der Transaktion selbst genannte Datum. Wenn Sie diesen Key haben, können Sie den Nutzer benachrichtigen, dass er seine Karteninformationen aktualisieren oder sein Guthaben aufladen muss.
  • is_in_billing_retry_period: dieses Flag zeigt an, ob das Abonnement den wiederholenden Abrechnungsstatus hat. Dies bedeutet, dass das Abonnement nicht gekündigt wurde, Apple die Verlängerungszahlung jedoch nicht in Rechnung stellen konnte und dies über einen Zeitraum von 60 Tagen weiterhin versuchen wird.
  • offer_code_ref_name: der Promocode, der im nächsten Abrechnungszeitraum verwendet wird.
  • promotional_offer_id: die Textkennung des Werbeangebots, das im nächsten Abrechnungszeitraum verwendet wird.
  • price_consent_status: diese Flag zeigt an, ob der Nutzer der bevorstehenden Preiserhöhung des Abonnement zugestimmt hat. Beträgt der Wert 0, sollten Sie dem Nutzer ein anderes Produkt oder ein Werbeangebot anbieten, damit er das Abonnement nicht kündigt.

Verbrauchbare, nicht verbrauchbare und nicht verlängerbare Abonnements

Wenn der Nutzer keine Produkte mit automatischer Verlängerung hat, werden die Keys latest_receipt_info und pending_renewal_info nicht zurückgegeben. In diesem Fall finden Sie Transaktionen in Receipt → in_app. Das Transaktionsformat ähnelt den automatisch erneuerbaren Transaktionen, hat aber keine Felder für Ablauf, Verlängerungen, Angebote und andere Eigenschaften, die nur für automatisch erneuerbare Transaktionen gelten.

Beachten Sie, dass auch bei Transaktionen mit automatischer Verlängerung receipt → in_app ausgegeben wird. Besser ist es jedoch, latest_receipt_info zu verwenden, da die darin enthaltenen Abonnementdaten die aktuellen sind.

Benachrichtigungen zu Server-Transaktionen

Vor einiger Zeit mussten Sie noch ein komplexes System entwickeln, um Änderungen des Abonnementstatus zu verfolgen. Um beispielsweise zu verstehen, ob das Abonnement verlängert wurde oder nicht, mussten Sie zuvor ab 24 Stunden vor Ablauf des Abonnements stündlich eine Abonnement-Statusanfrage an Apple-Server senden. Apple fügte im Laufe der Zeit immer mehr Serverbenachrichtigungen hinzu. Diese decken jetzt praktisch alle wichtigen Abonnement-Ereignisse ab. Das ist wirklich praktisch: Sobald es auf Apples Seite Änderungen gibt, werden Sie auf Ihrem eigenen Server darüber benachrichtigt. Sie werden über neue Käufe, Abonnementverlängerungen, Abrechnungsprobleme usw. benachrichtigt. Dadurch können Sie viel genauere Analysen durchführen und den Abonnementstatus viel einfacher verwalten.

Sie können die Serverbenachrichtigungen in App Store Connect aktivieren. Öffnen Sie die Seite der App und gehen Sie zu Allgemein -> App-Informationen. Fügen Sie als Nächstes den Link in das Feld URL für App Store-Serverbenachrichtigungen ein und speichern Sie Ihre Änderungen.

Serverbenachrichtigung:

{
  "notification_type": "DID_RENEW",
  "password": "f4d35830e3...52aae",
  "environment": "PROD",
  "auto_renew_product_id": "basic_subscription_1_month",
  "auto_renew_status": "true",
  "unified_receipt": {
    "status": 0,
    "environment": "Production",
    "latest_receipt_info": [
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "230001020690335",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-08-04 19:41:58 Etc/GMT",
        "purchase_date_ms": "1628106118000",
        "purchase_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-08-11 19:41:58 Etc/GMT",
        "expires_date_ms": "1628710918000",
        "expires_date_pst": "2021-08-11 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000438372383",
        "is_trial_period": "false",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED",
        "subscription_group_identifier": "272394410"
      },
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "230001017218955",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-07-28 19:41:58 Etc/GMT",
        "purchase_date_ms": "1627501318000",
        "purchase_date_pst": "2021-07-28 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-08-04 19:41:58 Etc/GMT",
        "expires_date_ms": "1628106118000",
        "expires_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000849023623",
        "is_trial_period": "false",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED",
        "subscription_group_identifier": "272394410"
      }
    ],
    "latest_receipt": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
    "pending_renewal_info": [
      {
        "auto_renew_status": "1",
        "auto_renew_product_id": "basic_subscription_1_month",
        "product_id": "basic_subscription_1_month",
        "original_transaction_id": "1000000831360853"
      }
    ]
  },
  "bid": "com.adapty.sample_app",
  "bvrs": "0"
}

Das Format der Serverbenachrichtigung ähnelt der Zahlungsvalidierungsantwort. Die Transaktionsdetails werden in unified_receipt → Latest_receipt_info gespeichert. Der password key speichert das gemeinsame Passwort für Ihre App, wodurch Sie die Echtheit der Anfrage überprüfen können. Der notification_type Key speichert den Ereignistyp. Die nützlichsten sind meiner Meinung nach:

  • DID_CHANGE_RENEWAL_STATUS: Dies bedeutet, dass der Benutzer die automatische Verlängerung des Abonnements deaktiviert oder aktiviert hat (letzteres ist viel seltener). Wenn der Nutzer sich gegen die automatische Verlängerung entschieden hat, möchten Sie ihn möglicherweise ermutigen, sich wieder Ihren aktiven Abonnenten anzuschließen.
  • DID_FAIL_TO_RENEW: Das Abonnement konnte aufgrund von Abrechnungsproblemen nicht verlängert werden. Sie sollten den Nutzer darüber informieren, damit das Abonnement nicht automatisch gekündigt wird.
  • DID_RENEW: Das Abonnement wurde erfolgreich verlängert. 
  • REFUND: Das Abonnement wurde zurückerstattet. Sie sollten den Zugriff des Nutzers auf Premium-Funktionen einschränken und die Rückerstattung (d. h. den Verlust des Geldes) in Ihren Analysen berücksichtigen.

Fazit

Die Servervalidierung verbessert die Analysen für Daten, die Sie von Ihrer App sammeln. Es erschwert Betrügern auch den Zugriff auf Ihre Premium-Inhalte und ermöglicht es Ihnen, Ihre Abonnements plattformübergreifend abzuschließen. Gleichzeitig kann die Implementierung der Servervalidierung ziemlich viel Zeit in Anspruch nehmen, insbesondere wenn Sie präzise Daten benötigen. Dazu müssten viele Nebenfälle berücksichtigt werden: Abonnement-Upgrades, Abonnement-Crossgrades, Testzeiträume, Promo-/Intro-Angebote, Schonfristen, Rückerstattungen, Familienabonnements usw. Sie müssen auch verschiedene Nuancen kennen und berücksichtigen. Apple bietet eine Reduzierung der Provision von 30 % auf 15 % für Abonnements, die regelmäßig um mehr als ein Jahr verlängert werden.

Further reading

Android in-app purchases, part 2: processing purchases with the Google Play Billing Library
Android in-app purchases, part 2: processing purchases with the Google Play Billing Library
March 24, 2021
12 min read
Android in-app purchases, part 4: billing Library Error Codes and how not to screw up with testing
Android in-app purchases, part 4: billing Library Error Codes and how not to screw up with testing
September 29, 2021
6 min read
Cohort Analysis for mobile apps: is your app making cash?
Cohort Analysis for mobile apps: is your app making cash?
June 28, 2021
10 min read