SDK in-app платежей для Flutter (версия 6.1.0)
RuStore позволяет интегрировать платежи в мобильное приложение.
Если не знаете с чего начать, прочтите инструкцию в сценариях использования.
Пример реализации
Ознакомьтесь с приложением-примером чтобы узнать, как правильно интегрировать SDK платежей.
Условия работы платежей
- Приложение загружено в Консоль RuStore.
- Приложение прошло модерацию (публиковать приложение необязательно).
- Подпись тестируемой сборки (например,
debug
) приложения должна совпадать с подписью сборки приложения, которая была загружена в консоль и прошла модерацию ранее (например,release
).
- Пользователь и приложение не должны быть заблокированы в RuStore.
- Для приложения включена возможность покупок в RuStore Консоли.
Сервис имеет некоторые ограничения на работу за пределами России.
Подготовка к работе
Для подключения пакета платежей к проекту выполните следующую команду.
flutter pub add flutter_rustore_billing
Эта команда добавит строчку в файл pubspec.yaml
.
dependencies: flutter_rustore_billing: ^6.1.0
Обработка deeplink
Deeplink в RuStore SDK платежей нужна для корректной работы со сторонними приложениями оплаты. Она помогает пользователям быстрее совершать покупки в стороннем приложении и возвращаться в ваше приложение.
Для настройки работы с deeplink в вашем приложении и RuStore SDK, укажите deeplinkScheme
внутри вашего AndroidManifest
файла и переопределите метод onNewIntent
вашего Activity
.
<activity
android:name=".sample.MainActivity">
<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
из примера выше укажите название своей схемы. Например, ru.package.name.rustore.scheme
.
Схема, указанная в AndroidManifest
файле должна совпадать со схемой, которую вы указываете в методе create
RuStore SDK платежей.
Инициализация
Перед вызовом методов библиотеки необходимо выполнить её инициализацию.
Для инициализации вызовите методRustoreBillingClient.initialize()
.
RustoreBillingClient.initialize(
"123456", //consolApplicationId
"yourappscheme", //deeplinkScheme
).then((value) {
print("initialize success: $value");
}, onError: (err) {
print("initialize err: $err");
}
);
-
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 генерирует свой хост к данной схеме.
deeplinkScheme
, должна совпадать со схемой, указанной в AndroidManifest.xml
(подробнее см. Обработка deeplink).Как работают платежи
Проверка доступности работы с платежами
Во время проверки доступности платежей проверяются следующие условия.
- На устройстве пользователя установлена актуальная версия RuStore.
- Приложение RuStore поддерживает функциональность платежей.
- Пользователь авторизован в RuStore.
- Пользователь и приложение не должны быть заблокированы в RuStore.
- Для приложения включена возможность покупок в RuStore Консоли.
Для проверки доступности платежей используйте метод RustoreBillingClient.available()
.Если все указанные выше условия выполняются, возвращается true
.
RustoreBillingClient.available().then((value) {
print("available success $value");
}, onError: (err) {
print("available err: $err");
}
);
Работа с SDK
Получение списка продуктов
Вы проверили, что платежи доступны и пользователи могут совершать покупки. Теперь можно получить список продуктов. Используйте метод RustoreBillingClient.products(ids)
, чтобы получить информацию о продуктах, добавленных в ваше приложение через RuStore Консоль.
RustoreBillingClient.products(ids).then((response) {
for (final product in response.products) {
print(product?.productId);
}
}, onError: (err) {
print("products err: $err");
}
);
ids: List<String?>
— список идентификаторов продуктов. В нём не должно быть более 100 позиций.
Чтобы указать id
продуктов, которые нужны для работы метода, выполните следующие действия.
- Откройте RuStore Консоль.
- Перейдите на вкладку Приложения.
- Выберите нужное приложение.
- В левом боковом меню выберите раздел Монетизация.
- Выберите тип товара: Подписки или Разовые покупки.
- Скопируйте идентификаторы нужных товаров. Это и есть
id
продуктов.
Метод возвращает ProductsResponse
class ProductsResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<Product?> products;
List<DigitalShopGeneralError?> errors;
}
code
— код ответа.errorMessage
— сообщение об ошибке для пользователя.errorDescription
— расшифровка сообщения об ошибке.traceId
— идентификатор ошибки.errors
— список ошибок для запрошенных продуктов.products
— список продуктов.
Структура ошибки DigitalShopGeneralError
class DigitalShopGeneralError {
String? name;
int ? code;
String? description
}
name
– имя ошибки.code
— код ошибки.description
– описание ошибки.
Структура продукта Product
class Product {
String productId;
String? productType;
String productStatus;
String? priceLabel;
int ? price;
String? currency;
String? language;
String? title;
String? description;
String? imageUrl;
String? promoImageUrl;
Subscription? subscription;
}
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
.
Структура подписки Subscription
class Subscription {
SubscriptionPeriod? subscriptionPeriod;
SubscriptionPeriod? freeTrialPeriod;
SubscriptionPeriod? gracePeriod;
String? introductoryPrice;
String? introductoryPriceAmount;
SubscriptionPeriod? introductoryPricePeriod;
}
subscriptionPeriod
— период подписки.freeTrialPeriod
— пробный период подписки.gracePeriod
— льготный период подписки.introductoryPrice
— отформатированная вступительная цена подписки, включая знак валюты, на языкеproduct:language
.introductoryPriceAmount
— вступительная цена в минимальных единицах валюты (в копейках).introductoryPricePeriod
— расчётный период вступительной цены.
Структура периода подписки SubscriptionPeriod
class SubscriptionPeriod {
int years;
int months;
int days;
}
years
— количество лет.months
— количество месяцев.days
— количество дней.
Покупка продукта
Для вызова покупки продукта используйте метод RustoreBillingClient.purchase(id)
.
RustoreBillingClient.purchase(id).then((response) {
print("purchase success: $response");
}, onError: (err) {
print("purchase err: $err");
}
);
id
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр)
Структура результата покупки PaymentResult
class PaymentResult {
SuccessInvoice? successInvoice;
InvalidInvoice? invalidInvoice;
SuccessPurchase? successPurchase;
InvalidPurchase? invalidPurchase;
}
Структура SuccessInvoice
class SuccessInvoice {
String invoiceId;
String finishCode;
}
Структура InvalidInvoice
class InvalidInvoice {
String? invoiceId;
}
Структура SuccessPurchase
class SuccessPurchase {
String finishCode;
String? orderId;
String purchaseId;
String productId;
String? invoiceId;
String? subscriptionToken;
bool? sandbox;
}
Структура InvalidPurchase
class InvalidPurchase {
String? purchaseId;
String? invoiceId;
String? orderId;
int? quantity;
String? productId;
int? errorCode;
bool? sandbox;
}
SuccessInvoice
— платежи завершились с результатом.InvalidInvoice
— платежи завершились без указания номера счёта. Вероятно, они были запущены с некорректным номером счёта (пустая строка, например).SuccessPurchase
— результат успешного завершения покупки цифрового товара.InvalidPurchase
— при оплате цифрового товара платежи завершились с ошибкой.
Возможные статусы, которые может содержать finishCode
SUCCESSFUL_PAYMENT
— успешная оплата.CLOSED_BY_USER
— отменено пользователем.UNHANDLED_FORM_ERROR
— неизвестная ошибка.PAYMENT_TIMEOUT
— ошибка оплаты по таймауту.DECLINED_BY_SERVER
— отклонено сервером.RESULT_UNKNOWN
— неизвестный статус оплаты.
Получение списка покупок
Метод возвращает только покупки со статусами из таблицы ниже. Подробнее о других возможных состояниях покупки смотрите в разделе Получение сведен ий о покупке.
Тип/Статус | INVOICE_CREATED | CONFIRMED | PAID | PAUSED |
---|---|---|---|---|
CONSUMABLE | + | + | ||
NON-CONSUMABLE | + | + | ||
SUBSCRIPTION | + | + | + |
Метод возвращает незавершённые состояния покупки и покупки потребляемых товаров, требующих обработки. Помимо этого, он показывает подтверждённые покупки для подписок и непотребляемых товаров — тех, которые нельзя купить повторно.
Для получения списка покупок пользователя используйте метод RustoreBillingClient.purchases
.
RustoreBillingClient.purchases().then((response) {
for (final product in response.purchases) {
print(product?.purchaseId);
}
}, onError: (err) {
print("purchases err: $err" );
}
);
Метод возвращает PurchasesResponse
(см. ниже).
class PurchasesResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<Purchase?> purchases;
List<DigitalShopGeneralError?> errors;
}
code
— код ответа.errorMessage
— сообщение об ошибке для пользователя.errorDescription
— расшифровка сообщения об ошибке.errors
— список ошибок для запрошенных продуктов.purchases
— список запрошенных покупок.
Структура ошибки DigitalShopGeneralError
(см. ниже).
class DigitalShopGeneralError {
String? name;
int ? code;
String? description;
}
name
– имя ошибки.code
— код ошибки.description
– описание ошибки.
class Purchase {
String? purchaseId;
String? productId;
String? productType;
String? language;
String? purchaseTime;
String? orderId;
String? amountLabel;
int? amount;
String? currency;
int? quantity;
String? purchaseState;
String? developerPayload;
String? invoiceId;
String? subscriptionToken;
}
Структура покупки Purchase
(см. ниже).
purchaseId
— идентификатор покупки.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).description
— описание на языкеlanguage
.language
— язык, указанный с помощью BCP 47 кодирования.purchaseTime
— время покупки.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.amountLable
— отформатированная цена покупки, включая валютный знак.amount
— цена в минимальных единицах валюты.currency
— код валюты ISO 4217.quantity
— количество продукта (необязательный параметр — если не указывать, будет подставлено значение1
).purchaseState
— состояние покупки.developerPayload
— строка с дополнительной информацией о заказе, кото рую вы можете установить при инициализации процесса покупки.
Получение сведений о покупке
Для получения информации о покупке, используйте методpurchaseInfo
.
RustoreBillingClient.purchaseInfo(purchaseId).then((purchase) {
print(purchase);
}, onError: (err) {
print("Error getPurchaseInfo: $err");
}
);
Структура покупки Purchase
(см. ниже).
class Purchase {
String? purchaseId;
String? productId;
String? productType;
String? language;
String? purchaseTime;
String? orderId;
String? amountLabel;
int? amount;
String? currency;
int? quantity;
String? purchaseState;
String? developerPayload;
String? invoiceId;
String? subscriptionToken;
}
purchaseId
— идентификатор покупки.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).description
— описание на языкеlanguage
.language
— язык, указанный с помощью BCP 47 кодирования.purchaseTime
— время покупки.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.amountLable
— отформатированная цена покупки, включая валютный знак.amount
— цена в минимальных единицах валюты.currency
— код валюты ISO 4217.quantity
— количество продукта (необязательный параметр — если не указывать, будет подставлено значение1
).purchaseState
— состояние покупки.developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при инициализации процесса покупки.
Статусная модель (purchaseState
)
Статусная модель покупки потребляемых продуктов (CONSUMABLES
)
Статусная модель покупки непотребляемых продуктов (NON-CONSUMABLES
)
Статусная модель покупки подписок (SUBSCRIPTIONS
)
Потребление (подтверждение) покупки
Продукты, требующие потребления (подтверждения)
Учитывайте тип покупки. Метод потребления (подтверждения) необходим, только если у вас потребляемый товар (CONSUMABLE
), который можно купить много раз.
Чтобы такие товары начислились пользователям без ошибок, подтвердите потребление (подтверждение) продукта с помощью метода confirmPurchase
.
При начислении товара в вашем приложении используйте серверную валидацию платежей. Начисляйте товар только когда платёж (счёт) перейдет в финальный статус CONFIRMED
.
Начисление продуктов пользователям надо делать в callback addOnSuccessListener
метода confirmPurchase
.
Статус PAID
является промежуточным и означает, что средства пользователя захолдированы на карте и вам нужно подтвердить покупку.
Исключение составляют платежи через СБП или мобильное списание — см. пояснения ниже.
При оплате потребляемых (CONSUMABLE
) товаров через СБП или мобильное списание используется одностадийный платёж, при этом модель счёта остаётся двухстадийной. Это значит, что при переходе счёта в статус PAID
при оплате через СБП или мобильное списание деньги уже списаны со счёта покупателя, а с разработчика удержана комиссия. В этом случае при отмене покупки в состоянии PAID
происходит возврат средств (refund), а не отмена холдирования — reverse. Удержанная комиссия разработчику не возвращается. При этом для завершения покупки всё равно нужно выполнить метод подтверждения (потребления) — см. также таблицу ниже.
Платёжный метод | Тип платежа | Платёж в статусе PAID |
---|---|---|
| Дву хстадийный |
|
| Одностадийный |
|
Вызов метода потребления (подтверждения)
Для потребления (подтверждения) покупки используйте методRustoreBillingClient.confirm(id)
. Запрос на потребление (подтверждение) покупки должен сопровождаться выдачей товара. После вызова подтверждения покупка перейдёт в статус CONSUMED
.
RustoreBillingClient.confirm(id).then((response) {
print( "confirm success: $response" );
}, onError: (err) {
print( "confirm err: $err" );
}
);
id
— идентификатор покупки.
Метод возвращает ConfirmPurchaseResponse
(см. ниже).
class ConfirmPurchaseResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<DigitalShopGeneralError?> errors;
}
code
— код ответа.errorMessage
— сообщение об ошибке для пользователя.errorDescription
— расшифровка сообщения об ошибке.traceId
— идентификатор ошибки.errors
— список ошибок для запрошенных продуктов.
Структура ошибки DigitalShopGeneralError
(см. ниже).
class DigitalShopGeneralError {
String? name;
int ? code;
String? description;
}
name
– имя ошибки.code
— код ошибки.description
– описание ошибки.
Отмена покупки
Для отмены покупки используйте метод deletePurchase
.
RustoreBillingClient.deletePurchase(purchaseId = "purchaseId").then(() {
print("Delete success");
}, onError: (err) {
print("Error getPurchaseInfo: $err");
}
);
purchaseId
— идентификатор покупки.
Используйте этот метод, если у вас есть логика, завязанная на удалении покупки. Покупка отменяется автоматически через таймаут в 20 минут, либо при повторной покупке от того же клиента.
Валидация покупок на сервере
Если вам необходимо произвести валидацию успешной покупки на сервере методами API RuStore, вы можете использовать subscriptionToken
в PurchaseResult
, возвращаемой purchaseProduct
при успешной покупке продукта.
SubscriptionToken
состоит из invoiceId
покупки и userId
, записанных через точку: $invoiceId.$userId
.
Также можно получить subscriptionToken
в сущности Purchase
. Сущность Purchase
можно получить, используя метод purchases()
.
Ведение журнала событий
Логирование включено по умолчанию внутри flutter sdk. Логи вы можете смотреть по тегу RuStoreFlutterBillingPlugin
.
Обработка ошибок
Возможные ошибки
RuStoreNotInstalledException
— на устройстве пользователя не установлен RuStore;RuStoreOutdatedException
— версия RuStore, установленная на устройстве пользователя, не поддерживает данный SDK;RuStoreUserUnauthorizedException
— пользователь не авторизован в RuStore;RuStoreApplicationBannedException
— приложение заблокировано в RuStore;RuStoreUserBannedException
— пользователь заблокирован в RuStore;RuStoreException
— базовая ошибка RuStore, от которой наследуются остальные ошибки.
По умолчанию в плагине включена нативная обработка ошибок, в случае возникновения ошибки выполняются эти действия:
-
Вызовется соответствующий обработчик ошибки.
-
Ошибка передаётся в метод
resolveForBilling
нативного SDK. Это нужно, чтобы показать пользователю диалог с ошибкой.
fun RuStoreException.resolveForBilling(context: Context)
Чтобы отключить передачу ошибки в нативный SDK, вызовите функцию offNativeErrorHandling
.
RustoreBillingClient.offNativeErrorHandling();