Ich habe fast einen ganzen Tag mit dem Undelivered Consumption Percentage Non Zero Error verloren. Nicht weil es ein besonders kniffliger Fehler ist. Sondern weil ich den Delivery Status auf etwas anderes als DELIVERED gesetzt und die Consumption Percentage bei 60000 belassen hatte – und Apple lehnt diese Kombination ohne viel Federlesens ab.
Das ist ein etwas umständlicher Einstieg in die eigentliche Frage.
Wenn Sie herausfinden wollen, wie Sie automatisch auf Apple-Rückerstattungsanfragen reagieren können, ist das eine kleinere Aufgabe, als die meisten Teams annehmen. Der schwierige Teil war nie der API-Aufruf. Es geht darum, die passenden Daten dafür zu haben. Die Erklärung Feld für Feld finden Sie unter Apple CONSUMPTION_REQUEST explained. In diesem Beitrag geht es darum, alles so zu verdrahten, dass niemand dafür wach bleiben muss.
Das Wichtigste in Kürze
Apple-Rückerstattungsautomatisierung besteht aus einem Webhook plus einem PUT. Apple sendet eine CONSUMPTION_REQUEST, Sie antworten innerhalb von 12 Stunden.
Sie benötigen einen In-App Purchase Key, nicht Ihren App Store Connect API-Key. Beide sind nicht austauschbar.
Drei der fünf Felder sind erforderlich, und zwei davon haben Einschränkungen, die sich erst als Fehler bemerkbar machen.
Ohne Einwilligung des Kunden sollten Sie überhaupt nicht antworten, und diese Einwilligung lässt sich nicht nachträglich einholen.
Der Endpunkt wurde auf v2 umgestellt. Produktiv ist api.storekit.apple.com, und die Sandbox gibt Ihnen 5 statt 12 Stunden Zeit.
Wie Apple-Rückerstattungsanfragen für Entwickler tatsächlich funktionieren
Ein Kunde öffnet reportaproblem.apple.com, findet den Kauf innerhalb der letzten 90 Tage, wählt einen Grund aus und sendet die Anfrage ab. Wenn Sie App Store Server Notifications V2 konfiguriert haben, erhält Ihr Endpunkt eine CONSUMPTION_REQUEST als signierte JWS-Payload mit der Transaktion, dem Produkt und dem gewählten Grund.
Dieser Webhook ist der gesamte Auslöser für eine Rückerstattung bei einem App Store In-App-Kauf. In Ihrem Dashboard erscheint nichts, und es gibt keine Warteschlange, die Sie prüfen könnten. Deshalb erfahren viele Teams erst Wochen, nachdem sie dadurch Geld verloren haben, dass dieses System überhaupt existiert.
Dann haben Sie 12 Stunden Zeit.
Ein Punkt, den es zu korrigieren gilt, weil er überall wiederholt wird: Das Ganze ist nicht auf Consumables beschränkt. Apples aktuelle Dokumentation besagt, dass eine CONSUMPTION_REQUEST für jeden Produkttyp eintrifft: Consumable, Non-Consumable, Non-Renewing Subscription, Auto-Renewable Subscription. Ältere Anleitungen behaupten noch, es beträfe nur Consumables und Abos. Diese sind veraltet.
Was „Antworten“ konkret bedeutet
Sie senden einen JSON-Body per PUT an einen Endpunkt.
PUT https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId}
Die Sandbox lautet api.storekit-sandbox.apple.com, mit demselben Pfad. Bei Erfolg wird 202 Accepted zurückgegeben, nicht 200 – das bringt Leute durcheinander, wenn sie Statuscodes prüfen.
Die Authentifizierung erfolgt über ein kurzlebiges ES256-JWT, das mit einem In-App Purchase Key signiert wird, erzeugt in App Store Connect unter Users and Access, im Bereich Integrations. Es handelt sich nicht um Ihren App Store Connect API-Key. Die .p8-Datei lässt sich nur genau einmal herunterladen, nämlich bei der Erstellung. Wenn Sie sie verloren haben, müssen Sie einen neuen Key erzeugen und alles, was ihn verwendet, umstellen. Fragen Sie mich, woher ich das weiß.
Die fünf Felder – und die zwei, die es in sich haben
Der Body ist ein Consumption Request. Drei Felder sind Pflicht, zwei optional.
Customer Consented (erforderlich). Nur true, wenn eine echte Einwilligung vorliegt. Mehr dazu weiter unten, denn davon hängt alles ab.
Delivery Status (erforderlich). Ob der Kauf angekommen ist und funktioniert hat.
Sample Content Provided (erforderlich). Ob vor dem Kauf eine kostenlose Probe, ein Test oder Informationen zur Funktionalität vorhanden waren.
Consumption Percentage (optional). Wie viel konsumiert wurde, in Milliunits. 0 bis 100000, also entspricht 45.2 Prozent dem Wert 45200.
Refund Preference (optional). GRANT_FULL, GRANT_PRORATED, DECLINE oder NO_PREFERENCE.
Zwei Einschränkungen, die Sie jeweils einen Nachmittag kosten werden.
Wenn Delivery Status nicht DELIVERED ist, muss Consumption Percentage 0 sein. Alles andere scheitert mit Undelivered Consumption Percentage Non Zero Error. Das war mein Tag.
Und wenn Sie GRANT_PRORATED für ein Auto-Renewable Subscription senden, lassen Sie consumptionPercentage komplett weg. Apple berechnet den Wert selbst anhand der verstrichenen Zeit im Abrechnungszeitraum und lehnt Ihren Wert mit Consumption Percentage Auto Renewable Subscription Error ab. Die Fehlernamen sind immerhin aussagekräftig, mehr, als ich erwartet hatte.
Die Einwilligung ist die Hürde, keine Formalität
Apples Dokumentation ist an dieser Stelle ungewöhnlich direkt: Hat der Kunde nicht eingewilligt, antworten Sie nicht auf die Benachrichtigung. Nicht etwa mit Customer Consented auf false antworten. Gar nicht antworten.
Sie können die Einwilligung auch nicht nachträglich einholen, nachdem eine Anfrage eingegangen ist. Sie muss vorher existieren – der entsprechende Text gehört also in Ihre AGB oder Datenschutzerklärung, bevor Sie irgendetwas davon ausrollen, nicht erst nach Ihrer ersten strittigen Rückerstattung. Apples Vorgabe lautet, dass die Einwilligung freiwillig, spezifisch, informiert und eindeutig sein muss, dass Opt-in besser ist als Opt-out, und dass die Einholung allein in Ihrer Verantwortung liegt.
Praktisch heißt das: ein Satz, der besagt, dass Sie Kauf- und Nutzungsdaten mit Apple teilen dürfen, um bei der Prüfung von Rückerstattungsanfragen zu helfen, plus eine Möglichkeit zum Widerruf. Lassen Sie das von eigener juristischer Seite prüfen. Ich bin kein Anwalt, und das ist keine Rechtsberatung.
Wie Entwickler App-Store-Rückerstattungsanfragen automatisieren
Die Pipeline, in der Reihenfolge, in der ich sie aufbauen würde:
Konfigurieren Sie eine Production Server URL in App Store Connect unter „App Information“ und wählen Sie Version 2. Consumption Requests werden nicht über V1 übertragen.
Überprüfen Sie die JWS-Signaturkette anhand von Apples Root-Zertifikaten, bevor Sie auch nur einem einzigen Feld vertrauen.
Ordnen Sie die Transaktion einem Nutzer zu. Das appAccountToken, das Sie beim Kauf angehängt haben, ist der schnelle Weg. Ohne es müssen Sie anhand von Produkt und Datum rückwärts suchen, was schnell unübersichtlich wird.
Prüfen Sie die Einwilligung. Keine Einwilligung, keine Antwort. Protokollieren Sie es und machen Sie weiter.
Ziehen Sie die tatsächliche Nutzung heran. Keine Schätzung, kein Standardwert. Wenn Sie die Frage „Wie viel davon wurde genutzt?“ nicht beantworten können, ist genau das die eigentliche Lücke in Ihrer Automatisierung.
Erstellen Sie den ConsumptionRequest unter Beachtung der beiden oben genannten Einschränkungen.
Senden Sie ihn per PUT, bestätigen Sie die 202 und speichern Sie die gesendete Payload.
An Schritt 5 scheitern die meisten Versuche. Der API-Aufruf ist ein Nachmittag Arbeit. Ehrliche Consumption-Daten pro Nutzer für einen Kauf, der vor sieben Wochen getätigt wurde, zu haben, ist ein Datenmodellierungsproblem, das Sie vielleicht noch nicht gelöst haben.
Was es im Produktivbetrieb stabil macht
Jenseits des Happy Path gibt es vier Dinge:
Idempotenz. Apple wiederholt Benachrichtigungen, und Ihr Handler wird dieselbe zweimal sehen. Deduplizieren Sie über die Notification-UUID, sonst reichen Sie doppelte Antworten ein.
Alerting, das tatsächlich jemanden benachrichtigt. Ein Handler, der stillschweigend ausfällt, ist schlimmer als gar kein Handler, weil Sie sich dann in falscher Sicherheit wiegen.
Ein Sandbox-Testaufbau. Die Sandbox gibt Ihnen 5 statt 12 Stunden Zeit, sodass Sie das nicht einfach mal manuell testen können. Automatisieren Sie es per Skript.
Und ein Protokoll dessen, was Sie pro Fall gesendet haben. Wird eine Rückerstattung abgelehnt, wollen Sie wissen, welche Preference Sie eingereicht haben und warum. Wird eine trotz Ihres DECLINE gewährt, wollen Sie das ebenfalls wissen.
Ehrliche Einschätzung: ein paar Tage Verbindungsarbeit, wenn Sie schon einmal Webhook-Infrastruktur gebaut haben, und ein paar Wochen, bis Sie dem System vertrauen. Der größte Teil davon betrifft nicht die Apple-Seite.
Oder lassen Sie es etwas anderes erledigen
Wenn Sie bereits RevenueCat oder Adapty einsetzen, prüfen Sie zuerst, was diese bereits mitbringen. Beide haben Funktionen zur Beantwortung von Rückerstattungsanfragen ergänzt, und Ihre Receipts sowie die Kundenzuordnung sind dort bereits vorhanden.
Speziell entwickelte Software für das Apple-Rückerstattungsmanagement geht noch weiter. Refund Sensor läuft mit den App Store Connect Keys, die Sie bereits haben, beantwortet jede Anfrage innerhalb des Zeitfensters mit Transaktionsnachweisen und protokolliert das Ergebnis pro Fall. Kein SDK, keine Code-Änderungen, rund fünf Minuten zum Verbinden. Einen Vergleich des breiteren Marktes finden Sie unter Apple refund management tools, falls Sie lieber vergleichen möchten.
Die Kundenseite, da häufig gefragt
Zur Frage, wie man eine Rückerstattung für App-Store-Käufe beantragt, aus Sicht der Nutzer: reportaproblem.apple.com, anmelden, den Kauf innerhalb der letzten 90 Tage finden, einen Grund auswählen. Apple teilt Kunden mit, dass sie innerhalb von 48 Stunden eine Rückmeldung erhalten.
Sie können dasselbe Formular auch direkt in Ihrer App anzeigen, über das Refund Request Sheet von StoreKit, das unzufriedene Nutzer zu Apple statt zu einer Ein-Stern-Bewertung lenkt. Es kann keine Rückerstattung gewähren. Das kann nur Apple. Apple Support's guide to requesting a refund zeigt, was der Kunde dabei sieht.
Das sollte man klar sagen, weil App Store Refund Management oft übertrieben dargestellt wird: Sie können eine Rückerstattung weder genehmigen noch ablehnen. Sie können Apple während der Entscheidung Beweise vorlegen und eine Preference angeben. Mehr Einfluss haben Sie nicht – und das ist bereits mehr, als die meisten Entwickler nutzen.
Quellen
Häufig gestellte Fragen
Ja. Genau dafür gibt es die CONSUMPTION_REQUEST-Benachrichtigung und den Send Consumption Information-Endpunkt. Sie können die Rückerstattung weder gewähren noch ablehnen, aber Apple bezieht das, was Sie senden, als Entscheidungsgrundlage mit ein.
App Store Server Notifications V2 empfangen, die JWS-Signatur prüfen, die Transaktion einem Nutzer zuordnen, die Einwilligung prüfen, echte Consumption-Daten zusammenstellen und diese innerhalb von 12 Stunden per PUT an den v2-Consumption-Endpunkt senden. Alles Weitere betrifft Idempotenz, Alerting und Protokollierung.
Einen In-App Purchase Key aus „Users and Access“, im Bereich „Integrations“. Ihr App Store Connect API-Key authentifiziert diese Aufrufe nicht, und Sie erhalten einen 401, der wie ein Zeitproblem aussieht.
Nein, das ist optional. Ist deliveryStatus jedoch nicht DELIVERED, muss der Wert 0 sein, und bei einem prorated Auto-Renewable Subscription müssen Sie das Feld komplett weglassen.
Antworten Sie nicht. Apples Vorgaben sind hier eindeutig, und die Einwilligung lässt sich nicht einholen, nachdem die Anfrage eingegangen ist.
Sie ist eine Entscheidungsgrundlage, keine Garantie. Apple gibt an, dass verschiedene Faktoren einfließen. In der Praxis wird ein gut belegtes DECLINE bei einem stark genutzten Kauf deutlich häufiger abgelehnt als eine Anfrage, auf die niemand geantwortet hat.



