Реализация Google Play Billing (одноразовые покупки) для Android
Google Play Billing Library 6+ (с 2023 года обязательна для новых приложений, с 2024 — для обновлений) переработала API покупок. BillingClient.queryPurchasesAsync заменил синхронный queryPurchases, появился PendingPurchasesParams для отложенных транзакций. Приложения на BillingClient 4 и ниже отклоняются при публикации.
One-time products: INAPP vs DURABLE
В Play Billing 6+ одноразовые покупки делятся на два подтипа:
- INAPP (старый тип) — consumables и non-consumables в одной корзине
- DURABLE — явно non-consumable, новый тип с BillingClient 6
На практике для «убрать рекламу» и «открыть уровни» используем ProductType.INAPP с acknowledged-статусом как маркером владения.
Acknowledgement — где всё падает
Каждая покупка должна быть подтверждена в течение 3 дней через acknowledgePurchase() или consumePurchase() (для consumables). Если не подтвердить — Google автоматически возвращает деньги и отзывает покупку. Это не очевидно из документации.
val billingClient = BillingClient.newBuilder(context)
.setListener { billingResult, purchases ->
purchases?.forEach { purchase ->
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
if (!purchase.isAcknowledged) {
val params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(params) { result ->
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
unlockFeature(purchase.products.first())
}
}
}
}
}
}
.enablePendingPurchases(
PendingPurchasesParams.newBuilder()
.enableOneTimeProducts()
.build()
)
.build()
enablePendingPurchases() теперь обязателен. Без него BillingClient.startConnection() бросает исключение. Pending-покупки (оплата наличными через партнёров Google Pay в некоторых регионах) переходят в PURCHASED не сразу — нужно слушать обновления через PurchasesUpdatedListener.
Восстановление покупок при переустановке
При переустановке приложения покупки восстанавливаются через queryPurchasesAsync(QueryPurchasesParams). Вызывать при каждом старте:
val params = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build()
billingClient.queryPurchasesAsync(params) { billingResult, purchaseList ->
purchaseList.filter {
it.purchaseState == Purchase.PurchaseState.PURCHASED && it.isAcknowledged
}.forEach { restoreAccess(it) }
}
Серверная верификация
Для серверных приложений — purchaseToken отправляем на свой бэкенд, который через Google Play Developer API (purchases.products.get) проверяет purchaseState и acknowledgementState. Только после этого разблокируем контент в БД.
Сроки — 2–3 дня: интеграция Billing Library 6, обработка всех состояний покупки, серверная верификация, тесты через Google Play Console License Testing.







