# Android SDK Reference

The Banxa Android SDK (`BanxaPaymentSDK`) is the payment execution layer of Banxa Native for native Android apps. It is the Kotlin 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: the `StartPayment` composable and its callback lambdas.

The SDK also ships configuration and quote helpers. 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 |
|  --- | --- |
| Android API level | 24 and above |
| Kotlin | 2.0 and above, with the Compose Compiler Gradle plugin |
| Jetpack Compose | Enabled in your project |


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

## Installation

The Android SDK is distributed as an AAR. Copy the AAR into your module's `libs` directory and reference it from your Gradle build file.

```kotlin
dependencies {
    implementation(files("libs/BanxaPayment-1.0.0.aar"))
}
```

AAR distribution
The Android SDK is not currently published to a Maven repository, so it does not resolve by coordinate and does not update through your dependency manager. Contact Banxa to obtain the AAR and to be notified when a new version is released.

## Configuration

Build a `BanxaConfig` and initialise the SDK once at app launch.

```kotlin
val config = BanxaConfig.Builder()
    .apiKey("YOUR_API_KEY")
    .partnerID("your-partner-id")
    .environment(Environment.SANDBOX)
    .build()

Banxa.initialize(config)
```

### BanxaConfig.Builder

| Method | Required | Description |
|  --- | --- | --- |
| `apiKey(String)` | Yes | Your Banxa API key from the merchant dashboard. |
| `partnerID(String)` | Yes | Your partner identifier. |
| `environment(Environment)` | Yes | `Environment.SANDBOX` or `Environment.PRODUCTION`. |
| `primerTheme(PrimerTheme)` | No | Overrides the payment sheet colour tokens. See [Payment sheet theming](#payment-sheet-theming). |


### Environments

| Environment | Host |
|  --- | --- |
| `Environment.SANDBOX` | `https://api.banxa-sandbox.com` |
| `Environment.PRODUCTION` | `https://api.banxa.com` |


Sandbox and production credentials are separate and are not interchangeable.

## Starting a payment

`StartPayment` is a composable. Render it when the customer confirms, and supply the callback lambdas you need.

```kotlin
StartPayment(
    createOrderRequest = CreateOrderRequest(
        fiat = "EUR",
        crypto = "ETH",
        fiatAmount = "40",
        cryptoAmount = null,
        walletAddress = "0x0000000000000000000000000000000000000000",
        redirectUrl = "your-app-scheme://banxa-return",
        paymentMethodId = "debit-credit-card",
        email = "user@example.com"
    ),
    banxaDidReceiveCheckout = { result ->
        // Payment succeeded.
        println("Paid: ${result.paymentId ?: result.rawQuery ?: "-"}")
    },
    banxaDidFail = { error ->
        // Any failure: API, validation, network, decoding, or checkout error.
        println("Failed: $error")
    },
    banxaDidDismiss = {
        // The customer closed the checkout UI without completing.
    }
)
```

`StartPayment` runs the full pipeline: it checks eligibility, creates the order, and presents the native payment sheet when a payment token is available. All callbacks have a default no-op implementation, so supply only what you need.

Gate on eligibility before you render 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.

## Callbacks

| Callback | Fires when |
|  --- | --- |
| `banxaDidReceiveCheckout` | The customer completed payment. Receives a checkout result. |
| `banxaDidFail` | Any Banxa, network, validation, or checkout failure. |
| `banxaDidDismiss` | The customer closed the checkout UI without completing. |


### Checkout result

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


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.

## Payment sheet theming

Pass a `PrimerTheme` to the config builder to override the payment sheet colour tokens.

```kotlin
val primerTheme = PrimerTheme(
    lightColorTokens = object : LightColorTokens() {
        override val primerColorBrand: Color = Color(0xFF6C5CE7)
        override val primerColorTextPrimary: Color = Color(0xFFD32E2E)
        override val primerColorBackground: Color = Color(0xFF9CFFA1)
    },
)

val config = BanxaConfig.Builder()
    .apiKey("YOUR_API_KEY")
    .partnerID("your-partner-id")
    .environment(Environment.SANDBOX)
    .primerTheme(primerTheme)
    .build()

Banxa.initialize(config)
```

Theming is optional. Without it, the payment sheet uses the default token set.

## Models

### CreateOrderRequest

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `paymentMethodId` | String | Yes | Banxa payment method id, for example `google-pay`. |
| `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. |
| `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. |


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.

## Configuration endpoints

These helpers return a `Result` and require an initialised SDK. In a Banxa Native integration, prefer the equivalent Native API endpoints so that configuration stays on the HMAC-authenticated surface.

```kotlin
val repository = BanxaRepository(
    RetrofitClient.getApi(config.baseUrl, config.environment)
)
```

### Payment methods

```kotlin
val result = repository.getPaymentMethods(
    partner = config.partner,
    apiKey = config.apiKey,
    orderType = "buy",
    fiat = "USD" // optional
)

result.onSuccess { methods ->
    methods.forEach { method ->
        println("${method.id}: ${method.name}")
    }
}
```

| `PaymentMethod` field | Type | Notes |
|  --- | --- | --- |
| `id` | String | Payment method id, for example `apple-pay`. |
| `name` | String | Display name. |
| `description` | String? | Human-readable description. |
| `supportedFiats` | List<String> | Fiat codes available for this method. |


### Countries

```kotlin
val result = repository.getCountries(
    partner = config.partner,
    apiKey = config.apiKey
)
```

| Type | Fields |
|  --- | --- |
| `Country` | `id`, `description`, `states: List<CountryState>` |
| `CountryState` | `id`, `description` |


The country list is the same for buy and sell.

### Fiats

```kotlin
val result = repository.getFiats(
    partner = config.partner,
    apiKey = config.apiKey,
    orderType = "buy"
)
```

| Type | Fields |
|  --- | --- |
| `Fiat` | `id`, `description`, `symbol?`, `supportedPaymentMethods: List<FiatPaymentMethod>` |
| `FiatPaymentMethod` | `id`, `name`, `minimum?`, `maximum?` |


### Cryptocurrencies

```kotlin
val result = repository.getCrypto(
    partner = config.partner,
    apiKey = config.apiKey,
    orderType = "buy"
)
```

| Type | Fields |
|  --- | --- |
| `Crypto` | `id`, `description`, `blockchains: List<Blockchain>` |
| `Blockchain` | `id`, `description`, `isDefaultBlockchain`, `address?`, `network?`, `minimum?`, `unsupportedCountries: Map<String, List<String>>` |


In `unsupportedCountries`, the key is a country code and the value is the restricted state codes. An empty list means the whole country is restricted.

### Quotes

```kotlin
val result = repository.getQuote(
    partner = config.partner,
    apiKey = config.apiKey,
    orderType = "buy",
    request = QuoteRequest(
        fiat = "AUD",
        crypto = "ETH",
        blockchain = "ETH",
        paymentMethodId = "debit-credit-card",
        fiatAmount = "200"
    )
)
```

| `QuoteRequest` field | Type | Required | Notes |
|  --- | --- | --- | --- |
| `fiat` | String | Yes | Fiat currency code. |
| `crypto` | String | Yes | Crypto asset symbol. |
| `blockchain` | String | Yes | Blockchain network. |
| `paymentMethodId` | String | Yes | Payment method id. |
| `fiatAmount` | String? | Conditional | Provide this or `cryptoAmount`. |
| `cryptoAmount` | String? | Conditional | Provide this or `fiatAmount`. |
| `externalCustomerId` | String? | No | Your customer identifier. |
| `ipAddress` | String? | No | Customer IP for regional pricing. |
| `discountCode` | String? | No | Promotion or discount code. |


| `Quote` field | Type | Notes |
|  --- | --- | --- |
| `paymentMethodId` | String | Payment method the quote applies to. |
| `cryptoAmount` | String | Crypto the customer receives. |
| `fiatAmount` | String | Fiat the customer spends. |
| `processingFee` | String? | Banxa processing fee in fiat. |
| `networkFee` | String? | Blockchain network fee in fiat. |
| `discount` | `QuoteDiscount?` | Present when a discount code was applied. |


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`. Banxa-originated failures are `APIError`, covering invalid URL, non-2xx server response, unauthorised (`401`), decoding failure, no network connectivity, missing credentials, uninitialised SDK, checkout failure, and unknown.

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 |


## Google Pay approval

When your Android app presents Google Pay, Google requires app-level approval before you can process live payments. Banxa's own approval does not cover payments presented inside your app. Start the approval process early: it is the most common cause of a delayed Android launch. See the [Google Pay guide](/products/native-api/docs/guides/google-pay).

## Payment methods and supported fiats

Availability varies by partner, region, and order type. Treat `getPaymentMethods` 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` |
| `google-pay` | `AUD`, `EUR`, `USD` |
| `apple-pay` | `AUD`, `EUR`, `GBP`, `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.
- [Google Pay](/products/native-api/docs/guides/google-pay): app approval 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.