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"} |
| Token Expired | Token expired. Client should login again | 401 | {"data":{},"message":"Provided token is expired","time":"2024-10-31T07:50:22Z","verdict":"expired_token"} |
| 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"} |
| Token Expired | Token expired. Client should login again | 401 | {"data":{},"message":"Provided token is expired","time":"2024-10-31T07:50:22Z","verdict":"expired_token"} |
| 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"} |
| Token Expired | Token expired. Client should login again | 401 | {"data":{},"message":"Provided token is expired","time":"2024-10-31T07:50:22Z","verdict":"expired_token"} |
| 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/full_checks | application/json |
| Header key | Header Value | Description |
|---|---|---|
Authorization | Bearer <JWT> | Include the JWT access token to support request authentication |
{
"card_type": string,
"image1": {
"base64": string,
"label": string,
"metadata": JSON
},
"image2": {
"base64": string,
"label": string,
"metadata": JSON
},
"selfies": [
{
"base64": string,
"label": string,
"metadata": JSON
},
...
{
"base64": string,
"label": string,
"metadata": JSON
}
]
"metadata": JSON
}
| key | type | required | description |
|---|---|---|---|
| `card_type | string | yes | card type of the image as described at Card types table |
image1 | ImageData | yes | image of the identity card's front side |
image2 | ImageData | no | image of the identity card's back side |
selfies | []ImageData | no | list selfie images of customer. Max 3 images |
metadata | json | yes | more information of identity. eg client_uid, phone_number, dob, name ... client_uid should be not empty |
Which each image's parameters contains:
| key | type | required | description |
|---|---|---|---|
base64 | string | yes | base64 encoded text of image data |
label | string | no | label of the image as described at Label table |
metadata | json | no | key-value, key should be string, value should be string, int, float, bool. Example "{\"id\":\"123456789\",\"type\":1}" |
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"} |
| Token Expired | Token expired. Client should login again | 401 | {"data":{},"message":"Provided token is expired","time":"2024-10-31T07:50:22Z","verdict":"expired_token"} |
| 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": {
"request_id": "538cdfe9-7009-4bcf-b514-6faf8904e82c",
"card_information": [
{
"field": "id",
"value": "123456789012",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "name",
"value": "FULL NAME",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "date_of_birth",
"value": "2005/03/02",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "gender",
"value": "F",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "address",
"value": "RD 1000 NDC COMPOUND,, SITH DISTRICT",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "last_name",
"value": "LAST NAME",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "first_name",
"value": "FIRST NAME",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "middle_name",
"value": "MIDDLE NAME",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "card_type",
"value": "ph.philhealth",
"confidence_verdict": "",
"confidence_score": 0
},
{
"field": "card_label",
"value": "ph.philhealth.qr.front",
"confidence_verdict": "",
"confidence_score": 0
}
],
"compare_faces": [
{
"face1_id": "7d71e6e2-a6bb-4ea6-8826-be0947afd6f4",
"face2_id": "1647fae2-7ff2-4500-89cc-4196e42512ce",
"result": "matched",
"score": 0.8136464
}
],
"liveness_check": {
"is_live": true,
"score": 0.99999917
},
"portrait_sanity": {
"verdict": "good",
"score": 1
},
"card_sanity": {
"verdict": "photo_not_qualified",
"score": 0.8199463,
"score_details": [
{
"verdict": "good",
"score": 1
},
{
"verdict": "photo_not_qualified",
"score": 0.8199463
}
],
"verdict_details": {}
},
"card_tampering": {
"verdict": "alert",
"score": 1,
"details": [
{
"verdict": "non_liveness",
"score": 0.99,
"name": "alert_1",
"info": "image1"
}
]
}
},
"message": "fully check eKYC successfully",
"time": "2026-01-27T18:03:56+07:00",
"verdict": "success"
}
verdict response mapping:| verdict | Description |
|---|---|
| success | Process the request successfully. |
| failure | Process the request failure. |