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

5.0.0

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

Пример реализации

Ознакомьтесь с приложением-примером, чтобы узнать, как правильно интегрировать SDK платежей.

Условия работы платежей

  • На устройстве пользователя установлена актуальная версия RuStore.
  • Пользователь авторизован в RuStore.
  • Пользователь и приложение не должны быть заблокированы в RuStore.
  • Для приложения включена возможность покупок в Консоли RuStore.
предупреждение

Сервис имеет некоторые ограничения на работу за пределами России.

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

Для подключения скачайте RuStore Billing SDK и импортируйте его в проект (Assets > Import Package > Custom Package). Зависимости подключаются автоматически с помощью External Dependency Manager (включен в SDK).

подсказка

Если вы используете операционную систему macOS, измените настройки утилиты архивации. В настройках Archive Utility снимите флажок Keep expanding if possible. В противном случае архив проекта будет скачан некорректно.

Также вы можете склонировать код с помощью Git.

Для корректной обработки зависимостей SDK установите следующие настройки.

  • Edit > Project Settings > Player Settings > Publishing Settings, включите Custom Main Gradle Template и Custom Gradle Properties Template.
  • Assets > External Dependencies Manager > Android Resolver > Settings, включите Use Jetifier, Patch mainTemplate.gradle, Patch gradleTemplate.properties.

После настройки обязательно сделать Assets > External Dependencies Manager > Android Resolver > Force Resolve.

Minimum API level должен быть установлен не ниже 24. Минификация приложения (ProGuard/R8) в данный момент не поддерживается, необходимо её отключить в настройках проекта (File > Build Settings > Player Settings > Publishing Settings > Minify).

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

Перед вызовом методов библиотеки необходимо выполнить её инициализацию.

Выберите в меню редактора пункт Window > RuStoreSDK > Settings > Billing Client.

RuStoreBillingClient.Instance.Init();

Если требуется инициализация с другими настройками, есть возможность передать их непосредственно из кода.

var config =  new RuStoreBillingClientConfig() {
consoleApplicationId = "11111" ,
deeplinkPrefix = "yourappscheme" ,
allowNativeErrorHandling = true,
enableLogs = true
};
RuStoreBillingClient.Instance.Init(config);
  • consoleApplicationId — код приложения из консоли разработчика RuStore (пример: https://console.rustore.ru/apps/123456).
  • deeplinkPrefix — URL-адрес для использования deeplink. В качестве названия может быть использовано любое уникальное имя (пример: yourappscheme).
  • allowNativeErrorHandling — разрешить обработку ошибок в нативном SDK (см. подробнее в разделе Обработка ошибок).
  • enableLogs — включить ведение журнала событий.
примечание
Схема deeplink, передаваемая в deeplinkPrefix, должна совпадать со схемой, указанной в AndroidManifest.xml (подробнее см. Обработка deeplink).

Если вам нужно проверить факт инициализации библиотеки, используйте свойство RuStoreBillingClient.Instance.isInitialized — его значение true, если библиотека инициализирована, и false, если Init еще не был вызван.

var isInitialized = RuStoreBillingClient.Instance.IsIninialized;

Для корректной работы оплаты через сторонние приложения (СБП, SberPay и др.) необходимо правильно реализовать обработку deeplink. Укажите в AndroidManifest.xml intent-filter с scheme вашего проекта (см. ниже).

AndroidManifest.xml
<activity
android:name="ru.rustore.unitysdk.RuStoreUnityActivity" android:theme ="@style/UnityThemeSelector" android:exported ="true">
<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>

Здесь: yourappscheme — схема вашего deeplink, её можно изменить на другую.

Далее расширьте класс UnityPlayerActivity и добавьте обработку входящего intent в onNewIntent.

package ru.rustore.unitysdk;import android.os.Bundle;import android.content.Intent;import ru.rustore.unitysdk.billingclient.RuStoreUnityBillingClient;import com.unity3d.player.UnityPlayerActivity;public class RuStoreUnityActivity extends UnityPlayerActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
if (savedInstanceState == null ) {
RuStoreUnityBillingClient.onNewIntent(getIntent());
}
}
@Override protected void onNewIntent(Intent intent) {
super .onNewIntent(intent);
RuStoreUnityBillingClient.onNewIntent(intent);
}}

Разместите Java-файл с кодом расширения UnityPlayerActivity в папке проекта Assets. Если у вас уже есть своё расширение UnityPlayerActivity, перенесите в него код функций onCreate и onNewIntent.

Как работают платежи

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

Для проверки доступности платежей используйте метод CheckPurchasesAvailability. Если все указанные выше условия выполняются, возвращается FeatureAvailabilityResult.isAvailable == true. В противном случае возвращается FeatureAvailabilityResult.isAvailable == false, где FeatureAvailabilityResult.cause — это ошибка о невыполненном условии.

Все возможные ошибки RuStoreException описаны в разделе Обработка ошибок. Прочие ошибки возвращаются в onFailure.

RuStoreBillingClient.Instance.CheckPurchasesAvailability( 
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
if (response.isAvailable) {
// Process purchases available
} else {
// Process purchases unavailable
}
});

Работа с SDK

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

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

RuStoreBillingClient.Instance.GetProducts(productIds,
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
// Process response
});

string[] productIds — список идентификаторов продуктов.

Метод возвращает список продуктов List<Product>.

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

public class Product {
public enum ProductStatus {
ACTIVE,
INACTIVE
}
public enum ProductType {
NON_CONSUMABLE,
CONSUMABLE,
SUBSCRIPTION
}
public string productId;
public ProductType productType;
public ProductStatus productStatus;
public string priceLabel;
public int price;
public string currency;
public string language;
public string title;
public string description;
public string imageUrl;
public string promoImageUrl;
public ProductSubscription subscription;
}
  • productId — идентификатор продукта.
  • productType — тип продукта.
  • productStatus — статус продукта.
  • priceLable — отформатированная цена товара, включая валютный знак на языке language.
  • price — цена в минимальных единицах (в копейках).
  • currency — код валюты ISO 4217.
  • language — язык, указанный с помощью BCP 47 кодирования.
  • title — название продукта на языке language.
  • description — описание на языке language.
  • imageUrl — ссылка на картинку.
  • promoImageUrl — ссылка на промокартинку.
  • subscription — описание подписки, возвращается только для продуктов с типом subscription.

Структура подписки

public class ProductSubscription {
public SubscriptionPeriod subscriptionPeriod;
public SubscriptionPeriod freeTrialPeriod;
public SubscriptionPeriod gracePeriod;
public string introductoryPrice;
public string introductoryPriceAmount;
public SubscriptionPeriod introductoryPricePeriod;
}
  • subscriptionPeriod — период подписки.
  • freeTrialPeriod — пробный период подписки.
  • gracePeriod — льготный период подписки.
  • introductoryPrice — отформатированная вступительная цена подписки, включая знак валюты, на языке product:language.
  • introductoryPriceAmount — вступительная цена в минимальных единицах валюты (в копейках).
  • introductoryPricePeriod — расчётный период вступительной цены.

Структура периода подписки

public class SubscriptionPeriod {
public int years;
public int months;
public int days;
}
  • years — количество лет.
  • months — количество месяцев.
  • days — количество дней.

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

Метод возвращает только покупки со статусами из таблицы ниже. Подробнее о других возможных состояниях покупки смотрите в разделе Получение сведений о покупке.

Тип/СтатусINVOICE_CREATEDCONFIRMEDPAIDPAUSED
consumable++
non-consumable++
subscription+++
примечание

Метод возвращает незавершённые состояния покупки и покупки потребляемых товаров, требующих обработки. Помимо этого, он показывает подтверждённые покупки для подписок и непотребляемых товаров — тех, которые нельзя купить повторно.

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

RuStoreBillingClient.Instance.GetPurchases(
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
// Process response
});

Метод возвращает List<Purchase> response — список покупок.

Структура покупки

public class Purchase {
public enum PurchaseState
{
CREATED,
INVOICE_CREATED,
CONFIRMED,
PAID,
CANCELLED,
CONSUMED,
CLOSED
}
public string purchaseId;
public string productId;
public Product.ProductType productType;
public string description;
public string invoiceId;
public string language;
public DateTime purchaseTime;
public string orderId;
public string amountLabel;
public int amount;
public string currency;
public int quantity;
public PurchaseState purchaseState;
public string developerPayload;
public string subscriptionToken;
}
  • purchaseId — идентификатор покупки.
  • productId — идентификатор продукта.
  • description — описание на языке language.
  • invoiceId — идентификатор счёта.
  • language — язык, указанный с помощью BCP 47 кодирования.
  • purchaseTime — время покупки.
  • orderId — уникальный идентификатор оплаты, сформированный приложением (UUID).
  • amountLable — отформатированная цена покупки, включая валютный знак на языке language.
  • amount — цена в минимальных единицах валюты.
  • currency — код валюты ISO 4217.
  • quantity — количество продукта.
  • purchaseState — состояние покупки:
  • developerPayload — указанная разработчиком строка, содержащая дополнительную информацию о заказе.
  • subscriptionToken — токен для валидации покупки на сервере.

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

Для получения информации о покупке, используйте метод getPurchaseInfo.
RuStoreBillingClient.Instance.GetPurchaseInfo(
purchaseId: "purchaseId",
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
// Process response
}
);

Метод возвращает Purchase response — покупку.

Структура покупки

public class Purchase {
public enum PurchaseState
{
CREATED,
INVOICE_CREATED,
CONFIRMED,
PAID,
CANCELLED,
CONSUMED,
CLOSED
}
public string purchaseId;
public string productId;
public Product.ProductType productType;
public string description;
public string invoiceId;
public string language;
public DateTime purchaseTime;
public string orderId;
public string amountLabel;
public int amount;
public string currency;
public int quantity;
public PurchaseState purchaseState;
public string developerPayload;
public string subscriptionToken;
}
  • purchaseId — идентификатор покупки.
  • productId — идентификатор продукта.
  • description — описание на языке language.
  • invoiceId — идентификатор счёта.
  • language — язык, указанный с помощью BCP 47 кодирования.
  • purchaseTime — время покупки.
  • orderId — уникальный идентификатор оплаты, сформированный приложением (UUID).
  • amountLable — отформатированная цена покупки, включая валютный знак на языке language.
  • amount — цена в минимальных единицах валюты.
  • currency — код валюты ISO 4217.
  • quantity — количество продукта.
  • purchaseState — состояние покупки:
  • developerPayload — указанная разработчиком строка, содержащая дополнительную информацию о заказе.
  • subscriptionToken — токен для валидации покупки на сервере.

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

Если вам необходимо произвести валидацию успешной покупки на сервере методами API RuStore, вы можете использовать subscriptionToken в PurchaseResult, возвращаемой purchaseProduct при успешной покупке продукта.

SubscriptionToken состоит из invoiceId покупки и userId, записанных через точку: $invoiceId.$userId.

RuStoreBillingClient.Instance.PurchaseProduct(
productId: productId,
developerPayload: "test payload",
onFailure: (error) => {
// process error
},
onSuccess: (paymentResult) => {
if (paymentResult is PaymentResult.Success) {
val subscriptionToken = paymentResult.subscriptionToken
yourApi.validate(subscriptionToken)
}
});

Также можно получить subscriptionToken в сущности Purchase. Сущность Purchase можно получить используя метод getPurchases.

RuStoreBillingClient.Instance.GetPurchases(
onFailure: (error) => {
// process error
},
onSuccess: (purchaseList) => {
foreach (var purchase in purchaseList) {
var subscriptionToken = purchase.subscriptionToken
yourApi.validate(subscriptionToken)
}
});

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

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

RuStoreBillingClient.Instance.PurchaseProduct(
productId: productId,
quantity: 1,
developerPayload: "your payload",
onFailure: (error) => {
// process error
},
onSuccess: (result) => {
// process result
});
  • productId: String — идентификатор продукта.
  • orderId: String — уникальный идентификатор оплаты, сформированный приложением (UUID).
  • quantity: Int — количество продукта.
  • developerPayload — указанная разработчиком строка, содержащая дополнительную информацию о заказе.

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

public class PaymentResult {
}

public class PaymentSuccess : PaymentResult {

public string orderId;
public string purchaseId;
public string productId;
public string invoiceId;
public string subscriptionToken;
}

public class PaymentCancelled : PaymentResult {

public string purchaseId;
}

public class PaymentFailure : PaymentResult {

public string purchaseId;
public string invoiceId;
public string orderId;
public int quantity;
public string productId;
public int errorCode;
}

public class InvalidPaymentState : PaymentResult {
}
  • PaymentSuccess - результат успешного завершения покупки цифрового товара.
  • PaymentFailure - при отправке запроса на оплату или получения статуса оплаты возникла проблема, невозможно установить статус покупки.
  • PaymentCancelled — запрос на покупку отправлен, при этом пользователь закрыл «платёжную шторку» на своём устройстве, и результат оплаты неизвестен.
  • InvalidPaymentState — ошибка работы SDK платежей. Может возникнуть, в случае некорректного обратного deeplink.

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

Продукты, требующие подтверждения

RuStore содержит продукты следующих типов.

  • SUBSCRIPTION — подписка (можно купить на период времени, например: подписка в стриминговом сервисе).
  • NON_CONSUMABLE — непотребляемый (можно купить один раз, например: отключение рекламы в приложении).
  • CONSUMABLE — потребляемый (можно купить много раз, например: кристаллы в приложении).

Подтверждения требуют только продукты типа CONSUMABLE, если они находятся в состоянии PurchaseState.PAID.

Вызов метода подтверждения

Для подтверждения покупки используйте метод confirmPurchase. Запрос на подтверждение покупки должен сопровождаться выдачей товара. После вызова подтверждения покупка перейдёт в статус CONSUMED.

RuStoreBillingClient.Instance.ConfirmPurchase(
purchaseId: "purchaseId" ,
onFailure: (error) => {
// Process error
},
onSuccess: () => {
// Process success
}
);
  • purchaseId — идентификатор покупки.

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

Для отмены покупки используйте метод DeletePurchase.

RuStoreBillingClient.Instance.DeletePurchase(
purchaseId: "purchaseId" ,
onFailure: (error) => {
// Process error
},
onSuccess: () => {
// Process success
}
);
  • purchaseId — идентификатор покупки.
к сведению

Используйте этот метод, если у вас есть логика, завязанная на удалении покупки. Покупка отменяется автоматически через таймаут в 20 минут, либо при повторной покупке от того же клиента.

Обработка незавершённых платежей

Обработка незавершённых платежей производится разработчиком.

Чтобы подтвердить покупку типа CONSUMABLE и в статусе PAID вызовите метод подтверждения покупки (см. Получение сведений о покупке).

В случае с отменой покупки при использовании методов обработки платежей учитывайте свой внутренний процесс. У некоторых разработчиков он предусматривает проверки перед потреблением или отменой покупки. В этом случае запросите отдельно статус такой покупки.

подсказка

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

В случаях, когда метод получения списка покупок возвращает покупку со статусом INVOICE_CREATED вы можете использовать метод отмены покупки. Например, если не хотите видеть покупку с такими статусами в списке покупок. Делать это самим не обязательно, поскольку RuStore обрабатывает отмену таких покупок на своей стороне.

к сведению

Иногда после оплаты через приложение банка (СБП, SberPay, Tinkoff Pay и др.) и при последующем возврате обратно в приложение статус покупки остаётся INVOICE_CREATED, при этом статус платежа — неуспешная покупка. Это связано с временем обработки покупки на стороне банка. Поэтому разработчику необходимо правильно связать логику получения списка покупок с жизненным циклом экрана.

Альтернативное решение — отмена покупки в статусе INVOICE_CREATED только через взаимодействие пользователя с приложением. Например, вы можете вынести эту логику в отдельную кнопку.

Смена темы интерфейса

SDK поддерживает динамическую смены темы через интерфейс провайдера BillingClientThemeProvider.

Получить текущую тему интерфейса можно с помощью метода GetTheme().

RuStoreBillingClient.Instance.GetTheme()

Изменить текущую тему можно использовав метод SetTheme(BillingClientTheme theme).

RuStoreBillingClient.Instance.SetTheme(BillingClientTheme.Dark)

Сущность BillingClientTheme.

public enum BillingClientTheme {
Dark,
Light,
}

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

Возможные ошибки

  • RuStoreNotInstalledException — на устройстве пользователя не установлен RuStore;
  • RuStoreOutdatedException — версия RuStore, установленная на устройстве пользователя, не поддерживает платежи;
  • RuStoreUserUnauthorizedException — пользователь не авторизован в RuStore;
  • RuStoreRequestLimitReached — с момента последнего отображения процесса прошло слишком мало времени;
  • RuStoreReviewExists — этот пользователь уже оценил ваше приложение;
  • RuStoreInvalidReviewInfo — проблемы с ReviewInfo;
  • RuStoreException — базовая ошибка RuStore, от которой наследуются остальные ошибки.

Возникающие ошибки передаются в обработчик методов SDK onFailure.

Структура ошибки

public class RuStoreError {
public string name;
public string description;
}
  • name – имя ошибки.
  • description – описание ошибки.

Автоматическая обработка ошибок

При вызове метода PurchaseProduct ошибки обрабатываются автоматически.

Если при инициализации SDK был передан параметр allowNativeErrorHandling == true, при возникновении ошибки, кроме вызова соответствующего обработчика Failure, пользователю будет показан диалог с ошибкой.

public fun RuStoreException.resolveForBilling(context: Context)

Изменить это поведение после инициализации можно установкой свойства AllowNativeErrorHandling.

RuStoreBillingClient.Instance.AllowNativeErrorHandling = false ;