Перейти к основному содержимому

Вопросы и ответы

к сведению

Настоящий раздел содержит примеры кода для Kotlin.

Q: Как исправить ошибку «Application is not verified yet»?

А: Ошибка возникает в следующих случаях:

  • приложение не прошло модерацию в Консоли RuStore;
  • тестируемая разработчиком APK не совпадает с APK, загруженной в Консоль RuStore.

Второй пункт нужно перепроверить следующим образом.

  • applicationId, указанный в build.gradle, должен совпадать с applicationId APK-файла, который вы публиковали в Консоли RuStore.
  • подпись keystore должна совпадать с подписью, которой было подписано приложение, опубликованное в Консоли RuStore. Убедитесь, что используемый buildType (например, debug) использует такую же подпись, что и опубликованное приложение (например, release).

Q: Что означает ошибка AuthTokenException?

A: SDK не смогло авторизоваться. Проблема с подписью/именем пакета/наличием RuStore на девайсе.

Q: Как исправить ошибку Application signature not correct?

A: Проблема с подписями. Убедитесь, что подпись keystore совпадает с подписью, которой вы подписали приложение в Консоли RuStore.

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, где можно запросить информацию о покупке, используя Метод получения данных подписки по токену подписки.

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/123456).

Q: Как отменить подписку?

A: Метода на отмену подписки нет, можно только отменить автопродление в приложении RuStore.

Экран с подписками можно открыть по deeplink (см. ниже).

startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("rustore://profile/subscriptions")))

Ниже приведена страница со списком deeplinks.

RuStore Deeplinks

Q: Можно ли публиковать в Google Play, Huawei Store приложение с RuStore SDK?

A: Да.

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: Есть ли поддержка 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» (на англ. языке).

Q: Какого типа покупки надо подтверждать?

A: Подтверждать надо только покупки типа CONSUMABLE.

Q: Если покупка не была подтверждена, то удаляется ли покупка сама или ее надо удалить?

A: Cтатус покупки будет изменен на CANCELLED в течение 3х дней, либо при повторной попытке купить данную покупку.

Q: Что такое consoleApplicationId?

A: Это код приложения из консоли разработчика RuStore (пример: https://console.rustore.ru/apps/111111). Здесь consoleApplicationId = 111111.

Q: Как тестировать платежи на этапе разработки?

A: Наша песочница для тестирования платежей доступна сразу после прохождения модерации.

Q: Как совершать повторные покупки позиций, указанных в разовых покупках?

A: Нужно сначала потребить покупку, чтобы иметь возможность купить ее заново.

Q: Для каких типов товаров генерируется subscriptionToken?

A: subscriptionToken генерируется для всех типов товаров.

Q: При каких условиях subscriptionToken будет равен null и как этого избежать?

A: subscriptionToken будет равен null только при внештатных ситуациях, когда, допустим, сломается что-то на бэкенде. Значение nullable, чтобы защититься от крашей при проблемах с парсингом nullable значений.

Q: Могу ли я сохранить объект AppUpdateInfo в локальную переменную, чтобы каждый раз не перезапрашивать его?

A: Объект AppUpdateInfo после однократного использования становится невалидным. Поэтому каждый раз при его использовании надо запрашивать новый с помощью метода getAppUpdateInfo().

Q: Сколько раз нужно вызвать сервис, чтобы Review API начало возвращать RuStoreRequestLimitReached?

A: Вызвать шторку SDK еще раз можно только после того, как пройдет 24 часа с момента предыдущего вызова. В целом, мы не рекомендуем запускать запрос оценки слишком часто – в первую очередь потому, что это ухудшит опыт использования приложения.

Q: Как узнать push токен устройства?

A: Вы можете вывести лог с пуш токеном.

Q: Будут ли работать ваши SDK, если сначала установить RuStore, а потом удалить с девайса? Если да, то какие?

A: Да, PushSdk будет работать без RuStore.

Q: Как проверить подпись APK?

A: Советуем ознакомиться со статьей Проверка подписи APK, чтобы узнать, как проверить подпись APK.