SDK Платежи in-app и подписки для Kotlin/Java (версия 6.0.0)
RuStore позволяет интегрировать платежи в мобильное приложение.
Если не знаете с чего начать, прочтите инструкцию в сценариях использования.
Подготовка к работе
- Kotlin
- Java
Добавление репозитория
Подключите репозиторий, как показано в примере ниже.
repositories {
maven {
url = uri("https://artifactory-external.vkpartner.ru/artifactory/maven")
}
}
Подключение зависимости
Добавьте следующий код в свой конфигурационный файл для подключения зависимости.
- Подключение BOM
- Подключение версии напрямую
Преимущества использования BOM-файла для конфигурации.
-
Единое управление версиями:
- С BOM вы можете управлять версиями всех зависимостей из одного файла. Это особенно полезно, если вы используете несколько библиотек, которые должны быть совместимы друг с другом.
- Например, если у вас есть несколько библиотек от RuStore, таких как
ru.rustore.sdk:billingclient
иru.rustore.sdk:pushclient
, вы можете использовать BOM, чтобы гарантировать, что все они будут совместимы друг с другом.
-
Упрощение обновлений:
- Обновление зависимостей становится проще, так как вам нужно изменить версию только в одном месте — в BOM-файле. Это снижает риск пропустить обновление какой-либо зависимости и избежать конфликтов версий.
- Например, если новая версия BOM-файла содержит обновленные версии всех библиотек, вам достаточно обновить только BOM-файл, а не каждую зависимость по отдельности.
-
Повышение совместимости:
- Использование BOM помогает избежать конфликтов версий между различными библиотеками. Это особенно важно, когда библиотеки имеют зависимости друг от друга.
- Например, если две библиотеки зависят от разных версий одной и той же библиотеки, это может вызвать конфликты. BOM помогает избежать таких ситуаций, гарантируя, что все зависимости совместимы.
dependencies {
implementation(platform("ru.rustore.sdk:bom:6.0.0"))
implementation("ru.rustore.sdk:billingclient")
}
dependencies {
implementation("ru.rustore.sdk:billingclient:6.0.0")
}
Добавление репозитория
Подключите репозиторий, как показано в примере ниже.
repositories {
maven {
url "https://artifactory-external.vkpartner.ru/artifactory/maven"
}
}
Подключение зависимости
Добавьте следующий код в свой конфигурационный файл для подключения зависимости.
- Подключение BOM
- Подключение версии напрямую
Преимущества использования BOM-файла для конфигурации.
-
Единое управление версиями:
- С BOM вы можете управлять версиями всех зависимостей из одного файла. Это особенно полезно, если вы используете несколько библиотек, которые должны быть совместимы друг с другом.
- Например, если у вас есть несколько библиотек от RuStore, таких как
ru.rustore.sdk:billingclient
иru.rustore.sdk:pushclient
, вы можете использовать BOM, чтобы гарантировать, что все они будут совместимы друг с другом.
-
Упрощение обновлений:
- Обновление зависимостей становится проще, так как вам нужно изменить версию только в одном месте — в BOM-файле. Это снижает риск пропустить обновление какой-либо зависимости и избежать конфликтов версий.
- Например, если новая версия BOM-файла содержит обновленные версии всех библиотек, вам достаточно обновить только BOM-файл, а не каждую зависимость по отдельности.
-
Повышение совместимости:
- Использование BOM помогает избежать конфликтов версий между различными библиотеками. Это особенно важно, когда библиотеки имеют зависимости друг от друга.
- Например, если две библиотеки зависят от разных версий одной и той же библиотеки, это может вызвать конфликты. BOM помогает избежать таких ситуаций, гарантируя, что все зависимости совместимы.
dependencies {
implementation platform("ru.rustore.sdk:bom:6.0.0")
implementation "ru.rustore.sdk:billingclient"
}
dependencies {
implementation "ru.rustore.sdk:billingclient:6.0.0"
}
Обработка deeplink
- Kotlin
- Java
class YourBillingActivity: AppCompatActivity() {
// Previously created with RuStoreBillingClientFactory.create()
private val billingClient: RuStoreBillingClient = YourDependencyInjection.getBillingClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
billingClient.onNewIntent(intent)
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
billingClient.onNewIntent(intent)
}
}
Для восстановления состояния вашего приложения при возврате с deeplink добавьте в
AndroidManifest.xml
android:launchMode="singleTask"
.
<activity
android:name=".YourBillingActivity"
android:launchMode="singleTask"
android:exported="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
public class YourBillingActivity extends AppCompatActivity {
// Previously created with RuStoreBillingClientFactory.create();
RuStoreBillingClient billingClient = YourDependencyInjection.getBillingClient();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
billingClient.onNewIntent(getIntent());
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
billingClient.onNewIntent(intent);
}
}
Для восстановления состояния вашего приложения при возврате с deeplink добавьте в
AndroidManifest.xml
android:launchMode="singleTask"
.
<activity
android:name=".YourBillingActivity"
android:launchMode="singleTask"
android:exported="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
Инициализация SDK
Перед вызовом методов библиотеки необходимо выполнить её инициализацию.
- Kotlin
- Java
Создайте RuStoreBillingClient
, используя RuStoreBillingClientFactory.create()
.
val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
// Опциональные параметры
themeProvider = null,
debugLogs = false,
externalPaymentLoggerFactory = null,
)
-
context
— это ключевой элемент, который предоставляет информацию о текущем состоянии приложения или объекта.к сведениюВ Android существует несколько типов контекста:
- Контекст приложения — это singleton-экземпляр, доступный через
getApplicationContext()
. - Контекст
Activity
— доступен внутриActivity
и привязан к её жизненному циклу. - Контекст в
ContentProvider
— доступен через методgetContext()
и аналогичен контексту приложения.
Для создания
RuStoreBillingClient
предпочтительнее использоватьapplicationContext
. - Контекст приложения — это singleton-экземпляр, доступный через
-
consoleApplicationId
— идентификатор приложения из RuStore консоли.
Где в RuStore Консоль отображаются идентификаторы приложений?
- Перейдите на вкладку Приложения и выберите нужное приложение.
- Скопируйте идентификатор из URL-адреса страницы приложения — это набор цифр между
apps/
и/versions
. Например, для URL-адресаhttps://console.rustore.ru/apps/123456/versions
ID приложения —123456
.
ApplicationId
, указанный в build.gradle
, должен совпадать с applicationId
APK-файла, который вы публиковали в RuStore Консоль.
-
deeplinkScheme
— схема deeplink, необходимая для возврата в ваше приложение после оплаты через стороннее приложение (например, SberPay или СБП). SDK генерирует свой хост к данной схеме.
yourappscheme
, должна совпадать со схемой, указанной в AndroidManifest.xml
(подробнее см. Обработка deeplink).-
themeProvider
— интерфейс, который предоставляет темуBillingClientTheme
. Возможны 2 реализации темыBillingClientTheme
: светлая (Light
) и тёмная (Dark
). Необязательный интерфейс, по умолчанию применяется светлая тема.примечаниеПо умолчанию тема будет светлой (
Light
), но вы можете настроить и тёмную (Dark
). -
externalPaymentLoggerFactory
— интерфейс, позволяющий вести журнал событий.подсказкаЖурнал событий полезен для версий, которые ещё не опубликованы на пользователей. С его помощью можно отследить возникающие ошибки.
-
debugLogs
— флаг, регулирующий ведение журнала событий. Укажите значениеtrue
, если хотите, чтобы события попадали в журнал. В ином случае укажитеfalse
.
Подпись keystore
должна совпадать с подписью, которой было подписано приложение, опубликованное в RuStore Консоль. Убедитесь, что используемый buildType
(пр. debug
) использует такую же подпись, что и опубликованное приложение (пр. release
).
Создайте RuStoreBillingClient
, используя RuStoreBillingClientFactory.create()
.
final Context context = getContext();
final String consoleApplicationId = "111111";
final String deeplinkScheme = "yourappscheme";
// Опциональные параметры
final BillingClientThemeProvider themeProvider = null;
final boolean debugLogs = false;
final ExternalPaymentLoggerFactory externalPaymentLoggerFactory = null;
final SuperAppTokenProvider superAppTokenProvider = null;
RuStoreBillingClient billingClient = RuStoreBillingClientFactory.INSTANCE.create(
context,
consoleApplicationId,
deeplinkScheme,
// Опциональные параметры
themeProvider,
superAppTokenProvider,
externalPaymentLoggerFactory
debugLogs,
);
-
context
— это ключевой элемент, который предоставляет информацию о текущем состоянии приложения или объекта.к сведениюВ Android существует несколько типов контекста:
- Контекст приложения — это singleton-экземпляр, доступный через
getApplicationContext()
. - Контекст
Activity
— доступен внутриActivity
и привязан к её жизненному циклу. - Контекст в
ContentProvider
— доступен через методgetContext()
и аналогичен контексту приложения.
Для создания
RuStoreBillingClient
предпочтительнее использоватьapplicationContext
. - Контекст приложения — это singleton-экземпляр, доступный через
-
consoleApplicationId
— идентификатор приложения из RuStore консоли.
Где в RuStore Консоль отображаются идентификаторы приложений?
- Перейдите на вкладку Приложения и выберите нужное приложение.
- Скопируйте идентификатор из URL-адреса страницы приложения — это набор цифр между
apps/
и/versions
. Например, для URL-адресаhttps://console.rustore.ru/apps/123456/versions
ID приложения —123456
.
ApplicationId
, указанный в build.gradle
, должен совпадать с applicationId
APK-файла, который вы публиковали в RuStore Консоль.
-
deeplinkScheme
— схема deeplink, необходимая для возврата в ваше приложение после оплаты через стороннее приложение (например, SberPay или СБП). SDK генерирует свой хост к данной схеме.
yourappscheme
, должна совпадать со схемой, указанной в AndroidManifest.xml
(подробнее см. Обработка deeplink).-
themeProvider
— интерфейс, который предоставляет темуBillingClientTheme
. Возможны 2 реализации темыBillingClientTheme
: светлая (Light
) и тёмная (Dark
). Необязательный интерфейс, по умолчанию применяется светлая тема.примечаниеПо умолчанию тема будет светлой (
Light
), но вы можете настроить и тёмную (Dark
). -
externalPaymentLoggerFactory
— интерфейс, позволяющий вести журнал событий.подсказкаЖурнал событий полезен для версий, которые ещё не опубликованы на пользователей. С его помощью можно отследить возникающие ошибки.
-
debugLogs
— флаг, регулирующий ведение журнала событий. Укажите значениеtrue
, если хотите, чтобы события попадали в журнал. В ином случае укажитеfalse
.
Подпись keystore
должна совпадать с подписью, которой было подписано приложение, опубликованное в RuStore Консоль. Убедитесь, что используемый buildType
(пр. debug
) использует такую же подпись, что и опубликованное приложение (пр. release
).
Методы SDK
Получение списка продуктов
- Kotlin
- Java
getProducts
, чтобы получить информацию о продуктах, добавленных в ваше приложение через RuStore Консоль.
val productsUseCase: ProductsUseCase = billingClient.products
productsUseCase.getProducts(productIds = listOf("id1", "id2"))
.addOnSuccessListener { products: List<Product> ->
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
productIds
— список идентификаторов продуктов. В нём не должно быть более 100 позиций.
Чтобы указать id
продуктов, которые нужны для работы метода, выполните следующие действия.
- Откройте RuStore Консоль.
- Перейдите на вкладку Приложения.
- Выберите нужное приложение.
- В левом боковом меню выберите раздел Монетизация.
- Выберите тип товара: Подписки или Разовые покупки.
- Скопируйте идентификаторы нужных товаров. Это и есть
id
продуктов.
Метод возвращает
data class Product(
val productId: String,
val productType: ProductType?,
val productStatus: ProductStatus,
val priceLabel: String?,
val price: Int?,
val currency: String?,
val language: String?,
val title: String?,
val description: String?,
val imageUrl: Uri?,
val promoImageUrl: Uri?,
val subscription: ProductSubscription?,
)
Структура продукта
productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productType
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.productStatus
— статус продукта.priceLable
— отформатированная цена товара, включая валютный знак на языкеlanguage
.price
— цена в минимальных единицах (в копейках).currency
— код валюты ISO 4217.language
— язык, указанный с помощью BCP 47 кодирования.title
— название продукта на языкеlanguage
.description
— описание на языкеlanguage
.imageUrl
— ссылка на картинку.promoImageUrl
— ссылка на промокартинку.subscription
— описание подписки, возвра щается только для продуктов с типомsubscription
.
Структура подписки
data class ProductSubscription(
val subscriptionPeriod: SubscriptionPeriod?,
val freeTrialPeriod: SubscriptionPeriod?,
val gracePeriod: SubscriptionPeriod?,
val introductoryPrice: String?,
val introductoryPriceAmount: String?,
val introductoryPricePeriod: SubscriptionPeriod?
)
subscriptionPeriod
— период подписки.freeTrialPeriod
— пробный период подписки.gracePeriod
— льготный период подписки.introductoryPrice
— отформатированная вступительная цена подписки, включая знак валюты, на языкеproduct:language
.introductoryPriceAmount
— вступительная цена в минимальных единицах валюты (в копейках).introductoryPricePeriod
— расчётный период вступительной цены.
Структура периода подписки
data class SubscriptionPeriod(
val years: Int,
val months: Int,
val days: Int,
)
years
— количество лет.months
— количество месяцев.days
— количество дней.
getProducts
, чтобы получить информацию о продуктах, добавленных в ваше приложение через RuStore Консоль.
ProductsUseCase productsUseCase = billingClient.getProducts();
productsUseCase.getProducts(Arrays.asList("id1", "id2"))
.addOnSuccessListener(products ->
// Process success
)
.addOnFailureListener(throwable ->
// Process error
);
productIds: List<String>
— список идентификаторов продуктов. В нём не должно быть более 100 позиций.
Чтобы указать id
продуктов, которые нужны для работы метода, выполните следующие действия.
- Откройте RuStore Консоль.
- Перейдите на вкладку Приложения.
- Выберите нужное прило жение.
- В левом боковом меню выберите раздел Монетизация.
- Выберите тип товара: Подписки или Разовые покупки.
- Скопируйте идентификаторы нужных товаров. Это и есть
id
продуктов.
Структура продукта
public Product(
String productId,
@Nullable
ProductType productType,
ProductStatus productStatus,
@Nullable
String priceLabel,
@Nullable
Integer price,
@Nullable
String currency,
@Nullable
String language,
@Nullable
String title,
@Nullable
String description,
@Nullable
Uri imageUrl,
@Nullable
Uri promoImageUrl,
@Nullable
ProductSubscription subscription
) {
this.productId = productId;
this.productType = productType;
this.productStatus = productStatus;
this.priceLabel = priceLabel;
this.price = price;
this.currency = currency;
this.language = language;
this.title = title;
this.description = description;
this.imageUrl = imageUrl;
this.promoImageUrl = promoImageUrl;
this.subscription = subscription;
}
productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productType
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.productStatus
— статус продукта.priceLabel
— отформатированная цена товара, включая валютный знак на языкеlanguage
.price
— цена в минимальных единицах (в копейках).currency
— код валюты ISO 4217.language
— язык, указанный с помощью BCP 47 кодирования.title
— название продукта на языкеlanguage
.description
— описание на языкеlanguage
.imageUrl
— ссылка на картинку.promoimageurl
— ссылка на промокартинку.subscription
— описание подписки, возвращается только для продуктов с типомsubscription
.
Структура подписки
public ProductSubscription(
@Nullable
SubscriptionPeriod subscriptionPeriod,
@Nullable
SubscriptionPeriod freeTrialPeriod,
@Nullable
SubscriptionPeriod gracePeriod,
@Nullable
String introductoryPrice,
@Nullable
String introductoryPriceAmount,
@Nullable
SubscriptionPeriod introductoryPricePeriod
) {
this.subscriptionPeriod = subscriptionPeriod;
this.freeTrialPeriod = freeTrialPeriod;
this.gracePeriod = gracePeriod;
this.introductoryPrice = introductoryPrice;
this.introductoryPriceAmount = introductoryPriceAmount;
this.introductoryPricePeriod = introductoryPricePeriod;
}
subscriptionPeriod
— период подписки.freeTrialPeriod
— пробный период подписки.gracePeriod
— льготный период подписки.introductoryPrice
— отформатированная вступительная цена подписки, включая знак валюты, на языкеproduct:language
.introductoryPriceAmount
— вступительная цена в минимальных единицах валюты (в копейках).introductoryPricePeriod
— расчётный период вступительной цены.
Структура периода подписки
public SubscriptionPeriod(
int years,
int months,
int days
) {
this.years = years;
this.months = months;
this.days = days;
}
years
— количество лет.months
— количество месяцев.days
— количество дней.
Покупка продукта
- Kotlin
- Java
Для вызова покупки продукта используйте метод purchaseProduct
.
val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.purchaseProduct(
productId = productId,
orderId = UUID.randomUUID().toString(),
quantity = 1,
developerPayload = null,
).addOnSuccessListener { paymentResult: PaymentResult ->
when (paymentResult) {
// Process PaymentResult
}
}.addOnFailureListener { throwable: Throwable ->
// Process error
}
productId: String
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).orderId: String
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.quantity: Int
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
).developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки.
Структура результата покупки
public sealed interface PaymentResult {
public data class Success(
val orderId: String?,
val purchaseId: String,
val productId: String,
val invoiceId: String,
val sandbox: Boolean,
val subscriptionToken: String? = null,
) : PaymentResult
public data class Cancelled(
val purchaseId: String,
val sandbox: Boolean,
) : PaymentResult
public data class Failure(
val purchaseId: String?,
val invoiceId: String?,
val orderId: String?,
val quantity: Int?,
val productId: String?,
val sandbox: Boolean,
val errorCode: Int?,
) : PaymentResult
public object InvalidPaymentState : PaymentResult()
}
Параметр sandbox
определяет, является ли платёж тестовым. Значения могут быть true
или false
,
где true
обозначает тестовый платёж, а false
– реальный.
Success
— результат успешного завершения покупки цифрового товара.Failure
— при отправке запроса на оплату или получения статуса оплаты возникла проблема, невозможно установить статус покупки.Cancelled
— запрос на покупку отправлен, при этом пользователь закрыл «платёжную шторку» на своём устройстве, и результат оплаты неизвестен.InvalidPaymentState
— ошибка работы SDK платежей. Может возникнуть, в случае некорректного обратного deeplink.
Для вызова покупки продукта используйте метод purchaseProduct
.
purchasesUseCase.purchaseProduct(productId, null, 1, developerPayload)
.addOnSuccessListener(result ->
// Process PaymentResult
)
.addOnFailureListener(throwable ->
// Process error
);
productId: String
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).orderId: String
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.quantity: Int
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
).developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки.
Структура результата покупки
public interface PaymentResult {
class Success implements PaymentResult {
@Nullable
String orderId;
String purchaseId;
String productId;
String invoiceId;
boolean sandbox;
@Nullable
String subscriptionToken;
public Success(@Nullable String orderId, String purchaseId, String productId, String invoiceId,
boolean sandbox, @Nullable String subscriptionToken) {
this.orderId = orderId;
this.purchaseId = purchaseId;
this.productId = productId;
this.invoiceId = invoiceId;
this.sandbox = sandbox;
this.subscriptionToken = subscriptionToken;
}
}
class Failure implements PaymentResult {
@Nullable
String purchaseId;
@Nullable
String invoiceId;
@Nullable
String orderId;
@Nullable
Integer quantity;
@Nullable
String productId;
boolean sandbox;
@Nullable
Integer errorCode;
public Failure(@Nullable String purchaseId, @Nullable String invoiceId, @Nullable String orderId,
@Nullable Integer quantity, @Nullable String productId, boolean sandbox, @Nullable Integer errorCode) {
this.purchaseId = purchaseId;
this.invoiceId = invoiceId;
this.orderId = orderId;
this.quantity = quantity;
this.productId = productId;
this.sandbox = sandbox;
this.errorCode = errorCode;
}
}
class Cancelled implements PaymentResult {
String purchaseId;
boolean sandbox;
public Cancelled(String purchaseId, boolean sandbox) {
this.purchaseId = purchaseId;
this.sandbox = sandbox;
}
}
class InvalidPaymentState implements PaymentResult {}
}
Параметр sandbox
определяет, является ли платёж тестовым. Значения могут быть true
или false
,
где true
обозначает тестовый платёж, а false
– реальный.
Success
— результат успешного завершения покупки цифрового товара.Failure
— при отправке запроса на оплату или получения статуса оплаты возникла проблема, невозможно установить статус покупки.Cancelled
— запрос на покупку отправлен, при этом пользователь закрыл «платёжную шторку» на своём устройстве, и результат оплаты неизвестен.InvalidPaymentState
— ошибка работы SDK платежей. Может возникнуть, в случае некорректного обратного deeplink.
Подтверждение (потребление) покупки
Продукты, требующие подтверждения (потребления)
Учитывайте тип покупки. Метод подтверждения (потребления) необходим, только если у вас потребляемый товар (CONSUMABLE
), который можно купить много раз.
Чтобы такие товары начислились пользователям без ошибок, подтвердите подтверждение (потребление) продукта с помощью метода confirmPurchase
.
При начислении товара в вашем приложении используйте серверную валидацию платежей. Начисляйте товар только когда платёж (счёт) перейдет в финальный статус CONFIRMED
.
Начисление продуктов пользователям надо делать в callback addOnSuccessListener
метода confirmPurchase
.
Статус PAID
является промежуточным и означает, что средства пользователя захол дированы на карте и вам нужно подтвердить покупку.
Исключение — платежи через СБП или со счёта телефона: при этих способах оплаты используется одностадийный платёж, но модель счёта остаётся двухстадийной. Подробнее см. пояснения ниже.
При оплате потребляемых (CONSUMABLE
) товаров через СБП или со счета телефона используется одностадийный платёж, при этом модель счёта остаётся двухстадийной. Это значит, что при переходе счёта в статус PAID
при оплате через СБП или со счета мобильного телефона деньги уже списаны со счёта покупателя, а с разработчика удержана комиссия. В этом случае при отмене покупки в состоянии PAID
происходит возврат средств (refund), а не отмена холдирования — reverse. Удержанная комиссия разработчику не возвращается. При этом для завершения покупки всё равно нужно выполнить метод подтверждения (потребления) — см. также таблицу ниже.
Платёжный метод | Тип платежа | Платёж в статусе PAID |
---|---|---|
| Двухстадийный |
|
| Одностадийный |
|
Вызов метода подтверждения (потребления)
- Kotlin
- Java
Для подтверждения (потребления) покупки используйте метод confirmPurchase
. Запрос на подтверждение (потребление) покупки должен сопровождаться выдачей товара. После вызова подтверждения покупка перейдёт в статус CONSUMED
.
val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.confirmPurchase(purchaseId = "purchaseId", developerPayload = null)
.addOnSuccessListener {
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
purchaseId
— идентификатор покупки.developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки.
Для подтверждения (потребления) покупки используйте метод confirmPurchase
. Запрос на подтверждение (потребление) покупки должен сопровождаться выдачей товара. После вызова подтверждения покупка перейдёт в статус CONSUMED
.
PurchasesUseCase purchasesUseCase = billingClient.getPurchases();
purchasesUseCase.confirmPurchase("purchaseId", "developerPayload")
.addOnSuccessListener(unit ->
// Process success
)
.addOnFailureListener(throwable ->
// Process error
);
purchaseId
— идентификатор покупки.developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки.
Отмена покупки
- Kotlin
- Java
Для отмены покупки используйте метод deletePurchase
.
val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.deletePurchase(purchaseId = "purchaseId")
.addOnSuccessListener {
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
purchaseId
— идентификатор покупки.
Используйте метод отмены покупки с осторожностью и только в исключительных случаях, только если покупка осталась в статусе PAID
, а вы не можете выдать товар пользователю. В остальных случаях не рекомендуется вручную отменять покупки, так как RuStore автоматически обрабатывает незавершённые покупки (например, таймаут в 20 минут или повторная покупка от того же клиента).
При оплате покупки через СБП или со счета мобильного телефона отмена покупки в статусе PAID
не отменяет холдирование, а приводит к возврату средств (refund). При этом с разработчика удерживается комиссия, которая не возвращается.
Также вы можете подтверждать или отменять покупки через API RuStore:
Обработка ошибок
При вызове метода RuStoreBillingClient.purchases.purchaseProduct()
, ошибки обрабатываются автоматически.
Метод устарел и не рекомендуется к использованию.
Для показа диалога с ошибкой пользователю используйте метод
resolveForBilling
public fun RuStoreException.resolveForBilling(context: Context)
Для отмены покупки используйте метод deletePurchase
.
PurchasesUseCase purchasesUseCase = billingClient.getPurchases();
purchasesUseCase.deletePurchase("purchaseId")
.addOnSuccessListener(unit ->
// Process success
)
.addOnFailureListener(throwable ->
// Process error
);
purchaseId
— идентификатор покупки.
Используйте метод отмены покупки с осторожностью и только в исключительных случаях, только если покупка осталась в статусе PAID
, а вы не можете выдать товар пользователю. В остальных случаях не рекомендуется вручную отменять покупки, так как RuStore автоматически обрабатывает незавершённые покупки (например, таймаут в 20 минут или повторная покупка от того же клиента).
При оплате покупки через СБП или со счета мобильного телефона отмена покупки в статусе PAID
не отменяет холдирование, а приводит к возврату средств (refund). При этом с разработчика удерживается комиссия, которая не возвращается.
Также вы можете подтверждать или отменять покупки через API RuStore:
Обработка ошибок
При вызове метода RuStoreBillingClient.purchases.purchaseProduct()
, ошибки обрабатываются автоматически.
Метод устарел и не рекомендуется к использованию.
Для показа диалога с ошибкой пользователю используйте метод
resolveForBilling
BillingRuStoreExceptionExtKt.resolveForBilling(exception, getContext());
Получение сведений о покупке
- Kotlin
- Java
getPurchaseInfo
.
val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.getPurchaseInfo("purchaseId")
.addOnSuccessListener { purchase: Purchase ->
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
Структура покупки
data class Purchase(
val purchaseId: String?,
val productId: String,
val productType: ProductType?,
val invoiceId: String?,
val language: String?,
val purchaseTime: Date?,
val orderId: String?,
val amountLabel: String?,
val amount: Int?,
val currency: String?,
val quantity: Int?,
val purchaseState: PurchaseState?,
val developerPayload: String?,
val subscriptionToken: String?
)
-
purchaseId
— идентификатор покупки. -
productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр). -
invoiceId
— идентификатор счёта. -
language
— язык, указанный с помощью BCP 47 кодирования. -
purchaseTime
— время покупки. -
orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов. -
amountLable
— отформатированная цена покупки, включая валютный знак. -
amount
— цена в минимальных единицах валюты. -
currency
— код валюты ISO 4217. -
quantity
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
). -
purchaseState
— состояние покупки:CREATED
— покупка создана;INVOICE_CREATED
— создан счёт на оплату, покупка ожидает оплаты;PAID
— только для покупки потребляемого товара — промежуточный статус, средства на счёте покупателя зарезервированы. Покупка ожидает подтверждения от разработчика;CONFIRMED
— платеж за непотребляемый товар успешно совершен;CONSUMED
— платеж за потребляемый товар успешно совершен;CANCELLED
— покупка отменена — оплата не была произведена или был совершен возврат средств покупателю (для подписок после возврата средств покупка не переходит вCANCELLED
);PAUSED
— для подписок — подписка перешла в HOLD период;TERMINATED
— подписка закрылась.
-
developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки. -
subscriptionToken
— токен для валидации покупки на сервере.
getPurchaseInfo
.
PurchasesUseCase purchasesUseCase = billingClient.getPurchases();
purchasesUseCase.getPurchaseInfo("purchaseId")
.addOnSuccessListener(purchase ->
// Process success
)
.addOnFailureListener(throwable ->
// Process error
);
Структура покупки
public Purchase(
@Nullable
String purchaseId,
String productId,
@Nullable
ProductType productType,
@Nullable
String invoiceId,
@Nullable
String description,
@Nullable
String language,
@Nullable
Date purchaseTime,
@Nullable
String orderId,
@Nullable
String amountLabel,
@Nullable
Integer amount,
@Nullable
String currency,
@Nullable
Integer quantity,
@Nullable
PurchaseState purchaseState,
@Nullable
String developerPayload,
@Nullable
String subscriptionToken
) {
this.purchaseId = purchaseId;
this.productId = productId;
this.productType = productType;
this.invoiceId = invoiceId;
this.description = description;
this.language = language;
this.purchaseTime = purchaseTime;
this.orderId = orderId;
this.amountLabel = amountLabel;
this.amount = amount;
this.currency = currency;
this.quantity = quantity;
this.purchaseState = purchaseState;
this.developerPayload = developerPayload;
this.subscriptionToken = subscriptionToken;
}
-
purchaseId
— идентификатор покупки. -
productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр). -
description
— описание на языкеlanguage
. -
invoiceId
— идентификатор счёта. -
language
— язык, указанный с помощью BCP 47 кодирования. -
purchaseTime
— время покупки. -
orderId
— уникальный идентификатор оплаты, сформирован ный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов. -
amountLable
— отформатированная цена покупки, включая валютный знак. -
amount
— цена в минимальных единицах валюты. -
currency
— код валюты ISO 4217. -
quantity
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
). -
purchaseState
— состояние покупки:CREATED
— покупка создана;INVOICE_CREATED
— создан счёт на оплату, покупка ожидает оплаты;PAID
— только для покупки потребляемого товара — промежуточный статус, средства на счёте покупателя зарезервированы. Покупка ожидает подтверждения от разработчика;CONFIRMED
— платеж за непотребляемый товар успешно совершен;CONSUMED
— платеж за потребляемый товар успешно совершен;CANCELLED
— покупка отменена — оплата не была произведена или был совершен возврат средств покупателю (для подписок после возврата средств покупка не переходит вCANCELLED
);PAUSED
— для подписок — подписка перешла в HOLD период.TERMINATED
— подписка закрылась.
-
developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки. -
subscriptionToken
— токен для валидации покупки на сервере.
Получение списка покупок
- Kotlin
- Java
Метод возвращает только покупки со статусами из таблицы ниже. Подробнее о других возможных состояниях покупки смотрите в разделе Получение сведений о покупке.
Тип/Стат ус | INVOICE_CREATED | CONFIRMED | PAID | PAUSED |
---|---|---|---|---|
CONSUMABLE | + | + | ||
NON-CONSUMABLE | + | + | ||
SUBSCRIPTION | + | + | + |
Метод возвращает незавершённые состояния покупки и покупки потребляемых товаров, требующих обработки. Помимо этого, он показывает подтверждённые покупки для подписок и непотребляемых товаров — тех, которые нельзя купить повторно.
Для получения списка покупок пользователя используйте метод getPurchases
.
val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.getPurchases()
.addOnSuccessListener { purchases: List<Purchase> ->
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
Структура покупки
data class Purchase(
val purchaseId: String?,
val productId: String,
val productType: ProductType?,
val invoiceId: String?,
val language: String?,
val purchaseTime: Date?,
val orderId: String?,
val amountLabel: String?,
val amount: Int?,
val currency: String?,
val quantity: Int?,
val purchaseState: PurchaseState?,
val developerPayload: String?,
val subscriptionToken: String?
)
purchaseId
— идентификатор покупки.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productType
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.invoiceId
— идентификатор счёта.language
— язык, указанный с помощью BCP 47 кодирования.purchaseTime
— время покупки.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.amountLable
— отформатированная цена покупки, включая валютный знак.amount
— цена в минимальных единицах валюты.currency
— код валюты ISO 4217.quantity
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
).purchaseState
— состояние покупки.
Структура ответа сервера на запрос покупок
data class PurchasesResponse(
override val meta: RequestMeta?,
override val code: Int,
override val errorMessage: String?,
override val errorDescription: String?,
override val errors: List<DigitalShopGeneralError>?,
val purchases: List<Purchase>?,
) : ResponseWithCode
meta
— дополнительная информация о запросе.code
— код ответа.errorMessage
— сообщение об ошибке для пользователя.errorDescription
— расшифровка сообщения об ошибке.errors
— список ошибок для запрошенных продуктов.purchases
— список запрошенных покупок.
Метод возвращает только покупки со статусами из таблицы ниже. Подробнее о других возможных состояниях покупки смотрите в разделе Получение сведений о покупке.
Тип/Статус | INVOICE_CREATED | CONFIRMED | PAID | PAUSED |
---|---|---|---|---|
CONSUMABLE | + | + | ||
NON-CONSUMABLE | + | + | ||
SUBSCRIPTION | + | + | + |
Метод возвращает незавершённые состояния покупки и покупки потребляемых товаров, требующих обработки. Помимо этого, он показывает подтверждённые покупки для подписок и непотребляемых товаров — тех, которые нельзя купить повторно.
Для получения списка покупок пользователя используйте метод getPurchases
.
PurchasesUseCase purchasesUseCase = billingClient.getPurchases();
purchasesUseCase.getPurchases()
.addOnSuccessListener(purchases -> {
// Process PaymentResult
})
.addOnFailureListener(throwable ->
// Process error
);
Структура покупки
public Purchase(
@Nullable
String purchaseId,
String productId,
@Nullable
ProductType productType,
@Nullable
String invoiceId,
@Nullable
String language,
@Nullable
Date purchaseTime,
@Nullable
String orderId,
@Nullable
String amountLabel,
@Nullable
Integer amount,
@Nullable
String currency,
@Nullable
Integer quantity,
@Nullable
PurchaseState purchaseState,
@Nullable
String developerPayload,
@Nullable
String subscriptionToken
) {
this.purchaseId = purchaseId;
this.productId = productId;
this.productType = productType;
this.invoiceId = invoiceId;
this.language = language;
this.purchaseTime = purchaseTime;
this.orderId = orderId;
this.amountLabel = amountLabel;
this.amount = amount;
this.currency = currency;
this.quantity = quantity;
this.purchaseState = purchaseState;
this.developerPayload = developerPayload;
this.subscriptionToken = subscriptionToken;
}
purchaseId
— идентификатор покупки.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productType
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.invoiceId
— идентификатор счёта.language
— язык, указанный с помощью BCP 47 кодирования.purchaseTime
— время покупки.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.AmountLabel
— отформатированная цена покупки, включая валютный знак.amount
— цена в минимальных единицах валюты.currency
— код валюты ISO 4217.quantity
— количество продукта (необязательный параметр — если не указывать, будет использоваться значение1
).purchaseState
— состояние покупки.developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализаци и процесса покупки.subscriptionToken
— токен для валидации покупки на сервере.
Проверка доступности работы с платежами
- Kotlin
- Java
Метод устарел и не рекомендуется к использованию.
Для проверки доступности платежей используйте метод
checkPurchasesAvailability
.
Начиная с версии 6.0.0 процедура и результат проверки доступности платежей зависит от наличия RuStore на устройстве пользователя.
Подробнее см. ниже.
Наличие RuStore | Процедура и результат проверки |
---|---|
Не установлен | Метод вернёт |
Установлен | Выполняется проверка следующих условий.
FeatureAvailabilityResult.Available .В противном случае возвращается FeatureAvailabilityResult.Unavailable(val cause: RuStoreException) , где cause — это ошибка о невыполненном условии.Все возможные ошибки |
Таким образом, положительный ответ вернётся в двух случаях:
- если RuStore не установлен;
- если RuStore установлен и установленный экземпляр RuStore на устройстве пользователя соответствует проверяемым условиям.
Сам по себе ответ метода checkPurchasesAvailability
не показывает, установлен ли RuStore на устройстве пользователя. Вы можете проверить наличие RuStore на устройстве пользователя с помощью метода isRuStoreInstalled
.
Ниже представлен пример использования метода checkPurchasesAvailability
.
RuStoreBillingClient.checkPurchasesAvailability(context)
.addOnSuccessListener { result ->
when (result) {
FeatureAvailabilityResult.Available -> {
// Process purchases available
}
is FeatureAvailabilityResult.Unavailable -> {
// Process purchases unavailable
}
}
}
.addOnFailureListener { throwable ->
// Process unknown error
}
context
— контекст Android.
Метод устарел и не рекомендуется к использованию.
Для проверки доступности платежей используйте метод
checkPurchasesAvailability
.
Начиная с версии 6.0.0 процедура и результат проверки доступности платежей зависит от наличия RuStore на устройстве пользователя.
Подробнее см. ниже.
Наличие RuStore | Процедура и результат проверки |
---|---|
Не установлен | Метод вернёт |
Установлен | Выполняется проверка следующих условий.
FeatureAvailabilityResult.Available .В противном случае возвращается FeatureAvailabilityResult.Unavailable(val cause: RuStoreException) , где cause — это ошибка о невыполненном условии.Все возможные ошибки |
Таким образом, положительный ответ вернётся в двух случаях:
- если RuStore не установлен;
- если RuStore установлен и установленный экземпляр RuStore на устройстве пользователя соответствует проверяемым условиям.
Сам по себе ответ метода checkPurchasesAvailability
не показывает, установлен ли RuStore на устройстве пользователя. Вы можете проверить наличие RuStore на устройстве пользователя с помощью метода isRuStoreInstalled
.
Ниже представлен пример использования метода checkPurchasesAvailability
.
RuStoreBillingClientExtKt.checkPurchasesAvailability(RuStoreBillingClient.Companion, getContext())
.addOnSuccessListener(result -> {
if (result instanceof FeatureAvailabilityResult.Available) {
// Hanlde purchases available
} else {
RuStoreException exception = ((FeatureAvailabilityResult.Unavailable) result).getCause();
// Hanlde purchases unavailable
}
})
.addOnFailureListener(error -> {
// Handle error
});
В данной версии SDK метод будет возвращать сообщение о доступности платежа.
Смена темы интерфейса
- Kotlin
- Java
SDK поддерживает динамическую смены темы через интерфейс провайдера BillingClientThemeProvider
.
val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
themeProvider? = BillingClientThemeProviderImpl(),
)
class BillingClientThemeProviderImpl: BillingClientThemeProvider {
override fun provide(): BillingClientTheme {
// Тут должна размещаться логика по проверке установленной темы
val darkTheme = ....
if(darkTheme){
BillingClientTheme.Dark
} else {
BillingClientTheme.Light
}
}
}
SDK поддерживает динамическую смены темы через интерфейс провайдера BillingClientThemeProvider
.
final Context context = getContext();
final String consoleApplicationId = "111111";
final String deeplinkScheme = "yourappscheme";
final BillingClientThemeProvider themeProvider = BillingClientThemeProviderImpl();
RuStoreBillingClient billingClient = RuStoreBillingClientFactory.INSTANCE.create(
context,
consoleApplicationId,
deeplinkScheme,
themeProvider
);
public class BillingClientThemeProviderImpl implements BillingClientThemeProvider {
@NonNull
@Override
public BillingClientTheme provide() {
// Тут должна размещаться логика по проверке установленной темы
boolean darkTheme = ...;
if (darkTheme) {
return BillingClientTheme.Dark;
} else {
return BillingClientTheme.Light;
}
}
}
Обработка ошибок
- Kotlin
- Java
Возможные ошибки
RuStoreNotInstalledException
— на устройстве пользователя не установлен RuStore;RuStoreOutdatedException
— версия RuStore, установленная на устройстве пользователя, не поддерживает данный SDK;RuStoreUserUnauthorizedException
— пользователь не авторизован в RuStore;RuStoreRequestLimitReached
— с момента последнего отображения процесса прошло слишком мало времени;RuStoreReviewExists
— этот пользователь уже оценил ваше приложение;RuStoreInvalidReviewInfo
— проблемы сReviewInfo
;RuStoreException
— базовая ошибка RuStore, от которой наследуются остальные ошибки.
RuStoreBillingClient.purchases.purchaseProduct()
ошибки обрабатываются автоматически.
Метод устарел и не рекомендуется к использованию.
Для показа диалога с ошибкой пользователю используйте метод
resolveForBilling
public fun RuStoreException.resolveForBilling(context: Context)
Возможные ошибки
RuStoreNotInstalledException
— на устройстве пользователя не установлен RuStore;RuStoreOutdatedException
— версия RuStore, установленная на устройстве пользователя, не поддерживает данный SDK;RuStoreUserUnauthorizedException
— пользователь не авторизован в RuStore;RuStoreRequestLimitReached
— с момента последнего отображения процесса прошло слишком мало времени;RuStoreReviewExists
— этот пользователь уже оценил ваше приложение;RuStoreInvalidReviewInfo
— проблемы сReviewInfo
;RuStoreException
— базовая ошибка RuStore, от которой наследуются остальные ошибки.
RuStoreBillingClient.purchases.purchaseProduct()
ошибки обрабатываются автоматически.
Метод устарел и не рекомендуется к использованию.
Для показа диалога с ошибкой пользователю используйте метод
resolveForBilling
BillingRuStoreExceptionExtKt.resolveForBilling(exception, getContext());
Коды ошибок
Ниже представлено описание возможных ошибок в поле errorCode
.
HTTP-код | Код ошибки | Описание |
---|---|---|
400 | 40001 | Параметры запроса неверны — не заполнены обязательные параметры/неверный формат параметров. |
400 | 40003 | Приложение не найдено. |
400 | 40004 | Статус приложения inactive . |
400 | 40005 | Продукт не найден. |
400 | 40006 | Статус продукта inactive . |
400 | 40007 | Недопустимый тип продукта. Поддерживаемые типы: consumable , non-consumable , subscription . |
400 | 40008 | Покупка с таким order_id уже сущес твует. |
400 | 40009 | У текущего клиента найдена покупка этого продукта со статусом invoice_created . Необходимо предложить клиенту оплатить/отменить покупку. |
400 | 40010 | Для типа продукта consumable . У текущего клиента найдена покупка этого продукта со статусом paid . Сначала требуется подтвердить потребление покупки на устройстве, а затем можно отправлять следующий запрос на покупку этого продукта. |
400 | 40011 | Для типа продукта non-consumable . У текущего клиента найдена покупка этого продукта со статусом pre_confirmed /confirmed . Такой продукт уже приобретён. Более одного раза продукт не продаётся. |
400 | 40012 | Для типа продукта subscription . У текущего клиента найдена покупка этого продукта со статусом pre_confirmed /confirmed . Такой продукт уже приобретён. Более одного раза продукт не продаётся. |
400 | 40013 | Для типа продукта subscription . При обращении в сервис подписок за списком продуктов GET/products (serviceId , user_id ) данные не были получены. |
400 | 40014 | Обязательный атрибут(-ы) не пришел в запросе. |
400 | 40015 | Не удалось изменить статус при обновлении покупки (переход запрещён). |
400 | 40016 | При покупке подписки непотребляемого продукта указано значение quantity > 1 . |
400 | 40017 | Продукт удалён, новые покупки не доступны. |
400 | 40018 | Нельзя подтверждать продукт с типом тип продукта . |
401 | 40101 | Невалидный токен. |
401 | 40102 | Время жизни токена истекло. |
403 | 40301 | Доступ к запрашиваемому ресурсу запрещён (неавторизованно). |
403 | 40302 | Для текущего токена текущий вызов не авторизован (метод запрещён). |
403 | 40303 | Идентификатор приложения в запросе и токен не совпадают. |
403 | 40305 | Неверный тип токена. |
404 | 40401 | Не найдено. |
408 | 40801 | Истекло время ожидания уведомления, указанное в запросе. |
500 | 50*** | Внутренняя ошибка платежного сервиса. |