# EsimLao Authentication API Documentation ## Overview API xác thực người dùng qua email với OTP (One-Time Password). URL_UAT : http://149.28.132.56:8360/ --- ## 1. Request OTP Gửi mã OTP đến email người dùng để xác thực đăng nhập. ### Endpoint ``` POST /apis/auth/request-otp ``` ### Request Headers | Header | Value | Required | |--------|-------|----------| | Content-Type | application/json | Yes | ### Request Body ```json { "email": "user@example.com", "lang": "lo" // Default: "lo" / en } ``` ### Parameters | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | email | string | Yes | - | Email address của người dùng | | lang | string | No | "vi" | Ngôn ngữ email: `vi` (Tiếng Việt), `en` (English), `lo` (ລາວ) | ### Response Success (200) ```json { "errorCode": "0", "message": "", "data": { "email": "user@example.com", "expireInSeconds": 300 } } ``` ### Response Error (200) ```json // Email không được cung cấp { "errorCode": "-801", "message": "", "data": {} } // Lỗi hệ thống { "errorCode": "-6", "message": "", "data": {} } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | errorCode| string | "0" = Success, khác "0" = Error (xem Error Codes) | | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) | | data.email | string | Email đã gửi OTP | | data.expireInSeconds | int | Thời gian OTP hết hạn (giây) | ### Notes - OTP gồm 6 chữ số - OTP có hiệu lực trong 5 phút - Mỗi lần request mới sẽ hủy các OTP cũ chưa sử dụng - Nếu email chưa tồn tại, hệ thống tự động tạo tài khoản mới --- ## 2. Verify OTP Xác thực mã OTP và hoàn tất đăng nhập. ### Endpoint ``` POST /apis/auth/verify-otp ``` ### Request Headers | Header | Value | Required | |--------|-------|----------| | Content-Type | application/json | Yes | ### Request Body ```json { "email": "user@example.com", "otpCode": "123456", "lang": "lo" // Default: "lo" / en } ``` ### Parameters | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | email | string | Yes | - | Email đã nhận OTP | | otpCode | string | Yes | - | Mã OTP 6 số | | lang | string | No | "lo" | Ngôn ngữ thông báo: `lo` (ລາວ), `en` (English) | ### Response Success (200) ```json { "errorCode": "0", "message": "", "data": { "userId": 12345, "email": "user@example.com", "fullName": "Nguyen Van A", "avatarUrl": "https://example.com/avatar.jpg", "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...", "expiresAt": "2024-12-30T10:00:00Z" } } ``` ### Response Error Cases ```json // Thiếu email hoặc OTP { "errorCode": "-801", "message": "", "data": {} } // OTP không hợp lệ { "errorCode": "-201", "message": "", "data": {} } // OTP đã được sử dụng { "errorCode": "-203", "message": "", "data": {} } // OTP đã hết hạn { "errorCode": "-202", "message": "", "data": {} } // Không tìm thấy người dùng { "errorCode": "-300", "message": "", "data": {} } // Lỗi hệ thống { "errorCode": "-6", "message": "", "data": {} } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | errorCode| string | "0" = Success, khác "0" = Error (xem Error Codes) | | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) | | data.userId | int | ID người dùng | | data.email | string | Email người dùng | | data.fullName | string | Họ tên đầy đủ | | data.avatarUrl | string | URL ảnh đại diện (nullable) | | data.accessToken | string | JWT access token | | data.refreshToken | string | Refresh token để làm mới access token | | data.expiresAt | datetime | Thời điểm access token hết hạn | ### Notes - Access token có hiệu lực 24 giờ - Refresh token có hiệu lực 30 ngày - Mỗi lần đăng nhập thành công, các token cũ sẽ bị thu hồi --- ## Error Codes ### Success | errorCode| Constant | Description | |------|----------|-------------| | "0" | Success | Thành công (mọi request thành công đều trả về "0") | ### General Errors (-1 to -99) | errorCode| Constant | Description | |------|----------|-------------| | "-1" | Error | Lỗi chung | | "-6" | SystemError | Lỗi hệ thống | ### OTP Errors (-200 to -299) | errorCode| Constant | Description | |------|----------|-------------| | "-200" | OtpRequired | Yêu cầu OTP | | "-201" | OtpInvalid | OTP không hợp lệ | | "-202" | OtpExpired | OTP đã hết hạn | | "-203" | OtpAlreadyUsed | OTP đã được sử dụng | | "-204" | OtpMaxAttemptsExceeded | Vượt quá số lần thử | | "-205" | OtpSendFailed | Gửi OTP thất bại | | "-206" | OtpTooManyRequests | Request quá nhiều | ### User Errors (-300 to -399) | errorCode| Constant | Description | |------|----------|-------------| | "-300" | UserNotFound | Không tìm thấy người dùng | | "-304" | InvalidEmail | Email không hợp lệ | ### Validation Errors (-800 to -899) | errorCode| Constant | Description | |------|----------|-------------| | "-801" | RequiredFieldMissing | Thiếu trường bắt buộc | --- ## Authentication Sau khi đăng nhập thành công, sử dụng `accessToken` trong header cho các API yêu cầu xác thực: ``` Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` --- ## Flow Diagram ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Client │ │ API │ │ Email │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ POST /request-otp │ │ │──────────────────>│ │ │ │ │ │ │ Generate OTP │ │ │ Save to DB │ │ │ Queue Email │ │ │ │ │ Response │ │ │<──────────────────│ │ │ │ │ │ │ Send OTP Email │ │ │──────────────────>│ │ │ │ │ │ │ OTP Email │<──────────────────────────────────────│ │ │ │ │ POST /verify-otp │ │ │──────────────────>│ │ │ │ │ │ │ Verify OTP │ │ │ Generate JWT │ │ │ │ │ Token Response │ │ │<──────────────────│ │ │ │ │ ``` --- ## Example Usage (cURL) ### Request OTP ```bash curl -X POST https://api.esimlao.com/apis/auth/request-otp \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "lang": "vi" }' ``` ### Verify OTP ```bash curl -X POST https://api.esimlao.com/apis/auth/verify-otp \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "otpCode": "123456" }' ``` ### Use Token ```bash curl -X GET https://api.esimlao.com/apis/user/profile \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` --- ## 3. Article Category Lấy danh sách danh mục bài viết. ### Endpoint ``` POST /apis/article/category ``` ### Request ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10, "parentId": null } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | lang | string | "lo" | Ngôn ngữ: "lo", "en" (có thể truyền qua header Accept-Language) | | pageNumber | int | 0 | Trang hiện tại | | pageSize | int | 10 | Số item mỗi trang | | parentId | int? | null | ID danh mục cha (null = root) | ### Response Success ```json { "errorCode": "0", "message": "Success", "data": { "categories": [ { "id": 1, "categoryName": "Cẩm nang du lịch", "categorySlug": "cam-nang-du-lich", "description": "Mô tả...", "iconUrl": "/icons/travel.png", "parentId": null, "displayOrder": 1 } ], "pagination": { "pageNumber": 0, "pageSize": 10, "totalCount": 5, "totalPages": 1 } } } ``` --- ## 4. Article Load Lấy danh sách bài viết hoặc chi tiết 1 bài viết. ### Endpoint ``` POST /apis/article/load ``` ### Request (danh sách) ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10, "categoryId": 1, "isFeatured": false } ``` ### Request (chi tiết) ```json { "lang": "lo", "slug": "huong-dan-cai-dat-esim" } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) | | pageNumber | int | 0 | Trang hiện tại | | pageSize | int | 10 | Số item mỗi trang | | categoryId | int? | null | Lọc theo danh mục | | isFeatured | bool? | null | Lọc bài viết nổi bật | | slug | string? | null | Slug để lấy chi tiết bài viết | ### Response Success (danh sách) ```json { "errorCode": "0", "message": "Success", "data": { "articles": [ { "id": 1, "title": "Hướng dẫn cài đặt eSIM", "slug": "huong-dan-cai-dat-esim", "summary": "Tóm tắt...", "thumbnailUrl": "/images/article1.jpg", "categoryId": 1, "viewCount": 150, "isFeatured": true, "isPinned": false, "publishedDate": "2024-12-25" } ], "pagination": {...} } } ``` ### Response Success (chi tiết) ```json { "errorCode": "0", "message": "Success", "data": { "article": { "id": 1, "title": "Hướng dẫn cài đặt eSIM", "slug": "huong-dan-cai-dat-esim", "summary": "Tóm tắt...", "content": "

Nội dung HTML...

", "thumbnailUrl": "/images/article1.jpg", "coverImageUrl": "/images/cover1.jpg", "metaDescription": "SEO description", "metaKeywords": "esim, laos", "categoryId": 1, "viewCount": 151, "isFeatured": true, "publishedDate": "2024-12-25", "createdDate": "2024-12-20" } } } ``` --- ## 5. Banner Load Lấy danh sách banner. ### Endpoint ``` POST /apis/content/banner ``` ### Request ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10, "position": "home" } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) | | pageNumber | int | 0 | Trang hiện tại | | pageSize | int | 10 | Số item mỗi trang | | position | string? | null | Vị trí: "home", "sidebar"... | ### Response ```json { "errorCode": "0", "data": { "banners": [ { "id": 1, "title": "Banner Title", "subtitle": "Subtitle", "imageUrl": "/images/banner1.jpg", "imageMobileUrl": "/images/banner1_m.jpg", "linkUrl": "/promo", "linkTarget": "_blank", "position": "home", "displayOrder": 1 } ], "pagination": {...} } } ``` --- ## 6. Customer Review Load Lấy đánh giá của khách hàng. ### Endpoint ``` POST /apis/content/review ``` ### Request ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10, "isFeatured": true } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | lang | string | "lo" | Ngôn ngữ | | pageNumber | int | 0 | Trang | | pageSize | int | 10 | Số item | | isFeatured | bool? | null | Lọc review nổi bật | ### Response ```json { "errorCode": "0", "data": { "reviews": [ { "id": 1, "customerName": "Nguyen Van A", "avatarUrl": "/avatars/user1.jpg", "rating": 1, "reviewContent": "Dich vu rat tot...", "destination": "Vientiane, Laos", "isFeatured": true, "createdDate": "2024-12-25" } ], "pagination": {...} } } ``` --- ## 6.1 Customer Review Create Khách hàng gửi đánh giá (chờ duyệt). ### Endpoint ``` POST /apis/content/review/create ``` ### Request ```json { "lang": "lo", "customerName": "Nguyen Van A", "reviewContent": "Dich vu rat tot, toi rat hai long!", "destination": "Vientiane, Laos", "rating": 5 } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | lang | string | No | Ngôn ngữ | | customerName | string | Yes | Tên khách hàng | | reviewContent | string | Yes | Nội dung đánh giá | | destination | string | No | Địa điểm | | rating | int | No | Đánh giá (1-5) | ### Response Success ```json { "errorCode": "0", "message": "Review submitted successfully", "data": { "reviewId": 123 } } ``` ### Note - Review mới sẽ có `Status = false` (chờ admin duyệt) --- ## 7. FAQ Category Load Lấy danh mục FAQ. ### Endpoint ``` POST /apis/content/faq-category ``` ### Request ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10 } ``` ### Response ```json { "errorCode": "0", "data": { "categories": [ { "id": 1, "categoryName": "Cài đặt eSIM", "categorySlug": "cai-dat-esim", "description": "Hướng dẫn cài đặt", "iconUrl": "/icons/setup.png", "displayOrder": 1 } ], "pagination": {...} } } ``` --- ## 8. FAQ Load Lấy danh sách câu hỏi thường gặp. ### Endpoint ``` POST /apis/content/faq ``` ### Request ```json { "lang": "lo", "pageNumber": 0, "pageSize": 10, "categoryId": 1, "isFeatured": false } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | lang | string | "lo" | Ngôn ngữ | | pageNumber | int | 0 | Trang | | pageSize | int | 10 | Số item | | categoryId | int? | null | Lọc theo danh mục | | isFeatured | bool? | null | Lọc FAQ nổi bật | ### Response ```json { "errorCode": "0", "data": { "faqs": [ { "id": 1, "question": "Làm sao để cài đặt eSIM?", "answer": "

Hướng dẫn chi tiết...

", "categoryId": 1, "viewCount": 100, "isFeatured": true } ], "pagination": {...} } } ```