For the complete documentation index, see llms.txt. Append
.mdto any page URL for its markdown version.
The Banxa Flutter SDK (banxa_payments_flutter) is a Dart interface to Banxa Hosted Checkout for Flutter apps. You configure it once and start a payment with a single call. The SDK creates the order, presents the native payment sheet when the order supports it, and otherwise hands you a WebView widget to render for hosted checkout. Outcomes from both routes arrive on one stream.
For the end-to-end walkthrough, see the Flutter SDK Integration Guide.
| Requirement | Version |
|---|---|
| Dart | >=3.12.2 <4.0.0 |
| Flutter | >=3.44.9 |
| iOS | 15.0 and above |
| Android | minSdk 24 and above |
The plugin ships iOS and Android implementations only. It is TLS only (https) and does not certificate-pin.
dependencies:
banxa_payments_flutter: 0.1.0flutter pub add banxa_payments_flutter:0.1.0Pin 0.1.0. Do not use a caret range: this is a preview release and Android native checkout is still on a Primer Checkout beta pin (3.0.0-beta.2; iOS is on 2.49.0).
Do not depend on banxa_payments_flutter_ios, banxa_payments_flutter_android, or banxa_payments_flutter_platform_interface directly. Only the umbrella package is a supported dependency.
Set the iOS deployment target to 15.0 in Xcode, then build once with Flutter before opening the iOS project:
flutter build ios --config-onlySkipping this can leave Xcode showing a 13.0 minimum, and plugin resolution fails.
Primer adds native binary size. Measure with flutter build appbundle --analyze-size and an iOS archive if binary size matters for your app.
The plugin merges INTERNET on Android. Checkout may open the camera or photo library for KYC document capture, and those permissions must be declared in the host app.
| Platform | Declare |
|---|---|
iOS (Info.plist) | NSCameraUsageDescription, NSPhotoLibraryUsageDescription, and NSMicrophoneUsageDescription if you enable video KYC |
Android (AndroidManifest.xml) | android.permission.CAMERA |
Missing iOS usage strings crash the app when capture starts.
Call configure once per process, before any other SDK method. Re-calling it replaces the HTTP client and rebinds checkout listeners.
import 'package:banxa_payments_flutter/banxa_payments_flutter.dart';
await BanxaPayments.configure(
const BanxaConfig(
apiKey: 'YOUR_API_KEY',
partnerId: 'your-partner-id',
environment: BanxaEnvironment.sandbox,
),
);| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey | String | Yes | Your v2 API key from the Partner Dashboard. |
partnerId | String | Yes | Your partner identifier. |
environment | BanxaEnvironment | No | Defaults to BanxaEnvironment.sandbox. |
applePayMerchantIdentifier | String? | No | Apple Pay merchant identifier, for example merchant.com.example. Primer refuses Apple Pay without it. |
applePayMerchantName | String? | No | Fallback name for the Apple Pay sheet, used only when the Primer client session does not carry a merchant name. |
configure also takes an optional httpClient:
await BanxaPayments.configure(config, httpClient: myClient);All partner-api traffic is issued from Dart, so it appears in the DevTools network view and can be intercepted, logged, or mocked with your own http.Client.
A blank apiKey or partnerId throws MissingCredentialsException.
BanxaEnvironment | Host |
|---|---|
sandbox | https://api.banxa-sandbox.com |
preprod | https://api.banxa-preprod.com |
production | https://api.banxa.com |
Requests go to {host}/{partnerId}/v2 with x-api-key and Content-Type: application/json. Sandbox and production credentials are separate and are not interchangeable.
| Method | Endpoint | Returns |
|---|---|---|
configure(config, {httpClient}) | — | Future<void> |
checkoutEvents | — | Stream<BanxaCheckoutEvent> |
fetchCountries() | GET /countries | Future<List<Country>> |
fetchFiats({required orderType}) | GET /fiats/{buy|sell} | Future<List<Fiat>> |
fetchCrypto({required orderType}) | GET /crypto/{buy|sell} | Future<List<Crypto>> |
fetchPaymentMethods({required orderType, fiat}) | GET /payment-methods/{buy|sell} | Future<List<PaymentMethod>> |
fetchQuotes({required orderType, required request}) | GET /quotes/{buy|sell} | Future<List<Quote>> |
checkEligibility(request) | POST /eligibility | Future<EligibilityResponse> |
createOrder(request) | POST /buy or POST /sell | Future<CreateOrderResponse> |
startPayment(request) | POST /buy or POST /sell, then checkout | Future<BanxaCheckoutLaunch> |
updatePrimerSession({required primerToken, required savedCard, additionalFields}) | POST /primer/session | Future<void> |
Every method is static on BanxaPayments. Calling any of them before configure throws SdkNotConfiguredException.
final launch = await BanxaPayments.startPayment(
const CreateOrderRequest(
orderType: OrderType.buy,
crypto: 'ETH',
fiat: 'EUR',
fiatAmount: '40',
walletAddress: '0x0000000000000000000000000000000000000000',
email: '[email protected]',
redirectUrl: 'https://example.com/redirect',
paymentMethodId: 'debit-credit-card',
blockchain: 'ETH',
),
);
switch (launch) {
case BanxaPrimerCheckoutLaunched():
// Native sheet is on screen. Wait for checkoutEvents.
case BanxaHostedCheckoutRequired():
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => Scaffold(
appBar: AppBar(title: const Text('Checkout')),
body: BanxaHostedCheckoutView(checkout: launch),
),
),
);
}startPayment creates the order, then presents the native payment sheet when the order carries a nativeToken and the requested method can run on the device. Otherwise it returns BanxaHostedCheckoutRequired.
BanxaCheckoutLaunch is a sealed class. Both variants expose the created order as a CreateOrderResponse.
| Type | Meaning | Extra fields |
|---|---|---|
BanxaPrimerCheckoutLaunched | The native payment sheet has been presented. | — |
BanxaHostedCheckoutRequired | Checkout must run in a WebView. | checkoutUrl, redirectUrl |
checkoutUrl is always https on a Banxa-owned host.
On iOS, Primer presents a single payment method (showPaymentMethod). On Android, Primer Checkout 3.x presents the methods from the client session; the requested Banxa method is still recorded natively.
startPayment throws NativeCheckoutNotEligibleException when there is neither a usable native route nor a valid Banxa https checkout URL.
Banxa checkout URLs must be loaded within about one minute of order creation. startPayment creates and presents in a single call. createOrder exists for creating an order without presenting checkout, but there is no supported pattern for creating an order early and presenting it later.
startPayment does not call POST /eligibility. Use checkEligibility explicitly if you want to gate checkout yourself.
BanxaPayments.checkoutEvents is a Stream<BanxaCheckoutEvent> carrying outcomes from both the native payment sheet and BanxaHostedCheckoutView. Subscribe before calling startPayment.
BanxaPayments.checkoutEvents.listen((event) {
switch (event) {
case BanxaCheckoutCompleted(:final paymentId, :final orderId, :final status):
// Success.
case BanxaCheckoutFailed(:final message):
// Error.
case BanxaCheckoutDismissed():
// Sheet closed.
}
});| Event | Fires when | Fields |
|---|---|---|
BanxaCheckoutCompleted | The customer completed payment. | paymentId, orderId, status, all String? |
BanxaCheckoutFailed | Any checkout failure. | message, a short reason, never a URL |
BanxaCheckoutDismissed | The customer closed checkout, or the hosted WebView was disposed before a terminal URL. | none |
status may be null on Android. Primer may emit dismissed after completed or failed, so treat the events as distinct rather than mutually exclusive.
Completion identifiers are read from Primer or from hosted-checkout query parameters (paymentId, orderId, status, plus payment_id and order_id aliases). Raw URLs are never surfaced to your app.
Checkout events are a UI signal, not the authoritative order state. Confirm the final state from webhooks or Order Lookup before you credit a customer.
BanxaHostedCheckoutView(checkout: launch)| Parameter | Type | Required | Description |
|---|---|---|---|
checkout | BanxaHostedCheckoutRequired | Yes | The launch result from startPayment. |
Behaviour to design around:
- The widget reports on
checkoutEventsand never pops itself. Dismiss the route yourself when a terminal event arrives. - Disposing it before a terminal URL emits
BanxaCheckoutDismissed. - A first page that has not loaded within 30 seconds emits
BanxaCheckoutFailed. - The first URL must be
httpson a Banxa-owned host:banxa.com,banxa-sandbox.com, orbanxa-preprod.com, including subdomains. An initial URL outside that set emitsBanxaCheckoutFailed. - Later navigations may leave Banxa for bank or wallet pages during 3DS, but must stay
https. Non-httpsnavigations are blocked. - JavaScript is enabled for the hosted UI. There is no JavaScript bridge into your app.
| Field | Type | Required | Description |
|---|---|---|---|
orderType | OrderType | Yes | OrderType.buy or OrderType.sell. Selects POST /buy or POST /sell. |
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 hosted checkout. |
id | String? | No | Your order id. |
paymentMethodId | String? | No | Banxa payment method slug, for example apple-pay. Required in practice: native checkout is skipped without it. |
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 stable customer identifier. Banxa uses it to recognise returning customers. |
externalOrderId | String? | No | Your order reference. |
discountCode | String? | No | Promotion or discount code. |
crypto, fiat, fiatAmount, walletAddress, email, and redirectUrl are validated locally before the request is sent. A blank value throws ValidationException with a FieldError per missing field.
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.
| Field | Type | Notes |
|---|---|---|
id | String | Banxa order id. |
checkoutUrl | String? | Hosted checkout URL. Drives the BanxaHostedCheckoutRequired route. |
nativeToken | String? | Primer client token. Present when native checkout is available. |
status | String? | Order status at creation. |
paymentMethodId | String? | Catalog slug. Numeric wire values are stringified. |
paymentMethodType | String? | Payment method type. |
orderType | String? | buy or sell. |
fiat, fiatAmount, crypto, cryptoAmount, blockchain | String? | Echoed order amounts and assets. |
walletAddress, walletAddressTag | String? | Echoed destination. |
externalCustomerId, externalOrderId | String? | Echoed partner references. |
createdAt, updatedAt | String? | Timestamps. |
| Field | Type | Notes |
|---|---|---|
id | String? | Eligibility check identifier. |
paymentReady | bool? | Whether the customer can pay now. |
kycRequirements | List<String> | Outstanding KYC requirement identifiers. |
message | String? | Human-readable detail. |
final countries = await BanxaPayments.fetchCountries();
final fiats = await BanxaPayments.fetchFiats(orderType: OrderType.buy);
final cryptos = await BanxaPayments.fetchCrypto(orderType: OrderType.buy);
final methods = await BanxaPayments.fetchPaymentMethods(
orderType: OrderType.buy,
fiat: 'USD',
);| Type | Fields |
|---|---|
Country | id, description, states: List<CountryState> |
CountryState | id, description |
Fiat | id, description?, symbol?, supportedPaymentMethods: List<FiatPaymentMethod> |
FiatPaymentMethod | id, name?, minimum?, maximum? |
Crypto | id, description?, blockchains: List<Blockchain> |
Blockchain | id, description?, isDefaultBlockchain?, address?, network?, minimum?, unsupportedCountries: Map<String, List<String>> |
PaymentMethod | id, name?, description?, supportedFiats: 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.
final quotes = await BanxaPayments.fetchQuotes(
orderType: OrderType.buy,
request: const QuoteRequest(
paymentMethodId: 'debit-credit-card',
crypto: 'ETH',
blockchain: 'ETH',
fiat: 'AUD',
fiatAmount: '200',
),
);QuoteRequest field | Type | Required | Notes |
|---|---|---|---|
paymentMethodId | String | Yes | Payment method slug. |
crypto | String | Yes | Crypto asset symbol. |
blockchain | String | Yes | Blockchain network. |
fiat | String | Yes | Fiat currency code. |
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. Carries discountCode and originalQuote: QuoteOriginalAmounts? with originalFiatAmount, originalCryptoAmount, originalProcessingFee, originalNetworkFee. |
fetchQuotes returns a list because Banxa returns multiple entries when discount codes apply. Quotes carry no quote id and are not accepted by startPayment. Do not cache them: call immediately before showing a price.
Every SDK failure is a subclass of the sealed BanxaPaymentsException, which exposes a message. Catch the sealed type and switch on the subclass.
| Exception | Cause |
|---|---|
SdkNotConfiguredException | A method was called before BanxaPayments.configure. |
MissingCredentialsException | apiKey or partnerId was blank. Exposes fields. |
UnauthorizedException | HTTP 401. Check the key matches the environment. |
ValidationException | HTTP 400 or 422, or local field validation. Exposes errors: List<FieldError>, each with field and messages. |
ServerException | Any other non-success HTTP status. Exposes statusCode. |
NetworkException | Transport failure, including TLS errors and timeouts. |
CheckoutFailedException | Native Primer presentation failed. |
NativeCheckoutNotEligibleException | The order has no usable native Primer path and no valid hosted checkoutUrl. |
UnknownException | Unexpected local failure, for example a JSON decode error. |
try {
await BanxaPayments.startPayment(request);
} on ValidationException catch (e) {
for (final error in e.errors) {
debugPrint('${error.field}: ${error.messages.join(', ')}');
}
} on BanxaPaymentsException catch (e) {
debugPrint(e.message);
}Show user-facing messages only from validated error fields. Do not expose raw error strings that may include internal detail. See Error Codes.
| Capability | Use instead |
|---|---|
| Order lookup and reconciliation | Order Lookup and Webhooks |
| Full order list across all customers | GET /{partnerRef}/v2/orders via API integration |
| KYC data sharing (Sumsub token share) | API integration. Uses HMAC auth, which the SDK does not use |
Locked pricing (quoteId) | Not supported. Quotes are indicative |
Availability varies by partner, region, and order type. Treat fetchPaymentMethods as the source of truth. The table below is a static reference.
| Payment method id | Supported fiats |
|---|---|
debit-credit-card | AED, ARS, AUD, 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 |
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 |
For the full list, see Supported Payment Methods.
- Flutter SDK Integration Guide: end-to-end walkthrough.
- Webhooks: configure webhook notifications.
- Order Statuses: full status reference.