Tích hợp T-VAN
Tích hợp T-VAN (Mạng giá trị gia tăng về thuế) cung cấp kết nối trực tiếp đến Cơ quan Thuế Việt Nam (CQT) để tra cứu thông tin thuế và tra cứu thông điệp hóa đơn.
Tổng quan
| Thuộc tính | Giá trị |
|---|---|
| Package | @nx/t-van |
| Trạng thái | Active |
| Mục đích | Tích hợp CQT trực tiếp để tuân thủ thuế |
| Tính năng | Tra cứu thông tin thuế, Tra cứu thông điệp hóa đơn |
Tính năng Chính
- Tra cứu thông tin thuế - Truy vấn chi tiết đăng ký thuế doanh nghiệp từ CQT
- Tra cứu thông điệp hóa đơn - Liệt kê và phân trang các thông điệp xử lý hóa đơn điện tử từ nhà cung cấp
- Hỗ trợ đa nhà cung cấp - Chọn nhà cung cấp T-VAN đã đăng ký qua tham số truy vấn
provider - Sẵn sàng tuân thủ - Đáp ứng các yêu cầu của Nghị định 123/2020 và 70/2025
Bắt đầu Nhanh
1. Cấu hình ứng dụng của bạn
// application.ts
import { NxTVanComponent, TVanBindingKeys, ITVanOptions } from '@nx/t-van';
class MyApplication extends BaseApplication {
preConfigure() {
// Cấu hình các client T-VAN
this.bind<ITVanOptions>({ key: TVanBindingKeys.TVAN_CLIENT_OPTIONS })
.toValue({
enableControllers: true,
clients: [
{
name: 'default',
provider: 'VNPAY',
apiKey: process.env.TVAN_API_KEY,
secretKey: process.env.TVAN_SECRET_KEY,
taxCode: process.env.COMPANY_TAX_CODE,
isProduction: process.env.NODE_ENV === 'production',
},
],
});
// Tải thành phần T-VAN
this.component(NxTVanComponent);
}
}2. Thiết lập biến môi trường
# Cấu hình T-VAN
APP_ENV_TVAN_API_KEY=your-api-key
APP_ENV_TVAN_SECRET_KEY=your-secret-key
APP_ENV_TVAN_TAX_CODE=0123456789
APP_ENV_TVAN_PROVIDER=VNPAY
APP_ENV_TVAN_IS_PRODUCTION=falseKiến trúc
Cấu trúc Thành phần
Luồng Tích hợp
Tham khảo API
TVanService cung cấp đúng hai thao tác: queryTaxInformation và queryInvoiceMessages. Cả hai đều nhận một đối tượng opts gồm provider (tùy chọn), params của yêu cầu, và options (tùy chọn).
Tra cứu Thông tin Thuế
Tra cứu chi tiết đăng ký kinh doanh từ CQT. Yêu cầu nhận một mảng taxCodes (tối đa 10 mã mỗi yêu cầu); mảng lớn hơn sẽ được chia lô tự động khi bật fetchAll.
import { inject } from '@venizia/ignis';
import { TVanService, TVanTaxInfoQueryTypes } from '@nx/t-van';
class TaxController {
constructor(
@inject({ key: 'services.TVanService' })
private tvanService: TVanService,
) {}
async lookupTaxInfo(taxCodes: string[]) {
const response = await this.tvanService.queryTaxInformation({
provider: 'vnpay',
params: {
type: TVanTaxInfoQueryTypes.TAX_CODE,
taxCodes,
},
options: { fetchAll: true, continueOnError: true },
});
return response.data; // TaxInfo[]
}
}Ví dụ Phản hồi:
{
"data": [
{
"taxCode": "0102182292",
"fullName": "Tên doanh nghiệp",
"type": "0100",
"status": "00",
"issuedDate": "2020-01-01T00:00:00",
"department": "...",
"managingTaxAuthority": "...",
"chapterLevel": "...",
"chapter": "555",
"updatedDate": "2024-01-01T00:00:00",
"addressLine": "Địa chỉ doanh nghiệp",
"cityCode": "01TTT",
"districtCode": "006HH",
"wardsCode": "1010937",
"fullAddress": "Địa chỉ chi tiết của doanh nghiệp",
"reason": null
}
]
}Tra cứu Thông điệp Hóa đơn
Liệt kê các thông điệp xử lý hóa đơn điện tử mà nhà cung cấp đang lưu cho một hóa đơn. Đây là thao tác tra cứu có phân trang - nó không xác minh tính xác thực của hóa đơn với CQT.
async listInvoiceMessages() {
const response = await this.tvanService.queryInvoiceMessages({
provider: 'vnpay',
params: {
messageTypeCode: 200,
messageCode: 'V0102182292A68308EFF92148B0A26',
taxPayerTaxCode: '1801545696-999',
invoiceNumberSymbol: '1',
invoiceSymbol: 'C22THC',
invoiceNumber: 13,
invoiceIssuerTaxCode: '0101352495',
fromDate: '2022-04-25T14:00:00',
toDate: '2022-04-26T14:00:00',
page: 0,
size: 10,
},
options: { fetchAll: false },
});
return response; // { messages, currentPage, pageSize, totalPages, totalItems }
}Các REST API Endpoint
Khi enableControllers: true, các endpoint sau được gắn dưới đường dẫn controller /t-van.
Endpoint Thông tin Thuế
# Tra cứu thông tin thuế (chia lô được tích hợp trong mảng taxCodes)
POST /t-van/tax-information?provider=vnpay
Content-Type: application/json
{
"type": "00003_TAX_CODE",
"taxCodes": ["0100231226-999", "0100240615", "0100256887"]
}Trả về { "result": [ ...TaxInfo ] }. Cờ truy vấn tùy chọn: fetchAll=true tự động chia lô mảng vượt giới hạn nhà cung cấp; continueOnError=true bỏ qua các lô lỗi riêng lẻ khi bật fetchAll.
Endpoint Thông điệp Hóa đơn
# Liệt kê thông điệp hóa đơn (có phân trang)
GET /t-van/invoices?provider=vnpay&messageTypeCode=200&messageCode=V0102182292A68308EFF92148B0A26&taxPayerTaxCode=1801545696-999&invoiceNumberSymbol=1&invoiceSymbol=C22THC&invoiceNumber=13&invoiceIssuerTaxCode=0101352495&fromDate=2022-04-25T14:00:00&toDate=2022-04-26T14:00:00&page=0&size=10Trả về { messages, currentPage, pageSize, totalPages, totalItems }. Thêm fetchAll=true để gộp mọi trang vào một mảng messages duy nhất.
Chọn Nhà cung cấp
Tham số truy vấn provider tùy chọn (ví dụ vnpay) chọn client đã đăng ký xử lý yêu cầu. Khi bỏ trống, client mặc định được dùng. Không có header chọn client và cũng không có tham số client.
Cấu hình Đa Client
Hỗ trợ nhiều nhà cung cấp T-VAN hoặc tài khoản:
this.bind<ITVanOptions>({ key: TVanBindingKeys.TVAN_CLIENT_OPTIONS })
.toValue({
enableControllers: true,
clients: [
// Nhà cung cấp chính
{
name: 'primary',
provider: 'VNPAY',
apiKey: process.env.VNPAY_API_KEY,
isDefault: true,
},
// Nhà cung cấp dự phòng
{
name: 'backup',
provider: 'VNPAY',
apiKey: process.env.VNPAY_BACKUP_API_KEY,
},
],
});Chọn Nhà cung cấp Cụ thể
// Sử dụng client mặc định
const info = await this.tvanService.queryTaxInformation({ params });
// Định tuyến qua một nhà cung cấp cụ thể
const info = await this.tvanService.queryTaxInformation({
provider: 'vnpay',
params,
});Các Loại Tra cứu Thông tin Thuế
Trường type chọn chế độ tra cứu cho queryTaxInformation:
| Loại Tra cứu | Mô tả |
|---|---|
00001_CITIZEN_IDENTITY_CARD | Tra cứu theo căn cước công dân |
00003_TAX_CODE | Tra cứu theo mã số thuế |
00130_IDENTITY_CARD_TAX_PAYER | Tra cứu người nộp thuế theo CMND/CCCD |
00132_PERSONAL_TAX_CODE | Tra cứu theo mã số thuế cá nhân |
Trường status trong mỗi bản ghi TaxInfo là mã do nhà cung cấp định nghĩa (ví dụ 00); BANA giữ nguyên giá trị này - hãy diễn giải theo tài liệu của nhà cung cấp.
Xử lý Lỗi
T-VAN không tự định nghĩa danh mục lỗi. Lỗi được trả về từ response của nhà cung cấp (HTTP status + payload lỗi) qua getError, và việc kiểm tra provider/options được thực thi trước khi gọi. Yêu cầu có mảng taxCodes rỗng sẽ bị từ chối với thông báo Tax codes array cannot be empty trước khi đến nhà cung cấp.
try {
const { data } = await this.tvanService.queryTaxInformation({
provider: 'vnpay',
params: { type: TVanTaxInfoQueryTypes.TAX_CODE, taxCodes },
});
return data;
} catch (error) {
// Kiểm tra payload response của nhà cung cấp và log service
this.logger.error('[lookupTaxInfo] T-VAN query failed: %s', error);
throw error;
}Tuân thủ Thuế Việt Nam
Xem Tuân thủ T-VAN để biết các nhà cung cấp được hỗ trợ, khi nào sử dụng T-VAN, và yêu cầu của Nghị định 123/2020 và 70/2025.
Các Thực hành Tốt nhất
Caching Thông tin Thuế
// Thông tin thuế không thay đổi thường xuyên - hãy cache nó
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 giờ
async getTaxInfoCached(taxCode: string) {
const cached = await this.cache.get(`tax:${taxCode}`);
if (cached) return cached;
const { data } = await this.tvanService.queryTaxInformation({
provider: 'vnpay',
params: { type: TVanTaxInfoQueryTypes.TAX_CODE, taxCodes: [taxCode] },
});
await this.cache.set(`tax:${taxCode}`, data[0], CACHE_TTL);
return data[0];
}Thao tác Hàng loạt
// queryTaxInformation nhận tối đa 10 mã số thuế mỗi yêu cầu;
// bật fetchAll để tự động chia lô mảng lớn hơn
const taxCodes = orders.map(o => o.customerTaxCode).filter(Boolean);
const uniqueTaxCodes = [...new Set(taxCodes)];
const { data } = await this.tvanService.queryTaxInformation({
provider: 'vnpay',
params: { type: TVanTaxInfoQueryTypes.TAX_CODE, taxCodes: uniqueTaxCodes },
options: { fetchAll: true, continueOnError: true },
});Liên quan
- Module Thuế & Hóa đơn - Yêu cầu nghiệp vụ
- Tích hợp IIAPI - Quản lý hóa đơn điện tử
- Tổng quan Hệ thống - Bối cảnh tuân thủ thuế Việt Nam