v1.0.0

DIGIHCE SDK Documentation

Complete reference for integrating NFC HCE payments into your Android application.

Quick Start

Get NFC payments running in your app in under 5 minutes.

1

Add the SDK dependency to your build.gradle.kts

2

Initialize the SDK in your Application.onCreate()

3

Create a reader and start collecting payments

Installation

Add the DIGIHCE Maven repository and SDK dependency:

settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        maven {
            url = uri("https://maven.hce.vas.et/releases")
            credentials {
                username = "token"
                password = providers.gradleProperty("DIGIHCE_TOKEN").get()
            }
        }
    }
}
app/build.gradle.kts
dependencies {
    implementation("com.digihce:sdk:1.0.0")
}

Alternative: AAR file

If using the AAR distribution, place the file in app/libs/ and add implementation(files("libs/digihcesdk.aar")) to your dependencies.

Initialization

Initialize the SDK in your Application class with your API key:

SampleApp.kt
class SampleApp : Application() {
    override fun onCreate() {
        super.onCreate()

        DigiHceSdk.init(applicationContext) {
            apiKey = "digi_live_xxxxxxxxxxxx"
            aid = "F0746C6272507961"        // optional, uses default
            environment = Environment.SANDBOX // or PRODUCTION
            cardIdentity = CardIdentity(
                name = "User A",
                accountId = "ACC-12345",
                msisdn = "+251912345678"     // optional, for USSD 2FA
            )
            cardEnabled = true
        }
    }
}

What happens during init()

  • Validates API key with backend via POST /sdk/activate
  • Receives JWT session token (24h TTL) and encryption key material
  • Stores credentials in EncryptedSharedPreferences
  • Dynamically registers the AID for NFC payment routing

Collecting Payments

Use the reader API to accept NFC tap payments from payers:

MainActivity.kt
val reader = DigiHceSdk.createReader(activity)

// Start collecting — returns a Flow scoped to this session
val sessionFlow: Flow<DigiHceEvent> = reader.collect(
    amount = "100.00",
    currency = "ETB"
)

// Observe events
sessionFlow.collect { event ->
    when (event) {
        is DigiHceEvent.ChargeCompleted -> {
            showSuccess(event.result)
        }
        is DigiHceEvent.ChargePending2FA -> {
            showWaiting("Awaiting USSD confirmation...")
        }
        is DigiHceEvent.TransactionReversed -> {
            showReversed(event.reason)
        }
        is DigiHceEvent.Error -> {
            showError(event.error)
        }
    }
}

// Stop when done
reader.stop()

The reader is lifecycle-aware and automatically cleans up when the Activity is destroyed.

Sending Payments (Push)

Prepare a push payment that broadcasts via HCE when another device taps:

PushPayment.kt
// Prepare server-signed payment payload
val result: Result<TransactionResult> = DigiHceSdk.preparePayment(
    amount = "50.00",
    currency = "ETB"
)

when {
    result.isSuccess -> {
        // HCE now broadcasts the signed payload
        // On tap + CONFIRM → emits PaymentSent
        showReady("Tap to send payment")
    }
    result.isFailure -> {
        showError(result.exceptionOrNull())
    }
}

// Cancel if needed
DigiHceSdk.cancelPayment()

Events & Callbacks

Flow API (recommended)

EventObserver.kt
DigiHceSdk.events.collect { event ->
    when (event) {
        is DigiHceEvent.ChargeReceived -> { }
        is DigiHceEvent.ChargePending2FA -> { }
        is DigiHceEvent.ChargeCompleted -> { }
        is DigiHceEvent.PaymentSent -> { }
        is DigiHceEvent.PaymentReceived -> { }
        is DigiHceEvent.TransactionReversed -> { }
        is DigiHceEvent.Error -> { }
        is DigiHceEvent.SdkDeactivated -> { }
    }
}

Callback API (alternative)

CallbackAdapter.kt
DigiHceSdk.setListener(object : DigiHceListener {
    override fun onChargeReceived(result: TransactionResult) {}
    override fun onChargePending2FA(transactionId: String) {}
    override fun onChargeCompleted(result: TransactionResult) {}
    override fun onPaymentSent(result: TransactionResult) {}
    override fun onPaymentReceived(result: TransactionResult) {}
    override fun onTransactionReversed(id: String, reason: String) {}
    override fun onError(error: DigiHceError) {}
    override fun onSdkDeactivated(reason: String) {}
})

Data Models

Models.kt
data class CardIdentity(
    val name: String,
    val accountId: String,
    val msisdn: String? = null   // for USSD 2FA
)

data class TransactionResult(
    val transactionId: String,
    val status: TransactionStatus,
    val amount: String,
    val currency: String,
    val counterparty: String,
    val timestamp: Long,
    val serverReceipt: String?
)

enum class TransactionStatus {
    SETTLED,
    PENDING_2FA,
    REVERSED,
    FAILED,
    PENDING_SYNC     // offline payer, awaiting connectivity
}

Security Architecture

The SDK implements 8 layers of security:

1

API Key Gating

Session tokens via POST /sdk/activate

2

Server-Signed Transactions

ECDSA P-256 signed transaction tokens

3

Encrypted NFC Payloads

AES-256-GCM with per-transaction keys

4

USSD 2FA

Configurable per-MSISDN with amount thresholds

5

License Validation

Periodic checks every 6 hours

6

Certificate Pinning

SHA-256 TLS pin with backup rotation

7

SDK Obfuscation

ProGuard with string encryption

8

Device Integrity

Device fingerprint bound to session

Error Handling

Errors.kt
sealed class DigiHceError {
    data class AuthFailed(val message: String) : DigiHceError()
    data class NfcUnavailable(val message: String) : DigiHceError()
    data class TransactionFailed(
        val transactionId: String?,
        val message: String
    ) : DigiHceError()
    data class NetworkError(val message: String) : DigiHceError()
    data class LicenseExpired(val message: String) : DigiHceError()
    data class CardDisabled(val message: String) : DigiHceError()
}

APDU Status Words

90 00Success6A 82AID not found69 85Conditions not satisfied69 82Security not satisfied6F 01Offline limit exceeded6F 02Verification key expired6F 00General error