Skip to main content

Verifying the transaction result

Verifying the transaction result​

After executeTransaction completes, the SDK returns a TransactionResult. Verifying it means inspecting the outcome, persisting the reference number, and distinguishing business outcomes from technical failures.

1. Cast to the specific result type

TransactionResult is a sealed hierarchy. Cast to the concrete type for the operation you ran:

kotlin

val result = sdk.terminal.payments.executeTransaction(context.transactionId)

when (result) {
is PurchaseResult -> handlePurchase(result)
is RefundResult -> handleRefund(result)
is ReservationCaptureResult -> handleCapture(result)
is VoidResult -> handleVoid(result)
else -> { /* unexpected result type */ }
}

2. Branch on the outcome

Every result carries a TransactionOutcome. Treat each value explicitly — do not assume non-approved means failure:

kotlin

when (result.outcome) {
TransactionOutcome.APPROVED -> { /* success — proceed */ }
TransactionOutcome.DECLINED -> { /* issuer or acquirer declined */ }
TransactionOutcome.CANCELLED -> { /* user or terminal cancelled */ }
TransactionOutcome.FAILED -> { /* business-level failure */ }
}

A declined or cancelled payment is a normal business outcome on the result, not an exception.

3. Persist the transaction reference number

On approval, extract and persist the transactionReferenceNumber. This is the acquirer reference used for reconciliation, refunds, and support cases:

kotlin

if (result.outcome == TransactionOutcome.APPROVED) {
val ref = result.transaction?.transactionReferenceNumber
?: error("Approved transaction missing reference number")

// Store ref against your order in your backend
}

transactionReferenceNumber is distinct from transactionId. Persist the reference number, not the transaction ID.

4. Separate technical failures

Infrastructure and SDK errors surface as exceptions from the call itself, not as TransactionResult values:

kotlin

try {
val result = sdk.terminal.payments.executeTransaction(transactionId)
// ... inspect result.outcome ...
} catch (e: TerminalException) {
// Transport, service crash, timeout — typically retryable
} catch (e: AtiSdkException) {
// SDK connection or setup issue — reconnect required
}
  • TransactionResult carries business outcomes (approved, declined, cancelled, failed).
  • TerminalException is a technical failure from the call.
  • AtiSdkException is an SDK lifecycle failure (not connected, bind lost).

Route each path through different UI: business outcomes go to the receipt or decline screen; technical failures go to a retry path.

5. Recover orphaned transactions on startup

If your app process was killed mid-transaction (memory pressure during cardholder interaction), the result may have been produced after your process died. Always check on startup before initiating a new transaction:

kotlin

val lastResult = sdk.terminal.payments.getLastResult()
if (lastResult.state == LastPaymentResultState.FOUND) {
val recovered = lastResult.result
// Reconcile against the order you initiated last
}

Skipping this step risks double-charging the cardholder.

Verification checklist

  • Cast TransactionResult to its concrete subtype
  • Branch on every TransactionOutcome value, not just APPROVED
  • Persist transactionReferenceNumber against your order on approval
  • Handle TerminalException and AtiSdkException on separate paths
  • Call getLastResult() on app startup and after reconnect

Common pitfalls

  • Treating non-approved outcomes as exceptions: declines are normal results, not errors.
  • Storing transactionId instead of transactionReferenceNumber: the reference number is the durable identifier; the transaction ID is only valid within the SDK session.
  • Skipping getLastResult(): leads to double-charges when the app is killed mid-transaction.
  • Single catch for both exception types: TerminalException and AtiSdkException require different recovery paths.
  • Assuming transaction is non-null: it may be absent on declined or failed outcomes — null-check before reading the reference.

More info:

For the full result hierarchy, all outcome values, and recovery semantics, see the Terminal API reference.