Xác thực CCCD gắn chip (eID C06)
C06 là dịch vụ của Bộ Công an để xác thực CCCD gắn chip. Hệ thống kiểm tra chứng thư số trong chip (qua SOD) và đối chiếu số CCCD với dữ liệu của Bộ Công an. eVerify thực hiện việc này qua POST /eid/verify và trả về kết luận VALID hoặc INVALID.
API chỉ trả kết luận và số CCCD đã che (id_number_masked). Thông tin cá nhân bạn đã có từ bước đọc chip.
Quy trình
- Đọc chip CCCD bằng đầu đọc thẻ hoặc điện thoại có NFC, dùng SDK đọc chip eMRTD (ICAO 9303) của bạn.
- Mã hoá từng data group sang base64 chuẩn (không dùng base64url).
- Gửi request từ máy chủ của bạn, kèm
X-API-KeyvàIdempotency-Key. - Đọc kết quả (xem bên dưới).
Dữ liệu cần gửi
| Trường | Bắt buộc | Ghi chú |
|---|---|---|
chip.sod | Có | Dùng để lấy DS certificate |
chip.dg1 | Cần ít nhất một trong dg1, dg13, id_number | Chứa số CCCD |
chip.dg13 | Như trên | Chứa số CCCD |
id_number | Như trên | Số CCCD 12 chữ số |
chip.dg2, chip.dg14, chip.dg15 | Không | Được chấp nhận để tương thích SDK nhưng không được lưu |
client_ref | Không | Mã giao dịch phía bạn, tối đa 64 ký tự |
Nếu gửi cả id_number và DG1/DG13, số CCCD phải khớp với chip. Nếu không khớp, API trả 400 ID_NUMBER_MISMATCH.
{
"chip": {
"sod": "<base64 SOD từ chip>",
"dg1": "<base64 DG1 từ chip>",
"dg13": "<base64 DG13 từ chip>"
},
"client_ref": "HS-2026-000123"
}
Không có dữ liệu chip mẫu dùng chung. Bộ Công an chỉ xác nhận dữ liệu đọc từ một thẻ CCCD thật, nên hãy dùng thẻ thật khi thử. Dữ liệu sai định dạng bị từ chối với mã 400 và không trừ lượt. Dữ liệu đúng định dạng nhưng không phải của thẻ thật thường nhận INVALID và vẫn trừ 1 lượt.
Gọi API
Các ví dụ đọc body từ request.json và dùng hai biến môi trường EVERIFY_API_KEY, EVERIFY_BASE_URL (xem Bắt đầu nhanh).
- curl
- Node.js
- Python
- Java
- C#
- PHP
curl -sS -X POST "$EVERIFY_BASE_URL/eid/verify" \
-H "X-API-Key: $EVERIFY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--max-time 30 \
--data @request.json
import { randomUUID } from 'node:crypto';
import { readFile } from 'node:fs/promises';
const baseUrl = process.env.EVERIFY_BASE_URL ?? 'https://everify.vtsi.vn/api/v1';
// Giữ key này nếu cần gửi lại cùng thao tác.
const idempotencyKey = randomUUID();
const res = await fetch(`${baseUrl}/eid/verify`, {
method: 'POST',
headers: {
'X-API-Key': process.env.EVERIFY_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: await readFile('request.json', 'utf8'),
signal: AbortSignal.timeout(30_000),
});
const body = await res.json();
if (body.error) {
// Không trừ lượt. Lỗi tạm thời thì gửi lại với cùng idempotencyKey.
console.error(res.status, body.error.code, body.error.message, body.request_id);
process.exit(1);
}
console.log(body.data.result, body.data.reason, body.data.billing);
import os
import sys
import uuid
import requests
base_url = os.environ.get("EVERIFY_BASE_URL", "https://everify.vtsi.vn/api/v1")
idempotency_key = str(uuid.uuid4()) # giữ key này nếu cần gửi lại cùng thao tác
with open("request.json", "rb") as f:
payload = f.read()
res = requests.post(
f"{base_url}/eid/verify",
headers={
"X-API-Key": os.environ["EVERIFY_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": idempotency_key,
},
data=payload,
timeout=30,
)
body = res.json()
if body["error"] is not None:
# Không trừ lượt. Lỗi tạm thời thì gửi lại với cùng idempotency_key.
err = body["error"]
sys.exit(f"{res.status_code} {err['code']}: {err['message']} ({body['request_id']})")
print(body["data"]["result"], body["data"]["reason"], body["data"]["billing"])
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
public class VerifyEid {
public static void main(String[] args) throws Exception {
String baseUrl = Objects.requireNonNullElse(System.getenv("EVERIFY_BASE_URL"), "https://everify.vtsi.vn/api/v1");
// Giữ key này nếu cần gửi lại cùng thao tác.
String idempotencyKey = UUID.randomUUID().toString();
HttpRequest req = HttpRequest.newBuilder(URI.create(baseUrl + "/eid/verify"))
.timeout(Duration.ofSeconds(30))
.header("X-API-Key", System.getenv("EVERIFY_API_KEY"))
.header("Content-Type", "application/json")
.header("Idempotency-Key", idempotencyKey)
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("request.json")))
.build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
// Thành công: "error": null và data.result là VALID hoặc INVALID.
// Có lỗi: không trừ lượt; lỗi tạm thời thì gửi lại với cùng idempotencyKey.
System.out.println(res.statusCode() + " " + res.body());
}
}
using System.Text;
using System.Text.Json;
var baseUrl = Environment.GetEnvironmentVariable("EVERIFY_BASE_URL") ?? "https://everify.vtsi.vn/api/v1";
// Giữ key này nếu cần gửi lại cùng thao tác.
var idempotencyKey = Guid.NewGuid().ToString();
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var req = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/eid/verify")
{
Content = new StringContent(File.ReadAllText("request.json"), Encoding.UTF8, "application/json"),
};
req.Headers.Add("X-API-Key", Environment.GetEnvironmentVariable("EVERIFY_API_KEY"));
req.Headers.Add("Idempotency-Key", idempotencyKey);
using var res = await http.SendAsync(req);
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var root = doc.RootElement;
if (root.GetProperty("error").ValueKind != JsonValueKind.Null)
{
// Không trừ lượt. Lỗi tạm thời thì gửi lại với cùng idempotencyKey.
var err = root.GetProperty("error");
Console.Error.WriteLine($"{(int)res.StatusCode} {err.GetProperty("code")}: {err.GetProperty("message")}");
return 1;
}
Console.WriteLine(root.GetProperty("data").GetProperty("result"));
return 0;
<?php
$baseUrl = getenv('EVERIFY_BASE_URL') ?: 'https://everify.vtsi.vn/api/v1';
// Giữ key này nếu cần gửi lại cùng thao tác.
$idempotencyKey = bin2hex(random_bytes(16));
$ch = curl_init($baseUrl . '/eid/verify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('EVERIFY_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . $idempotencyKey,
],
CURLOPT_POSTFIELDS => file_get_contents('request.json'),
]);
$raw = curl_exec($ch);
if ($raw === false) {
// Lỗi mạng: gửi lại với cùng $idempotencyKey.
exit('Network error: ' . curl_error($ch) . PHP_EOL);
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$body = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if ($body['error'] !== null) {
// Không trừ lượt. Lỗi tạm thời thì gửi lại với cùng $idempotencyKey.
exit("$status {$body['error']['code']}: {$body['error']['message']}" . PHP_EOL);
}
echo $body['data']['result'], PHP_EOL;
Tạo key một lần cho mỗi thao tác, ví dụ mỗi hồ sơ, và dùng lại key đó khi gửi lại. Các ví dụ trên tạo key mới mỗi lần chạy cho gọn.
Đọc kết quả
| Kết quả | Ý nghĩa | Trừ lượt |
|---|---|---|
error = null, data.result = VALID | Bộ Công an xác nhận thẻ hợp lệ | Có, 1 lượt |
error = null, data.result = INVALID, data.reason.code = CARD_NOT_VERIFIED | Bộ Công an không xác nhận chứng thư trong chip | Có, 1 lượt |
error = null, data.result = INVALID, data.reason.code = ID_NUMBER_NOT_MATCHED | Bộ Công an báo số CCCD không khớp với thẻ | Có, 1 lượt |
error khác null | Không có kết luận. Xử lý theo mã lỗi | Không |
Với lỗi tạm thời (429, 500, 502, 503, 504), gửi lại với cùng Idempotency-Key.