Skip to content
Last updated

iOS SDK Reference

For the complete documentation index, see llms.txt. Append .md to 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.


Which parts are relevant for Banxa Native

Your backend still owns orchestration

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.


Requirements

RequirementVersion
iOS13.1 and above
Xcode16 and above
Swift6.0 and above

You also need a Banxa partner account, which supplies the apiKey and partnerID used to configure the SDK.


Installation

Swift Package Manager

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")
    ]
)

Through Xcode

  1. Select File, Add Package Dependencies.
  2. Enter the repository URL.
  3. 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.


Configuration

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

BanxaConfig

FieldTypeRequiredDescription
apiKeyStringYesYour Banxa API key from the merchant dashboard.
partnerIDStringYesYour partner identifier.
environmentEnvironmentYes.sandbox or .production.

Calling any SDK method before configure(config:) fails with APIError.sdkNotConfigured. Blank credentials fail with APIError.missingCredentials.

Environments

EnvironmentHost
.sandboxhttps://api.banxa-sandbox.com
.productionhttps://api.banxa.com

Sandbox and production credentials are separate and are not interchangeable.


Starting a payment

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.

Gate on eligibility before you call startPayment

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.

See Interpreting Eligibility.

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.


Handling callbacks

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.

CallbackFires when
banxaDidCompleteCheckout(_:)The customer completed payment.
banxaDidFail(error:)Any Banxa, network, validation, or checkout failure.
banxaDidDismiss()The customer closed the checkout UI without completing.

BanxaCheckoutResult

FieldPopulated when
paymentIdThe native payment sheet was used.
orderIdThe native payment sheet was used.
statusThe native payment sheet was used.
rawQueryThe 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.


Models

CreateOrderRequest

FieldTypeRequiredDescription
cryptoStringYesCrypto asset symbol, for example ETH.
fiatStringYesFiat currency code, for example EUR.
fiatAmountStringYesFiat amount as a string.
paymentMethodIDString?YesBanxa payment method id, for example debit-credit-card or apple-pay.
walletAddressStringYesDestination wallet address.
emailStringYesCustomer email address.
redirectURLStringYesURL the customer returns to after the fallback flow.
idString?NoYour order id.
blockchainString?NoExplicit blockchain network.
cryptoAmountString?NoCrypto amount when ordering by crypto value.
walletAddressTagString?NoTag or memo for chains that require it.
subPartnerIDString?NoSub-partner identifier.
metadataString?NoOpaque metadata string.
externalCustomerIDString?NoYour customer identifier. Pass the same value you use as identityReference.
externalOrderIDString?NoYour order reference.
discountCodeString?NoPromotion or discount code.
paymentMethodID is optional in the type, required in practice

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

A missing memo can permanently lose funds

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.


URL scheme

If you use redirect-based payment methods, register a URL scheme in your Info.plist and use the same scheme for redirectURL on CreateOrderRequest.


Configuration endpoints

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.

Countries

let countries = try await BanxaPaymentSDK.shared.fetchCountries()
TypeFields
Countryid, description, states: [CountryState]?
CountryStateid, description

Fiats

let fiats = try await BanxaPaymentSDK.shared.fetchFiats(orderType: .buy)
TypeFields
OrderType.buy, .sell
Fiatid, description?, symbol?, supportedPaymentMethods: [FiatPaymentMethod]?
FiatPaymentMethodid, name?, minimum?, maximum?

Crypto

let assets = try await BanxaPaymentSDK.shared.fetchCrypto(orderType: .buy)
TypeFields
Cryptocurrencyid, description?, blockchains: [CryptoBlockchain]?
CryptoBlockchainid, description?, isDefaultBlockchain?, address?, network?, minimum?, unsupportedCountries: [String: [String]]?

Payment methods

let methods = try await BanxaPaymentSDK.shared.fetchPaymentMethods(orderType: .buy)
let usdOnly = try await BanxaPaymentSDK.shared.fetchPaymentMethods(orderType: .buy, fiat: "USD")
TypeFields
PaymentMethodid, name?, description?, supportedFiats: [String]?

Quotes

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)
TypeFields
QuoteRequestpaymentMethodID, crypto, blockchain, fiat, fiatAmount?, cryptoAmount?, externalCustomerID?, ipAddress?, discountCode?
QuotepaymentMethodID?, cryptoAmount?, fiatAmount?, processingFee?, networkFee?, discount: QuoteDiscount?
QuoteDiscountoriginalQuote: QuoteOriginalAmounts?, discountCode?
QuoteOriginalAmountsoriginalCryptoAmount?, 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.


Error handling

Failures are surfaced through banxaDidFail(error:) as APIError. Each case provides a human-readable errorDescription.

CaseMeaning
.invalidURLThe endpoint URL could not be built.
.serverError(Int)Non-2xx HTTP response.
.unauthorized401 from Banxa. Check apiKey.
.decodingFailed(String)The response payload failed to decode.
.networkUnavailableNo network connectivity.
.missingCredentials([String])apiKey or partnerID was blank in BanxaConfig.
.sdkNotConfiguredstartPayment 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.


Not available in the SDK

CapabilityUse instead
Identity creation, KYC sharing, document sharingNative 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 reconciliationWebhooks and the Native API
Off-ramp (sell)Native API. The SDK covers on-ramp payment execution only
Payment sheet themingNot exposed on iOS. Available on Android

Payment methods and supported fiats

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 idSupported fiats
debit-credit-cardAED, 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-payAUD, EUR, GBP, USD
google-payAUD, EUR, USD
payid-bank-transferAUD
pixBRL
zar-bank-transferZAR
pseCOP
khipuCLP
interac-bank-transferCAD
klarna-paynowEUR
ideal-bank-transferAUD, EUR
sepa-bank-transferEUR
gbp-bank-transferGBP
speiMXN

Next steps