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.
A React Native, iOS, Android, 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.
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.
BushaPay iOS SDK
View the full iOS SDK source, releases, and Swift Package Manager setup on
GitHub.
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-iosSelect your version, choose the BushaPay library, and add it to your app target.For Package.swift:
The SDK is authored in the bushaHQ/pay
monorepo under ios/. Each release is mirrored to the standalone
bushaHQ/pay-ios repository so Swift
Package Manager can resolve it correctly. Issues, PRs, and source code live in
the monorepo.
BushaPay Android SDK
View the full Android SDK source, releases, and changelog on GitHub.
Requirements: Android 5.0 (API 21)+, Kotlin 1.9+, AndroidXInstalling via JitPackAdd the JitPack repository to your settings.gradle.kts:
The SDK is authored in the bushaHQ/pay
monorepo under android/. Each release is mirrored to the standalone
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.
busha_pay on pub.dev
View the full Flutter SDK package, documentation, and changelog on pub.dev.
Requirements: Flutter 3.0+, Dart 3.0+Installing from pub.dev (recommended)Add busha_pay to your pubspec.yaml:
dependencies: busha_pay: ^0.0.1
Then run:
flutter pub get
Installing from GitHubEach 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:
<!-- 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>
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:
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.
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.
React Native
iOS (Swift)
Android (Kotlin)
Flutter
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>
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { for context in URLContexts { if BushaPay.handleDeepLink(context.url) { continue } // your own URL handling }}
SwiftUI:
@mainstruct MyApp: App { var body: some Scene { WindowGroup { ContentView() .onOpenURL { url in if BushaPay.handleDeepLink(url) { return } // your own URL handling } } }}
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):
import android.content.Intentimport android.os.Bundleimport co.busha.pay.BushaPayclass 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) }}
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:
When checkout() is called, the SDK opens a chooser with two options:
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.
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.
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.
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.
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
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.