IronPDF 教程 C# 中的發票處理 使用 C# 進行發票處理:利用 .NET 產生、提取和自動化 PDF 發票 Curtis Chau 更新:2026年1月20日 下載 IronPDF NuGet 下載 DLL 下載 Windows 安裝程式 開始免費試用 法學碩士副本 法學碩士副本 將頁面複製為 Markdown 格式,用於 LLMs 在 ChatGPT 中打開 請向 ChatGPT 諮詢此頁面 在雙子座打開 請向 Gemini 詢問此頁面 在 Grok 中打開 向 Grok 詢問此頁面 打開困惑 向 Perplexity 詢問有關此頁面的信息 分享 在 Facebook 上分享 分享到 X(Twitter) 在 LinkedIn 上分享 複製連結 電子郵件文章 This article was translated from English: Does it need improvement? Translated View the article in English 使用 IronPDF 在C# .NET中進行發票處理,涵蓋了文件的整個生命週期:從 HTML 模板生成專業的PDF 發票,符合ZUGFeRD和Factur-X電子發票標準,使用文字解析和AI 驅動的處理從接收到的發票中提取結構化數據,以及構建與 QuickBooks、Xero 和 SAP 等會計系統集成的批量自動化管道。 TL;DR:快速入門指南 本教學涵蓋了在 C# .NET 中產生、提取和自動化 PDF 發票,包括電子發票合規性、AI 驅動的解析和會計系統整合。 -適用對象:建置發票模組、應付帳款自動化或電子發票合規性的 .NET 開發人員。 -你將建立的功能: HTML範本發票產生(包含明細和稅額計算)、用於支付連結的二維碼、符合ZUGFeRD/Factur-X標準的PDF/A-3輸出、使用正規表示式提取文字、AI驅動的發票解析以及與會計系統整合的批次處理。 -運行環境: .NET 10、.NET 8 LTS、.NET Framework 4.6.2+ 和 .NET Standard 2.0。無外部服務相依性。 -何時使用此方法:當您需要產生發票 PDF 、滿足歐盟電子發票要求或從供應商發票中提取應付帳款資料時。 -從技術角度來看,這很重要: IronPDF 可以以像素級的精確度將HTML 渲染成 PDF ,支援 PDF/A-3 格式的嵌入式 XML,並提供文字擷取 API,可與正規表示式或 AI 結合使用,將非結構化的發票轉換為結構化資料。 只需幾行程式碼即可產生您的第一份 PDF 發票: 立即開始使用 NuGet 建立 PDF 檔案: 使用 NuGet 套件管理器安裝 IronPDF PM > Install-Package IronPdf 複製並運行這段程式碼。 var renderer = new IronPdf.ChromePdfRenderer(); var pdf = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Total: $500.00</p>"); pdf.SaveAs("invoice.pdf"); 部署到您的生產環境進行測試 立即開始在您的專案中使用 IronPDF,免費試用! 免費試用30天 購買或註冊 IronPDF 的 30 天試用版後,請在應用程式開始時新增您的授權金鑰。 IronPdf.License.LicenseKey = "KEY"; IronPdf.License.LicenseKey = "KEY"; Imports IronPdf IronPdf.License.LicenseKey = "KEY" $vbLabelText $csharpLabel !{--01001100010010010100001001010010010000010101001001011001010111110101001101010100010001010101010 10100010111110101010001010010010010010100000101001100010111110100001001001100010011111010000100100110001001111010101 !{--010011000100100101000010010100100100000101010010010110010101111101001110010101010101010101010101010101010101010 0100010111110100100101001101010100010000010100110001001100010111110100001001001100010011110010101010 as-heading:2(目錄) TL;DR:快速入門指南 快速概覽 產生專業PDF發票 -建立發票 HTML 模板 -新增動態行項目併計算總計 -添加公司品牌識別及浮水印 -嵌入支付連結二維碼 -遵守電子發票標準 -什麼是 ZUGFeRD?它是如何運作的? Factur-X是什麼? -在PDF/A-3發票中嵌入XML數據 -面向未來的歐盟授權發票 -從PDF發票中提取數據 從PDF發票中提取文本 -提取行項目表格數據 -發票編號、日期和總額的模式匹配 -人工智慧驅動的發票處理 -整合人工智慧進行發票解析 提取結構化 JSON 數據 -處理格式不一致的發票 建立應付帳款自動化流程 -與會計系統集成 QuickBooks、Xero 和 SAP 的整合模式 大量處理數百張發票 !{--010011000100100101000010010100100100000101010010010110010101111101001110010101010101010101010101010101010101010 0100010111110100100101001101010100010000010100110001001100010111110100001001001100010011110010101010 發票生命週期是什麼?為什麼 PDF 仍然是標準格式? 在深入研究程式碼之前,了解發票在現代業務系統中的完整流程會很有幫助。 發票生命週期包括五個不同的階段:產生、分發、接收、資料提取和會計整合。 發票流程始於產生發票。 企業創建發票,其中包括明細項目、價格、稅金計算、付款條款和品牌識別。 發票必須看起來專業,並符合所有法律要求。 接下來是分發環節,即透過電子郵件、客戶入口網站或傳統郵件將發票發送給客戶。 客戶收到文件後,應付帳款團隊會接收該文件並準備處理。 資料提取從發票中提取關鍵信息,例如供應商詳細信息、明細項目、總計和到期日,以便與採購訂單進行核對和匹配。 最後,會計整合會將這些資料匯入 QuickBooks、Xero 或 SAP 等財務系統,用於支付和記錄保存。 為什麼多年來PDF仍然是使用最廣泛的格式? 歸根結底,是多種優勢的獨特組合。 無論您使用什麼裝置或作業系統,PDF 都能確保您的發票格式保持一致。 無論有人在 Windows、Mac 還是手機上打開您的發票,它看起來都和您設計的一樣。 PDF 檔案也很難被誤修改,因此比 Word 或 Excel 等格式更能保護文件的完整性。 您可以添加數位簽名以驗證真實性,並使用加密技術來確保安全。 最重要的是,PDF 已成為所有商業系統都能識別和支援的通用標準。 當然,這其中也存在著挑戰。 PDF檔案的設計目的是為了方便人閱讀,而不是為了方便電腦處理。 PDF 檔案不是將資訊儲存在結構化資料中,而是根據文字、線條、形狀和圖像在頁面上的位置來保存它們。 這就是為什麼像 IronPDF 這樣的工具如此有用,它們可以將人類可讀的文件轉換為軟體可以處理的資料。 如何使用 C# 產生專業的 PDF 發票 以程式設計方式產生發票需要將結構化資料(例如客戶資訊、明細專案和計算結果)轉換為精美的 PDF 文件。 IronPDF 利用 HTML 和 CSS 這兩種大多數開發人員已經很熟悉的技術,讓這一切變得非常簡單。 在本教程中,我們將探討您在現實世界中可能遇到的各種情況。 您也可以在此下載下方所示的項目。 如何建立發票 HTML 模板 IronPDF 發票產生的基礎是 HTML。 您無需費力處理底層 PDF 繪圖指令,只需使用標準 HTML 和 CSS 設計發票,然後讓 IronPDF 基於 Chrome 的渲染引擎將其轉換為像素級完美的 PDF 即可。 以下是一個示範此方法的基本發票範本: :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/basic-invoice-template.cs using IronPdf; // Define the HTML template for a basic invoice // Uses inline CSS for styling headers, tables, and totals string invoiceHtml = @" <!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; padding: 40px; } .header { text-align: right; margin-bottom: 40px; } .company-name { font-size: 24px; font-weight: bold; color: #333; } .invoice-title { font-size: 32px; margin: 20px 0; } .bill-to { margin: 20px 0; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th { background-color: #2A95D5; color: white; padding: 10px; text-align: left; } td { padding: 10px; border-bottom: 1px solid #ddd; } .total { text-align: right; font-size: 20px; font-weight: bold; margin-top: 20px; } </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> <div>123 Business Street</div> <div>City, State 12345</div> </div> <div class='invoice-title'>INVOICE</div> <div class='bill-to'> <strong>Bill To:</strong><br> Customer Name<br> 456 Customer Avenue<br> City, State 67890 </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Price</th> <th>Total</th> </tr> <tr> <td>Web Development Services</td> <td>10 hours</td> <td>$100.00</td> <td>$1,000.00</td> </tr> <tr> <td>Consulting</td> <td>5 hours</td> <td>$150.00</td> <td>$750.00</td> </tr> </table> <div class='total'>Total: $1,750.00</div> </body> </html>"; // Initialize the Chrome-based PDF renderer var renderer = new ChromePdfRenderer(); // Convert the HTML string to a PDF document var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Save the generated PDF to disk pdf.SaveAs("basic-invoice.pdf"); Imports IronPdf ' Define the HTML template for a basic invoice ' Uses inline CSS for styling headers, tables, and totals Dim invoiceHtml As String = " <!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; padding: 40px; } .header { text-align: right; margin-bottom: 40px; } .company-name { font-size: 24px; font-weight: bold; color: #333; } .invoice-title { font-size: 32px; margin: 20px 0; } .bill-to { margin: 20px 0; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th { background-color: #2A95D5; color: white; padding: 10px; text-align: left; } td { padding: 10px; border-bottom: 1px solid #ddd; } .total { text-align: right; font-size: 20px; font-weight: bold; margin-top: 20px; } </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> <div>123 Business Street</div> <div>City, State 12345</div> </div> <div class='invoice-title'>INVOICE</div> <div class='bill-to'> <strong>Bill To:</strong><br> Customer Name<br> 456 Customer Avenue<br> City, State 67890 </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Price</th> <th>Total</th> </tr> <tr> <td>Web Development Services</td> <td>10 hours</td> <td>$100.00</td> <td>$1,000.00</td> </tr> <tr> <td>Consulting</td> <td>5 hours</td> <td>$150.00</td> <td>$750.00</td> </tr> </table> <div class='total'>Total: $1,750.00</div> </body> </html>" ' Initialize the Chrome-based PDF renderer Dim renderer As New ChromePdfRenderer() ' Convert the HTML string to a PDF document Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml) ' Save the generated PDF to disk pdf.SaveAs("basic-invoice.pdf") $vbLabelText $csharpLabel 範例輸出 這種方法提供了極大的靈活性。 任何在 Chrome 中有效的 CSS 程式碼都可以在 PDF 中生效,包括 flexbox、網格佈局和自訂字體等現代功能。 您甚至可以透過引用 URL 或本機檔案路徑來使用外部樣式表和圖像。 如何新增動態行項目並計算總計 真實的發票很少有靜態內容。 您需要從資料庫中填入行項目,計算小計,套用稅率,並格式化貨幣值。 以下範例展示了一個可用於生產環境的動態發票產生模式: using IronPdf; using System; using System.Collections.Generic; using System.Linq; // Represents a single line item on an invoice public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } // Auto-calculates line total from quantity and unit price public decimal Total => Quantity * UnitPrice; } // Represents a complete invoice with customer details and line items public class Invoice { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public string CustomerName { get; set; } public string CustomerAddress { get; set; } public List<InvoiceLineItem> LineItems { get; set; } // Computed properties for invoice totals public decimal Subtotal => LineItems.Sum(item => item.Total); public decimal TaxRate { get; set; } = 0.08m; // Default 8% tax rate public decimal Tax => Subtotal * TaxRate; public decimal Total => Subtotal + Tax; } // Generates PDF invoices from Invoice objects using HTML templates public class InvoiceGenerator { public PdfDocument GenerateInvoice(Invoice invoice) { // Build HTML table rows dynamically from line items string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@" <tr> <td>{item.Description}</td> <td>{item.Quantity}</td> <td>${item.UnitPrice:F2}</td> <td>${item.Total:F2}</td> </tr> ")); // Build the complete HTML invoice using string interpolation // All invoice data is injected into the template dynamically string invoiceHtml = $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .header {{ text-align: right; margin-bottom: 40px; }} .company-name {{ font-size: 24px; font-weight: bold; color: #333; }} .invoice-details {{ margin: 20px 0; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }} td {{ padding: 10px; border-bottom: 1px solid #ddd; }} .totals {{ text-align: right; margin-top: 20px; }} .totals div {{ margin: 5px 0; }} .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }} </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> </div> <h1>INVOICE</h1> <div class='invoice-details'> <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br> <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br> <strong>Bill To:</strong> {invoice.CustomerName}<br> {invoice.CustomerAddress} </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Unit Price</th> <th>Total</th> </tr> {lineItemsHtml} </table> <div class='totals'> <div>Subtotal: ${invoice.Subtotal:F2}</div> <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div> <div class='grand-total'>Total: ${invoice.Total:F2}</div> </div> </body> </html>"; // Render HTML to PDF and return the document var renderer = new ChromePdfRenderer(); return renderer.RenderHtmlAsPdf(invoiceHtml); } } using IronPdf; using System; using System.Collections.Generic; using System.Linq; // Represents a single line item on an invoice public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } // Auto-calculates line total from quantity and unit price public decimal Total => Quantity * UnitPrice; } // Represents a complete invoice with customer details and line items public class Invoice { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public string CustomerName { get; set; } public string CustomerAddress { get; set; } public List<InvoiceLineItem> LineItems { get; set; } // Computed properties for invoice totals public decimal Subtotal => LineItems.Sum(item => item.Total); public decimal TaxRate { get; set; } = 0.08m; // Default 8% tax rate public decimal Tax => Subtotal * TaxRate; public decimal Total => Subtotal + Tax; } // Generates PDF invoices from Invoice objects using HTML templates public class InvoiceGenerator { public PdfDocument GenerateInvoice(Invoice invoice) { // Build HTML table rows dynamically from line items string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@" <tr> <td>{item.Description}</td> <td>{item.Quantity}</td> <td>${item.UnitPrice:F2}</td> <td>${item.Total:F2}</td> </tr> ")); // Build the complete HTML invoice using string interpolation // All invoice data is injected into the template dynamically string invoiceHtml = $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .header {{ text-align: right; margin-bottom: 40px; }} .company-name {{ font-size: 24px; font-weight: bold; color: #333; }} .invoice-details {{ margin: 20px 0; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }} td {{ padding: 10px; border-bottom: 1px solid #ddd; }} .totals {{ text-align: right; margin-top: 20px; }} .totals div {{ margin: 5px 0; }} .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }} </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> </div> <h1>INVOICE</h1> <div class='invoice-details'> <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br> <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br> <strong>Bill To:</strong> {invoice.CustomerName}<br> {invoice.CustomerAddress} </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Unit Price</th> <th>Total</th> </tr> {lineItemsHtml} </table> <div class='totals'> <div>Subtotal: ${invoice.Subtotal:F2}</div> <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div> <div class='grand-total'>Total: ${invoice.Total:F2}</div> </div> </body> </html>"; // Render HTML to PDF and return the document var renderer = new ChromePdfRenderer(); return renderer.RenderHtmlAsPdf(invoiceHtml); } } Imports IronPdf Imports System Imports System.Collections.Generic Imports System.Linq ' Represents a single line item on an invoice Public Class InvoiceLineItem Public Property Description As String Public Property Quantity As Decimal Public Property UnitPrice As Decimal ' Auto-calculates line total from quantity and unit price Public ReadOnly Property Total As Decimal Get Return Quantity * UnitPrice End Get End Property End Class ' Represents a complete invoice with customer details and line items Public Class Invoice Public Property InvoiceNumber As String Public Property InvoiceDate As DateTime Public Property CustomerName As String Public Property CustomerAddress As String Public Property LineItems As List(Of InvoiceLineItem) ' Computed properties for invoice totals Public ReadOnly Property Subtotal As Decimal Get Return LineItems.Sum(Function(item) item.Total) End Get End Property Public Property TaxRate As Decimal = 0.08D ' Default 8% tax rate Public ReadOnly Property Tax As Decimal Get Return Subtotal * TaxRate End Get End Property Public ReadOnly Property Total As Decimal Get Return Subtotal + Tax End Get End Property End Class ' Generates PDF invoices from Invoice objects using HTML templates Public Class InvoiceGenerator Public Function GenerateInvoice(invoice As Invoice) As PdfDocument ' Build HTML table rows dynamically from line items Dim lineItemsHtml As String = String.Join("", invoice.LineItems.Select(Function(item) $" <tr> <td>{item.Description}</td> <td>{item.Quantity}</td> <td>${item.UnitPrice:F2}</td> <td>${item.Total:F2}</td> </tr> ")) ' Build the complete HTML invoice using string interpolation ' All invoice data is injected into the template dynamically Dim invoiceHtml As String = $" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .header {{ text-align: right; margin-bottom: 40px; }} .company-name {{ font-size: 24px; font-weight: bold; color: #333; }} .invoice-details {{ margin: 20px 0; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }} td {{ padding: 10px; border-bottom: 1px solid #ddd; }} .totals {{ text-align: right; margin-top: 20px; }} .totals div {{ margin: 5px 0; }} .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }} </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> </div> <h1>INVOICE</h1> <div class='invoice-details'> <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br> <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br> <strong>Bill To:</strong> {invoice.CustomerName}<br> {invoice.CustomerAddress} </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Unit Price</th> <th>Total</th> </tr> {lineItemsHtml} </table> <div class='totals'> <div>Subtotal: ${invoice.Subtotal:F2}</div> <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div> <div class='grand-total'>Total: ${invoice.Total:F2}</div> </div> </body> </html>" ' Render HTML to PDF and return the document Dim renderer As New ChromePdfRenderer() Return renderer.RenderHtmlAsPdf(invoiceHtml) End Function End Class $vbLabelText $csharpLabel 範例輸出 Invoice類封裝了所有發票數據,並計算了小計、稅額和總計等屬性。 生成器使用字串插值將這些資料轉換為 HTML,然後將其渲染為 PDF。 這種關注點分離使得程式碼易於維護和測試。 如何在發票上新增公司品牌識別和浮水印 專業的發票需要品牌元素,例如徽標,在某些情況下還需要浮水印來表明付款狀態。 IronPDF 支援在 HTML 中嵌入圖像以及在渲染後以程式設計方式新增浮水印。 :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/branding-watermarks.cs using IronPdf; using IronPdf; var renderer = new ChromePdfRenderer(); // Invoice HTML template with company logo embedded via URL // Logo can also be Base64-encoded or a local file path string htmlWithLogo = @" <!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; padding: 40px; } .logo { width: 200px; margin-bottom: 20px; } </style> </head> <body> <div style='text-align: center;'> <img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' /> </div> <h1>INVOICE</h1> <p><strong>Invoice Number:</strong> INV-2024-001</p> <p><strong>Total:</strong> $1,250.00</p> </body> </html>"; // Render the HTML to PDF var pdf = renderer.RenderHtmlAsPdf(htmlWithLogo); // Apply a diagonal "UNPAID" watermark to mark invoice status // 30% opacity keeps the content readable while the watermark is visible pdf.ApplyWatermark("<h1 style='color: red;'>UNPAID</h1>", opacity: 30, rotation: 45, verticalAlignment: IronPdf.Editing.VerticalAlignment.Middle); pdf.SaveAs("invoice-with-watermark.pdf"); using IronPdf; Imports IronPdf Dim renderer As New ChromePdfRenderer() ' Invoice HTML template with company logo embedded via URL ' Logo can also be Base64-encoded or a local file path Dim htmlWithLogo As String = " <!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; padding: 40px; } .logo { width: 200px; margin-bottom: 20px; } </style> </head> <body> <div style='text-align: center;'> <img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' /> </div> <h1>INVOICE</h1> <p><strong>Invoice Number:</strong> INV-2024-001</p> <p><strong>Total:</strong> $1,250.00</p> </body> </html>" ' Render the HTML to PDF Dim pdf = renderer.RenderHtmlAsPdf(htmlWithLogo) ' Apply a diagonal "UNPAID" watermark to mark invoice status ' 30% opacity keeps the content readable while the watermark is visible pdf.ApplyWatermark("<h1 style='color: red;'>UNPAID</h1>", opacity:=30, rotation:=45, verticalAlignment:=IronPdf.Editing.VerticalAlignment.Middle) pdf.SaveAs("invoice-with-watermark.pdf") $vbLabelText $csharpLabel 範例輸出 ApplyWatermark方法接受 HTML 內容,讓您可以完全控制浮水印的外觀。 您可以調整不透明度、旋轉角度和位置,以達到您想要的效果。 這對於將發票標記為"已付款"、"草稿"或"已取消"尤其有用,而無需重新產生整個文件。 如何嵌入支付連結的二維碼 現代發票通常包含二維碼,客戶可以掃描二維碼快速付款。 IronPDF 雖然主要功能是產生 PDF 文件,但它可以與 IronQR 無縫協作,用於建立條碼: :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/qr-code-payment.cs using IronPdf; using IronQr; using IronSoftware.Drawing; // Generates PDF invoices with embedded QR codes for quick mobile payment public class InvoiceWithQRGenerator { public void GenerateInvoiceWithQR(string invoiceNumber, decimal amount) { // Create a payment URL with invoice details as query parameters string paymentUrl = $"https://yourcompany.com/pay?invoice={invoiceNumber}&amount={amount}"; // Generate QR code from the payment URL using IronQR QrCode qrCode = QrWriter.Write(paymentUrl); AnyBitmap qrImage = qrCode.Save(); qrImage.SaveAs("payment-qr.png", AnyBitmap.ImageFormat.Png); // Build invoice HTML with the QR code image embedded // Customers can scan the QR to pay directly from their phone string invoiceHtml = $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .payment-section {{ margin-top: 40px; text-align: center; border-top: 2px solid #eee; padding-top: 20px; }} .qr-code {{ width: 150px; height: 150px; }} </style> </head> <body> <h1>INVOICE {invoiceNumber}</h1> <p><strong>Amount Due:</strong> ${amount:F2}</p> <div class='payment-section'> <p><strong>Scan to Pay Instantly:</strong></p> <img src='payment-qr.png' alt='Payment QR Code' class='qr-code' /> <p style='font-size: 12px; color: #666;'> Or visit: {paymentUrl} </p> </div> </body> </html>"; // Convert HTML to PDF and save var renderer = new ChromePdfRenderer(); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); pdf.SaveAs($"invoice-{invoiceNumber}.pdf"); } } Imports IronPdf Imports IronQr Imports IronSoftware.Drawing ' Generates PDF invoices with embedded QR codes for quick mobile payment Public Class InvoiceWithQRGenerator Public Sub GenerateInvoiceWithQR(invoiceNumber As String, amount As Decimal) ' Create a payment URL with invoice details as query parameters Dim paymentUrl As String = $"https://yourcompany.com/pay?invoice={invoiceNumber}&amount={amount}" ' Generate QR code from the payment URL using IronQR Dim qrCode As QrCode = QrWriter.Write(paymentUrl) Dim qrImage As AnyBitmap = qrCode.Save() qrImage.SaveAs("payment-qr.png", AnyBitmap.ImageFormat.Png) ' Build invoice HTML with the QR code image embedded ' Customers can scan the QR to pay directly from their phone Dim invoiceHtml As String = $" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .payment-section {{ margin-top: 40px; text-align: center; border-top: 2px solid #eee; padding-top: 20px; }} .qr-code {{ width: 150px; height: 150px; }} </style> </head> <body> <h1>INVOICE {invoiceNumber}</h1> <p><strong>Amount Due:</strong> ${amount:F2}</p> <div class='payment-section'> <p><strong>Scan to Pay Instantly:</strong></p> <img src='payment-qr.png' alt='Payment QR Code' class='qr-code' /> <p style='font-size: 12px; color: #666;'> Or visit: {paymentUrl} </p> </div> </body> </html>" ' Convert HTML to PDF and save Dim renderer = New ChromePdfRenderer() Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml) pdf.SaveAs($"invoice-{invoiceNumber}.pdf") End Sub End Class $vbLabelText $csharpLabel 範例輸出 二維碼直接連結到付款頁面,減少客戶的支付摩擦,加快您的現金流。 此模式適用於任何支援基於 URL 的付款發起的付款提供者。 如何在 C# 遵守 ZUGFeRD 和 Factur-X 電子發票標準 電子發票在歐洲正迅速成為強制性要求。 德國率先推出了 ZUGFeRD,法國緊跟在後推出了 Factur-X。 這些標準將機器可讀的 XML 資料嵌入到 PDF 發票中,從而實現自動化處理,同時保持文件的可讀性。 對於在歐洲市場經營的企業而言,理解和實施這些標準變得越來越重要。 什麼是 ZUGFeRD?它是如何運作的? ZUGFeRD(德國電子發票論壇中央使用者指南)是德國電子發票標準,它將發票資料作為 XML 文件附件嵌入到符合 PDF/A-3 標準的文件中。 嵌入式 XML 無需 OCR 或解析即可實現自動資料擷取。 此標準定義了三個符合性級別,每個級別提供的資料結構化程度逐漸提高: -基本型:包含適用於簡單自動化處理的核心發票數據 -便利性:添加詳細信息,實現發票處理的完全自動化。 -擴充版:包含跨產業複雜業務場景的全面數據 XML 遵循 UN/CEFACT 跨產業發票 (CII) 模式,該模式已成為歐洲電子發票標準化的基礎。 什麼是Factur-X?它與ZUGFeRD有何不同? Factur-X 是法國對同一底層標準的實現。 ZUGFeRD 2.0 和 Factur-X 在技術上是相同的。 它們共用基於歐洲標準 EN 16931 的相同 XML 模式和一致性設定檔。差異純粹在於區域命名:根據 ZUGFeRD 規範創建的發票在 Factur-X 下有效,反之亦然。 如何在PDF/A-3發票中嵌入XML數據 IronPDF 提供建立符合規範的電子發票所需的配件功能。 此流程包括產生發票 PDF、根據 CII 架構建立 XML 數據,以及將 XML 作為附件嵌入到發票中並遵循正確的命名約定: using System; using System.Xml.Linq; // Generates ZUGFeRD-compliant invoices by embedding structured XML data // ZUGFeRD allows automated processing while keeping a human-readable PDF public class ZUGFeRDInvoiceGenerator { public void GenerateZUGFeRDInvoice(Invoice invoice) { // First, create the visual PDF that humans will read var renderer = new ChromePdfRenderer(); string invoiceHtml = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Define the UN/CEFACT namespaces required by the ZUGFeRD standard // These are mandatory for compliance with European e-invoicing regulations XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"; XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"; XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"; // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema var zugferdXml = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement(rsm + "CrossIndustryInvoice", new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName), new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName), new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName), // Document context identifies which e-invoicing guideline is being followed new XElement(rsm + "ExchangedDocumentContext", new XElement(ram + "GuidelineSpecifiedDocumentContextParameter", new XElement(ram + "ID", "urn:cen.eu:en16931:2017") ) ), // Core document identification: invoice number, type, and date new XElement(rsm + "ExchangedDocument", new XElement(ram + "ID", invoice.InvoiceNumber), new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT new XElement(ram + "IssueDateTime", new XElement(udt + "DateTimeString", new XAttribute("format", "102"), invoice.InvoiceDate.ToString("yyyyMMdd") ) ) ), // A complete implementation would include additional sections: // - Seller information (ram:SellerTradeParty) // - Buyer information (ram:BuyerTradeParty) // - Line items (ram:IncludedSupplyChainTradeLineItem) // - Payment terms (ram:SpecifiedTradePaymentTerms) // - Tax summaries (ram:ApplicableTradeTax) // Financial summary with all monetary totals new XElement(rsm + "SupplyChainTradeTransaction", new XElement(ram + "ApplicableHeaderTradeSettlement", new XElement(ram + "InvoiceCurrencyCode", "EUR"), new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation", new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal), new XElement(ram + "TaxTotalAmount", new XAttribute("currencyID", "EUR"), invoice.Tax), new XElement(ram + "GrandTotalAmount", invoice.Total), new XElement(ram + "DuePayableAmount", invoice.Total) ) ) ) ) ); // Save the XML to a temp file for embedding string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml"; zugferdXml.Save(xmlPath); // Attach the XML to the PDF - filename must follow ZUGFeRD conventions pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data"); // Final PDF contains both visual invoice and machine-readable XML pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf"); } // Generates simple HTML for the visual portion of the invoice private string BuildInvoiceHtml(Invoice invoice) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} h1 {{ color: #333; }} .zugferd-notice {{ margin-top: 30px; padding: 10px; background: #f0f0f0; font-size: 11px; }} </style> </head> <body> <h1>RECHNUNG / INVOICE</h1> <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p> <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p> <p><strong>Betrag:</strong> €{invoice.Total:F2}</p> <div class='zugferd-notice'> This invoice contains embedded ZUGFeRD data for automated processing. </div> </body> </html>"; } } using System; using System.Xml.Linq; // Generates ZUGFeRD-compliant invoices by embedding structured XML data // ZUGFeRD allows automated processing while keeping a human-readable PDF public class ZUGFeRDInvoiceGenerator { public void GenerateZUGFeRDInvoice(Invoice invoice) { // First, create the visual PDF that humans will read var renderer = new ChromePdfRenderer(); string invoiceHtml = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Define the UN/CEFACT namespaces required by the ZUGFeRD standard // These are mandatory for compliance with European e-invoicing regulations XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"; XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"; XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"; // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema var zugferdXml = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement(rsm + "CrossIndustryInvoice", new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName), new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName), new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName), // Document context identifies which e-invoicing guideline is being followed new XElement(rsm + "ExchangedDocumentContext", new XElement(ram + "GuidelineSpecifiedDocumentContextParameter", new XElement(ram + "ID", "urn:cen.eu:en16931:2017") ) ), // Core document identification: invoice number, type, and date new XElement(rsm + "ExchangedDocument", new XElement(ram + "ID", invoice.InvoiceNumber), new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT new XElement(ram + "IssueDateTime", new XElement(udt + "DateTimeString", new XAttribute("format", "102"), invoice.InvoiceDate.ToString("yyyyMMdd") ) ) ), // A complete implementation would include additional sections: // - Seller information (ram:SellerTradeParty) // - Buyer information (ram:BuyerTradeParty) // - Line items (ram:IncludedSupplyChainTradeLineItem) // - Payment terms (ram:SpecifiedTradePaymentTerms) // - Tax summaries (ram:ApplicableTradeTax) // Financial summary with all monetary totals new XElement(rsm + "SupplyChainTradeTransaction", new XElement(ram + "ApplicableHeaderTradeSettlement", new XElement(ram + "InvoiceCurrencyCode", "EUR"), new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation", new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal), new XElement(ram + "TaxTotalAmount", new XAttribute("currencyID", "EUR"), invoice.Tax), new XElement(ram + "GrandTotalAmount", invoice.Total), new XElement(ram + "DuePayableAmount", invoice.Total) ) ) ) ) ); // Save the XML to a temp file for embedding string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml"; zugferdXml.Save(xmlPath); // Attach the XML to the PDF - filename must follow ZUGFeRD conventions pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data"); // Final PDF contains both visual invoice and machine-readable XML pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf"); } // Generates simple HTML for the visual portion of the invoice private string BuildInvoiceHtml(Invoice invoice) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} h1 {{ color: #333; }} .zugferd-notice {{ margin-top: 30px; padding: 10px; background: #f0f0f0; font-size: 11px; }} </style> </head> <body> <h1>RECHNUNG / INVOICE</h1> <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p> <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p> <p><strong>Betrag:</strong> €{invoice.Total:F2}</p> <div class='zugferd-notice'> This invoice contains embedded ZUGFeRD data for automated processing. </div> </body> </html>"; } } Imports System Imports System.Xml.Linq ' Generates ZUGFeRD-compliant invoices by embedding structured XML data ' ZUGFeRD allows automated processing while keeping a human-readable PDF Public Class ZUGFeRDInvoiceGenerator Public Sub GenerateZUGFeRDInvoice(invoice As Invoice) ' First, create the visual PDF that humans will read Dim renderer = New ChromePdfRenderer() Dim invoiceHtml As String = BuildInvoiceHtml(invoice) Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml) ' Define the UN/CEFACT namespaces required by the ZUGFeRD standard ' These are mandatory for compliance with European e-invoicing regulations Dim rsm As XNamespace = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" Dim ram As XNamespace = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" Dim udt As XNamespace = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" ' Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema Dim zugferdXml = New XDocument( New XDeclaration("1.0", "UTF-8", Nothing), New XElement(rsm + "CrossIndustryInvoice", New XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName), New XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName), New XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName), ' Document context identifies which e-invoicing guideline is being followed New XElement(rsm + "ExchangedDocumentContext", New XElement(ram + "GuidelineSpecifiedDocumentContextParameter", New XElement(ram + "ID", "urn:cen.eu:en16931:2017") ) ), ' Core document identification: invoice number, type, and date New XElement(rsm + "ExchangedDocument", New XElement(ram + "ID", invoice.InvoiceNumber), New XElement(ram + "TypeCode", "380"), ' 380 = Commercial Invoice per UN/CEFACT New XElement(ram + "IssueDateTime", New XElement(udt + "DateTimeString", New XAttribute("format", "102"), invoice.InvoiceDate.ToString("yyyyMMdd") ) ) ), ' A complete implementation would include additional sections: ' - Seller information (ram:SellerTradeParty) ' - Buyer information (ram:BuyerTradeParty) ' - Line items (ram:IncludedSupplyChainTradeLineItem) ' - Payment terms (ram:SpecifiedTradePaymentTerms) ' - Tax summaries (ram:ApplicableTradeTax) ' Financial summary with all monetary totals New XElement(rsm + "SupplyChainTradeTransaction", New XElement(ram + "ApplicableHeaderTradeSettlement", New XElement(ram + "InvoiceCurrencyCode", "EUR"), New XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation", New XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal), New XElement(ram + "TaxTotalAmount", New XAttribute("currencyID", "EUR"), invoice.Tax), New XElement(ram + "GrandTotalAmount", invoice.Total), New XElement(ram + "DuePayableAmount", invoice.Total) ) ) ) ) ) ' Save the XML to a temp file for embedding Dim xmlPath As String = $"zugferd-{invoice.InvoiceNumber}.xml" zugferdXml.Save(xmlPath) ' Attach the XML to the PDF - filename must follow ZUGFeRD conventions pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data") ' Final PDF contains both visual invoice and machine-readable XML pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf") End Sub ' Generates simple HTML for the visual portion of the invoice Private Function BuildInvoiceHtml(invoice As Invoice) As String Return $" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} h1 {{ color: #333; }} .zugferd-notice {{ margin-top: 30px; padding: 10px; background: #f0f0f0; font-size: 11px; }} </style> </head> <body> <h1>RECHNUNG / INVOICE</h1> <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p> <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p> <p><strong>Betrag:</strong> €{invoice.Total:F2}</p> <div class='zugferd-notice'> This invoice contains embedded ZUGFeRD data for automated processing. </div> </body> </html>" End Function End Class $vbLabelText $csharpLabel 範例輸出 合規的關鍵在於使用正確的 XML 命名空間,遵循 CII 模式結構,並將 XML 嵌入到適當的檔案名稱中。 TypeCode"380"明確表明該文件是聯合國/CEFACT標準中的商業發票。 如何使發票符合歐盟法規要求 歐盟正在逐步強製成員國使用電子發票。 義大利已經要求企業對企業交易使用電子發票,法國正在分階段實施,將於 2026 年前完成,德國已宣布從 2025 年開始強制執行企業對企業電子發票。現在建立 ZUGFeRD/Factur-X 支持,即可使您的系統為這些監管要求做好準備。 以下是一個符合不同標準的合規性發票產生器的範本: using IronPdf; using System; // Enum representing supported European e-invoicing standards public enum InvoiceStandard { None, ZUGFeRD, // German standard - uses CII XML format FacturX, // French standard - technically identical to ZUGFeRD 2.0 Peppol // Pan-European standard - uses UBL XML format } // Factory class that generates invoices compliant with different e-invoicing standards // Allows switching between standards without changing core invoice generation logic public class CompliantInvoiceGenerator { public PdfDocument GenerateCompliantInvoice(Invoice invoice, InvoiceStandard standard) { // Generate the base PDF from HTML var renderer = new ChromePdfRenderer(); string html = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(html); // Attach the appropriate XML format based on target market/regulation switch (standard) { case InvoiceStandard.ZUGFeRD: case InvoiceStandard.FacturX: // Both use Cross-Industry Invoice format, just different filenames EmbedCIIXmlData(pdf, invoice, standard); break; case InvoiceStandard.Peppol: // Peppol uses Universal Business Language format EmbedUBLXmlData(pdf, invoice); break; } return pdf; } // Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X) private void EmbedCIIXmlData(PdfDocument pdf, Invoice invoice, InvoiceStandard standard) { string xml = GenerateCIIXml(invoice); // Filename convention differs between German and French standards string filename = standard == InvoiceStandard.ZUGFeRD ? "zugferd-invoice.xml" : "factur-x.xml"; System.IO.File.WriteAllText("temp-invoice.xml", xml); pdf.Attachments.AddFile("temp-invoice.xml", filename, $"{standard} Invoice Data"); } // Creates and embeds UBL-format XML for Peppol network compliance private void EmbedUBLXmlData(PdfDocument pdf, Invoice invoice) { // UBL (Universal Business Language) is the Peppol standard format string xml = $@"<?xml version='1.0' encoding='UTF-8'?> <Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'> <ID>{invoice.InvoiceNumber}</ID> <IssueDate>{invoice.InvoiceDate:yyyy-MM-dd}</IssueDate> <DocumentCurrencyCode>EUR</DocumentCurrencyCode> <LegalMonetaryTotal> <PayableAmount currencyID='EUR'>{invoice.Total}</PayableAmount> </LegalMonetaryTotal> </Invoice>"; System.IO.File.WriteAllText("peppol-invoice.xml", xml); pdf.Attachments.AddFile("peppol-invoice.xml", "invoice.xml", "Peppol UBL Invoice"); } // Generates minimal CII XML structure for demonstration private string GenerateCIIXml(Invoice invoice) { return $@"<?xml version='1.0' encoding='UTF-8'?> <rsm:CrossIndustryInvoice xmlns:rsm='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100' xmlns:ram='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'> <rsm:ExchangedDocument> <ram:ID>{invoice.InvoiceNumber}</ram:ID> <ram:TypeCode>380</ram:TypeCode> </rsm:ExchangedDocument> </rsm:CrossIndustryInvoice>"; } private string BuildInvoiceHtml(Invoice invoice) { return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>"; } } using IronPdf; using System; // Enum representing supported European e-invoicing standards public enum InvoiceStandard { None, ZUGFeRD, // German standard - uses CII XML format FacturX, // French standard - technically identical to ZUGFeRD 2.0 Peppol // Pan-European standard - uses UBL XML format } // Factory class that generates invoices compliant with different e-invoicing standards // Allows switching between standards without changing core invoice generation logic public class CompliantInvoiceGenerator { public PdfDocument GenerateCompliantInvoice(Invoice invoice, InvoiceStandard standard) { // Generate the base PDF from HTML var renderer = new ChromePdfRenderer(); string html = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(html); // Attach the appropriate XML format based on target market/regulation switch (standard) { case InvoiceStandard.ZUGFeRD: case InvoiceStandard.FacturX: // Both use Cross-Industry Invoice format, just different filenames EmbedCIIXmlData(pdf, invoice, standard); break; case InvoiceStandard.Peppol: // Peppol uses Universal Business Language format EmbedUBLXmlData(pdf, invoice); break; } return pdf; } // Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X) private void EmbedCIIXmlData(PdfDocument pdf, Invoice invoice, InvoiceStandard standard) { string xml = GenerateCIIXml(invoice); // Filename convention differs between German and French standards string filename = standard == InvoiceStandard.ZUGFeRD ? "zugferd-invoice.xml" : "factur-x.xml"; System.IO.File.WriteAllText("temp-invoice.xml", xml); pdf.Attachments.AddFile("temp-invoice.xml", filename, $"{standard} Invoice Data"); } // Creates and embeds UBL-format XML for Peppol network compliance private void EmbedUBLXmlData(PdfDocument pdf, Invoice invoice) { // UBL (Universal Business Language) is the Peppol standard format string xml = $@"<?xml version='1.0' encoding='UTF-8'?> <Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'> <ID>{invoice.InvoiceNumber}</ID> <IssueDate>{invoice.InvoiceDate:yyyy-MM-dd}</IssueDate> <DocumentCurrencyCode>EUR</DocumentCurrencyCode> <LegalMonetaryTotal> <PayableAmount currencyID='EUR'>{invoice.Total}</PayableAmount> </LegalMonetaryTotal> </Invoice>"; System.IO.File.WriteAllText("peppol-invoice.xml", xml); pdf.Attachments.AddFile("peppol-invoice.xml", "invoice.xml", "Peppol UBL Invoice"); } // Generates minimal CII XML structure for demonstration private string GenerateCIIXml(Invoice invoice) { return $@"<?xml version='1.0' encoding='UTF-8'?> <rsm:CrossIndustryInvoice xmlns:rsm='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100' xmlns:ram='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'> <rsm:ExchangedDocument> <ram:ID>{invoice.InvoiceNumber}</ram:ID> <ram:TypeCode>380</ram:TypeCode> </rsm:ExchangedDocument> </rsm:CrossIndustryInvoice>"; } private string BuildInvoiceHtml(Invoice invoice) { return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>"; } } Imports IronPdf Imports System ' Enum representing supported European e-invoicing standards Public Enum InvoiceStandard None ZUGFeRD ' German standard - uses CII XML format FacturX ' French standard - technically identical to ZUGFeRD 2.0 Peppol ' Pan-European standard - uses UBL XML format End Enum ' Factory class that generates invoices compliant with different e-invoicing standards ' Allows switching between standards without changing core invoice generation logic Public Class CompliantInvoiceGenerator Public Function GenerateCompliantInvoice(invoice As Invoice, standard As InvoiceStandard) As PdfDocument ' Generate the base PDF from HTML Dim renderer As New ChromePdfRenderer() Dim html As String = BuildInvoiceHtml(invoice) Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html) ' Attach the appropriate XML format based on target market/regulation Select Case standard Case InvoiceStandard.ZUGFeRD, InvoiceStandard.FacturX ' Both use Cross-Industry Invoice format, just different filenames EmbedCIIXmlData(pdf, invoice, standard) Case InvoiceStandard.Peppol ' Peppol uses Universal Business Language format EmbedUBLXmlData(pdf, invoice) End Select Return pdf End Function ' Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X) Private Sub EmbedCIIXmlData(pdf As PdfDocument, invoice As Invoice, standard As InvoiceStandard) Dim xml As String = GenerateCIIXml(invoice) ' Filename convention differs between German and French standards Dim filename As String = If(standard = InvoiceStandard.ZUGFeRD, "zugferd-invoice.xml", "factur_x.xml") System.IO.File.WriteAllText("temp-invoice.xml", xml) pdf.Attachments.AddFile("temp-invoice.xml", filename, $"{standard} Invoice Data") End Sub ' Creates and embeds UBL-format XML for Peppol network compliance Private Sub EmbedUBLXmlData(pdf As PdfDocument, invoice As Invoice) ' UBL (Universal Business Language) is the Peppol standard format Dim xml As String = $"<?xml version='1.0' encoding='UTF-8'?> <Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'> <ID>{invoice.InvoiceNumber}</ID> <IssueDate>{invoice.InvoiceDate:yyyy-MM-dd}</IssueDate> <DocumentCurrencyCode>EUR</DocumentCurrencyCode> <LegalMonetaryTotal> <PayableAmount currencyID='EUR'>{invoice.Total}</PayableAmount> </LegalMonetaryTotal> </Invoice>" System.IO.File.WriteAllText("peppol-invoice.xml", xml) pdf.Attachments.AddFile("peppol-invoice.xml", "invoice.xml", "Peppol UBL Invoice") End Sub ' Generates minimal CII XML structure for demonstration Private Function GenerateCIIXml(invoice As Invoice) As String Return $"<?xml version='1.0' encoding='UTF-8'?> <rsm:CrossIndustryInvoice xmlns:rsm='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100' xmlns:ram='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'> <rsm:ExchangedDocument> <ram:ID>{invoice.InvoiceNumber}</ram:ID> <ram:TypeCode>380</ram:TypeCode> </rsm:ExchangedDocument> </rsm:CrossIndustryInvoice>" End Function Private Function BuildInvoiceHtml(invoice As Invoice) As String Return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>" End Function End Class $vbLabelText $csharpLabel 這種架構允許您在新標準出現時新增這些標準,而無需重構核心發票產生邏輯。 基於枚舉的方法使得使用者或配置能夠輕鬆地確定要使用的合規模式。 如何使用 C# 從 PDF 發票中提取數據 開立發票只是成功的一半。 大多數企業也會收到供應商的發票,需要提取資料進行處理。 IronPDF 提供強大的文字擷取功能,這是發票資料收集的基礎。 如何從PDF發票中提取文本 最基本的提取操作是從 PDF 中提取所有文字內容。 IronPDF 的ExtractAllText方法可以處理 PDF 文字編碼和定位的複雜性: using IronPdf; using System; // Extracts raw text content from PDF invoices for further processing public class InvoiceTextExtractor { // Extracts all text from a PDF in one operation // Best for single-page invoices or when you need the complete content public string ExtractInvoiceText(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); // IronPDF handles the complexity of PDF text encoding and positioning string allText = pdf.ExtractAllText(); Console.WriteLine("Full invoice text:"); Console.WriteLine(allText); return allText; } // Extracts text page by page - useful for multi-page invoices // Allows you to process header info separately from line items public void ExtractTextByPage(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); // Iterate through each page (0-indexed) for (int i = 0; i < pdf.PageCount; i++) { string pageText = pdf.ExtractTextFromPage(i); Console.WriteLine($"\n--- Page {i + 1} ---"); Console.WriteLine(pageText); } } } using IronPdf; using System; // Extracts raw text content from PDF invoices for further processing public class InvoiceTextExtractor { // Extracts all text from a PDF in one operation // Best for single-page invoices or when you need the complete content public string ExtractInvoiceText(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); // IronPDF handles the complexity of PDF text encoding and positioning string allText = pdf.ExtractAllText(); Console.WriteLine("Full invoice text:"); Console.WriteLine(allText); return allText; } // Extracts text page by page - useful for multi-page invoices // Allows you to process header info separately from line items public void ExtractTextByPage(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); // Iterate through each page (0-indexed) for (int i = 0; i < pdf.PageCount; i++) { string pageText = pdf.ExtractTextFromPage(i); Console.WriteLine($"\n--- Page {i + 1} ---"); Console.WriteLine(pageText); } } } Imports IronPdf Imports System ' Extracts raw text content from PDF invoices for further processing Public Class InvoiceTextExtractor ' Extracts all text from a PDF in one operation ' Best for single-page invoices or when you need the complete content Public Function ExtractInvoiceText(pdfPath As String) As String Dim pdf = PdfDocument.FromFile(pdfPath) ' IronPDF handles the complexity of PDF text encoding and positioning Dim allText As String = pdf.ExtractAllText() Console.WriteLine("Full invoice text:") Console.WriteLine(allText) Return allText End Function ' Extracts text page by page - useful for multi-page invoices ' Allows you to process header info separately from line items Public Sub ExtractTextByPage(pdfPath As String) Dim pdf = PdfDocument.FromFile(pdfPath) ' Iterate through each page (0-indexed) For i As Integer = 0 To pdf.PageCount - 1 Dim pageText As String = pdf.ExtractTextFromPage(i) Console.WriteLine(vbCrLf & "--- Page " & (i + 1).ToString() & " ---") Console.WriteLine(pageText) Next End Sub End Class $vbLabelText $csharpLabel 逐頁提取對於多頁發票特別有用,因為您需要查找特定部分,例如查找跨越多頁的行項目,而標題資訊僅出現在第一頁上。 如何提取表格中的行項目數據 發票明細通常以表格呈現。 PDF 檔案本身沒有表格結構,但您可以提取文字並進行解析,從而重建表格資料: using IronPdf; using System; using System.Collections.Generic; // Data model for a single invoice line item public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts tabular line item data from PDF invoices // Note: PDFs don't have native table structure, so this uses text parsing public class InvoiceTableExtractor { public List<InvoiceLineItem> ExtractLineItems(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var lineItems = new List<InvoiceLineItem>(); string[] lines = text.Split('\n'); foreach (string line in lines) { // Currency symbols indicate potential line items with amounts if (line.Contains("$") || line.Contains("€")) { Console.WriteLine($"Potential line item: {line.Trim()}"); // Split on whitespace to separate columns // Actual parsing logic depends on your invoice format string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); // Try to find numeric values that could be amounts foreach (string part in parts) { string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", ""); if (decimal.TryParse(cleaned, out decimal amount)) { Console.WriteLine($" Found amount: {amount:C}"); } } } } return lineItems; } } using IronPdf; using System; using System.Collections.Generic; // Data model for a single invoice line item public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts tabular line item data from PDF invoices // Note: PDFs don't have native table structure, so this uses text parsing public class InvoiceTableExtractor { public List<InvoiceLineItem> ExtractLineItems(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var lineItems = new List<InvoiceLineItem>(); string[] lines = text.Split('\n'); foreach (string line in lines) { // Currency symbols indicate potential line items with amounts if (line.Contains("$") || line.Contains("€")) { Console.WriteLine($"Potential line item: {line.Trim()}"); // Split on whitespace to separate columns // Actual parsing logic depends on your invoice format string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); // Try to find numeric values that could be amounts foreach (string part in parts) { string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", ""); if (decimal.TryParse(cleaned, out decimal amount)) { Console.WriteLine($" Found amount: {amount:C}"); } } } } return lineItems; } } Imports IronPdf Imports System Imports System.Collections.Generic ' Data model for a single invoice line item Public Class InvoiceLineItem Public Property Description As String Public Property Quantity As Decimal Public Property UnitPrice As Decimal Public Property Total As Decimal End Class ' Extracts tabular line item data from PDF invoices ' Note: PDFs don't have native table structure, so this uses text parsing Public Class InvoiceTableExtractor Public Function ExtractLineItems(pdfPath As String) As List(Of InvoiceLineItem) Dim pdf = PdfDocument.FromFile(pdfPath) Dim text As String = pdf.ExtractAllText() Dim lineItems As New List(Of InvoiceLineItem)() Dim lines() As String = text.Split(ControlChars.Lf) For Each line As String In lines ' Currency symbols indicate potential line items with amounts If line.Contains("$") OrElse line.Contains("€") Then Console.WriteLine($"Potential line item: {line.Trim()}") ' Split on whitespace to separate columns ' Actual parsing logic depends on your invoice format Dim parts() As String = line.Split(New Char() {ControlChars.Tab, " "c}, StringSplitOptions.RemoveEmptyEntries) ' Try to find numeric values that could be amounts For Each part As String In parts Dim cleaned As String = part.Replace("$", "").Replace("€", "").Replace(",", "") Dim amount As Decimal If Decimal.TryParse(cleaned, amount) Then Console.WriteLine($" Found amount: {amount:C}") End If Next End If Next Return lineItems End Function End Class $vbLabelText $csharpLabel 解析邏輯會根據您的發票格式而有所不同。 對於來自已知供應商且格式一致的發票,您可以建立特定格式的解析器。 對於各種格式,請考慮本文後面將介紹的 AI 驅動擷取。 如何使用模式匹配來匹配發票編號、日期和總金額 正規表示式對於從發票文字中提取特定資料點非常有價值。 發票編號、日期和總額等關鍵欄位通常遵循可識別的模式: using IronPdf; using System; using System.Text.RegularExpressions; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public decimal TotalAmount { get; set; } public string VendorName { get; set; } } // Extracts key invoice fields using regex pattern matching // Multiple patterns handle variations across different vendors public class InvoiceParser { public InvoiceData ParseInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var invoiceData = new InvoiceData(); // Try multiple patterns to find invoice number // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer" string[] invoiceNumberPatterns = new[] { @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)", @"INV[-\s]?(\d+)", @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)", @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)" }; foreach (string pattern in invoiceNumberPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceNumber = match.Groups[1].Value; Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}"); break; } } // Date patterns for US, European, and written formats string[] datePatterns = new[] { @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"(\d{1,2}\.\d{1,2}\.\d{4})", // European: DD.MM.YYYY @"(\w+\s+\d{1,2},?\s+\d{4})" // Written: January 15, 2024 }; foreach (string pattern in datePatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceDate = match.Groups[1].Value; Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}"); break; } } // Look for total amount with various labels string[] totalPatterns = new[] { @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})" }; foreach (string pattern in totalPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { // Remove commas before parsing string amountStr = match.Groups[1].Value.Replace(",", ""); if (decimal.TryParse(amountStr, out decimal amount)) { invoiceData.TotalAmount = amount; Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}"); break; } } } return invoiceData; } } using IronPdf; using System; using System.Text.RegularExpressions; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public decimal TotalAmount { get; set; } public string VendorName { get; set; } } // Extracts key invoice fields using regex pattern matching // Multiple patterns handle variations across different vendors public class InvoiceParser { public InvoiceData ParseInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var invoiceData = new InvoiceData(); // Try multiple patterns to find invoice number // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer" string[] invoiceNumberPatterns = new[] { @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)", @"INV[-\s]?(\d+)", @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)", @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)" }; foreach (string pattern in invoiceNumberPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceNumber = match.Groups[1].Value; Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}"); break; } } // Date patterns for US, European, and written formats string[] datePatterns = new[] { @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"(\d{1,2}\.\d{1,2}\.\d{4})", // European: DD.MM.YYYY @"(\w+\s+\d{1,2},?\s+\d{4})" // Written: January 15, 2024 }; foreach (string pattern in datePatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceDate = match.Groups[1].Value; Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}"); break; } } // Look for total amount with various labels string[] totalPatterns = new[] { @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})" }; foreach (string pattern in totalPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { // Remove commas before parsing string amountStr = match.Groups[1].Value.Replace(",", ""); if (decimal.TryParse(amountStr, out decimal amount)) { invoiceData.TotalAmount = amount; Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}"); break; } } } return invoiceData; } } Imports IronPdf Imports System Imports System.Text.RegularExpressions ' Data model for extracted invoice information Public Class InvoiceData Public Property InvoiceNumber As String Public Property InvoiceDate As String Public Property TotalAmount As Decimal Public Property VendorName As String End Class ' Extracts key invoice fields using regex pattern matching ' Multiple patterns handle variations across different vendors Public Class InvoiceParser Public Function ParseInvoice(pdfPath As String) As InvoiceData Dim pdf = PdfDocument.FromFile(pdfPath) Dim text As String = pdf.ExtractAllText() Dim invoiceData As New InvoiceData() ' Try multiple patterns to find invoice number ' Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer" Dim invoiceNumberPatterns As String() = { "Invoice\s*#?\s*:?\s*([A-Z0-9-]+)", "INV[-\s]?(\d+)", "Invoice\s+Number\s*:?\s*([A-Z0-9-]+)", "Rechnungsnummer\s*:?\s*([A-Z0-9-]+)" } For Each pattern As String In invoiceNumberPatterns Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase) If match.Success Then invoiceData.InvoiceNumber = match.Groups(1).Value Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}") Exit For End If Next ' Date patterns for US, European, and written formats Dim datePatterns As String() = { "Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", "Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", "(\d{1,2}\.\d{1,2}\.\d{4})", ' European: DD.MM.YYYY "(\w+\s+\d{1,2},?\s+\d{4})" ' Written: January 15, 2024 } For Each pattern As String In datePatterns Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase) If match.Success Then invoiceData.InvoiceDate = match.Groups(1).Value Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}") Exit For End If Next ' Look for total amount with various labels Dim totalPatterns As String() = { "Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", "Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", "Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", "Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})" } For Each pattern As String In totalPatterns Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase) If match.Success Then ' Remove commas before parsing Dim amountStr As String = match.Groups(1).Value.Replace(",", "") Dim amount As Decimal If Decimal.TryParse(amountStr, amount) Then invoiceData.TotalAmount = amount Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}") Exit For End If End If Next Return invoiceData End Function End Class $vbLabelText $csharpLabel 這種基於模式的方法對於格式可預測的發票非常有效。 多種模式變體可處理不同供應商之間常見的格式差異,例如"發票號碼"和"發票號碼:"。 掃描件或影像發票呢? 上述文字擷取方法適用於包含嵌入式文字的 PDF 檔案。 但是,掃描文件和基於圖像的 PDF 文件不包含可提取的文字。 它們本質上是發票的照片。 要處理掃描的發票,您需要 OCR(光學字元辨識)功能。 IronOCR 是 Iron Suite 的一部分,可與 IronPDF 無縫集成,以應對這些場景。 請造訪https://ironsoftware.com/csharp/ocr/以了解更多關於從掃描文件和映像中提取文字的資訊。 如何在 .NET 中使用 AI 處理發票 傳統模式匹配對標準化發票效果很好,但現實世界中的應付帳款部門會收到無數種格式的文件。 這就是人工智慧資料擷取的優勢所在。 大型語言模型能夠理解發票語義,即使面對不熟悉的佈局也能提取結構化資料。 如何將人工智慧整合到發票解析中 AI驅動的發票處理模式結合了IronPDF的文字擷取和LLM API呼叫。 以下是一個適用於任何 OpenAI 相容 API 的通用實作: using IronPdf; using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public string VendorName { get; set; } public decimal TotalAmount { get; set; } } // Leverages AI/LLM APIs to extract structured data from any invoice format // Works with OpenAI or any compatible API endpoint public class AIInvoiceParser { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _apiUrl; public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions") { _apiKey = apiKey; _apiUrl = apiUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath) { // First extract raw text from the PDF using IronPDF var pdf = PdfDocument.FromFile(pdfPath); string invoiceText = pdf.ExtractAllText(); // Construct a prompt that instructs the AI to return structured JSON // Being explicit about the format reduces parsing errors string prompt = $@"Extract the following information from this invoice text. Return ONLY valid JSON with no additional text or markdown formatting. Required fields: - InvoiceNumber: The invoice or document number - InvoiceDate: The invoice date in YYYY-MM-DD format - VendorName: The company or person who sent the invoice - TotalAmount: The total amount due as a number (no currency symbols) Invoice text: {invoiceText} JSON response:"; // Build the API request with a system prompt for context var requestBody = new { model = "gpt-4", messages = new[] { new { role = "system", content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only." }, new { role = "user", content = prompt } }, temperature = 0.1 // Low temperature ensures consistent, deterministic results }; var json = JsonSerializer.Serialize(requestBody); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_apiUrl, content); var responseJson = await response.Content.ReadAsStringAsync(); // Navigate the API response structure to get the extracted content using var doc = JsonDocument.Parse(responseJson); var messageContent = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); Console.WriteLine("AI Extracted Data:"); Console.WriteLine(messageContent); // Deserialize the AI's JSON response into our data class var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return invoiceData; } } using IronPdf; using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public string VendorName { get; set; } public decimal TotalAmount { get; set; } } // Leverages AI/LLM APIs to extract structured data from any invoice format // Works with OpenAI or any compatible API endpoint public class AIInvoiceParser { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _apiUrl; public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions") { _apiKey = apiKey; _apiUrl = apiUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath) { // First extract raw text from the PDF using IronPDF var pdf = PdfDocument.FromFile(pdfPath); string invoiceText = pdf.ExtractAllText(); // Construct a prompt that instructs the AI to return structured JSON // Being explicit about the format reduces parsing errors string prompt = $@"Extract the following information from this invoice text. Return ONLY valid JSON with no additional text or markdown formatting. Required fields: - InvoiceNumber: The invoice or document number - InvoiceDate: The invoice date in YYYY-MM-DD format - VendorName: The company or person who sent the invoice - TotalAmount: The total amount due as a number (no currency symbols) Invoice text: {invoiceText} JSON response:"; // Build the API request with a system prompt for context var requestBody = new { model = "gpt-4", messages = new[] { new { role = "system", content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only." }, new { role = "user", content = prompt } }, temperature = 0.1 // Low temperature ensures consistent, deterministic results }; var json = JsonSerializer.Serialize(requestBody); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_apiUrl, content); var responseJson = await response.Content.ReadAsStringAsync(); // Navigate the API response structure to get the extracted content using var doc = JsonDocument.Parse(responseJson); var messageContent = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); Console.WriteLine("AI Extracted Data:"); Console.WriteLine(messageContent); // Deserialize the AI's JSON response into our data class var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return invoiceData; } } Imports IronPdf Imports System Imports System.Net.Http Imports System.Text Imports System.Text.Json Imports System.Threading.Tasks ' Data model for extracted invoice information Public Class InvoiceData Public Property InvoiceNumber As String Public Property InvoiceDate As String Public Property VendorName As String Public Property TotalAmount As Decimal End Class ' Leverages AI/LLM APIs to extract structured data from any invoice format ' Works with OpenAI or any compatible API endpoint Public Class AIInvoiceParser Private ReadOnly _httpClient As HttpClient Private ReadOnly _apiKey As String Private ReadOnly _apiUrl As String Public Sub New(apiKey As String, Optional apiUrl As String = "https://api.openai.com/v1/chat/completions") _apiKey = apiKey _apiUrl = apiUrl _httpClient = New HttpClient() _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}") End Sub Public Async Function ParseInvoiceWithAI(pdfPath As String) As Task(Of InvoiceData) ' First extract raw text from the PDF using IronPDF Dim pdf = PdfDocument.FromFile(pdfPath) Dim invoiceText As String = pdf.ExtractAllText() ' Construct a prompt that instructs the AI to return structured JSON ' Being explicit about the format reduces parsing errors Dim prompt As String = $"Extract the following information from this invoice text. Return ONLY valid JSON with no additional text or markdown formatting. Required fields: - InvoiceNumber: The invoice or document number - InvoiceDate: The invoice date in YYYY-MM-DD format - VendorName: The company or person who sent the invoice - TotalAmount: The total amount due as a number (no currency symbols) Invoice text: {invoiceText} JSON response:" ' Build the API request with a system prompt for context Dim requestBody = New With { .model = "gpt-4", .messages = New Object() { New With { .role = "system", .content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only." }, New With { .role = "user", .content = prompt } }, .temperature = 0.1 ' Low temperature ensures consistent, deterministic results } Dim json As String = JsonSerializer.Serialize(requestBody) Dim content As New StringContent(json, Encoding.UTF8, "application/json") Dim response = Await _httpClient.PostAsync(_apiUrl, content) Dim responseJson As String = Await response.Content.ReadAsStringAsync() ' Navigate the API response structure to get the extracted content Using doc = JsonDocument.Parse(responseJson) Dim messageContent As String = doc.RootElement _ .GetProperty("choices")(0) _ .GetProperty("message") _ .GetProperty("content") _ .GetString() Console.WriteLine("AI Extracted Data:") Console.WriteLine(messageContent) ' Deserialize the AI's JSON response into our data class Dim invoiceData As InvoiceData = JsonSerializer.Deserialize(Of InvoiceData)(messageContent, New JsonSerializerOptions With {.PropertyNameCaseInsensitive = True}) Return invoiceData End Using End Function End Class $vbLabelText $csharpLabel 低溫設定 (0.1) 有利於獲得確定性輸出,這對於希望對相同輸入獲得一致結果的資料擷取任務來說非常重要。 如何從發票中提取結構化 JSON 數據 對於包含明細、供應商詳細資訊和客戶資訊的更複雜的發票,您可以要求更豐富的 JSON 結構: using IronPdf; using System; using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; // Comprehensive invoice data model with all details public class DetailedInvoiceData { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public VendorInfo Vendor { get; set; } public CustomerInfo Customer { get; set; } public List<LineItem> LineItems { get; set; } public decimal Subtotal { get; set; } public decimal Tax { get; set; } public decimal Total { get; set; } } public class VendorInfo { public string Name { get; set; } public string Address { get; set; } public string TaxId { get; set; } } public class CustomerInfo { public string Name { get; set; } public string Address { get; set; } } public class LineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts comprehensive invoice data including line items and party details public class StructuredInvoiceExtractor { private readonly AIInvoiceParser _aiParser; public StructuredInvoiceExtractor(string apiKey) { _aiParser = new AIInvoiceParser(apiKey); } public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // Define the exact JSON structure we want the AI to return // This schema guides the AI to extract all relevant fields string jsonSchema = @"{ ""InvoiceNumber"": ""string"", ""InvoiceDate"": ""YYYY-MM-DD"", ""DueDate"": ""YYYY-MM-DD"", ""Vendor"": { ""Name"": ""string"", ""Address"": ""string"", ""TaxId"": ""string or null"" }, ""Customer"": { ""Name"": ""string"", ""Address"": ""string"" }, ""LineItems"": [ { ""Description"": ""string"", ""Quantity"": 0.0, ""UnitPrice"": 0.00, ""Total"": 0.00 } ], ""Subtotal"": 0.00, ""Tax"": 0.00, ""Total"": 0.00 }"; // Prompt includes both the schema and the extracted text string prompt = $@"Extract all invoice data and return it in this exact JSON structure: {jsonSchema} Invoice text: {text} Return only valid JSON, no markdown formatting or additional text."; // Call AI API and parse response (implementation as shown above) // Return deserialized DetailedInvoiceData return new DetailedInvoiceData(); // Placeholder } } using IronPdf; using System; using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; // Comprehensive invoice data model with all details public class DetailedInvoiceData { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public VendorInfo Vendor { get; set; } public CustomerInfo Customer { get; set; } public List<LineItem> LineItems { get; set; } public decimal Subtotal { get; set; } public decimal Tax { get; set; } public decimal Total { get; set; } } public class VendorInfo { public string Name { get; set; } public string Address { get; set; } public string TaxId { get; set; } } public class CustomerInfo { public string Name { get; set; } public string Address { get; set; } } public class LineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts comprehensive invoice data including line items and party details public class StructuredInvoiceExtractor { private readonly AIInvoiceParser _aiParser; public StructuredInvoiceExtractor(string apiKey) { _aiParser = new AIInvoiceParser(apiKey); } public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // Define the exact JSON structure we want the AI to return // This schema guides the AI to extract all relevant fields string jsonSchema = @"{ ""InvoiceNumber"": ""string"", ""InvoiceDate"": ""YYYY-MM-DD"", ""DueDate"": ""YYYY-MM-DD"", ""Vendor"": { ""Name"": ""string"", ""Address"": ""string"", ""TaxId"": ""string or null"" }, ""Customer"": { ""Name"": ""string"", ""Address"": ""string"" }, ""LineItems"": [ { ""Description"": ""string"", ""Quantity"": 0.0, ""UnitPrice"": 0.00, ""Total"": 0.00 } ], ""Subtotal"": 0.00, ""Tax"": 0.00, ""Total"": 0.00 }"; // Prompt includes both the schema and the extracted text string prompt = $@"Extract all invoice data and return it in this exact JSON structure: {jsonSchema} Invoice text: {text} Return only valid JSON, no markdown formatting or additional text."; // Call AI API and parse response (implementation as shown above) // Return deserialized DetailedInvoiceData return new DetailedInvoiceData(); // Placeholder } } Imports IronPdf Imports System Imports System.Collections.Generic Imports System.Text.Json Imports System.Threading.Tasks ' Comprehensive invoice data model with all details Public Class DetailedInvoiceData Public Property InvoiceNumber As String Public Property InvoiceDate As DateTime Public Property DueDate As DateTime Public Property Vendor As VendorInfo Public Property Customer As CustomerInfo Public Property LineItems As List(Of LineItem) Public Property Subtotal As Decimal Public Property Tax As Decimal Public Property Total As Decimal End Class Public Class VendorInfo Public Property Name As String Public Property Address As String Public Property TaxId As String End Class Public Class CustomerInfo Public Property Name As String Public Property Address As String End Class Public Class LineItem Public Property Description As String Public Property Quantity As Decimal Public Property UnitPrice As Decimal Public Property Total As Decimal End Class ' Extracts comprehensive invoice data including line items and party details Public Class StructuredInvoiceExtractor Private ReadOnly _aiParser As AIInvoiceParser Public Sub New(apiKey As String) _aiParser = New AIInvoiceParser(apiKey) End Sub Public Async Function ExtractDetailedData(pdfPath As String) As Task(Of DetailedInvoiceData) Dim pdf = PdfDocument.FromFile(pdfPath) Dim text As String = pdf.ExtractAllText() ' Define the exact JSON structure we want the AI to return ' This schema guides the AI to extract all relevant fields Dim jsonSchema As String = "{ ""InvoiceNumber"": ""string"", ""InvoiceDate"": ""YYYY-MM-DD"", ""DueDate"": ""YYYY-MM-DD"", ""Vendor"": { ""Name"": ""string"", ""Address"": ""string"", ""TaxId"": ""string or null"" }, ""Customer"": { ""Name"": ""string"", ""Address"": ""string"" }, ""LineItems"": [ { ""Description"": ""string"", ""Quantity"": 0.0, ""UnitPrice"": 0.00, ""Total"": 0.00 } ], ""Subtotal"": 0.00, ""Tax"": 0.00, ""Total"": 0.00 }" ' Prompt includes both the schema and the extracted text Dim prompt As String = $" Extract all invoice data and return it in this exact JSON structure: {jsonSchema} Invoice text: {text} Return only valid JSON, no markdown formatting or additional text." ' Call AI API and parse response (implementation as shown above) ' Return deserialized DetailedInvoiceData Return New DetailedInvoiceData() ' Placeholder End Function End Class $vbLabelText $csharpLabel 如何處理格式不一致的發票 人工智慧資料擷取的真正威力體現在處理來自多個供應商、格式各異的發票時。 智慧處理器可以先嘗試基於模式的提取(速度更快、成本更低),僅在必要時才回退到人工智慧: using IronPdf; using System.Threading.Tasks; // Hybrid processor that optimizes for cost and capability // Tries fast regex patterns first, uses AI only when patterns fail public class SmartInvoiceProcessor { private readonly AIInvoiceParser _aiParser; public SmartInvoiceProcessor(string aiApiKey) { _aiParser = new AIInvoiceParser(aiApiKey); } public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // First attempt: regex patterns (fast and free) var patternParser = new InvoiceParser(); var standardResult = patternParser.ParseInvoiceFromText(text); // If pattern matching found all required fields, use that result if (IsComplete(standardResult)) { Console.WriteLine("Pattern extraction successful"); return standardResult; } // Fallback: use AI for complex or unusual invoice formats // This costs money but handles any layout Console.WriteLine("Using AI extraction for complex invoice format"); var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath); return aiResult; } // Validates that we have the minimum required fields private bool IsComplete(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.InvoiceDate) && data.TotalAmount > 0; } } using IronPdf; using System.Threading.Tasks; // Hybrid processor that optimizes for cost and capability // Tries fast regex patterns first, uses AI only when patterns fail public class SmartInvoiceProcessor { private readonly AIInvoiceParser _aiParser; public SmartInvoiceProcessor(string aiApiKey) { _aiParser = new AIInvoiceParser(aiApiKey); } public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // First attempt: regex patterns (fast and free) var patternParser = new InvoiceParser(); var standardResult = patternParser.ParseInvoiceFromText(text); // If pattern matching found all required fields, use that result if (IsComplete(standardResult)) { Console.WriteLine("Pattern extraction successful"); return standardResult; } // Fallback: use AI for complex or unusual invoice formats // This costs money but handles any layout Console.WriteLine("Using AI extraction for complex invoice format"); var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath); return aiResult; } // Validates that we have the minimum required fields private bool IsComplete(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.InvoiceDate) && data.TotalAmount > 0; } } Imports IronPdf Imports System.Threading.Tasks ' Hybrid processor that optimizes for cost and capability ' Tries fast regex patterns first, uses AI only when patterns fail Public Class SmartInvoiceProcessor Private ReadOnly _aiParser As AIInvoiceParser Public Sub New(aiApiKey As String) _aiParser = New AIInvoiceParser(aiApiKey) End Sub Public Async Function ProcessAnyInvoice(pdfPath As String) As Task(Of InvoiceData) Dim pdf = PdfDocument.FromFile(pdfPath) Dim text As String = pdf.ExtractAllText() ' First attempt: regex patterns (fast and free) Dim patternParser = New InvoiceParser() Dim standardResult = patternParser.ParseInvoiceFromText(text) ' If pattern matching found all required fields, use that result If IsComplete(standardResult) Then Console.WriteLine("Pattern extraction successful") Return standardResult End If ' Fallback: use AI for complex or unusual invoice formats ' This costs money but handles any layout Console.WriteLine("Using AI extraction for complex invoice format") Dim aiResult = Await _aiParser.ParseInvoiceWithAI(pdfPath) Return aiResult End Function ' Validates that we have the minimum required fields Private Function IsComplete(data As InvoiceData) As Boolean Return Not String.IsNullOrEmpty(data.InvoiceNumber) AndAlso Not String.IsNullOrEmpty(data.InvoiceDate) AndAlso data.TotalAmount > 0 End Function End Class $vbLabelText $csharpLabel 如何建立應付帳款自動化流程 將所有這些部分整合在一起,就形成了一個完整的自動化流程,它可以處理收到的發票、提取資料、驗證資料並將其準備好供您的會計系統使用: using IronPdf; using System; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; // Tracks the outcome of processing each invoice public class ProcessingResult { public string FileName { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string ErrorMessage { get; set; } } // Complete automation pipeline for accounts payable // Watches a folder, extracts data, validates, and routes to accounting system public class InvoiceAutomationPipeline { private readonly SmartInvoiceProcessor _processor; private readonly string _inputFolder; private readonly string _processedFolder; private readonly string _errorFolder; public InvoiceAutomationPipeline(string apiKey, string inputFolder) { _processor = new SmartInvoiceProcessor(apiKey); _inputFolder = inputFolder; _processedFolder = Path.Combine(inputFolder, "processed"); _errorFolder = Path.Combine(inputFolder, "errors"); // Create output directories if they don't exist Directory.CreateDirectory(_processedFolder); Directory.CreateDirectory(_errorFolder); } // Main entry point - processes all PDFs in the input folder public async Task<List<ProcessingResult>> ProcessInvoiceBatch() { string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf"); Console.WriteLine($"Found {invoiceFiles.Length} invoices to process"); var results = new List<ProcessingResult>(); foreach (string invoicePath in invoiceFiles) { string fileName = Path.GetFileName(invoicePath); try { Console.WriteLine($"Processing: {fileName}"); // Extract data using smart processor (patterns first, then AI) var invoiceData = await _processor.ProcessAnyInvoice(invoicePath); // Ensure we have minimum required fields before proceeding if (ValidateInvoiceData(invoiceData)) { // Send to accounting system (QuickBooks, Xero, etc.) await SaveToAccountingSystem(invoiceData); // Archive successful invoices string destPath = Path.Combine(_processedFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }); Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}"); } else { throw new Exception("Validation failed - missing required fields"); } } catch (Exception ex) { Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}"); // Quarantine failed invoices for manual review string destPath = Path.Combine(_errorFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = false, ErrorMessage = ex.Message }); } } GenerateReport(results); return results; } // Checks for minimum required fields private bool ValidateInvoiceData(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.VendorName) && data.TotalAmount > 0; } // Placeholder for accounting system integration private async Task SaveToAccountingSystem(InvoiceData data) { // Integrate with your accounting system here // Examples: QuickBooks API, Xero API, SAP, or database storage Console.WriteLine($" Saved invoice {data.InvoiceNumber} to accounting system"); await Task.CompletedTask; } // Outputs a summary of the batch processing results private void GenerateReport(List<ProcessingResult> results) { int successful = results.Count(r => r.Success); int failed = results.Count(r => !r.Success); Console.WriteLine($"\n========== Processing Complete =========="); Console.WriteLine($"Total Processed: {results.Count}"); Console.WriteLine($"Successful: {successful}"); Console.WriteLine($"Failed: {failed}"); if (failed > 0) { Console.WriteLine("\nFailed invoices requiring review:"); foreach (var failure in results.Where(r => !r.Success)) { Console.WriteLine($" • {failure.FileName}: {failure.ErrorMessage}"); } } } } using IronPdf; using System; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; // Tracks the outcome of processing each invoice public class ProcessingResult { public string FileName { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string ErrorMessage { get; set; } } // Complete automation pipeline for accounts payable // Watches a folder, extracts data, validates, and routes to accounting system public class InvoiceAutomationPipeline { private readonly SmartInvoiceProcessor _processor; private readonly string _inputFolder; private readonly string _processedFolder; private readonly string _errorFolder; public InvoiceAutomationPipeline(string apiKey, string inputFolder) { _processor = new SmartInvoiceProcessor(apiKey); _inputFolder = inputFolder; _processedFolder = Path.Combine(inputFolder, "processed"); _errorFolder = Path.Combine(inputFolder, "errors"); // Create output directories if they don't exist Directory.CreateDirectory(_processedFolder); Directory.CreateDirectory(_errorFolder); } // Main entry point - processes all PDFs in the input folder public async Task<List<ProcessingResult>> ProcessInvoiceBatch() { string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf"); Console.WriteLine($"Found {invoiceFiles.Length} invoices to process"); var results = new List<ProcessingResult>(); foreach (string invoicePath in invoiceFiles) { string fileName = Path.GetFileName(invoicePath); try { Console.WriteLine($"Processing: {fileName}"); // Extract data using smart processor (patterns first, then AI) var invoiceData = await _processor.ProcessAnyInvoice(invoicePath); // Ensure we have minimum required fields before proceeding if (ValidateInvoiceData(invoiceData)) { // Send to accounting system (QuickBooks, Xero, etc.) await SaveToAccountingSystem(invoiceData); // Archive successful invoices string destPath = Path.Combine(_processedFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }); Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}"); } else { throw new Exception("Validation failed - missing required fields"); } } catch (Exception ex) { Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}"); // Quarantine failed invoices for manual review string destPath = Path.Combine(_errorFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = false, ErrorMessage = ex.Message }); } } GenerateReport(results); return results; } // Checks for minimum required fields private bool ValidateInvoiceData(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.VendorName) && data.TotalAmount > 0; } // Placeholder for accounting system integration private async Task SaveToAccountingSystem(InvoiceData data) { // Integrate with your accounting system here // Examples: QuickBooks API, Xero API, SAP, or database storage Console.WriteLine($" Saved invoice {data.InvoiceNumber} to accounting system"); await Task.CompletedTask; } // Outputs a summary of the batch processing results private void GenerateReport(List<ProcessingResult> results) { int successful = results.Count(r => r.Success); int failed = results.Count(r => !r.Success); Console.WriteLine($"\n========== Processing Complete =========="); Console.WriteLine($"Total Processed: {results.Count}"); Console.WriteLine($"Successful: {successful}"); Console.WriteLine($"Failed: {failed}"); if (failed > 0) { Console.WriteLine("\nFailed invoices requiring review:"); foreach (var failure in results.Where(r => !r.Success)) { Console.WriteLine($" • {failure.FileName}: {failure.ErrorMessage}"); } } } } Imports IronPdf Imports System Imports System.IO Imports System.Threading.Tasks Imports System.Collections.Generic Imports System.Linq ' Tracks the outcome of processing each invoice Public Class ProcessingResult Public Property FileName As String Public Property Success As Boolean Public Property InvoiceNumber As String Public Property ErrorMessage As String End Class ' Complete automation pipeline for accounts payable ' Watches a folder, extracts data, validates, and routes to accounting system Public Class InvoiceAutomationPipeline Private ReadOnly _processor As SmartInvoiceProcessor Private ReadOnly _inputFolder As String Private ReadOnly _processedFolder As String Private ReadOnly _errorFolder As String Public Sub New(apiKey As String, inputFolder As String) _processor = New SmartInvoiceProcessor(apiKey) _inputFolder = inputFolder _processedFolder = Path.Combine(inputFolder, "processed") _errorFolder = Path.Combine(inputFolder, "errors") ' Create output directories if they don't exist Directory.CreateDirectory(_processedFolder) Directory.CreateDirectory(_errorFolder) End Sub ' Main entry point - processes all PDFs in the input folder Public Async Function ProcessInvoiceBatch() As Task(Of List(Of ProcessingResult)) Dim invoiceFiles As String() = Directory.GetFiles(_inputFolder, "*.pdf") Console.WriteLine($"Found {invoiceFiles.Length} invoices to process") Dim results As New List(Of ProcessingResult)() For Each invoicePath As String In invoiceFiles Dim fileName As String = Path.GetFileName(invoicePath) Try Console.WriteLine($"Processing: {fileName}") ' Extract data using smart processor (patterns first, then AI) Dim invoiceData = Await _processor.ProcessAnyInvoice(invoicePath) ' Ensure we have minimum required fields before proceeding If ValidateInvoiceData(invoiceData) Then ' Send to accounting system (QuickBooks, Xero, etc.) Await SaveToAccountingSystem(invoiceData) ' Archive successful invoices Dim destPath As String = Path.Combine(_processedFolder, fileName) File.Move(invoicePath, destPath, overwrite:=True) results.Add(New ProcessingResult With { .FileName = fileName, .Success = True, .InvoiceNumber = invoiceData.InvoiceNumber }) Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}") Else Throw New Exception("Validation failed - missing required fields") End If Catch ex As Exception Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}") ' Quarantine failed invoices for manual review Dim destPath As String = Path.Combine(_errorFolder, fileName) File.Move(invoicePath, destPath, overwrite:=True) results.Add(New ProcessingResult With { .FileName = fileName, .Success = False, .ErrorMessage = ex.Message }) End Try Next GenerateReport(results) Return results End Function ' Checks for minimum required fields Private Function ValidateInvoiceData(data As InvoiceData) As Boolean Return Not String.IsNullOrEmpty(data.InvoiceNumber) AndAlso Not String.IsNullOrEmpty(data.VendorName) AndAlso data.TotalAmount > 0 End Function ' Placeholder for accounting system integration Private Async Function SaveToAccountingSystem(data As InvoiceData) As Task ' Integrate with your accounting system here ' Examples: QuickBooks API, Xero API, SAP, or database storage Console.WriteLine($" Saved invoice {data.InvoiceNumber} to accounting system") Await Task.CompletedTask End Function ' Outputs a summary of the batch processing results Private Sub GenerateReport(results As List(Of ProcessingResult)) Dim successful As Integer = results.Count(Function(r) r.Success) Dim failed As Integer = results.Count(Function(r) Not r.Success) Console.WriteLine(vbCrLf & "========== Processing Complete ==========") Console.WriteLine($"Total Processed: {results.Count}") Console.WriteLine($"Successful: {successful}") Console.WriteLine($"Failed: {failed}") If failed > 0 Then Console.WriteLine(vbCrLf & "Failed invoices requiring review:") For Each failure In results.Where(Function(r) Not r.Success) Console.WriteLine($" • {failure.FileName}: {failure.ErrorMessage}") Next End If End Sub End Class $vbLabelText $csharpLabel 該管道實現了一個完整的工作流程:它掃描資料夾中的傳入 PDF 文件,處理每個文件,驗證提取的數據,將成功提取的文件路由到您的會計系統,並將失敗的文件隔離以供人工審核。 總結報告可以清楚地顯示處理結果。 如何將 C# 發票處理與會計系統集成 提取的發票資料最終需要匯入會計系統進行支付和記錄保存。 具體細節因平台而異,但整合模式是一致的。 QuickBooks、Xero 和 SAP 的常見整合模式有哪些? 大多數會計平台都提供 REST API,用於以程式設計方式建立帳單或發票。 以下是一個通用模式,您可以根據自己的平台進行調整: using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Generic integration layer for pushing invoice data to accounting systems // Adapt the API calls based on your specific platform public class AccountingSystemIntegration { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _baseUrl; public AccountingSystemIntegration(string apiKey, string baseUrl) { _apiKey = apiKey; _baseUrl = baseUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } // Creates a Bill in QuickBooks (vendor invoices are called "Bills") public async Task SendToQuickBooks(InvoiceData invoice) { // QuickBooks Bill structure - see their API docs for full schema var bill = new { VendorRef = new { name = invoice.VendorName }, TxnDate = invoice.InvoiceDate, DocNumber = invoice.InvoiceNumber, TotalAmt = invoice.TotalAmount, Line = new[] { new { Amount = invoice.TotalAmount, DetailType = "AccountBasedExpenseLineDetail", AccountBasedExpenseLineDetail = new { AccountRef = new { name = "Accounts Payable" } } } } }; await PostToApi("/v3/company/{companyId}/bill", bill); } // Creates an accounts payable invoice in Xero public async Task SendToXero(InvoiceData invoice) { // ACCPAY type indicates this is a bill to pay (not a sales invoice) var bill = new { Type = "ACCPAY", Contact = new { Name = invoice.VendorName }, Date = invoice.InvoiceDate, InvoiceNumber = invoice.InvoiceNumber, Total = invoice.TotalAmount }; await PostToApi("/api.xro/2.0/Invoices", bill); } // Generic POST helper with error handling private async Task PostToApi(string endpoint, object payload) { string json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content); if (!response.IsSuccessStatusCode) { string error = await response.Content.ReadAsStringAsync(); throw new Exception($"API Error: {response.StatusCode} - {error}"); } Console.WriteLine($"Successfully posted to {endpoint}"); } } using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Generic integration layer for pushing invoice data to accounting systems // Adapt the API calls based on your specific platform public class AccountingSystemIntegration { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _baseUrl; public AccountingSystemIntegration(string apiKey, string baseUrl) { _apiKey = apiKey; _baseUrl = baseUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } // Creates a Bill in QuickBooks (vendor invoices are called "Bills") public async Task SendToQuickBooks(InvoiceData invoice) { // QuickBooks Bill structure - see their API docs for full schema var bill = new { VendorRef = new { name = invoice.VendorName }, TxnDate = invoice.InvoiceDate, DocNumber = invoice.InvoiceNumber, TotalAmt = invoice.TotalAmount, Line = new[] { new { Amount = invoice.TotalAmount, DetailType = "AccountBasedExpenseLineDetail", AccountBasedExpenseLineDetail = new { AccountRef = new { name = "Accounts Payable" } } } } }; await PostToApi("/v3/company/{companyId}/bill", bill); } // Creates an accounts payable invoice in Xero public async Task SendToXero(InvoiceData invoice) { // ACCPAY type indicates this is a bill to pay (not a sales invoice) var bill = new { Type = "ACCPAY", Contact = new { Name = invoice.VendorName }, Date = invoice.InvoiceDate, InvoiceNumber = invoice.InvoiceNumber, Total = invoice.TotalAmount }; await PostToApi("/api.xro/2.0/Invoices", bill); } // Generic POST helper with error handling private async Task PostToApi(string endpoint, object payload) { string json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content); if (!response.IsSuccessStatusCode) { string error = await response.Content.ReadAsStringAsync(); throw new Exception($"API Error: {response.StatusCode} - {error}"); } Console.WriteLine($"Successfully posted to {endpoint}"); } } Imports System Imports System.Net.Http Imports System.Text Imports System.Text.Json Imports System.Threading.Tasks ' Generic integration layer for pushing invoice data to accounting systems ' Adapt the API calls based on your specific platform Public Class AccountingSystemIntegration Private ReadOnly _httpClient As HttpClient Private ReadOnly _apiKey As String Private ReadOnly _baseUrl As String Public Sub New(apiKey As String, baseUrl As String) _apiKey = apiKey _baseUrl = baseUrl _httpClient = New HttpClient() _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}") End Sub ' Creates a Bill in QuickBooks (vendor invoices are called "Bills") Public Async Function SendToQuickBooks(invoice As InvoiceData) As Task ' QuickBooks Bill structure - see their API docs for full schema Dim bill = New With { .VendorRef = New With {.name = invoice.VendorName}, .TxnDate = invoice.InvoiceDate, .DocNumber = invoice.InvoiceNumber, .TotalAmt = invoice.TotalAmount, .Line = New Object() { New With { .Amount = invoice.TotalAmount, .DetailType = "AccountBasedExpenseLineDetail", .AccountBasedExpenseLineDetail = New With { .AccountRef = New With {.name = "Accounts Payable"} } } } } Await PostToApi("/v3/company/{companyId}/bill", bill) End Function ' Creates an accounts payable invoice in Xero Public Async Function SendToXero(invoice As InvoiceData) As Task ' ACCPAY type indicates this is a bill to pay (not a sales invoice) Dim bill = New With { .Type = "ACCPAY", .Contact = New With {.Name = invoice.VendorName}, .Date = invoice.InvoiceDate, .InvoiceNumber = invoice.InvoiceNumber, .Total = invoice.TotalAmount } Await PostToApi("/api.xro/2.0/Invoices", bill) End Function ' Generic POST helper with error handling Private Async Function PostToApi(endpoint As String, payload As Object) As Task Dim json As String = JsonSerializer.Serialize(payload) Dim content = New StringContent(json, Encoding.UTF8, "application/json") Dim response = Await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content) If Not response.IsSuccessStatusCode Then Dim error As String = Await response.Content.ReadAsStringAsync() Throw New Exception($"API Error: {response.StatusCode} - {error}") End If Console.WriteLine($"Successfully posted to {endpoint}") End Function End Class $vbLabelText $csharpLabel 每個平台都有自己的驗證機制(QuickBooks 和 Xero 使用 OAuth,SAP 使用各種方法)、必填欄位和 API 約定。 有關具體細節,請參閱目標平台的文檔,但將提取的發票資料轉換為 API 有效負載的模式保持不變。 如何大量處理數百張發票 大批量發票處理需要格外注意並發性和資源管理。 以下是使用可控並發的平行處理模式: using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; // Tracks the result of processing a single invoice in a batch public class BatchResult { public string FilePath { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string Error { get; set; } } // High-volume invoice processor with controlled parallelism // Prevents overwhelming APIs while maximizing throughput public class BatchInvoiceProcessor { private readonly SmartInvoiceProcessor _invoiceProcessor; private readonly AccountingSystemIntegration _accountingIntegration; private readonly int _maxConcurrency; public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey, string accountingUrl, int maxConcurrency = 5) { _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey); _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl); _maxConcurrency = maxConcurrency; // Adjust based on API rate limits } // Processes multiple invoices in parallel with controlled concurrency public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths) { // Thread-safe collection for gathering results from parallel tasks var results = new ConcurrentBag<BatchResult>(); // Semaphore limits how many invoices process simultaneously var semaphore = new SemaphoreSlim(_maxConcurrency); // Create a task for each invoice var tasks = invoicePaths.Select(async path => { // Wait for a slot to become available await semaphore.WaitAsync(); try { var result = await ProcessSingleInvoice(path); results.Add(result); } finally { // Release slot for next invoice semaphore.Release(); } }); // Wait for all invoices to complete await Task.WhenAll(tasks); // Output summary statistics var resultList = results.ToList(); int successful = resultList.Count(r => r.Success); int failed = resultList.Count(r => !r.Success); Console.WriteLine($"\nBatch Processing Complete:"); Console.WriteLine($" Total: {resultList.Count}"); Console.WriteLine($" Successful: {successful}"); Console.WriteLine($" Failed: {failed}"); return resultList; } // Processes one invoice: extract data and send to accounting system private async Task<BatchResult> ProcessSingleInvoice(string pdfPath) { try { Console.WriteLine($"Processing: {pdfPath}"); var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath); await _accountingIntegration.SendToQuickBooks(invoiceData); Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}"); return new BatchResult { FilePath = pdfPath, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }; } catch (Exception ex) { Console.WriteLine($"✗ Failed: {pdfPath}"); return new BatchResult { FilePath = pdfPath, Success = false, Error = ex.Message }; } } } using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; // Tracks the result of processing a single invoice in a batch public class BatchResult { public string FilePath { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string Error { get; set; } } // High-volume invoice processor with controlled parallelism // Prevents overwhelming APIs while maximizing throughput public class BatchInvoiceProcessor { private readonly SmartInvoiceProcessor _invoiceProcessor; private readonly AccountingSystemIntegration _accountingIntegration; private readonly int _maxConcurrency; public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey, string accountingUrl, int maxConcurrency = 5) { _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey); _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl); _maxConcurrency = maxConcurrency; // Adjust based on API rate limits } // Processes multiple invoices in parallel with controlled concurrency public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths) { // Thread-safe collection for gathering results from parallel tasks var results = new ConcurrentBag<BatchResult>(); // Semaphore limits how many invoices process simultaneously var semaphore = new SemaphoreSlim(_maxConcurrency); // Create a task for each invoice var tasks = invoicePaths.Select(async path => { // Wait for a slot to become available await semaphore.WaitAsync(); try { var result = await ProcessSingleInvoice(path); results.Add(result); } finally { // Release slot for next invoice semaphore.Release(); } }); // Wait for all invoices to complete await Task.WhenAll(tasks); // Output summary statistics var resultList = results.ToList(); int successful = resultList.Count(r => r.Success); int failed = resultList.Count(r => !r.Success); Console.WriteLine($"\nBatch Processing Complete:"); Console.WriteLine($" Total: {resultList.Count}"); Console.WriteLine($" Successful: {successful}"); Console.WriteLine($" Failed: {failed}"); return resultList; } // Processes one invoice: extract data and send to accounting system private async Task<BatchResult> ProcessSingleInvoice(string pdfPath) { try { Console.WriteLine($"Processing: {pdfPath}"); var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath); await _accountingIntegration.SendToQuickBooks(invoiceData); Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}"); return new BatchResult { FilePath = pdfPath, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }; } catch (Exception ex) { Console.WriteLine($"✗ Failed: {pdfPath}"); return new BatchResult { FilePath = pdfPath, Success = false, Error = ex.Message }; } } } Imports System Imports System.Collections.Concurrent Imports System.Collections.Generic Imports System.Linq Imports System.Threading Imports System.Threading.Tasks ' Tracks the result of processing a single invoice in a batch Public Class BatchResult Public Property FilePath As String Public Property Success As Boolean Public Property InvoiceNumber As String Public Property Error As String End Class ' High-volume invoice processor with controlled parallelism ' Prevents overwhelming APIs while maximizing throughput Public Class BatchInvoiceProcessor Private ReadOnly _invoiceProcessor As SmartInvoiceProcessor Private ReadOnly _accountingIntegration As AccountingSystemIntegration Private ReadOnly _maxConcurrency As Integer Public Sub New(aiApiKey As String, accountingApiKey As String, accountingUrl As String, Optional maxConcurrency As Integer = 5) _invoiceProcessor = New SmartInvoiceProcessor(aiApiKey) _accountingIntegration = New AccountingSystemIntegration(accountingApiKey, accountingUrl) _maxConcurrency = maxConcurrency ' Adjust based on API rate limits End Sub ' Processes multiple invoices in parallel with controlled concurrency Public Async Function ProcessInvoiceBatch(invoicePaths As List(Of String)) As Task(Of List(Of BatchResult)) ' Thread-safe collection for gathering results from parallel tasks Dim results = New ConcurrentBag(Of BatchResult)() ' Semaphore limits how many invoices process simultaneously Dim semaphore = New SemaphoreSlim(_maxConcurrency) ' Create a task for each invoice Dim tasks = invoicePaths.Select(Async Function(path) ' Wait for a slot to become available Await semaphore.WaitAsync() Try Dim result = Await ProcessSingleInvoice(path) results.Add(result) Finally ' Release slot for next invoice semaphore.Release() End Try End Function) ' Wait for all invoices to complete Await Task.WhenAll(tasks) ' Output summary statistics Dim resultList = results.ToList() Dim successful = resultList.Count(Function(r) r.Success) Dim failed = resultList.Count(Function(r) Not r.Success) Console.WriteLine(vbCrLf & "Batch Processing Complete:") Console.WriteLine($" Total: {resultList.Count}") Console.WriteLine($" Successful: {successful}") Console.WriteLine($" Failed: {failed}") Return resultList End Function ' Processes one invoice: extract data and send to accounting system Private Async Function ProcessSingleInvoice(pdfPath As String) As Task(Of BatchResult) Try Console.WriteLine($"Processing: {pdfPath}") Dim invoiceData = Await _invoiceProcessor.ProcessAnyInvoice(pdfPath) Await _accountingIntegration.SendToQuickBooks(invoiceData) Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}") Return New BatchResult With { .FilePath = pdfPath, .Success = True, .InvoiceNumber = invoiceData.InvoiceNumber } Catch ex As Exception Console.WriteLine($"✗ Failed: {pdfPath}") Return New BatchResult With { .FilePath = pdfPath, .Success = False, .Error = ex.Message } End Try End Function End Class $vbLabelText $csharpLabel SemaphoreSlim可確保您不會對外部 API 造成過載或耗盡系統資源。 根據您的 API 速率限制和伺服器容量調整_maxConcurrency 。 ConcurrentBag安全地收集並行操作的結果。 後續步驟 發票自動化為減少人工操作、最大限度地減少錯誤和加快業務流程提供了重要機會。 本指南帶您了解了完整的生命週期:從HTML 範本產生專業發票,遵守ZUGFeRD 和 Factur-X電子發票標準,使用模式匹配和AI 驅動的處理從收到的發票中提取數據,以及建立可擴展的自動化管道。 IronPDF為這些功能奠定了基礎,提供強大的 HTML 到 PDF 渲染、可靠的文字擷取以及符合 PDF/A-3 電子發票要求的配件功能。 其基於 Chrome 的渲染引擎確保您的發票看起來與設計完全一致,而其提取方法可自動處理 PDF 文字編碼的複雜性。 這裡展示的模式只是起點。 實際應用程式中需要根據您特定的發票格式、會計系統和業務規則進行調整。 對於大批量處理場景,批次教程涵蓋了受控並發的並行執行和錯誤恢復。 準備開始建造了嗎? 下載 IronPDF並免費試用。 該庫包含一個免費的開發許可證,因此您可以在購買生產許可證之前充分評估發票生成、資料提取和PDF 報告功能。 如果您對發票自動化或會計系統整合有任何疑問,請聯絡我們的工程支援團隊。 常見問題解答 IronPDF 在 C# 發票處理上有什麼用途? IronPDF 用於 C# 發票處理,以產生專業的 PDF 發票、提取結構化資料並自動化發票工作流程,同時確保符合 ZUGFeRD 和 Factur-X 等標準。 如何使用C#中的IronPDF產生PDF發票? 您可以使用 IronPDF 在 C# 中產生 PDF 發票,透過利用其 API 以程式設計方式建立和自訂 PDF 文件。這包括添加構成發票的文字、表格和圖像等元素。 ZUGFeRD 和 Factur-X 是什麼? IronPDF 如何支援它們? ZUGFeRD 和 Factur-X 是電子發票標準,確保發票既便於人閱讀也方便機器讀取。 IronPDF 支援這些標準,讓您可以產生符合這些規範的 PDF 發票。 IronPDF 如何協助應付帳款流程自動化? IronPDF 可以透過從發票中提取結構化資料並與自動化管道整合來自動化應付帳款流程,從而減少手動資料輸入並提高效率。 IronPDF能否從現有的PDF發票中擷取資料? 是的,IronPDF 可以從現有的 PDF 發票中提取結構化數據,從而更容易自動處理和分析發票資訊。 在 C# 中使用 IronPDF 進行發票處理有哪些優點? 使用 IronPDF 在 C# 中進行發票處理的好處包括簡化發票產生、符合國際發票標準、高效資料提取和增強自動化功能。 是否可以使用 IronPDF 自訂 PDF 發票的外觀? 是的,IronPDF 允許您透過添加各種設計元素(例如標誌、文字格式和佈局調整)來自訂 PDF 發票的外觀,以滿足品牌要求。 使用 IronPDF 實現發票處理自動化的典型步驟是什麼? 要使用 IronPDF 自動處理發票,通常需要產生發票、提取必要的數據,並與其他系統或自動化工具集成,以簡化工作流程。 IronPDF如何處理不同的發票格式? IronPDF 提供產生、操作和讀取 PDF 文件的工具,能夠處理各種發票格式,確保與常見的電子發票標準相容。 Curtis Chau 立即與工程團隊聊天 技術撰稿人 Curtis Chau 擁有電腦科學學士學位(卡爾頓大學),專長於前端開發,精通 Node.js、TypeScript、JavaScript 和 React。Curtis 對製作直覺且美觀的使用者介面充滿熱情,他喜歡使用現代化的架構,並製作結構良好且視覺上吸引人的手冊。除了開發之外,Curtis 對物聯網 (IoT) 也有濃厚的興趣,他喜歡探索整合硬體與軟體的創新方式。在空閒時間,他喜歡玩遊戲和建立 Discord bots,將他對技術的熱愛與創意結合。 準備好開始了嗎? Nuget 下載 17,386,124 | 版本: 2026.2 剛剛發布 免費 NuGet 下載 總下載量:17,386,124 查看許可證