Zig
Zig
Send ZATCA Invoice for Clearance
See more ZATCA Examples
Demonstrates how to send a ZATCA invoice for clearance, which is to send the HTTP POST with invoice hash and the signed invoice in base64 format.Chilkat Zig Downloads
const std = @import("std");
const chilkat = @import("chilkat");
pub fn main(init: std.process.Init) !void {
const alloc = init.arena.allocator();
// This example requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.
// This example will send a POST with HTTP Basic Authentication to
// https://gw-fatoora.zatca.sa/e-invoicing/simulation/invoices/clearance/single
// NOTE: At the time of this writing, I don't know for sure if the above URL is correct.
//
// The body of the POST will contain JSON formatted as such:
// {
// "summary": "Standard Invoice",
// "value": {
// "invoiceHash": "PEx8bNFcEMEpHzUVvQntQI6ot8eFqTT/l59b+H1HqX4=",
// "uuid": "16e78469-64af-406d-9cfd-895e724198f0",
// "invoice": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPEl....."
// }
// }
// The above JSON is created from the signed XML invoice, such as the following:
// *** Look at the above base64 data for the invoiceHash and uuid. These values are found in the invoice as shown below.
// *** We'll write code to load the signed invoice into a Chilkat XML object, grab the invoice hash and the uuid,
// *** and then construct the above JSON, which will then be sent in a POST. (See below...)
// <?xml version="1.0" encoding="UTF-8"?>
// <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"><ext:UBLExtensions>
// <ext:UBLExtension>
// <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI>
// <ext:ExtensionContent>
// <!-- Please note that the signature values are sample values only -->
// <sig:UBLDocumentSignatures xmlns:sig="urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2" xmlns:sac="urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2" xmlns:sbc="urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2">
// <sac:SignatureInformation>
// <cbc:ID>urn:oasis:names:specification:ubl:signature:1</cbc:ID>
// <sbc:ReferencedSignatureID>urn:oasis:names:specification:ubl:signature:Invoice</sbc:ReferencedSignatureID>
// <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="signature">
// <ds:SignedInfo>
// <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
// <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"/>
// <ds:Reference Id="invoiceSignedData" URI="">
// <ds:Transforms>
// <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
// <ds:XPath>not(//ancestor-or-self::ext:UBLExtensions)</ds:XPath>
// </ds:Transform>
// <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
// <ds:XPath>not(//ancestor-or-self::cac:Signature)</ds:XPath>
// </ds:Transform>
// <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
// <ds:XPath>not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])</ds:XPath>
// </ds:Transform>
// <ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
// </ds:Transforms>
// <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
// <ds:DigestValue>PEx8bNFcEMEpHzUVvQntQI6ot8eFqTT/l59b+H1HqX4=</ds:DigestValue>
// </ds:Reference>
// <ds:Reference Type="http://www.w3.org/2000/09/xmldsig#SignatureProperties" URI="#xadesSignedProperties">
// <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
// <ds:DigestValue>ZDEyMDUyODJjYzk4MGViNTJhNmYzMGIyZTgxODhkY2JlOWEzNmRiMTFlZTVhMDAxNjk5OTRkYTg3ODhlY2ZiMw==</ds:DigestValue>
// </ds:Reference>
// </ds:SignedInfo>
// ...
// ...
// ...
// </ext:ExtensionContent>
// </ext:UBLExtension>
// </ext:UBLExtensions>
//
// <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
// <cbc:ID>SME00062</cbc:ID>
// <cbc:UUID>16e78469-64af-406d-9cfd-895e724198f0</cbc:UUID>
// <cbc:IssueDate>2022-03-13</cbc:IssueDate>
// <cbc:IssueTime>14:40:40</cbc:IssueTime>
// <cbc:InvoiceTypeCode name="0111010">388</cbc:InvoiceTypeCode>
// ...
// ...
// ...
// </cac:InvoiceLine>
// </Invoice>
// -------------------------------------------------------------------
// First, let's load our signed XML into a Chilkat XML object so we can get the uuid and invoice hash.
const signed_xml_file_path = "qa_data/zatca/http_post_clearance/sample_signed_invoice.xml";
const signed_xml = try chilkat.Xml.init();
defer signed_xml.deinit();
signed_xml.loadXmlFile(signed_xml_file_path) catch {
std.debug.print("{s}\n", .{try signed_xml.getLastErrorText(alloc)});
return;
};
const invoice_hash = try signed_xml.getChildContent(alloc, "ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures|sac:SignatureInformation|ds:Signature|ds:SignedInfo|ds:Reference[0]|ds:DigestValue");
std.debug.print("invoiceHash: {s}\n", .{invoice_hash});
const cbc_uuid = try signed_xml.getChildContent(alloc, "cbc:UUID");
std.debug.print("UUID: {s}\n", .{cbc_uuid});
// The Chilkat XML object was used purely for getting the above 2 values.
// We must send the EXACT signed XML in base64 format. Therefore, we should load the signed XML into a Chilkat BinData and get it in base64 single-line format.
const bd = try chilkat.BinData.init();
defer bd.deinit();
try bd.loadFile(signed_xml_file_path);
const base64_invoice = try bd.getEncoded(alloc, "base64");
// OK, let's build the JSON that will be in the HTTP POST body.
const json = try chilkat.JsonObject.init();
defer json.deinit();
json.updateString("summary", "Standard Invoice") catch {};
json.updateString("value.invoiceHash", invoice_hash) catch {};
json.updateString("value.uuid", cbc_uuid) catch {};
json.updateString("value.invoice", base64_invoice) catch {};
// Now create the HTTP object and send the POST.
const http = try chilkat.Http.init();
defer http.deinit();
// We'll want HTTP basic authentication.
http.setBasicAuth(true);
http.setLogin("your login");
http.setPassword("your password");
const resp = try chilkat.HttpResponse.init();
defer resp.deinit();
http.httpJson("POST", "https://gw-fatoora.zatca.sa/e-invoicing/simulation/invoices/clearance/single", json, "application/json", resp) catch {
std.debug.print("{s}\n", .{try http.getLastErrorText(alloc)});
return;
};
// Check the response status code. A 200 or 202 indicates success..
const status_code = resp.getStatusCode();
std.debug.print("Response status code = {d}\n", .{status_code});
// Examine the response, which is JSON.
const json_resp = try chilkat.JsonObject.init();
defer json_resp.deinit();
resp.getBodyJson(json_resp) catch {};
json_resp.setEmitCompact(false);
std.debug.print("JSON Response:\n", .{});
std.debug.print("{s}\n", .{try json_resp.emit(alloc)});
// Use this online tool to generate parsing code from sample JSON:
// Generate Parsing Code from JSON
}