Mô hình miền
Payment sở hữu 2 bảng (
WebhookConfig+ các row theo merchant trong bảngConfigurationchia sẻ). Schema Transaction / PaymentAttempt / PaymentResult thuộc sở hữu của@nx/mq-pay(cross-package read cho payment).
1. ERD đầy đủ
2. Cột chung
Mọi entity thêm các cột này qua generateCommonColumnDefs():
| Cột | Kiểu |
|---|---|
id | text (PK, Snowflake) |
createdAt / modifiedAt / deletedAt | timestamptz |
createdBy / modifiedBy | text |
metadata | jsonb |
3. Entity
3.1 WebhookConfig
| Thuộc tính | Giá trị |
|---|---|
| Bảng | WebhookConfig |
| Nguồn | core/src/models/schemas/public/webhook-config/schema.ts |
| Soft-delete | có |
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
name | text | ✓ | Tên hiển thị |
url | text | ✓ | Endpoint webhook |
eventTypes | text[] | ✓ | Sự kiện đã đăng ký (ví dụ ['mq-pay:attempt.success']) |
status | text | ✓ | ACTIVATED (mặc định) / DEACTIVATED |
signingMethod | text | ✓ | NONE (mặc định) / HMAC_SHA256 |
secret | text | Secret ký (sử dụng khi signingMethod ≠ NONE) | |
headers | jsonb | HTTP header tùy chỉnh, mặc định {} | |
metadata | jsonb | Mặc định { timeoutMs: 30000, maxRetries: 3 } |
Index: (status).
3.2 Configuration (các row liên quan đến payment)
| Thuộc tính | Giá trị |
|---|---|
| Bảng | Configuration (chia sẻ) |
| Nguồn | core/src/models/schemas/public/configuration/schema.ts |
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
code | text | ✓ | VNPAY_QR_MMS / VNPAY_PHONE_POS / mã credential theo merchant |
group | text | ✓ | INTEGRATION cho payment config |
principalType | text | MERCHANT cho row theo merchant | |
principalId | text | Merchant id | |
environment | text | ✓ | DEVELOPMENT / PRODUCTION |
tValue | text | JSON config được mã hóa (provider config như appId, masterMerchantCode) | |
credential | text | Credential được mã hóa — ẩn khỏi đọc CRUD; AES-256-GCM | |
dataType | text | ✓ | TEXT / JSON / BOOLEAN |
status | text | ✓ | ACTIVATED (mặc định) |
Ràng buộc:
- Partial unique:
(group, code, principalId, principalType, environment) WHERE deletedAt IS NULL - Index:
(group, code, environment),(group, principalType), GINmetadata,(principalId),(principalType, principalId),(status)
3.3 Transaction (từ @nx/mq-pay)
| Trường | Kiểu | Ghi chú |
|---|---|---|
status | enum | NEW (1xx) / PARTIAL (3xx) / SETTLED (3xx) / CANCELLED (5xx) / BLOCKED (5xx) / CLOSED (5xx) |
total / paid | decimal | Theo dõi số tiền |
sourceType / sourceId | text | Đa hình — thường là SaleOrder / SaleCheck |
metadata.merchant.source.id | jsonb path | Phạm vi merchant, dùng bởi PaymentSocketEventService |
3.4 PaymentAttempt (từ @nx/mq-pay)
| Trường | Kiểu | Ghi chú |
|---|---|---|
transactionId | text | FK |
status | enum | NEW (100) / SENT (200) / SUCCESS (300) / FAIL (500) / EXPIRED (510) |
paymentProvider | text | VNPAY_QR_MMS / VNPAY_PHONE_POS / VNPAY_SMART_POS / SYSTEM |
metadata.source | jsonb | { id, uid, type } — order/check khởi tạo |
4. Status Enums
4.1 PaymentProviders (từ @nx/mq-pay)
| Giá trị | Trạng thái |
|---|---|
SYSTEM | Tiền mặt / chuyển khoản ngân hàng thủ công (in-platform) |
VNPAY_QR_MMS | Hỗ trợ |
VNPAY_PHONE_POS | Hỗ trợ |
VNPAY_SMART_POS | Hỗ trợ (placeholder) |
MOMO | KHÔNG HỖ TRỢ |
ZALOPAY | KHÔNG HỖ TRỢ |
VIETQR | Placeholder |
4.2 Vòng đời Transaction
| Code | Trạng thái | Ghi chú |
|---|---|---|
100 | NEW | Đã tạo |
300 | PARTIAL | Đã nhận một phần thanh toán |
300 | SETTLED | Đã thanh toán đầy đủ (immutable) |
500 | CANCELLED | Tự động hủy (chưa có thanh toán) |
500 | BLOCKED | Admin chặn (có thể đảo ngược) |
500 | CLOSED | Admin đóng (không thể đảo ngược) |
4.3 Vòng đời PaymentAttempt
| Code | Trạng thái |
|---|---|
100 | NEW |
200 | SENT (đã có QR) |
300 | SUCCESS |
500 | FAIL |
510 | EXPIRED |
4.4 Hành động credential (theo IMQPayOptions.credentialGetter)
| Hành động | Sử dụng |
|---|---|
CREATE_PAYMENT | Ký request đến nhà cung cấp |
VERIFY_IPN | Xác minh IPN của nhà cung cấp |
CHECK_TRANSACTION | Truy vấn trạng thái transaction |
CANCEL_PAYMENT | Hủy thanh toán đang chờ |
REFUND | Hoàn tiền |
4.5 Loại credential
| Loại | Mục đích |
|---|---|
REQUEST | Ký request đến nhà cung cấp |
RESPONSE | Xác minh response từ nhà cung cấp |
IPN | Xác minh IPN |
PLATFORM_UNIT_TOKEN | Token theo từng đơn vị (ví dụ cấp terminal) |
4.6 Topic WebSocket (PaymentWebSocketTopics)
| Topic | Nguồn |
|---|---|
observation/payment/transaction | Sự kiện vòng đời Transaction |
observation/payment/payment-attempt | Sự kiện Attempt |
5. Invariant liên entity
| Invariant | Cưỡng chế |
|---|---|
Configuration.credential đã mã hóa không bao giờ được đọc trực tiếp qua CRUD repository | Schema đánh dấu credential là hidden; truy cập thô qua drizzle connector chỉ trong PaymentConfigurationService |
WebhookConfig.eventTypes[] chứa tên sự kiện hợp lệ | Validation zod tại lớp service |
| Chuyển trạng thái Transaction tuân theo state machine MQ-Pay | Thuộc sở hữu của @nx/mq-pay |
| Phân phối webhook at-least-once đến subscriber | WebhookDispatcherService với retry + timeout |
| Secret mã hóa nhất quán giữa các pod | Lấy từ APP_ENV_APPLICATION_SECRET (phải giống nhau) |
6. Hành vi Soft-delete
| Entity | Soft-delete | Ghi chú |
|---|---|---|
WebhookConfig | ✓ | Ưu tiên deactivate qua status thay vì delete |
Configuration (row payment) | ✓ | Migration seed tạo lại khi bootstrap |