SDK Lifecycle and Deinitialization

deinit() ends the SDK's current setup so your app can initialize it again without restarting. Use it when replacing the API key, changing environments, switching to a different merchant on the device, or testing a complete reset. These examples require Android SDK 1.0.7 or later and apply to UAT and Production.

Choose the right cleanup operation

Operation What it does What you do next
logout() Clears the merchant session and active location; preserves SDK initialization and device enrollment Log in again and select a location
unenrollDevice() Attempts Visa unenrollment and clears local enrollment certificates, keys, device identifiers, and enrollment state; keeps the SDK initialized Enroll again before accepting taps
deinit() Unenrolls, logs out, releases the reader service, clears the SDK's API key, and removes the SDK instance Initialize again, log in, select a location, and enroll

Use logout() for an ordinary sign-out when the device will continue with the same merchant. Use deinit() for a complete reset. For canceling a tap or releasing only the reader connection, see Running Payments.

What deinit does

The SDK runs these cleanup steps in order:

  1. Calls unenrollDevice() before logging out. It attempts to unenroll with Visa and clears local enrollment state, including device keys, certificates, the Visa device ID, and saved enrollment details. Local enrollment state is cleared even if Visa unenrollment fails.
  2. Calls logout() to clear the merchant session and active location.
  3. Resets the connection to the Visa reader service.
  4. Clears the API-key provider and saved key fingerprint, removes the singleton instance, and cancels its internal coroutine scope.

Cleanup is best effort: a Visa unenrollment failure does not prevent the remaining cleanup. A completed local reset is not confirmation that remote unenrollment succeeded. Deinitialization does not revoke your API key, delete the merchant or transaction records in Koard, or uninstall Visa Tap to Pay Ready.

Check initialization state

import com.koardlabs.merchant.sdk.KoardMerchantSdk

val sdkInitialized = KoardMerchantSdk.isInitialized()

This static check is safe before initialization and from any thread. It returns false before initialize() and after deinit() completes. getInstance() throws when no instance exists.

Initialization only means an SDK instance exists. It does not prove the merchant is authenticated, enrolled, or ready to accept a payment. While initialized, observe sdk.readinessState and require isReadyForTransactions before starting a tap.

Deinitialize and initialize again

Call both lifecycle operations on a worker thread. The helpers below switch to Dispatchers.IO:

import android.app.Application
import com.koardlabs.merchant.sdk.KoardMerchantSdk
import com.koardlabs.merchant.sdk.domain.KoardEnvironment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

suspend fun deinitializeSdk() = withContext(Dispatchers.IO) {
    if (KoardMerchantSdk.isInitialized()) {
        KoardMerchantSdk.getInstance().deinit()
    }
    check(!KoardMerchantSdk.isInitialized())
}

suspend fun initializeSdk(
    application: Application,
    apiKey: String,
    environment: KoardEnvironment
) = withContext(Dispatchers.IO) {
    if (!KoardMerchantSdk.isInitialized()) {
        KoardMerchantSdk.initialize(
            application = application,
            apiKey = apiKey,
            environment = environment
        )
    }
}

Use KoardEnvironment.UAT with your UAT key or KoardEnvironment.PROD with your production key. Your app supplies the key again; the deinitialized SDK cannot recover it for you. Keep it in your app's existing credential/configuration mechanism and never print it in logs.

Run deinitialization after payment and reader work has finished. Use an application/service coroutine scope that remains alive during cleanup, and await completion before offering Initialize or launching another SDK operation. The initialization flag is a snapshot, not a lock or a stream of cleanup progress; keep a separate UI busy state while deinitializing.

After initialization, obtain a new KoardMerchantSdk.getInstance(). Discard references to the old SDK and subscribe to the new instance's readiness state. Then log in, choose the active location, and enroll and check readiness again before accepting taps. Android deinit clears enrollment, so initialization alone cannot restore payment readiness.

Verify the flow in the demo

In the internal Android demo with lifecycle controls:

  1. Open Settings → Deinitialize SDK after completing any payment.
  2. Wait for cleanup. The app returns to Login and shows the SDK as not initialized.
  3. Tap Initialize SDK. The demo reuses its build configuration's API key and enables merchant sign-in.
  4. Log in, select a location, and enroll again. Confirm payment readiness separately from initialization.

The demo retains its own test configuration; this does not mean the SDK retains the key after deinit. The same flow works in the UAT and Production builds with the corresponding configuration.

See also