Skip to content

Android SDK Integration ​

Integration guide for the SmartOTP Android SDK, version 0.4.0. Written in Kotlin; the entry point is the SdkSoft object.

Installation ​

Extract Cake_SOTP_SDK_v0.4.0_1.zip into your project — in the example project it goes under a libs folder.

groovy
repositories {
    maven { url rootDir.path + "/libs/" }
}
groovy
implementation "xyz.be.cake.smartotpsdk:legacy:0.4.0"

Initialize ​

kotlin
SdkSoft.initialize(
  context = this,
  baseURL = "<BASE_URL>",
  networkConfig = NetworkConfig(),
  isDebug = false)  // never ship a release build with request tracing on

Parameters:

ParamTypeRequiredDefaultDescription
contextContextYes—Android context.
baseURLStringYes—Base URL for SDK API calls.
networkConfigNetworkConfigYes—Network client configuration.
isDebugBooleanNofalseDebug mode. Set true to trace request logs.

NetworkConfig ​

kotlin
class NetworkConfig(
    val readTimeoutSeconds: Long = 60,
    val writeTimeoutSeconds: Long = 60,
    val connectTimeoutSeconds: Long = 60,
    val callTimeoutSeconds: Long = 0
)
ParamTypeRequiredDefaultDescription
readTimeoutSecondsLongNo60Read timeout for new connections.
writeTimeoutSecondsLongNo60Write timeout for new connections.
connectTimeoutSecondsLongNo60Connect timeout for new connections.
callTimeoutSecondsLongNo0Timeout for complete calls.

These map onto OkHttpClient settings.

Custom headers ​

kotlin
SdkSoft.setRequestHeaders(
    mapOf(
        Pair("key", "value")
    )
)
ParamTypeRequiredDefaultDescription
headersMap<String,String>Yes—Extra headers added to every SDK API request.

Activate ​

Call after Register succeeds on your backend.

kotlin
val activateBody = ActivateBody(
    partnerId = "{{partner_id}}",
    userId = "user_00000001",
    username = "nguyenvana",
    deviceId = "device_00000001",
    deviceOs = "Android",
    appVersion = "1.0.0",
    registrationId = "reg_00000001",
    activationKey = "fake_activation_key",
    token = "fake_device_token",
    pushToken = "fake_push_token",
)

SdkSoft.activate(activeBody = activateBody, overrideBaseUrl = overrideUrl, callback = {
    // Don't log the response — it carries the client secret key.
})

ActivateBody ​

ParamTypeRequiredDefaultDescription
partnerIdStringYes—The partner id.
deviceIdStringYes—The device UUID.
userIdStringYes—The user id.
usernameStringNo—The username.
pushTokenStringYes—Firebase token used to push the challenge key from server to client.
tokenStringYes—The registration token from Register.
registrationIdStringYes—The registration id from Register.
activationKeyStringYes—The activation key from Register. Single-use — call Register again for a fresh one.
deviceOsStringYes—Device OS for tracking, e.g. Android.
appVersionStringYes—Current app version.

Call parameters ​

ParamTypeRequiredDefaultDescription
activateBodyActivateBodyYes—The activation payload.
overrideBaseUrlStringNo—Base URL to override for this call.
callbackUnitYes—Receives an ActivateResponse.

Response ​

json
{
    "success": true,
    "code": 1,
    "message": "success",
    "data": {
        "key": "fake_client_secret_key",
        "period": "15",
        "algorithm": "HmacSHA512",
        "length": "6",
        "type": "totp",
        "issuer": "SOTP",
        "id": "123456"
    }
}
FieldTypeDescription
keyStringThe secret key used to generate an OTP.
periodStringSeconds an OTP stays valid.
lengthStringNumber of digits in an OTP.
issuerStringThe issuer that generated the OTP.
typeStringThe type of soft OTP.
idStringThe activation ID, also called the serial number.

The SDK hands this back as ActivateResponse, with key, period, algorithm, length, type, issuer, code, message, id and unhandledException.

INFO

period and length come back as strings here, while Sync Time returns the same two fields as integers. Parse defensively.

CodeDescription
3Parameter invalid
103Thông tin Partner không chính xác — Partner details are incorrect.
123Yêu cầu vượt giới hạn. Vui lòng thử lại sau ít phút — Request limit exceeded.
124OTP không hợp lệ — Invalid OTP.
125OTP hết hạn sử dụng — OTP expired.
129Token không hợp lệ — Invalid token.
-2Secret token decryption failed.

PIN and Storage ​

kotlin
SdkSoft.storedSecureSmartOTP(pin = "1234")   // Boolean
SdkSoft.isValidPin(pin = "1234")             // Boolean
ParamTypeRequiredDefaultDescription
pinStringYes—SmartOTP PIN, 4 digits.

storedSecureSmartOTP returns true when the secret was stored successfully. Call it once after activation.

Change PIN ​

kotlin
val result = SdkSoft.changePin(oldPin = oldPin, newPin = newPin)

when (result) {
    ChangePinResult.UNKNOWN_FAILURE -> {}
    ChangePinResult.INVALID_OLD_PIN -> {}
    ChangePinResult.SUCCESS -> {}
}
FieldTypeDescription
SUCCESSChangePinResultThe PIN was changed.
INVALID_OLD_PINChangePinResultoldPin does not match the stored PIN.
UNKNOWN_FAILUREChangePinResultAn internal SDK error occurred.

The new PIN cannot repeat either of the previous two.

INFO

Android returns three ChangePinResult values; iOS returns five, splitting out sameOldAndNewPin and samePreviousPins. On Android both of those collapse into UNKNOWN_FAILURE, so you cannot tell the user precisely why their new PIN was rejected.

Generate OTP ​

kotlin
val otp = SdkSoft.generateSmartOTP()                            // basic
val otp = SdkSoft.generateAdvancedSotp(challengeKey = "fake_challenge_key")  // advance
ParamTypeRequiredDefaultDescription
challengeKeyStringYes—Challenge key from Create Transaction. Advance OTP only.

Both return a 6-digit string, or an empty string on failure.

Get Status ​

kotlin
SdkSoft.getStatus(
  userId = "user_00000001",
  overrideBaseUrl = overrideUrl,
  callback = {
      Log.d("TAG", "${it?.code} ${it?.message} ${it?.status}")
  }
)
ParamTypeRequiredDefaultDescription
userIdString?No—The user to check. When null, returns the status of the currently activated user.
overrideBaseUrlStringNo—Base URL to override.
callbackUnitYes—Receives a StatusResponse with code, message, status.

Returns one of NOT_REGISTERED, NOT_CURRENT_USER, NON_ACTIVATED, ACTIVATED, TEMPORARY_BLOCKED. The meaning shifts depending on whether you passed a userId — see Mobile SDK Overview § Status Values.

Sync Time ​

kotlin
val syncTimeBody = SyncTimeBody(appId = "app_00000001")

SdkSoft.syncTime(
    syncTimeBody = syncTimeBody,
    useOtp = false,
    overrideBaseUrl = overrideUrl,
    callback = { }
)
ParamTypeRequiredDefaultDescription
syncTimeBodySyncTimeBodyYes—Carries appId, used for tracking.
useOtpBooleanYes—Submit the current and previous period OTP to unblock an account. The SDK uses the stored PIN to generate them.
overrideBaseUrlStringNo—Base URL to override.
callbackUnitYes—Receives a SyncTimeResponse.

useOtp = true is how a TEMPORARY_BLOCKED account gets unblocked — the pair of consecutive OTPs proves the device still holds the right secret.

CodeDescription
103Thông tin Partner không chính xác — Partner details are incorrect.
129Token không hợp lệ — Invalid token.
190108Tài khoản chưa hoạt động — Account not activated.
190302OTP không chính xác — Incorrect OTP.
-1Thông tin không hợp lệ — Invalid information.

Retire ​

kotlin
SdkSoft.retire(
    overrideBaseUrl = overrideUrl,
    callback = {
        Log.d("TAG", "${it?.code} ${it?.message}")
    }
)

Deactivates the currently active SmartOTP. See Retire for what this does server-side.

Latest Transaction ​

kotlin
SdkSoft.getLatestTransaction(
  overrideBaseUrl = overrideUrl,
  callback = { }
)

Returns a TransactionResponse carrying challengeKey plus the transaction fields — appId, transactionId, transactionDesc, transactionAmount, receiverAccount, senderAccount, currency, type, transactionDate, createdAt, transactionMetadata.

Equivalent to Get Latest Transaction; the same caveat applies about it returning whichever transaction is newest.

Status Polling ​

kotlin
SdkSoft.startStatusPolling(
    interval = 30,
    appId = "app_00000001"
)

SdkSoft.cancelStatusPolling()
ParamTypeRequiredDefaultDescription
intervalIntNo30Seconds between status checks.
appIdStringNo—App ID used in the sync time call.

The job checks the SmartOTP status on each tick and runs a sync time whenever the status is ACTIVATED. Always pair it with cancelStatusPolling on teardown.

Accessors ​

FieldTypeDescription
getRegistrationId()StringRegistration id stored at activation.
getSerialId()StringRegistered serial ID. Empty if not registered or on error.
getCurrentUserId()String?Registered user ID. Null if not registered or on error.
getCurrentUsername()StringRegistered username. Empty if not registered.
isSmartOtpRegistered()BooleanWhether this device has registered SmartOTP.
getTime()LongThe server timestamp.
getPeriod()IntCurrent period config in seconds.
getRemainingValidityTime()IntSeconds left on the current OTP.

INFO

ActivateResponse.length is a String?.

Next Steps ​

Developers