# Creating ZUGFeRD 2.3 (XRechnung, Factur-X) Documents with .NET C#

> This article shows how to create ZUGFeRD 2.3 compliant invoices using TX Text Control .NET Server. ZUGFeRD 2.3 is the latest version of the ZUGFeRD data model and complies with the European standard EN 16931. The article shows how to create ZUGFeRD 2.3 invoices and how to embed the XML invoice data in a PDF document.

- **Author:** Bjoern Meyer
- **Published:** 2025-01-02
- **Modified:** 2025-11-16
- **Description:** This article shows how to create ZUGFeRD 2.3 compliant invoices using TX Text Control .NET Server. ZUGFeRD 2.3 is the latest version of the ZUGFeRD data model and complies with the European standard EN 16931. The article shows how to create ZUGFeRD 2.3 invoices and how to embed the XML invoice data in a PDF document.
- **6 min read** (1170 words)
- **Tags:**
  - ASP.NET
  - ASP.NET Core
  - ZUGFeRD
  - XRechnung
  - Factur-X
- **Web URL:** https://www.textcontrol.com/blog/2025/01/02/creating-zugferd-2-3-xrechnung-factur-x-documents-with-net-c-sharp/
- **LLMs URL:** https://www.textcontrol.com/blog/2025/01/02/creating-zugferd-2-3-xrechnung-factur-x-documents-with-net-c-sharp/llms.txt
- **LLMs-Full URL:** https://www.textcontrol.com/blog/2025/01/02/creating-zugferd-2-3-xrechnung-factur-x-documents-with-net-c-sharp/llms-full.txt
- **GitHub Repository:** https://github.com/TextControl/TXTextControl.ZUGFeRD23

---

Invoicing standards and laws have evolved significantly with the digital transformation of business processes. ZUGFeRD is a hybrid electronic invoice format that integrates structured machine-readable data with human-readable PDF/A-3 format. Version 2.3 of ZUGFeRD complies with European standard EN 16931, ensuring compatibility with EU-wide e-invoicing directives. Unlike ZUGFeRD, XRechnung focuses exclusively on structured, machine-readable XML data without incorporating a visual PDF representation.

Factur-X is the French counterpart to ZUGFeRD and uses the same hybrid invoicing concept. Developed jointly by Germany and France, it also complies with the EN 16931 standard. Factur-X combines structured XML data with a PDF/A-3 file, making it versatile for both automated processing and human readability. ZUGFeRD 2.3 and Factur-X are built on the same technical foundation and comply with EN 16931.

### ZUGFeRD 2.3 and Factur-X

ZUGFeRD 2.3 and Factur-X are hybrid formats that provide both human-readable and machine-readable components. XRechnung, on the other hand, is a purely structured XML format designed to automate government-related transactions. The pure XML formats are being introduced mainly for government invoices, and it looks like the PDF hybrid versions will be the format of choice for businesses. Both standards are in compliance with the European laws that have been introduced.

This tutorial shows how to create a valid ZUGFeRD 2.3 invoice using [ZUGFeRD-csharp](https://github.com/stephanstapel/ZUGFeRD-csharp), an open source library, and TX Text Control to create the PDF/3-3b with the XML attachment.

### Creating a ZUGFeRD Invoice

The example implements the *XRechnung* class to abstract the data layer from ZUGFeRD-csharp.

```
public class XRechnung
{
    public string InvoiceNumber { get; set; } = Guid.NewGuid().ToString();
    public DateTime InvoiceDate { get; set; } = DateTime.Now;
    public CurrencyCodes Currency { get; set; } = CurrencyCodes.EUR;
    public string OrderNumber { get; set; }
    public DateTime DeliveryDate { get; set; }
    public string PaymentTerms { get; set; } = "Zahlbar innerhalb 30 Tagen netto bis 04.07.2023";
    public DateTime PaymentDueDate { get; set; } = DateTime.Now.AddDays(30);
    public Seller Seller { get; set; }
    public Buyer Buyer { get; set; }
    public List<LineItem> LineItems { get; set; } = new();

    public decimal TotalNetAmount => LineItems.Sum(item => item.LineTotal);
    public decimal TotalTaxAmount => LineItems.Sum(item => item.TaxAmount);
    public decimal TotalGrossAmount => LineItems.Sum(item => item.Total);

    public decimal TotalAllowanceChargeAmount { get; set; } = 0;
    public decimal TotalChargeAmount { get; set; } = 0;
    public decimal TotalPrepaidAmount { get; set; } = 0;
    public decimal TotalRoundingAmount { get; set; } = 0;

    public decimal TotalDueAmount => TotalGrossAmount - TotalPrepaidAmount + TotalTaxAmount;
}
```

The *CreateXML* method creates an XML representation of the *XRechnung* object using the *ZUGFeRD-csharp* library and returns this XML.

```
public string CreateXML()
{
    // Create a new invoice descriptor with basic details
    InvoiceDescriptor desc = InvoiceDescriptor.CreateInvoice(
        this.InvoiceNumber,
        this.InvoiceDate,
        this.Currency
    );

    // Set buyer details
    desc.ReferenceOrderNo = this.OrderNumber;
    desc.SetBuyer(this.Buyer.Name, this.Buyer.ZipCode, this.Buyer.City, this.Buyer.Street, CountryCodes.DE, this.Buyer.Phone);
    desc.AddBuyerTaxRegistration(this.Buyer.VATID, TaxRegistrationSchemeID.VA);
    desc.SetBuyerContact(this.Buyer.Contact);
    desc.SetBuyerOrderReferenceDocument(this.Buyer.OrderReferenceDocument, this.Buyer.OrderReferenceDocumentDate);

    // Set seller details
    desc.SetSeller(this.Seller.Name, this.Seller.ZipCode, this.Seller.City, this.Seller.Street, CountryCodes.DE, this.Seller.Phone);
    desc.AddSellerTaxRegistration(this.Seller.VATID, TaxRegistrationSchemeID.VA);
    desc.SetSellerContact(this.Seller.Contact, this.Seller.OrganizationUnit, this.Seller.Email, this.Seller.Phone);

    // Set delivery date
    desc.ActualDeliveryDate = this.DeliveryDate;

    // Set invoice totals
    desc.SetTotals(
        this.TotalNetAmount,
        this.TotalChargeAmount,
        this.TotalAllowanceChargeAmount,
        this.TotalGrossAmount,
        this.TotalTaxAmount,
        this.TotalNetAmount + this.TotalTaxAmount, // Total Amount Including Taxes
        this.TotalPrepaidAmount,
        this.TotalDueAmount
    );

    // Set payment terms
    desc.AddTradePaymentTerms(this.PaymentTerms, this.PaymentDueDate);

    // Add line items to the invoice
    foreach (LineItem lineItem in this.LineItems)
    {
        desc.AddTradeLineItem(
            lineItem.Name,
            lineItem.Description,
            lineItem.Unit,
            lineItem.Quantity,
            lineItem.UnitPrice + (lineItem.UnitPrice * lineItem.TaxPercent / 100), // Gross Unit Price
            lineItem.UnitPrice, // Net Unit Price
            lineItem.Quantity,
            lineItem.LineTotal,
            lineItem.TaxType,
            TaxCategoryCodes.S,
            lineItem.TaxPercent
        );
    }

    // Add applicable trade tax
    desc.AddApplicableTradeTax(this.TotalNetAmount, 19m, TaxTypes.VAT, TaxCategoryCodes.S);

    // Set payment means
    desc.SetPaymentMeans(PaymentMeansTypeCodes.ClearingBetweenPartners);

    // Write XML to a memory stream
    using MemoryStream stream = new();
    desc.Save(stream, ZUGFeRDVersion.Version23, Profile.XRechnung);

    // Convert the memory stream content to a string
    stream.Seek(0, SeekOrigin.Begin);
    using StreamReader reader = new(stream);
    return reader.ReadToEnd();
}
```

Using sample data, the following code demonstrates how to create a new XRechnung object.

```
XRechnung xRechnung = new XRechnung()
{
    InvoiceNumber = "471102",
    InvoiceDate = new DateTime(2024,12,30),
    Currency = CurrencyCodes.EUR,
    OrderNumber = "AB-312",
    DeliveryDate = new DateTime(2024, 12, 30),
    PaymentTerms = "Zahlbar innerhalb 30 Tagen netto.",
    PaymentDueDate = new DateTime(2025,01,15),
    Seller = new Seller()
    {
        Name = "Lieferant GmbH",
        Street = "Lieferantenstraße",
        ZipCode = "80333",
        City = "München",
        VATID = "DE123456789",
        TaxRegistrationSchemeID = TaxRegistrationSchemeID.VA,
        Contact = "Max Mustermann",
        Phone = "+4942142706710",
        Email = "max@mustermann.de"
    },
    Buyer = new Buyer()
    {
        Name = "Kunden Mitte AG",
        Street = "Kundenstraße",
        ZipCode = "69876",
        City = "Frankfurt",
        VATID = "DE234567890",
        TaxRegistrationSchemeID = TaxRegistrationSchemeID.VA,
        Contact = "Hans Muster",
        OrganizationUnit = "Einkauf",
        Email = "hans@muster.de",
        Phone = "22342424",
        OrderReferenceDocument = "2013-471102",
    },
    LineItems = new List<LineItem>()
        {
            new LineItem()
            {
                Name = "Item name",
                Description = "Detail description",
                Quantity = 1,
                Unit = QuantityCodes.H87,
                UnitPrice = 10m,
                TaxCategory = TaxCategoryCodes.S,
                TaxType = TaxTypes.VAT,
                TaxPercent = 19,
            }
        }
};
```

The *CreateXML* method is then called to generate the XML, which is then used in an EmbeddedFile object along with the XML metadata.

```
// Serialize the XRechnung object to JSON
string json = JsonSerializer.Serialize(xRechnung);

// Generate the ZUGFeRD XML
string xmlZugferd = xRechnung.CreateXML();

// Load metadata from the XML file
string metaData = File.ReadAllText("metadata.xml");

// Create an embedded file for the ZUGFeRD invoice
var zugferdInvoice = new TXTextControl.EmbeddedFile(
    "factur-x.xml",
    Encoding.UTF8.GetBytes(xmlZugferd),
    metaData)
{
    Description = "factur-x",
    Relationship = "Alternative",
    MIMEType = "application/xml",
    LastModificationDate = DateTime.Now
};

// Configure save settings with the embedded file
var saveSettings = new TXTextControl.SaveSettings
{
    EmbeddedFiles = new[] { zugferdInvoice }
};

// Create, modify, and save the PDF document
using (var tx = new TXTextControl.ServerTextControl())
{
    tx.Create();
    tx.Text = "Test Document";
    tx.Save("test.pdf", TXTextControl.StreamType.AdobePDFA, saveSettings);
}
```

The required XML metadata includes the RDF description of the embedded structures.

```
<rdf:Description xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
	                       xmlns:pdfaField="http://www.aiim.org/pdfa/ns/field#"
	                       xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#"
	                       xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
	                       xmlns:pdfaType="http://www.aiim.org/pdfa/ns/type#"
	                       rdf:about=""
	                       >
  <pdfaExtension:schemas>
    <rdf:Bag>
      <rdf:li rdf:parseType="Resource">
        <pdfaSchema:schema>ZUGFeRD PDFA Extension Schema</pdfaSchema:schema>
        <pdfaSchema:namespaceURI>urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#</pdfaSchema:namespaceURI>
        <pdfaSchema:prefix>fx</pdfaSchema:prefix>
        <pdfaSchema:property>
          <rdf:Seq>
            <rdf:li rdf:parseType="Resource">
              <pdfaProperty:name>DocumentFileName</pdfaProperty:name>
              <pdfaProperty:valueType>Text</pdfaProperty:valueType>
              <pdfaProperty:category>external</pdfaProperty:category>
              <pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
            </rdf:li>
            <rdf:li rdf:parseType="Resource">
              <pdfaProperty:name>DocumentType</pdfaProperty:name>
              <pdfaProperty:valueType>Text</pdfaProperty:valueType>
              <pdfaProperty:category>external</pdfaProperty:category>
              <pdfaProperty:description>INVOICE</pdfaProperty:description>
            </rdf:li>
            <rdf:li rdf:parseType="Resource">
              <pdfaProperty:name>Version</pdfaProperty:name>
              <pdfaProperty:valueType>Text</pdfaProperty:valueType>
              <pdfaProperty:category>external</pdfaProperty:category>
              <pdfaProperty:description>The actual version of the ZUGFeRD data</pdfaProperty:description>
            </rdf:li>
            <rdf:li rdf:parseType="Resource">
              <pdfaProperty:name>ConformanceLevel</pdfaProperty:name>
              <pdfaProperty:valueType>Text</pdfaProperty:valueType>
              <pdfaProperty:category>external</pdfaProperty:category>
              <pdfaProperty:description>The conformance level of the ZUGFeRD data</pdfaProperty:description>
            </rdf:li>
          </rdf:Seq>
        </pdfaSchema:property>
      </rdf:li>
    </rdf:Bag>
  </pdfaExtension:schemas>
</rdf:Description>
<rdf:Description xmlns:fx="urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#"
                 fx:ConformanceLevel="EN 16931"
                 fx:DocumentFileName="factur-x.xml"
                 fx:DocumentType="INVOICE"
                 fx:Version="2p0"
                 rdf:about=""/>
```

### Creating the PDF

In this example, we will not generate an actual invoice PDF, but only a dummy text, as this should only focus on showing how to embed the XML invoice part.

> **Learn More**
> 
> This article shows how to create valid XRechnung and ZUGFeRD invoices with ASP.NET Core C#. The invoice is created with the help MailMerge component and the ZUGFeRD-csharp library.
> 
> [Creating Valid XRechnung / ZUGFeRD Invoices with ASP.NET Core C# ](https://www.textcontrol.com/blog/2023/12/28/creating-valid-xrechnung-zugferd-invoices-with-asp-net-core-csharp/llms-full.txt)

When you open the PDF in Acrobat Reader, you can see the attachment in the *Attachments* panel.

![TX Text Control ZUGFeRD invoice](https://s1-www.textcontrol.com/assets/dist/blog/2025/01/02/a/assets/invoice1.webp "TX Text Control ZUGFeRD invoice")

### Validation

To verify that the document is valid according to the settings, you can now upload the document to any validation service.

![ZUGFeRD Validator](https://s1-www.textcontrol.com/assets/dist/blog/2025/01/02/a/assets/invoice2.webp "ZUGFeRD Validator")

### Conclusion

Creating ZUGFeRD invoices is a common requirement for many companies. The *ZUGFeRD-csharp* library provides an easy way to programmatically create the XML content. The TX Text Control library can be used to create the PDF/A-3b file with the embedded XML attachment.

By combining these two libraries, you can create valid ZUGFeRD invoices in C#.

Download the sample from GitHub and test it with your own data.

---

## About Bjoern Meyer

As CEO, Bjoern is the visionary behind our strategic direction and business development, bridging the gap between our customers and engineering teams. His deep passion for coding and web technologies drives the creation of innovative products. If you're at a tech conference, be sure to stop by our booth - you'll most likely meet Bjoern in person. With an advanced graduate degree (Dipl. Inf.) in Computer Science, specializing in AI, from the University of Bremen, Bjoern brings significant expertise to his role. In his spare time, Bjoern enjoys running, paragliding, mountain biking, and playing the piano.

- [LinkedIn](https://www.linkedin.com/in/bjoernmeyer/)
- [X](https://x.com/txbjoern)
- [GitHub](https://github.com/bjoerntx)

---

## Related Posts

- [Visualize and Preview XRechnung, ZUGFeRD, and Factur-X Documents in .NET C#](https://www.textcontrol.com/blog/2025/01/02/visualize-and-preview-xrechnung-zugferd-and-factur-x-documents-in-net-csharp/llms.txt)
- [Why Structured E-Invoices Still Need Tamper Protection using C# and .NET](https://www.textcontrol.com/blog/2026/03/24/why-structured-e-invoices-still-need-tamper-protection-using-csharp-and-dotnet/llms.txt)
- [Using XRechnung / ZUGFeRD XML to Create PDF/A-3b Invoices with ASP.NET Core C#](https://www.textcontrol.com/blog/2024/09/23/using-xrechnung-zugferd-xml-to-create-pdf-a-3b-invoices-with-asp-net-core-c-sharp/llms.txt)
- [Generating a ZUGFeRD PDF with Existing XML Data using .NET C#](https://www.textcontrol.com/blog/2024/07/25/generating-a-zugferd-pdf-with-existing-xml-data-using-net-c-sharp/llms.txt)
- [Creating Valid XRechnung / ZUGFeRD Invoices with ASP.NET Core C#](https://www.textcontrol.com/blog/2023/12/28/creating-valid-xrechnung-zugferd-invoices-with-asp-net-core-csharp/llms.txt)
- [Extract ZUGFeRD/Factur-X XML Attachments from Adobe PDF/A-3b Documents](https://www.textcontrol.com/blog/2021/01/18/extract-zugferd-facturx-attachments-from-adobe-pdf-documents/llms.txt)
