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:
  • Agency account: xxxx / xxxx
  • Customer account: xxx / xxx

API base URL

Use the sandbox API base URL:
  • https://xxxxxxxxx

Required fields

You will use the following fields for requests:
FieldExample
agencyCodexxxx
appIdxxxx
birthday2003-12-08
documentTypePASSPORT
mobilexxxxxxx86
secretKeyxxxxxxxxx

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"] = "xxxxx"
requestParam["appId"] = "xxxxxxx"
requestParam["appUserId"] = "xxxxxx"
requestParam["birthday"] = "2003-12-08"
requestParam["documentNo"] = "xxxxxxxx"
requestParam["documentType"] = "ID_CARD"
requestParam["email"] = "xxxxxx"
requestParam["firstName"] = "xxxx"
requestParam["lastName"] = "xxxxx"
requestParam["mobile"] = "xxxxxxx"
requestParam["photos"] = "xxxxxxx"
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()
    }
}