Official iOS (Swift Package) SDK for accepting crypto payments via Busha.
- iOS 14+
- Swift 5.9+
- Xcode 15+
In Xcode: File → Add Package Dependencies… and enter:
https://github.com/bushaHQ/pay-ios
Pick the version and add the BushaPay library to your target.
For a Package.swift:
.package(url: "https://github.com/bushaHQ/pay-ios", from: "0.0.1"),then list BushaPay as a target dependency.
Note: the SDK is authored in the bushaHQ/pay monorepo under
ios/. Each release is mirrored to the standalone bushaHQ/pay-ios repo above so Swift Package Manager can resolve it the way it expects (Package.swiftat root, plainv<version>tags). Issues, PRs, and source live in the monorepo.
In your AppDelegate or @main app:
import BushaPay
BushaPay.initialize(
publicKey: "pub_xxx",
environment: .sandbox // or .live
)import BushaPay
import UIKit
final class CheckoutButton: UIButton {
func payNow(from presenter: UIViewController) {
BushaPay.checkout(
config: BushaPayConfig(
quoteAmount: "10000",
quoteCurrency: "NGN",
targetCurrency: "NGN",
sourceCurrency: "USDT",
metaName: "Jane Doe",
metaEmail: "jane@example.com"
),
from: presenter
) { 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:
let result = await BushaPay.checkout(config: config, from: viewController)The SDK derives the callback URL scheme from your bundle identifier:
<your.bundle.id>.busha-pay
Add this to Info.plist under CFBundleURLTypes:
<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>Add the Busha app's URL schemes to LSApplicationQueriesSchemes so UIApplication.canOpenURL(_:) returns true when the app is installed:
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- Production Busha app -->
<string>co.busha.apple</string>
<!-- Sandbox/staging Busha app (only needed if you use .sandbox) -->
<string>co.busha.boro.development</string>
</array>The SDK does not subscribe to incoming URLs itself — that avoids conflicts with whatever URL handling your app already does (Universal Links, scene-delegate routing, third-party deep-link libs, etc.). You forward Busha callbacks with one call:
BushaPay.handleDeepLink(url) // returns true if the URL was a Busha callbackfunc application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
if BushaPay.handleDeepLink(url) {
return true
}
// …your own URL handling…
return false
}func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for context in URLContexts {
if BushaPay.handleDeepLink(context.url) { continue }
// …your own URL handling…
}
}@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
if BushaPay.handleDeepLink(url) { return }
// …your own URL handling…
}
}
}
}xcrun simctl openurl booted "com.example.myapp.busha-pay://callback?status=completed&paymentRequestId=PAYR_test"Replace com.example.myapp with your bundle identifier. If wired correctly, your app comes to the foreground and the BushaPay.checkout(…) completion fires with .success(_).
- The SDK opens a chooser with two options: Pay with Busha app or Pay with Stablecoins.
- Busha app — if installed (
UIApplication.canOpenURL), the SDK deep-links into it. If not, it falls back to the web checkout. - Stablecoins — opens the web checkout in an in-app
WKWebView. - The result is delivered to the completion handler.
| Case | Description |
|---|---|
.success(BushaPaySuccess) |
Payment completed. Contains paymentId and status, plus optional full payment data. |
.cancelled(BushaPayCancelled) |
Checkout ended without a completed payment. Inspect reason (see below). |
.error(BushaPayError) |
Something went wrong. Contains message and optional code. |
BushaPayCancelled carries a reason so you can tell how the checkout ended:
BushaPayCancelledReason |
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 (webhook / status API) before showing the user a final state. |
When payment completes via the web checkout, BushaPaySuccess includes full data (amounts, currencies). When payment completes via the Busha app, only paymentId and status are available — check result.hasFullData.
Always verify the payment server-side via webhooks. The client result is a UX hint, not the source of truth.
BushaPayError.message is diagnostic — useful for logs and support tickets but not safe to surface to end users verbatim. Branch on BushaPayError.code for UX decisions:
| Code | Meaning |
|---|---|
CHECKOUT_IN_PROGRESS |
A previous BushaPay.checkout(…) call hasn't resolved yet |
WEBVIEW_LOAD_ERROR |
Network failure, DNS error, or other platform-level load failure |
WEBVIEW_HTTP_ERROR |
Non-2xx HTTP response from the checkout endpoint |
WEBVIEW_TIMEOUT |
Checkout page didn't bootstrap within 30 seconds |
HTML_LOAD_ERROR |
The bundled checkout HTML resource failed to load |
BUSHA_APP_LAUNCH_FAILED |
UIApplication.open reported the deep-link launch as unsuccessful |
| 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 |
[PaymentMethod]? |
No | Restricts which payment methods the chooser offers. See Restricting payment methods. |
By default, checkout shows a chooser with two tiles: Busha and Stablecoins. Pass allowedPaymentMethods to skip the chooser or hide tiles:
BushaPayConfig(
quoteAmount: "10000",
quoteCurrency: "NGN",
targetCurrency: "NGN",
sourceCurrency: "USDT",
// Skip the chooser and route directly to the Busha app
// (with web fallback if it isn't installed).
allowedPaymentMethods: [.bushaApp]
)allowedPaymentMethods |
Behavior |
|---|---|
nil (default) or [] |
Show the full chooser. |
[.bushaApp] |
Skip the chooser; deep-link into the Busha app, falling through to the web checkout if the app isn't installed. |
[.stablecoins] |
Skip the chooser; open the web checkout directly. |
[.bushaApp, .stablecoins] |
Show the chooser with only those tiles. |
- Log in to your Busha Business dashboard.
- Go to Settings → Developer Tools.
- Copy your Public Key (starts with
pub_).
Use your sandbox key for testing and production key for live payments.
The package builds with xcodebuild against an iOS simulator destination. From the repo root:
make build-ios
make test-iosOverride the destination with IOS_DESTINATION=… if you don't have an iPhone simulator handy.
MIT