Skip to main content

Kotlin SDK

Android consumers use the self-contained gateway-sdk-android AAR instead of adding core artifacts alongside it; duplicate classes are not a supported installation. The optional NearPay AAR is additive only for that rail.

Clients and authentication

GatewayPayClient owns payment-facing operations and GatewayAdminClient organization administration. Both use GatewayConfig and suspend APIs. Machine client credentials belong on a trusted backend, not in a distributed app. Mobile apps should obtain an authorized token from their backend; managed terminal enrollment uses the Android SDK.

import com.myriad.gateway.Environment
import com.myriad.gateway.GatewayConfig
import com.myriad.gateway.GatewayPayClient

val pay = GatewayPayClient(
    GatewayConfig(
        clientId = clientId,
        clientSecret = clientSecret,
        environment = Environment.STAGING,
    ),
)
// Use pay for authorized payment work; close it when its owner shuts down.

SDK-managed OAuth caches and refreshes tokens. A pre-issued bearer token uses accessToken; its issuing layer owns validity. See Authentication and Organizations and Locations for scopes and tenant selection.

Integration choices

Use the SDK request types and API reference rather than constructing vendor requests. Monetary fields are integer minor units where named as amounts; tax rates use basis points.

Durable checkout creation

pay.checkoutSessions.create(request, idempotencyKey) requires a nonblank key of at most 64 characters. Persist the key, original location and request before dispatch. Recover a lost response with the authenticated pay.checkoutSessions.getByKey(idempotencyKey, locationId) suspend operation. It returns the existing session's current state without repricing or reopening it.

The authenticated organization, location and key form the durable identity. The original request fingerprint is frozen: identical input replays before mutable capability, policy and tax validation; changed input returns HTTP 409. Only GatewayException.NotFoundError establishes absence at the lookup read, not that a delayed original create cannot commit. Network, authorization and contract failures do not establish absence. Any recovery retry must use the original key and body; only a genuinely new business attempt uses a new persisted identity.

For pre-cutover requests, the guarantee covers retained legacy successes only after verified adoption of their original session. Adoption freezes the current full typed body; the old sanitized hash cannot prove excluded original fields. Legacy in-progress entries fail closed, and purged legacy identities cannot be recovered by key alone. Rollout requires an in-flight census and fence; see existing identities at cutover.

Outcome handling

Distinguish explicit approval, explicit decline, and failure/unknown results. Catch the SDK's typed authentication, rate-limit, network, server, validation, and not-found errors. Network failure is not evidence that a payment was not executed. Preserve the operation identity and reconcile an indeterminate result before any authorized follow-on operation. Generic transport retries do not make non-idempotent terminal or payment commands safe to resubmit.

INTERNAL_STATE_ERROR is a server-side invariant failure (HTTP 500). The SDK preserves its gatewayErrorCode on GatewayException.ServerError and does not retry it, including raw responses and bounded sync attempts. Non-retryable does not mean unexecuted: Android retains durable reconciliation evidence and requires an authoritative lookup before acknowledging an uncertain recording, refund, or reversal. Cancellation and recording redispatch remain blocked for that operation across restart and recovery. SoftPOS sync and enrichment do not schedule another automatic attempt. Genuine state conflicts remain HTTP 409.

Verify webhooks against the raw body with WebhookVerifier.verifyDelivery and the signature/timestamp headers. Keep only safe event/reference/correlation IDs in logs and handle duplicate delivery idempotently.

The decoded WebhookEvent has one variant per Gateway event type (payment.completed → WebhookEvent.PaymentCompleted), holding the delivery envelope and the event payload. An event type newer than the SDK decodes to WebhookEvent.Unknown with the whole body in raw, so give every when (event) an else or is WebhookEvent.Unknown branch.