SDK Error Codes

Use this reference to handle errors from KoardSDK 1.0.21, the published iOS release. The cases below are exported by its KoardSDK.xcframework and available with import KoardSDK. For recovery steps, see Troubleshooting.

Reading an SDK Error

The iOS wrapper is KoardMerchantSDKError. It conforms to KoardDescribableError and provides an errorDescription string. Switch on the enum case to decide how your app recovers. There is no public numeric code or errorCode property on this enum, and bridging it to NSError does not produce a documented Koard code.

Error information Where to read it How to use it
Koard classification KoardMerchantSDKError case and nested case Choose the recovery action
Description error.errorDescription Explain the failure; do not parse it as a stable code
Associated detail message, description, or other associated value Retain relevant, redacted diagnostic context
Underlying error, when supplied Associated Error on the cases listed below Inspect its native type or NSError.domain and code for diagnostics; these belong to that underlying error domain

A thrown error and a transaction outcome are different. A returned response can contain a declined, canceled, timed-out, or errored transaction without throwing. Follow Running Payments to inspect the transaction, partial approvals, and surcharge decisions before displaying a completed receipt.

If a payment or follow-up request may have reached the server, a timeout or unreadable response leaves the outcome uncertain. Reconcile the existing transaction before offering another payment, capture, refund, or surcharge confirmation. Do not automatically replay the operation from an error handler.

KoardMerchantSDKError Reference

Case Meaning Recovery
.network Transport failure, including offline connections and timeouts Restore connectivity. Inspect the wrapped error when present and reconcile any submitted operation
.unauthorized Authentication is missing or expired, or the API returned HTTP 401/403 Authenticate again and verify access to the selected merchant/location
.blockedAccount Merchant account is blocked (HTTP 423) Contact Koard support; repeated login or payment attempts will not unblock it
.invalidRequest The SDK could not construct a valid request Check the request and SDK configuration
.rateLimited API rate limit (HTTP 429) Stop immediate retries and back off; retain the message if supplied
.server API rejected the request or returned a server error Inspect the message and request context; reconcile submitted payments before retrying
.decodingFailure Response could not be decoded Retain the operation context and contact support if persistent; do not assume the payment failed
.unsupportedPlatform Feature requires a newer iOS version Use the required OS version on a supported device
.unknown Unclassified failure Inspect the underlying error when present and retain a fallback handler
.TTPConfigurationFailed Tap to Pay setup or account-linking failure Inspect the nested configuration case below
.TTPPaymentFailed Tap to Pay read or payment failure Inspect the nested payment case below
.missingTerminalID Required terminal context is missing Check the active location's terminal configuration
.missingLocationID No active location is set Select an authorized location with setActiveLocationID(...)
.invalidTransactionResponse Expected transaction data is missing or invalid Recover the transaction outcome; do not show a successful receipt
.failedToConfirmSurcharge Confirming or rejecting the surcharge failed Inspect the associated error and retrieve the transaction before another confirmation attempt
.invalidParameters A required argument is missing or invalid Correct the argument described by the associated string
.accountNotLinked This device is not linked to the merchant's Tap to Pay account Offer explicit account linking, then prepare the reader
.readerTokenInvalid Reader token was rejected, is invalid, or has expired Check the environment and credentials, then retry reader preparation; escalate persistent failures

Associated values preserve additional context: .network(description:underlying:) carries a String and optional Error; .unknown(underlying:) carries an optional Error; .rateLimited(message:), .server(message:), and .readerTokenInvalid(...) carry optional strings. .unsupportedPlatform(requiredVersion:) and .invalidParameters(...) carry strings, and .failedToConfirmSurcharge(...) carries an Error. The two Tap to Pay wrappers carry the nested enums listed below.

HTTP Status Mapping

The published error-handling contract maps HTTP failures into Koard cases. These HTTP statuses are not a separate numeric Koard error enum.

HTTP status Koard case
401, 403 .unauthorized
423 .blockedAccount
429 .rateLimited(message:)
Other non-success responses, including 400, 404, and 5xx .server(message:)

Do not expect an HTTP 400 to become .invalidRequest, or a 404 to have its own enum case. The wrapper does not expose the original HTTP status as a dedicated property. A .server message may include it, but message parsing is not a supported classification mechanism.

Tap to Pay Configuration Errors

These are nested under .TTPConfigurationFailed(...) as KoardMerchantSDKError.TTPConfigurationError.

Nested case Meaning Recovery
.noReader No payment card reader is available Check device support, OS, and entitlement setup
.noReaderIdentifier(Error) Reader identifier could not be obtained Inspect the associated error and verify reader setup
.accountLinkingError(Error) Account linking failed or was declined Inspect the associated error; keep a merchant-triggered linking action available
.noPSPToken Reader token could not be obtained Check authentication, active location, environment, and connectivity
.deviceConfigurationFailed Reader/device configuration failed Review setup and retry preparation after resolving the cause
.notAuthenticated Reader setup requires authentication Log in before preparing or linking the reader

prepare() does not automatically link the account. linkAccountAsync() throws when linking fails or is declined. Follow Account linking and reader preparation instead of repeatedly presenting Apple's linking sheet.

Tap to Pay Payment Errors

These are nested under .TTPPaymentFailed(...) as KoardMerchantSDKError.TTPPaymentError.

Nested case Meaning Recovery
.noPaymentResult No payment result was available Treat the outcome as unresolved until checked
.paymentCardReaderError(Error) Reader returned an underlying error Inspect the associated error for reader/session context
.paymentCardReaderNilResult Reader returned no result Do not report approval; check any existing transaction before a new attempt
.canceled Customer or system canceled the Tap to Pay sheet Return to a canceled checkout state; do not show a generic payment-failed alert

Apple reader errors remain associated Error values where supplied. Their numeric codes and availability depend on Apple's error domain and OS version. They are not additional numeric Koard error codes.

Other Exported Error Data

The framework also exports RefundError, a KoardDescribableError with statusCode: Int, message: String, and errorDescription. If your caller receives this type, those are its diagnostic fields. It does not give every KoardMerchantSDKError a statusCode property, and the current refund API returns TransactionResponse on its success path.

ApiErrorDetail (detail, httpStatusCode) and ApiErrorItem (code, message, context) are exported Codable data models, not cases of the thrown error enum. Do not assume a caught KoardMerchantSDKError exposes those fields.

Handling Errors in Code

This reader-preparation example runs UI updates on the main actor and leaves account linking as an explicit merchant action. showMessage and offerAccountLinking are callbacks supplied by your app. For transaction handling, keep the complete Running Payments implementation as the source of truth.

import Foundation
import KoardSDK

@MainActor
func prepareReader(
    showMessage: (String) -> Void,
    offerAccountLinking: () -> Void
) async throws {
    do {
        try await KoardMerchantSDK.shared.prepare()
    } catch let cancellation as CancellationError {
        throw cancellation
    } catch let error as KoardMerchantSDKError {
        switch error {
        case .accountNotLinked:
            offerAccountLinking()
        case .unauthorized, .TTPConfigurationFailed(.notAuthenticated):
            showMessage("Sign in before preparing the reader.")
        case .readerTokenInvalid:
            showMessage("Check the SDK environment, then prepare the reader again.")
        case .rateLimited:
            showMessage("Too many requests. Wait before trying again.")
        case .network(_, let underlying):
            if (underlying as? URLError)?.code == .cancelled {
                throw CancellationError()
            }
            showMessage("Check your connection before preparing the reader again.")
        default:
            showMessage(error.errorDescription)
        }
    } catch {
        // Preserve an unexpected error for the caller's fallback handler.
        throw error
    }
}

For a payment catch block, handle .TTPPaymentFailed(.canceled) as cancellation. Keep a fallback branch for other errors and future cases, and never use a successful prepare() as proof that a payment succeeded.

Troubleshooting and Support

See Troubleshooting for setup, linking, token, and payment recovery. Include the SDK version, iOS version, operation, environment, enum case (including its nested case), redacted description, and transaction/event ID when available. Include an underlying error's domain/code only when that associated error exists. Omit credentials and card data.

Published Sources