Skip to main content

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.

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. Three SDKs are available; React Native, iOS (Swift), and Flutter.
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

Prerequisites

Before you begin, ensure you have:
  • A Busha Business Account and Public API Key (from the Quick Start Tutorial).
  • A React Native, iOS, or Flutter mobile application.
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.

Installation

@busha/pay-react-native

View the full React Native SDK source, releases, and changelog on GitHub.
Requirements: React Native 0.68+, Node.js 18+Install the SDK and its peer dependencies:
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 releasesEach 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:
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:
npm install react-native-webview expo-application
Bare React Native setupIf your app is bare React Native (not Expo), run this once to enable Expo modules:
npx install-expo-modules@latest
Expo managed, Expo dev client, and Expo Go users can skip this step.

Initialize the SDK

Initialize once at app startup before any checkout is triggered.
Wrap your root component with BushaPayProvider. The provider calls BushaPay.init() on mount and auto-detects your app’s bundle ID.
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.

Launch Checkout

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} />
}

Platform Setup

Both iOS and Android require two things: registering your app’s callback URL scheme so the Busha app can return the payment result to you, and declaring the Busha app’s URL scheme as launchable so the SDK can deep-link into it when installed.

iOS

Add the following to your Info.plist (React Native: ios/YourApp/Info.plist, Flutter: ios/Runner/Info.plist):
<!-- 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 and the package visibility query in android/app/src/main/AndroidManifest.xml:
<activity android:name=".MainActivity" ...>

    <!-- 1. Register your callback URL scheme -->
    <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>

<!-- 2. Allow the SDK to launch the Busha app (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:
{
  "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:
npx expo install expo-build-properties
{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "manifestQueries": {
              "package": ["co.busha.android", "co.busha.android.development"]
            }
          }
        }
      ]
    ]
  }
}
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 — BushaPay.handleDeepLink(url) returns true if the URL was a Busha callback.
Pick whichever matches the deep-link approach your app already uses.Option A — React Native Linking:
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:
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:
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>
You can verify your deep link setup without making a real payment. Run the following in your terminal and confirm your app comes to the foreground and checkout() resolves with a success result.iOS simulator:
xcrun simctl openurl booted "com.example.myapp.busha-pay://callback?status=completed&paymentRequestId=PAYR_test"
Android emulator:
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 package name.

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 three SDKs return the same three result types:
ResultDescription
successPayment completed. Contains paymentId and status.
cancelledCheckout ended without a completed payment. Inspect reason — see below.
errorSomething went wrong. Contains message and optional code.

Cancellation Reasons

A cancelled result carries a reason so you can tell exactly how the checkout ended:
ReasonMeaning
dismissedThe user closed the in-app chooser or web checkout sheet.
rejectedThe Busha app reported the user explicitly rejected the payment. paymentId is populated so you can reconcile the request server-side.
abandonedThe 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.
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.

Checkout Configuration

ParameterTypeRequiredDescription
quoteAmountstringYesAmount to charge (e.g. '10000')
quoteCurrencystringYesCurrency for the amount (e.g. 'NGN')
targetCurrencystringYesSettlement currency
sourceCurrencystringYesCrypto asset for payment (e.g. 'USDT')
referencestringNoCustom transaction reference
metaNamestringNoCustomer name
metaEmailstringNoCustomer email
metaPhonestringNoCustomer phone
allowedPaymentMethodsarrayNoRestricts 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:
ValueBehaviour
undefined or []Show the full chooser
['bushaApp']Skip chooser; deep-link into the Busha app, with web fallback if not installed
['stablecoins']Skip chooser; open web checkout directly
Multiple methodsShow 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 and Flutter only) — The bundled checkout HTML asset failed to load. Try reinstalling the SDK.
  • BUSHA_APP_LAUNCH_FAILED (iOS only) — The SDK could not deep-link into the Busha app. Verify LSApplicationQueriesSchemes is configured correctly in your Info.plist.
  • 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?