Tài liệu / Quy chuẩn API & Mã lỗi

Quy chuẩn API & Mã lỗi

API Standards — EKYC Middleware

Quy chuẩn API chung cho toàn bộ hệ thống. Mọi API đều phải tuân thủ.

1. Response Format Chuẩn

1.1 Thành công (HTTP 2xx)

{
  "status": "success",
  "data": {},
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-07-29T13:00:00+07:00",
    "response_time_ms": 125
  }
}

1.2 Lỗi nghiệp vụ (HTTP 4xx)

{
  "status": "error",
  "data": null,
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-07-29T13:00:00+07:00",
    "response_time_ms": 42
  },
  "code": "SESSION_EXPIRED",
  "message": "Phiên làm việc đã hết hạn"
}

1.3 Lỗi validation (HTTP 422)

{
  "status": "fail",
  "data": null,
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-07-29T13:00:00+07:00",
    "response_time_ms": 15
  },
  "code": "VALIDATION_ERROR",
  "message": "Dữ liệu không hợp lệ",
  "errors": {
    "field_name": [
      "Trường này là bắt buộc",
      "Định dạng không đúng"
    ]
  }
}

1.4 Lỗi hệ thống (HTTP 5xx)

{
  "status": "error",
  "data": null,
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-07-29T13:00:00+07:00",
    "response_time_ms": 2340
  },
  "code": "INTERNAL_SERVER_ERROR",
  "message": "Đã có lỗi xảy ra, vui lòng thử lại sau"
}

2. Field Descriptions

FieldXuất hiệnTypeBắt buộcMô tả
statusallstringCósuccess / error / fail
dataallobject|array|nullCóDữ liệu chính. null khi có lỗi
metaallobjectCóMetadata
meta.request_idallstringCóUUID v4, sinh từ Str::uuid()
meta.timestampallstringCóISO 8601, now()->toIso8601String()
meta.response_time_mssuccessintCóThời gian xử lý (milliseconds)
meta.paginationlistobjectKhôngThông tin phân trang (nếu có)
codeerror/failstringCóMã lỗi máy đọc được
messageerror/failstringCóThông báo cho developer/client
errorsfailobjectCóMap field → mảng lỗi, chỉ validation

Pagination meta (khi response là list)

{
  "data": [...],
  "meta": {
    "request_id": "...",
    "timestamp": "...",
    "response_time_ms": 50,
    "pagination": {
      "current_page": 1,
      "per_page": 15,
      "total": 100,
      "last_page": 7,
      "has_next": true,
      "has_prev": false
    }
  }
}

3. Error Code System

Mã lỗi được tổ chức theo namespace, mã hóa cứng trong App\Enums\ErrorCode.

3.1 Authentication & Authorization (4xx)

CodeHTTPMô tả
AUTHENTICATION_REQUIRED401Thiếu header Authorization
AUTHENTICATION_EXPIRED401JWT đã hết hạn
AUTHENTICATION_INVALID401JWT sai format/chữ ký
MTLS_CERT_INVALID401mTLS certificate không hợp lệ
PARTNER_INACTIVE403Partner đang bị khóa
PARTNER_IP_NOT_ALLOWED403IP không nằm trong whitelist
SCOPE_NOT_PERMITTED403Token không có quyền truy cập resource này

3.2 Validation & Request (4xx)

CodeHTTPMô tả
VALIDATION_ERROR422Dữ liệu đầu vào không hợp lệ (kèm errors)
MALFORMED_REQUEST400JSON body bị lỗi cú pháp
RESOURCE_NOT_FOUND404Endpoint hoặc resource không tồn tại
METHOD_NOT_ALLOWED405Method không được hỗ trợ
RATE_LIMIT_EXCEEDED429Vượt quá rate limit

3.3 Business — Session (4xx)

CodeHTTPMô tả
SESSION_NOT_FOUND404session_id không tồn tại
SESSION_EXPIRED410Session đã hết hạn
SESSION_INVALID_STATUS409Trạng thái session không cho phép hành động
DUPLICATE_TRANSACTION409partner_tx_id đã tồn tại

3.4 Business — Partner (4xx)

CodeHTTPMô tả
PARTNER_TX_NOT_FOUND404Mã giao dịch partner không tồn tại
CONTRACT_NOT_FOUND404Hợp đồng không tồn tại
CONTRACT_ALREADY_SIGNED409Hợp đồng đã được ký
IDENTITY_ALREADY_SUBMITTED409Thông tin định danh đã được gửi trước đó

3.5 System — Core EKYC (5xx)

CodeHTTPMô tả
CORE_EKYC_CONNECTION_FAILED502Không thể kết nối Core EKYC
CORE_EKYC_TIMEOUT504Core EKYC không phản hồi (timeout)
CORE_EKYC_REJECTED502Core EKYC từ chối yêu cầu
CORE_EKYC_INVALID_RESPONSE502Core EKYC trả dữ liệu không hợp lệ

3.6 System — Internal (5xx)

CodeHTTPMô tả
INTERNAL_SERVER_ERROR500Lỗi không xác định, không log detail ra production
DATABASE_ERROR500Lỗi cơ sở dữ liệu
QUEUE_FAILED500Lỗi xử lý job
ENCRYPTION_ERROR500Lỗi mã hóa/giải mã dữ liệu

4. Request Validation

4.1 FormRequest chuẩn

Tất cả validation đều qua FormRequest, custom message tiếng Việt:

class InitSessionRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true; // Auth handled by middleware
    }

    public function rules(): array
    {
        return [
            'partner_tx_id'          => ['required', 'string', 'max:100'],
            'user_id'                => ['required', 'string', 'max:100'],
            'account_number'         => ['required', 'string', 'max:50'],
            'identity_info'          => ['required', 'array'],
            'identity_info.full_name' => ['required', 'string', 'max:255'],
            'identity_info.id_number' => ['required', 'string', 'regex:/^\d{9,12}$/'],
        ];
    }

    public function messages(): array
    {
        return [
            'partner_tx_id.required'           => 'Mã giao dịch đối tác là bắt buộc',
            'identity_info.full_name.required' => 'Họ tên là bắt buộc',
            'identity_info.id_number.regex'    => 'Số CMND/CCCD phải là 9-12 chữ số',
        ];
    }
}

4.2 Validation error response mẫu

Khi validation fail, response tự động:

{
  "status": "fail",
  "data": null,
  "meta": { "...": "..." },
  "code": "VALIDATION_ERROR",
  "message": "Dữ liệu không hợp lệ",
  "errors": {
    "user_id": ["Trường user_id là bắt buộc"],
    "identity_info.id_number": ["Số CMND/CCCD phải là 9-12 chữ số"]
  }
}

5. Headers Chuẩn

5.1 Request Headers

HeaderBắt buộcMô tả
Content-TypeCóapplication/json
AuthorizationCóBearer <JWT> hoặc Bearer <webview_token>
X-Request-IDKhôngUUID để trace, nếu không có BE tự sinh
AcceptKhôngapplication/json

5.2 Response Headers

HeaderMô tả
X-Request-IDRequest ID (trace)
X-RateLimit-LimitSố request tối đa
X-RateLimit-RemainingSố request còn lại
X-RateLimit-ResetThời gian reset (Unix timestamp)
X-Response-Time-MsThời gian xử lý

6. Autentication & Authorization

6.1 Partner API (Bank → BE)

LayerPhương thứcXử lý khi fail
TransportmTLS 1.3 — client cert required401 MTLS_CERT_INVALID
TokenJWT RS256 — Authorization: Bearer401 AUTHENTICATION_*
IPIP Whitelist (cấu hình trong partners)403 PARTNER_IP_NOT_ALLOWED
PartnerCheck partner status403 PARTNER_INACTIVE

6.2 Webview API (Webview → BE)

LayerPhương thứcXử lý khi fail
TokenJWT HS256 — webview_token401 AUTHENTICATION_*
SessionCheck session_id ownership403 SCOPE_NOT_PERMITTED
ExpiryCheck expired_at410 SESSION_EXPIRED

7. Rate Limiting

API GroupLimitKey
Partner API100 requests/phútpartner:{partner_id}
Webview API30 requests/phútsession:{session_id}

Khi vượt quá:

{
  "status": "error",
  "data": null,
  "meta": { "...": "..." },
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Vượt quá giới hạn request, vui lòng thử lại sau 45 giây"
}

8. Logging & Audit

Mọi API request đều được ghi log:

// Structured logging
Log::channel('api')->info('Partner API Request', [
    'request_id'  => $requestId,
    'partner_id'  => $partnerId,
    'endpoint'    => $request->path(),
    'method'      => $request->method(),
    'duration_ms' => $duration,
    'status_code' => $response->status(),
    'ip'          => $request->ip(),
]);

Bảng ekyc_logs lưu toàn bộ request/response cho audit.


9. Scramble Integration

Documentation tự động qua Scramble:

// config/scramble.php
return [
    'api_path' => 'api/v1',
    'info' => [
        'title' => 'EKYC Middleware API',
        'description' => 'API Middleware giữa các Bank và Core EKYC',
        'version' => '1.0.0',
    ],
];

10. Exception Handling Chain

Request
  ├── Parse fail (malformed JSON)
  │   └── MALFORMED_REQUEST (400)
  ├── Validation fail
  │   └── FormRequest → VALIDATION_ERROR (422)
  ├── Auth fail
  │   ├── No JWT → AUTHENTICATION_REQUIRED (401)
  │   ├── JWT expired → AUTHENTICATION_EXPIRED (401)
  │   ├── mTLS invalid → MTLS_CERT_INVALID (401)
  │   └── IP not allowed → PARTNER_IP_NOT_ALLOWED (403)
  ├── Business logic fail
  │   ├── Session expired → SESSION_EXPIRED (410)
  │   ├── Duplicate TX → DUPLICATE_TRANSACTION (409)
  │   └── Custom PartnerException → code tương ứng (4xx)
  ├── Core EKYC fail
  │   └── CoreEkycException → CORE_EKYC_* (502/504)
  └── Unhandled / System
      └── Throwable → INTERNAL_SERVER_ERROR (500)