# iOS SDK Reference

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](/products/native-api/docs/sdk/sdk-reference): 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](/products/native-api/docs/guides/foundations).

## 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

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

## Installation

### Swift Package Manager

Add the package to `Package.swift`:

```swift
dependencies: [
    .package(url: "https://github.com/BanxaOfficial/ios-payment-sdk", from: "1.0.0")
]
```

Then add `BanxaPaymentSDK` to your target:

```swift
.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`.

```swift
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

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

### Environments

| Environment | Host |
|  --- | --- |
| `.sandbox` | `https://api.banxa-sandbox.com` |
| `.production` | `https://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.

```swift
let request = CreateOrderRequest(
    crypto: "ETH",
    fiat: "EUR",
    fiatAmount: "40",
    walletAddress: "0x0000000000000000000000000000000000000000",
    email: "user@example.com",
    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](/products/native-api/docs/how-it-works/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.

```swift
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. |


### BanxaCheckoutResult

| 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](/products/native-api/docs/transaction-lifecycle/webhooks) before you credit a customer.

## Models

### CreateOrderRequest

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

```swift
let countries = try await BanxaPaymentSDK.shared.fetchCountries()
```

| Type | Fields |
|  --- | --- |
| `Country` | `id`, `description`, `states: [CountryState]?` |
| `CountryState` | `id`, `description` |


### Fiats

```swift
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?` |


### Crypto

```swift
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]]?` |


### Payment methods

```swift
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]?` |


### Quotes

```swift
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.

## Error handling

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.

## Not available in the SDK

| Capability | Use instead |
|  --- | --- |
| Identity creation, KYC sharing, document sharing | Native API. See [Identity and KYC](/products/native-api/docs/how-it-works/identity-kyc) |
| Eligibility result and `requirements[]` | `POST /eapi/v0/eligibility` from your backend |
| Locked pricing (`quoteId`) | Bank transfer ramps only. See [Quotes and Pricing](/products/native-api/docs/how-it-works/quotes-and-pricing) |
| Order lookup and reconciliation | [Webhooks](/products/native-api/docs/transaction-lifecycle/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 |


## 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 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` |


## Next steps

- [Integration Guide](/products/native-api/docs/guides/foundations): end-to-end walkthrough.
- [Apple Pay](/products/native-api/docs/guides/apple-pay): entitlements and platform setup.
- [Cards](/products/native-api/docs/guides/cards): 3D Secure setup.
- [Interpreting Eligibility](/products/native-api/docs/how-it-works/interpreting-eligibility): the requirements dictionary and remediation loop.