FAQ по проблемам с платежами
Q: Как исправить ошибку «Application is not verified yet»?
А: Ошибка возникает в следующих случаях:
-
приложение не прошло модерацию в RuStore Консоли;
-
тестируемая разработчиком apk не совпадает с apk, загруженной в RuStore Консоли.
Второй пункт нужно перепроверить следующим образом:
-
applicationId, указанный в build.gradle, должен совпадать с applicationId apk-файла, который вы публиковали в RuStore Консоли.
-
подпись keystore должна совпадать с подписью, которой было подписано приложение, опубликованное в RuStore Консоли. Убедитесь, что используемый buildType (например, debug) использует такую же подпись, что и опубликованное приложение (например, release).
Для работы платежей нужно полностью опубликовать приложение, модерации недостаточно. В скором времени логика будет переработана так, чтобы для тестирования платежей достаточно было прохождения модерации.
Q: Как исправить ошибку «Созданная ранее покупка продукта "..." в количестве ... на сумму ... рублей оплачивается в другой сессии».
A: Ошибка возникает при попытке купить продукт, покупка которого была прекращена и не переведена в конечное состояние с помощью методов deletePurchase и confirmPurchase.
Зачастую это происходит, когда процесс был прерван, а удаление или потребление не было вызвано в purchaseProduct из-за некорректного завершения процесса.
Для таких случаев необходимо произвести отмену или потребление «подвисших» покупок при старте приложения или открытии магазина.
Ниже представлен пример реализации обработки списка покупок. Запускайте данный код при старте приложения или при открытии экрана магазина:
val purchasesUseCase = billingClient.purchases val purchases = purchasesUseCase.getPurchases().await().purchases.orEmpty() purchases.forEach { purchase -> val purchaseId = purchase.purchaseId if (purchaseId != null) { when (purchase.purchaseState) { PurchaseState.CREATED, PurchaseState.INVOICE_CREATED -> { purchasesUseCase.deletePurchase(purchaseId).await() } PurchaseState.PAID -> { purchasesUseCase.confirmPurchase(purchaseId).await() } else -> Unit } } } |
Производить отмену или потребление покупки нужно также и в purchaseProduct().
Обработку покупок в purchaseProduct(), которая переводила бы покупку в финальное состояние, подтверждая или отменяя её, можно реализовать так:
private fun purchaseProduct(product: Product) { val purchasesUseCase = billingClient.purchases purchasesUseCase.purchaseProduct(product.productId) .addOnSuccessListener { paymentResult -> handlePaymentResult(paymentResult, product) } .addOnFailureListener { // Handle error } }
private fun handlePaymentResult(paymentResult: PaymentResult, product: Product) { when (paymentResult) { is PaymentResult.InvalidPurchase -> { paymentResult.purchaseId?.let { deletePurchase(it) } }
is PaymentResult.PurchaseResult -> { when (paymentResult.finishCode) { PaymentFinishCode.SUCCESSFUL_PAYMENT -> { if (product.productType == ProductType.CONSUMABLE) { confirmPurchase(paymentResult.purchaseId) } } PaymentFinishCode.CLOSED_BY_USER, PaymentFinishCode.UNHANDLED_FORM_ERROR, PaymentFinishCode.PAYMENT_TIMEOUT, PaymentFinishCode.DECLINED_BY_SERVER, PaymentFinishCode.RESULT_UNKNOWN, -> { deletePurchase(paymentResult.purchaseId) } } } else -> Unit } } |
Как потреблять и отменять покупки описано в разделе «Сценарий потребления и отмены покупки».
Ознакомиться со статусной моделью покупок можно в разделе «Получение списка покупок».
Q: Как провести серверную валидацию покупки?
A: Сначала необходимо получить subscriptionToken, который является уникальным идентификатором покупки пользователя, процесс описан в статье «Серверная валидация покупки».
Далее, необходимо отправить subscriptionToken на ваш backend, где можно запросить информацию о покупке, используя «Метод получения платежа по его subscription token».
Q: Как исправить 404 при вызове confirmPurchase или deletePurchase?
A: Убедитесь, что передаете purchaseId в параметр методов confirmPurchase и deletePurchase:
val purchasesUseCase = billingClient.purchases val purchases = purchasesUseCase.getPurchases().await().purchases.orEmpty() purchases.forEach { purchase -> val purchaseId = purchase.purchaseId if (purchaseId != null) { when (purchase.purchaseState) { PurchaseState.CREATED, PurchaseState.INVOICE_CREATED -> { // purchasesUseCase.deletePurchase(purchaseId = purchase.productId).await() WRONG purchasesUseCase.deletePurchase(purchaseId = purchaseId).await() // CORRECT } PurchaseState.PAID -> { // purchasesUseCase.confirmPurchase(purchaseId = purchase.productId).await() WRONG purchasesUseCase.confirmPurchase(purchaseId = purchaseId).await() // CORRECT } else -> Unit } } } |
Q: Как исправить ошибку «Метод недоступен»
A: consoleApplicationId должен совпадать с кодом приложения из RuStore Консоли (пример: https://console.rustore.ru/apps/111111).
Q: Как отменить подписку?
A: Метода на отмену подписки нет, можно только отменить автопродление в приложении RuStore.
Экран с подписками можно открыть по deeplink:
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("rustore://profile/subscriptions"))) |
Ниже приведена страница со списком deeplinks: https://help.rustore.ru/rustore/for_developers/developer-documentation/RuStore_deeplinks
Q: Можно ли публиковать в Google Play, Huawei Store приложение с RuStore SDK?
A: Так как разработчики подписывают приложение в Google Play подписью от Google, а в RuStore подписывают своей, то подписи всегда не будут совпадать и RuStore SDK внутри Google Play (или другого магазина приложений) работать не будет. Это значит, что для корректной работы RuStore SDK пользователю нужно будет скачать приложение из RuStore и оплатить подписку в нём.
Q: Какой packageName у RuStore?
A: ru.vk.store.
Q: Как определить из какого магазина установлено приложение?
A: Это можно сделать следующим образом:
val installerPackage = packageManager.getInstallerPackageName(applicationInfo.packageName) |
будет возвращаться ru.vk.store, но функция нестабильна:
- Этот способ будет работать только для приложений, изначально установленных из RuStore. Если изначально приложение было установлено из Google Play или иными способами, то источником будет стандартный установщик пакетов.
- Если для установки был использован режим совместимости (как на некоторых моделях Xiaomi), то источником установки будет системный установщик Xiaomi.
- Если удалить RuStore, то источник установки будет полностью удалён. Переустановка не вернёт источник установки.
Рекомендуем делать отдельный buildFlavor для RuStore.
Q: Почему падает timeout в методах оплаты? (PayLibBackendFailure$TimeoutError)
A: Оплата RuStore SDK недоступна вне России. Также может мешать включенный VPN.
Q: Как тестировать платежи? На реальных картах?
A: Тестировать можно только на реальных картах. Песочница для тестирования находится в разработке.
Q: Есть ли поддержка Java?
A: Да, есть. У Kotlin есть обратная совместимость с Java, но с некоторыми особенностями.
К примеру, возьмем сущность object из Kotlin, являющейся аналогом static class в Java:
RuStoreReviewManagerFactory.create(context) |
Так будет выглядеть обращение к object RuStoreReviewManagerFactory в Java:
RuStoreReviewManagerFactory.INSTANCE.create(getContext()); |
Подробнее об использовании Kotlin кода в Java классах можно узнать в статье «Calling Kotlin from Java».