SDK Setup

Requirements, installation, environments, the credential provider and screen protection for the Android SDK.

Before You Start

Read the following guides before proceeding:

GuideWhy
Android SDKSurfaces, isolation model and flows
AuthenticationThe user token accessToken() returns
Api BasicsX-Chain-Id and the identity headers the SDK sends

Requirements

RequirementValue
Android6.0 (API 23) or later
JVM / KotlinJava 17 bytecode; Kotlin 2.0 or later for a Kotlin host
RepositoriesmavenCentral() for the SDK, google() for its AndroidX dependency
PermissionsThe 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

ValueWhat it isWhere it goes
testModefalse — production, true — the Wirex test environment. Used by the card surfacesWirexReveal.create, WirexCollect.create, WirexThreeDS(testMode = …)
Environment nameThe same choice by name: "production" or "test", resolved with WirexWalletEnvironment.resolve(name). Used by the walletWirexWalletConfig(environment = …)
chainIdThe chain your integration operates on, sent as X-Chain-Id on every authenticated call. Required for Reveal and CollectchainId = on create(...)
clientIdOptional, wallet only. Selects your branding on the Wirex-hosted checkout pages. Leave null unless Wirex assigned one. It is not a credentialWirexWalletConfig(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(...) and WirexCollect.create(...) return null — they do not throw — when
the build has no endpoints for the environment or chainId is 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"
}
MemberReturnsUsed by
accessToken()The user token from User Token Issuance (Login as User), sent as Authorization: BearerReveal, Collect
actionToken(scope)An action_token for scope: CARD_DATA (PAN, expiry, CVV) or PIN. Called once per scope per operationReveal
userEmailSent as X-User-Email. null is not sentReveal, Collect
userWalletSent as X-User-Wallet. null is not sentReveal, Collect
  • Exactly one identity. Return userEmail or userWallet, never both. Both set is a 400 from 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 as WirexError with code
    UNAUTHORIZED.
  • Both action scopes are answered with a GetCardDetails action token. Mint it through
    POST /api/v1/confirmation/signature/verify or the SMS OTP flow in
    Card Details. The token is valid for 5 minutes.
  • Throw WirexDeclined() from actionToken when 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, WirexCollect and WirexThreeDS once and keep them; clear the cached instance when
    create(...) returns null, so a later attempt retries.
  • Derive testMode from your runtime environment when your app switches backends at runtime.
  • Log at DEBUG in development builds and WARN in 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 Dialog or PopupWindow, the SDK cannot resolve the window on its
own. Pass it with secureWindow(window), otherwise reveal is refused.

Important: dangerouslyDisableScreenProtection = true on create(...) — and on WirexThreeDS(...) for
the challenge — turns FLAG_SECURE off 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)
        }
    }
}

Did this page help you?