For the complete documentation index, see llms.txt. Append
.mdto any page URL for its markdown version.
The Banxa iOS SDK (BanxaPaymentSDK) is the payment execution layer of Banxa Native for native iOS apps. It is the Swift counterpart to the React Native SDK: used alongside the Native API to present the native payment sheet for cards, Apple Pay, and Google Pay, the payment methods that cannot be handled through the API alone due to PCI requirements.
Bank transfers are handled directly by the Native API and do not involve the SDK. For the end-to-end integration walkthrough, see the Integration Guide.
In a Banxa Native integration your backend handles pricing, identity, and eligibility through the Native API. The SDK's role is limited to payment execution: startPayment(request:controller:) and the BanxaPaymentSDKDelegate callbacks.
The SDK also ships configuration helpers (fetchCountries, fetchFiats, fetchCrypto, fetchPaymentMethods, fetchQuotes). Prefer the equivalent Native API endpoints for those, so that configuration and pricing stay on one authenticated surface. The helpers are documented below for completeness.
| Requirement | Version |
|---|---|
| iOS | 13.1 and above |
| Xcode | 16 and above |
| Swift | 6.0 and above |
You also need a Banxa partner account, which supplies the apiKey and partnerID used to configure the SDK.
Add the package to Package.swift:
dependencies: [
.package(url: "https://github.com/BanxaOfficial/ios-payment-sdk", from: "1.0.0")
]Then add BanxaPaymentSDK to your target:
.target(
name: "YourApp",
dependencies: [
.product(name: "BanxaPaymentSDK", package: "ios-payment-sdk")
]
)- Select File, Add Package Dependencies.
- Enter the repository URL.
- Select BanxaPaymentSDK and add it to your app target.
The SDK transitively pulls in the payment provider SDK (>= 2.49.0). You do not import or reference it directly.
Configure the SDK once at app launch, for example in your App entry point or AppDelegate.
import BanxaPaymentSDK
let config = BanxaConfig(
apiKey: "YOUR_API_KEY",
partnerID: "your-partner-id",
environment: .sandbox // .sandbox or .production
)
BanxaPaymentSDK.shared.configure(config: config)
BanxaPaymentSDK.shared.delegate = self| Field | Type | Required | Description |
|---|---|---|---|
apiKey | String | Yes | Your Banxa API key from the merchant dashboard. |
partnerID | String | Yes | Your partner identifier. |
environment | Environment | Yes | .sandbox or .production. |
Calling any SDK method before configure(config:) fails with APIError.sdkNotConfigured. Blank credentials fail with APIError.missingCredentials.
| Environment | Host |
|---|---|
.sandbox | https://api.banxa-sandbox.com |
.production | https://api.banxa.com |
Sandbox and production credentials are separate and are not interchangeable.
Build a CreateOrderRequest and call startPayment(request:controller:). Pass the view controller that should host the checkout presentation.
let request = CreateOrderRequest(
crypto: "ETH",
fiat: "EUR",
fiatAmount: "40",
walletAddress: "0x0000000000000000000000000000000000000000",
email: "[email protected]",
redirectURL: "your-app-scheme://banxa-return",
paymentMethodID: "debit-credit-card"
)
BanxaPaymentSDK.shared.startPayment(request: request, controller: self)startPayment runs the full pipeline in one call: it checks eligibility, creates the order, and presents the native payment sheet when a payment token is available. Results arrive on the delegate, not as a return value.
Call POST /eapi/v0/eligibility from your backend and confirm paymentReady: true before invoking the SDK. The SDK performs its own internal eligibility check, but it does not expose the result or the requirements[] array to your app, so it cannot tell your user what is outstanding. Without the backend check you have no way to drive the KYC remediation loop.
The SDK does not accept a quoteId. Use indicative pricing from GET /eapi/v0/price and re-price close to the point of payment to reduce rate drift.
Conform to BanxaPaymentSDKDelegate. All callback types are Banxa-owned: you never import or reference the underlying payment provider SDK. Every method has a default no-op implementation, so implement only what you need.
import BanxaPaymentSDK
extension MyViewController: BanxaPaymentSDKDelegate {
func banxaDidCompleteCheckout(_ result: BanxaCheckoutResult) {
// Payment succeeded.
print("Paid:", result.paymentId ?? result.rawQuery ?? "-")
}
func banxaDidFail(error: Error) {
// Any failure: API, validation, network, decoding, or checkout error.
// Banxa-originated failures are APIError.
print("Failed:", error.localizedDescription)
}
func banxaDidDismiss() {
// The customer closed the checkout UI without completing.
}
}All delegate callbacks are delivered on the main actor.
| Callback | Fires when |
|---|---|
banxaDidCompleteCheckout(_:) | The customer completed payment. |
banxaDidFail(error:) | Any Banxa, network, validation, or checkout failure. |
banxaDidDismiss() | The customer closed the checkout UI without completing. |
| Field | Populated when |
|---|---|
paymentId | The native payment sheet was used. |
orderId | The native payment sheet was used. |
status | The native payment sheet was used. |
rawQuery | The fallback flow was used. Contains the terminal success URL query string. |
Delegate callbacks are a UI signal, not the authoritative order state. Confirm the final state from webhooks before you credit a customer.
| Field | Type | Required | Description |
|---|---|---|---|
crypto | String | Yes | Crypto asset symbol, for example ETH. |
fiat | String | Yes | Fiat currency code, for example EUR. |
fiatAmount | String | Yes | Fiat amount as a string. |
paymentMethodID | String? | Yes | Banxa payment method id, for example debit-credit-card or apple-pay. |
walletAddress | String | Yes | Destination wallet address. |
email | String | Yes | Customer email address. |
redirectURL | String | Yes | URL the customer returns to after the fallback flow. |
id | String? | No | Your order id. |
blockchain | String? | No | Explicit blockchain network. |
cryptoAmount | String? | No | Crypto amount when ordering by crypto value. |
walletAddressTag | String? | No | Tag or memo for chains that require it. |
subPartnerID | String? | No | Sub-partner identifier. |
metadata | String? | No | Opaque metadata string. |
externalCustomerID | String? | No | Your customer identifier. Pass the same value you use as identityReference. |
externalOrderID | String? | No | Your order reference. |
discountCode | String? | No | Promotion or discount code. |
paymentMethodID is declared as an optional in the Swift signature, but Banxa requires a value. Omitting it fails at payment execution rather than at compile time, so the compiler will not catch it for you. Always pass an id returned by fetchPaymentMethods or by GET /eapi/v0/payment-methods/{transactionType}.
XRP, XLM, EOS, and ATOM require a memo or tag. Pass it as walletAddressTag. An on-ramp to one of these chains without the memo can be unrecoverable.
If you use redirect-based payment methods, register a URL scheme in your Info.plist and use the same scheme for redirectURL on CreateOrderRequest.
These helpers are async throws and require a configured SDK. In a Banxa Native integration, prefer the equivalent Native API endpoints so that configuration stays on the HMAC-authenticated surface.
let countries = try await BanxaPaymentSDK.shared.fetchCountries()| Type | Fields |
|---|---|
Country | id, description, states: [CountryState]? |
CountryState | id, description |
let fiats = try await BanxaPaymentSDK.shared.fetchFiats(orderType: .buy)| Type | Fields |
|---|---|
OrderType | .buy, .sell |
Fiat | id, description?, symbol?, supportedPaymentMethods: [FiatPaymentMethod]? |
FiatPaymentMethod | id, name?, minimum?, maximum? |
let assets = try await BanxaPaymentSDK.shared.fetchCrypto(orderType: .buy)| Type | Fields |
|---|---|
Cryptocurrency | id, description?, blockchains: [CryptoBlockchain]? |
CryptoBlockchain | id, description?, isDefaultBlockchain?, address?, network?, minimum?, unsupportedCountries: [String: [String]]? |
let methods = try await BanxaPaymentSDK.shared.fetchPaymentMethods(orderType: .buy)
let usdOnly = try await BanxaPaymentSDK.shared.fetchPaymentMethods(orderType: .buy, fiat: "USD")| Type | Fields |
|---|---|
PaymentMethod | id, name?, description?, supportedFiats: [String]? |
let request = QuoteRequest(
paymentMethodID: "debit-credit-card",
crypto: "ETH",
blockchain: "ETH",
fiat: "USD",
fiatAmount: "200"
)
let quotes = try await BanxaPaymentSDK.shared.fetchQuotes(orderType: .buy, request: request)| Type | Fields |
|---|---|
QuoteRequest | paymentMethodID, crypto, blockchain, fiat, fiatAmount?, cryptoAmount?, externalCustomerID?, ipAddress?, discountCode? |
Quote | paymentMethodID?, cryptoAmount?, fiatAmount?, processingFee?, networkFee?, discount: QuoteDiscount? |
QuoteDiscount | originalQuote: QuoteOriginalAmounts?, discountCode? |
QuoteOriginalAmounts | originalCryptoAmount?, originalNetworkFee?, originalProcessingFee?, originalFiatAmount? |
Provide either fiatAmount or cryptoAmount. If both are set, Banxa uses cryptoAmount. Quotes carry no quote id and are not accepted by startPayment. Do not cache them: call immediately before showing a price.
Failures are surfaced through banxaDidFail(error:) as APIError. Each case provides a human-readable errorDescription.
| Case | Meaning |
|---|---|
.invalidURL | The endpoint URL could not be built. |
.serverError(Int) | Non-2xx HTTP response. |
.unauthorized | 401 from Banxa. Check apiKey. |
.decodingFailed(String) | The response payload failed to decode. |
.networkUnavailable | No network connectivity. |
.missingCredentials([String]) | apiKey or partnerID was blank in BanxaConfig. |
.sdkNotConfigured | startPayment was called before configure(config:). |
.checkoutFailed(String?) | The checkout reached the failure URL. The payload is the raw query string. |
.unknown(String) | Any other unexpected error. |
Show user-facing messages only from validated error fields. Do not expose raw error strings that may include internal detail.
| Capability | Use instead |
|---|---|
| Identity creation, KYC sharing, document sharing | Native API. See Identity and KYC |
Eligibility result and requirements[] | POST /eapi/v0/eligibility from your backend |
Locked pricing (quoteId) | Bank transfer ramps only. See Quotes and Pricing |
| Order lookup and reconciliation | Webhooks and the Native API |
| Off-ramp (sell) | Native API. The SDK covers on-ramp payment execution only |
| Payment sheet theming | Not exposed on iOS. Available on Android |
Availability varies by partner, region, and order type. Treat fetchPaymentMethods or the Native API configuration endpoints as the source of truth. The table below is a static reference.
| Payment method id | Supported fiats |
|---|---|
debit-credit-card | AED, ARS, AUD, BRL, CAD, CHF, CZK, DKK, EUR, GBP, HKD, IDR, INR, JPY, KRW, MXN, MYR, NGN, NOK, NZD, PHP, PLN, QAR, RUB, SAR, SEK, SGD, THB, TRY, TWD, USD, VND, ZAR |
apple-pay | AUD, EUR, GBP, USD |
google-pay | AUD, EUR, USD |
payid-bank-transfer | AUD |
pix | BRL |
zar-bank-transfer | ZAR |
pse | COP |
khipu | CLP |
interac-bank-transfer | CAD |
klarna-paynow | EUR |
ideal-bank-transfer | AUD, EUR |
sepa-bank-transfer | EUR |
gbp-bank-transfer | GBP |
spei | MXN |
- Integration Guide: end-to-end walkthrough.
- Apple Pay: entitlements and platform setup.
- Cards: 3D Secure setup.
- Interpreting Eligibility: the requirements dictionary and remediation loop.