Skip to content

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ínhGiá trị
Package@nx/t-van
Trạng tháiActive
Mục đíchTích hợp CQT trực tiếp để tuân thủ thuế
Tính năngTra 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

typescript
// 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

bash
# 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=false

Kiế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: queryTaxInformationqueryInvoiceMessages. 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.

typescript
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:

json
{
  "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.

typescript
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ế

http
# 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

http
# 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=10

Trả 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:

typescript
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ể

typescript
// 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ứuMô tả
00001_CITIZEN_IDENTITY_CARDTra cứu theo căn cước công dân
00003_TAX_CODETra cứu theo mã số thuế
00130_IDENTITY_CARD_TAX_PAYERTra cứu người nộp thuế theo CMND/CCCD
00132_PERSONAL_TAX_CODETra 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.

typescript
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ế

typescript
// 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

typescript
// 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

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.