Gateway Android SDK
Overview
gateway-sdk-android has three package tiers:
com.myriad.gateway.sdk.androidStandard Android wrapper for the plain Gateway admin/pay HTTP clients.com.myriad.gateway.sdk.android.hostThe 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.referencehostOptional 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:
GatewayAndroidConfigGatewayAndroidClient
This is just the Android-friendly shell around the core Kotlin SDK.
Embedded terminal app
Use:
GatewayAndroidAppGatewayAndroidHostRuntimeConfigGatewayAndroidDeviceRuntimeConfigGatewayAndroidHostStateGatewayAndroidUiState
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:
GatewayAndroidReferenceHostGatewayAndroidReferenceHostLauncherGatewayAndroidReferenceHostAppGatewayAndroidDeviceManagementSurface
Package Structure
com.myriad.gateway.sdk.android
Purpose:
- normal Gateway HTTP clients on Android
- lifecycle-friendly wrapper over the core Kotlin SDK
Files:
GatewayAndroidConfig.ktGatewayAndroidClient.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:
GatewayAndroidHostAdapterGatewayAndroidAppSessionGatewayAndroidHostSessionGatewayAndroidHostManagerGatewayAndroidHostIntegration
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.NEXGOGatewayAndroidDeviceFamily.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:
GatewayAndroidHostStateruntime truth: lifecycle, pending recovery, replay backlog, attention levelGatewayAndroidUiStateapp-facing banner and actionable recovery items
Storage and Key Material
The public bootstrap primitives are now real GatewayAndroid* types:
GatewayAndroidStoreLocationStrategyGatewayAndroidDefaultStoreLocationStrategyGatewayAndroidStoragePathsGatewayAndroidKeyProviderGatewayAndroidStaticKeyProviderGatewayAndroidRootKeySourceGatewayAndroidStaticRootKeySourceGatewayAndroidDerivedKeyProviderGatewayAndroidInitializer
That means app code no longer has to import SmartConnect-branded bootstrap types just to configure local storage or encryption keys.
Typical Embedded Runtime Setup
import com.myriad.gateway.sdk.android.host.GatewayAndroidApp
import com.myriad.gateway.sdk.android.host.GatewayAndroidDeviceFamily
import com.myriad.gateway.sdk.android.host.GatewayAndroidDeviceRuntimeConfig
import com.myriad.gateway.sdk.android.host.GatewayAndroidHostRuntimeConfig
import com.myriad.gateway.sdk.android.host.GatewayAndroidStaticRootKeySource
val runtimeConfig =
GatewayAndroidDeviceRuntimeConfig(
family = GatewayAndroidDeviceFamily.NEXGO,
runtime = GatewayAndroidHostRuntimeConfig(filesDir = filesDir),
)
val app =
GatewayAndroidApp.withDeviceRuntime(
deviceRuntimeConfig = runtimeConfig,
rootKeySource = GatewayAndroidStaticRootKeySource(rootKeyBytes),
)
val started = app.ensureStarted()
if (started.canTakePayment) {
// Render payment-ready UI.
}
Managed Host Example
import com.myriad.gateway.sdk.android.host.GatewayAndroidDeviceFamily
import com.myriad.gateway.sdk.android.host.GatewayAndroidDeviceRuntimeConfig
import com.myriad.gateway.sdk.android.host.GatewayAndroidHostRuntimeConfig
import com.myriad.gateway.sdk.android.host.GatewayAndroidStaticRootKeySource
import com.myriad.gateway.sdk.android.host.GatewayAndroidTerminalPlatformInfo
import com.myriad.gateway.sdk.android.referencehost.GatewayAndroidReferenceHost
val referenceHost =
GatewayAndroidReferenceHost.withRootKeySource(
deviceRuntimeConfig =
GatewayAndroidDeviceRuntimeConfig(
family = GatewayAndroidDeviceFamily.TTP,
runtime = GatewayAndroidHostRuntimeConfig(filesDir = filesDir),
),
rootKeySource = GatewayAndroidStaticRootKeySource(rootKeyBytes),
)
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:
GatewayAndroidPendingOperationGatewayAndroidRecoveryInspectionGatewayAndroidRecoveryResolutionGatewayAndroidRecoveryItem
Migration Rule
For any new Android integration:
- import from
hostfor normal runtime/payment work - import from
referencehostonly 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:
GatewayAndroidAppas the primary runtime surfacereferencehostas 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-smokeand//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 three documented adoption paths:
- HTTP-only client via
GatewayAndroidClient/GatewayAndroidConfig. - Embedded terminal runtime via
GatewayAndroidApp.withDeviceRuntime. - Managed host / device-management via
GatewayAndroidReferenceHost.withRootKeySource.
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:
| Destination | What goes there | Version resolution |
|---|---|---|
Google Artifact Registry (us-east1-maven.pkg.dev/pinpoint-gateway/gateway-maven) | gateway-sdk-android-{VERSION}.aar + POM and gateway-sdk-core-{VERSION}.jar + POM (plus the internal shared libs) | Git tag on HEAD → SDK_VERSION_OVERRIDE → fallback in sdks/kotlin/publishing/version.bzl |
| GitHub Release assets | gateway-sdk-android-{VERSION}.aar + .pom, gateway-sdk-core-{VERSION}.jar + .pom, peak-pay-sample-android-{VERSION}.zip, plus iOS equivalents | Same 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 artifact shape is considered final for 0.2.x:
- Maven coordinates:
com.myriad.gateway:gateway-sdk-androidandcom.myriad.gateway:gateway-sdk-core. gateway-sdk-androidis an AAR (Android Archive). The POM declares<packaging>aar</packaging>. AGP and Gradle resolve.aarcoordinates automatically when this packaging type is present.gateway-sdk-coreremains a plain JVM jar (<packaging>jar</packaging>) for the0.2.xrelease lane. The intended follow-on is to convert it to the shared KMP API-client layer that the iOS Swift package can consume as an XCFramework or SwiftPM wrapper, while keeping the Android terminal runtime in the AAR.- 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.
| Area | Gateway owns (this SDK / server) | Monorepo POS app owns |
|---|---|---|
| Payment runtime | Card-present runtime, SmartConnect transport, pending-operation recovery, post-approval server replay | Cart model, receipt rendering, tender selection UI |
| Device enrollment | Registration/heartbeat payloads, managed-host snapshot shaping, device-bound credential contract | Device identity persistence, operator-facing enrollment UX |
| Readiness / health | GatewayAndroidHostState, GatewayAndroidUiState, attention-level model, device-management capability inspection | Mapping readiness → screen/banner placement, error retries at UI level |
| Attestation / device auth | On-device credential shape, refresh/revocation state machine, (future) server issuance endpoints | Lifecycle ownership — when to call ensureValid, which operator triggers enrollment |
| Auth / credentials | OAuth client credentials, Firebase Auth token exchange, future device-bound credential | Secure storage of client secret + root key material, per-operator session |
| Recovery UX copy | Recovery item dispositions, state machine, banner types | Strings, localization, screen flow |
| Card data | Never handled by app layer | Never handled by app layer (SDK owns the wire) |
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.
Device-First Authorization Contract (Peak Pay)
Peak Pay is moving toward a device-first authorization model so that payment and device-management traffic is authenticated by an attested device credential rather than a bearer-token client secret. The SDK-side shape is stable today; the server side is still in progress.
What the SDK ships now
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).GatewayAndroidDeviceCredentialState—ABSENT/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.GatewayAndroidStaticDeviceCredentialSource— in-memory fixture-backed source for tests and pre-server-endpoint integration sprints.
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.
What the server still owes
The SDK-side contract is deliberately shipped ahead of the server work so the monorepo POS app has a stable binding surface. Before this carries real traffic, gateway still needs to land:
- Attestation mint endpoint — accepts Android-device attestation evidence and returns a signed, time-bound device credential.
- Attestation refresh endpoint — accepts an existing credential plus a device-signed proof-of-possession.
- Verifier on the processing/management Cloud Run services —
translates an inbound
Authorization: DeviceAttestation <token>header into the authenticated principal used downstream. - Revocation path wired through device-management so that losing a device or disabling an operator invalidates outstanding credentials.
The kernel-specific attestation evidence and verification anchors continue
to live in //sdks/kotlin/soft-pos-sdk. That module intentionally stays
out of gateway-sdk-android's dependency graph. When the server work
lands, gateway will ship a SoftPosDeviceCredentialSource implementation
of GatewayAndroidDeviceCredentialSource inside soft-pos-sdk — app
code continues to depend only on the Android SDK and does not need to
follow the soft-pos-sdk release cadence.
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. No deprecation of API keys is planned.