SDK Платежи in-app и подписки для Defold (версия 9.1.0)
RuStore позволяет интегрировать платежи в мобильное приложение.
Если не знаете с чего начать, прочтите инструкцию в сценариях использования.
Подключение в проект
Подключение в проект
- Скопируйте проекты плагина и приложения-примера из официального репозитория RuStore на GitFlic.
- Скопируйте папки
billing_example/extension_rustore_billing
иbilling_example/extension_rustore_core
в корень вашего проекта.
Обработка deeplink
Для корректной работы оплаты через сторонние приложения (СБП, SberPay и др.) необходимо правильно реализовать обработку deeplink.
Плагин extension_rustore_billing
содержит реализацию RuStoreIntentFilterActivity
, которая обрабатывает входящие intent
и возвращается в игровую активити com.dynamo.android.DefoldActivity
.
Для включения в сборку добавьте RuStoreIntentFilterActivity
в AndroidManifest.xml
с указанием двух intent-filter
.
<activity android:name="ru.rustore.defold.billing.RuStoreIntentFilterActivity"
android:theme="@android:style/Theme.NoDisplay"
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" />
<!-- Set your appscheme -->
<data android:scheme="yourappscheme" />
</intent-filter>
</activity>
Вместо yourappscheme
из примера выше укажите название своей схемы. Например, ru.package.name.rustore.scheme
.
Схема, указанная в AndroidManifest
файле должна совпадать со схемой,
которую вы указываете в методе create
RuStore SDK платежей.
К игровому активити добавьте атрибут android:launchMode="singleTask"
и удалите intent-filter
с android.intent.action.MAIN
.
<activity android:name="com.dynamo.android.DefoldActivity"
android:label="{{project.title}}"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenOrientation="{{orientation-support}}"
android:exported="true"
android:launchMode="singleTask">
<meta-data android:name="android.app.lib_name"
android:value="{{exe-name}}" />
</activity>
Пример приложения содержит модифицированный манифест в файле billing_example/extension_rustore_billing/manifests/android/AndroidManifest.xml
Пример оформления манифеста
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="{{android.package}}"
android:versionCode="{{android.version_code}}"
android:versionName="{{project.version}}"
android:installLocation="auto">
<uses-feature android:required="true" android:glEsVersion="0x00020000" />
<uses-sdk android:minSdkVersion="{{android.minimum_sdk_version}}" android:targetSdkVersion="{{android.target_sdk_version}}" />
<application
{{#has-icons?}}
android:icon="@drawable/icon"
{{/has-icons?}}
android:label="{{project.title}}" android:hasCode="true"
android:name="android.support.multidex.MultiDexApplication"
android:enableOnBackInvokedCallback="true"
android:debuggable="{{android.debuggable}}">
<meta-data android:name="android.max_aspect" android:value="2.1" />
<meta-data android:name="android.notch_support" android:value="true"/>
<activity android:name="ru.rustore.defold.billing.RuStoreIntentFilterActivity"
android:theme="@android:style/Theme.NoDisplay"
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" />
<!-- Set your appscheme -->
<data android:scheme="example" />
</intent-filter>
</activity>
<activity android:name="com.dynamo.android.DefoldActivity"
android:label="{{project.title}}"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenOrientation="{{orientation-support}}"
android:exported="true"
android:launchMode="singleTask">
<meta-data android:name="android.app.lib_name"
android:value="{{exe-name}}" />
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>
<!-- END_INCLUDE(manifest) -->
Реализация RuStoreIntentFilterActivity
package ru.rustore.defold.billing;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class RuStoreIntentFilterActivity extends Activity {
private final String defoldActivityClassName = "com.dynamo.android.DefoldActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
RuStoreBilling.onNewIntent(getIntent());
}
if (!isTaskRoot()) {
finish();
return;
}
startGameActivity(defoldActivityClassName);
finish();
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
RuStoreBilling.onNewIntent(intent);
}
private void startGameActivity(String gameActivityClassName) {
Class<?> gameActivityClass = getActivityClass(gameActivityClassName);
if (gameActivityClass != null) {
Intent intent = new Intent(this, gameActivityClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
private Class<?> getActivityClass(String activityClassName) {
try {
return Class.forName(activityClassName);
} catch(ClassNotFoundException ex) {
return null;
}
}
}
Инициализация
Перед вызовом методов библиотеки необходимо выполнить её инициализацию.
Для инициализации вызовите методinit()
.
local APPLICATION_ID = "123456"
local DEEPLINK_SCHEME = "yourappscheme"
local DEBUG_LOGS = true
rustorebilling.init(APPLICATION_ID, DEEPLINK_SCHEME, DEBUG_LOGS)
-
APPLICATION_ID
— идентификатор приложения из RuStore консоли.
Где в RuStore Консоль отображаются идентификаторы приложений?
- Перейдите на вкладку Приложения и выберите нужное приложение.
- Скопируйте идентификатор из URL-адреса страницы приложения — это набор цифр между
apps/
и/versions
. Например, для URL-адресаhttps://console.rustore.ru/apps/123456/versions
ID приложения —123456
.
-
DEEPLINK_SCHEME
— схема deeplink, необходимая для возврата в ваше приложение после оплаты через стороннее приложение (например, SberPay или СБП). SDK генерирует свой хост к данной схеме. -
DEBUG_LOGS
— флаг, регулирующий ведение журнала событий. Укажите значениеtrue
, если хотите, чтобы события попадали в журнал. В ином случае укажитеfalse
.
DEEPLINK_SCHEME
, должна совпадать со схемой, указанной в AndroidManifest.xml
(подробнее см. Обработка deeplink).Как работают платежи
Проверка доступности работы с платежами
Метод устарел и не рекомендуется к использованию.
Для проверки доступности платежей используйте метод
check_purchases_availability
.
Начиная с версии 6.0.0 процедура и результат проверки доступности платежей зависит от наличия RuStore на устройстве пользователя.
Подробнее см. ниже.
Наличие RuStore | Процедура и результат проверки |
---|---|
Не установлен | Метод вернёт |
Установлен | Выполняется проверка следующих условий.
isAvailable == true .В противном случае возвращается isAvailable == false, cause.simpleName, cause.detailMessage , где cause — это ошибка о невыполненном условии. |
Таким образом, положительный ответ вернётся в двух случаях:
- если RuStore не установлен;
- если RuStore установлен и установленный экземпляр RuStore на устройстве пользователя соответствует проверяемым условиям.
Сам по себе ответ метода check_purchases_availability
не показывает, установлен ли RuStore на устройстве пользователя. Вы можете проверить наличие RuStore на устройстве пользователя с помощью метода isRuStoreInstalled
.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_check_purchases_available_success
;rustore_check_purchases_available_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_check_purchases_available_success", _check_purchases_available_success)
rustorecore.connect("rustore_check_purchases_available_failure", _check_purchases_available_failure)
end
function _check_purchases_availability_success(self, channel, value)
local data = json.decode(value)
end
function _check_purchases_availability_failure(self, channel, value)
local data = json.decode(value)
end
rustorebilling.check_purchases_availability()
Обратный вызов (callback) rustore_check_purchases_availability_success
возвращает строку JSON с информацией о доступности сервиса.
-
isAvailable
— выполнение условий выполнения платежей (true
/false
). -
cause
— информация об ошибке.
Структура ошибки описана в разделе Обработка ошибок.
Обратный вызов (callback) rustore_check_purchases_availability_failure
возвращает строку JSON с информацией об ошибке. Структура ошибки описана в разделе Обработка ошибок.
Методы SDK
Определение наличия авторизации у пользователя
В rustorebilling есть возможность определить, является ли пользователь авторизованным. Для определения наличия авторизации у пользователя требуется вызвать метод get_authorization_status
.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_on_get_authorization_status_success
;rustore_on_get_authorization_status_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_on_get_authorization_status_success", _on_get_authorization_status_success)
rustorecore.connect("rustore_on_get_authorization_status_failure", _on_get_authorization_status_failure)
end
function _on_get_authorization_status_success(self, channel, value)
local isAuthorized = value -- boolean
end
function _on_get_authorization_status_failure(self, channel, value)
local data = json.decode(value)
end
rustorebilling.get_authorization_status()
В случае использования SDK вне RuStore результат true
также может вернуться, если в процессе оплаты пользователь авторизовался через VK ID и с момента авторизации прошло менее 15 минут.
Получение списка продуктов
Вы проверили, что платежи доступны и пользователи могут совершать покупки. Теперь можно получить список продуктов. Используйте метод get_products()
, чтобы получить информацию о продуктах, добавленных в ваше приложение через RuStore Консоль.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_on_get_products_success
;rustore_on_get_products_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_on_get_products_success", _on_get_products_success)
rustorecore.connect("rustore_on_get_products_failure", _on_get_products_failure)
end
function _on_get_products_success(self, channel, value)
local data = json.decode(value)
end
function _on_get_products_failure(self, channel, value)
local data = json.decode(value)
end
local PRODUCT_IDS = {
"non_con2",
"non_con1",
"con2",
"con1",
"sub2",
"sub1"}
rustorebilling.get_products(PRODUCT_IDS)
PRODUCT_IDS
— список идентификаторов продуктов. В нём не должно быть более 100 позиций.
Чтобы указать id
продуктов, которые нужны для работы метода, выполните следующие действия.
- Откройте RuStore Консоль.
- Перейдите на вкладку Приложения.
- Выберите нужное приложение.
- В левом боковом меню выберите раздел Монетизация.
- Выберите тип товара: Подписки или Разовые поку пки.
- Скопируйте идентификаторы нужных товаров. Это и есть
id
продуктов.
Обратный вызов (callback) rustore_on_get_products_success
возвращает строку JSON с информацией о продуктах (см. ниже).
currency
— код валюты ISO 4217.description
— описание на языкеlanguage
.imageUrl
— ссылка на картинку.language
— язык, указанный с помощью BCP 47 кодирования.price
— цена в минимальных единицах (в копейках).priceLable
— отформатированная цена товара, включая валютный знак на языкеlanguage
.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productStatus
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.productType
— статус продукта.promoImageUrl
— ссылка на промокартинку.title
— название продукта на языкеlanguage
.subscription
— описа ние подписки, возвращается только для продуктов с типомsubscription
.
Доступные поля объекта subscription
(см. ниже).
subscriptionPeriod
— период подписки.freeTrialPeriod
— пробный период подписки.gracePeriod
— льготный период подписки.introductoryPrice
— отформатированная вступительная цена подписки, включая знак валюты, на языкеproduct:language
.introductoryPriceAmount
— вступительная цена в минимальных единицах валюты (в копейках).introductoryPricePeriod
— расчётный период вступительной цены.
Доступные поля объекта «период» (см. ниже).
years
— количество лет.months
— количество месяцев.days
— количество дней.
Обратный вызов (callback) rustore_on_get_products_failure
возвращает строку JSON с информацией об ошибке. Структура ошибки описана в разделе Обработка ошибок.
Покупка продукта
Для вызова покупки продукта используйте метод purchase_product()
.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_on_purchase_product_success
;rustore_on_purchase_product_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_on_purchase_product_success", _on_purchase_product_success)
rustorecore.connect("rustore_on_purchase_product_failure", _on_purchase_product_failure)
end
function _on_purchase_product_success(self, channel, value)
local data = json.decode(value)
end
function _on_purchase_product_failure(self, channel, value)
local data = json.decode(value)
end
local PRODUCT_ID = "example_id"
local PARAMS = "{" ..
"\"orderId\":\"example\"," ..
"\"quantity\":1," ..
"\"payload\":\"example\"" ..
"}"
rustorebilling.purchase_product(PRODUCT_ID, PARAMS)
PRODUCT_ID
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр);PARAMS
— опциональные параметры:orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов;quantity
— количество продукта. Необязательный параметр со стандартным значением1
. Применим только к покупке потребляемых товаров;payload
— строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации.
Обратный вызов rustore_on_purchase_product_success
возвращает строку JSON с информацией о покупке. Доступны следующие поля.
-
type
— тип результата запросаSuccess
— результат успешного завершения покупки цифрового товара;Failure
— при отправке запроса на оплату или получения статуса оплаты возникла проблема, невозможно установить статус покупки;Cancelled
— запрос на покупку отправлен, при этом пользователь закрыл «платёжную шторку» на своём устройстве, и результат оплаты неизвестен;InvalidPaymentState
— ошибка работы SDK платежей. Может возникнуть, в случае некорректного обратного deeplink.
-
data
— строка JSON с опциональными полями.
Объект типа Success
возвращается в случае удачного выполнения запроса. Доступны следующие поля.
orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.purchaseId
— идентификатор покупки.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).invoiceId
— идентификатор счёта.
.sandbox
— флаг тестового платежа. Значениеtrue
— тестовый платёж,false
— реальный платёж.subscriptionToken
— токен для валидации покупки на сервере.
Объект типа Failure
возвращается в случае ошибки при выполнения запроса. Доступны следующие поля.
purchaseId
— идентификатор покупки.invoiceId
— идентификатор счёта.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.quantity
— количество продукта. Необязательный параметр со стандартным значением1
. Применим только к покупке потребляемых товаров.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).
.sandbox
— флаг тестового платежа. Значениеtrue
— тестовый платёж,false
— реальный платёж.errorCode
— код ошибки.
Коды ошибок описаны в разделе Коды ошибок.
Объект типа Cancelled
возвращается в случае отмены покупки пользователем. Доступны следующие поля.
purchaseId
— идентификатор покупки.
.sandbox
— флаг тестового платежа. Значениеtrue
— тестовый платёж,false
— реальный платёж.
Объект типа InvalidPaymentState
возвращается в случае ошибки работы SDK платежей. Например в случае некорректного обратного deeplink.
Обратный вызов (callback) rustore_on_purchase_product_failure
возвращает строку JSON с информацией об ошибке. Структура ошибки описана в разделе Обработка ошибок.
Получение списка покупок
Метод возвращает только покупки со статусами из таблицы ниже. Подробнее о других возможных состояниях покупки смотрите в разделе Получение сведений о покупке.
Тип/Статус | INVOICE_CREATED | CONFIRMED | PAID |
---|---|---|---|
CONSUMABLE | + | + | |
NON-CONSUMABLE | + | + | |
SUBSCRIPTION | + | + |
Метод возвращает незавершённые состояния покупки и покупки потребляемых товаров, требующих обработки. Помимо этого, он показывает подтверждённые покупки для подписок и непотребляемых товаров — тех, которые нельзя купить повторно.
Для получения списка покупок пользователя используйте метод get_purchases()
.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_on_get_purchases_success
;rustore_on_get_purchases_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_on_get_purchases_success", _on_get_purchases_success)
rustorecore.connect("rustore_on_get_purchases_failure", _on_get_purchases_failure)
end
function _on_get_purchases_success(self, channel, value)
local data = json.decode(value)
for key, value in pairs(data) do
-- value.amount
end
end
function _on_get_purchases_failure(self, channel, value)
local data = json.decode(value)
end
rustorebilling.get_purchases()
Обратный вызов (callback) rustore_on_get_purchases_success
возвращает массив объектов с информацией о покупках. Доступны следующие поля.
amount
— цена в минимальных единицах валюты.amountLable
— отформатированная цена покупки, включая валютный знак.currency
— код валюты ISO 4217.developerPayload
— строка с дополнительной информацией о заказе, которую вы можете установить при подтверждении покупки. Эта строка переопределяет значение, заданное при инициализации.invoiceId
— идентификатор счёта.language
— язык, указанный с помощью BCP 47 кодирования.orderId
— уникальный идентификатор оплаты, сформированный приложением (опциональный параметр). Если вы укажете этот параметр в вашей системе, вы получите его в ответе при работе с API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.productId
— идентификатор продукта, который был присвоен продукту в RuStore Консоли (обязательный параметр).productType
— тип продукта (потребляемый / непотребляемый / подписка):CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.purchaseId
— идентификатор покупки.purchaseState
— состояние покупки:purchaseTime
— время покупки.quantity
— количество продукта. Необязательный параметр со стандартным значением1
. Применим только к покупке потребляемых товаров.subscriptionToken
— токен для валидации покупки на сервере.
Обратный вызов (callback) rustore_on_get_purchases_failure
возвращает строку JSON с информацией об ошибке. Структура ошибки описана в разделе Обработка ошибок.
Получение сведений о покупке
Для получения информации о покупке, используйте методget_purchase_info()
.
Перед использованием метода необходимо единожды выполнить подписку на события:
rustore_on_get_purchase_info_success
;rustore_on_get_purchase_info_failure
.
function init(self)
# Инициализация rustorebilling
rustorecore.connect("rustore_on_get_purchase_info_success", _on_get_purchase_info_success)
rustorecore.connect("rustore_on_get_purchase_info_failure", _on_get_purchase_info_failure)
end
function _on_get_purchase_info_success(self, channel, value)
local data = json.decode(value)
end
function _on_get_purchase_info_failure(self, channel, value)
local data = json.decode(value)
end
# Ваша реализация UI запроса информации о покупке
function _on_get_purchase_info_pressed(purchaseId):
rustorebilling.get_purchase_info(purchaseId)
end