Skip to main content

Gateway Android SDK

Overview

gateway-sdk-android has three package tiers:

  • com.myriad.gateway.sdk.android Standard Android wrapper for the plain Gateway admin/pay HTTP clients.
  • com.myriad.gateway.sdk.android.host The primary terminal/runtime SDK surface for apps that need to support both Nexgo and Tap to Pay without caring that SmartConnect exists internally.
  • com.myriad.gateway.sdk.android.referencehost Optional managed-host tooling for registration, heartbeat shaping, workspace snapshots, and operator-facing device-management flows.

Everything under com.myriad.gateway.sdk.android.smartconnect is internal implementation detail and should not be imported by app code.

Choose Your Path

HTTP-only Android app

Use:

  • GatewayAndroidConfig
  • GatewayAndroidClient

This is just the Android-friendly shell around the core Kotlin SDK.

Embedded terminal app

Use:

  • GatewayAndroidApp
  • GatewayAndroidHostRuntimeConfig
  • GatewayAndroidDeviceRuntimeConfig
  • GatewayAndroidHostState
  • GatewayAndroidUiState

This is the default integration path for a POS app that needs one API surface for both Nexgo and Tap to Pay.

Managed host / device-management app

Use referencehost only if the app also needs:

  • device registration payloads
  • heartbeat payloads
  • workspace snapshots
  • command or operator-facing host tooling

Primary types:

  • GatewayAndroidReferenceHost
  • GatewayAndroidReferenceHostLauncher
  • GatewayAndroidReferenceHostApp
  • GatewayAndroidDeviceManagementSurface

Package Structure

com.myriad.gateway.sdk.android

Purpose:

  • normal Gateway HTTP clients on Android
  • lifecycle-friendly wrapper over the core Kotlin SDK

Files:

  • GatewayAndroidConfig.kt
  • GatewayAndroidClient.kt

com.myriad.gateway.sdk.android.host

Purpose:

  • public terminal/runtime API
  • public state, snapshot, config, transaction, recovery, and bootstrap models

The main entrypoint is GatewayAndroidApp.

Important model groups:

  • runtime and device selection
  • host state and UI state
  • recovery and replay inspection
  • transport and bootstrap primitives

com.myriad.gateway.sdk.android.referencehost

Purpose:

  • managed-host and device-management tooling
  • registration and heartbeat request shaping
  • workspace and operator snapshot helpers

This package is optional. Treat it as an additive tooling layer, not the default way to start taking payments.

The public device-management requirement models also live in gateway-sdk-android itself, so Android consumers do not need a separate soft-pos-sdk artifact just to inspect readiness, attention level, or queued device-management work.

com.myriad.gateway.sdk.android.host.internal

Purpose:

  • seam/orchestration layer behind the public API

These classes exist to keep the public package small:

  • GatewayAndroidHostAdapter
  • GatewayAndroidAppSession
  • GatewayAndroidHostSession
  • GatewayAndroidHostManager
  • GatewayAndroidHostIntegration

They should not be part of the integration story.

com.myriad.gateway.sdk.android.smartconnect

Purpose:

  • runtime implementation
  • transport
  • recovery persistence
  • replay durability
  • protocol-specific storage and state

Apps should not import this package directly.

Core Concepts

GatewayAndroidHostRuntimeConfig

This is the base runtime wiring:

  • filesDir
  • transport
  • storage location strategy
  • terminal platform info collector
  • recovery backfill client
  • clock / operation id hooks
  • gateway name

The default terminalPlatformInfoCollector best-effort gathers Android device metadata for device-management payloads, so apps do not need to hand-build the full terminal profile just to register a device or send heartbeats.

GatewayAndroidDeviceRuntimeConfig

This is how the app declares device family:

  • GatewayAndroidDeviceFamily.NEXGO
  • GatewayAndroidDeviceFamily.TTP

That config resolves into GatewayAndroidDeviceRuntimeSelection, which keeps the selected runtime label and gateway name consistent.

GatewayAndroidApp

This is the main product surface.

It owns:

  • startup / reload
  • transaction execution
  • pending-operation recovery
  • offline server replay queue management
  • app snapshot/state projection

GatewayAndroidAppSnapshot

This is the “what should the app render right now?” model.

It includes:

  • runtime readiness
  • selected device family/runtime
  • recovery inspection
  • replay backlog state
  • terminal-device snapshot
  • device-management snapshot when applicable

GatewayAndroidTerminalPlatformInfo

This is now primarily an override model, not a required full payload.

The SDK auto-collects best-effort Android metadata for registration, heartbeat, and managed-host snapshots:

  • manufacturer
  • model
  • Android version
  • screen resolution
  • timezone
  • battery / charging status when available
  • network type when available

If the app wants to override specific fields, pass only those fields in GatewayAndroidTerminalPlatformInfo; the SDK merges them on top of the collected metadata.

GatewayAndroidHostState and GatewayAndroidUiState

Use these for app rendering:

  • GatewayAndroidHostState runtime truth: lifecycle, pending recovery, replay backlog, attention level
  • GatewayAndroidUiState app-facing banner and actionable recovery items

Storage and Key Material

The public bootstrap primitives are now real GatewayAndroid* types:

  • GatewayAndroidStoreLocationStrategy
  • GatewayAndroidDefaultStoreLocationStrategy
  • GatewayAndroidStoragePaths
  • GatewayAndroidKeyProvider
  • GatewayAndroidRootKeySource
  • GatewayAndroidDerivedKeyProvider
  • GatewayAndroidInitializer

That means app code no longer has to import SmartConnect-branded bootstrap types just to configure local storage or encryption keys. The static key/root-key fixtures live in the unpublished :gateway-sdk-android-testing library and are not part of the published AAR; production key material is the Keystore-backed provider the managed entry point wires by default.

Managed Production Runtime Setup

Production hosts integrate through one approved entry point: GatewayAndroidApp.withManagedRuntime(...). Gateway Cloud selects the runtime family (NEXGO, TTP_NATIVE, TTP_NEARPAY); the SDK owns device enrollment (Play Integrity + Android Keystore proof-of-possession), credential storage/refresh, key material, and client authorization. The host never branches on the device family and never handles raw credential tokens.

import com.myriad.gateway.sdk.android.cardpresent.deviceauth.GatewayAndroidManagedCredentialConfig
import com.myriad.gateway.sdk.android.host.GatewayAndroidApp
import com.myriad.gateway.sdk.android.host.GatewayAndroidHostCapabilities

// withManagedRuntime and ensureStarted are suspend functions — call them from a
// coroutine (e.g. lifecycleScope.launch { ... } at app start).
suspend fun startGateway(): GatewayAndroidApp {
val app =
GatewayAndroidApp.withManagedRuntime(
host = GatewayAndroidHostCapabilities(applicationContext) { currentActivity },
gatewayClient = gatewayClient, // backend-vended bearer tokenProvider
credentialConfig =
GatewayAndroidManagedCredentialConfig(
organizationId = "org_...",
cloudProjectNumber = GCP_PROJECT_NUMBER, // Play Integrity, from build config
),
) // NearPay deployments just bundle gateway-sdk-android-nearpay — the SDK
// discovers its runtime module automatically when Gateway Cloud selects it

val started = app.ensureStarted()
if (started.canTakePayment) {
// Render payment-ready UI.
}
return app
}

Lab / Test Fixture Setup (never production)

Lower-level runtime constructors have been removed from the public SDK surfaceGatewayAndroidApp.withManagedRuntime(...) is the only way to create a runtime. In-repo lab and test fixtures reach the SDK-internal constructors through Bazel associates (friend-module) access, and the static credential/root-key sources live in the separate, unpublished :gateway-sdk-android-testing library. Neither is reachable from a consumer app: there is no opt-out.

Managed Host Example

Managed-host tooling wraps an existing managed app — it is not a second way to construct a runtime:

import com.myriad.gateway.sdk.android.host.GatewayAndroidTerminalPlatformInfo
import com.myriad.gateway.sdk.android.referencehost.asReferenceHost

val referenceHost = app.asReferenceHost() // app from GatewayAndroidApp.withManagedRuntime(...)

val started = referenceHost.ensureStarted()
val registration = started.registrationRequest(deviceId = "device-123")

val managedHost =
started.managedHostSnapshot(
deviceId = "device-123",
platformInfo = GatewayAndroidTerminalPlatformInfo(deviceName = "Front Counter"),
)

Recovery Model

The Android runtime supports two operational recovery concepts:

  • pending-operation recovery
  • offline server replay queue flushing

It does not expose a fake offline terminal authorization mode. The public API is intentionally explicit about that boundary.

Key public types:

  • GatewayAndroidPendingOperation
  • GatewayAndroidRecoveryInspection
  • GatewayAndroidRecoveryResolution
  • GatewayAndroidRecoveryItem

Migration Rule

For any new Android integration:

  • import from host for normal runtime/payment work
  • import from referencehost only for device-management tooling
  • do not import from smartconnect

If app code knows smartconnect exists, the package boundary is being used incorrectly.

Sample Path

The shipped sample lives under com.myriad.gateway.sdk.android.peakpay.

It demonstrates:

  • GatewayAndroidApp as the primary runtime surface
  • referencehost as optional managed-host tooling layered on top
  • device-management payloads built from SDK-collected Android metadata with optional app overrides

Consumer Smoke Target

In addition to the sample, gateway-sdk-android ships a consumer-path smoke target that models what a downstream monorepo POS app would look like when adopting the published artifact:

  • source: sdks/kotlin/gateway-sdk-android/src/consumerSmoke
  • tests: sdks/kotlin/gateway-sdk-android/src/consumerSmokeTest
  • Bazel targets: //sdks/kotlin/gateway-sdk-android:consumer-smoke and //sdks/kotlin/gateway-sdk-android:consumer-smoke-test

This module depends only on :gateway-sdk-android (the same artifact we publish), does not depend on soft-pos-sdk, and does not import anything from the smartconnect or host.internal packages. If a public-surface rename accidentally leaks an internal type onto an app-facing API, the consumer smoke target stops compiling in CI.

Consumer smoke covers the documented adoption paths:

  1. HTTP-only client via GatewayAndroidClient / GatewayAndroidConfig.
  2. Managed production runtime via GatewayAndroidApp.withManagedRuntime — the approved production path.
  3. Embedded terminal + managed-host lab paths (exercised only from src/consumerSmokeTest, which uses SDK-internal constructors via Bazel friend-module access — these are not part of the public surface).

Run it locally with bazel test //sdks/kotlin/gateway-sdk-android:consumer-smoke-test.

Release Wiring

The Android SDK ships from a single workflow, sdk-publish.yml. A release lands artifacts in two places:

DestinationWhat goes thereVersion resolution
Google Artifact Registry (us-east1-maven.pkg.dev/pinpoint-gateway/gateway-maven)gateway-sdk-android-{VERSION}.aar + POM, gateway-sdk-core-kmp-{VERSION}.jar + POM, and gateway-sdk-core-kmp-android-{VERSION}.aar + POMGit tag on HEAD → SDK_VERSION_OVERRIDE → fallback in sdks/kotlin/publishing/version.bzl
GitHub Release assetsgateway-sdk-android-{VERSION}.aar + .pom, gateway-sdk-core-kmp-{VERSION}.jar + .pom, gateway-sdk-core-kmp-android-{VERSION}.aar + .pom, plus iOS equivalentsSame resolution. POMs are rewritten with the resolved version before attach so they match what Artifact Registry served.

Consumers using Gradle (with rules_jvm_external or the standard Gradle dependency block) against Artifact Registry get the Maven-standard AAR layout and can resolve the full dependency closure (Android depends on core at the same version). Consumers pulling from the GitHub Release get the exact same closure in a single download — no separate Artifact Registry auth required for bring-up.

The current artifact shape is:

  • Maven coordinates: com.myriad.gateway:gateway-sdk-android, com.myriad.gateway:gateway-sdk-core-kmp, and com.myriad.gateway:gateway-sdk-core-kmp-android.
  • gateway-sdk-android is an AAR (Android Archive). The POM declares <packaging>aar</packaging>. AGP and Gradle resolve .aar coordinates automatically when this packaging type is present.
  • gateway-sdk-core-kmp is the JVM jar for the shared KMP API-client layer. gateway-sdk-core-kmp-android is the matching Android core AAR used by gateway-sdk-android.
  • No Maven classifier, no sources jars in the release bundle.

Ownership Boundaries

This SDK draws a sharp boundary between what gateway owns and what the monorepo POS app owns. Treat the table below as the single source of truth when adding a new capability.

AreaGateway owns (this SDK / server)Monorepo POS app owns
Payment runtimeCard-present runtime, SmartConnect transport, sale/auth/capture/refund/void/tip-adjust mapping, timeout/receipt/processor/terminal config, pending-operation recovery, post-approval server replayCart model, receipt rendering, tender selection UI
Device enrollmentRegistration/heartbeat payloads, managed-host snapshot shaping, device-bound credential contractDevice identity persistence, operator-facing enrollment UX
Readiness / healthGatewayAndroidHostState, GatewayAndroidUiState, attention-level model, device-management capability inspectionMapping readiness → screen/banner placement, error retries at UI level
Attestation / device authOn-device credential shape, live Play Integrity/Nexgo enrollment and refresh transport, server issuance endpointsLifecycle ownership — when to call ensureValid, which operator triggers enrollment
Auth / credentialsOAuth client credentials, Firebase Auth token exchange, device-bound credential issuance and refreshSecure storage of client secret + root key material, per-operator session
Recovery UX copyRecovery item dispositions, state machine, banner typesStrings, localization, screen flow
Card dataNever handled by app layerNever handled by app layer (SDK owns the wire)
Nexgo Cloud / XTMSMerchant/device provisioning, terminal binding, SmartConnect parameter pushes, credential rotation, push status, Gateway runtime commandsMDM/vendor-cloud administration outside explicit Gateway commands

Rules of thumb:

  • If a change affects wire format, network retry semantics, or the persistence of recovery/replay state, it belongs in the SDK.
  • If a change affects user-facing copy, navigation, or cross-feature orchestration (tax, split-tender, receipts), it belongs in the monorepo POS app.
  • If both sides need to change, the SDK lands the neutral contract first and the app adopts it — never the other way around.
  • XTMS app push, firmware/OTA, RKI/certificate distribution, Easy Deploy templates, and fleet MDM functions are not POS app responsibilities and are not generic SDK APIs. They remain vendor-cloud/operations functions unless Gateway exposes them as explicit runtime commands: runtime wipe, credential rotation, reconciliation trigger, SDK state reset, payment-runtime reprovision, SmartConnect prepare, or SmartConnect status.

Device-First Authorization Contract (Peak Pay)

Peak Pay uses a device-first authorization model: payment and device-management traffic is authenticated by an attested device credential rather than a bearer-token client secret. Both the SDK and the server sides are shipped.

What the SDK ships

Contract file: sdks/kotlin/gateway-sdk-android/src/main/kotlin/com/myriad/gateway/sdk/android/host/GatewayAndroidDeviceAuth.kt

Public types:

  • GatewayAndroidDeviceCredential — the on-device credential shape (token, attestation id, device id, issued/expires, optional refresh hint).
  • GatewayAndroidDeviceCredentialStateABSENT / VALID / REFRESH_DUE / EXPIRED / REVOKED.
  • GatewayAndroidDeviceCredentialSnapshot — snapshot the SDK hands to the app for UI/readiness binding.
  • GatewayAndroidDeviceCredentialSource — the pluggable surface through which the SDK obtains and refreshes credentials.

The production implementation is SDK-owned and assembled by GatewayAndroidManagedDeviceCredentialSource.create(...) (cardpresent.deviceauth package), or implicitly by GatewayAndroidApp.withManagedRuntime(host, ...):

  • Android Keystore P-256 proof-of-possession signer (private key never leaves secure hardware)
  • Play Integrity standard-API attestation (server nonce bound via requestHash)
  • core-client transport against the auth-service device endpoints (/api/v1/devices/enroll[/nonce], /api/v1/devices/{id}/token/*)
  • AES-256-GCM encrypted durable credential storage keyed by an Android Keystore key — the app never persists raw credential tokens, expiry fields, or activation seed data

GatewayAndroidStaticDeviceCredentialSource and the stub attestation/signing providers are test/local fixtures onlyProductionSourceGuardTest enforces that production SDK code never references them. Never pass a device credential token as GatewayConfig.accessToken; GatewayAndroidClient fails fast if one is detected there.

Apps can bind GatewayAndroidDeviceCredentialSnapshot.state into the same readiness / capability surface they already use for GatewayAndroidHostState — REVOKED/EXPIRED stop payment just like an outstanding operator recovery does.

The kernel-specific attestation evidence and verification anchors continue to live in //sdks/kotlin/soft-pos-sdk, which intentionally stays out of gateway-sdk-android's dependency graph.

Nexgo terminal activation diagnostics

Normal Android enrollment requires Play Integrity. Gateway-managed Nexgo terminal enrollment is the exception: GatewayAndroidLiveDeviceCredentialSource automatically sends attestationMode = "NEXGO_TERMINAL" when the enrollment uses platform = "ANDROID", linkedCardPresentProvider = "NEXGO", and a Nexgo/XTMS terminal serial in linkedCardPresentSerialNumber. Raw API callers can send the same mode explicitly. The request must also include a Gateway locationId. Gateway then validates that the requested location belongs to the organization and that an active transit_devices inventory row binds the terminal to that same location.

Attestation-policy enrollment rejections return the generic 401 device-attestation failure payload. The detailed reason is logged server-side with one of these codes:

  • PLAY_INTEGRITY_REQUIRED — the request used the normal platform path without attestation evidence. This is expected for non-terminal Android devices and for Nexgo clients that did not opt into terminal activation.
  • NEXGO_MISSING_LOCATION — the request omitted locationId, or Gateway cannot resolve a merchant/location row for it.
  • NEXGO_MISSING_XTMS_BINDING — Gateway has no active Nexgo/XTMS-backed transit_devices inventory row for the submitted terminal serial.
  • NEXGO_MERCHANT_LOCATION_MISMATCH — the location is outside the requested organization, inactive, or the terminal inventory row is bound to another location.
  • NEXGO_UNSUPPORTED_TERMINAL_TYPE — the request is not Android/Nexgo, or the inventory row is for a non-terminal device type.

API-key / OAuth coexistence

The device-first path is additive. API-key and OAuth client-credentials authentication remain supported on the Android SDK; the device credential is layered on top for card-present Peak Pay flows that the processor requires a device-bound token for. API keys remain supported for the Android SDK flows that use them today.