Skip to content

Invoice XML Format

Overview

Vietnamese e-invoices must follow the XML format defined by the General Department of Taxation (GDT). This document describes the XML structure, required fields, and validation rules.

XML Structure

Complete XML Example

xml
<?xml version="1.0" encoding="UTF-8"?>
<HDon>
  <DLHDon>
    <!-- General Information -->
    <TTChung>
      <PBan>2.0.0</PBan>              <!-- XML Version -->
      <THDon>Hóa đơn GTGT</THDon>     <!-- Invoice Type Name -->
      <KHMSHDon>1</KHMSHDon>          <!-- Template Code -->
      <KHHDon>C25TAA</KHHDon>         <!-- Template Symbol -->
      <SHDon>00000001</SHDon>         <!-- Invoice Number -->
      <MHSo>01GTKT0/001</MHSo>        <!-- Model Number -->
      <NLap>2025-01-20</NLap>         <!-- Issue Date -->
      <SBKe></SBKe>                   <!-- Serial Declaration -->
      <NBKe></NBKe>                   <!-- Declaration Date -->
      <DVTTe>VND</DVTTe>              <!-- Currency -->
      <TGia>1</TGia>                  <!-- Exchange Rate -->
      <HTTToan>TM/CK</HTTToan>        <!-- Payment Method -->
      <MSTTCGP></MSTTCGP>             <!-- Provider Tax Code -->
      <MSTDVNUNLHDon></MSTDVNUNLHDon> <!-- E-Invoice Provider -->
      <THTToan>1</THTToan>            <!-- Payment Status -->
    </TTChung>

    <!-- Invoice Content -->
    <NDHDon>
      <!-- Seller Information -->
      <NBan>
        <Ten>Công ty TNHH ABC</Ten>        <!-- Company Name -->
        <MST>0123456789</MST>               <!-- Tax Code -->
        <DChi>123 Nguyễn Huệ, Q1, TP.HCM</DChi>  <!-- Address -->
        <SDThoai>028-1234-5678</SDThoai>    <!-- Phone -->
        <DCTDTu>contact@abc.com.vn</DCTDTu> <!-- Email -->
        <STKNHang>1234567890</STKNHang>     <!-- Bank Account -->
        <TNHang>Vietcombank</TNHang>        <!-- Bank Name -->
        <Fax></Fax>                         <!-- Fax -->
        <Website>www.abc.com.vn</Website>   <!-- Website -->
      </NBan>

      <!-- Buyer Information -->
      <NMua>
        <Ten>Khách hàng XYZ</Ten>           <!-- Customer Name -->
        <MST>9876543210</MST>               <!-- Tax Code (B2B) -->
        <DChi>456 Lê Lợi, Q1, TP.HCM</DChi> <!-- Address -->
        <SDThoai>0901234567</SDThoai>       <!-- Phone -->
        <DCTDTu>customer@xyz.com</DCTDTu>   <!-- Email -->
        <HVTNMHang>Nguyễn Văn A</HVTNMHang> <!-- Contact Person -->
        <STKNHang></STKNHang>               <!-- Bank Account -->
        <TNHang></TNHang>                   <!-- Bank Name -->
      </NMua>

      <!-- Invoice Items -->
      <DSHHDVu>
        <HHDVu>
          <TChat>1</TChat>                  <!-- Item Nature: 1=Goods, 2=Service -->
          <STT>1</STT>                      <!-- Line Number -->
          <MHHDVu>SP001</MHHDVu>            <!-- Product Code -->
          <THHDVu>Sản phẩm A</THHDVu>       <!-- Product Name -->
          <DVTinh>Cái</DVTinh>              <!-- Unit -->
          <SLuong>2</SLuong>                <!-- Quantity -->
          <DGia>100000</DGia>               <!-- Unit Price -->
          <TLCKhau>0</TLCKhau>              <!-- Discount Percent -->
          <STCKhau>0</STCKhau>              <!-- Discount Amount -->
          <ThTien>200000</ThTien>           <!-- Line Total (before VAT) -->
          <TSuat>10%</TSuat>                <!-- VAT Rate -->
        </HHDVu>
        <HHDVu>
          <TChat>2</TChat>
          <STT>2</STT>
          <MHHDVu>DV001</MHHDVu>
          <THHDVu>Dịch vụ B</THHDVu>
          <DVTinh>Lần</DVTinh>
          <SLuong>1</SLuong>
          <DGia>50000</DGia>
          <TLCKhau>10</TLCKhau>
          <STCKhau>5000</STCKhau>
          <ThTien>45000</ThTien>
          <TSuat>10%</TSuat>
        </HHDVu>
      </DSHHDVu>

      <!-- Totals Summary -->
      <TToan>
        <TgTCThue>245000</TgTCThue>         <!-- Total before VAT -->
        <TgTThue>24500</TgTThue>            <!-- Total VAT -->
        <TgTTTBSo>269500</TgTTTBSo>         <!-- Grand Total (number) -->
        <TgTTTBChu>Hai trăm sáu mươi chín nghìn năm trăm đồng</TgTTTBChu>
                                            <!-- Grand Total (words) -->

        <!-- VAT Summary by Rate -->
        <THTTLTSuat>
          <LTSuat>
            <TSuat>10%</TSuat>              <!-- VAT Rate -->
            <ThTien>245000</ThTien>         <!-- Taxable Amount -->
            <TThue>24500</TThue>            <!-- VAT Amount -->
          </LTSuat>
        </THTTLTSuat>
      </TToan>
    </NDHDon>
  </DLHDon>

  <!-- Digital Signatures -->
  <DSCKS>
    <NBan>
      <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <!-- Seller's Digital Signature (PKCS#7) -->
      </Signature>
    </NBan>
  </DSCKS>
</HDon>

Field Reference

TTChung (General Information)

ElementRequiredDescriptionFormat
PBanYesXML Version"2.0.0"
THDonYesInvoice Type NameText
KHMSHDonYesTemplate Code1-6
KHHDonYesTemplate SymbolMax 6 chars
SHDonYesInvoice Number8 digits
MHSoYesModel NumberPattern: ##GTKT#/###
NLapYesIssue DateYYYY-MM-DD
DVTTeYesCurrencyISO 4217 (VND)
TGiaYesExchange RateDecimal
HTTToanYesPayment MethodTM/CK/TM-CK

NBan (Seller)

ElementRequiredDescription
TenYesCompany Name
MSTYesTax Code (10 or 13 digits)
DChiYesAddress
SDThoaiNoPhone Number
DCTDTuNoEmail
STKNHangNoBank Account
TNHangNoBank Name

NMua (Buyer)

ElementRequiredDescription
TenYesCustomer Name
MSTB2B OnlyTax Code
DChiNoAddress
HVTNMHangNoContact Person

HHDVu (Line Item)

ElementRequiredDescription
TChatYesNature: 1=Goods, 2=Service
STTYesLine Number
MHHDVuNoProduct Code
THHDVuYesProduct Name
DVTinhYesUnit of Measure
SLuongYesQuantity
DGiaYesUnit Price
TLCKhauNoDiscount Percent
STCKhauNoDiscount Amount
ThTienYesLine Total (before VAT)
TSuatYesVAT Rate

TToan (Totals)

ElementRequiredDescription
TgTCThueYesTotal before VAT
TgTThueYesTotal VAT
TgTTTBSoYesGrand Total (number)
TgTTTBChuYesGrand Total (words)

VAT Rates

RateCodeDescription
0%0%Export, tax-free goods
5%5%Essential goods
8%8%Standard rate (temporary reduction)
10%10%Standard rate
KCTKCTNot subject to VAT
KKKNTKKKNTNon-taxable items

XmlGeneratorService

typescript
@injectable()
class XmlGeneratorService {
  /**
   * Generate GDT-compliant XML from invoice data
   */
  async generateXml(invoice: Invoice): Promise<string> {
    const builder = new xml2js.Builder({
      xmldec: { version: '1.0', encoding: 'UTF-8' },
      renderOpts: { pretty: true, indent: '  ' },
    });

    const xmlObject = this.buildXmlObject(invoice);
    return builder.buildObject(xmlObject);
  }

  private buildXmlObject(invoice: Invoice): object {
    return {
      HDon: {
        DLHDon: {
          TTChung: this.buildTTChung(invoice),
          NDHDon: {
            NBan: this.buildNBan(invoice),
            NMua: this.buildNMua(invoice),
            DSHHDVu: this.buildItems(invoice.items),
            TToan: this.buildTToan(invoice),
          },
        },
        // Signature added after signing
      },
    };
  }

  private buildTTChung(invoice: Invoice): object {
    return {
      PBan: '2.0.0',
      THDon: this.getInvoiceTypeName(invoice.type),
      KHMSHDon: invoice.templateCode,
      KHHDon: invoice.templateSymbol,
      SHDon: invoice.invoiceNumber,
      NLap: this.formatDate(invoice.issueDate),
      DVTTe: invoice.currency,
      TGia: invoice.exchangeRate || 1,
      HTTToan: invoice.paymentMethod,
    };
  }

  private buildItems(items: InvoiceItem[]): object {
    return {
      HHDVu: items.map((item, index) => ({
        TChat: item.isService ? '2' : '1',
        STT: index + 1,
        MHHDVu: item.productCode,
        THHDVu: item.productName,
        DVTinh: item.unit,
        SLuong: item.quantity,
        DGia: item.unitPrice,
        TLCKhau: item.discountPercent || 0,
        STCKhau: item.discountAmount || 0,
        ThTien: item.totalBeforeVat,
        TSuat: this.formatVatRate(item.vatRate),
      })),
    };
  }

  private formatVatRate(rate: number): string {
    if (rate === 0) return '0%';
    return `${rate}%`;
  }
}

Number to Vietnamese Words

typescript
/**
 * Convert number to Vietnamese words for invoice
 */
function numberToVietnameseWords(num: number): string {
  const ones = ['', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín'];
  const tens = ['', 'mười', 'hai mươi', 'ba mươi', 'bốn mươi', 'năm mươi',
                'sáu mươi', 'bảy mươi', 'tám mươi', 'chín mươi'];

  if (num === 0) return 'không đồng';

  let result = '';
  let billions = Math.floor(num / 1000000000);
  let millions = Math.floor((num % 1000000000) / 1000000);
  let thousands = Math.floor((num % 1000000) / 1000);
  let remainder = num % 1000;

  if (billions > 0) {
    result += readThreeDigits(billions) + ' tỷ ';
  }
  if (millions > 0) {
    result += readThreeDigits(millions) + ' triệu ';
  }
  if (thousands > 0) {
    result += readThreeDigits(thousands) + ' nghìn ';
  }
  if (remainder > 0) {
    result += readThreeDigits(remainder);
  }

  return capitalizeFirst(result.trim()) + ' đồng';
}

// Example: 269500 -> "Hai trăm sáu mươi chín nghìn năm trăm đồng"

XML Validation

Schema Validation

typescript
import * as libxmljs from 'libxmljs';

async function validateXml(xml: string, schemaPath: string): Promise<ValidationResult> {
  const xmlDoc = libxmljs.parseXml(xml);
  const xsdDoc = libxmljs.parseXml(
    await fs.readFile(schemaPath, 'utf-8')
  );

  const isValid = xmlDoc.validate(xsdDoc);
  const errors = xmlDoc.validationErrors;

  return {
    isValid,
    errors: errors.map(e => ({
      line: e.line,
      message: e.message,
    })),
  };
}

Required Field Validation

typescript
interface ValidationRule {
  path: string;
  required: boolean;
  pattern?: RegExp;
  maxLength?: number;
}

const validationRules: ValidationRule[] = [
  { path: 'HDon.DLHDon.TTChung.PBan', required: true, pattern: /^2\.0\.0$/ },
  { path: 'HDon.DLHDon.TTChung.SHDon', required: true, pattern: /^\d{8}$/ },
  { path: 'HDon.DLHDon.NDHDon.NBan.MST', required: true, pattern: /^\d{10}|\d{13}$/ },
  { path: 'HDon.DLHDon.NDHDon.NBan.Ten', required: true, maxLength: 400 },
  { path: 'HDon.DLHDon.NDHDon.NMua.Ten', required: true, maxLength: 400 },
];

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