SDK Response Codes & Error Handling

Understand how the Koard Android SDK surfaces errors and transaction outcomes — including the KoardException / KoardError types your app catches, transaction response codes, display messages, and error scenarios.

What you learn
  • The two error channels: KoardException (thrown) vs KoardTransactionResponse (emitted)
  • The KoardError and KoardErrorType sealed hierarchy your app inspects for error details
  • How the SDK wraps all underlying Visa KiC errors into Koard types — you never handle raw KiC exceptions
  • The four final transaction statuses: Approve, Decline, Abort, and Failure
  • What statusCode means and the numeric codes the underlying Visa KiC kernel sends
  • Display message IDs shown during the tap-to-pay flow
  • Common abort and error scenarios and how to handle them

Error Model Overview

The SDK surfaces errors through two channels depending on context:

Channel When How What to inspect
KoardException (thrown) Non-transaction operations: enrollment, SDK init, API calls (capture, refund, reverse, tipAdjust), validation try / catch exception.error.errorType — a KoardErrorType sealed class
KoardTransactionResponse (emitted) During sale() / preauth() / refund() tap flows Callback / Flow response.actionStatus, response.finalStatus, response.statusCode

You never handle raw KiC exceptions. The SDK catches every KiCSdkException from the Visa Kernel in the Cloud SDK and maps it to a KoardException with a typed KoardErrorType. Your app only needs to handle Koard types.

KoardException & KoardError

KoardException is the main exception thrown by the SDK for all non-transaction-flow errors. It wraps a KoardError with a human-readable message and a typed error classification:

class KoardException(
    cause: Throwable? = null,
    val error: KoardError
) : Throwable(error.shortMessage, cause)

data class KoardError(
    val shortMessage: String,      // Human-readable error description
    val errorType: KoardErrorType  // Typed error classification (sealed hierarchy)
)

Catching KoardException

try {
    sdk.capture(transactionId, amount)
} catch (e: KoardException) {
    when (e.error.errorType) {
        is KoardErrorType.KoardServiceErrorType.HttpError -> {
            val code = (e.error.errorType as KoardErrorType.KoardServiceErrorType.HttpError).errorCode
            showError("Server error (HTTP $code): ${e.error.shortMessage}")
        }
        is KoardErrorType.KoardServiceErrorType.ConnectionError ->
            showError("Network error — check your connection")
        is KoardErrorType.KoardServiceErrorType.Unauthorized ->
            showError("Session expired — please log in again")
        is KoardErrorType.VACEnrollmentError ->
            showError("Enrollment failed — re-enroll the device")
        else ->
            showError(e.error.shortMessage)
    }
}

KoardErrorType Reference

KoardErrorType is a sealed class hierarchy. Every error the SDK produces maps to one of these types.

Top-Level Error Types

Error Type When It Occurs
GeneralError Catch-all for unmapped or unexpected errors
CertificateError TLS or certificate validation failure
MainThreadError SDK method called on the main thread (must use a worker thread)
NfcTransactionError NFC transaction-level failure
VACEligibilityError Device failed Visa Acceptance Cloud eligibility check (e.g., Android < 12)
DeviceNotProvisionedError Device has not been provisioned for Tap to Pay
VACEnrollmentError Enrollment with the Visa Acceptance Cloud failed

KoardServiceErrorType — API / HTTP Errors

Thrown when SDK methods call the Koard REST API (capture, refund, reverse, tipAdjust, getTransaction, etc.):

Error Type Description
HttpError(errorCode: Int) Server returned an HTTP error — inspect errorCode for the status (400, 401, 404, 500, etc.)
InvalidRequest Request validation failed before sending (e.g., negative amount, missing transaction ID)
NotFound Resource not found (404)
Unauthorized Missing or invalid API key / session (401)
UnexpectedError Unexpected server error or empty response body
ConnectionError Network unreachable, DNS failure, or timeout

DeviceIntegrityError — Security Checks

Thrown when the device fails security validation during enrollment or transaction preparation:

Error Type KiC Code Description
EmulatorDetected 1000 Running on an emulator — use a physical device
RootDetected 1001 Device is rooted or has superuser binaries
TamperDetected 1002 Device tamper detection triggered
DeveloperModeEnabled 2000 Developer options must be disabled
DebugModeEnabled 2001 USB debugging must be disabled
HookDetected 2003 Runtime instrumentation detected (Frida, Xposed, etc.)
GenericIntegrityFailure -1 Generic device integrity attestation failure

TransactionErrorType — Card & Payment Errors

These appear as the errorType on a KoardException when a transaction-level error is mapped from the KiC thin client. They correspond to EMV-level outcomes:

Error Type Description
TransactionAmountNonPositive Amount must be greater than zero
RefundMissingParentTransactionId Refund requires a parent transaction ID
CancelOrEnter Cardholder prompted to cancel or confirm
CardError Unrecoverable card data error
NotAuthorisedOrDeclined Issuer declined the transaction
PinRequired PIN entry is required
IncorrectPin Cardholder entered an incorrect PIN
ProcessingError Generic processing failure
TryAnotherCard Card cannot complete — try a different card
InsertOrSwipe Contactless not supported — use chip or mag-stripe
TryAnotherChoice Try a different payment method
Cancelled Transaction was cancelled
StrongCvm Strong Customer Verification required (SCA)
PinBypassed PIN entry was bypassed
PinNotProvided PIN was requested but not provided
TransactionNotAllowed Transaction type not allowed on this card/terminal
NotApplicable Status not applicable to this transaction type
UnknownStatus Unmapped status from the kernel
TransactionError Generic transaction error
EnableReader NFC reader needs to be enabled
NetworkError Network error during transaction processing
AuthenticationFailed Authentication with the payment backend failed
CouldNotAttestError Device attestation failed during transaction
AsiError Visa auth service interface error
TcConfigError Thin client configuration error
VACResponseFailedError VAC response indicated failure
VACResponseParseError Could not parse VAC response
TransactionInProgressError Another transaction is already in progress
DeviceDisabledError Device has been disabled for transactions
ErrorLoadingConfig Could not load transaction configuration
VACInternalError Internal VAC error
TransactionApprovedUploadFailed Transaction approved but receipt upload failed

KiC Connector Errors

Thrown when the SDK cannot communicate with the Visa Tap to Pay Ready kernel app:

Error Type KiC Code Description
BindingError 92 Failed to bind to the Visa kernel service
ConnectorSendError 93 Sending a message to the kernel failed
KernelParseError 94 Kernel response could not be parsed
ConnectorParseError 95 Connector-side serialization failed
KernelAppNotInstalled 96 Visa kernel app missing — install from Google Play
PlayProtectOrVerifyAppDisabled 97 Google Play Protect must be enabled

KiC General Errors

Error Type KiC Code Description
NoNetworkOrTimedOut 10 Network unavailable or request timed out
UnsupportedAndroidVersion 21 Device OS below Android 12
TapToPayReadyAppUpdateRequired 62 Visa Tap to Pay Ready app is outdated

KiC Eligibility Errors

Error Type KiC Code Description
UnsupportedOs 80 OS build is unsupported
HardwareKeystoreNotPresent 81 No hardware-backed keystore
ECEncryptionNotAvailable 82 Elliptic-curve crypto unavailable
AESEncryptionNotAvailable 83 AES crypto unavailable
DESEncryptionNotAvailable 84 DES crypto unavailable
NfcNotAvailable 85 NFC hardware missing or disabled
GooglePlayServicesNotAvailableOrOldVersion 86 Google Play Services absent or outdated
EligibilityCheckFailed -1 Generic eligibility failure

KiC Initialize Errors

Error Type KiC Code Description
AlreadyEnrolled 1 Device already enrolled — no action needed
DeviceAuthPubKidEmpty 3 Missing device-auth public key — re-enroll
VacDeviceIdEmpty 4 VAC device ID not provided
XRandomValueEmpty 52 Random nonce required by enrollment missing
Failed -1 Generic initialization failure

KiC Prepare Errors

Pre-transaction secure channel setup failures:

Error Type KiC Code Description
AuthenticationFailed 7 VAC authentication failed
SdkInitNotDone 11 init() not completed before use
SdkEnrollNotDone 12 enrollDevice() not completed — re-enroll
ErrorLoadingConfig 17 Could not load config blobs
AsiError 18 Visa auth service interface error
HardwareKeystoreNotPresent 20 Hardware keystore missing during key prep
AttestationFailed 24 Device attestation failed
DoLoginFailed 25 Login exchange with Visa backend failed
NullLoginAssertion 26 Login response missing assertion
NullLoginCrypto 27 Login response missing crypto payload
NullLoginResponse 28 Entire login response was null
NullLoginResponseBody 29 Login HTTP body empty
NullLoginResponseAuthStatus 30 Login response missing auth status
NullSharedSecret 31 Shared secret not derived — re-enroll
NullSessionKeys 32 Session keys missing — re-enroll
FailedResponseVerification 33 MAC/signature mismatch — possible tampering
FailedMacTagVerification 34 MAC tag verification failed
FailedAuthStatus 36 Visa backend rejected authorization
EmptyAuthStatus 37 Auth status element empty
GetSeedListFailure 49 Could not fetch key-rotation seed list
CertificatePinningError 50 TLS pinning failed — possible MITM
TransactionKeyDerivationFailed 51 Could not derive transaction keys — re-enroll
KeyRotationNeeded 87 Kernel requested key rotation (handled automatically)
KeyRotationNotNeeded 88 Key rotation not needed (informational)
KeyRotationSuccess 89 Key rotation completed (informational)
KeyRotationFailure 90 Key rotation failed — re-enroll if transactions fail
KeyRotationNullResponse 91 Kernel did not return rotation status

Transaction Response Flow

Every KoardTransactionResponse emitted by sdk.sale() or sdk.preauth() includes an action status that tells your app what stage the transaction is in. Use this to drive your UI:

Action Status Meaning What to do
OnProgress Transaction is in flight — the reader is active Update your UI with the current displayMessage and readerStatus
OnFailure A non-recoverable error occurred before completion Read the statusCode to determine the failure reason and display an appropriate error
OnComplete The transaction has finished — check finalStatus for the outcome Route to your receipt, decline, or error screen based on finalStatus
when (response.actionStatus) {
    KoardTransactionActionStatus.OnProgress -> {
        showStatus(response.readerStatus.toString(), response.displayMessage)
    }
    KoardTransactionActionStatus.OnFailure -> {
        showError(response.statusCode, response.displayMessage ?: "Transaction failed")
    }
    KoardTransactionActionStatus.OnComplete -> {
        when (response.finalStatus) {
            KoardTransactionFinalStatus.Approve  -> showReceipt(response.transaction!!)
            KoardTransactionFinalStatus.Decline  -> showDeclined(response)
            KoardTransactionFinalStatus.Abort    -> showAborted(response)
            KoardTransactionFinalStatus.Failure  -> showFailure(response)
        }
    }
}

Final Transaction Statuses

When actionStatus is OnComplete, the SDK sets finalStatus to one of these values. These are the only terminal outcomes your app needs to handle:

Final Status Description Typical Cause
Approve Transaction was authorized by the issuer Successful payment — display receipt with approval code and transaction details
Decline Transaction was explicitly declined Issuer denied the authorization, card restricted, insufficient funds, or Strong CVM required (SCA interface switch)
Abort Transaction was terminated before completion User cancelled, PIN entry cancelled, device security issue, NFC read failure, timeout, or network loss
Failure An internal or system-level error prevented the transaction SDK/kernel error, device misconfiguration, or unexpected processing failure
AltService Card requested an alternative service The card network indicated that an alternative acceptance method should be used

The SDK consolidates the underlying processor response into these statuses so your app does not need to interpret raw processor-level codes. The original acquirer responseCode (ISO 8583 field 39) is still available in the transaction receipt for logging and support purposes.

Acquirer Authorization Statuses

Behind the scenes, the acquirer returns a more granular authStatus in the authorization response. The SDK maps these to the final statuses above, but they are available in the transaction details for advanced use cases:

Auth Status Description Maps to Final Status
Approve Issuer approved the transaction Approve
Decline Issuer declined the transaction (also used for internal acquirer errors) Decline
PartialApproval Issuer approved a lesser amount than requested Approve (with reduced authorizedAmount)
InvalidPIN The PIN entered by the cardholder was incorrect Decline
UnableToGoOnline The terminal could not connect to the acquirer for online authorization Decline or Abort
AdditionalInfo Acquirer returned supplementary information (e.g., referral) Varies

Transaction Response Details

When a transaction completes (regardless of outcome), the KoardTransactionResponse contains the following fields:

Field Type Description
transactionId String Unique identifier for the transaction
finalStatus KoardTransactionFinalStatus Terminal outcome: Approve, Decline, Abort, Failure, or AltService
actionStatus KoardTransactionActionStatus Current action phase: OnProgress, OnFailure, or OnComplete
readerStatus KoardReaderStatus Reader state: preparing, readyForTap, cardDetected, processing, complete, etc.
displayMessage String? Human-readable message from the reader/kernel
statusCode Int? Numeric status code from the Visa KiC kernel — see Status Code Reference below
statusCodeDescription String? Human-readable description of the status code (auto-generated from the code)
transaction KoardTransaction? Full transaction object (populated on completion)

Status Code Reference

The statusCode field on KoardTransactionResponse is a numeric integer forwarded from the underlying Visa Kernel in the Cloud (KiC) SDK. These codes are passed through on the transaction response for troubleshooting and logging.

These same codes drive the KoardErrorType mapping. When the SDK catches a KiCSdkException with one of these codes, it maps it to the corresponding KoardErrorType documented in the KoardErrorType Reference above. You don't need to handle numeric codes directly — use KoardErrorType pattern matching instead.

For most apps, routing on actionStatus + finalStatus is sufficient. The statusCode is useful for debugging, logging, and handling edge cases like re-enrollment (12) or developer mode (2000).

The status codes fall into several categories based on what layer of the KiC stack generated them:

Connector Status (92–97) — Service Binding Failures

These indicate problems communicating between the Koard SDK and the Visa Tap to Pay Ready app installed on the device.

Code Description What to do
92 Failed to bind to the Visa kernel service Ensure the Visa Tap to Pay Ready app is installed and up to date
93 Sending a message to the kernel service failed Retry the operation; if persistent, restart both apps
94 Kernel response payload could not be parsed Update the Visa Tap to Pay Ready app
95 Connector-side serialization/deserialization failed Update the Koard SDK to the latest version
96 Visa kernel service app missing on device Install the Visa Tap to Pay Ready app from Google Play
97 Google Play Protect / Verify Apps is disabled Enable Play Protect in Google Play settings

General Status (10, 21, 62) — Environment Readiness

Code Description What to do
10 Network unavailable or SDK request timed out Check network connectivity and retry
21 Device OS level not supported by Tap to Pay Device must run Android 12 (API 31) or later
62 Visa Tap to Pay Ready app is outdated Update the Tap to Pay Ready app from Google Play

Eligibility Status (80–86) — Device Capability Checks

Returned when getKiCEligibility() detects a device hardware or software limitation.

Code Description What to do
80 OS flavor/build is unsupported Device uses an incompatible Android build (e.g., custom ROM)
81 Device lacks a hardware-backed keystore Device does not meet security requirements
82 Elliptic-curve crypto APIs missing or disabled Device crypto hardware insufficient
83 AES crypto acceleration unavailable Device crypto hardware insufficient
84 DES crypto unavailable Device crypto hardware insufficient
85 NFC hardware missing or disabled Enable NFC in device settings, or device has no NFC
86 Google Play Services absent or out of date Install or update Google Play Services

Initialize Status (1, 3, 4, 52) — Enrollment & Bootstrap

Code Description What to do
1 Device already enrolled for Tap to Pay No action needed — the device is already set up
3 Missing device-auth public key identifier Re-run the enrollment flow
4 Merchant/VAC device ID not provided Ensure the SDK is configured with a valid merchant profile
52 Random nonce required by enrollment is missing Re-run the enrollment flow

Security Status (1000–2003) — Device Integrity

Code Description What to do
1000 Emulator detected Tap to Pay cannot run on emulators — use a physical device
1001 Device rooted or superuser binaries present Device must not be rooted
1002 Device tamper detection triggered Device has been modified and is not trusted
2000 Developer options must be disabled Disable developer mode before running transactions
2001 USB debugging/logging must be disabled Turn off USB debugging in developer options
2003 Runtime hook/instrumentation detected Remove any instrumentation frameworks (Frida, Xposed, etc.)

Prepare Status (7–91) — Pre-Transaction Secure Channel

These codes occur during startUpSdk() or when the SDK prepares for a transaction. They relate to the secure channel between the device and the Visa backend.

Code Description What to do
7 VAC authentication call to Visa failed Check API credentials and network connectivity
11 init() not completed before use Complete SDK initialization before starting transactions
12 enrollDevice() not completed Device needs enrollment — show the enrollment UI and re-enroll. This also occurs if the Tap to Pay Ready app was reinstalled
17 Could not load enrollment/transaction config blobs Re-initialize the SDK
18 ASI (Visa auth service interface) returned error Transient backend issue — retry
20 Hardware keystore missing when preparing keys Device does not meet security requirements
24 Device attestation failed or invalid Re-enroll the device; ensure Play Protect is enabled
25 Login exchange with Visa backend failed Check network; retry
26 Login response missing assertion blob Transient backend issue — retry
27 Login response missing crypto payload Transient backend issue — retry
28 Entire login response was null Transient backend issue — retry
29 Login HTTP body empty Transient backend issue — retry
30 Login response missing auth status Transient backend issue — retry
31 Shared secret not derived Re-enroll the device
32 Session keys missing Re-enroll the device
33 MAC/signature mismatch in response Possible tampering — re-enroll the device
34 MAC tag verification failed Possible tampering — re-enroll the device
36 Visa backend explicitly rejected authorization Check merchant configuration with Koard support
37 Auth status element empty Transient backend issue — retry
49 Could not fetch key-rotation seed list Check network connectivity
50 TLS pinning check failed Possible man-in-the-middle — check network security
51 Could not derive transaction keys Re-enroll the device

Key Rotation Status (87–91)

Code Description What to do
87 Kernel requested key rotation SDK handles this automatically — no action needed
88 Key rotation already satisfied (not needed) Informational — no action needed
89 Key rotation completed successfully Informational — no action needed
90 Key rotation failed Re-enroll the device if transactions fail
91 Kernel did not return key rotation status Re-enroll the device if transactions fail

Transaction Status — In-Progress Codes

These codes appear during an active transaction and are reflected in the readerStatus field:

Code Reader Status Description
109 readyForTap POS state started — reader is waiting for a card tap
112 preparing POS state message — reader is preparing for the transaction
106 readCompleted Card read completed successfully

Generic Failure (-1)

A status code of -1 indicates a generic failure. The SDK uses the statusCodeDescription field to provide more context:

Description Contains Meaning
"eligibility" Generic eligibility evaluation failure
"initialise" or "enrol" Generic initialization or enrollment failure
"integrity" or "attestation" Device integrity attestation failed

Status code 12 (enrollment not done) deserves special handling. In the demo app, the SDK treats statusCode == 12 during OnFailure as a recoverable state — it shows a status message rather than an error because re-enrollment can be triggered automatically. If the Tap to Pay Ready app is reinstalled or clears its data, the first transaction attempt will return code 12, and the merchant app should clear its stored enrollment data and re-run the enrollment flow.

Display Message IDs

During the tap-to-pay flow, the SDK emits display messages via displayMessage on each OnProgress event. These correspond to standard EMV message identifiers from the kernel:

Message ID Description
Approved 03 Authorization obtained — transaction approved
Cancel or Enter 05 Prompt to cancel or confirm
Card Error 06 Unrecoverable card data error
Not Authorized / Declined 07 Transaction was declined by the issuer
Please remove card 10 Card not yet removed from the reader field
Please try again 13 Recoverable error — retry the tap
Welcome 14 Idle state — reader is ready
Present card 15 Prompt the cardholder to tap
Processing 16 Transaction is being processed
Card read OK / Remove card 17 Card was read successfully — may be removed
Please insert or swipe card 18 Contactless not supported — try contact/mag-stripe
Please present one card only 19 Card collision detected — present only one card
Approved. Please Sign 1A Approved; signature required
Authorizing. Please Wait 1B Online authorization in progress
Insert, swipe, or try another card 1C Contactless failed — use another interface or card
Please insert card 1D Chip card should be inserted into the slot
(Empty string) 1E Clear the display
See Phone for instructions 20 Mobile device CVM required (Touch ID, Face ID, etc.)
Present card again 21 Recoverable error — present the card again
Practice Mode 40 Successful test/practice transaction
Partial Approval 43 Issuer approved a lesser amount
Cancelled for Device Security 46 Transaction cancelled due to a device security issue
Cancelled 47 Generic transaction cancellation
Try another card - No contact interface 48 Card returned GPO error (SW 6984) — transaction aborted
Strong CVM 49 SCA issuer response requires interface switch — transaction declined

Abort and Error Scenarios

The SDK returns an Abort or Failure final status in several well-defined situations. Understanding these helps you build robust error handling:

Transaction Abort Scenarios

Scenario What Happens Message ID
User cancels PIN entry User selects "Cancel Transaction" on the PIN keypad
PIN session timeout 1 minute of inactivity on the PIN keypad
PIN keypad interrupted Another app covers the PIN screen
Network loss during PIN Network drops before the PIN event is sent
Split screen mode Device enters split screen while on PIN screen — sends CVEntrySecurity cancel
Device security issue Security configuration problem detected 46
Generic cancellation User or system cancelled the transaction 47
Card NFC failure (GPO 6984) Card cannot complete contactless — abort with MACompletion indicator 48
Strong CVM / SCA switch Issuer requires contact interface (not supported) — decline with MACompletion 49
Developer mode enabled Developer options are on — reader blocks the transaction

OnFailure Status Codes

When actionStatus is OnFailure, check statusCode for the specific reason:

Status Code Constant Description
TRANSACTION_WINDOW_FOCUS_CHANGED The transaction window lost focus (another app came to foreground)
CAMERA_IS_ACTIVE Device camera is active — conflicts with the secure NFC session
DEVELOPER_MODE_ENABLED Developer options are enabled on the device
NFC_NOT_AVAILABLE Device NFC is disabled or unavailable
DEVICE_NOT_ENROLLED Device has not completed enrollment
SESSION_TIMEOUT The transaction session timed out

Developer Mode: The most common cause of unexpected transaction failures during development. Always disable developer mode before running transactions. Follow the workflow: Enable dev mode → Install app → Disable dev mode → Run transactions.

Completion Indicators

The receipt field emv.tx.tm.CompletionIndicator tells you how the transaction concluded at the kernel level:

Value Meaning
FullCompletion Transaction completed normally through the full authorization flow
MACompletion Transaction was terminated by the kernel (Merchant Application completion) — typically an abort or forced decline

Handling Responses in Practice

The SDK uses two error channels. Here is a complete pattern for handling both:

Channel 1: Transaction Flow (KoardTransactionResponse)

For sale(), preauth(), and tap-based refund() — errors come via the response callback:

private fun handleTransactionEvent(response: KoardTransactionResponse) {
    when (response.actionStatus) {
        KoardTransactionActionStatus.OnProgress -> {
            // Update UI with reader status and display message
            updateUI(
                status = response.readerStatus.toString(),
                message = response.displayMessage ?: "Processing..."
            )
        }

        KoardTransactionActionStatus.OnFailure -> {
            // Check for re-enrollment scenario
            if (response.statusCode == 12) {
                // Tap to Pay Ready app was reinstalled or cleared data
                // Clear stored enrollment info and re-run enrollment
                triggerReEnrollment()
                return
            }

            // Transaction could not proceed — show the reason
            val reason = buildString {
                append("Transaction Failed")
                response.displayMessage?.let { append("\n\n$it") }
                response.statusCodeDescription?.let { append("\n\n$it") }
                response.statusCode?.let { append("\n\nStatus Code: $it") }
            }
            showError(reason)
        }

        KoardTransactionActionStatus.OnComplete -> {
            when (response.finalStatus) {
                KoardTransactionFinalStatus.Approve -> {
                    showReceipt(response.transaction!!)
                }
                KoardTransactionFinalStatus.Decline -> {
                    showDeclined(response.displayMessage ?: "Transaction declined")
                }
                KoardTransactionFinalStatus.Abort -> {
                    showAborted(response.displayMessage ?: "Transaction aborted")
                }
                KoardTransactionFinalStatus.Failure -> {
                    showError(response.displayMessage ?: "Transaction failed")
                }
                is KoardTransactionFinalStatus.Unknown -> {
                    showError("Unexpected status: ${response.finalStatus}")
                }
            }
        }

        else -> Unit
    }
}

Channel 2: API & SDK Operations (KoardException)

For capture(), reverse(), refundTransaction(), tipAdjust(), enrollDevice(), and other non-tap operations — errors are thrown as KoardException:

private suspend fun capturePayment(transactionId: String, amount: Int) {
    try {
        val result = sdk.capture(transactionId, amount)
        showReceipt(result)
    } catch (e: KoardException) {
        when (val errorType = e.error.errorType) {
            // HTTP errors from the Koard API
            is KoardErrorType.KoardServiceErrorType.HttpError ->
                showError("Server error (HTTP ${errorType.errorCode}): ${e.error.shortMessage}")
            is KoardErrorType.KoardServiceErrorType.ConnectionError ->
                showError("Network error — check your connection and retry")
            is KoardErrorType.KoardServiceErrorType.Unauthorized ->
                showError("Session expired — please log in again")
            is KoardErrorType.KoardServiceErrorType.NotFound ->
                showError("Transaction not found")
            is KoardErrorType.KoardServiceErrorType.InvalidRequest ->
                showError("Invalid request: ${e.error.shortMessage}")

            // Device integrity failures
            is KoardErrorType.DeviceIntegrityError.DeveloperModeEnabled ->
                showError("Disable developer mode before processing payments")
            is KoardErrorType.DeviceIntegrityError ->
                showError("Device security check failed: ${e.error.shortMessage}")

            // Enrollment issues
            is KoardErrorType.VACEnrollmentError ->
                promptReEnrollment(e.error.shortMessage)
            is KoardErrorType.VACEligibilityError ->
                showError("Device not eligible for Tap to Pay: ${e.error.shortMessage}")

            // KiC connector/kernel errors
            is KoardErrorType.KicConnectorError.KernelAppNotInstalled ->
                showError("Install the Visa Tap to Pay Ready app from Google Play")
            is KoardErrorType.KicConnectorError ->
                showError("Kernel communication error: ${e.error.shortMessage}")
            is KoardErrorType.KicPrepareError.SdkEnrollNotDone ->
                promptReEnrollment("Device needs re-enrollment")

            // Fallback
            else -> showError(e.error.shortMessage)
        }
    }
}

Next Steps

  • Review the Running Payments guide for the complete payment flow implementation
  • See the Demo App for a working example of response handling in MainScreenViewModel
  • Consult the API Response Codes for HTTP-level status codes from the Koard REST API