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

SDK Платежи in-app для Kotlin/Java (версия 10.1.0)

RuStore позволяет интегрировать платежи в мобильное приложение.

подсказка
  • Если не знаете с чего начать, прочтите инструкцию в сценариях использования.

  • Если вы переходите на Pay SDK с billingClient SDK, ознакомьтесь с инструкцией по переходу. Подробности о Pay SDK можно узнать тут.

Подготовка к работе

Добавление репозитория

build.gradle
repositories {
maven {
url = uri("https://artifactory-external.vkpartner.ru/artifactory/maven")
}
}

Подключение зависимости

Добавьте следующий код в свой конфигурационный файл для подключения зависимости.

build.gradle
dependencies {
implementation(platform("ru.rustore.sdk:bom:2025.11.01"))
implementation("ru.rustore.sdk:pay")
}

Обработка deeplink в RuStore SDK позволяет эффективно взаимодействовать со сторонними приложениями, при проведении платежей через банковские приложения (СБП, SberPay и др.). Это позволяет перевести пользователя на экран оплаты, а после завершения транзакции — вернуть в ваше приложение.

Для настройки работы с deeplink в вашем приложении и Pay SDK, укажите deeplinkScheme с помощью sdk_pay_scheme_value
в вашем AndroidManifest.xml файле и переопределите метод onNewIntent вашего Activity

Внимание
  • При использовании deeplinks указание схемы является обязательным.
  • При попытке платежа без указания схемы будет возникать ошибка.
  • Допускаются только символы в кодировке ASCII. Формат должен соответствовать спецификации RFC-3986.

Указание deeplinkScheme:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.app.package.name">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"
tools:targetApi="n">
<!-- ... -->

<activity
android:name=".YourPayActivity">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourappscheme" />
</intent-filter>

</activity>

<meta-data
android:name="sdk_pay_scheme_value"
android:value="yourappscheme" />

</application>
</manifest>
подсказка

Замените yourappscheme на название своей схемы. Например, ru.package.name.rustore.scheme.

Затем, добавьте следующий код в Activity, в которую необходимо вернуться после совершения оплаты (ваша страница приложения):

Вызов метода getPurchaseAvailability
сlass YourBillingActivity: AppCompatActivity() {

private val intentInteractor: IntentInteractor by lazy {
RuStorePayClient.instance.getIntentInteractor()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
intentInteractor.proceedIntent(intent, sdkTheme = SdkTheme.LIGHT) // Опциональная тема
}
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intentInteractor.proceedIntent(intent, sdkTheme = SdkTheme.LIGHT) // Опциональная тема
}
}

Для восстановления состояния вашего приложения при возврате с deeplink добавьте в AndroidManifest.xml атрибут android:launchMode="singleTop".

Указание console app id
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.app.package.name">
<!-- ... -->

<application>
<!-- ... -->
<activity
android:name=".YourPayActivity"
android:launchMode="singleTop"
android:exported="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">

<!-- ... -->

</activity>
<!-- ... -->
</application>

</manifest>

Инициализация SDK

Перед вызовом методов библиотеки необходимо выполнить её инициализацию. Сама инициализация происходит автоматически, но для работы SDK в вашем файле Manifest.xml необходимо прописать console_app_id_value. Значение необходимо указать в строковых ресурсах.

Сделать это можно следующим образом.

manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.app.package.name">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"
tools:targetApi="n">
...

<meta-data
android:name="console_app_id_value"
android:value="@string/CONSOLE_APPLICATION_ID" />

</application>
</manifest>
  • CONSOLE_APPLICATION_ID — идентификатор приложения из RuStore консоли.

    Пример: https://console.rustore.ru/apps/111111.
Где в RuStore Консоль отображаются идентификаторы приложений?
  1. Перейдите на вкладку Приложения и выберите нужное приложение.
  2. Скопируйте идентификатор из URL-адреса страницы приложения — это набор цифр между apps/ и /versions. Например, для URL-адреса https://console.rustore.ru/apps/123456/versions ID приложения — 123456.

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

информация

В целях безопасности, SDK устанавливает android:usesCleartextTraffic="false" по умолчанию, чтобы предотвратить передачу данных по незащищённому HTTP и защитить от атак типа "Man-in-the-Middle". Если ваше приложение требует использования HTTP, вы можете изменить этот атрибут на true, но делайте это на свой страх и риск, так как это увеличивает шанс перехвата и подмены данных. Мы рекомендуем разрешать незащищённый трафик только в исключительных случаях и для доверенных доменов, предпочитая HTTPS для всех сетевых взаимодействий. Если необходимо переопределить значение этого атрибута, укажите в манифесте tools:replace="android:usesCleartextTraffic".

Необходимые разрешения и параметры безопасности

Pay SDK автоматически добавляет в манифест приложения некоторые разрешения и параметры, необходимые для работы функциональности, связанной с безопасностью платежей.

Разрешения BLUETOOTH и BLUETOOTH_CONNECT

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Эти разрешения требуются для работы сессионного антифрода — системы, защищающей платежи пользователей. Сбор подобных данных может быть полезен для анализа инцидентов, например, при расследовании кражи учетной записи.

Если вы не хотите предоставлять доступ к Bluetooth, эти разрешения можно удалить из манифеста — Pay SDK продолжит работу, просто не будет собирать эти дополнительные данные для скоринга.

Прочие разрешения

Также Pay SDK может потребовать другие стандартные разрешения, такие как:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

Методы SDK

Доступные публичные интеракторы:

  • PurchaseInteractor - интерактор, который позволяет работать с платежами и имеет несколько публичных методов.

    • getPurchase(purchaseId: PurchaseId): Task<Purchase> - позволяет получить информацию о покупке по её ID.
    • getPurchases(productType: ProductType? = null, purchaseStatus: PurchaseStatus? = null): Task<List<Purchase>> — позволяет получить покупки пользователя. Данный метод поддерживает опциональную фильтрацию по типу товаров (потребляемые, непотребляемые товары или подписки), а также по статусу покупки (поддерживаются статусы PAID, CONFIRMED ACTIVE и PAUSED). По умолчанию фильтры отключены, и вернутся все покупки пользователя (независимо от типа товара) в статусах PAID, CONFIRMED ACTIVE и PAUSED.
    • getPurchaseAvailability(): Task<PurchaseAvailabilityResult> - возвращает результат проверки доступности работы с платежами.
    • purchase(params: ProductPurchaseParams, preferredPurchaseType: PreferredPurchaseType = PreferredPurchaseType.ONE_STEP,sdkTheme: SdkTheme = SdkTheme.LIGHT): Task<ProductPurchaseResult> - позволяет совершить покупку продукта с указанием желаемого типа оплаты - одностадийной (ONE_STEP) или двухстадийной (TWO_STEP). Для данного метода оплаты на платёжной шторке доступны все способы оплаты. Если параметр не задан, по умолчанию запускается с оплатой в одну стадию.
    Важно!

    Если указан тип оплаты TWO_STEP, будет произведена попытка запустить двухстадийную оплату, но итоговый результат напрямую будет зависеть от того, какой способ оплаты (карта, СБП и др.) будет выбран пользователем.

    Обратите внимание, что тип оплаты TWO_STEP недоступен:

    • При выборе способа оплаты СБП.

    • При покупке подписок.

    Двухстадийная оплата доступна только для определенного набора способов оплаты (на текущий момент — только для карт и SberPay). Если выбран способ оплаты, который не поддерживает холдирование, то покупка будет запущена по сценарию с одной стадией.

    • purchaseTwoStep(params: ProductPurchaseParams,sdkTheme: SdkTheme = SdkTheme.LIGHT): Task<ProductPurchaseResult> - запускает сценарий гарантированной двухстадийной покупки товара. При использовании данного метода пользователю на платёжной шторке доступен ограниченный набор способов оплаты - только те, которые поддерживают двухстадийную оплату. В процессе платежа сначала осуществляется холдирование денежных средств покупателя, которые списываются только после подтверждения покупки методом confirmTwoStepPurchase.
    • confirmTwoStepPurchase(purchaseId: PurchaseId, developerPayload: DeveloperPayload? = null) - подтверждение покупки, совершенной по двухстадийной оплате.
    • cancelTwoStepPurchase(purchaseId: PurchaseId) - отмена покупки, совершённой по двухстадийной оплате.
  • ProductInteractor - интерактор, который позволяет работать с продуктами:

    • getProducts(productsId: List<ProductId>): Task<List<Product>> - позволяет получить информацию по активным продуктам, опубликованным в RuStore консоль.
    Важно

    Данный метод возвращает не более 1000 продуктов и работает без авторизации и наличия установленного RuStore на устройстве пользователя.

  • UserInteractor - интерактор, который позволяет получить статус авторизации пользователя UserAuthorizationStatus. У данной модели может быть 2 состояния:

    • Authorized - пользователь авторизован в RuStore
    • Unauthorized - пользователь неавторизован в RuStore.
  • IntentInteractor - интерактор, который позволяет обрабатывать intent-ы и deeplink-и. Необходим для корректного возвращения из приложения банка обратно в приложение и корректного восстановления состояния платежной шторки.

    • proceedIntent(intent: Intent?, sdkTheme: SdkTheme = SdkTheme.LIGHT) - метод для обработки диплинков и восстановление состояния платежной шторки при возвращении в Ваше приложение из приложения банка. Вызов данного метода необходим для корректного отображения шторки оплаты при возвращении в приложение из приложения банка.
  • блок RuStoreUtils - набор открытых методов, таких как:

    • isRuStoreInstalled - проверки наличия приложения RuStore на устройстве пользователя.
    • openRuStoreDownloadInstruction - открывает веб-страницу для скачивания приложения RuStore.
    • openRuStore - запускает приложение RuStore.
    • openRuStoreAuthorization - запускает приложение RuStore для авторизации. После успешной авторизации пользователя приложение RuStore автоматически закроется.

Получение списка продуктов

Для получения продуктов, добавленных в ваше приложение через RuStore консоль, необходимо использовать метод getProducts.

Вызов метода getProducts
RuStorePayClient.instance.getProductInteractor().getProducts(productsId = listOf(ProductId("id1"), ProductId("id2")))
.addOnSuccessListener { products: List<Product> ->
// Логика работы со списком продуктов
}
.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}

productsId: List<ProductId> — список идентификаторов продуктов (задаются при создании продукта в консоли разработчика). Список продуктов имеет ограничение в размере 1000 элементов.

Где в RuStore Консоль отображаются идентификаторы продуктов?
  1. Перейдите на вкладку Приложения и выберите нужное приложение.
  2. Выберите Монетизация в меню слева.
  3. Выберите тип товара: Подписки или Разовые покупки.
  4. Скопируйте идентификаторы нужных товаров.

Метод возвращает список активных продуктов. Ниже представлена модель продукта.

Структура продукта
public class Product internal constructor(
public val productId: ProductId,
public val type: ProductType,
public val amountLabel: AmountLabel,
public val price: Price?,
public val currency: Currency,
public val imageUrl: Url,
public val title: Title,
public val description: Description?,
public val subscriptionInfo: SubscriptionInfo?,
)
  • productId — идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).
  • type — тип продукта. CONSUMABLE/NON-CONSUMABLE/SUBSCRIPTION (потребляемый/непотребляемый/подписка).
  • amountLabel — отформатированная цена покупки, включая валютный знак.
  • price — цена в минимальных единицах (в копейках).
  • currency — код валюты ISO 4217.
  • title — название продукта на языке language.
  • description — описание на языке language.
  • imageUrl — ссылка на картинку.
  • subscriptionInfo — информация о подписке (будет не null, если тип продукта SUBSCRIPTION).

Модель subscriptionInfo содержит информацию о продукте подписки.

Внимание

Наличие полей не означает, что пользователю по-прежнему доступен бесплатный или стартовый период: он мог ранее уже исчерпать эти периоды.

Ниже представлена сама модель.

Структура информации о подписке
public class SubscriptionInfo internal constructor(
public val periods: List<SubscriptionPeriod>,
)

public sealed interface SubscriptionPeriod

public class TrialPeriod internal constructor(
public val duration: String,
public val currency: String,
public val price: Int,
) : SubscriptionPeriod {

public class PromoPeriod internal constructor(
public val duration: String,
public val currency: String,
public val price: Int,
) : SubscriptionPeriod

public class MainPeriod internal constructor(
public val duration: String,
public val currency: String,
public val price: Int,
) : SubscriptionPeriod

public class GracePeriod internal constructor(
public val duration: String,
) : SubscriptionPeriod

public class HoldPeriod internal constructor(
public val duration: String,
) : SubscriptionPeriod
  • duration - длительность периода в формате ISO 8601 (как в Public API)
  • currency - код валюты ISO 4217
  • price - цена в минимальных единицах (копейках).

Пример работы с subscriptionInfo

Работа с subscriptionInfo
RuStorePayClient.instance.getProductInteractor().getProducts(productsId = listOf(ProductId("id1"), ProductId("id2")))
.addOnSuccessListener { products: List<Product> ->
products.forEach { product ->
val periods = product.subscriptionInfo?.periods
when (period) {
is TrialPeriod -> {
println("Бесплатный период: ${period.duration} за ${period.price} ${period.currency}")
}
is PromoPeriod -> {
println("Стартовый период: ${period.duration} за ${period.price} ${period.currency}")
}
is MainPeriod -> {
println("Основной период: ${period.duration} за ${period.price} ${period.currency}")
}
is GracePeriod -> {
println("Период отсрочки: ${period.duration}")
}
is HoldPeriod -> {
println("Период удержания: ${period.duration}")
}
null -> {
println("subscriptionInfo is null")
}
}
}
}
.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}

Примеры ответа

Пример модели Потребляемого продукта
Product(
productId = ProductId("conProduct1"),
type = ProductType.CONSUMABLE_PRODUCT,
amountLabel = AmountLabel("100.00 руб."),
price = Price(10000),
currency = Currency("RUB"),
imageUrl = Url("https://your_image_consumable_product.png"),
title = Title("Название Потребляемого продукта"),
description = Description("Описание потребляемого продукта"),
)
Пример модели Непотребляемого продукта
Product(
productId = ProductId("nonConProduct1"),
type = ProductType.NON_CONSUMABLE_PRODUCT,
amountLabel = AmountLabel("200.00 руб."),
price = Price(20000),
currency = Currency("RUB"),
imageUrl = Url("https://your_image_non_consumable_product.png"),
title = Title("Название Непотребляемого продукта"),
description = Description("Описание Непотребляемого продукта"),
)
Пример модели Подписки
Product(
productId = ProductId("sub_1"),
type = ProductType.SUBSCRIPTION,
amountLabel = AmountLabel("300.00 руб."),
price = Price(30000),
currency = Currency("RUB"),
imageUrl = Url("https://your_image_subscription.png"),
title = Title("Название вашей подписки"),
description = Description("Описание вашей подписки"),
subscriptionInfo = SubscriptionInfo(
periods = listOf(
TrialPeriod(
duration = "P1M",
currency = "RUB",
price = 0
),
PromoPeriod(
duration = "P5D",
currency = "RUB",
price = 149
),
MainPeriod(
duration = "P1Y",
currency = "RUB",
price = 299
),
GracePeriod(
duration = "P3D"
),
HoldPeriod(
duration = "P5D"
)
)
)
)

Определение наличия авторизации у пользователя

Для проверки статуса авторизации пользователя, вызовите метод getUserAuthorizationStatus у UserInteractor. Результатом выполнения метода является класс UserAuthorizationStatus. Доступно 2 значения:

  • AUTHORIZED - пользователь авторизован в RuStore или через VK ID на платежной шторке.
  • UNAUTHORIZED - пользователь не авторизован. Данное значение также вернется если у пользователя нет установленного RuStore на устройстве.
Вызов метода getUserAuthorizationStatus
RuStorePayClient.instance.getUserInteractor().getUserAuthorizationStatus()
.addOnSuccessListener { result ->
when (result) {
UserAuthorizationStatus.AUTHORIZED -> {
// Логика когда пользователь авторизован в RuStore или на платежной шторке
}

UserAuthorizationStatus.UNAUTHORIZED -> {
// огика когда пользователь НЕ авторизован
}
}
}.addOnFailureListener { throwable ->
// Обработка ошибки
}

Проверка доступности работы с платежами

Для проверки доступности платежей, вызовите метод getPurchaseAvailability у PurchaseInteractor. При его вызове проверяются следующие условия.

  • У компании подключена монетизация через консоль разработчика RuStore.
  • Приложение не должно быть заблокировано в RuStore.
  • Пользователь не должен быть заблокирован в RuStore.

Если все условия выполняются, возвращается PurchaseAvailabilityResult.Available. Иначе, возвращается PurchaseAvailabilityResult.Unavailable(val cause: Throwable), где cause - это ошибка о невыполненном условии. Для проверки причины возвращения такого результата нужно проверить тип ошибки на RuStoreException (данные ошибки описаны в разделе Обработка ошибок).

Вызов метода getPurchaseAvailability
RuStorePayClient.instance.getPurchaseInteractor().getPurchaseAvailability()
.addOnSuccessListener { result ->
when (result) {
is PurchaseAvailabilityResult.Available -> {
// Обработка результата доступности платежей
}

is PurchaseAvailabilityResult.Unavailable -> {
// Обработка результата недоступности платежей
}
}
}.addOnFailureListener { throwable ->
// Обработка ошибки
}

Типы покупок

В SDK предусмотрен базовый интерфейс Purchase, который объединяет общие поля всех типов покупок. На его основе созданы две реализации:

  • ProductPurchase: для потребляемых и непотребляемых покупок.
  • SubscriptionPurchase: для подписок.

Данное разделение позволяет каждому типу покупки иметь свои уникальные свойства и поведение.

Интерфейс Purchase
public interface Purchase {
public val purchaseId: PurchaseId
public val invoiceId: InvoiceId
public val orderId: OrderId?
public val purchaseType: PurchaseType
public val status: PurchaseStatus
public val description: Description
public val purchaseTime: Date?
public val price: Price
public val amountLabel: AmountLabel
public val currency: Currency
public val developerPayload: DeveloperPayload?
public val sandbox: Boolean
}

Модель разовой покупки ProductPurchase

Модель разовой покупки ProductPurchase
public class ProductPurchase internal constructor(
public override val purchaseId: PurchaseId,
public override val invoiceId: InvoiceId,
public override val orderId: OrderId?,
public override val purchaseType: PurchaseType,
public override val status: ProductPurchaseStatus,
public override val description: Description,
public override val purchaseTime: Date?,
public override val price: Price,
public override val amountLabel: AmountLabel,
public override val currency: Currency,
public override val developerPayload: DeveloperPayload?,
public override val sandbox: Boolean,
public val productId: ProductId,
public val quantity: Quantity,
public val productType: ProductType,
) : Purchase
  • purchaseId — идентификатор покупки. Идентификатор покупки. Используется для получения информации о покупке в SDK методом получения информации о покупке.
  • invoiceId — идентификатор счёта. Идентификатор счёта. Используется для серверной валидации платежа, поиска платежей в консоли разработчика, а также отображается покупателю в истории платежей в мобильном приложении RuStore.
  • orderId - уникальный идентификатор оплаты, указанный разработчиком или сформированный автоматически (uuid).
  • PurchaseType — тип покупки:
    • ONE_STEP - одностадийная покупка;
    • TWO_STEP - двухстадийная покупка;
    • UNDEFINED - стадийность не определена.
  • status — состояние покупки:
    • INVOICE_CREATED — создан счёт на оплату, покупка ожидает оплаты;
    • CANCELLED — покупка отменена покупателем;
    • PROCESSING — запущена оплата;
    • REJECTED — покупка отклонена (например, ввиду недостатка средств);
    • EXPIRED — истекло время на оплату покупки;
    • PAID — только для двухстадийной оплаты, промежуточный статус, средства на счёте покупателя захолдированы, покупка ожидает подтверждения от разработчика;
    • CONFIRMED — покупка успешно оплачена;
    • REFUNDING — инициирован возврат средств, запрос отправлен в эквайер;
    • REFUNDED — запрос на возврат средств за покупку совершён успешно. Деньги будут возвращены пользователю в течение 10 рабочих дней.;
    • REVERSED — только для двухстадийной оплаты, покупка была отменена разработчиком или не было произведено подтверждение покупки в течение 6 часов, холдирование средств отменено.
  • description - описание покупки.
  • purchaseTime — время покупки.
  • price — цена в минимальных единицах (в копейках).
  • amountLabel — отформатированная цена покупки, включая валютный знак.
  • currency — код валюты ISO 4217.
  • developerPayload — строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации
  • sandbox — флаг тестового платежа. Значение true — тестовый платёж, false— реальный платёж.

  • productId — идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр). Идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).
  • quantity — количество продукта.
  • productType — тип продукта. (CONSUMABLE/NON-CONSUMABLE - потребляемый/непотребляемый.)

Статусная модель покупки

Статусная модель одностадийного платежа.

Статусная модель двухстадийного платежа.

Модель подписки SubscriptionPurchase

Модель подписки SubscriptionPurchase
public class SubscriptionPurchase internal constructor(
public override val purchaseId: PurchaseId,
public override val invoiceId: InvoiceId,
public override val orderId: OrderId?,
public override val purchaseType: PurchaseType,
public override val status: SubscriptionPurchaseStatus,
public override val description: Description,
public override val purchaseTime: Date?,
public override val price: Price,
public override val amountLabel: AmountLabel,
public override val currency: Currency,
public override val developerPayload: DeveloperPayload?,
public override val sandbox: Boolean,
public val productId: ProductId,
public val expirationDate: Date,
public val gracePeriodEnabled: Boolean,
) : Purchase
  • purchaseId — идентификатор покупки. Идентификатор покупки. Используется для получения информации о покупке в SDK методом получения информации о покупке.
  • invoiceId — идентификатор счёта. Идентификатор счёта. Используется для серверной валидации платежа, поиска платежей в консоли разработчика, а также отображается покупателю в истории платежей.
  • orderId - уникальный идентификатор оплаты, указанный разработчиком или сформированный автоматически (uuid).
  • PurchaseType — тип покупки:
    • ONE_STEP - одностадийная покупка;
    • TWO_STEP - двухстадийная покупка;
    • UNDEFINED - стадийность не определена.
  • status - состояние подписки:
    • INVOICE_CREATED - создан счет на оплату, подписка ожидает оплаты.
    • CANCELLED - счет на оплату подписки отменен.
    • EXPIRED - срок действия оплаты счета истек.
    • PROCESSING - первый платеж по подписке в обработке.
    • REJECTED - первый платеж по подписке отклонен. Подписка не оформлена.
    • ACTIVE - подписка активна.
    • EXPIRED - срок действия подписки истек. - тоже самое, не подписки, а время на оплату счета первого для оформления подписки истекло, никакой подписки ещё не было
    • PAUSED - подписка приостановлена из-за проблем с оплатой.
    • TERMINATED - закончились попытки списания по подписке (все были неуспешными). Подписка закрыта автоматически из-за проблем с оплатой.
    • CLOSED - подписка была отменена пользователем или разработчиком. Истек срок оплаченного периода, подписка закрыта.
  • description - описание покупки.
  • purchaseTime — время покупки.
  • price — цена в минимальных единицах (в копейках).
  • amountLabel — отформатированная цена покупки, включая валютный знак.
  • currency — код валюты ISO 4217.
  • developerPayload — строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации
  • sandbox — флаг тестового платежа. Значение true — тестовый платёж, false— реальный платёж.

  • productId — идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр). Идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).
  • expirationDate - дата окончания действия подписки.
  • gracePeriodEnabled - флаг, указывающий, активен ли Grace-период для подписки.

Статусная модель подписки

Покупка продукта

Пояснения по работе с одностадийными и двухстадийными оплатами
  • При использовании одностадийного платежа покупка не требует подтверждения, денежные средства сразу списываются со счёта покупателя, а с разработчика удерживается комиссия. В таком случае, если требуется вернуть денежные средства клиенту (например, по какой-то причине нет возможности поставить продукт), возможен только возврат средств через RuStore Консоль, денежные средства возвращаются покупателю через несколько дней. Возвращается полная стоимость покупки, при этом удержанная комиссия разработчику не возмещается.
  • В случае использования двухстадийного платежа сначала производится холдирование средств на счете покупателя. Комиссия в этом случае не удерживается. После холдирования покупка требует подтверждения или отмены. Комиссия с разработчика удерживается при подтверждении покупки. Отмена покупки означает снятие холда - денежные средства мгновенно снова доступны покупателю.
Ограничения по типам платежей для подписок

На данный момент покупка подписки (SubscriptionPurchase) может быть совершена только с использованием одностадийного платежа (PurchaseType.ONE_STEP).

Важно

Двухстадийная оплата доступна только для определенного набора способов оплаты (на текущий момент — только для карт и SberPay). Технологии СБП не поддерживают двухстадийную оплату. Если выбран способ оплаты, который не поддерживает холдирование, то покупка будет запущена по сценарию с одной стадией.

Оплата с выбором типа покупки

Для вызова покупки продукта с выбором стадийности оплаты используйте метод purchase.

Вызов метода покупки продукта
val params = ProductPurchaseParams(
productId = ProductId("productId"),
orderId = null,
quantity = null,
developerPayload = null,
appUserId = null,
appUserEmail = null,
)
RuStorePayClient.instance.getPurchaseInteractor()
.purchase(params = params, preferredPurchaseType = PreferredPurchaseType.ONE_STEP, sdkTheme = SdkTheme.LIGHT)
.addOnSuccessListener { result ->
// Логика обработки успешного результата покупки
}
.addOnFailureListener { throwable: Throwable ->
when(throwable){
is RuStorePaymentException.ProductPurchaseException -> // Обработка ошибки покупки продукта
is RuStorePaymentException.ProductPurchaseCancelled -> // Обработка отмены покупки продукта
else -> // Обработка ошибки
}
}
  • preferredPurchaseType — желаемый тип покупки: одностадийная (ONE_STEP) или двухстадийная (TWO_STEP).
Важно

Данный метод по умолчанию запускается по одностадийному сценарию оплаты (preferredPurchaseType = PreferredPurchaseType.ONE_STEP), т.е. без холдирования средств.

Для двухстадийной оплаты нужно указать preferredPurchaseType = PreferredPurchaseType.TWO_STEP. Двухстадийная оплата (т.е. оплата с холдированием средств) для данного метода не гарантирована и напрямую зависит от того, какой способ оплаты (карта, СПБ и др.) выбрал пользователь.

При запуске данного метода (с предпочитаемым preferredPurchaseType = twoStep), до тех пор пока пользователь не выберет способ оплаты, стадийность покупки будет UNDEFINED. Учитывайте данное поведение при обработке результатов отмены (ProductPurchaseCancelled) или ошибки (ProductPurchaseException) покупки.

Двухстадийная оплата (с холдированием средств)

Для вызова покупки продукта по двухстадийному сценарию используйте метод purchaseTwoStep.

к сведению

При вызове данного метода пользователю будет доступен ограниченный набор способов оплаты — только те, которые поддерживают двухстадийную оплату.

Ограничения по типам платежей для подписок

На данный момент покупка подписки (SubscriptionPurchase) может быть совершена только с использованием одностадийного платежа (PurchaseType.ONE_STEP).

Вызов метода покупки продукта
val params = ProductPurchaseParams(
productId = ProductId("productId"),
orderId = null,
quantity = null,
developerPayload = null,
appUserId = null,
appUserEmail = null,
)
RuStorePayClient.instance.getPurchaseInteractor()
.purchaseTwoStep(params = params, sdkTheme = SdkTheme.LIGHT)
.addOnSuccessListener { result ->
// Логика обработки успешного результата покупки
}
.addOnFailureListener { throwable: Throwable ->
when(throwable){
is RuStorePaymentException.ProductPurchaseException -> // Обработка ошибки покупки продукта
is RuStorePaymentException.ProductPurchaseCancelled -> // Обработка отмены покупки продукта
else -> // Обработка ошибки
}
}

Структура параметров покупки

Структура параметров покупки
public class ProductPurchaseParams(
public val productId: ProductId,
public val quantity: Quantity? = null,
public val orderId: OrderId? = null,
public val developerPayload: DeveloperPayload? = null,
public val appUserId: AppUserId? = null,
public val appUserEmail: AppUserEmail? = null,
)
  • productId — идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).
  • quantity — количество продукта. Необязательный параметр со стандартным значением 1. Применим только к покупке потребляемых товаров.
  • orderId — уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.
  • developerPayload — строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации. Максимальная длина 250 символов. Символы не экранируются.
  • appUserId — внутренний ID пользователя в вашем приложении (опциональный параметр). Строка с максимальной длиной в 128 символов.
    подсказка

    Например, данный параметр может использоваться для выявления случаев мошенничества в вашем приложении, что позволит повысить его безопасность.

  • appUserEmail — это необязательный параметр, позволяющий задать адрес электронной почты пользователя в вашем приложении. Если адрес электронной почты покупателя был указан при регистрации в приложении, его можно передать для автоматического заполнения поля email при отправке чека — как для платежей вне RuStore, так и для случаев, когда пользователь не авторизован в RuStore. Это избавляет пользователя от необходимости вручную вводить email, сокращает путь до покупки и способствует повышению конверсии.
  • preferredPurchaseType - желаемый тип покупки - одностадийная (ONE_STEP) или двухстадийная (TWO_STEP).
  • sdkTheme - цветовая тема платежной шторки. Доступно 2 варианта LIGHT и DARK (светлая и темная темы, соответственно). Данный параметр, для сохранения обратной совместимости между версиями SDK, реализован с параметром по умолчанию LIGHT.

Подтверждение покупки

Подтверждения требуют только покупки, которые были запущены по двухстадийному сценарию оплаты, т.е. с холдированием средств. Такие покупки, после успешного холдирования будут находиться в статусе PurchaseStatus.PAID.

Для списания средств с карты покупателя требуется подтверждение покупки. Для этого вы должны использовать метод confirmTwoStepPurchase.

Вызов метода подтверждения
RuStorePayClient.instance.getPurchaseInteractor().confirmTwoStepPurchase(
purchaseId = PurchaseId("purchaseId"),
developerPayload = null,
)
.addOnSuccessListener {
// Логика успешного подтверждения покупки
}.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}
  • purchaseId — идентификатор покупки.
  • developerPayload — строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации. Максимум 250 символов (символы не экранируются). Если передан, заменяет значение, записанное при старте покупки методом purchase/purchaseTwoStep.

Отмена покупки

Через SDK можно отменять только те покупки, которые были запущены по двухстадийному сценарию оплаты, т.е. с холдированием средств. Такие покупки после успешного холдирования будут находиться в статусе PurchaseStatus.PAID. После отмены покупки будут переходить в статус PurchaseStatus.REVERSED.

подсказка

Используйте отмену покупки в случаях, если после оплаты (холдирования средств) вы не можете предоставить покупателю товар.

Для отмены покупки (холда) используйте метод cancelTwoStepPurchase.

RuStorePayClient.instance.getPurchaseInteractor().cancelTwoStepPurchase(
purchaseId = PurchaseId("purchaseId"),
)
.addOnSuccessListener {
// Process success
}.addOnFailureListener { throwable: Throwable ->
// Process error
}
  • purchaseId — идентификатор покупки.

Получение сведений о покупке

Для получения информации о покупке, используйте метод getPurchase.
Вызов метода получения покупки пользователя
RuStorePayClient.instance.getPurchaseInteractor().getPurchase(PurchaseId("purchaseId"))
.addOnSuccessListener { purchase: Purchase ->
when(purchase) {
is ProductPurchase -> {
// Логика обработки результата покупки продукта
}
is SubscriptionPurchase -> {
// Логика обработки результата покупки подписки
}
else -> {
// Логика обработки результата покупки c базовыми полями
}
}
}
.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}

Метод возвращает информацию о конкретной покупке в любом статусе.

Детали моделей покупки ProductPurchase и SubscriptionPurchase представлены в соответствующих разделах.

Получение списка покупок

Для получения списка покупок пользователя используйте метод getPurchases.

Вызов метода получения списка покупок пользователя
RuStorePayClient.instance.getPurchaseInteractor().getPurchases()
.addOnSuccessListener { purchases: List<Purchase> ->
// Логика работы со списком покупок пользователя
}
.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}

Данный метод позволяет фильтровать покупки по типу товаров и статусу покупки:

Типы товаров:

  • Потребляемые товары - ProductType.CONSUMABLE_PRODUCT
  • Непотребляемые товары - ProductType.NON_CONSUMABLE_PRODUCT
  • Подписки - ProductType.SUBSCRIPTION

Статусы покупок:

  • Для продуктов:

    • PAID: Успешное холдирование средств, покупка ожидает подтверждения со стороны разработчика.
    • CONFIRMED: Покупка подтверждена, средства списаны.
  • Для подписок:

    • ACTIVE: Подписка активна.
    • PAUSED: Подписка в Hold периоде (например, из-за недостатка средств на карте), продолжаются попытки списания в соответствии с настройками тарифа подписки.

По умолчанию фильтры выключены, если значения не заданы, метод вернёт все покупки пользователя в статусах PAID, CONFIRMED, ACTIVE и PAUSED, независимо от типа товара.

Вызов метода получения списка покупок пользователя с фильтрацией
RuStorePayClient.instance.getPurchaseInteractor().getPurchases(
productType = ProductType.CONSUMABLE_PRODUCT,
purchaseStatus = ProductPurchaseStatus.PAID,
)
.addOnSuccessListener { purchases: List<Purchase> ->
// Логика работы со списком покупок пользователя
}
.addOnFailureListener { throwable: Throwable ->
// Обработка ошибки
}

Структура результата покупки

ProductPurchaseResult - результат успешной оплаты цифрового товара, подписки (для одностадийной оплаты) или успешного холдирования средств (для двухстадийной оплаты).

public class ProductPurchaseResult internal constructor(
public val orderId: OrderId?,
public val purchaseId: PurchaseId,
public val productId: ProductId,
public val invoiceId: InvoiceId,
public val purchaseType: PurchaseType,
public val productType: ProductType,
public val quantity: Quantity,
public val sandbox: Boolean,
)
  • ProductPurchaseResult — результат успешной оплаты цифрового товара (для одностадийной оплаты) или успешного холдирования средств (для двухстадийной оплаты).

    • purchaseId - идентификатор покупки. Используется для получения информации о покупке в SDK методом получения информации о покупке и серверной валидации подписки.
    • productId - идентификатор приобретенного продукта, указанный при создании в консоли разработчика RuStore.
    • invoiceId - идентификатор счета. Используется для серверной валидации платежа, поиска платежей в консоли разработчика, а также отображается покупателю в истории платежей в мобильном приложении RuStore.
    • orderId - уникальный идентификатор оплаты, указанный разработчиком или сформированный автоматически (uuid).
    • purchaseType - тип покупки (ONE_STEP/TWO_STEP/UNDEFINED - одностадийная/двухстадийная/стадийность не определена).
    • productType - тип продукта (NON_CONSUMABLE_PRODUCT - непотребляемый товар, CONSUMABLE_PRODUCT - потребляемый товар, SUBSCRIPTION - подписка).
    • quantity - количество товара, заданное при старте покупки.
    • sandbox - флаг, указывающий признак тестового платежа в песочнице. Если TRUE - покупка совершена в режиме тестирования.

Обработка ошибок

Если в процессе оплаты возникает ошибка или пользователь отменяет покупку, выполнение метода оплаты (как с выбором типа покупки, так и двухстадийного метода) завершается с ошибкой:

  • ProductPurchaseException - ошибка покупки продукта.
  • ProductPurchaseCancelled - ошибка, вызванная отменой покупки продукта (пользователь закрыл платежную шторку) до получения результата покупки. В таком случае рекомендуется дополнительно проверить статус покупки методом получения информации о покупке.

Структура ошибки и отмены покупки:

public class ProductPurchaseException internal constructor(
public val orderId: OrderId?,
public val purchaseId: PurchaseId?,
public val productId: ProductId?,
public val invoiceId: InvoiceId?,
public val quantity: Quantity?,
public val purchaseType: PurchaseType?,
public val productType: ProductType?,
public val sandbox: Boolean?,
public override val cause: Throwable,
) : RuStorePaymentException(message = "Error purchase product", cause = cause)
  • purchaseId — идентификатор покупки. Используется для получения информации о покупке в SDK методом получения информации о покупке.
  • productId — идентификатор приобретённого продукта, указанный при создании в консоли разработчика RuStore.
  • invoiceId — идентификатор счёта. Используется для серверной валидации платежа, поиска платежей в консоли разработчика, а также отображается покупателю в истории платежей в мобильном приложении RuStore.
  • orderId — уникальный идентификатор оплаты, указанный разработчиком или сформированный автоматически (uuid).
  • purchaseType — тип покупки (ONE_STEP/TWO_STEP/UNDEFINED — одностадийная/двухстадийная/стадийность не определена).
  • productType - тип продукта (NON_CONSUMABLE_PRODUCT - непотребляемый товар, CONSUMABLE_PRODUCT - потребляемый товар, SUBSCRIPTION - подписка).
  • quantity — количество товара, заданное при старте покупки.

ProductPurchaseCancelled — отмена покупки цифрового товара. Платёжный диалог был закрыт до получения результата покупки, поэтому состояние покупки неизвестно. Рекомендуется запросить статус покупки отдельно методом получения информации о покупке.

public class ProductPurchaseCancelled internal constructor(
public val purchaseId: PurchaseId?,
public val purchaseType: PurchaseType?,
public val productType: ProductType?,
) : RuStorePaymentException(message = "Purchase product is cancelled")
  • purchaseId — идентификатор покупки. Используется для получения информации о покупке в SDK методом получения информации о покупке.
  • purchaseType — тип покупки (ONE_STEP/TWO_STEP/UNDEFINED — одностадийная/двухстадийная/стадийность не определена).
  • productType - тип продукта (NON_CONSUMABLE_PRODUCT - непотребляемый товар, CONSUMABLE_PRODUCT - потребляемый товар, SUBSCRIPTION - подписка).

Валидация покупки на сервере

Если вам необходимо произвести валидацию успешной покупки в RuStore, вы можете использовать публичные API-интерфейсы валидации. Для валидации продуктов и подписок используются разные методы:

  • Для валидации покупки продукта используйте invoiceId из модели ProductPurchaseResult, возвращаемой после завершения покупки.
  • Для валидации покупки подписки используйте purchaseId из модели ProductPurchaseResult, возвращаемой после завершения покупки.

Тип приобретённого продукта можно определить по данным, полученным в ответе ProductPurchaseResult.

Получение invoiceId из результата покупки
val params = ProductPurchaseParams(ProductId("productId"))

RuStorePayClient.instance.getPurchaseInteractor()
.purchase(params = params, preferredPurchaseType = PreferredPurchaseType.TWO_STEP)
.addOnSuccessListener { result ->
when (purchaseResult.productType) {
CONSUMABLE_PRODUCT,
NON_CONSUMABLE_PRODUCT -> {
val invoiceId = purchaseResult.invoiceId.value
yourApi.validateProduct(invoceId)
}

SUBSCRIPTION -> {
val purchaseId = purchaseResult.purchaseId.value
yourApi.validateSubscription(purchaseId)
}
}
}

Также можно получить invoiceId в модели Purchase. Модель Purchase можно получить используя метод getPurchases() или метод getPurchase.

Получение subscriptionToken из результата покупки
RuStorePayClient.instance.getPurchaseInteractor().getPurchases()
.addOnSuccessListener { purchases ->
purchases.forEach { purchase ->
if(purchase is SubscriptionPurchase){
val purchaseId = purchase.purchaseId.value
yourApi.validateSubscription(purchaseId)
} else {
val invoiceId = purchase.invoiceId.value
yourApi.validateProduct(invoiceId)
}
}
}

RuStoreUtils

RuStoreUtils — это блок в нативном SDK, содержащий набор публичных методов, предназначенных для взаимодействия с приложением RuStore на устройстве пользователя.

Для доступа к методам блока в среде Unity используется синглтон класса RuStoreCoreClient.

Метод IsRuStoreInstalled проверяет наличие приложения RuStore на устройстве пользователя.

Вызов метода IsRuStoreInstalled
if (RuStorePayClient.Instance.IsRuStoreInstalled()) {
// RuStore установлен на устройстве пользователя
} else {
// RuStore не установлен на устройстве пользователя
}

Метод openRuStoreDownloadInstruction открывает веб-страницу для скачивания мобильного приложения RuStore.

Вызов метода openRuStoreDownloadInstruction
RuStoreCoreClient.Instance.openRuStoreDownloadInstruction();

Метод openRuStore запускает мобильное приложение RuStore. При вызове данного метода, в случае отсутствия установленного приложения RuStore, будет отображено Toast уведомление с сообщением "Не удалось открыть приложение".

Вызов метода openRuStore
RuStoreCoreClient.Instance.openRuStore();

Метод openRuStoreAuthorization запускает мобильное приложение RuStore для авторизации. После успешной авторизации пользователя приложение RuStore автоматически закрывается. При вызове данного метода, в случае отсутствия установленного приложения RuStore, будет отображено Toast уведомление с сообщением "Не удалось открыть приложение".

Вызов метода openRuStoreAuthorization
RuStoreCoreClient.Instance.openRuStoreAuthorization();

Использование RuStoreUtils для проверки платежных сценариев

Кейс применения RuStoreUtils для проверки наличия RuStore на устройстве и работы с авторизацией пользователя рассмотрен в статье Приём платежей без установки RuStore.

В статье приведены:

  • Сценарии работы с покупками при отсутствии установленного RuStore;

  • Примеры последовательного вызова методов проверки установки и авторизации через RuStoreUtils;

  • Особенности поведения SDK в разных условиях (наличие/отсутствие RuStore, авторизация пользователя и др.).

Список ошибок

RuStorePaymentNetworkException - ошибка сетевого взаимодействия SDK. В модели ошибки возвращается код ошибки (поле code), по которому можно определить причину ошибки. Таблица с кодами ошибок доступна в разделе коды ошибок.

Поле message содержит описание причины ошибки.

public class RuStorePaymentNetworkException internal constructor(  
public val code: String?,
public val id: String,
public override val message: String,
public override val cause: Throwable? \= null,
) : RuStorePaymentException(message, cause)
  • RuStorePaymentNetworkException — ошибка сетевого взаимодействия SDK;
  • RuStorePaymentCommonException — общая ошибка SDK;
  • RuStorePayClientAlreadyExist — ошибка повторной инициализации SDK;
  • RuStorePayClientNotCreated — попытка обратиться к публичным интерфейсам SDK до момента её инициализации;
  • RuStorePayInvalidActivePurchase — запущен процесс оплаты неизвестного типа продукта;
  • RuStorePayInvalidConsoleAppId — не задан обязательный параметр console_application_id для инициализации SDK;
  • RuStorePaySignatureException — неверная сигнатура ответа. Возникает при попытке совершить мошеннические действия;
  • EmptyPaymentTokenException — ошибка получения платёжного токена;
  • InvalidCardBindingIdException — ошибка оплаты сохранённой картой;
  • ApplicationSchemeWasNotProvided — не указана схема для обратного диплинка;
  • ProductPurchaseException - ошибка покупки продукта. Структура модели представлена в разделе структура результата покупки;
  • ProductPurchaseCancelled - произошла отмена покупки продукта (пользователь закрыл платёжную шторку). Структура модели представлена в разделе структура результата покупки;
  • ProductPurchaseException — ошибка покупки продукта;
  • RuStoreNotInstalledException — на устройстве пользователя не установлен RuStore;
  • RuStoreOutdatedException — установленная на устройстве версия RuStore не поддерживает платежи;
  • RuStoreUserUnauthorizedException — пользователь не авторизован в RuStore;
  • RuStoreApplicationBannedException — приложение заблокировано в RuStore;
  • RuStoreUserBannedException — пользователь заблокирован в RuStore.

Коды ошибок

Код ошибкиОписание
4000001Запрос сформирован некорректно: отсутствует или неверно заполнен обязательный параметр, неверный формат данных.
4000002, 4000016, 4040005Приложение не найдено.
4000003Приложение заблокировано.
4000004Подпись приложения не совпадает с зарегистрированной.
4000005Компания не найдена.
4000006Компания заблокирована.
4000007Монетизация компании отключена или неактивна.
4000014Продукт не найден.
4000015Продукт не опубликован.
4000017Некорректный параметр quantity.
4000018Превышен лимит покупок.
4000020Продукт уже приобретен.
4000021Незавершенная покупка продукта.
4000022Покупка не найдена.
4000025Не найден подходящий способ оплаты.
4000026Неверный тип покупки для подтверждения (должна быть двухстадийная оплата).
4000027Неверный статус покупки для подтверждения.
4000028Неверный тип покупки для отмены (должна быть двухстадийная оплата).
4000029Неверный статус покупки для отмены.
4000030Выписанный токен не соответствует покупаемому товару.
4000041У пользователя уже есть активная подписка для данного кода продукта.
4000045Превышено максимальное значение размера.
4010001Доступ к запрашиваему ресурсу запрещен (неавторизовано).
4010002Время жизни токена истекло.
4010003Платежный токен невалиден.
4030001Не передан платежный токен.
4030002Пользователь заблокирован по требованиям безопасности.
4040002, 4040003, 4040004Ошибка платежной системы.
5000***Внутренняя ошибка.