Skip to main content
This page summarizes the CardPlus sandbox access details and a signing example you can use for testing.

Web login

Open <SANDBOX_PORTAL_URL> and sign in with one of the sandbox accounts:

API base URL

Use the sandbox API base URL:
  • https://access-wallet.test.ftrader.pro

Required fields

You will use the following fields for requests:
FieldExample
agencyCodeCARDTEST
appIdCP15222622
birthday2003-12-08
documentTypePASSPORT
mobile86-18512789986
secretKey156113543243119

API example

Send request

private val json = "application/json; charset=utf-8".toMediaType()

private fun sendRequest(requestURL: String, param: Any): Boolean {
    val request = Request.Builder()
        .url(requestURL)
        .post(JSON.toJSONString(param).toRequestBody(json))
        .build()

    val response = okHttpClient.newCall(request).execute()
    response.use {
        if (!it.isSuccessful || it.body == null) {
            return false
        }

        val result = it.body!!.string()
        return result == "SUCCESS"
    }
}

Parameter signature

Generate the MD5 signature using the following rules:
  1. Sort and concatenate keys in lexicographic order: key1=value1&key2=value2
  2. Append the downstream private key: key1=value1&key2=value2&key=privateKey
  3. Convert the final hash value to uppercase

Signature example

val url = "/wallet/api/user/registerAndKyc"
val requestParam = TreeMap<String, String>()

requestParam["agencyCode"] = "CARDTEST"
requestParam["appId"] = "CP15222622"
requestParam["appUserId"] = "123456"
requestParam["birthday"] = "2003-12-08"
requestParam["documentNo"] = "123456"
requestParam["documentType"] = "ID_CARD"
requestParam["email"] = "[email protected]"
requestParam["firstName"] = "Tian"
requestParam["lastName"] = "Tian"
requestParam["mobile"] = "86-13000000000"
requestParam["photos"] = "https://photo1"
requestParam["region"] = "HK"

// sign
requestParam["sign"] = DSSignUtil.getMD5Hash(requestParam, secretKey)
val result = sendRequest(url, requestParam)

Signature tools

object DSSignUtil {
    private const val encodingCharset = "UTF-8"

    /**
     * key1=value1&key2=value2&key=privateKey
     */
    fun getMD5Hash(map: Map<String, Any?>, key: String): String {
        val list = ArrayList<String>()
        map.forEach {
            if (it.value != null) {
                list.add("${it.key}=${it.value}&")
            }
        }

        val arrayToSort = list.toTypedArray()
        Arrays.sort(arrayToSort, CASE_INSENSITIVE_ORDER)

        val sb = StringBuilder()
        for (item in arrayToSort) {
            sb.append(item)
        }

        var result = sb.toString()
        result += "key=$key"
        result = md5(result, encodingCharset)!!.toUpperCase()
        return result
    }

    fun md5(value: String, charset: String?): String? {
        return try {
            val data = value.toByteArray(charset(charset!!))
            val md = MessageDigest.getInstance("MD5")
            val digestData = md.digest(data)
            toHex(digestData)
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
            null
        }
    }

    private fun toHex(input: ByteArray?): String? {
        if (input == null) {
            return null
        }

        val output = StringBuffer(input.size * 2)
        for (i in input.indices) {
            val current: Int = input[i] and 0xff
            if (current < 16) {
                output.append("0")
            }
            output.append(current.toString(16))
        }
        return output.toString()
    }
}