Skip to content

T-VAN Integration

T-VAN (Tax Value Added Network) integration provides direct connection to Vietnam's Tax Authority (CQT) for tax information queries and invoice submission compliance.

Overview

AttributeValue
Package@nx/t-van
StatusProduction
PurposeDirect CQT integration for tax compliance
FeaturesTax info lookup, Invoice validation

Key Features

  • Tax information lookup - Query business tax registration details
  • Invoice validation - Verify invoice authenticity with CQT
  • Multi-provider support - Connect to various T-VAN providers
  • Compliance ready - Meet Decree 123/2020 and 70/2025 requirements

Quick Start

1. Configure your application

typescript
// application.ts
import { NxTVanComponent, TVanBindingKeys, ITVanOptions } from '@nx/t-van';

class MyApplication extends BaseApplication {
  preConfigure() {
    // Configure T-VAN clients
    this.bind<ITVanOptions>({ key: TVanBindingKeys.TVAN_CLIENT_OPTIONS })
      .toValue({
        enableControllers: true,
        clients: [
          {
            name: 'default',
            provider: 'VIETTEL',
            apiKey: process.env.TVAN_API_KEY,
            secretKey: process.env.TVAN_SECRET_KEY,
            taxCode: process.env.COMPANY_TAX_CODE,
            isProduction: process.env.NODE_ENV === 'production',
          },
        ],
      });

    // Load T-VAN component
    this.component(NxTVanComponent);
  }
}

2. Set environment variables

bash
# T-VAN Configuration
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=VIETTEL
APP_ENV_TVAN_IS_PRODUCTION=false

Architecture

Component Structure

🧩
NxTVanComponent
Tax VAN integration component
🔗
Custom Bindings
🏭TVanClientProviderClient factory
📚TVanClientRegistryMulti-client registry
⚙️
Services
🏛️TVanServiceCore T-VAN operations
🌐
Controllers
📄TVanInvoiceControllerInvoice-related endpoints
🔍TVanTaxInfoControllerTax info lookup endpoints

Integration Flow

📱
BANA
Application
API Call
🔌
T-VAN
Provider
Forward
🏛️
CQT
Tax Authority
1Application sends Tax Info Request to T-VAN Provider
2T-VAN forwards request to CQT (Tax Authority)
3CQT returns response to T-VAN Provider
4T-VAN returns processed result to Application

API Reference

Lookup Tax Information

Query business registration details from CQT:

typescript
import { inject } from '@venizia/ignis';
import { TVanService } from '@nx/t-van';

class TaxController {
  constructor(
    @inject({ key: 'services.TVanService' })
    private tvanService: TVanService,
  ) {}

  async lookupTaxInfo(taxCode: string) {
    const result = await this.tvanService.getTaxInfo({
      taxCode: taxCode,
    });

    return {
      taxCode: result.taxCode,
      companyName: result.companyName,
      address: result.address,
      representative: result.representative,
      status: result.status, // ACTIVE, SUSPENDED, CLOSED
      registrationDate: result.registrationDate,
    };
  }
}

Response Example:

json
{
  "taxCode": "0123456789",
  "companyName": "CÔNG TY TNHH ABC",
  "address": "123 Nguyễn Huệ, Quận 1, TP.HCM",
  "representative": "Nguyễn Văn A",
  "status": "ACTIVE",
  "registrationDate": "2020-01-15",
  "businessType": "Retail",
  "phoneNumber": "028-1234-5678"
}

Validate Invoice

Verify an invoice exists and is valid with CQT:

typescript
async validateInvoice(invoiceNumber: string, sellerTaxCode: string) {
  const result = await this.tvanService.validateInvoice({
    invoiceNumber: invoiceNumber,
    sellerTaxCode: sellerTaxCode,
    invoiceDate: new Date('2024-01-15'),
  });

  return {
    isValid: result.isValid,
    invoiceStatus: result.status, // VALID, CANCELLED, REPLACED
    totalAmount: result.totalAmount,
    buyerTaxCode: result.buyerTaxCode,
  };
}

Batch Tax Info Lookup

Query multiple businesses at once:

typescript
async batchLookup(taxCodes: string[]) {
  const results = await this.tvanService.batchGetTaxInfo({
    taxCodes: taxCodes,
  });

  return results.map(r => ({
    taxCode: r.taxCode,
    companyName: r.companyName,
    status: r.status,
    found: r.found,
  }));
}

REST API Endpoints

When enableControllers: true, these endpoints are available:

Tax Info Endpoints

http
# Get tax information by tax code
GET /api/t-van/tax-info/:taxCode

# Batch lookup
POST /api/t-van/tax-info/batch
Content-Type: application/json

{
  "taxCodes": ["0123456789", "9876543210"]
}

Invoice Endpoints

http
# Validate invoice
POST /api/t-van/invoice/validate
Content-Type: application/json

{
  "invoiceNumber": "AA/24E0001234",
  "sellerTaxCode": "0123456789",
  "invoiceDate": "2024-01-15"
}

Multi-Client Configuration

Support multiple T-VAN providers or accounts:

typescript
this.bind<ITVanOptions>({ key: TVanBindingKeys.TVAN_CLIENT_OPTIONS })
  .toValue({
    enableControllers: true,
    clients: [
      // Primary provider
      {
        name: 'primary',
        provider: 'VIETTEL',
        apiKey: process.env.VIETTEL_API_KEY,
        isDefault: true,
      },
      // Backup provider
      {
        name: 'backup',
        provider: 'VNPT',
        apiKey: process.env.VNPT_API_KEY,
      },
    ],
  });

Using Specific Client

typescript
// Use default client
const info = await this.tvanService.getTaxInfo({ taxCode });

// Use specific client
const info = await this.tvanService.getTaxInfo({ taxCode }, {
  clientName: 'backup',
});

Tax Status Reference

Business Status

StatusDescriptionCan Issue Invoices
ACTIVEOperating normallyYes
SUSPENDEDTemporarily suspendedNo
CLOSEDPermanently closedNo
PENDINGRegistration pendingNo

Invoice Status

StatusDescription
VALIDInvoice is valid and accepted
CANCELLEDInvoice has been cancelled
REPLACEDInvoice replaced by another
NOT_FOUNDInvoice not found in CQT

Error Handling

Common Errors

Error CodeDescriptionSolution
TVAN_001Invalid tax code formatTax code must be 10 or 13 digits
TVAN_002Tax code not foundVerify tax code exists
TVAN_003Connection timeoutRetry or check network
TVAN_004Provider errorCheck provider status
TVAN_005Rate limit exceededWait and retry

Error Handling Example

typescript
try {
  const info = await this.tvanService.getTaxInfo({ taxCode });
  return info;
} catch (error) {
  if (error.code === 'TVAN_002') {
    return { found: false, message: 'Tax code not registered' };
  }
  throw error;
}

Vietnam Tax Compliance

When to Use T-VAN

Use CaseT-VAN Feature
Verify customer tax codeTax info lookup
Validate supplier invoicesInvoice validation
Check business legitimacyTax info lookup
Pre-invoice verificationTax info lookup

Compliance Requirements

RequirementT-VAN Support
Verify buyer tax code before B2B invoiceTax info lookup
Validate received invoicesInvoice validation
Check supplier statusTax info lookup

Best Practices

Caching Tax Info

typescript
// Tax info doesn't change frequently - cache it
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async getTaxInfoCached(taxCode: string) {
  const cached = await this.cache.get(`tax:${taxCode}`);
  if (cached) return cached;

  const info = await this.tvanService.getTaxInfo({ taxCode });
  await this.cache.set(`tax:${taxCode}`, info, CACHE_TTL);

  return info;
}

Batch Operations

typescript
// More efficient than individual lookups
const taxCodes = orders.map(o => o.customerTaxCode).filter(Boolean);
const uniqueTaxCodes = [...new Set(taxCodes)];

const taxInfoMap = await this.tvanService.batchGetTaxInfo({
  taxCodes: uniqueTaxCodes,
});

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