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

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

примечание

Настоящий раздел содержит сведения о переходе на версию платежей для Kotlin.

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

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

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

  • 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(), которая переводила бы покупку в финальное состояние, подтверждая или отменяя её, можно реализовать следующим образом.

privatefun purchaseProduct(product: Product) {
val purchasesUseCase = billingClient.purchases
purchasesUseCase.purchaseProduct(product.productId)
.addOnSuccessListener { paymentResult ->
handlePaymentResult(paymentResult, product)
}
.addOnFailureListener {
// Handle error
}
}
privatefun 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: Так как разработчики подписывают приложение в 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]«(https://kotlinlang.org/docs/java-to-kotlin-interop.html) (на англ. языке).