Tapaya
Прийом платежів у торговій точціПосібник з інтеграціїІнтеграція мобільного SDK

Інтеграція мобільного SDK

Android

Інтегруйте Tapaya Accept SDK у свій мобільний застосунок.

Tapaya Accept SDK вбудовує обробку платежів карткою безпосередньо у ваш застосунок. Він доступний для Android (Kotlin) і для React Native / Expo, причому обидва варіанти орієнтовані на пристрої з Android. Пакет для React Native є тонкою обгорткою над Android SDK, тож інтерфейси, типи та випадки помилок, описані нижче, однакові в обох варіантах.

Кожен приклад коду на цій сторінці має вкладки за мовами. Виберіть одну, і вся сторінка перемкнеться на неї.

Встановлення

build.gradle.kts
dependencies {
    implementation("com.tapaya:accept:1.15.0")
}

Android: додайте залежність до файлу build.gradle.kts на рівні модуля, а потім синхронізуйте проєкт.

React Native: встановіть пакет командою npx expo install @tapayadot/accept-react-native, додайте конфігураційний плагін, як показано, потім повторно згенеруйте нативний проєкт командою npx expo prebuild --clean і запустіть його командою npx expo run:android.

Ініціалізація

Accept є точкою входу до всіх функцій SDK. Ініціалізуйте його один раз під час запуску застосунку (на Android зазвичай у класі Application), а потім окремо автентифікуйтеся за допомогою токена мерчанта.

Крок 1: ініціалізація

// Sandbox is the default (isProduction = false)
Accept.initialize(context = this)

// Target production explicitly
Accept.initialize(context = this, isProduction = true)

// Or drive it from your build type
Accept.initialize(context = this, isProduction = !BuildConfig.DEBUG)

У React Native context не передається; його зберігає за вас нативний модуль.

Крок 2: автентифікація

Після ініціалізації викличте Accept.auth.authenticate(), щоб виконати вхід мерчанта за допомогою токена, отриманого з вашого бекенду. Виклик асинхронний і в разі невдачі завершується помилкою SDK. У разі успіху стан SDK змінюється на authenticated.

// suspend; call from a coroutine
try {
    val sdkToken = myBackendApi.getSdkToken()
    Accept.auth.authenticate(sdkToken)
    // Accept.state now emits SdkState.Authenticated
} catch (e: AcceptException) {
    // handle authentication failure
}

Відстежуйте життєвий цикл, щоб реагувати на ці переходи: idle → initialized → authenticated.

// Accept.state is a StateFlow<SdkState>
Accept.state.collect { state ->
    if (state == SdkState.Authenticated) loadMerchant()
}

Основні поняття

Об'єкт Accept надає доступ до SDK через невеликий набір інтерфейсів:

ІнтерфейсПризначення
Accept.authАвтентифікація сесії мерчанта.
Accept.merchantПрофіль мерчанта, конфігурація, статус онбордингу.
Accept.paymentsСтворення платежів карткою та керування ними.
Accept.pluginВстановлення та активація термінала (застосунку-плагіна).
Accept.displaysІнформація про дисплеї пристрою для двобічних терміналів («double-sided»).
Accept.sdkВерсія SDK, середовище, ID пристрою, мінімальні суми.
Accept.stateСтан життєвого циклу: StateFlow<SdkState> у Kotlin, getter і addStateListener() у React Native.

Прийом платежу

Викличте Accept.payments.pay() із сумою (у найменшій одиниці валюти) і кодом валюти за ISO 4217, а потім відстежуйте потік подій PaymentEvent, які повідомляє метод: спочатку перебіг, потім остаточний результат. pay() ніколи не викидає винятків; невдача під час створення платежу натомість надходить як подія CreationFailed.

Відрізняється лише спосіб, у який ви відстежуєте цей потік: Kotlin повертає Flow<PaymentEvent>, який ви збираєте в корутині, тоді як React Native приймає listener і повертає підписку, яку можна скасувати методом remove().

amount і currency обов'язкові. Необов'язкові параметри (paymentToken, receiptConfig, nfcPosition, metadata, tip, transactionTimeout, display) описано в розділі Створення платежу.

У React Native

Ті самі параметри передаються одним об'єктом PaymentRequest, pay({ amount, currency, tip, … }), з number замість Long і Record<string, string> замість Map<String, String>. Один параметр має іншу назву: тайм-аут називається transactionTimeoutMs і приймає звичайні мілісекунди (0…120000), а не Duration.

Процес оплати

Accept.payments.pay(amount = 15000, currency = "CZK") // 150.00 Kč
    .collect { event ->
        when (event) {
            PaymentEvent.Creating -> Log.d("pay", "creating")
            is PaymentEvent.Created -> Log.d("pay", "created ${event.paymentToken}")
            PaymentEvent.Launched -> Log.d("pay", "launched")
            is PaymentEvent.Result -> Log.d("pay", event.payResult.toString())
            is PaymentEvent.CreationFailed -> Log.e("pay", event.cause.toString())
        }
    }

Остаточна подія з результатом містить PayResult: успіх (з paymentToken, metadata, authCode, receiptDetails), відмову (metadata, receiptDetails), скасування (metadata) або невдачу (reason, metadata). Kotlin моделює їх як підкласи PayResult.Success / Declined / Canceled / Failed; React Native як дискриміноване об'єднання, у якому ви розгалужуєте логіку за payResult.type === 'success'. Повний розбір наведено в розділі Результат платежу. Пізніше можна запитати статус платежу за допомогою Accept.payments.status(paymentToken), зупинити його за допомогою Accept.payments.cancel(paymentToken) або повернути кошти за допомогою Accept.payments.refund(paymentToken, reason).

Лише результат (React Native)

Якщо ваш UI не відображає проміжні стани, payAsync() зводить увесь потік до остаточного PayResult і відхиляє проміс із помилкою AcceptError у всіх випадках, про які pay() повідомив би як creationFailed. У Kotlin відповідника немає; там збирайте Flow або беріть лише його останню подію.

const result = await Accept.payments.payAsync({ amount: 15000, currency: 'CZK' });
if (result.type === 'success') {
    console.log(result.paymentToken, result.receiptUrl);
}

Інформація про мерчанта та SDK

Accept.merchant надає профіль і платіжну конфігурацію мерчанта, який увійшов у систему. Accept.sdk надає відомості про збірку SDK і цей пристрій. Кожен член, наведений нижче, потребує попереднього виклику Accept.initialize() (крім Accept.sdk.version), а все в Accept.merchant додатково потребує завершеного Accept.auth.authenticate(), інакше виклик викидає NotInitialized або NotAuthenticated.

Мерчант

val info = Accept.merchant.info()                      // MerchantInformation?
val config = Accept.merchant.config()                  // MerchantConfig
val currencies = Accept.merchant.availableCurrencies() // List<String>
val status = Accept.merchant.onboardingStatus()        // OnboardingStatus
ЧленПовертаєОпис
info()MerchantInformation?Зареєстрований бізнес-профіль мерчанта. null, якщо профіль ще недоступний.
config()MerchantConfigПлатіжна конфігурація мерчанта.
availableCurrencies()List<String>Коди ISO 4217, у яких мерчант може приймати платежі. Скорочення для config().availableCurrencies.
onboardingStatus()OnboardingStatusЧи може мерчант уже приймати платежі.

Усі чотири є suspend і в разі невдачі викидають NotAuthenticated, NoInternetConnection або Unknown.

MerchantInformation містить зареєстрований бізнес-профіль. Завжди присутні: merchantId (UUID), email, businessType (BusinessType), legalName, registrationNumber, mcc і зареєстрована адреса (addressLine1, city, country). Необов'язкові (null, якщо мерчант їх не вказав): vatNumber, incorporationDate, addressLine2, postalCode, state, phone, averageItemValue, expectedMonthlyTurnover, businessModelDescription, numberOfEmployees, annualTurnover, balanceSheetTotal, statementDescriptor, termsVersion.

BusinessType набуває одного зі значень INDIVIDUAL, COMPANY, NONPROFIT_ORGANIZATION, GOVERNMENT_ENTITY або PARTNERSHIP.

MerchantConfig містить availableCurrencies: List<String> і валюту мерчанта за замовчуванням currency: String?.

OnboardingStatus є sealed interface: або ReadyForPayments, або RequiresAction(state), де state є OnboardingState: NOT_STARTED, PENDING, ACTIVE, ATTENTION_NEEDED, ACTION_REQUIRED, BLOCKED, WAITING_FOR_KYB, WAITING_FOR_STAKEHOLDERS або PENDING_ACQUIRER_APPROVAL. Що означає кожен стан, описано в розділі Онбординг мерчантів.

SDK

Accept.sdk.version            // String, e.g. "1.15.0"
Accept.sdk.isProduction       // Boolean
Accept.sdk.deviceId()         // String?  (suspend)
Accept.sdk.minimumAmounts()   // List<MinimumAmount>  (suspend)
ЧленПовертаєОпис
versionStringВерсія SDK. Її можна прочитати ще до initialize().
isProductionBooleanЧи initialize() було спрямовано на робоче середовище (true) або на пісочницю (false).
deviceId()String?Стабільний ID цього пристрою, який призначається під час першого успішного authenticate() і зберігається до видалення застосунку. До того null. Вказуйте його в запитах до підтримки.
minimumAmounts()List<MinimumAmount>Мінімальна сума платежу для кожної підтримуваної валюти. Кожен елемент має вигляд currency: String, amount: Long у найменшій одиниці валюти.

Застосунок-плагін

Кожен платіж карткою передається в Tapaya Terminal, застосунок-плагін Tapaya; він потрібен завжди, і немає жодного способу платежу карткою, який би його оминав. Перевірте, чи його встановлено, а потім активуйте термінал або відкрийте сторінку застосунку в магазині, щоб мерчант міг його встановити.

Для цього використовуйте Accept.plugin.isInstalled(), Accept.plugin.activateTerminal() і Accept.plugin.install(). Код і всі можливі результати активації наведено в розділі Встановлення та активація термінала.

Поточний стан плагіна можна отримати за допомогою Accept.plugin.status(), а вийти з облікового запису мерчанта в плагіні можна за допомогою Accept.plugin.logout().

Дозволи

Для платежів карткою хост-застосунок повинен мати дозвіл на доступ до геолокації (ACCESS_FINE_LOCATION). SDK не запитує його за вас; надайте його за допомогою стандартних API дозволів Android перед викликом pay().

class PaymentActivity : AppCompatActivity() {
    private val requestLocation = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        if (granted) launchPaymentFlow() else showPermissionRationale()
    }

    fun startPayment() {
        requestLocation.launch(Manifest.permission.ACCESS_FINE_LOCATION)
    }
}

Якщо на момент виконання pay() геолокація відсутня або її неможливо визначити, потік видає PaymentEvent.CreationFailed із причиною LocationPermissionRequired, LocationUnavailable або LocationTimeout.

Обробка помилок

Кожна контрольована помилка має одну з наведених нижче назв. У Kotlin кожна з них є підкласом sealed-класу AcceptException; у React Native усі вони належать до одного класу AcceptError, поле code якого містить назву, а поля з додатковими даними (minimum, currency, state, …) розташовані безпосередньо в тому самому об'єкті.

Асинхронні виклики (authenticate, merchant.*, payments.status/cancel/refund, plugin.activateTerminal/status/logout/updateInfo, clear) завершуються з однією з них. Винятком є payments.pay(): він ніколи не викидає винятків і повідомляє про ті самі помилки як про подію CreationFailed із причиною.

Типи винятків

ВинятокОпис
Життєвий цикл
NotInitializedВиклик потребує попереднього Accept.initialize().
NotAuthenticatedВиклик потребує попереднього Accept.auth.authenticate().
SessionExpiredТермін дії автентифікованої сесії минув; автентифікуйтеся повторно.
UnknownEnvironment(key)Ключ середовища не розпізнано (внутрішнє перевантаження методу initialize).
Платежі
PaymentInProgressПопередній pay() досі очікує на результат.
CurrencyNotAvailableForMerchant(code)Валюту не налаштовано для мерчанта.
UnsupportedCurrency(code)SDK не розпізнає валюту.
AmountBelowMinimum(minimum, currency)Сума менша за налаштований мінімум.
TransactionTimeoutOutOfRange(transactionTimeout)transactionTimeout поза діапазоном від 0 до 120 секунд.
PaymentCreationFailed(cause)Будь-яка інша помилка під час створення платежу.
PaymentNotFoundБекенду невідомий токен для status()/cancel()/refund().
Геолокація
LocationPermissionRequiredХост-застосунок не має дозволу на доступ до геолокації.
LocationUnavailableЖоден провайдер не зміг визначити місцезнаходження.
LocationTimeoutМісцезнаходження не вдалося визначити вчасно.
Плагін / термінал
PluginUnavailableЗастосунок-плагін не встановлено або не вдалося під'єднатися до його служби.
PluginTimeoutПлагін не відповів вчасно.
ActivationInProgressПопередній activateTerminal() досі очікує на результат.
NoTidsAvailableForMerchantУ мерчанта не залишилося непризначених терміналів (TID) для активації.
PluginAlreadyAuthenticatedПлагін уже автентифіковано для цього мерчанта.
PluginMerchantMismatchПлагін автентифіковано під іншим мерчантом; спочатку викличте plugin.logout().
PluginNotReadyForPayments(status)pay() виконано, коли плагін не перебував у стані PluginStatus.ReadyForPayments; status містить поточний стан.
MerchantOnboardingIncomplete(state)Мерчант не завершив онбординг.
Система
NoInternetConnectionПристрій не має підключення до мережі.
Unknown(cause)Будь-яка помилка, яку SDK не розпізнає як власну.

Результат чи виняток

Платіж, який дійшов до термінала, але не був успішним, не є винятком; він надходить як PaymentEvent.Result із PayResult.Declined, Canceled або Failed(reason). Так само активація термінала повідомляє ActivateTerminalResult.Failed(reason).

Обробка винятків

Обгорніть виклик у try/catch, розгалужте логіку за конкретними випадками, які ви обробляєте, а для решти використайте загальне перехоплення. Kotlin розгалужує логіку за підкласом винятку; React Native перевіряє за допомогою AcceptError.is() і перемикається за code.

try {
    val status = Accept.payments.status(paymentToken)
} catch (e: SessionExpired) {
    // Re-authenticate
} catch (e: NoInternetConnection) {
    // Ask the user to check connectivity
} catch (e: AcceptException) {
    // Handle any other SDK error
    Log.e("Payment", "Error: ${e.message}")
}

Журналювання

Під час розробки ввімкніть налагоджувальне журналювання, щоб діагностувати проблеми.

Accept.setDebugLoggingEnabled(true)

Вихід

Очистьте весь локально збережений стан SDK (токен автентифікації, ID пристрою, кешовану конфігурацію) і поверніть стан SDK до idle. Викликайте це, коли користувач виходить з облікового запису. Щоб також вийти з облікового запису мерчанта в застосунку Tapaya Terminal, спочатку викличте Accept.plugin.logout().

Accept.plugin.logout() // suspend; signs out of the Tapaya Terminal app
Accept.clear()         // suspend; clears local SDK state
Чогось бракує на цій сторінці?Надішліть запит на функцію. Ми додамо цю сторінку, щоб команда бачила контекст.