Coinme Risk SDK for Android Native Apps

Introduction

The Android Coinme Risk Engine SDK is a Kotlin library, distributed privately via Cloudsmith, that allows you to interact with the Coinme Risk Engine from your application. It provides utilities that allow you to integrate against the Coinme risk engine for the various transactional flows in your application so that Coinme may appropriately assess transactional risk when processing transactions.

ℹ️

Note:

In order to utilize Coinme transactional services from within your application, you must implement and use the Coinme Risk Engine SDK.

What is it?

The Coinme Risk Engine aggregates data about user behavior for your application. It collects:

  • Device data such as the type of device, OS name and version, etc.
  • Location data such as where the user is connecting from.
  • Connection data such as if the user is using a proxy or VPN, etc.
  • Behavioral data such as how a user is interacting with your application, how quickly they are entering data into fields, use of clipboard functions, etc.

This data is used to determine the relative riskiness of the user / transaction.

⚠️

Why is Risk Tagging Important?

Risk tagging allows the SDK to track user interactions tied to specific actions within your application. When an element is tagged, the risk engine can better assess whether a user is behaving normally or if suspicious automation patterns are detected.

As an example, if the risk engine determines that the user location changes suddenly via a VPN to access a region restricted transaction type, then the engine may determine automation is at play and may rank the transaction as high risk.


Implementing the Android SDK

⚠️

This package is not public

You will need an auth token in order to install it. You may obtain this by working with the Coinme business development team.


What You'll Need to Get Started

Before integrating the SDK, you will need the following credentials from the Coinme business development team:

CredentialRequired ForNotes
Cloudsmith auth tokenAccessing the libraryRequired to access the private library in Cloudsmith
clientIdRiskEngineSetupOptionsEnvironment-specific — you will need one for development and one for production
partnerIdgetPartnerSessionTag()Passed via GetPartnerSessionTagOptions, this is your Partner identifier needed for integration with Coinme APIs

⚠️ You will need two clientIds, one for your development builds and one for your prod builds; you may obtain these from the Coinme business development team. It is very important that you pass the correct mode and clientId properties into the risk engine based on development or production usage, otherwise you may not stream your behavioral data to the correct environment, which can severely impact the risk engine’s ability to assess risk. This would ultimately negatively impact your transaction approvals.



Installation

Set Your Entitlement Token

Add your token to local.properties file in your project root directory:

COINME_TOKEN=<YOUR_ENTITLEMENT_TOKEN>

Replace <YOUR_ENTITLEMENT_TOKEN> with the token provided by the Coinme business development team.

Note: local.properties is already gitignored by Android Studio to keep your tokens secure, but it is good to ensure this token is not exposed and the file is in your .gitignore


Add the SDK Repository and Dependency to your project

Add the repository to either your settings.gradle OR build.gradle.kts depending on your setup

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()

        maven {
            name = "CoinmeSDK"
            url = uri("https://dl.cloudsmith.io/${findProperty("COINME_TOKEN") ?: ""}/coinme/coinme-sdk-mobile/maven/")
        }
    }
}
allprojects {
    repositories {
        google()
        mavenCentral()

        maven {
            name = "CoinmeSDK"
            val token = project.findProperty("COINME_TOKEN")?.toString() ?: ""
            url = uri("https://dl.cloudsmith.io/$token/coinme/coinme-sdk-mobile/maven/")
        }
    }
}

Then in your app/build.gradle.kts add the dependency

dependencies {
    implementation("com.coinme:risk-sdk-android:1.0.0")
}

Google Play Requirements

Before releasing your application with the Coinme Risk SDK, update your Google Play settings in your developer portal.

Privacy Policy

Update your Google Play Store privacy policy with the following message:

For fraud protection purposes only, (1) we collect your Battery Usage, Device Identifier, Device Storage, MAC Address, and SIM information; (2) we also collect enough information to determine if you are trying to fake your current location by using a VPN, VPN apps with location spoofing, or other related tools.

Data Safety

For the Data Safety section in the Google Play Console:

  1. Navigate to App content > Data safety in your Play Console
  2. Declare the following data types are collected:
    1. Location (Approximate and Precise)
    2. Device or other IDs (Device ID)
    3. App info and performance (Crash logs, Diagnostics)
    4. Files and docs (for storage-related fraud detection)

Note: You may need to add any other data types your app collects beyond what the SDK requires. For easier setup, you can download and import the Data Safety CSV file from the Sardine SDK documentation if provided by your integration team.


Required Permissions

Add these permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

Permission usage:

PermissionUsage
INTERNETNetwork communication for fraud detection
ACCESS_WIFI_STATEGet user's WiFi name and status
ACCESS_NETWORK_STATEGet user's network information and state
READ_PHONE_STATEGet phone and network info (MNC, MCC, IMEI, phone number, phone type, SIM number)
USE_BIOMETRIC / USE_FINGERPRINTGet user's biometric authentication settings (Face or Fingerprint)
WRITE_EXTERNAL_STORAGEWrite data to check re-installation behavior
READ_EXTERNAL_STORAGEGet external storage status, total size, free size
ACCESS_COARSE_LOCATION / ACCESS_FINE_LOCATIONGet user's GPS location for fraud detection
READ_GSERVICESGet Device GSF ID (Google Services Framework ID)
📘

Note: Some permissions (like location and phone state) are considered dangerous permissions on Android 6.0+ and require runtime permission requests. Ensure your app handles these properly.


ProGuard Configuration

If you use ProGuard or R8 for code obfuscation, add the following rules to your proguard-rules.pro file:

# Keep Google Play Services classes
-keep class com.google.android.gms.** { *; }
-keep class com.google.android.gms.tasks.** { *; }
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient { *; }

# Keep Sardine SDK classes
-keep class ai.sardine.** { *; }
-keepclassmembers class ai.sardine.** { *; }

Implementation

Privacy & Compliance Notice

Important: If you use the Behavior Biometrics feature (enableBehaviourBiometrics = true), please ensure that your end users agree to your privacy policy before you begin collecting their Behavior Biometric data. This is required for compliance with privacy regulations.


Configuration Options

RiskEngineSetupOptions

OptionTypeRequiredDescription
modeModeYesUsed to point to environment .test or .prod
clientIdStringYesYour environment-specific Client ID
partnerIdStringYesPartner Identifier
customerIdString?NoCustomer/User's identifier
sessionKeyString?NoSession key (automatically generated if not provided)
flowFlowType?NoCurrent User flow (Onboarding, CardTransaction, or CardLinking)
regionAppRegionNoYour app's region of service (.default(US), .ca, .eu, .au, .in)
enableBehaviorBiometricsBoolNoEnabled behavior tracking (default: true)
enableClipboardTrackingBoolNoEnable clipboard tracking (default: false)
enableFieldTrackingBoolNoEnable automatic field tracking (default: true)
setCloudEntitlementsBoolNoEnable iCloud feature (default: false, requires additional setup)

FlowType Options

  • FlowType.Onboarding - New user registration flow
  • FlowType.CardTransaction - Payment/card transaction flow
  • FlowType.CardLinking - Adding payment methods flow

sessionKey

The sessionKey is a unique identifier for tracking a specific user session.

Once a key is provided for the set up, it will be cached for the duration of the app's active session. If no value is provided, the key will be automatically generated.

You will want to invoke .getPartnerSessionTag() to get an updated value that our backend will use to associate collected user data and risk score. Failure to call this and get the expected session key value may result in loss of data for accurate risk assessment.

Using .updateCoinmeRiskEngine() with a new key will override and update the cached value.

To clear a sessionKey, you may call .resetStoredSessionKey()

⚠️

Important:

When you call getPartnerSessionTag(), the returned webSessionID automatically becomes the active session key. All subsequent calls to updateCoinmeRiskEngine() will use this session key unless you explicitly provide a different one.


getPartnerSessionTag()

Retrieve the session tag from the backend API. The resulting webSessionID is automatically used to update the CoinmeRiskEngine configuration.

suspend CoinmeRiskEngine.getPartnerSessionTag(options: GetPartnerSessionTagOptions): SessionTagData

Request body parameters for GetPartnerSessionTagOptions:

ParamTypeRequiredDescription
partnerIdStringYesYour partner identifier
accountIdStringYesThe customer ID
fingerprintStringYesThe device fingerprint
additionalHeadersDict<String, String>NoAdditional headers that may be included if needed by the backend
timeoutIntNoRequest timeout in milliseconds (default: 10000)


Initialize the SDK

Initialize the Risk Engine in your Activity's onCreate():

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val options = RiskEngineSetupOptions(
            mode = Mode.Prod,  // Use Mode.Test for testing
            clientId = "your-client-id",
            enableBehaviourBiometrics = true,
            enableFieldTracking = false  // Set to false for manual tracking
        )
        
        CoinmeRiskEngine.setupCoinmeRiskEngine(this, options)
    }
}

Note: Initializing the configuration automatically calls submit(), no need to do it manually. If you need to update the configuration, please use update() method.


Update the SDK

You can update the configuration with flow, sessionKey and customerId options:

CoinmeRiskEngine.updateCoinmeRiskEngine(
    RiskEngineUpdateOptions(
        sessionKey = "new-session-key",
        flow = "new-flow",
        customerId = "customer-id")

Submit Data Before Transactions

Submits all collected behavioral and device date to the backend for risk assessment. Use callback version to ensure data is submitted before making subsequent API calls.

CoinmeRiskEngine.submit(
    onSuccess = {
        // Data successfully submitted - safe to proceed
        lifecycleScope.launch {
            processCardPayment()
        }
    },
    onError = { error ->
        Log.e("RiskSDK", "Submission failed", error)
        handleError(error)
    }
)

What it collects:

  • Sensor data (motion, orientation, pressure)
  • Behavioral biometrics (typing patterns, interaction timing)
  • Device context (clipboard activity, focus patterns)
  • Page/screen engagement metrics

When to call:

  • Before payment/transaction API calls
  • Before critical actions (login, signup, money transfer)
  • After form completion or user interaction flow

Other Utility Methods

Setup Risk Engine (Factory Method)

Initializes the Risk Engine singleton. Safe to call multiple times - returns existing instance if already initialized.

CoinmeRiskEngine.setupCoinmeRiskEngine(
    activity: Activity, 
    options: RiskEngineSetupOptions
)

Update Configuration

Updates the runtime configuration (customer ID, session key, flow). Note: This method may throw exceptions if the update fails. Wrap in try-catch for production:

CoinmeRiskEngine.updateCoinmeRiskEngine(config: RiskEngineUpdateOptions)

Get Current Config

Returns the current runtime configuration

CoinmeRiskEngine.getCoinmeRiskEngineConfig(): RiskEngineUpdateOptions

Reset Session

Resets the stored session key.

CoinmeRiskEngine.resetStoredSessionKey()

Reset SDK

Clears the singleton instance and cleans up resources.

CoinmeRiskEngine.reset()

Manual Input Field Tracking

ℹ️

If you choose to manually track input fields, first disabled automatic field tracking in the configuration. This can be done by ensuring enableFieldTracking = false when you set options for the CoinmeRiskEngine.

How to set up custom fields for tracking:

// Track text changes
emailEditText.addTextChangedListener(object : TextWatcher {
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        CoinmeRiskEngine.trackTextChange("email", s?.toString() ?: "")
    }
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
    override fun afterTextChanged(s: Editable?) {}
})

// Track focus changes
emailEditText.setOnFocusChangeListener { _, hasFocus ->
    CoinmeRiskEngine.trackFocusChange("email", hasFocus)
}

Be descriptive when assigning a value for viewId ("email", "password", "debitCardNumber", etc)


Tracking Methods

Track custom events and user interactions

// Track a user action
val data = JSONObject().apply {
    put("action", "card_transaction_started")
    put("amount", 149.99)
    put("currency", "USD")
}
CoinmeRiskEngine.trackCustomData(data)

Or use the Map extension for convenience

val data = mapOf(
    "action" to "card_payment_initiated",
    "amount" to 299.99
).toJSONObject()
CoinmeRiskEngine.trackCustomData(data)

Track Text Change

Track text input in a text field

CoinmeRiskEngine.trackTextChange(viewId: String, text: String)

Track Focus Change

Track focus change events on a field or UI component

CoinmeRiskEngine.trackFocusChange(viewId: String, isFocus: Boolean)

Note: If referencing the same UI component but calling various tracking methods, make sure to use the same viewId.