Подключение QR‑код от Яндекс Пэй Android SDK
Шаг 1. Добавьте зависимости
// build.gradle.kts
dependencies {
implementation("com.yandex.pay:quickpay:LATEST_VERSION")
}
// build.gradle (Groovy DSL)
dependencies {
implementation 'com.yandex.pay:quickpay:LATEST_VERSION'
}
Актуальная версия: com.yandex.pay:quickpay
ProGuard / R8: дополнительная настройка не нужна. SDK поставляется с consumer-rules.pro — правила применяются автоматически при сборке приложения.
Multidex: явно не требуется (minSdk = 24). SDK включает несколько крупных зависимостей, поэтому если суммарное число методов в вашем приложении приближается к 64 000, включите multidex:
android {
defaultConfig {
multiDexEnabled = true
}
}
dependencies {
implementation("androidx.multidex:multidex:2.0.1")
}
Манифест: дополнительные объявления не нужны. SDK добавляет необходимые пермиссии и <queries> через Manifest Merger автоматически:
android.permission.INTERNETandroid.permission.ACCESS_NETWORK_STATE<queries>для пакетовcom.yandex.bankиcom.yandex.bank.dev(для взаимодействия с приложением Яндекс Банк на Android 11+)
Шаг 2. Инициализируйте SDK
Инициализация выполняется один раз в Application.onCreate().
import com.yandex.pay.quickpay.api.YandexQuickPay
import com.yandex.pay.quickpay.api.QuickPayConfig
import com.yandex.pay.quickpay.api.QuickPayEnvironment
import com.yandex.pay.quickpay.api.QuickPayLocale
import com.yandex.pay.quickpay.api.QuickPayThemeColorScheme
import com.yandex.pay.quickpay.api.QuickPaymentStateListener
import com.yandex.pay.quickpay.api.QuickPayResult
import com.yandex.pay.quickpay.api.IsPaymentEnabled
class MyApp : Application() {
val quickPayListener = object : QuickPaymentStateListener {
override fun onPaymentEnabledStateChanged(isEnabled: IsPaymentEnabled) {}
override fun onSessionExpired() {}
override fun onPaymentResult(result: QuickPayResult) {}
}
override fun onCreate() {
super.onCreate()
YandexQuickPay.locale = QuickPayLocale.SYSTEM
YandexQuickPay.theme = QuickPayThemeColorScheme.SYSTEM
val config = QuickPayConfig(
merchantId = "YOUR_MERCHANT_ID",
environment = QuickPayEnvironment.SANDBOX, // SANDBOX для разработки, PRODUCTION для релиза
)
YandexQuickPay.initialize(
config = config,
context = this,
quickPaymentStateListener = quickPayListener,
)
}
}
Важно
SDK хранит листенер как WeakReference. Держите сильную ссылку в Application, Activity или ViewModel, иначе объект будет собран GC и колбэки перестанут приходить.
Важно
Перед релизом установите QuickPayEnvironment.PRODUCTION в конфиге. В sandbox-окружении реальные платежи не проводятся.
initialize() можно вызывать повторно — например, если нужно сменить merchantId или обновить листенер. При повторном вызове старый листенер заменяется.
Шаг 3. Инициализируйте UI-компоненты
initUi() необходимо вызвать в Activity, которая будет работать с быстрой оплатой. Метод не показывает никакого UI сразу — он регистрирует лончеры для OAuth-авторизации и биометрии, а также сохраняет ссылки для отображения bottom sheets.
lifecycleScope.launch {
YandexQuickPay.initUi(
activity = this@QrActivity,
fragmentManager = supportFragmentManager
)
// После initUi можно вызывать getPaymentSessionId() и enableQuickPayment()
}
Когда initUi() обязателен
- При вызове
getPaymentSessionId()(требуется биометрическая аутентификация) - При вызове
enableQuickPayment()(OAuth-авторизация через Яндекс ID) - При показе bottom sheets в процессе оплаты
Когда можно обойтись без initUi()
Если вы только отображаете виджет YandexPaymentMethodsWidget в read-only режиме без авторизации пользователя.
Шаг 4. Добавьте виджет методов оплаты
YandexPaymentMethodsWidget показывает доступные пользователю платёжные методы и элементы управления быстрой оплатой. Виджет обновляется автоматически.
Через XML:
<com.yandex.pay.quickpay.api.YandexPaymentMethodsWidget
android:id="@+id/paymentMethodsWidget"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Программно:
val widget = YandexPaymentMethodsWidget(context)
widgetContainer.addView(widget)
Публичного API у виджета нет — он управляет своим состоянием самостоятельно через SDK.
Шаг 5. Управляйте состоянием быстрой оплаты
Рекомендуем вынести логику в ViewModel для корректного lifecycle.
sealed interface QrScreenState {
object Loading : QrScreenState
data class QrReady(val sessionId: String) : QrScreenState
object PaymentDisabled : QrScreenState
data class Error(val message: String) : QrScreenState
}
class QrViewModel : ViewModel() {
private val _state = MutableStateFlow<QrScreenState>(QrScreenState.Loading)
val state: StateFlow<QrScreenState> = _state.asStateFlow()
val quickPayListener = object : QuickPaymentStateListener {
override fun onPaymentEnabledStateChanged(isEnabled: IsPaymentEnabled) {
if (isEnabled.value) refreshSession() else _state.value = QrScreenState.PaymentDisabled
}
override fun onSessionExpired() { refreshSession() }
override fun onPaymentResult(result: QuickPayResult) { refreshSession() }
}
fun init() {
viewModelScope.launch {
val isEnabled = runCatching { YandexQuickPay.isQuickPaymentEnabled() }
.getOrDefault(IsPaymentEnabled(false))
if (isEnabled.value) refreshSession() else _state.value = QrScreenState.PaymentDisabled
}
}
fun refreshSession() {
viewModelScope.launch {
_state.value = QrScreenState.Loading
runCatching { YandexQuickPay.getPaymentSessionId() }
.onSuccess { _state.value = QrScreenState.QrReady(it) }
.onFailure { _state.value = QrScreenState.Error(it.message ?: "Неизвестная ошибка") }
}
}
fun enableQuickPayment() {
viewModelScope.launch { YandexQuickPay.enableQuickPayment().onFailure { /* ... */ } }
}
fun disableQuickPayment() {
viewModelScope.launch { YandexQuickPay.disableQuickPayment().onFailure { /* ... */ } }
}
}
Использование в Activity:
class QrActivity : AppCompatActivity() {
private val viewModel: QrViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_qr)
// Передаём листенер из ViewModel в SDK
YandexQuickPay.initialize(
config = QuickPayConfig(merchantId = "YOUR_MERCHANT_ID", environment = QuickPayEnvironment.SANDBOX),
context = application,
quickPaymentStateListener = viewModel.quickPayListener,
)
lifecycleScope.launch {
YandexQuickPay.initUi(this@QrActivity, supportFragmentManager)
viewModel.init()
}
lifecycleScope.launch {
viewModel.state.collect { state ->
when (state) {
is QrScreenState.Loading -> showLoading()
is QrScreenState.QrReady -> showQr(state.sessionId)
is QrScreenState.PaymentDisabled -> showEnableButton()
is QrScreenState.Error -> showError(state.message)
}
}
}
}
}
Включение и отключение
- Включение:
viewModel.enableQuickPayment()— может показать экран авторизации Яндекс ID или биометрический запрос. После успеха —onPaymentEnabledStateChanged(isEnabled = true). - Отключение:
viewModel.disableQuickPayment(). - Выход из аккаунта:
YandexQuickPay.logout()— очищает все данные, потребуется повторная авторизация.
Шаг 6. Работайте с платёжной сессией и QR-кодом
getPaymentSessionId() создаёт новую сессию или возвращает существующую, если срок её действия ещё не истёк. Срок действия определяется сервером.
viewModelScope.launch {
runCatching { YandexQuickPay.getPaymentSessionId() }
.onSuccess { sessionId ->
val bitmap = generateQrBitmap(sessionId)
qrImageView.setImageBitmap(bitmap)
}
.onFailure { error ->
Log.e("QuickPay", "Ошибка получения сессии: ${error.message}", error)
}
}
Примечание
При вызове getPaymentSessionId() может появиться системный запрос биометрической аутентификации, если с момента последней разблокировки прошло достаточно времени. Убедитесь, что initUi() был вызван заранее.
Когда обновлять QR:
| Событие | Действие |
|---|---|
| Первое отображение экрана (быстрая оплата включена) | Вызвать getPaymentSessionId() |
onSessionExpired() |
Вызвать getPaymentSessionId() |
onPaymentResult() (любой результат) |
Вызвать getPaymentSessionId() для следующей транзакции |
onPaymentEnabledStateChanged(isEnabled = true) |
Вызвать getPaymentSessionId() |
Обработка ошибок
getPaymentSessionId() может бросить следующие исключения:
| Исключение | Причина | Решение |
|---|---|---|
RuntimeException("YandexQuickPay must be initialized...") |
initialize() не был вызван |
Вызовите initialize() в Application.onCreate() |
IllegalStateException("Activity not available") |
initUi() не был вызван |
Вызовите initUi() перед первым запросом сессии |
IllegalStateException("User is not authorized...") |
Пользователь не авторизован | Вызовите enableQuickPayment() и дождитесь onPaymentEnabledStateChanged |
RuntimeException("Failed to generate session: ...") |
Ошибка сети или сервера | Повторите запрос, проверьте соединение |
Аналогичные исключения бросают isQuickPaymentEnabled(), enableQuickPayment() и disableQuickPayment().
Рекомендация: оборачивайте все suspend-вызовы SDK в runCatching { } и обрабатывайте ошибки в UI.
Безопасность
merchantId— публичный идентификатор магазина, не является секретом. Можно хранить в коде или конфигурации.sessionId— идентификатор платёжной сессии, привязан к пользователю и устройству. Не логируйтеsessionIdв production и не передавайте третьим сторонам.- SDK не хранит данные банковских карт на устройстве.
Тестирование
Sandbox-окружение: используйте QuickPayEnvironment.SANDBOX для разработки. В sandbox все платежи симулируются — реальных списаний не происходит.
Сценарии для проверки:
| Сценарий | Как воспроизвести | Ожидаемое поведение |
|---|---|---|
| Первая авторизация | Вызов enableQuickPayment() для нового пользователя |
Показ экрана авторизации Яндекс ID |
| Успешный платёж | Сканирование QR на тестовой кассе | onPaymentResult(QuickPayResult.Success) |
| Истечение сессии | Ожидание истечения expires_at |
onSessionExpired(), затем новый QR после getPaymentSessionId() |
| Отключение быстрой оплаты | Вызов disableQuickPayment() |
onPaymentEnabledStateChanged(isEnabled = false) |
| Выход из аккаунта | Вызов logout() |
Очистка данных, требуется повторная авторизация |