SDK Setup
Requirements, installation, environments, the credential provider and screen protection for the Android SDK.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Android SDK | Surfaces, isolation model and flows |
| Authentication | The user token accessToken() returns |
| Api Basics | X-Chain-Id and the identity headers the SDK sends |
Requirements
| Requirement | Value |
|---|---|
| Android | 6.0 (API 23) or later |
| JVM / Kotlin | Java 17 bytecode; Kotlin 2.0 or later for a Kotlin host |
| Repositories | mavenCentral() for the SDK, google() for its AndroidX dependency |
| Permissions | The SDK declares none. INTERNET is your app's to request |
Install
// build.gradle.kts (app)
dependencies {
implementation("com.wirexapp.baas:mobile-sdk:0.3.15")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2") // to call the suspend APIs
}The SDK depends on the Kotlin standard library, kotlinx-coroutines-android and androidx.webkit. The AAR
ships consumer ProGuard/R8 rules and merges the <queries> block Google Pay needs on Android 11 and later
into your manifest — no manual manifest change is required.
Integration Values
| Value | What it is | Where it goes |
|---|---|---|
testMode | false — production, true — the Wirex test environment. Used by the card surfaces | WirexReveal.create, WirexCollect.create, WirexThreeDS(testMode = …) |
| Environment name | The same choice by name: "production" or "test", resolved with WirexWalletEnvironment.resolve(name). Used by the wallet | WirexWalletConfig(environment = …) |
chainId | The chain your integration operates on, sent as X-Chain-Id on every authenticated call. Required for Reveal and Collect | chainId = on create(...) |
clientId | Optional, wallet only. Selects your branding on the Wirex-hosted checkout pages. Leave null unless Wirex assigned one. It is not a credential | WirexWalletConfig(clientId = …) |
The SDK holds the environment hosts. No public API takes a URL. An unknown environment name is an error — the
SDK never falls back to another environment.
Trap:
WirexReveal.create(...)andWirexCollect.create(...)returnnull— they do not throw — when
the build has no endpoints for the environment orchainIdis blank. Call
WirexEnvironment.resolve(name, chainId)to get the reason.
Credential Provider
The SDK never issues credentials. You implement WirexCredentialProvider; the SDK calls it when it needs a
token, so tokens are never pushed in advance.
import com.wirexapp.baas.cards.core.WirexActionScope
import com.wirexapp.baas.cards.core.WirexCredentialProvider
import com.wirexapp.baas.cards.core.WirexDeclined
class AppCredentials(private val session: HostSession) : WirexCredentialProvider {
override suspend fun accessToken(): String = session.freshAccessToken()
override suspend fun actionToken(scope: WirexActionScope): String =
session.stepUpToken(scope) ?: throw WirexDeclined()
override val userEmail: String? get() = null
override val userWallet: String? get() = "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
}| Member | Returns | Used by |
|---|---|---|
accessToken() | The user token from User Token Issuance (Login as User), sent as Authorization: Bearer | Reveal, Collect |
actionToken(scope) | An action_token for scope: CARD_DATA (PAN, expiry, CVV) or PIN. Called once per scope per operation | Reveal |
userEmail | Sent as X-User-Email. null is not sent | Reveal, Collect |
userWallet | Sent as X-User-Wallet. null is not sent | Reveal, Collect |
- Exactly one identity. Return
userEmailoruserWallet, never both. Both set is a400from the API. - The SDK sends the bearer as-is and never retries a
401. Check the token's expiry inside
accessToken()and refresh before returning. A stale token surfaces asWirexErrorwith code
UNAUTHORIZED. - Both action scopes are answered with a
GetCardDetailsaction token. Mint it through
POST /api/v1/confirmation/signature/verifyor the SMS OTP flow in
Card Details. The token is valid for 5 minutes. - Throw
WirexDeclined()fromactionTokenwhen the user backs out. The SDK stops the operation and reports
it as declined, not as an error. - Values are read per call. A refreshed token, a sign-out and a sign-in take effect on the next SDK
request. Keep the provider reading live state rather than a captured copy — no reconfiguration is needed
when the user changes.
Configure Once Per Process
- Create
WirexReveal,WirexCollectandWirexThreeDSonce and keep them; clear the cached instance when
create(...)returnsnull, so a later attempt retries. - Derive
testModefrom your runtime environment when your app switches backends at runtime. - Log at
DEBUGin development builds andWARNin release — see
Logging.
Screen Protection
The SDK sets FLAG_SECURE on the hosting window while a sealed view is attached, and refuses to unmask a
revealed value without it (onRevealRefused). The 3-D Secure challenge applies FLAG_SECURE while it is
visible.
Trap: when a sealed view lives in a
DialogorPopupWindow, the SDK cannot resolve the window on its
own. Pass it withsecureWindow(window), otherwise reveal is refused.
Important:
dangerouslyDisableScreenProtection = trueoncreate(...)— and onWirexThreeDS(...)for
the challenge — turnsFLAG_SECUREoff for automated UI tests and device clouds. It defeats PCI
screen-capture protection. Never ship it enabled.
Quickstart
One credential provider, both card surfaces created once, a top-up card collected and an issued card
revealed. Each surface is covered in detail on its own page.
import android.app.Activity
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import com.wirexapp.baas.cards.collect.WirexBillingDetails
import com.wirexapp.baas.cards.collect.WirexCollect
import com.wirexapp.baas.cards.collect.WirexCollectProfile
import com.wirexapp.baas.cards.collect.WirexTopUpTokenizeParams
import com.wirexapp.baas.cards.core.WirexActionScope
import com.wirexapp.baas.cards.core.WirexCredentialProvider
import com.wirexapp.baas.cards.core.WirexDeclined
import com.wirexapp.baas.cards.reveal.WirexReveal
import com.wirexapp.baas.cards.reveal.WirexRevealField
import com.wirexapp.baas.cards.reveal.ui.WirexRevealFieldView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
interface Auth {
suspend fun freshAccessToken(): String
suspend fun confirmStepUp(): Boolean
suspend fun mintActionToken(): String // GetCardDetails, via POST /api/v1/confirmation/signature/verify
val walletAddress: String
}
class AppCredentials(private val auth: Auth) : WirexCredentialProvider {
override suspend fun accessToken(): String = auth.freshAccessToken()
override suspend fun actionToken(scope: WirexActionScope): String {
if (!auth.confirmStepUp()) throw WirexDeclined()
return auth.mintActionToken()
}
override val userEmail: String? get() = null
override val userWallet: String? get() = auth.walletAddress
}
class CardsScreen(
private val activity: Activity,
auth: Auth,
private val topUp: suspend (externalCardId: String) -> Unit, // your backend: estimate + execute
) {
private val scope = CoroutineScope(Dispatchers.Main)
private val credentials = AppCredentials(auth)
private val collect = WirexCollect.create(credentials = credentials, testMode = true, chainId = "<chain id>")
?: error("no endpoints for this environment, or blank chainId")
private val reveal = WirexReveal.create(credentials = credentials, testMode = true, chainId = "<chain id>")
?: error("no endpoints for this environment, or blank chainId")
private val form = collect.form(context = activity, profile = WirexCollectProfile.TOPUP)
fun mount(container: LinearLayout, cardId: String) {
container.addView(form, MATCH_PARENT, WRAP_CONTENT)
val session = reveal.session(cardId = cardId, fields = listOf(WirexRevealField.PAN), cardholderName = "ALEX GREY")
val panRow = WirexRevealFieldView(activity)
panRow.bind(session, WirexRevealField.PAN)
container.addView(panRow, MATCH_PARENT, WRAP_CONTENT)
scope.launch {
try { session.load() } catch (_: WirexDeclined) { /* the user declined the step-up */ }
}
}
fun addCard() {
scope.launch {
val token = form.tokenizeTopUp(WirexTopUpTokenizeParams(
billingDetails = WirexBillingDetails(line1 = "1 High Street", zipCode = "N1 9GU", city = "London", country = "GB"),
))
topUp(token.cardId)
}
}
}Updated about 3 hours ago

