> ## Documentation Index
> Fetch the complete documentation index at: https://docs.busha.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Accept Stablecoin with Mobile SDK

> Add Busha crypto checkout to your iOS, Android, or Flutter app using the Busha Pay mobile SDKs.

The Busha Pay mobile SDKs let you embed a crypto checkout experience directly into your mobile app. Users can pay with their Busha wallet or externally without leaving your app. Four SDKs are available; React Native, iOS (Swift), Android (Kotlin), and Flutter.

<Tip>
  **What You'll Achieve:**

  1. Install the Busha Pay SDK for your platform
  2. Initialize the SDK with your public key
  3. Launch checkout and handle payment results
  4. Configure platform-specific deep link callbacks
</Tip>

## Prerequisites

Before you begin, ensure you have:

* A Busha Business Account and Public API Key (from the [Quick Start Tutorial](../getting-started/quick-start)).
* A React Native, iOS, Android, or Flutter mobile application.

<Danger>
  **Important Note:** The mobile SDKs use your Public API Key, not your Secret
  API Key. Your Public API Key is automatically generated for your account — you
  do not need to create a new token to access it. You can find it in **Settings
  → Developer Tools → API Tokens**.
</Danger>

## Installation

<Tabs>
  <Tab title="React Native">
    <Card title="@busha/pay-react-native" icon="github" href="https://github.com/bushaHQ/pay/tree/dev/react-native">
      View the full React Native SDK source, releases, and changelog on GitHub.
    </Card>

    **Requirements:** React Native 0.68+, Node.js 18+

    Install the SDK and its peer dependencies:

    ```bash theme={null}
    npm install @busha/pay-react-native react-native-webview expo-application
    # or
    yarn add @busha/pay-react-native react-native-webview expo-application
    ```

    **Installing from GitHub releases**

    Each release ships an npm-installable tarball on GitHub under tags shaped `react-native/v<version>`. You can install directly without going through the npm registry:

    ```bash theme={null}
    npm install https://github.com/bushaHQ/pay/releases/download/react-native/v0.0.1/busha_pay_react_native-0.0.1.tgz
    # or with yarn:
    yarn add https://github.com/bushaHQ/pay/releases/download/react-native/v0.0.1/busha_pay_react_native-0.0.1.tgz
    ```

    Then install the peer dependencies separately:

    ```bash theme={null}
    npm install react-native-webview expo-application
    ```

    **Bare React Native setup**

    If your app is bare React Native (not Expo), run this once to enable Expo modules:

    ```bash theme={null}
    npx install-expo-modules@latest
    ```

    Expo managed, Expo dev client, and Expo Go users can skip this step.
  </Tab>

  <Tab title="iOS (Swift)">
    <Card title="BushaPay iOS SDK" icon="github" href="https://github.com/bushaHQ/pay-ios">
      View the full iOS SDK source, releases, and Swift Package Manager setup on
      GitHub.
    </Card>

    **Requirements:** iOS 14+, Swift 5.9+, Xcode 15+

    **Installing via Swift Package Manager (recommended)**

    In Xcode, go to **File → Add Package Dependencies…** and enter:
    [https://github.com/bushaHQ/pay-ios](https://github.com/bushaHQ/pay-ios)

    Select your version, choose the `BushaPay` library, and add it to your app target.

    For `Package.swift`:

    ```swift theme={null}
    .package(url: "https://github.com/bushaHQ/pay-ios", from: "0.0.1"),
    ```

    Then add `BushaPay` as a dependency to your target:

    ```swift theme={null}
    .target(
        name: "YourApp",
        dependencies: ["BushaPay"]
    )
    ```

    <Note>
      The SDK is authored in the [bushaHQ/pay](https://github.com/bushaHQ/pay)
      monorepo under `ios/`. Each release is mirrored to the standalone
      [bushaHQ/pay-ios](https://github.com/bushaHQ/pay-ios) repository so Swift
      Package Manager can resolve it correctly. Issues, PRs, and source code live in
      the monorepo.
    </Note>
  </Tab>

  <Tab title="Android (Kotlin)">
    <Card title="BushaPay Android SDK" icon="github" href="https://github.com/bushaHQ/pay-android">
      View the full Android SDK source, releases, and changelog on GitHub.
    </Card>

    **Requirements:** Android 5.0 (API 21)+, Kotlin 1.9+, AndroidX

    **Installing via JitPack**

    Add the JitPack repository to your `settings.gradle.kts`:

    ```kotlin theme={null}
    dependencyResolutionManagement {
        repositories {
            google()
            mavenCentral()
            maven { url = uri("https://jitpack.io") }
        }
    }
    ```

    Then add the dependency to your module's `build.gradle.kts`:

    ```kotlin theme={null}
    dependencies {
        implementation("com.github.bushaHQ.pay-android:pay-android:0.0.1")
    }
    ```

    <Note>
      The SDK is authored in the [bushaHQ/pay](https://github.com/bushaHQ/pay)
      monorepo under `android/`. Each release is mirrored to the standalone
      [bushaHQ/pay-android](https://github.com/bushaHQ/pay-android) repository that
      JitPack builds from. Issues, PRs, and source code live in the monorepo. The
      SDK ships with a single runtime dependency — `androidx.webkit` — which it uses
      to inject the checkout bridge reliably across WebView versions.
    </Note>
  </Tab>

  <Tab title="Flutter">
    <Card title="busha_pay on pub.dev" icon="link" href="https://pub.dev/packages/busha_pay">
      View the full Flutter SDK package, documentation, and changelog on pub.dev.
    </Card>

    **Requirements:** Flutter 3.0+, Dart 3.0+

    **Installing from pub.dev (recommended)**

    Add `busha_pay` to your `pubspec.yaml`:

    ```yaml theme={null}
    dependencies:
      busha_pay: ^0.0.1
    ```

    Then run:

    ```bash theme={null}
    flutter pub get
    ```

    **Installing from GitHub**

    Each release ships as a zipped artifact on GitHub under tags shaped `flutter/v<version>`. To install directly from the monorepo without going through pub.dev:

    ```yaml theme={null}
    dependencies:
      busha_pay:
        git:
          url: https://github.com/bushaHQ/pay
          ref: flutter/v0.0.1
          path: flutter
    ```

    Replace `v0.0.1` with the version you want. Every git checkout re-resolves from the tagged commit, so your lockfile stays reproducible.
  </Tab>
</Tabs>

## Initialize the SDK

Initialize once at app startup before any checkout is triggered.

<Tabs>
  <Tab title="React Native">
    Wrap your root component with `BushaPayProvider`. The provider calls `BushaPay.init()` on mount and auto-detects your app's bundle ID.

    ```javascript theme={null}
    import { BushaPayProvider } from '@busha/pay-react-native'

    export default function App() {
      return (
        <BushaPayProvider publicKey="pub_xxx" environment="sandbox">
          <Home />
        </BushaPayProvider>
      )
    }
    ```

    Use `environment="live"` for production.
  </Tab>

  <Tab title="iOS (Swift)">
    Call `BushaPay.initialize` in your `AppDelegate` or `@main` app before any views load:

    ```swift theme={null}
    import BushaPay

    BushaPay.initialize(
        publicKey: "pub_xxx",
        environment: .sandbox  // or .live
    )
    ```
  </Tab>

  <Tab title="Android (Kotlin)">
    Call `BushaPay.initialize` once from your `Application` class:

    ```kotlin theme={null}
    import android.app.Application
    import co.busha.pay.BushaEnvironment
    import co.busha.pay.BushaPay

    class MyApp : Application() {
        override fun onCreate() {
            super.onCreate()
            BushaPay.initialize(
                context = this,
                publicKey = "pub_xxx",
                environment = BushaEnvironment.SANDBOX, // or BushaEnvironment.LIVE
            )
        }
    }
    ```
  </Tab>

  <Tab title="Flutter">
    Call `BushaPay.init` in `main()` before `runApp`:

    ```dart theme={null}
    import 'package:busha_pay/busha_pay.dart';

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();

      await BushaPay.init(
        publicKey: 'pub_xxx',
        environment: BushaEnvironment.sandbox, // or .live
      );

      runApp(const MyApp());
    }
    ```
  </Tab>
</Tabs>

## Launch Checkout

<Tabs>
  <Tab title="React Native">
    ```javascript theme={null}
    import { Button } from 'react-native'
    import { useBushaPay } from '@busha/pay-react-native'

    function PayButton() {
      const { checkout } = useBushaPay()

      const onPress = async () => {
        const result = await checkout({
          quoteAmount: '10000',
          quoteCurrency: 'NGN',
          targetCurrency: 'NGN',
          sourceCurrency: 'USDT',
          metaName: 'John Doe',
          metaEmail: 'john@example.com',
        })

        switch (result.type) {
          case 'success':
            console.log('Payment completed:', result.paymentId)
            break
          case 'cancelled':
            console.log('Cancelled:', result.reason)
            break
          case 'error':
            console.log('Error:', result.message)
            break
        }
      }

      return <Button title="Pay Now" onPress={onPress} />
    }
    ```
  </Tab>

  <Tab title="iOS (Swift)">
    ```swift theme={null}
    import BushaPay
    import UIKit

    BushaPay.checkout(
        config: BushaPayConfig(
            quoteAmount: "10000",
            quoteCurrency: "NGN",
            targetCurrency: "NGN",
            sourceCurrency: "USDT",
            metaName: "Jane Doe",
            metaEmail: "jane@example.com"
        ),
        from: viewController
    ) { result in
        switch result {
        case .success(let payment):
            print("Paid: \(payment.paymentId)")
        case .cancelled(let cancelled):
            print("Cancelled: \(cancelled.reason)")
        case .error(let err):
            print("Error: \(err.message)")
        }
    }
    ```

    Or with async/await:

    ```swift theme={null}
    let result = await BushaPay.checkout(config: config, from: viewController)
    ```
  </Tab>

  <Tab title="Android (Kotlin)">
    ```kotlin theme={null}
    import co.busha.pay.BushaPay
    import co.busha.pay.BushaPayConfig
    import co.busha.pay.BushaPayResult

    BushaPay.checkout(
        activity = this,
        config = BushaPayConfig(
            quoteAmount = "10000",
            quoteCurrency = "NGN",
            targetCurrency = "NGN",
            sourceCurrency = "USDT",
            metaName = "Jane Doe",
            metaEmail = "jane@example.com",
        ),
    ) { result ->
        when (result) {
            is BushaPayResult.Success -> println("Paid: ${result.paymentId}")
            is BushaPayResult.Cancelled -> println("Cancelled: ${result.reason}")
            is BushaPayResult.Error -> println("Error: ${result.message}")
        }
    }
    ```

    The completion lambda is called exactly once, on the main thread.
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    ElevatedButton(
      onPressed: () {
        BushaPay.checkout(
          context: context,
          config: BushaPayConfig(
            quoteAmount: '10000',
            quoteCurrency: 'NGN',
            targetCurrency: 'NGN',
            sourceCurrency: 'USDT',
            metaName: 'John Doe',
            metaEmail: 'john@example.com',
          ),
          onComplete: (result) {
            switch (result) {
              case BushaPaySuccess(:final paymentId):
                print('Payment $paymentId completed');
              case BushaPayCancelled(:final reason):
                print('Cancelled: $reason');
              case BushaPayError(:final message):
                print('Error: $message');
            }
          },
        );
      },
      child: Text('Pay Now'),
    )
    ```
  </Tab>
</Tabs>

## Platform Setup

<Tabs>
  <Tab title="React Native">
    ### iOS

    Add the following to `ios/YourApp/Info.plist`:

    ```xml theme={null}
    <!-- 1. Register your app's callback URL scheme -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            </array>
        </dict>
    </array>

    <!-- 2. Allow the SDK to launch the Busha app -->
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>co.busha.apple</string>
        <string>co.busha.boro.development</string> <!-- sandbox only -->
    </array>
    ```

    ### Android

    Add the callback intent filter to your main activity in `android/app/src/main/AndroidManifest.xml`:

    ```xml theme={null}
    <activity android:name=".MainActivity" ...>
        <intent-filter android:autoVerify="false">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="${applicationId}.busha-pay" />
        </intent-filter>
    </activity>

    <!-- Required on Android 11+ -->
    <queries>
        <package android:name="co.busha.android" />
        <package android:name="co.busha.android.development" /> <!-- sandbox only -->
    </queries>
    ```

    ### Expo (managed or prebuild)

    In `app.json`:

    ```json theme={null}
    {
      "expo": {
        "ios": {
          "bundleIdentifier": "com.example.myapp",
          "infoPlist": {
            "LSApplicationQueriesSchemes": [
              "co.busha.apple",
              "co.busha.boro.development"
            ]
          }
        },
        "android": {
          "package": "com.example.myapp"
        },
        "scheme": "com.example.myapp.busha-pay"
      }
    }
    ```

    For Android 11+ package visibility, install `expo-build-properties` and add this plugin:

    ```bash theme={null}
    npx expo install expo-build-properties
    ```

    ```json theme={null}
    {
      "expo": {
        "plugins": [
          [
            "expo-build-properties",
            {
              "android": {
                "manifestQueries": {
                  "package": ["co.busha.android", "co.busha.android.development"]
                }
              }
            }
          ]
        ]
      }
    }
    ```
  </Tab>

  <Tab title="iOS (Swift)">
    Add the following to your `Info.plist`:

    ```xml theme={null}
    <!-- 1. Register your app's callback URL scheme -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            </array>
        </dict>
    </array>

    <!-- 2. Allow the SDK to launch the Busha app -->
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>co.busha.apple</string>
        <string>co.busha.boro.development</string> <!-- sandbox only -->
    </array>
    ```
  </Tab>

  <Tab title="Android (Kotlin)">
    Add an `<intent-filter>` for the callback URL scheme to your launcher activity in `AndroidManifest.xml`. Also set `launchMode="singleTask"` so the callback reuses the existing activity instance:

    ```xml theme={null}
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTask">

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <!-- Register your callback URL scheme -->
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="${applicationId}.busha-pay" />
        </intent-filter>
    </activity>
    ```

    <Note>
      The required `<queries>` entries and `INTERNET` permission ship inside the
      SDK's own manifest and merge into your app automatically — no extra setup
      needed on your side.
    </Note>
  </Tab>

  <Tab title="Flutter">
    ### iOS

    Add the following to `ios/Runner/Info.plist`:

    ```xml theme={null}
    <!-- 1. Register your app's callback URL scheme -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER).busha-pay</string>
            </array>
        </dict>
    </array>

    <!-- 2. Allow the SDK to launch the Busha app -->
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>co.busha.apple</string>
        <string>co.busha.boro.development</string> <!-- sandbox only -->
    </array>
    ```

    ### Android

    Add the callback intent filter to your main activity in `android/app/src/main/AndroidManifest.xml`:

    ```xml theme={null}
    <activity android:name=".MainActivity" ...>
        <intent-filter android:autoVerify="false">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="${applicationId}.busha-pay" />
        </intent-filter>
    </activity>

    <!-- Required on Android 11+ -->
    <queries>
        <package android:name="co.busha.android" />
        <package android:name="co.busha.android.development" /> <!-- sandbox only -->
    </queries>
    ```
  </Tab>
</Tabs>

## Forwarding Deep Link Callbacks

The SDK does not subscribe to incoming URLs itself to avoid conflicts with your existing deep-link setup. Forward Busha callbacks with a single call — it returns `true` if the URL was a Busha callback.

<Tabs>
  <Tab title="React Native">
    Pick whichever matches the deep-link approach your app already uses.

    **Option A — React Native Linking:**

    ```javascript theme={null}
    import { useEffect } from 'react'
    import { Linking } from 'react-native'
    import { BushaPay } from '@busha/pay-react-native'

    useEffect(() => {
      const sub = Linking.addEventListener('url', ({ url }) => {
        BushaPay.handleDeepLink(url)
      })
      Linking.getInitialURL().then((url) => {
        if (url) BushaPay.handleDeepLink(url)
      })
      return () => sub.remove()
    }, [])
    ```

    **Option B — expo-linking:**

    ```javascript theme={null}
    import { useEffect } from 'react'
    import * as Linking from 'expo-linking'
    import { BushaPay } from '@busha/pay-react-native'

    useEffect(() => {
      const sub = Linking.addEventListener('url', ({ url }) => {
        BushaPay.handleDeepLink(url)
      })
      Linking.getInitialURL().then((url) => {
        if (url) BushaPay.handleDeepLink(url)
      })
      return () => sub.remove()
    }, [])
    ```

    **Option C — React Navigation:**

    ```javascript theme={null}
    import { NavigationContainer } from '@react-navigation/native'
    import { Linking } from 'react-native'
    import { BushaPay } from '@busha/pay-react-native'

    const linking = {
      prefixes: ['com.example.myapp.busha-pay://', 'https://yourapp.com'],
      config: { /* your screens */ },
      subscribe(listener) {
        const sub = Linking.addEventListener('url', ({ url }) => {
          if (BushaPay.handleDeepLink(url)) return
          listener(url)
        })
        return () => sub.remove()
      },
    }

    <NavigationContainer linking={linking}>
      {/* ... */}
    </NavigationContainer>
    ```
  </Tab>

  <Tab title="iOS (Swift)">
    **UIKit (AppDelegate):**

    ```swift theme={null}
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        if BushaPay.handleDeepLink(url) { return true }
        return false
    }
    ```

    **UISceneDelegate (scene-based apps):**

    ```swift theme={null}
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        for context in URLContexts {
            if BushaPay.handleDeepLink(context.url) { continue }
            // your own URL handling
        }
    }
    ```

    **SwiftUI:**

    ```swift theme={null}
    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .onOpenURL { url in
                        if BushaPay.handleDeepLink(url) { return }
                        // your own URL handling
                    }
            }
        }
    }
    ```
  </Tab>

  <Tab title="Android (Kotlin)">
    Forward the callback from the activity that owns the intent filter. You need to handle both `onCreate` (cold start) and `onNewIntent` (warm start via `singleTask`):

    ```kotlin theme={null}
    import android.content.Intent
    import android.os.Bundle
    import co.busha.pay.BushaPay

    class MainActivity : AppCompatActivity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            forwardBushaCallback(intent)
        }

        override fun onNewIntent(intent: Intent) {
            super.onNewIntent(intent)
            setIntent(intent)
            forwardBushaCallback(intent)
        }

        private fun forwardBushaCallback(intent: Intent?) {
            val data = intent?.data?.toString() ?: return
            BushaPay.handleDeepLink(data)
        }
    }
    ```
  </Tab>

  <Tab title="Flutter">
    Flutter has a built-in deep-link handler that, when enabled, routes incoming URLs through your app's Router/Navigator. Whether you enable it depends on your wiring approach:

    | Approach                                    | `FlutterDeepLinkingEnabled` (iOS) / `flutter_deeplinking_enabled` (Android) |
    | ------------------------------------------- | --------------------------------------------------------------------------- |
    | Option A or B (Flutter Router / go\_router) | `true` — let Flutter deliver URLs to your router                            |
    | Option C or D (app\_links / uni\_links)     | `false` — otherwise Flutter and the plugin both handle the same URL         |

    Set the flag in your platform files:

    ```xml theme={null}
    <!-- iOS: ios/Runner/Info.plist -->
    <key>FlutterDeepLinkingEnabled</key>
    <true/>
    ```

    ```xml theme={null}
    <!-- Android: inside your <activity> in AndroidManifest.xml -->
    <meta-data
      android:name="flutter_deeplinking_enabled"
      android:value="true" />
    ```

    **Option A — Flutter Router API:**

    ```dart theme={null}
    class AppRouteInformationParser extends RouteInformationParser<AppRoute> {
      @override
      Future<AppRoute> parseRouteInformation(RouteInformation info) async {
        final uri = info.uri;
        if (BushaPay.handleDeepLink(uri)) {
          return AppRoute.current();
        }
        return AppRoute.fromUri(uri);
      }
    }
    ```

    **Option B — go\_router:**

    ```dart theme={null}
    final router = GoRouter(
      routes: [...],
      redirect: (context, state) {
        if (BushaPay.handleDeepLink(state.uri)) return null;
        return null;
      },
    );
    ```

    **Option C — app\_links:**

    ```dart theme={null}
    import 'package:app_links/app_links.dart';

    final _appLinks = AppLinks();

    @override
    void initState() {
      super.initState();
      _appLinks.uriLinkStream.listen(BushaPay.handleDeepLink);
    }
    ```

    **Option D — uni\_links:**

    ```dart theme={null}
    import 'package:uni_links/uni_links.dart';

    uriLinkStream.listen((uri) {
      if (uri != null) BushaPay.handleDeepLink(uri);
    });
    ```
  </Tab>
</Tabs>

<Note>
  You can verify your deep link setup without making a real payment.

  **iOS simulator:**

  ```bash theme={null}
  xcrun simctl openurl booted "com.example.myapp.busha-pay://callback?status=completed&paymentRequestId=PAYR_test"
  ```

  **Android emulator:**

  ```bash theme={null}
  adb shell am start -a android.intent.action.VIEW \
    -d "com.example.myapp.busha-pay://callback?status=completed&paymentRequestId=PAYR_test"
  ```

  Replace `com.example.myapp` with your actual bundle ID or application ID.
</Note>

***

## How It Works

When `checkout()` is called, the SDK opens a chooser with two options:

1. **Pay with Busha app** — if the Busha app is installed on the device, the SDK deep-links directly into it. The user completes the payment inside the Busha app and is returned to your app via the callback URL scheme. If the Busha app is not installed, the SDK falls back to the web checkout automatically.

2. **Pay with Stablecoins** — opens the Busha web checkout in an in-app WebView (WKWebView on iOS, WebView on Android). The user completes the payment inside your app without switching to another application.

Either way, the result is delivered to the promise or callback returned by `checkout()`.

When payment completes via the **web checkout**, the result includes full payment data — amounts, currencies, exchange rate, and timeline. When payment completes via the **Busha app**, only `paymentId` and `status` are available. Use `result.hasFullData` to check before accessing the additional fields.

## Payment Results

All four SDKs return the same three result types:

| Result      | Description                                                               |
| ----------- | ------------------------------------------------------------------------- |
| `success`   | Payment completed. Contains `paymentId` and `status`.                     |
| `cancelled` | Checkout ended without a completed payment. Inspect `reason` — see below. |
| `error`     | Something went wrong. Contains `message` and optional `code`.             |

### Cancellation Reasons

A `cancelled` result carries a `reason` so you can tell exactly how the checkout ended:

| Reason      | Meaning                                                                                                                                                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dismissed` | The user closed the in-app chooser or web checkout sheet.                                                                                                                                                                     |
| `rejected`  | The Busha app reported the user explicitly rejected the payment. `paymentId` is populated so you can reconcile the request server-side.                                                                                       |
| `abandoned` | The user returned from the Busha app without a callback. The outcome is unverified — the payment may still have succeeded. Always reconcile server-side via webhooks or the status API before showing the user a final state. |

<Note>
  When payment completes via the Busha app, only `paymentId` and `status` are
  available. Full payment data (amounts, currencies, rate, timeline) is only
  returned when payment completes via the web checkout. Use `result.hasFullData`
  to check. Always verify payments server-side via webhooks — the client result
  is a UX hint, not the source of truth.
</Note>

## Checkout Configuration

| Parameter               | Type   | Required | Description                                           |
| ----------------------- | ------ | -------- | ----------------------------------------------------- |
| `quoteAmount`           | string | Yes      | Amount to charge (e.g. `'10000'`)                     |
| `quoteCurrency`         | string | Yes      | Currency for the amount (e.g. `'NGN'`)                |
| `targetCurrency`        | string | Yes      | Settlement currency                                   |
| `sourceCurrency`        | string | Yes      | Crypto asset for payment (e.g. `'USDT'`)              |
| `reference`             | string | No       | Custom transaction reference                          |
| `metaName`              | string | No       | Customer name                                         |
| `metaEmail`             | string | No       | Customer email                                        |
| `metaPhone`             | string | No       | Customer phone                                        |
| `allowedPaymentMethods` | array  | No       | Restricts which payment methods are shown. See below. |

### Restricting Payment Methods

By default, checkout shows a chooser with two options: **Pay with Busha app** and **Pay with Stablecoins**. Pass `allowedPaymentMethods` to skip the chooser or restrict options:

| Value                               | Behaviour                                                                      |
| ----------------------------------- | ------------------------------------------------------------------------------ |
| `undefined`, `null`, or `[]`        | Show the full chooser                                                          |
| `['bushaApp']` / `[BUSHA_APP]`      | Skip chooser; deep-link into the Busha app, with web fallback if not installed |
| `['stablecoins']` / `[STABLECOINS]` | Skip chooser; open web checkout directly                                       |
| Multiple methods                    | Show the chooser with only those tiles                                         |

## Troubleshooting

* **`CHECKOUT_IN_PROGRESS`** — A previous `checkout()` call hasn't resolved yet. Wait for it to complete before calling again.
* **`WEBVIEW_LOAD_ERROR`** — Network failure or DNS error. Check the device's internet connection.
* **`WEBVIEW_HTTP_ERROR`** — The checkout endpoint returned a non-2xx HTTP response. Check your public key and environment configuration.
* **`WEBVIEW_TIMEOUT`** — Checkout page didn't load within 30 seconds. Retry on a stable connection.
* **`HTML_LOAD_ERROR`** (iOS, Android, and Flutter only) — The bundled checkout HTML asset failed to load. Try reinstalling the SDK.
* **`WEBVIEW_PROCESS_TERMINATED`** (Android only) — The WebView renderer process was killed by the system. Retry the checkout.
* **`BUSHA_APP_LAUNCH_FAILED`** — The SDK could not deep-link into the Busha app. On iOS verify `LSApplicationQueriesSchemes` is configured correctly in your `Info.plist`. On Android verify the `<queries>` block is present in your manifest.
* **`401 Unauthorized`** — Double-check that your public API key is correct and matches your environment (sandbox vs live).
* **Callback not received after Busha app payment** — Your deep link setup is incomplete. Test it manually using the `xcrun simctl` or `adb` commands in the note above.

## What's Next?

* [How to Set Up Webhooks](/guides/webhooks/setup)
* [Accept Stablecoin with SDK Integration](/guides/accepting-stablecoin-payments/payment-widget)
* [API Reference: Payment Request Object](/api-reference/paymentrequests/create-a-payment-request)
