We use AES encryption to encrypt user identify of client and send it to eKYC Platform.
How it works:
A pair of:
Key: 256 bits (32 bytes)
IV (initialization vector): 16 bytes
TrustVision team will provide an (key, iv) pair and client_code for each customers before integration.
eg.
Pseudo code:
UserID = "user_id" // client-generated value
KeyBytes = Base64StdDecode(aesKey) // Base64 Standard Decoding
IVBytes = Base64StdDecode(aesIV) // Base64 Standard Decoding
// Step 1
EncryptedUserID = AES256CbcPkcs7PaddingEncrypt(UserID, KeyBytes, IVBytes)
// Step 2
EncodedBase64EncryptedValue = Base64UrlEncode(EncryptedUserID)
// Step 3
UniqueToken = KeyID + ":" + EncryptedUserID
// Step 4
EncodedBase64Token = Base64UrlEncode(UniqueToken) // Base64 URL Encoding
URL = "https://ekyc-platform-ph-staging.trustingsocial.com/lu/" + EncodedBase64
Sample code:
Reference:
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"github.com/zenazn/pkcs7pad"
)
func main() {
key := "YOUR_KEY"
iv := "YOUR_IV"
keyID := "YOUR_KEY_ID"
url := "YOUR_URL"
keyBytes, err := base64.StdEncoding.DecodeString(key)
if err != nil {
panic(err)
}
ivBytes, err := base64.StdEncoding.DecodeString(iv)
if err != nil {
panic(err)
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
panic(err)
}
userID := "PLiOA_EdK3xx1M4sJPsOlQZqgX"
// Step 1: Padding userID and encrypt
contentPadding := pkcs7pad.Pad([]byte(userID), block.BlockSize())
encryptedValue := make([]byte, len(contentPadding))
blockMode := cipher.NewCBCEncrypter(block, ivBytes)
blockMode.CryptBlocks(encryptedValue, contentPadding)
// Step 2: Encode encrypted value to base64
encodedBase64EncryptedValue := base64.RawURLEncoding.EncodeToString(encryptedValue)
// Step 3: Generate unique token
uniqueToken := keyID + ":" + encodedBase64EncryptedValue
// Step 4: Encode unique token to base64
encodedBase64Token := base64.RawURLEncoding.EncodeToString([]byte(uniqueToken))
fmt.Println(url + "/" + encodedBase64Token)
}
The client authenticate using username
and password
to receive JWT access token.
Method | Path | Content-Type | Request Body |
---|---|---|---|
POST | /auth/users/login | application/json | {"user_name":"user_name","password":"password"} |
Name | Description | Status code | Response Body |
---|---|---|---|
Success | Login successfully | 200 | {"data":{"access_token":"<JWT>"},"message":"login success","time":"2024-10-31T07:34:52Z","verdict":"success"} |
Bad Request | Invalid username pattern | 400 | {"data":{"invalid_parameters":["user_name"],"parameter_formats":{"user_name":"[regex: ^[a-z0-9_]{1,63}$]"}},"message":"some parameters has invalid format","time":"2024-11-06T03:30:42Z","verdict":"invalid_parameters"} |
Unauthorized | Incorrent username or password | 401 | {"data":{},"message":"username or password is incorrect","time":"2024-10-31T07:50:22Z","verdict":"invalid_credential"} |
Invalid Method | Invalid URL or method | 405 | {"data":{},"message":"method is not correct for the requested route","time":"2024-11-06T03:23:26Z","verdict":"invalid_method"} |
Too many requests | Too many failed login attempts | 429 | {"data":{},"message":"too many failed login attempts","time":"2024-11-06T03:33:43Z","verdict":"limit_exceeded"} |
Internal Server Error | Internal Server Error | 500 | {"data":{},"message":"Unexpected error. Error ID: ff4d5572d9df4560bb6dabc2eadd5a1e","time":"2024-11-06T03:32:09Z","verdict":"failure"} |
Use the access token JWT to call GET /application/detail
Method | Path | Content-Type | Request Body |
---|---|---|---|
GET | /portal/application/detail |
Header key | Header Value | Description |
---|---|---|
Authorization | Bearer <JWT> | Include the JWT access token to support request authentication |
Parameter key | Description |
---|---|
application_id | corressponding with the application_id in the callback result |
id | corressponding with the application_user_id in the callback result |
Name | Description | Status code | Response Body |
---|---|---|---|
Success | request successfully | 200 | See the sample success response below |
Bad Request | Invalid parameters | 400 | {"data":{},"message":"","verdict":"invalid_parameters"} |
Unauthorized | Invalid authentication token | 401 | {"data":{},"message":"authorization header is missing","time":"2024-10-31T07:50:22Z","verdict":"missing_authorization"} |
Not found | Not found | 404 | {{"data":{},"message":"applications not available","time":"2024-10-31T08:27:27Z","verdict":"record_not_found"} |
Invalid Method | Invalid URL or method | 405 | {"data":{},"message":"method is not correct for the requested route","time":"2024-11-06T03:23:26Z","verdict":"invalid_method"} |
Too many requests | Too many failed login attempts | 429 | {"data":{},"message":"too many failed login attempts","time":"2024-11-06T03:33:43Z","verdict":"limit_exceeded"} |
Internal Server Error | Internal Server Error | 500 | {"data":{},"message":"Unexpected error. Error ID: ff4d5572d9df4560bb6dabc2eadd5a1e","time":"2024-11-06T03:32:09Z","verdict":"failure"} |
On success case: 200 OK
{
"data": {
"verdict": "pending|approve|review|reject",
"application_id": 10,
"application_unique_token": "ad083945-9620-486c-be7d-c3f5dab870a1",
"application_user_id": "01234567889",
"file_ids": {
"gesture_images": [
{
"gesture": "frontal",
"image_id": "fa9a4fee-41c3-4988-a126-1431c130aa94"
},
{
"gesture": "frontal",
"image_id": "3b02828e-1d99-4bfc-9b9c-8aec102a315d"
}
],
"id_back_img_uuid": "93cef1cd-2782-4264-9c65-cce78ce3f549",
"id_front_img_uuid": "8fdcb17e-5d41-4814-8d4f-8c1bfb51df20",
"liveness_transformed_video_id": "b5a23e55-773f-4cf2-985b-835b9b3d0077",
"qr1_img_uuid": "",
"selfie_image_uuid": "3b02828e-1d99-4bfc-9b9c-8aec102a315d",
"sequence_frames": [
{
"file_id": "4591fd8b-578f-422f-92d4-3e54a66837d5",
"file_size": 480965
},
{
"file_id": "32e870ca-816f-46c0-b303-98bb89173843",
"file_size": 486082
},
{
"file_id": "078765c1-9e55-4740-a0fe-60b05ed2f348",
"file_size": 487386
},
{
"file_id": "5e129fb2-8c41-4e23-959b-958e607092c7",
"file_size": 146095
}
]
},
"ekyc_face": {
"compare_score": 0.699894,
"compare_status": "matched",
"liveness_score": 0.99999946,
"liveness_status": "success",
"process_status": "success",
"processed_at": "2024-09-26T18:11:54+07:00",
"sanity_check_result": "good",
"sanity_check_score": 1,
"sanity_check_status": "success",
"search_face_result": {
"faces": [
{
"id_number": "3129326470849810",
"image_id": "7db53bf6-2b20-45d0-a2c3-eaf63b142d9e",
"metadata": {
"phone_number": "",
"unique_token": "UiZW2HrO"
},
"score": 0.86089295
}
],
"response": {
"faces": [
[
{
"id": "95e4db8d-448c-4a64-8799-b9701b08358c",
"image_id": "7db53bf6-2b20-45d0-a2c3-eaf63b142d9e",
"metadata": [
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "national_id",
"value": "3129326470849810"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "phone_number",
"value": ""
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "unique_token",
"value": "UiZW2HrO"
}
],
"score": 0.86089295
}
]
],
"request_id": "3cf7723d-f13b-49a0-95f8-7e2e5f8f62fa",
"status": "success"
}
},
"search_face_status": "found"
},
"ekyc_ocr": {
"card_info": {
"card_info": [
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "crn",
"value": "0123456789"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "last_name",
"value": "SANTOS"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "first_name",
"value": "JOSE"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "middle_name",
"value": "CRUZ"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "date_of_birth",
"value": "1990/01/01"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "gender",
"value": "F"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "address",
"value": "CABAROAN SAN ESTEBAN ILOCOS SUR PHL 2706"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "id",
"value": "0123456789"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "card_type",
"value": "ph.ump"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "card_label",
"value": "ph.ump.front"
},
{
"confidence_score": 0,
"confidence_verdict": "",
"field": "name",
"value": "JOSE CRUZ SANTOS"
}
],
"converted_card_info": {
"address": "CABAROAN SAN ESTEBAN ILOCOS SUR PHL 2706",
"birth_date": "1990/01/01",
"card_label": "ph.ump.front",
"card_type": "ph.ump",
"gender": "F",
"id_number": "0123456789",
"name": "JOSE CRUZ SANTOS"
}
},
"process_status": "success",
"processed_at": "2024-09-26T18:11:29+07:00",
"sanity_check_result": "good",
"sanity_check_score": 1,
"sanity_check_status": "success",
"search_face_result": null,
"search_face_status": "disabled"
},
"form_data": {
"contactInfo": {
"familyAddress": "CABAROAN SAN ESTEBAN ILOCOS SUR PHL 2706"
},
"personalInfo": {
"birthday": "1990/01/01",
"fullName": "JOSE CRUZ SANTOS",
"gender": "",
"idCard": "0123456789"
}
},
"id_tampering": {
"score": 1,
"verdict": "good"
},
"time": "2024-09-27T09:50:32+07:00"
}
}
verdict
response mapping:verdict | Description |
---|---|
pending | The user has not completed the flow |
approve | Our recommendation to approve this application |
review | Our recommendation to review this application |
reject | Our recommendation to reject this application |
file_ids
response mapping:key | Description |
---|---|
id_front_img_uuid | The image id of front side |
id_back_img_uuid | The image id of back side |
qr1_img_uuid | The image id of QR code |
selfie_image_uuid | The image id of selfie that use to compare faces |
gesture_images | List images during user do liveness |
sequence_frames | List frames video during user do liveness |
liveness_transformed_video_id | The video id merge from sequence frames |
form_data
response object:{
"contactInfo": {
"familyAddress": "CABAROAN SAN ESTEBAN ILOCOS SUR PHL 2706"
},
"personalInfo": {
"birthday": "1990/01/01",
"fullName": "JOSE CRUZ SANTOS",
"gender": "",
"idCard": "0123456789"
}
}
Method | Path | Content-Type | Request Body |
---|---|---|---|
GET | /portal/document/download_ekyc_file |
Header key | Header Value | Description |
---|---|---|
Authorization | Bearer <JWT> | Include the JWT access token to support request authentication |
Parameter key | Description |
---|---|
application_id | corressponding with the application_id in the callback result |
id | corressponding with the file_ids in the callback result |
Name | Description | Status code | Response Body |
---|---|---|---|
Success | request successfully | 200 | See the sample success response below |
Bad Request | Invalid parameters | 400 | {"data":{},"message":"","verdict":"invalid_parameters"} |
Unauthorized | Invalid authentication token | 401 | {"data":{},"message":"authorization header is missing","time":"2024-10-31T07:50:22Z","verdict":"missing_authorization"} |
Not found | Not found | 404 | {{"data":{},"message":"applications not available","time":"2024-10-31T08:27:27Z","verdict":"record_not_found"} |
Invalid Method | Invalid URL or method | 405 | {"data":{},"message":"method is not correct for the requested route","time":"2024-11-06T03:23:26Z","verdict":"invalid_method"} |
Too many requests | Too many failed login attempts | 429 | {"data":{},"message":"too many failed login attempts","time":"2024-11-06T03:33:43Z","verdict":"limit_exceeded"} |
Internal Server Error | Internal Server Error | 500 | {"data":{},"message":"Unexpected error. Error ID: ff4d5572d9df4560bb6dabc2eadd5a1e","time":"2024-11-06T03:32:09Z","verdict":"failure"} |
On success case: the HTTP status code will be 200, and server will send the file as binary content.
Method | Path | Content-Type |
---|---|---|
POST | /portal/ekyc/index_faces | application/json |
Header key | Header Value | Description |
---|---|---|
Authorization | Bearer <JWT> | Include the JWT access token to support request authentication |
{
"image_base64": string,
"collection": string,
"label": string,
"metadata": JSON
}
key | type | required | description | |
---|---|---|---|---|
image_base64 | string | yes | encode base64 of binary image without prefix eg. data:image/jpeg;base64, | |
label | string | yes | label of the image as described at Label table | |
metadata | json | yes | more information of image. eg client_uid, phone_number, dob, name ... client_uid should be not empty | |
collection | string | no | index faces to this collection. case-sensitive |
Which each metadata's parameters contains:
key | type | required | description |
---|---|---|---|
client_uid | string | yes | ID of an user or application or any unique token |
national_id | string | no | id number of ID Card of an user |
unique_token | string | no | the token application_unique_token from application detail |
Name | Description | Status code | Response Body |
---|---|---|---|
Success | request successfully | 200 | See the sample success response below |
Bad Request | Invalid parameters | 400 | {"data":{},"message":"","verdict":"invalid_parameters"} |
Unauthorized | Invalid authentication token | 401 | {"data":{},"message":"authorization header is missing","time":"2024-10-31T07:50:22Z","verdict":"missing_authorization"} |
Not found | Not found | 404 | {{"data":{},"message":"applications not available","time":"2024-10-31T08:27:27Z","verdict":"record_not_found"} |
Invalid Method | Invalid URL or method | 405 | {"data":{},"message":"method is not correct for the requested route","time":"2024-11-06T03:23:26Z","verdict":"invalid_method"} |
Too many requests | Too many failed login attempts | 429 | {"data":{},"message":"too many failed login attempts","time":"2024-11-06T03:33:43Z","verdict":"limit_exceeded"} |
Internal Server Error | Internal Server Error | 500 | {"data":{},"message":"Unexpected error. Error ID: ff4d5572d9df4560bb6dabc2eadd5a1e","time":"2024-11-06T03:32:09Z","verdict":"failure"} |
On success case: the HTTP status code will be 200, and server will send the file as binary content.