Поддержка BillingClient SDK прекращается 1 августа 2026 года. После 1 августа 2026 года платежи перестанут обрабатываться для всех покупок, включая подписки.
До этого времени BillingClient SDK продолжает свою работу, но устранение неисправностей, влияющих на работу платежей, может занимать больше времени. Новая функциональность добавляться не будет.
Рекомендуем использовать Pay SDK в проектах.
Для перехода на Pay SDK воспользуйтесь инструкцией по миграции.
SDK Платежи in-app и подписки для Defold (версия 10.3.1)
RuStore позволяет интегрировать платежи в мобильное приложение.
Если не знаете с чего начать, прочтите инструкцию в сценариях использования.
Подключение в проект
-
Перейдите в раздел Релизы репозитория проекта на GitFlic.
-
Скачайте из выбранного релиза артефакты:
extension_rustore_billing.zipextension_rustore_core.zip
-
Распакуйте скачанные архивы в корень вашего Defold-проекта, чтобы получилась следующая структура:
📁 your_project
├─ 📁 extension_rustore_billing
│ ├─ 📁 lib
│ ├─ 📁 manifests
│ ├─ 📁 src
│ └─ ext.manifest
└─ 📁 extension_rustore_core
├─ 📁 include
├─ 📁 lib
├─ 📁 manifests
├─ 📁 src
└─ ext.manifest
Обработка 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/versionsID приложения —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 минут.