<aside> <img src="/icons/checklist_green.svg" alt="/icons/checklist_green.svg" width="40px" />
</aside>
Table payments [note: 'Ghi nhận giao dịch thanh toán cho order']
{
id uuid [pk, note: 'Khóa chính giao dịch thanh toán']
user_id uuid [ref: > users.id, note: 'Người thanh toán']
order_id uuid [ref: > orders.id, note: 'Thuộc đơn hàng nào']
amount int [not null, note: 'Số tiền thực thu']
currency varchar [not null, note: 'Đơn vị tiền']
method payment_method [not null, note: 'manual/paypal/stripe']
status payment_status [default: 'pending', note: 'pending/completed/failed']
notes text [null]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes { (user_id) (order_id) }
}
Table manual_payments [note: 'Chi tiết giao dịch thanh toán chuyển khoản tay']
{
id uuid [pk, default: gen_random_uuid(), note: 'Khóa chính riêng cho bảng này']
payment_id uuid [pk, ref: > payments.id, note: 'Liên kết payments']
proof_image_url varchar [null, note: 'Ảnh biên lai chuyển khoản']
user_confirmed_at timestamp [null, note: 'User nhấn "Tôi đã chuyển"']
admin_confirmed_by uuid [null, ref: > users.id, note: 'Admin duyệt']
admin_confirmed_at timestamp [null, note: 'Thời điểm admin xác nhận']
status enum [default: 'pending', enum: 'pending', 'submitted', 'approved', 'rejected']
rejection_reason text [NULL]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
}
Table gateway_payments [note: 'Chi tiết giao dịch qua PayPal/Stripe và webhook callback']
{
id uuid [pk, default: gen_random_uuid(), note: 'Khóa chính riêng cho bảng này']
payment_id uuid [ref: > payments.id, note: 'Liên kết đến payments']
gateway payment_gateway [not null, note: 'paypal hoặc stripe']
provider_transaction_id varchar(255) [not null, note: 'Mã giao dịch do cổng trả về (PaymentIntent ID, Order ID…)']
provider_event_id varchar(255) [not null, note: 'ID của webhook event (để idempotency)']
provider_response jsonb [not null, note: 'Payload gốc webhook lưu lại để debug']
provider_status varchar(50) [not null, note: 'Status như COMPLETED, FAILED, PENDING… do cổng trả về']
failure_code varchar(100) [note: 'Mã lỗi nếu thanh toán thất bại (nếu có)']
failure_message text [note: 'Thông điệp lỗi từ cổng (nếu có)']
processed_at timestamptz [not null, note: 'Thời điểm hệ thống xử lý callback']
created_at timestamptz [default: now(), note: 'Thời điểm bản ghi được tạo']
updated_at timestamptz [default: now(), note: 'Thời điểm bản ghi cuối cùng được cập nhật']
indexes {
(provider_transaction_id) [unique], -- Mỗi giao dịch cổng chỉ lưu 1 lần
(provider_event_id) [unique], -- Đảm bảo idempotency khi callback lặp
(payment_id) [non_unique]
}
}