# Інтеграція мобільного SDK (/uk/in-store/integration/sdk_integration)





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

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

## Встановлення [#installation]

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin title="build.gradle.kts"
    dependencies {
        implementation("com.tapaya:accept:1.15.0")
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```json title="app.json"
    {
      "expo": {
        "plugins": ["@tapayadot/accept-react-native"]
      }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

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

## Ініціалізація [#initialization]

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

### Крок 1: ініціалізація [#step-1-initialize]

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    // 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)
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    import Accept from '@tapayadot/accept-react-native';

    // Sandbox is the default
    Accept.initialize();

    // Target production explicitly
    Accept.initialize({ isProduction: true });

    // Or drive it from your build
    Accept.initialize({ isProduction: !__DEV__ });
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

### Крок 2: автентифікація [#step-2-authenticate]

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    // 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
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    try {
        const sdkToken = await myBackendApi.getSdkToken();
        await Accept.auth.authenticate(sdkToken);
        // Accept.state is now 'authenticated'
    } catch (error) {
        // handle authentication failure
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

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

  <CodeBlockTab value="typescript">
    ```typescript
    // Accept.state is a plain getter; subscribe for changes
    const subscription = Accept.addStateListener((state) => {
        if (state === 'authenticated') loadMerchant();
    });

    // later
    subscription.remove();
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Основні поняття [#core-concepts]

Об'єкт `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. |

### Прийом платежу [#taking-a-payment]

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

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

`amount` і `currency` обов'язкові. Необов'язкові параметри (`paymentToken`, `receiptConfig`, `nfcPosition`,
`metadata`, `tip`, `transactionTimeout`, `display`) описано в розділі [Створення платежу](./card_terminal#create-a-payment).

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

<div className="bg-background relative h-auto w-full overflow-hidden rounded-lg border px-10">
    <img alt="Процес оплати" src="__img0" />
</div>

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    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())
            }
        }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    const subscription = Accept.payments.pay(
        { amount: 15000, currency: 'CZK' }, // 150.00 Kč
        (event) => {
            switch (event.type) {
                case 'creating':
                    setStatus('Creating payment…');
                    break;
                case 'created':
                    // The payment token and receipt URL exist before the plugin is launched.
                    setReceiptUrl(event.receiptUrl);
                    break;
                case 'launched':
                    setStatus('Waiting for the card…');
                    break;
                case 'result':
                    setResult(event.payResult);
                    break;
                case 'creationFailed':
                    setError(event.error); // the payment never started
                    break;
            }
        },
    );

    // The stream ends itself after `result` or `creationFailed`.
    // Call subscription.remove() only to stop listening early.
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

#### Лише результат (React Native) [#just-the-outcome-react-native]

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

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

## Інформація про мерчанта та SDK [#merchant--sdk-info]

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

### Мерчант [#merchant]

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    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
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    const info = await Accept.merchant.info();                      // MerchantInformation | null
    const config = await Accept.merchant.config();                  // MerchantConfig
    const currencies = await Accept.merchant.availableCurrencies(); // string[]
    const status = await Accept.merchant.onboardingStatus();        // OnboardingStatus
    ```
  </CodeBlockTab>
</CodeBlockTabs>

| Член                    | Повертає               | Опис                                                                                                |
| :---------------------- | :--------------------- | :-------------------------------------------------------------------------------------------------- |
| `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`. Що означає кожен стан,
описано в розділі [Онбординг мерчантів](/merchants/onboarding).

### SDK [#sdk]

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    Accept.sdk.version            // String, e.g. "1.15.0"
    Accept.sdk.isProduction       // Boolean
    Accept.sdk.deviceId()         // String?  (suspend)
    Accept.sdk.minimumAmounts()   // List<MinimumAmount>  (suspend)
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    Accept.sdk.version                  // string, e.g. "1.15.0"
    Accept.sdk.isProduction             // boolean
    await Accept.sdk.deviceId()         // string | null
    await Accept.sdk.minimumAmounts()   // MinimumAmount[]
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

## Застосунок-плагін [#plugin-app]

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

Для цього використовуйте `Accept.plugin.isInstalled()`, `Accept.plugin.activateTerminal()` і `Accept.plugin.install()`. Код
і всі можливі результати активації наведено в розділі [Встановлення та активація термінала](./card_terminal#install-and-activate-the-terminal).

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

## Дозволи [#permissions]

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    class PaymentActivity : AppCompatActivity() {
        private val requestLocation = registerForActivityResult(
            ActivityResultContracts.RequestPermission()
        ) { granted ->
            if (granted) launchPaymentFlow() else showPermissionRationale()
        }

        fun startPayment() {
            requestLocation.launch(Manifest.permission.ACCESS_FINE_LOCATION)
        }
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    import { PermissionsAndroid } from 'react-native';

    const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
    );

    if (granted === PermissionsAndroid.RESULTS.GRANTED) launchPaymentFlow();
    else showPermissionRationale();
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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

## Обробка помилок [#error-handling]

Кожна контрольована помилка має одну з наведених нижче назв. У 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` із причиною.

### Типи винятків [#exception-types]

| Виняток                                            | Опис                                                                                                                |
| :------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ |
| **Життєвий цикл**                                  |                                                                                                                     |
| `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 не розпізнає як власну.                                                                   |

<Callout title="Результат чи виняток" type="info">
  Платіж, який дійшов до термінала, але не був успішним, **не є** винятком; він надходить як
  `PaymentEvent.Result` із `PayResult.Declined`, `Canceled` або `Failed(reason)`. Так само активація
  термінала повідомляє `ActivateTerminalResult.Failed(reason)`.
</Callout>

### Обробка винятків [#handling-exceptions]

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    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}")
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    import Accept, { AcceptError } from '@tapayadot/accept-react-native';

    try {
        const status = await Accept.payments.status(paymentToken);
    } catch (error) {
        if (!AcceptError.is(error)) throw error;

        switch (error.code) {
            case 'SessionExpired':
                // Re-authenticate
                break;
            case 'NoInternetConnection':
                // Ask the user to check connectivity
                break;
            case 'AmountBelowMinimum':
                // Payload fields sit on the error itself
                alert(`Minimum is ${error.minimum} ${error.currency}`);
                break;
            default:
                throw error;
        }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Журналювання [#logging]

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    Accept.setDebugLoggingEnabled(true)
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    Accept.setDebugLoggingEnabled(true);
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Вихід [#logging-out]

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

<CodeBlockTabs defaultValue="kotlin" groupId="accept-language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="kotlin">
      Android (Kotlin)
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="typescript">
      React Native (TypeScript)
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="kotlin">
    ```kotlin
    Accept.plugin.logout() // suspend; signs out of the Tapaya Terminal app
    Accept.clear()         // suspend; clears local SDK state
    ```
  </CodeBlockTab>

  <CodeBlockTab value="typescript">
    ```typescript
    await Accept.plugin.logout(); // signs out of the Tapaya Terminal app
    await Accept.clear();         // clears local SDK state
    ```
  </CodeBlockTab>
</CodeBlockTabs>
