Реалізація Google Play Billing (видатні покупки) для Android
Consumable у Google Play Billing — це INAPP-продукт, який після покупки потрібно явно «витратити» через consumeAsync(). Поки покупка не consumed, повторна покупка того ж продукту неможлива: BillingClient.launchBillingFlow повертає ITEM_ALREADY_OWNED. Це головне відмінність від iOS, де логіка consumable визначається на стороні додатку, а не платформи.
Порядок consume і ризик подвійного нарахування
Паттерн «купив → нарахував → consume» працює тільки при стабільній мережі. В реальності:
// Невірно: consume одразу після отримання покупки
billingClient.consumeAsync(params) { result, token ->
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
addCoins(100) // крах тут = consume пройшов, монети не нараховані
}
}
Правильний порядок з серверною архітектурою:
- Отримуємо
purchaseTokenзPurchasesUpdatedListener - Надсилаємо token на сервер — сервер через Google Play Developer API верифікує покупку і ідемпотентно нараховує валюту
- Тільки після відповіді
200 OK— викликаємоconsumeAsyncна клієнті - Без кроку 3 — не consume, транзакція залишається відкритою
scope.launch {
val credited = serverApi.creditPurchase(purchase.purchaseToken)
if (credited) {
val consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
val result = billingClient.consumePurchase(consumeParams)
if (result.billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
// Логуємо, але не паникуємо — наступний раз consume при queryPurchasesAsync
}
}
}
Незавершені consume при перезапуску
При кожному запуску додатку — queryPurchasesAsync для INAPP-продуктів. Якщо знаходимо покупку в PURCHASED стані — це незавершена транзакція (або consume не дійшов, або крах). Повторюємо кроки верифікації та consume.
Сервер повинен зберігати purchaseToken як idempotency key. Повторний запит з тим же токеном не нараховує валюту двічі — повертає раніше створений результат.
Pending purchases для consumables
В регіонах, де доступна оплата через кіоски або накладений платіж (Індія, Бразилія, Південно-Східна Азія), покупка може бути PENDING годинами. enablePendingPurchases() з enableOneTimeProducts() обов'язковий з Billing Library 6. Для pending-стану — не consume, чекаємо переходу в PURCHASED через PurchasesUpdatedListener або queryPurchasesAsync при наступному старті.
Терміни — 2–3 дні: інтеграція з ідемпотентною серверною логікою, обробка pending, тести через ліцензійних тестерів Google Play.







