Hallo. Ich bin zm soft und habe mich Ende letzten Jahres (Ende 2023) als Entwickler registriert und begonnen, Apps zu veröffentlichen. Wir planen auch die Veröffentlichung einer App für Entwickler, um Entwicklern bei der Überwindung von geschlossenen Tests zu helfen. Schauen Sie sich diese gerne an.
Implementieren Sie In-App-Käufe in Ihren Apps? Als App-Entwickler ist es das Beste, wenn Benutzer zufrieden sind und Sie dafür bezahlt werden. Das ist der Moment, in dem sich die Mühe der Entwicklung lohnt. Diesmal möchte ich in einem Artikel erklären, wie man das umsetzt.
Was sind In-App-Käufe überhaupt?
Wie Sie wahrscheinlich wissen, werden In-App-Käufe in zwei Kategorien unterteilt:
- Verbrauchsprodukte
- Abonnementprodukte
In beiden Fällen greifen Sie über die Google Play Billing Library auf Artikel zu, die Sie vorher im Play Store konfiguriert haben. Ein Teil der Einnahmen wird an Google als Gebühr gezahlt. Die Implementierung ist für beide ähnlich, aber Abonnementprodukte haben einige einzigartige Überlegungen. Ich werde diese basierend auf meinen praktischen Erfahrungen erklären.
Play Store-Konfiguration
Produktregistrierung
Zunächst müssen Sie Produkte registrieren, damit sie angezeigt werden. Preis, Name und andere Details können später geändert werden. Allerdings können Produktkennungen (IDs) nicht geändert werden. Wenn Sie planen, Testprodukte später in der Produktionsumgebung zu verwenden, sollten Sie IDs wie [product_1,2,3...] oder [sub_1,2,3...] verwenden, die wiederverwendet werden können. Die Registrierung erfolgt über [Monetize], wobei [In-app products] und [Subscriptions] für Verbrauchsprodukte bzw. Abonnementprodukte stehen. Sie können Produkte über die [Create xx]-Schaltfläche registrieren.

Sie können sich grundsätzlich auf dem Eingabebildschirm orientieren, aber die Preisgestaltung für Abonnementprodukte war verwirrend. Ich wusste anfangs nicht, wie ich Preise für alle Regionen auf einmal einstellen konnte. Ich dachte, ich müsste jede Region einzeln eingeben, aber Sie müssen [Set prices] - [Country / region] - [Set price] befolgen.

Wenn Sie die Schaltfläche drücken, wird das folgende Dialogfeld angezeigt, in dem Sie Preise auf einmal einstellen können.

Registrierung von Test-Konten
Anschließend registrieren Sie Test-Konten. Wenn Sie Käufe testen, ohne Test-Konten zu registrieren, entstehen echte Zahlungen. Registrieren Sie Test-Konten, indem Sie eine Mailingliste hinzufügen. Gehen Sie auf dem Startbildschirm der Play Console (Entwickler-Dashboard) zu [Settings] - [License testing].

Wenn ein Benutzer in der Mailingliste das Kaufverfahren durchführt, wird die Zahlungsinformation als "Testkarte" angezeigt, und Sie können Tests durchführen, ohne tatsächlich zu bezahlen.
Implementierungsarbeit
Die Implementierung auf der App-Seite erfordert folgende Aufgaben:
- Bibliothek-Import
- Verbindung zum Store und Abrufen von Produktinformationen
- Implementierung der Produktanzeigeseite
- Kaufanforderung
- Verarbeitung nach Kaufabschluss
Im Folgenden werde ich jede dieser Aufgaben im Detail erklären.
Bibliothek-Import
Um die Bibliothek zu importieren, benötigen Sie Folgendes:
- Änderung von build.gradle
- Änderung von android.manifest
Ein Beispiel für die build.gradle-Änderung:
dependencies {
implementation "com.android.billingclient:6.0.0"
}
Ein Beispiel für die manifest-Änderung:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.android.vending.BILLING" />
<application
Überprüfen Sie die offizielle Dokumentation für Versionen und Details.
Verbindung zum Store und Abrufen von Produktinformationen
Durch Initialisierung des BillingClient und Ausführung von startConnection ist die Kommunikation mit dem Store möglich. Mit queryProductDetailsAsync können Sie Informationen zu jedem Produkt abrufen. Weitere Einzelheiten finden Sie in der offiziellen Dokumentation. Bei mir funktionierte der Massenabruf irgendwie nicht, daher habe ich eine Liste der Produkte abgerufen und dann mit queryProductDetailAsync die Details jedes Produkts einzeln abgerufen. Sie können zwischen Verbrauchsprodukten und Abonnementprodukten durch den Typ unterscheiden: inapp oder subs.
Implementierung der Produktanzeigeseite
Sobald Sie Produktinformationen haben, können Sie diese als Liste anzeigen und Kauf-Optionen auswählen lassen. Bei der Verwendung von Abonnementprodukten ist eine Einhaltung der Abonnement-Richtlinie erforderlich. In meinem Fall wurde das App-Update zweimal abgelehnt:
- Unvollständige Lokalisierung von Preis und Bedingungen
- Fehlende Angabe von Angebotsbedingungen
Zuerst zur unvollständigen Lokalisierung von Preis und Bedingungen. Das Problem war die Anzeige des Abrechnungszeitraums (z.B. $10/month). Wenn die App in einer Region einer nicht unterstützten Sprache ausgeführt wurde, wurde das Wort "month" nicht übersetzt. Wenn Sie BillingClient zur Informationsbeschaffung verwenden, wird der Preisbetrag als Zeichenkette formattedPrice mit Einheit empfangen und die App muss nicht übersetzen. Die Periodeninfo wird jedoch im ISO8601-Format wie P1M mitgeteilt, und die App muss dies übersetzen. Ich habe diese Anforderung erfüllt, indem ich für jede Sprache Umwandlungsfunktionen hinzugefügt habe:
fun formatBillingPeriod(billingPeriod: String, languageCode: String): String {
return when(languageCode) {
"en" -> {
when (billingPeriod) {
"P1W" -> "weekly"
"P1M" -> "monthly"
"P3M" -> "every 3 months"
"P6M" -> "every 6 months"
"P1Y" -> "annually"
else -> "unknown"
}
}
"ja" -> {
when (billingPeriod) {
"P1W" -> "週間"
"P1M" -> "月額"
"P3M" -> "3ヶ月ごと"
"P6M" -> "6ヶ月ごと"
"P1Y" -> "年額"
else -> "不明"
}
}
"de" -> {
when (billingPeriod) {
"P1W" -> "wöchentlich"
"P1M" -> "monatlich"
"P3M" -> "alle 3 Monate"
"P6M" -> "alle 6 Monate"
"P1Y" -> "jährlich"
else -> "unbekannt"
}
}
else -> "unknown"
}
}
Als nächstes folgt die fehlende Angabe von Angebotsbedingungen.

Ich verstand die Beschwerde, aber ich war unsicher, welche genauen Formulierungen ich schreiben sollte. Im Folgenden ist ein Auszug aus dem Originaltext der Rejection-E-Mail:
Issue found: Violation of Subscriptions policy
Your app does not comply with the Subscriptions policy.
- Your offer does not clearly and accurately describe the terms of your trial offer or introductory pricing, including when a free trial will convert to a paid subscription, how much the paid subscription will cost, and that a user can cancel if they do not want to convert to a paid subscription.
Zur Lösung habe ich die Formulierungen anderer Apps übernommen. Da es sich um Standardformulierungen zu handeln scheint, ist es besser, bewährten Vorlagen zu folgen, als selbst zu formulieren.
Kaufanforderung
Sobald Sie ein Produkt auf dem Bildschirm auswählen können, folgt endlich der Kaufprozess. Durch Ausführung des Kaufablaufs für das abgerufene Produkt können Sie den eigentlichen Kaufbildschirm aufrufen.
Testen
Das Testen erfolgt durch Anmeldung mit dem vorbereiteten Test-Konto, wodurch Sie Tests ohne tatsächliche Zahlungen durchführen können. Es gibt zwei wichtige Punkte:
- Der Kaufablauf muss mit der im Store registrierten App durchgeführt werden
- Bei Abonnementprodukten wird ein eindeutiger Abrechnungszeitraum festgelegt
Zunächst zur Ausführung des Kaufablaufs: Bei Debug-Builds zeigt der Kaufbildschirm einen Fehler an und der Ablauf kann nicht ausgeführt werden.
Bei Abonnementprodukten wird die Testkarte im tatsächlichen Kaufbildschirm angezeigt. Der Abrechnungszeitraum wird auf diesem Bildschirm angezeigt, unterscheidet sich aber vom tatsächlich für das Produkt eingestellten Zeitraum. Bei mir wurde 5 Minuten angezeigt. Dies scheint eine Spezifikation zu sein, bei der ein extrem kurzer Zeitraum fest angezeigt wird, um das Abonnement-Update-Verhalten zu testen.
Zusammenfassung
Anfangs scheint es schwierig, aber sobald Sie die Gesamtstruktur verstehen, ist die In-App-Kaufimplementierung selbst überraschend einfach mit den verfügbaren öffentlichen Informationen. Abonnementprodukte haben jedoch versteckte Probleme, die man erst durch praktische Erfahrung entdeckt. Ich hoffe, dass angehende Entwickler von den Hürden, auf die ich gestoßen bin, profitieren.