├── .github
└── workflows
│ └── dotnet.yml
├── .gitignore
├── Bee.ZatcaHelper.IntegrationTests
├── Bee.ZatcaHelper.IntegrationTests.csproj
├── CreditNote_E2ETest.cs
├── DebitNote_E2ETest.cs
├── Program.cs
├── StandardInvoice_E2ETest.cs
├── StandardInvoice_ZeroTax_E2ETest.cs
└── Usings.cs
├── Bee.ZatcaHelper.UnitTests
├── Bee.ZatcaHelper.UnitTests.csproj
├── ComplianceCsrApiClientTests.cs
├── ProdCsidApiClientTests.cs
├── StandardInvoiceXmlGeneratorTests.cs
├── Usings.cs
└── UtilTests
│ └── InvoiceHashHelperTests.cs
├── Bee.ZatcaHelper
├── Bee.ZatcaHelper.csproj
├── ComplianceCsrAPIClient.cs
├── Contracts
│ ├── ComplianceCsrRequest.cs
│ ├── ComplianceCsrResponse.cs
│ ├── InvoiceClearanceRequest.cs
│ ├── InvoiceClearanceResponse.cs
│ ├── ProdCsidOnboardingRequest.cs
│ └── ProdCsidOnboardingResponse.cs
├── CreditNote.xml
├── DebitNote.xml
├── GlobalVariables.cs
├── IsExternalInit.cs
├── Model
│ ├── CustomerInfo.cs
│ ├── InvoiceLineItem.cs
│ ├── LegalMonetaryTotal.cs
│ ├── Money.cs
│ ├── PartyPostalAddress.cs
│ ├── StandardInvoice.cs
│ ├── StandardInvoiceAdjustment.cs
│ ├── SupplierInfo.cs
│ └── TaxTotal.cs
├── ProdCsidApiClient.cs
├── StanadardInvoiceXpaths.cs
├── StandardInvoiceClearanceAPIClient.cs
├── StandardInvoiceTemplate.xml
├── StandardInvoiceXmlGenerator.cs
└── Util
│ ├── EmbeddedResourceHelper.cs
│ ├── InvoiceHashHelper.cs
│ ├── StandardInvoiceHashingTemplate.xsl
│ ├── WebClient.cs
│ └── XmlHelper.cs
├── Logo
└── Devbee-Logo.png
├── README.md
├── Zatca-Standard-Invoice-Integration-Client.sln
└── pro-version-features.md
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: Publish Packages
2 | on:
3 | push:
4 | branches: [ main ]
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Setup .NET
13 | uses: actions/setup-dotnet@v1
14 | with:
15 | dotnet-version: 6.0.x
16 | - name: Restore dependencies
17 | run: dotnet restore
18 | - name: Build
19 | run: dotnet build --configuration Release --no-restore
20 | - name: Publish package
21 | uses: brandedoutcast/publish-nuget@v2.5.2
22 | with:
23 | PROJECT_FILE_PATH: Bee.ZatcaHelper/Bee.ZatcaHelper.csproj
24 | NUGET_KEY: ${{secrets.NUGET_API_KEY}}
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | bin/
3 | obj/
4 | .vs/
5 | .idea/
6 | Zatca-Standard-Invoice-Integration-Client.sln.DotSettings.user
7 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/Bee.ZatcaHelper.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/CreditNote_E2ETest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.IntegrationTests;
2 |
3 | public static class CreditNote_E2ETest
4 | {
5 | private static readonly GlobalVariables GlobalVariables = new GlobalVariables()
6 | {
7 | BaseUrl = "https://gw-apic-gov.gazt.gov.sa",
8 | ComplianceCsidEndpoint = "/e-invoicing/developer-portal/compliance",
9 | ProdCsidEndpoint = "/e-invoicing/developer-portal/production/csids",
10 | InvoiceClearanceEndPoint = "/e-invoicing/developer-portal/invoices/clearance/single"
11 | };
12 |
13 | private const string Otp = "123456";
14 |
15 | private const string Csr =
16 | "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0NIE1JSUI3akNDQVpNQ0FRQXdZREVMTUFrR0ExVUVCaE1DVTBFeEZUQVRCZ05WQkFzTURGSnBlV0ZrSUVKeVlXNWoNIGFERXRNQ3NHQTFVRUNnd2tWVzVwZEdWa0lFZDFiR1lnUVdseVkzSmhablFnUm5WbGJHbHVaeUJEYjIxd1lXNTUNIE1Rc3dDUVlEVlFRRERBSlRRVEJXTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFLQTBJQUJQWHFkK1Q1ZkZhK1ZlYmcNIHlzL1d4bkJPNHBwOHNVeWtlM1BMSmVKYjZLbkk1Z0ZEcHVsWWY1K1NvUFJZVGFmK1o3Y0g1RzdJSmExdVNYSXENIHkwWVdoVytnZ2RNd2dkQUdDU3FHU0liM0RRRUpEakdCd2pDQnZ6QWhCZ2tyQmdFRUFZSTNGQUlFRkJNU1drRlUNIFEwRXRRMjlrWlMxVGFXZHVhVzVuTUlHWkJnTlZIUkVFZ1pFd2dZNmtnWXN3Z1lneEp6QWxCZ05WQkFRTUhqRXQNIFZXZGhabU52ZkRJdFJWTkhWVTVKVkh3ekxURXlNelExTmpjNE9URWZNQjBHQ2dtU0pvbVQ4aXhrQVFFTUR6TXgNIE1ERTNOVE01TnpRd01EQXdNekVOTUFzR0ExVUVEQXdFTVRFd01ERVNNQkFHQTFVRUdnd0pUWGxCWkdSeVpYTnoNIE1Sa3dGd1lEVlFRUERCQkdkV1ZzYVc1bklFbHVaSFZ6ZEhKNU1Bb0dDQ3FHU000OUJBTUNBMGtBTUVZQ0lRQ1ANIFlFVGJuazFSblFYTDdvSUxJek1qMFJGQWg3QmErUEFPenJIQnMyYzdLZ0loQUxDaUVKNnRjVzdGcHdyVEZ0RGMNIFA1QjVoRE5nYjFGUTJXSVZjMzc1VS9PdA0gLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t";
17 |
18 | public static void Run()
19 | {
20 | var csrResponse = GetComplianceCsr(GlobalVariables, Otp, Csr);
21 |
22 | var prodOnboard = GetProdCsid(GlobalVariables, csrResponse);
23 |
24 | ClearInvoice(GlobalVariables, prodOnboard);
25 |
26 | Console.WriteLine("-----------------------------------------------------------------------------------------");
27 | Console.WriteLine("----------------------------------Test Run Completed-------------------------------------");
28 | Console.WriteLine("-----------------------------------------------------------------------------------------");
29 | }
30 |
31 | private static void ClearInvoice(GlobalVariables globalVariables, ProdCsidOnboardingResponse prodOnboard)
32 | {
33 | var invoice = new StandardInvoiceAdjustment()
34 | {
35 | AdjustmentReason = "test adjustment",
36 | AdjsutmentInvoiceNumber = "11221222",
37 | Id = "100012",
38 | UUID = Guid.NewGuid().ToString(),
39 | ActualDeliveryDate = "2022-09-13",
40 | DocumentCurrencyCode = "SAR",
41 | TaxCurrencyCode = "SAR",
42 | InvoiceTypeCode = "388",
43 | IssuedDate = "2022-09-13",
44 | IssuedTime = "14:40:40",
45 | LatestDeliveryDate = "2022-09-13",
46 | PreviousInvoiceHash =
47 | "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
48 | SupplierInfo = new SupplierInfo()
49 | {
50 | CRN = "310175397400003",
51 | PartyPostalAddress = new PartyPostalAddress()
52 | {
53 | StreetName = "testA",
54 | BuildingNumber = "3454",
55 | CityName = "RiyadhA",
56 | CitySubdivisionName = "fgffA",
57 | Country = "SA",
58 | CountrySubentity = "test",
59 | PlotIdentification = "1234",
60 | PostalZone = "12345",
61 | },
62 | RegistrationName = "United Gulf Aircraft Fueling Company",
63 | VAT = "310175397400003"
64 | },
65 | CustomerInfo = new CustomerInfo()
66 | {
67 | NAT = "2345",
68 | PartyPostalAddress = new PartyPostalAddress()
69 | {
70 | BuildingNumber = "3353",
71 | CityName = "DhurmaA",
72 | CitySubdivisionName = "fgffA",
73 | Country = "SA",
74 | CountrySubentity = "ulhkA",
75 | PlotIdentification = "3434",
76 | PostalZone = "34341",
77 | StreetName = "baaounA",
78 | },
79 | RegistrationName = "testA",
80 | VAT = "300075588700003",
81 | },
82 | InvoiceLineItem = new InvoiceLineItem()
83 | {
84 | ID = "1",
85 | InvoicedQuantity = "100",
86 | ItemName = "item",
87 | LineExtensionAmount = new Money("SAR", 1000.00),
88 | PriceAmount = new Money("SAR", 10.00),
89 | RoundingAmount = new Money("SAR", 1150.00),
90 | TaxAmount = new Money("SAR", 150.00),
91 | TaxPercent = "15.00",
92 | TaxScheme = "VAT",
93 | TaxSchemeId = "S"
94 | },
95 | LegalMonetaryTotal = new LegalMonetaryTotal()
96 | {
97 | AllowanceTotalAmount = new Money("SAR", 0.00),
98 | LineExtensionAmount = new Money("SAR", 1000.00),
99 | PayableAmount = new Money("SAR", 1150.00),
100 | PrepaidAmount = new Money("SAR", 0.00),
101 | TaxExclusiveAmount = new Money("SAR", 1000.00),
102 | TaxInclusiveAmount = new Money("SAR", 1150.00),
103 | },
104 | TaxTotal = new TaxTotal()
105 | {
106 | Percent = "15.00",
107 | TaxableAmount = new Money("SAR", 1000.00),
108 | TaxAmount = new Money("SAR", 150.00),
109 | TaxCategory = "SA"
110 | }
111 | };
112 |
113 |
114 | var generatedClearanceRequest = StandardInvoiceXmlGenerator.GenerateForCreditNote(invoice);
115 | Console.WriteLine("----------------------Standard Invoice Generation Request---------------------------------");
116 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(generatedClearanceRequest));
117 | var clearanceResponse = new StandardInvoiceClearanceApiClient(globalVariables).ClearInvoice(
118 | new InvoiceClearanceRequest(generatedClearanceRequest, prodOnboard.BinarySecurityToken,
119 | prodOnboard.Secret));
120 | if (clearanceResponse.ClearanceStatus == "NOT_CLEARED")
121 | {
122 | Console.WriteLine("----------------------Standard Invoice Clearance Request - Failure reason---------------------------------");
123 | Console.WriteLine(clearanceResponse.validationResults);
124 | }
125 | else
126 | {
127 | Console.WriteLine("----------------------Standard Invoice Clearance Request---------------------------------");
128 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(clearanceResponse));
129 | }
130 | Assert.IsNotNull(clearanceResponse);
131 | Assert.IsNotEmpty(clearanceResponse.GeneratedQR);
132 | }
133 |
134 | private static ProdCsidOnboardingResponse GetProdCsid(GlobalVariables globalVariables,
135 | ComplianceCsrResponse csrResponse)
136 | {
137 | var prodOnboard = new ProdCsidApiClient(globalVariables).GetToken(
138 | new ProdCsidOnboardingRequest(csrResponse.RequestId, csrResponse.BinarySecurityToken, csrResponse.Secret));
139 | Console.WriteLine("--------------------------------Prod Csid Request-----------------------------------");
140 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(prodOnboard));
141 | Assert.IsNotNull(prodOnboard);
142 | Assert.IsTrue(prodOnboard.Errors is null);
143 | return prodOnboard;
144 | }
145 |
146 | private static ComplianceCsrResponse GetComplianceCsr(GlobalVariables globalVariables, string otp, string csr)
147 | {
148 | var csrResponse = new ComplianceCsrApiClient(globalVariables).GetToken(new ComplianceCsrRequest(otp, csr));
149 | Console.WriteLine("--------------------------------Compliance Csr Request-----------------------------------");
150 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(csrResponse));
151 | Assert.IsNotNull(csrResponse);
152 | Assert.IsTrue(csrResponse.Errors is null);
153 | return csrResponse;
154 | }
155 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/DebitNote_E2ETest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.IntegrationTests;
2 |
3 | public static class DebitNote_E2ETest
4 | {
5 | private static readonly GlobalVariables GlobalVariables = new GlobalVariables()
6 | {
7 | BaseUrl = "https://gw-apic-gov.gazt.gov.sa",
8 | ComplianceCsidEndpoint = "/e-invoicing/developer-portal/compliance",
9 | ProdCsidEndpoint = "/e-invoicing/developer-portal/production/csids",
10 | InvoiceClearanceEndPoint = "/e-invoicing/developer-portal/invoices/clearance/single"
11 | };
12 |
13 | private const string Otp = "123456";
14 |
15 | private const string Csr =
16 | "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0NIE1JSUI3akNDQVpNQ0FRQXdZREVMTUFrR0ExVUVCaE1DVTBFeEZUQVRCZ05WQkFzTURGSnBlV0ZrSUVKeVlXNWoNIGFERXRNQ3NHQTFVRUNnd2tWVzVwZEdWa0lFZDFiR1lnUVdseVkzSmhablFnUm5WbGJHbHVaeUJEYjIxd1lXNTUNIE1Rc3dDUVlEVlFRRERBSlRRVEJXTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFLQTBJQUJQWHFkK1Q1ZkZhK1ZlYmcNIHlzL1d4bkJPNHBwOHNVeWtlM1BMSmVKYjZLbkk1Z0ZEcHVsWWY1K1NvUFJZVGFmK1o3Y0g1RzdJSmExdVNYSXENIHkwWVdoVytnZ2RNd2dkQUdDU3FHU0liM0RRRUpEakdCd2pDQnZ6QWhCZ2tyQmdFRUFZSTNGQUlFRkJNU1drRlUNIFEwRXRRMjlrWlMxVGFXZHVhVzVuTUlHWkJnTlZIUkVFZ1pFd2dZNmtnWXN3Z1lneEp6QWxCZ05WQkFRTUhqRXQNIFZXZGhabU52ZkRJdFJWTkhWVTVKVkh3ekxURXlNelExTmpjNE9URWZNQjBHQ2dtU0pvbVQ4aXhrQVFFTUR6TXgNIE1ERTNOVE01TnpRd01EQXdNekVOTUFzR0ExVUVEQXdFTVRFd01ERVNNQkFHQTFVRUdnd0pUWGxCWkdSeVpYTnoNIE1Sa3dGd1lEVlFRUERCQkdkV1ZzYVc1bklFbHVaSFZ6ZEhKNU1Bb0dDQ3FHU000OUJBTUNBMGtBTUVZQ0lRQ1ANIFlFVGJuazFSblFYTDdvSUxJek1qMFJGQWg3QmErUEFPenJIQnMyYzdLZ0loQUxDaUVKNnRjVzdGcHdyVEZ0RGMNIFA1QjVoRE5nYjFGUTJXSVZjMzc1VS9PdA0gLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t";
17 |
18 | public static void Run()
19 | {
20 | var csrResponse = GetComplianceCsr(GlobalVariables, Otp, Csr);
21 |
22 | var prodOnboard = GetProdCsid(GlobalVariables, csrResponse);
23 |
24 | ClearInvoice(GlobalVariables, prodOnboard);
25 |
26 | Console.WriteLine("-----------------------------------------------------------------------------------------");
27 | Console.WriteLine("----------------------------------Test Run Completed-------------------------------------");
28 | Console.WriteLine("-----------------------------------------------------------------------------------------");
29 | }
30 |
31 | private static void ClearInvoice(GlobalVariables globalVariables, ProdCsidOnboardingResponse prodOnboard)
32 | {
33 | var invoice = new StandardInvoiceAdjustment()
34 | {
35 | AdjustmentReason = "test adjustment",
36 | AdjsutmentInvoiceNumber = "11221222",
37 | Id = "100012",
38 | UUID = Guid.NewGuid().ToString(),
39 | ActualDeliveryDate = "2022-09-13",
40 | DocumentCurrencyCode = "SAR",
41 | TaxCurrencyCode = "SAR",
42 | InvoiceTypeCode = "388",
43 | IssuedDate = "2022-09-13",
44 | IssuedTime = "14:40:40",
45 | LatestDeliveryDate = "2022-09-13",
46 | PreviousInvoiceHash =
47 | "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
48 | SupplierInfo = new SupplierInfo()
49 | {
50 | CRN = "310175397400003",
51 | PartyPostalAddress = new PartyPostalAddress()
52 | {
53 | StreetName = "testA",
54 | BuildingNumber = "3454",
55 | CityName = "RiyadhA",
56 | CitySubdivisionName = "fgffA",
57 | Country = "SA",
58 | CountrySubentity = "test",
59 | PlotIdentification = "1234",
60 | PostalZone = "12345",
61 | },
62 | RegistrationName = "United Gulf Aircraft Fueling Company",
63 | VAT = "310175397400003"
64 | },
65 | CustomerInfo = new CustomerInfo()
66 | {
67 | NAT = "2345",
68 | PartyPostalAddress = new PartyPostalAddress()
69 | {
70 | BuildingNumber = "3353",
71 | CityName = "DhurmaA",
72 | CitySubdivisionName = "fgffA",
73 | Country = "SA",
74 | CountrySubentity = "ulhkA",
75 | PlotIdentification = "3434",
76 | PostalZone = "34341",
77 | StreetName = "baaounA",
78 | },
79 | RegistrationName = "testA",
80 | VAT = "300075588700003",
81 | },
82 | InvoiceLineItem = new InvoiceLineItem()
83 | {
84 | ID = "1",
85 | InvoicedQuantity = "100",
86 | ItemName = "item",
87 | LineExtensionAmount = new Money("SAR", 1000.00),
88 | PriceAmount = new Money("SAR", 10.00),
89 | RoundingAmount = new Money("SAR", 1150.00),
90 | TaxAmount = new Money("SAR", 150.00),
91 | TaxPercent = "15.00",
92 | TaxScheme = "VAT",
93 | TaxSchemeId = "S"
94 | },
95 | LegalMonetaryTotal = new LegalMonetaryTotal()
96 | {
97 | AllowanceTotalAmount = new Money("SAR", 0.00),
98 | LineExtensionAmount = new Money("SAR", 1000.00),
99 | PayableAmount = new Money("SAR", 1150.00),
100 | PrepaidAmount = new Money("SAR", 0.00),
101 | TaxExclusiveAmount = new Money("SAR", 1000.00),
102 | TaxInclusiveAmount = new Money("SAR", 1150.00),
103 | },
104 | TaxTotal = new TaxTotal()
105 | {
106 | Percent = "15.00",
107 | TaxableAmount = new Money("SAR", 1000.00),
108 | TaxAmount = new Money("SAR", 150.00),
109 | TaxCategory = "SA"
110 | }
111 | };
112 |
113 |
114 | var generatedClearanceRequest = StandardInvoiceXmlGenerator.GenerateForDebitNote(invoice);
115 | Console.WriteLine("----------------------Standard Invoice Generation Request---------------------------------");
116 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(generatedClearanceRequest));
117 | var clearanceResponse = new StandardInvoiceClearanceApiClient(globalVariables).ClearInvoice(
118 | new InvoiceClearanceRequest(generatedClearanceRequest, prodOnboard.BinarySecurityToken,
119 | prodOnboard.Secret));
120 | if (clearanceResponse.ClearanceStatus == "NOT_CLEARED")
121 | {
122 | Console.WriteLine("----------------------Standard Invoice Clearance Request - Failure reason---------------------------------");
123 | Console.WriteLine(clearanceResponse.validationResults);
124 | }
125 | else
126 | {
127 | Console.WriteLine("----------------------Standard Invoice Clearance Request---------------------------------");
128 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(clearanceResponse));
129 | }
130 | Assert.IsNotNull(clearanceResponse);
131 | Assert.IsNotEmpty(clearanceResponse.GeneratedQR);
132 | }
133 |
134 | private static ProdCsidOnboardingResponse GetProdCsid(GlobalVariables globalVariables,
135 | ComplianceCsrResponse csrResponse)
136 | {
137 | var prodOnboard = new ProdCsidApiClient(globalVariables).GetToken(
138 | new ProdCsidOnboardingRequest(csrResponse.RequestId, csrResponse.BinarySecurityToken, csrResponse.Secret));
139 | Console.WriteLine("--------------------------------Prod Csid Request-----------------------------------");
140 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(prodOnboard));
141 | Assert.IsNotNull(prodOnboard);
142 | Assert.IsTrue(prodOnboard.Errors is null);
143 | return prodOnboard;
144 | }
145 |
146 | private static ComplianceCsrResponse GetComplianceCsr(GlobalVariables globalVariables, string otp, string csr)
147 | {
148 | var csrResponse = new ComplianceCsrApiClient(globalVariables).GetToken(new ComplianceCsrRequest(otp, csr));
149 | Console.WriteLine("--------------------------------Compliance Csr Request-----------------------------------");
150 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(csrResponse));
151 | Assert.IsNotNull(csrResponse);
152 | Assert.IsTrue(csrResponse.Errors is null);
153 | return csrResponse;
154 | }
155 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using Bee.ZatcaHelper.IntegrationTests;
4 |
5 | StandardInvoice_E2ETest.Run();
6 | StandardInvoiceZeroTaxE2ETest.Run();
7 | DebitNote_E2ETest.Run();
8 | CreditNote_E2ETest.Run();
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/StandardInvoice_E2ETest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.IntegrationTests;
2 |
3 | public static class StandardInvoice_E2ETest
4 | {
5 | private static readonly GlobalVariables GlobalVariables = new GlobalVariables()
6 | {
7 | BaseUrl = "https://gw-apic-gov.gazt.gov.sa",
8 | ComplianceCsidEndpoint = "/e-invoicing/developer-portal/compliance",
9 | ProdCsidEndpoint = "/e-invoicing/developer-portal/production/csids",
10 | InvoiceClearanceEndPoint = "/e-invoicing/developer-portal/invoices/clearance/single"
11 | };
12 |
13 | private const string Otp = "123456";
14 |
15 | private const string Csr =
16 | "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0NIE1JSUI3akNDQVpNQ0FRQXdZREVMTUFrR0ExVUVCaE1DVTBFeEZUQVRCZ05WQkFzTURGSnBlV0ZrSUVKeVlXNWoNIGFERXRNQ3NHQTFVRUNnd2tWVzVwZEdWa0lFZDFiR1lnUVdseVkzSmhablFnUm5WbGJHbHVaeUJEYjIxd1lXNTUNIE1Rc3dDUVlEVlFRRERBSlRRVEJXTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFLQTBJQUJQWHFkK1Q1ZkZhK1ZlYmcNIHlzL1d4bkJPNHBwOHNVeWtlM1BMSmVKYjZLbkk1Z0ZEcHVsWWY1K1NvUFJZVGFmK1o3Y0g1RzdJSmExdVNYSXENIHkwWVdoVytnZ2RNd2dkQUdDU3FHU0liM0RRRUpEakdCd2pDQnZ6QWhCZ2tyQmdFRUFZSTNGQUlFRkJNU1drRlUNIFEwRXRRMjlrWlMxVGFXZHVhVzVuTUlHWkJnTlZIUkVFZ1pFd2dZNmtnWXN3Z1lneEp6QWxCZ05WQkFRTUhqRXQNIFZXZGhabU52ZkRJdFJWTkhWVTVKVkh3ekxURXlNelExTmpjNE9URWZNQjBHQ2dtU0pvbVQ4aXhrQVFFTUR6TXgNIE1ERTNOVE01TnpRd01EQXdNekVOTUFzR0ExVUVEQXdFTVRFd01ERVNNQkFHQTFVRUdnd0pUWGxCWkdSeVpYTnoNIE1Sa3dGd1lEVlFRUERCQkdkV1ZzYVc1bklFbHVaSFZ6ZEhKNU1Bb0dDQ3FHU000OUJBTUNBMGtBTUVZQ0lRQ1ANIFlFVGJuazFSblFYTDdvSUxJek1qMFJGQWg3QmErUEFPenJIQnMyYzdLZ0loQUxDaUVKNnRjVzdGcHdyVEZ0RGMNIFA1QjVoRE5nYjFGUTJXSVZjMzc1VS9PdA0gLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t";
17 |
18 | public static void Run()
19 | {
20 | var csrResponse = GetComplianceCsr(GlobalVariables, Otp, Csr);
21 |
22 | var prodOnboard = GetProdCsid(GlobalVariables, csrResponse);
23 |
24 | ClearInvoice(GlobalVariables, prodOnboard);
25 |
26 | Console.WriteLine("-----------------------------------------------------------------------------------------");
27 | Console.WriteLine("----------------------------------Test Run Completed-------------------------------------");
28 | Console.WriteLine("-----------------------------------------------------------------------------------------");
29 | }
30 |
31 | private static void ClearInvoice(GlobalVariables globalVariables, ProdCsidOnboardingResponse prodOnboard)
32 | {
33 | var invoice = new StandardInvoice()
34 | {
35 | Id = "100012",
36 | UUID = Guid.NewGuid().ToString(),
37 | ActualDeliveryDate = "2022-09-13",
38 | DocumentCurrencyCode = "SAR",
39 | TaxCurrencyCode = "SAR",
40 | InvoiceTypeCode = "388",
41 | IssuedDate = "2022-09-13",
42 | IssuedTime = "14:40:40",
43 | LatestDeliveryDate = "2022-09-13",
44 | PreviousInvoiceHash =
45 | "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
46 | SupplierInfo = new SupplierInfo()
47 | {
48 | CRN = "310175397400003",
49 | PartyPostalAddress = new PartyPostalAddress()
50 | {
51 | StreetName = "testA",
52 | BuildingNumber = "3454",
53 | CityName = "RiyadhA",
54 | CitySubdivisionName = "fgffA",
55 | Country = "SA",
56 | CountrySubentity = "test",
57 | PlotIdentification = "1234",
58 | PostalZone = "12345",
59 | },
60 | RegistrationName = "United Gulf Aircraft Fueling Company",
61 | VAT = "310175397400003"
62 | },
63 | CustomerInfo = new CustomerInfo()
64 | {
65 | NAT = "2345",
66 | PartyPostalAddress = new PartyPostalAddress()
67 | {
68 | BuildingNumber = "3353",
69 | CityName = "DhurmaA",
70 | CitySubdivisionName = "fgffA",
71 | Country = "SA",
72 | CountrySubentity = "ulhkA",
73 | PlotIdentification = "3434",
74 | PostalZone = "34341",
75 | StreetName = "baaounA",
76 | },
77 | RegistrationName = "testA",
78 | VAT = "300075588700003",
79 | },
80 | InvoiceLineItem = new InvoiceLineItem()
81 | {
82 | ID = "1",
83 | InvoicedQuantity = "100",
84 | ItemName = "item",
85 | LineExtensionAmount = new Money("SAR", 1000.00),
86 | PriceAmount = new Money("SAR", 10.00),
87 | RoundingAmount = new Money("SAR", 1150.00),
88 | TaxAmount = new Money("SAR", 150.00),
89 | TaxPercent = "15.00",
90 | TaxScheme = "VAT",
91 | TaxSchemeId = "S"
92 | },
93 | LegalMonetaryTotal = new LegalMonetaryTotal()
94 | {
95 | AllowanceTotalAmount = new Money("SAR", 0.00),
96 | LineExtensionAmount = new Money("SAR", 1000.00),
97 | PayableAmount = new Money("SAR", 1150.00),
98 | PrepaidAmount = new Money("SAR", 0.00),
99 | TaxExclusiveAmount = new Money("SAR", 1000.00),
100 | TaxInclusiveAmount = new Money("SAR", 1150.00),
101 | },
102 | TaxTotal = new TaxTotal()
103 | {
104 | Percent = "15.00",
105 | TaxableAmount = new Money("SAR", 1000.00),
106 | TaxAmount = new Money("SAR", 150.00),
107 | TaxCategory = "SA"
108 | }
109 | };
110 |
111 |
112 | var generatedClearanceRequest = StandardInvoiceXmlGenerator.Generate(invoice);
113 | Console.WriteLine("----------------------Standard Invoice Generation Request---------------------------------");
114 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(generatedClearanceRequest));
115 | var clearanceResponse = new StandardInvoiceClearanceApiClient(globalVariables).ClearInvoice(
116 | new InvoiceClearanceRequest(generatedClearanceRequest, prodOnboard.BinarySecurityToken,
117 | prodOnboard.Secret));
118 | if (clearanceResponse.ClearanceStatus == "NOT_CLEARED")
119 | {
120 | Console.WriteLine("----------------------Standard Invoice Clearance Request - Failure reason---------------------------------");
121 | Console.WriteLine(clearanceResponse.validationResults);
122 | }
123 | else
124 | {
125 | Console.WriteLine("----------------------Standard Invoice Clearance Request---------------------------------");
126 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(clearanceResponse));
127 | }
128 | Assert.IsNotNull(clearanceResponse);
129 | Assert.IsNotEmpty(clearanceResponse.GeneratedQR);
130 | }
131 |
132 | private static ProdCsidOnboardingResponse GetProdCsid(GlobalVariables globalVariables,
133 | ComplianceCsrResponse csrResponse)
134 | {
135 | var prodOnboard = new ProdCsidApiClient(globalVariables).GetToken(
136 | new ProdCsidOnboardingRequest(csrResponse.RequestId, csrResponse.BinarySecurityToken, csrResponse.Secret));
137 | Console.WriteLine("--------------------------------Prod Csid Request-----------------------------------");
138 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(prodOnboard));
139 | Assert.IsNotNull(prodOnboard);
140 | Assert.IsTrue(prodOnboard.Errors is null);
141 | return prodOnboard;
142 | }
143 |
144 | private static ComplianceCsrResponse GetComplianceCsr(GlobalVariables globalVariables, string otp, string csr)
145 | {
146 | var csrResponse = new ComplianceCsrApiClient(globalVariables).GetToken(new ComplianceCsrRequest(otp, csr));
147 | Console.WriteLine("--------------------------------Compliance Csr Request-----------------------------------");
148 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(csrResponse));
149 | Assert.IsNotNull(csrResponse);
150 | Assert.IsTrue(csrResponse.Errors is null);
151 | return csrResponse;
152 | }
153 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/StandardInvoice_ZeroTax_E2ETest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.IntegrationTests;
2 |
3 | public static class StandardInvoiceZeroTaxE2ETest
4 | {
5 | private static readonly GlobalVariables GlobalVariables = new GlobalVariables()
6 | {
7 | BaseUrl = "https://gw-apic-gov.gazt.gov.sa",
8 | ComplianceCsidEndpoint = "/e-invoicing/developer-portal/compliance",
9 | ProdCsidEndpoint = "/e-invoicing/developer-portal/production/csids",
10 | InvoiceClearanceEndPoint = "/e-invoicing/developer-portal/invoices/clearance/single"
11 | };
12 |
13 | private const string Otp = "123456";
14 |
15 | private const string Csr =
16 | "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0NIE1JSUI3akNDQVpNQ0FRQXdZREVMTUFrR0ExVUVCaE1DVTBFeEZUQVRCZ05WQkFzTURGSnBlV0ZrSUVKeVlXNWoNIGFERXRNQ3NHQTFVRUNnd2tWVzVwZEdWa0lFZDFiR1lnUVdseVkzSmhablFnUm5WbGJHbHVaeUJEYjIxd1lXNTUNIE1Rc3dDUVlEVlFRRERBSlRRVEJXTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFLQTBJQUJQWHFkK1Q1ZkZhK1ZlYmcNIHlzL1d4bkJPNHBwOHNVeWtlM1BMSmVKYjZLbkk1Z0ZEcHVsWWY1K1NvUFJZVGFmK1o3Y0g1RzdJSmExdVNYSXENIHkwWVdoVytnZ2RNd2dkQUdDU3FHU0liM0RRRUpEakdCd2pDQnZ6QWhCZ2tyQmdFRUFZSTNGQUlFRkJNU1drRlUNIFEwRXRRMjlrWlMxVGFXZHVhVzVuTUlHWkJnTlZIUkVFZ1pFd2dZNmtnWXN3Z1lneEp6QWxCZ05WQkFRTUhqRXQNIFZXZGhabU52ZkRJdFJWTkhWVTVKVkh3ekxURXlNelExTmpjNE9URWZNQjBHQ2dtU0pvbVQ4aXhrQVFFTUR6TXgNIE1ERTNOVE01TnpRd01EQXdNekVOTUFzR0ExVUVEQXdFTVRFd01ERVNNQkFHQTFVRUdnd0pUWGxCWkdSeVpYTnoNIE1Sa3dGd1lEVlFRUERCQkdkV1ZzYVc1bklFbHVaSFZ6ZEhKNU1Bb0dDQ3FHU000OUJBTUNBMGtBTUVZQ0lRQ1ANIFlFVGJuazFSblFYTDdvSUxJek1qMFJGQWg3QmErUEFPenJIQnMyYzdLZ0loQUxDaUVKNnRjVzdGcHdyVEZ0RGMNIFA1QjVoRE5nYjFGUTJXSVZjMzc1VS9PdA0gLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t";
17 |
18 | public static void Run()
19 | {
20 | var csrResponse = GetComplianceCsr(GlobalVariables, Otp, Csr);
21 |
22 | var prodOnboard = GetProdCsid(GlobalVariables, csrResponse);
23 |
24 | ClearInvoice(GlobalVariables, prodOnboard);
25 |
26 | Console.WriteLine("-----------------------------------------------------------------------------------------");
27 | Console.WriteLine("----------------------------------Test Run Completed-------------------------------------");
28 | Console.WriteLine("-----------------------------------------------------------------------------------------");
29 | }
30 |
31 | private static void ClearInvoice(GlobalVariables globalVariables, ProdCsidOnboardingResponse prodOnboard)
32 | {
33 | var invoice = new StandardInvoice()
34 | {
35 | Id = "100012",
36 | UUID = Guid.NewGuid().ToString(),
37 | ActualDeliveryDate = "2022-09-13",
38 | DocumentCurrencyCode = "SAR",
39 | TaxCurrencyCode = "SAR",
40 | InvoiceTypeCode = "388",
41 | IssuedDate = "2022-09-13",
42 | IssuedTime = "14:40:40",
43 | LatestDeliveryDate = "2022-09-13",
44 | PreviousInvoiceHash =
45 | "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
46 | SupplierInfo = new SupplierInfo()
47 | {
48 | CRN = "310175397400003",
49 | PartyPostalAddress = new PartyPostalAddress()
50 | {
51 | StreetName = "testA",
52 | BuildingNumber = "3454",
53 | CityName = "RiyadhA",
54 | CitySubdivisionName = "fgffA",
55 | Country = "SA",
56 | CountrySubentity = "test",
57 | PlotIdentification = "1234",
58 | PostalZone = "12345",
59 | },
60 | RegistrationName = "United Gulf Aircraft Fueling Company",
61 | VAT = "310175397400003"
62 | },
63 | CustomerInfo = new CustomerInfo()
64 | {
65 | NAT = "2345",
66 | PartyPostalAddress = new PartyPostalAddress()
67 | {
68 | BuildingNumber = "3353",
69 | CityName = "DhurmaA",
70 | CitySubdivisionName = "fgffA",
71 | Country = "SA",
72 | CountrySubentity = "ulhkA",
73 | PlotIdentification = "3434",
74 | PostalZone = "34341",
75 | StreetName = "baaounA",
76 | },
77 | RegistrationName = "testA",
78 | VAT = "300075588700003",
79 | },
80 | InvoiceLineItem = new InvoiceLineItem()
81 | {
82 | ID = "1",
83 | InvoicedQuantity = "100",
84 | ItemName = "item",
85 | LineExtensionAmount = new Money("SAR", 1000.00),
86 | PriceAmount = new Money("SAR", 10.00),
87 | RoundingAmount = new Money("SAR", 1000.00),
88 | TaxAmount = new Money("SAR", 0.00),
89 | TaxPercent = "0.00",
90 | TaxScheme = "VAT",
91 | TaxSchemeId = "S"
92 | },
93 | LegalMonetaryTotal = new LegalMonetaryTotal()
94 | {
95 | AllowanceTotalAmount = new Money("SAR", 0.00),
96 | LineExtensionAmount = new Money("SAR", 1000.00),
97 | PayableAmount = new Money("SAR", 1000.00),
98 | PrepaidAmount = new Money("SAR", 0.00),
99 | TaxExclusiveAmount = new Money("SAR", 1000.00),
100 | TaxInclusiveAmount = new Money("SAR", 1000.00),
101 | },
102 | TaxTotal = new TaxTotal()
103 | {
104 | Percent = "0.00",
105 | TaxableAmount = new Money("SAR", 1000.00),
106 | TaxAmount = new Money("SAR", 0.00),
107 | TaxCategory = "SA"
108 | }
109 | };
110 |
111 |
112 | var generatedClearanceRequest = StandardInvoiceXmlGenerator.Generate(invoice);
113 | Console.WriteLine("----------------------Standard Invoice Generation Request---------------------------------");
114 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(generatedClearanceRequest));
115 | var clearanceResponse = new StandardInvoiceClearanceApiClient(globalVariables).ClearInvoice(
116 | new InvoiceClearanceRequest(generatedClearanceRequest, prodOnboard.BinarySecurityToken,
117 | prodOnboard.Secret));
118 | if (clearanceResponse.ClearanceStatus == "NOT_CLEARED")
119 | {
120 | Console.WriteLine("----------------------Standard Invoice Clearance Request - Failure reason---------------------------------");
121 | Console.WriteLine(clearanceResponse.validationResults);
122 | }
123 | else
124 | {
125 | Console.WriteLine("----------------------Standard Invoice Clearance Request---------------------------------");
126 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(clearanceResponse));
127 | }
128 | Assert.IsNotNull(clearanceResponse);
129 | Assert.IsNotEmpty(clearanceResponse.GeneratedQR);
130 | }
131 |
132 | private static ProdCsidOnboardingResponse GetProdCsid(GlobalVariables globalVariables,
133 | ComplianceCsrResponse csrResponse)
134 | {
135 | var prodOnboard = new ProdCsidApiClient(globalVariables).GetToken(
136 | new ProdCsidOnboardingRequest(csrResponse.RequestId, csrResponse.BinarySecurityToken, csrResponse.Secret));
137 | Console.WriteLine("--------------------------------Prod Csid Request-----------------------------------");
138 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(prodOnboard));
139 | Assert.IsNotNull(prodOnboard);
140 | Assert.IsTrue(prodOnboard.Errors is null);
141 | return prodOnboard;
142 | }
143 |
144 | private static ComplianceCsrResponse GetComplianceCsr(GlobalVariables globalVariables, string otp, string csr)
145 | {
146 | var csrResponse = new ComplianceCsrApiClient(globalVariables).GetToken(new ComplianceCsrRequest(otp, csr));
147 | Console.WriteLine("--------------------------------Compliance Csr Request-----------------------------------");
148 | Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(csrResponse));
149 | Assert.IsNotNull(csrResponse);
150 | Assert.IsTrue(csrResponse.Errors is null);
151 | return csrResponse;
152 | }
153 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.IntegrationTests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using NUnit.Framework;
2 | global using Bee.ZatcaHelper.Contracts;
3 | global using Bee.ZatcaHelper.Model;
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/Bee.ZatcaHelper.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 | false
9 |
10 | Bee.ZatcaHelper.UnitTests
11 |
12 | Bee.ZatcaHelper.UnitTests
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/ComplianceCsrApiClientTests.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.UnitTests;
2 |
3 | public class ComplianceCsrApiClientTests
4 | {
5 | [Test]
6 | public void ShouldGetTokenFromAfterComplianceCheck()
7 | {
8 | var globalVariables = new GlobalVariables()
9 | {
10 | BaseUrl = "https://www.google.com/",
11 | ComplianceCsidEndpoint = "/endpoint"
12 | };
13 |
14 | var complianceCsrApiClient = new ComplianceCsrApiClient(globalVariables);
15 | var complianceCsrRequest = new ComplianceCsrRequest("1234", "csr");
16 | var response = complianceCsrApiClient.GetToken(complianceCsrRequest);
17 |
18 | Assert.Pass();
19 | }
20 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/ProdCsidApiClientTests.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.UnitTests;
2 |
3 | public class ProdCsidApiClientTests
4 | {
5 | [Test]
6 | public void ShouldGetProdCsidTokenWithComplianceCheckBinaryToken()
7 | {
8 | var globalVariables = new GlobalVariables()
9 | {
10 | BaseUrl = "sample base",
11 | ProdCsidEndpoint = "end point"
12 | };
13 |
14 | var prodCsidApiClient = new ProdCsidApiClient(globalVariables);
15 | var prodCsidRequest = new ProdCsidOnboardingRequest(1234, "binary token", "secret");
16 | var response = prodCsidApiClient.GetToken(prodCsidRequest);
17 |
18 | Assert.Pass();
19 | }
20 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/StandardInvoiceXmlGeneratorTests.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.UnitTests;
2 |
3 | public class StandardInvoiceXmlGeneratorTests
4 | {
5 | [Test]
6 | public void ShouldBeAbleToGenerateXml()
7 | {
8 | var invoice = new StandardInvoice()
9 | {
10 | Id = "100012",
11 | UUID = Guid.NewGuid().ToString(),
12 | ActualDeliveryDate = "2022-09-13",
13 | DocumentCurrencyCode = "SAR",
14 | TaxCurrencyCode = "SAR",
15 | InvoiceTypeCode = "388",
16 | IssuedDate = "2022-09-13",
17 | IssuedTime = "14:40:40",
18 | LatestDeliveryDate = "2022-09-13",
19 | PreviousInvoiceHash =
20 | "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
21 | SupplierInfo = new SupplierInfo()
22 | {
23 | CRN = "310175397400003",
24 | PartyPostalAddress = new PartyPostalAddress()
25 | {
26 | StreetName = "testA",
27 | BuildingNumber = "3454",
28 | CityName = "RiyadhA",
29 | CitySubdivisionName = "fgffA",
30 | Country = "SA",
31 | CountrySubentity = "test",
32 | PlotIdentification = "1234",
33 | PostalZone = "12345",
34 | },
35 | RegistrationName = "United Gulf Aircraft Fueling Company",
36 | VAT = "310175397400003"
37 | },
38 | CustomerInfo = new CustomerInfo()
39 | {
40 | NAT = "2345",
41 | PartyPostalAddress = new PartyPostalAddress()
42 | {
43 | BuildingNumber = "3353A",
44 | CityName = "DhurmaA",
45 | CitySubdivisionName = "fgffA",
46 | Country = "SA",
47 | CountrySubentity = "ulhkA",
48 | PlotIdentification = "3434",
49 | PostalZone = "34341",
50 | StreetName = "baaounA",
51 | },
52 | RegistrationName = "testA",
53 | VAT = "300075588700003",
54 | },
55 | InvoiceLineItem = new InvoiceLineItem()
56 | {
57 | ID = "1",
58 | InvoicedQuantity = "100",
59 | ItemName = "item",
60 | LineExtensionAmount = new Money("SAR", 1000.00),
61 | PriceAmount = new Money("SAR", 10.00),
62 | RoundingAmount = new Money("SAR", 1150.00),
63 | TaxAmount = new Money("SAR", 150.00),
64 | TaxPercent = "15.00",
65 | TaxScheme = "VAT",
66 | TaxSchemeId = "S"
67 | },
68 | LegalMonetaryTotal = new LegalMonetaryTotal()
69 | {
70 | AllowanceTotalAmount = new Money("SAR", 0.00),
71 | LineExtensionAmount = new Money("SAR", 1000.00),
72 | PayableAmount = new Money("SAR", 1150.00),
73 | PrepaidAmount = new Money("SAR", 0.00),
74 | TaxExclusiveAmount = new Money("SAR", 1000.00),
75 | TaxInclusiveAmount = new Money("SAR", 1150.00),
76 | },
77 | TaxTotal = new TaxTotal()
78 | {
79 | Percent = "15.00",
80 | TaxableAmount = new Money("SAR", 1000.00),
81 | TaxAmount = new Money("SAR", 150.00),
82 | TaxCategory = "SA"
83 | }
84 | };
85 |
86 | var request = StandardInvoiceXmlGenerator.Generate(invoice);
87 |
88 | var actualUuid = request?.GetType().GetProperty("uuid")?.GetValue(request, null);
89 | var actualInvoiceHash = request?.GetType().GetProperty("invoiceHash")?.GetValue(request, null);
90 | var actualInvoice = request?.GetType().GetProperty("invoice")?.GetValue(request, null);
91 | Assert.Multiple(() =>
92 | {
93 | Assert.That(actualInvoiceHash, Is.Not.Null);
94 | Assert.That(actualInvoice, Is.Not.Null);
95 | });
96 | Assert.That(actualUuid, Is.EqualTo(invoice.UUID));
97 | Assert.Pass();
98 | }
99 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using NUnit.Framework;
2 | global using Bee.ZatcaHelper.Contracts;
3 | global using Bee.ZatcaHelper.Model;
--------------------------------------------------------------------------------
/Bee.ZatcaHelper.UnitTests/UtilTests/InvoiceHashHelperTests.cs:
--------------------------------------------------------------------------------
1 | using Bee.ZatcaHelper.Util;
2 |
3 | namespace Bee.ZatcaHelper.UnitTests.UtilTests;
4 |
5 | public class InvoiceHashHelperTests
6 | {
7 | [SetUp]
8 | public void Setup()
9 | {
10 | }
11 |
12 | [Test]
13 | public void ShouldGenerateValidHashForProvidedInvoice()
14 | {
15 | const string expectedHash = "UVySRXxjE3r+aWN93hfnhc2w15aoaZO0v9pQtOKpupA=";
16 | var xmlString = typeof(StandardInvoiceXmlGenerator).GetFileContent("StandardInvoiceTemplate.xml");
17 | var actualHash = InvoiceHashHelper.GenerateEInvoiceHashing(xmlString);
18 | Assert.That(actualHash, Is.EqualTo(expectedHash));
19 | Assert.Pass();
20 | }
21 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Bee.ZatcaHelper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net462;net6.0;
5 | Bee.ZatcaHelper
6 | 10.0
7 | enable
8 | enable
9 | Bee.ZatcaHelper
10 | Bee.ZatcaHelper
11 | 1.0.6
12 | DevBee
13 | This nuget helps in integertation of systems to Zatca systems.
14 | MIT
15 | https://github.com/fasilmarshooq/Bee.ZatcaHelper
16 | ../Logo/Devbee-Logo.png
17 | https://github.com/fasilmarshooq/Bee.ZatcaHelper
18 | Zatca, ZatcaHelper, ZatcaIntegration, Fatoora, Tax
19 | Bee.ZatcaHelper
20 | Bee.ZatcaHelper
21 | True
22 | MIT
23 |
24 |
25 |
26 |
27 | True
28 | \
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | PreserveNewest
46 |
47 |
48 | PreserveNewest
49 |
50 |
51 |
52 | PreserveNewest
53 |
54 |
55 |
56 | PreserveNewest
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/ComplianceCsrAPIClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using Bee.ZatcaHelper.Contracts;
3 | using Bee.ZatcaHelper.Util;
4 | using Serilog;
5 |
6 | namespace Bee.ZatcaHelper;
7 |
8 | public class ComplianceCsrApiClient
9 | {
10 | private readonly string _baseUrl;
11 | private readonly string _complianceEndPoint;
12 |
13 | public ComplianceCsrApiClient(GlobalVariables globalVariables)
14 | {
15 | _baseUrl = globalVariables.BaseUrl;
16 | _complianceEndPoint = globalVariables.ComplianceCsidEndpoint;
17 | if (!string.IsNullOrEmpty(_baseUrl) && !string.IsNullOrEmpty(_complianceEndPoint)) return;
18 | Log.Error("Base Url or Compliance Csid Endpoint cant be empty");
19 | throw new Exception("Base Url or Compliance Csid Endpoint cant be empty");
20 | }
21 |
22 | public ComplianceCsrResponse? GetToken(ComplianceCsrRequest complianceCsrRequest)
23 | {
24 | var customHeaders = new Dictionary {{"OTP", complianceCsrRequest.Otp}};
25 | var result = new WebClient(_baseUrl, customHeaders).PostAsJsonAsync(
26 | _complianceEndPoint, new
27 | {
28 | csr = complianceCsrRequest.Csr
29 | });
30 | return result.Result.Content.ReadFromJsonAsync().Result;
31 | }
32 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/ComplianceCsrRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class ComplianceCsrRequest
4 | {
5 | public ComplianceCsrRequest(string otp, string csr)
6 | {
7 | Otp = otp;
8 | Csr = csr;
9 | }
10 |
11 | public string Otp { get; init; }
12 | public string Csr { get; init; }
13 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/ComplianceCsrResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class ComplianceCsrResponse
4 | {
5 | public long RequestId { get; set; }
6 | public string? DispositionMessage { get; set; }
7 | public string? BinarySecurityToken { get; set; }
8 | public string? Secret { get; set; }
9 | public string[]? Errors { get; set; }
10 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/InvoiceClearanceRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class InvoiceClearanceRequest
4 | {
5 | public InvoiceClearanceRequest(object body, string binaryToken, string secret)
6 | {
7 | Body = body;
8 | BinaryToken = binaryToken;
9 | Secret = secret;
10 | }
11 |
12 | public object Body { get; }
13 | public string BinaryToken { get; }
14 | public string Secret { get; }
15 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/InvoiceClearanceResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class InvoiceClearanceResponse
4 | {
5 | public string ClearanceStatus { get; set; }
6 | public string ClearedInvoice { get; set; }
7 | public object validationResults { get; set; }
8 | public string? Hash { get; set; }
9 | public string? GeneratedQR { get; set; }
10 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/ProdCsidOnboardingRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class ProdCsidOnboardingRequest
4 | {
5 | public ProdCsidOnboardingRequest(long complianceRequestId, string csrBinaryToken, string csrSecret)
6 | {
7 | ComplianceRequestId = complianceRequestId;
8 | CsrBinaryToken = csrBinaryToken;
9 | CsrSecret = csrSecret;
10 | }
11 |
12 | public long ComplianceRequestId { get; }
13 | public string CsrBinaryToken { get; }
14 | public string CsrSecret { get; }
15 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Contracts/ProdCsidOnboardingResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Contracts;
2 |
3 | public class ProdCsidOnboardingResponse
4 | {
5 | public long RequestId { get; set; }
6 | public string? DispositionMessage { get; set; }
7 | public string? BinarySecurityToken { get; set; }
8 | public string? Secret { get; set; }
9 | public string[]? Errors { get; set; }
10 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/CreditNote.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
10 |
11 |
12 |
16 |
17 | urn:oasis:names:specification:ubl:signature:1
18 | urn:oasis:names:specification:ubl:signature:Invoice
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | not(//ancestor-or-self::ext:UBLExtensions)
29 |
30 |
31 | not(//ancestor-or-self::cac:Signature)
32 |
33 |
34 |
35 | not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])
36 |
37 |
38 |
39 |
40 |
41 | BK6cj9p18ffIJHe/pWkkIT0myGMrFXYiczWq3se+nm8=
42 |
43 |
45 |
46 |
47 | ZDZjMzQyZTBjNGVjY2VlODgxYTBiMzhiNDFlM2QxZTlkZDkyMDQ1NTEwNzUwN2RhYjUwNGNmZmNmNTFmY2I2OA==
48 |
49 |
50 |
51 |
52 | MEYCIQCYh5e3irIqWl5YtaCnKiujMITmokXYOPMpIDCIoWW79wIhAMXmc+MFHGtaeBseOuEGTlwD6g09gNQPRPuY89s65OWS
53 |
54 |
55 |
56 |
57 | MIID1DCCA3mgAwIBAgITbwAAe3UAYVU34I/+5QABAAB7dTAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMDYxMjE3NDA1MloXDTI0MDYxMTE3NDA1MlowSTELMAkGA1UEBhMCU0ExDjAMBgNVBAoTBWFnaWxlMRYwFAYDVQQLEw1oYXlhIHlhZ2htb3VyMRIwEAYDVQQDEwkxMjcuMC4wLjEwVjAQBgcqhkjOPQIBBgUrgQQACgNCAATTAK9lrTVko9rkq6ZYcc9HDRZP4b9S4zA4Km7YXJ+snTVhLkzU0HsmSX9Un8jDhRTOHDKaft8C/uuUY934vuMNo4ICJzCCAiMwgYgGA1UdEQSBgDB+pHwwejEbMBkGA1UEBAwSMS1oYXlhfDItMjM0fDMtMTEyMR8wHQYKCZImiZPyLGQBAQwPMzAwMDc1NTg4NzAwMDAzMQ0wCwYDVQQMDAQxMTAwMREwDwYDVQQaDAhaYXRjYSAxMjEYMBYGA1UEDwwPRm9vZCBCdXNzaW5lc3MzMB0GA1UdDgQWBBSgmIWD6bPfbbKkmTwOJRXvIbH9HjAfBgNVHSMEGDAWgBR2YIz7BqCsZ1c1nc+arKcrmTW1LzBOBgNVHR8ERzBFMEOgQaA/hj1odHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVJTlZPSUNFLVN1YkNBLTEuY3JsMIGtBggrBgEFBQcBAQSBoDCBnTBuBggrBgEFBQcwAYZiaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2EvQ2VydEVucm9sbC9UU1pFaW52b2ljZVNDQTEuZXh0Z2F6dC5nb3YubG9jYWxfVFNaRUlOVk9JQ0UtU3ViQ0EtMSgxKS5jcnQwKwYIKwYBBQUHMAGGH2h0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL29jc3AwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzAnBgkrBgEEAYI3FQoEGjAYMAoGCCsGAQUFBwMCMAoGCCsGAQUFBwMDMAoGCCqGSM49BAMCA0kAMEYCIQCVwDMcq6PO+McmsBXUz/v1GdhGp7rqSa2AxTKSv838IAIhAOBNDBt9+3DSlijoVfxzrdDh528WC37smEdoGWVrSpG1
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 | 2022-06-13T11:06:14Z
67 |
68 |
69 |
70 |
72 |
73 | NDliYWY0YmVmNzAyZmE4OWIwZDMzYWIzZjFkOTY5YzFhZjRhZDE4ZTFkODlkNDY2Y2UwYzI2N2QyNDg0MWVlYQ==
74 |
75 |
76 |
77 | CN=TSZEINVOICE-SubCA-1, DC=extgazt, DC=gov,
78 | DC=local
79 |
80 |
81 | 2475382881139449094209233032847838632732556149
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | reporting:1.0
97 | SME00062
98 | 6f4d20e0-6bfe-4a80-9389-7dabe6620f12
99 | 2022-03-13
100 | 14:40:40
101 | 381
102 | SAR
103 | SAR
104 |
105 |
106 | TOSL108
107 |
108 |
109 |
110 | ICV
111 | 62
112 |
113 |
114 | PIH
115 |
116 |
117 | NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==
118 |
119 |
120 |
121 |
122 | urn:oasis:names:specification:ubl:signature:Invoice
123 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
124 |
125 |
126 |
127 |
128 | 310175397400003
129 |
130 |
131 | test
132 | 3454
133 | 1234
134 | fgff
135 | Riyadh
136 | 12345
137 | test
138 |
139 | SA
140 |
141 |
142 |
143 | 310175397400003
144 |
145 | VAT
146 |
147 |
148 |
149 | United Gulf Aircraft Fueling Company
150 |
151 |
152 |
153 |
154 |
155 |
156 | 2345
157 |
158 |
159 | baaoun
160 | sdsd
161 | 3353
162 | 3434
163 | fgff
164 | Dhurma
165 | 34534
166 | ulhk
167 |
168 | SA
169 |
170 |
171 |
172 |
173 | VAT
174 |
175 |
176 |
177 | sdsa
178 |
179 |
180 |
181 |
182 | 2022-03-13
183 | 2022-03-13
184 |
185 |
186 |
187 | 42
188 | string
189 |
190 |
191 |
192 | 1
193 | false
194 | discount
195 | 2
196 |
197 | S
198 | 15
199 |
200 | VAT
201 |
202 |
203 |
204 |
205 | 144.9
206 |
207 | 966.00
208 | 144.90
209 |
210 | S
211 | 15.00
212 |
213 | VAT
214 |
215 |
216 |
217 |
218 |
219 | 151.00
220 |
221 |
222 | 966.00
223 | 964.00
224 | 1108.90
225 | 2.00
226 | 0.00
227 | 1108.90
228 |
229 |
230 | 1
231 | 44.000000
232 | 966.00
233 |
234 | 144.90
235 | 1110.90
236 |
237 |
238 | dsd
239 |
240 | S
241 | 15.00
242 |
243 | VAT
244 |
245 |
246 |
247 |
248 | 22.00
249 |
250 | false
251 | discount
252 | 2.00
253 |
254 |
255 |
256 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/DebitNote.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
10 |
11 |
12 |
16 |
17 | urn:oasis:names:specification:ubl:signature:1
18 | urn:oasis:names:specification:ubl:signature:Invoice
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | not(//ancestor-or-self::ext:UBLExtensions)
29 |
30 |
31 | not(//ancestor-or-self::cac:Signature)
32 |
33 |
34 |
35 | not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])
36 |
37 |
38 |
39 |
40 |
41 | BK6cj9p18ffIJHe/pWkkIT0myGMrFXYiczWq3se+nm8=
42 |
43 |
45 |
46 |
47 | ZDZjMzQyZTBjNGVjY2VlODgxYTBiMzhiNDFlM2QxZTlkZDkyMDQ1NTEwNzUwN2RhYjUwNGNmZmNmNTFmY2I2OA==
48 |
49 |
50 |
51 |
52 | MEYCIQCYh5e3irIqWl5YtaCnKiujMITmokXYOPMpIDCIoWW79wIhAMXmc+MFHGtaeBseOuEGTlwD6g09gNQPRPuY89s65OWS
53 |
54 |
55 |
56 |
57 | MIID1DCCA3mgAwIBAgITbwAAe3UAYVU34I/+5QABAAB7dTAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMDYxMjE3NDA1MloXDTI0MDYxMTE3NDA1MlowSTELMAkGA1UEBhMCU0ExDjAMBgNVBAoTBWFnaWxlMRYwFAYDVQQLEw1oYXlhIHlhZ2htb3VyMRIwEAYDVQQDEwkxMjcuMC4wLjEwVjAQBgcqhkjOPQIBBgUrgQQACgNCAATTAK9lrTVko9rkq6ZYcc9HDRZP4b9S4zA4Km7YXJ+snTVhLkzU0HsmSX9Un8jDhRTOHDKaft8C/uuUY934vuMNo4ICJzCCAiMwgYgGA1UdEQSBgDB+pHwwejEbMBkGA1UEBAwSMS1oYXlhfDItMjM0fDMtMTEyMR8wHQYKCZImiZPyLGQBAQwPMzAwMDc1NTg4NzAwMDAzMQ0wCwYDVQQMDAQxMTAwMREwDwYDVQQaDAhaYXRjYSAxMjEYMBYGA1UEDwwPRm9vZCBCdXNzaW5lc3MzMB0GA1UdDgQWBBSgmIWD6bPfbbKkmTwOJRXvIbH9HjAfBgNVHSMEGDAWgBR2YIz7BqCsZ1c1nc+arKcrmTW1LzBOBgNVHR8ERzBFMEOgQaA/hj1odHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVJTlZPSUNFLVN1YkNBLTEuY3JsMIGtBggrBgEFBQcBAQSBoDCBnTBuBggrBgEFBQcwAYZiaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2EvQ2VydEVucm9sbC9UU1pFaW52b2ljZVNDQTEuZXh0Z2F6dC5nb3YubG9jYWxfVFNaRUlOVk9JQ0UtU3ViQ0EtMSgxKS5jcnQwKwYIKwYBBQUHMAGGH2h0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL29jc3AwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzAnBgkrBgEEAYI3FQoEGjAYMAoGCCsGAQUFBwMCMAoGCCsGAQUFBwMDMAoGCCqGSM49BAMCA0kAMEYCIQCVwDMcq6PO+McmsBXUz/v1GdhGp7rqSa2AxTKSv838IAIhAOBNDBt9+3DSlijoVfxzrdDh528WC37smEdoGWVrSpG1
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 | 2022-06-13T11:06:14Z
67 |
68 |
69 |
70 |
72 |
73 | NDliYWY0YmVmNzAyZmE4OWIwZDMzYWIzZjFkOTY5YzFhZjRhZDE4ZTFkODlkNDY2Y2UwYzI2N2QyNDg0MWVlYQ==
74 |
75 |
76 |
77 | CN=TSZEINVOICE-SubCA-1, DC=extgazt, DC=gov,
78 | DC=local
79 |
80 |
81 | 2475382881139449094209233032847838632732556149
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | reporting:1.0
97 | SME00062
98 | 6f4d20e0-6bfe-4a80-9389-7dabe6620f12
99 | 2022-03-13
100 | 14:40:40
101 | 383
102 | SAR
103 | SAR
104 |
105 |
106 | TOSL108
107 |
108 |
109 |
110 | ICV
111 | 62
112 |
113 |
114 | PIH
115 |
116 |
117 | NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==
118 |
119 |
120 |
121 |
122 | urn:oasis:names:specification:ubl:signature:Invoice
123 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
124 |
125 |
126 |
127 |
128 | 310175397400003
129 |
130 |
131 | test
132 | 3454
133 | 1234
134 | fgff
135 | Riyadh
136 | 12345
137 | test
138 |
139 | SA
140 |
141 |
142 |
143 | 310175397400003
144 |
145 | VAT
146 |
147 |
148 |
149 | United Gulf Aircraft Fueling Company
150 |
151 |
152 |
153 |
154 |
155 |
156 | 2345
157 |
158 |
159 | baaoun
160 | sdsd
161 | 3353
162 | 3434
163 | fgff
164 | Dhurma
165 | 34534
166 | ulhk
167 |
168 | SA
169 |
170 |
171 |
172 |
173 | VAT
174 |
175 |
176 |
177 | sdsa
178 |
179 |
180 |
181 |
182 | 2022-03-13
183 | 2022-03-13
184 |
185 |
186 |
187 | 42
188 | string
189 |
190 |
191 |
192 | 1
193 | false
194 | discount
195 | 2
196 |
197 | S
198 | 15
199 |
200 | VAT
201 |
202 |
203 |
204 |
205 | 144.9
206 |
207 | 966.00
208 | 144.90
209 |
210 | S
211 | 15.00
212 |
213 | VAT
214 |
215 |
216 |
217 |
218 |
219 | 151.00
220 |
221 |
222 | 966.00
223 | 964.00
224 | 1108.90
225 | 2.00
226 | 0.00
227 | 1108.90
228 |
229 |
230 | 1
231 | 44.000000
232 | 966.00
233 |
234 | 144.90
235 | 1110.90
236 |
237 |
238 | dsd
239 |
240 | S
241 | 15.00
242 |
243 | VAT
244 |
245 |
246 |
247 |
248 | 22.00
249 |
250 | false
251 | discount
252 | 2.00
253 |
254 |
255 |
256 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/GlobalVariables.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper;
2 |
3 | public class GlobalVariables
4 | {
5 | //"https://gw-apic-gov.gazt.gov.sa"
6 | public string BaseUrl { get; set; }
7 |
8 | //"/e-invoicing/developer-portal/compliance"
9 | public string ComplianceCsidEndpoint { get; set; }
10 |
11 | //"/e-invoicing/developer-portal/production/csids"
12 | public string ProdCsidEndpoint { get; set; }
13 | //"/e-invoicing/developer-portal/invoices/clearance/single"
14 | public string InvoiceClearanceEndPoint { get; set; }
15 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/IsExternalInit.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | public static class IsExternalInit { }
4 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/CustomerInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class CustomerInfo
4 | {
5 | public string NAT { get; set; }
6 | public string VAT { get; set; }
7 | public string RegistrationName { get; set; }
8 | public PartyPostalAddress PartyPostalAddress { get; set; }
9 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/InvoiceLineItem.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class InvoiceLineItem
4 | {
5 | public string ID { get; set; }
6 | public string InvoicedQuantity { get; set; }
7 | public Money LineExtensionAmount { get; set; }
8 | public Money TaxAmount { get; set; }
9 | public Money RoundingAmount { get; set; }
10 | public string TaxSchemeId { get; set; }
11 | public string TaxScheme { get; set; }
12 | public string TaxPercent { get; set; }
13 | public string ItemName { get; set; }
14 |
15 | //rate of item
16 | public Money PriceAmount { get; set; }
17 |
18 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/LegalMonetaryTotal.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class LegalMonetaryTotal
4 | {
5 | public Money LineExtensionAmount { get; set; }
6 | public Money TaxExclusiveAmount { get; set; }
7 | public Money TaxInclusiveAmount { get; set; }
8 | public Money AllowanceTotalAmount { get; set; }
9 | public Money PrepaidAmount { get; set; }
10 | public Money PayableAmount { get; set; }
11 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/Money.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class Money
4 | {
5 | public Money(string currencyCode, double amount)
6 | {
7 | CurrencyCode = currencyCode;
8 | Amount = amount;
9 | }
10 |
11 | public string CurrencyCode { get; }
12 | public double Amount { get; }
13 |
14 | public string GetAmountString() => $"{Amount:0.00}";
15 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/PartyPostalAddress.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class PartyPostalAddress
4 | {
5 | public string StreetName { get; set; }
6 | public string BuildingNumber { get; set; }
7 | public string PlotIdentification { get; set; }
8 | public string CitySubdivisionName { get; set; }
9 | public string CityName { get; set; }
10 | public string PostalZone { get; set; }
11 | public string CountrySubentity { get; set; }
12 | public string Country { get; set; }
13 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/StandardInvoice.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class StandardInvoice
4 | {
5 | public string Id { get; set; }
6 | public string UUID { get; set; }
7 | public string IssuedDate { get; set; }
8 | public string IssuedTime { get; set; }
9 | public string InvoiceTypeCode { get; set; }
10 | public string DocumentCurrencyCode { get; set; }
11 | public string TaxCurrencyCode { get; set; }
12 | public string PreviousInvoiceHash { get; set; }
13 | public string ActualDeliveryDate { get; set; }
14 | public string LatestDeliveryDate { get; set; }
15 |
16 |
17 |
18 | public SupplierInfo SupplierInfo { get; set; }
19 | public CustomerInfo CustomerInfo { get; set; }
20 | public InvoiceLineItem InvoiceLineItem { get; set; }
21 | public LegalMonetaryTotal LegalMonetaryTotal { get; set; }
22 | public TaxTotal TaxTotal { get; set; }
23 |
24 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/StandardInvoiceAdjustment.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class StandardInvoiceAdjustment : StandardInvoice
4 | {
5 | public string AdjustmentReason { get; set; }
6 | public string AdjsutmentInvoiceNumber { get; set; }
7 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/SupplierInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class SupplierInfo
4 | {
5 | public string CRN { get; set; }
6 | public string VAT { get; set; }
7 | public string RegistrationName { get; set; }
8 | public PartyPostalAddress PartyPostalAddress { get; set; }
9 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Model/TaxTotal.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Model;
2 |
3 | public class TaxTotal
4 | {
5 | public Money TaxAmount { get; set; }
6 | public Money TaxableAmount { get; set; }
7 | public string TaxCategory { get; set; }
8 | public string Percent { get; set; }
9 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/ProdCsidApiClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using Bee.ZatcaHelper.Contracts;
3 | using Bee.ZatcaHelper.Util;
4 |
5 | namespace Bee.ZatcaHelper;
6 |
7 | public class ProdCsidApiClient
8 | {
9 | private readonly string _baseUrl;
10 | private readonly string _prodCsidEndPoint;
11 |
12 | public ProdCsidApiClient(GlobalVariables globalVariables)
13 | {
14 | _baseUrl = globalVariables.BaseUrl;
15 | _prodCsidEndPoint = globalVariables.ProdCsidEndpoint;
16 | }
17 |
18 | public ProdCsidOnboardingResponse? GetToken(ProdCsidOnboardingRequest prodCsidOnboardingRequest)
19 | {
20 | var result = new WebClient(_baseUrl, new Dictionary(),
21 | prodCsidOnboardingRequest.CsrBinaryToken, prodCsidOnboardingRequest.CsrSecret).PostAsJsonAsync(
22 | _prodCsidEndPoint, new
23 | {
24 | compliance_request_id = prodCsidOnboardingRequest.ComplianceRequestId
25 | });
26 | return result.Result.Content.ReadFromJsonAsync().Result;
27 | }
28 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/StanadardInvoiceXpaths.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper;
2 |
3 | public static partial class StandardInvoiceXmlGenerator
4 | {
5 | public const string QrCodeXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'AdditionalDocumentReference' and *[local-name()='ID' and .='QR']]/*[local-name() = 'Attachment']/*[local-name() = 'EmbeddedDocumentBinaryObject']";
6 |
7 | private const string InvoicePreviousInvoiceHashXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'AdditionalDocumentReference' and *[local-name()='ID' and .='PIH']]/*[local-name() = 'Attachment']/*[local-name() = 'EmbeddedDocumentBinaryObject']";
8 |
9 | private const string HashXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'UBLExtensions']/*[local-name() = 'UBLExtension']/*[local-name() = 'ExtensionContent']/*[local-name() = 'UBLDocumentSignatures']/*[local-name() = 'SignatureInformation']/*[local-name() = 'Signature']/*[local-name() = 'SignedInfo']/*[local-name() = 'Reference' and @Id='invoiceSignedData']/*[local-name() = 'DigestValue']";
10 |
11 | private const string InvoiceIdXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'ID']";
12 | private const string InvoiceUuidXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'UUID']";
13 |
14 | private const string InvoiceActualDeliveryDateXpath =
15 | "/*[local-name() = 'Invoice']/*[local-name() = 'ActualDeliveryDate']";
16 |
17 | private const string InvoiceDocumentCurrencyCodeXpath =
18 | "/*[local-name() = 'Invoice']/*[local-name() = 'DocumentCurrencyCode']";
19 |
20 | private const string InvoiceTaxCurrencyCodeXpath =
21 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxCurrencyCode']";
22 |
23 | private const string InvoiceIssueDateXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'IssueDate']";
24 | private const string InvoiceIssueTimeXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'IssueTime']";
25 |
26 | private const string InvoiceLatestDeliveryDateXpath =
27 | "/*[local-name() = 'Invoice']/*[local-name() = 'Delivery']/*[local-name() = 'LatestDeliveryDate']";
28 |
29 | private const string SupplierStreetNameXpath =
30 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'StreetName']";
31 |
32 | private const string SupplierBuildingNumberXpath =
33 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'BuildingNumber']";
34 |
35 | private const string SupplierPlotIdentificationXpath =
36 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'PlotIdentification']";
37 |
38 | private const string SupplierCitySubdivisionNameXpath =
39 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CitySubdivisionName']";
40 |
41 | private const string SupplierCityNameXpath =
42 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CityName']";
43 |
44 | private const string SupplierPostalZoneXpath =
45 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'PostalZone']";
46 |
47 | private const string SupplierCountrySubEntityXpath =
48 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CountrySubentity']";
49 |
50 | private const string SupplierCountryXpath =
51 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'Country']/*[local-name() = 'Country']";
52 |
53 | private const string SupplierRegistrationNameXpath =
54 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PartyLegalEntity']/*[local-name() = 'RegistrationName']";
55 |
56 | private const string SupplierCrnXpath =
57 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PartyIdentification']/*[local-name() = 'ID']";
58 |
59 | private const string SupplierTaxSchemeXpath =
60 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingSupplierParty']/*[local-name() = 'Party']/*[local-name() = 'PartyTaxScheme']/*[local-name() = 'TaxScheme']/*[local-name() = 'ID']";
61 |
62 | private const string CustomerStreetNameXpath =
63 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'StreetName']";
64 |
65 | private const string CustomerBuildingNumberXpath =
66 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'BuildingNumber']";
67 |
68 | private const string CustomerPlotIdentificationXpath =
69 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'PlotIdentification']";
70 |
71 | private const string CustomerCitySubdivisionNameXpath =
72 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CitySubdivisionName']";
73 |
74 | private const string CustomerCityNameXpath =
75 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CityName']";
76 |
77 | private const string CustomerPostalZoneXpath =
78 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'PostalZone']";
79 |
80 | private const string CustomerCountrySubEntityXpath =
81 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'CountrySubentity']";
82 |
83 | private const string CustomerCountryXpath =
84 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PostalAddress']/*[local-name() = 'Country']/*[local-name() = 'Country']";
85 |
86 | private const string CustomerRegistrationNameXpath =
87 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PartyLegalEntity']/*[local-name() = 'RegistrationName']";
88 |
89 | private const string CustomerNatXpath =
90 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PartyIdentification']/*[local-name() = 'ID']";
91 |
92 | private const string CustomerTaxSchemeXpath =
93 | "/*[local-name() = 'Invoice']/*[local-name() = 'AccountingCustomerParty']/*[local-name() = 'Party']/*[local-name() = 'PartyTaxScheme']/*[local-name() = 'TaxScheme']/*[local-name() = 'ID']";
94 |
95 | private const string AllowanceXpath = "/*[local-name() = 'Invoice']/*[local-name() = 'AllowanceCharge']";
96 |
97 | private const string TaxTotalTaxAmountXpath =
98 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxAmount']";
99 |
100 | private const string TaxTotalTaxAmountSecondTagXpath =
101 | "(/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxAmount'])[2]";
102 |
103 | private const string TaxTotalSubTotalTaxableAmountXpath =
104 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxSubtotal']/*[local-name() = 'TaxableAmount']";
105 |
106 | private const string TaxTotalSubTotalTaxAmountXpath =
107 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxSubtotal']/*[local-name() = 'TaxAmount']";
108 |
109 | private const string TaxTotalSubTotalTaxCategoryIdTaxAmountXpath =
110 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxSubtotal']/*[local-name() = 'TaxCategory']/*[local-name() = 'ID']";
111 |
112 | private const string TaxTotalSubTotalTaxPercentTaxAmountXpath =
113 | "/*[local-name() = 'Invoice']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxSubtotal']/*[local-name() = 'TaxCategory']/*[local-name() = 'Percent']";
114 |
115 | private const string CurrencyIdAttributeName = "currencyID";
116 |
117 | private const string LineItemIdXpath =
118 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'ID']";
119 |
120 | private const string LineItemInvoicedQuantityXpath =
121 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'InvoicedQuantity']";
122 |
123 | private const string LineItemLineExtensionAmountXpath =
124 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'LineExtensionAmount']";
125 |
126 | private const string LineItemTaxAmountXpath =
127 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'TaxTotal']/*[local-name() = 'TaxAmount']";
128 |
129 | private const string LineItemRoundingAmountXpath =
130 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'TaxTotal']/*[local-name() = 'RoundingAmount']";
131 |
132 | private const string LineItemNameXpath =
133 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Item']/*[local-name() = 'Name']";
134 |
135 | private const string LineItemTaxClassificationIdXpath =
136 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Item']/*[local-name() = 'ClassifiedTaxCategory']/*[local-name() = 'ID']";
137 |
138 | private const string LineItemTaxPercentXpath =
139 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Item']/*[local-name() = 'ClassifiedTaxCategory']/*[local-name() = 'Percent']";
140 |
141 | private const string LineItemTaxSchemeXpath =
142 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Item']/*[local-name() = 'ClassifiedTaxCategory']/*[local-name() = 'TaxScheme']/*[local-name() = 'ID']";
143 |
144 | private const string LineItemPriceAmountXpath =
145 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Price']/*[local-name() = 'PriceAmount']";
146 |
147 | private const string LineItemAllowanceXpath =
148 | "/*[local-name() = 'Invoice']/*[local-name() = 'InvoiceLine']/*[local-name() = 'Price']/*[local-name() = 'AllowanceCharge']";
149 |
150 | private const string LegalMonetaryTotalLineExtensionAmountXpath =
151 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'LineExtensionAmount']";
152 |
153 | private const string LegalMonetaryTotalTaxExclusiveAmountXpath =
154 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'TaxExclusiveAmount']";
155 |
156 | private const string LegalMonetaryTotalTaxInclusiveAmountXpath =
157 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'TaxInclusiveAmount']";
158 |
159 | private const string LegalMonetaryTotalAllowanceTotalAmountXpath =
160 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'AllowanceTotalAmount']";
161 |
162 | private const string LegalMonetaryTotalPrepaidAmountXpath =
163 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'PrepaidAmount']";
164 |
165 | private const string LegalMonetaryTotalPayableAmountXpath =
166 | "/*[local-name() = 'Invoice']/*[local-name() = 'LegalMonetaryTotal']/*[local-name() = 'PayableAmount']";
167 |
168 | private const string InvoiceReferenceXpath =
169 | "/*[local-name() = 'Invoice']/*[local-name() = 'BillingReference']/*[local-name() = 'InvoiceDocumentReference']/*[local-name() = 'ID']";
170 |
171 | private const string AdjustmentReasonXpath =
172 | "/*[local-name() = 'Invoice']/*[local-name() = 'PaymentMeans']/*[local-name() = 'InstructionNote']";
173 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/StandardInvoiceClearanceAPIClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Http.Json;
3 | using System.Text;
4 | using System.Xml;
5 | using Bee.ZatcaHelper.Contracts;
6 | using Bee.ZatcaHelper.Util;
7 | using WebClient = Bee.ZatcaHelper.Util.WebClient;
8 |
9 | namespace Bee.ZatcaHelper;
10 |
11 | public class StandardInvoiceClearanceApiClient
12 | {
13 | private readonly string _baseUrl;
14 | private readonly string _invoiceClearanceEndPoint;
15 |
16 | public StandardInvoiceClearanceApiClient(GlobalVariables globalVariables)
17 | {
18 | _baseUrl = globalVariables.BaseUrl;
19 | _invoiceClearanceEndPoint = globalVariables.InvoiceClearanceEndPoint;
20 | }
21 |
22 | public InvoiceClearanceResponse ClearInvoice(InvoiceClearanceRequest invoiceClearanceRequest)
23 | {
24 | var customHeaders = new Dictionary {{"Clearance-Status", "1"},{"accept-language","en"}};
25 | var result = new WebClient(_baseUrl, customHeaders,
26 | invoiceClearanceRequest.BinaryToken, invoiceClearanceRequest.Secret).PostAsJsonAsync(
27 | _invoiceClearanceEndPoint, invoiceClearanceRequest.Body);
28 | var response = result.Result.Content.ReadFromJsonAsync().Result;
29 |
30 | if (result.Result.StatusCode == HttpStatusCode.InternalServerError)
31 | {
32 | throw new Exception("Something went wrong " + result.Result.Content);
33 | }
34 |
35 | if (response?.ClearanceStatus != "CLEARED") return response;
36 | response.Hash = ((dynamic) invoiceClearanceRequest.Body).invoiceHash;
37 | response.GeneratedQR = GetGeneratedQrCode(response);
38 | return response;
39 | }
40 |
41 | private static string? GetGeneratedQrCode(InvoiceClearanceResponse response)
42 | {
43 | var data = Convert.FromBase64String(response.ClearedInvoice);
44 | var decodedString = Encoding.UTF8.GetString(data);
45 |
46 | var xmlDoc = new XmlDocument
47 | {
48 | PreserveWhitespace = true
49 | };
50 |
51 | xmlDoc.LoadXml(decodedString);
52 |
53 | var value = xmlDoc.GetNodeValue(Bee.ZatcaHelper.StandardInvoiceXmlGenerator.QrCodeXpath);
54 |
55 | return value;
56 | }
57 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/StandardInvoiceTemplate.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
10 |
11 |
12 |
16 |
17 | urn:oasis:names:specification:ubl:signature:1
18 | urn:oasis:names:specification:ubl:signature:Invoice
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | not(//ancestor-or-self::ext:UBLExtensions)
29 |
30 |
31 | not(//ancestor-or-self::cac:Signature)
32 |
33 |
34 |
35 | not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])
36 |
37 |
38 |
39 |
40 |
41 | BK6cj9p18ffIJHe/pWkkIT0myGMrFXYiczWq3se+nm8=
42 |
43 |
45 |
46 |
47 | ZDZjMzQyZTBjNGVjY2VlODgxYTBiMzhiNDFlM2QxZTlkZDkyMDQ1NTEwNzUwN2RhYjUwNGNmZmNmNTFmY2I2OA==
48 |
49 |
50 |
51 |
52 | MEYCIQCYh5e3irIqWl5YtaCnKiujMITmokXYOPMpIDCIoWW79wIhAMXmc+MFHGtaeBseOuEGTlwD6g09gNQPRPuY89s65OWS
53 |
54 |
55 |
56 |
57 | MIID1DCCA3mgAwIBAgITbwAAe3UAYVU34I/+5QABAAB7dTAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMDYxMjE3NDA1MloXDTI0MDYxMTE3NDA1MlowSTELMAkGA1UEBhMCU0ExDjAMBgNVBAoTBWFnaWxlMRYwFAYDVQQLEw1oYXlhIHlhZ2htb3VyMRIwEAYDVQQDEwkxMjcuMC4wLjEwVjAQBgcqhkjOPQIBBgUrgQQACgNCAATTAK9lrTVko9rkq6ZYcc9HDRZP4b9S4zA4Km7YXJ+snTVhLkzU0HsmSX9Un8jDhRTOHDKaft8C/uuUY934vuMNo4ICJzCCAiMwgYgGA1UdEQSBgDB+pHwwejEbMBkGA1UEBAwSMS1oYXlhfDItMjM0fDMtMTEyMR8wHQYKCZImiZPyLGQBAQwPMzAwMDc1NTg4NzAwMDAzMQ0wCwYDVQQMDAQxMTAwMREwDwYDVQQaDAhaYXRjYSAxMjEYMBYGA1UEDwwPRm9vZCBCdXNzaW5lc3MzMB0GA1UdDgQWBBSgmIWD6bPfbbKkmTwOJRXvIbH9HjAfBgNVHSMEGDAWgBR2YIz7BqCsZ1c1nc+arKcrmTW1LzBOBgNVHR8ERzBFMEOgQaA/hj1odHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVJTlZPSUNFLVN1YkNBLTEuY3JsMIGtBggrBgEFBQcBAQSBoDCBnTBuBggrBgEFBQcwAYZiaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2EvQ2VydEVucm9sbC9UU1pFaW52b2ljZVNDQTEuZXh0Z2F6dC5nb3YubG9jYWxfVFNaRUlOVk9JQ0UtU3ViQ0EtMSgxKS5jcnQwKwYIKwYBBQUHMAGGH2h0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL29jc3AwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzAnBgkrBgEEAYI3FQoEGjAYMAoGCCsGAQUFBwMCMAoGCCsGAQUFBwMDMAoGCCqGSM49BAMCA0kAMEYCIQCVwDMcq6PO+McmsBXUz/v1GdhGp7rqSa2AxTKSv838IAIhAOBNDBt9+3DSlijoVfxzrdDh528WC37smEdoGWVrSpG1
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 | 2022-06-13T11:06:14Z
67 |
68 |
69 |
70 |
72 |
73 | NDliYWY0YmVmNzAyZmE4OWIwZDMzYWIzZjFkOTY5YzFhZjRhZDE4ZTFkODlkNDY2Y2UwYzI2N2QyNDg0MWVlYQ==
74 |
75 |
76 |
77 | CN=TSZEINVOICE-SubCA-1, DC=extgazt, DC=gov,
78 | DC=local
79 |
80 |
81 | 2475382881139449094209233032847838632732556149
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | reporting:1.0
97 | SME00062
98 | 6f4d20e0-6bfe-4a80-9389-7dabe6620f12
99 | 2022-03-13
100 | 14:40:40
101 | 388
102 | SAR
103 | SAR
104 |
105 | ICV
106 | 63
107 |
108 |
109 | PIH
110 |
111 |
112 | NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==
113 |
114 |
115 |
116 |
117 | urn:oasis:names:specification:ubl:signature:Invoice
118 | urn:oasis:names:specification:ubl:dsig:enveloped:xades
119 |
120 |
121 |
122 |
123 | 310175397400003
124 |
125 |
126 | test
127 | 3454
128 | 1234
129 | fgff
130 | Riyadh
131 | 12345
132 | test
133 |
134 | SA
135 |
136 |
137 |
138 | 310175397400003
139 |
140 | VAT
141 |
142 |
143 |
144 | United Gulf Aircraft Fueling Company
145 |
146 |
147 |
148 |
149 |
150 |
151 | 2345
152 |
153 |
154 | baaoun
155 | sdsd
156 | 3353
157 | 3434
158 | fgff
159 | Dhurma
160 | 34534
161 | ulhk
162 |
163 | SA
164 |
165 |
166 |
167 |
168 | VAT
169 |
170 |
171 |
172 | sdsa
173 |
174 |
175 |
176 |
177 | 2022-03-13
178 | 2022-03-13
179 |
180 |
181 | 1
182 | false
183 | discount
184 | 2
185 |
186 | S
187 | 15
188 |
189 | VAT
190 |
191 |
192 |
193 |
194 | 144.9
195 |
196 | 966.00
197 | 144.90
198 |
199 | S
200 | 15.00
201 |
202 | VAT
203 |
204 |
205 |
206 |
207 |
208 | 151.00
209 |
210 |
211 | 966.00
212 | 964.00
213 | 1108.90
214 | 2.00
215 | 0.00
216 | 1108.90
217 |
218 |
219 | 1
220 | 44.000000
221 | 966.00
222 |
223 | 144.90
224 | 1110.90
225 |
226 |
227 | dsd
228 |
229 | S
230 | 15.00
231 |
232 | VAT
233 |
234 |
235 |
236 |
237 | 22.00
238 |
239 | false
240 | discount
241 | 2.00
242 |
243 |
244 |
245 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/StandardInvoiceXmlGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Xml;
2 | using Bee.ZatcaHelper.Model;
3 | using Bee.ZatcaHelper.Util;
4 |
5 | namespace Bee.ZatcaHelper;
6 |
7 | public static partial class StandardInvoiceXmlGenerator
8 | {
9 | private const string StandardInvoiceXmlFormat = "StandardInvoiceTemplate.xml";
10 | private static string? _currentHash;
11 |
12 | public static object GenerateForDebitNote(StandardInvoiceAdjustment standardInvoiceAdjustment)
13 | {
14 | const string debitNoteXmlFormat = "DebitNote.xml";
15 | return GenerateForAdjustment(standardInvoiceAdjustment, debitNoteXmlFormat);
16 | }
17 |
18 | public static object GenerateForCreditNote(StandardInvoiceAdjustment standardInvoiceAdjustment)
19 | {
20 | const string debitNoteXmlFormat = "CreditNote.xml";
21 | return GenerateForAdjustment(standardInvoiceAdjustment, debitNoteXmlFormat);
22 | }
23 |
24 | private static object GenerateForAdjustment(StandardInvoiceAdjustment standardInvoiceAdjustment,
25 | string debitNoteXmlFormat)
26 | {
27 | var xmlDoc = new XmlDocument
28 | {
29 | PreserveWhitespace = true
30 | };
31 | var xmlString = typeof(StandardInvoiceXmlGenerator).GetFileContent(debitNoteXmlFormat);
32 |
33 |
34 | var standardInvoice = (StandardInvoice) standardInvoiceAdjustment;
35 | xmlDoc.LoadXml(xmlString);
36 | PopulateInvoiceBasicInfo(xmlDoc, standardInvoice);
37 | PopulateSupplierInfo(xmlDoc, standardInvoice);
38 | PopulateCustomerInfo(xmlDoc, standardInvoice);
39 | PopulateDiscountInfo(xmlDoc, standardInvoice);
40 | PopulateTaxTotals(xmlDoc, standardInvoice);
41 | PopulateInvoiceLineItems(xmlDoc, standardInvoice);
42 | PopulateTotals(xmlDoc, standardInvoice);
43 |
44 | xmlDoc.SetNodeValue(InvoiceReferenceXpath, standardInvoiceAdjustment.AdjsutmentInvoiceNumber);
45 | xmlDoc.SetNodeValue(AdjustmentReasonXpath, standardInvoiceAdjustment.AdjustmentReason);
46 |
47 | PopulateInvoiceHash(xmlDoc);
48 |
49 |
50 | var xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlDoc.OuterXml);
51 | var sample = xmlDoc.OuterXml;
52 |
53 | var resultAPiCall = new
54 | {
55 | invoiceHash = _currentHash,
56 | uuid = standardInvoice.UUID,
57 | invoice = Convert.ToBase64String(xmlBytes)
58 | };
59 | return resultAPiCall;
60 | }
61 |
62 | public static object Generate(StandardInvoice standardInvoice)
63 | {
64 |
65 | var xmlDoc = new XmlDocument
66 | {
67 | PreserveWhitespace = true
68 | };
69 | var xmlString = typeof(StandardInvoiceXmlGenerator).GetFileContent(StandardInvoiceXmlFormat);
70 |
71 |
72 | xmlDoc.LoadXml(xmlString);
73 | PopulateInvoiceBasicInfo(xmlDoc, standardInvoice);
74 | PopulateSupplierInfo(xmlDoc, standardInvoice);
75 | PopulateCustomerInfo(xmlDoc, standardInvoice);
76 | PopulateDiscountInfo(xmlDoc, standardInvoice);
77 | PopulateTaxTotals(xmlDoc, standardInvoice);
78 | PopulateInvoiceLineItems(xmlDoc, standardInvoice);
79 | PopulateTotals(xmlDoc, standardInvoice);
80 | PopulateInvoiceHash(xmlDoc);
81 |
82 |
83 |
84 | var xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlDoc.OuterXml);
85 | var sample = xmlDoc.OuterXml;
86 |
87 | var resultAPiCall = new
88 | {
89 | invoiceHash = _currentHash,
90 | uuid = standardInvoice.UUID,
91 | invoice = Convert.ToBase64String(xmlBytes)
92 | };
93 |
94 | // Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(resultAPiCall));
95 | // Console.WriteLine(xmlDoc.OuterXml);
96 |
97 | return resultAPiCall;
98 | }
99 |
100 | private static void PopulateInvoiceHash(XmlDocument xmlDoc)
101 | {
102 | _currentHash = InvoiceHashHelper.GenerateEInvoiceHashing(xmlDoc.OuterXml);
103 | xmlDoc.SetNodeValue(HashXpath, _currentHash);
104 | }
105 |
106 | private static void PopulateTotals(XmlDocument xmlDoc, StandardInvoice standardInvoice)
107 | {
108 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalLineExtensionAmountXpath, standardInvoice.LegalMonetaryTotal.LineExtensionAmount);
109 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalTaxExclusiveAmountXpath, standardInvoice.LegalMonetaryTotal.TaxExclusiveAmount);
110 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalTaxInclusiveAmountXpath, standardInvoice.LegalMonetaryTotal.TaxInclusiveAmount);
111 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalAllowanceTotalAmountXpath, standardInvoice.LegalMonetaryTotal.AllowanceTotalAmount);
112 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalAllowanceTotalAmountXpath, standardInvoice.LegalMonetaryTotal.AllowanceTotalAmount);
113 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalPrepaidAmountXpath, standardInvoice.LegalMonetaryTotal.PrepaidAmount);
114 | xmlDoc.SetCurrencyNodeValue(LegalMonetaryTotalPayableAmountXpath, standardInvoice.LegalMonetaryTotal.PayableAmount);
115 | }
116 |
117 | private static void PopulateInvoiceLineItems(XmlDocument xmlDoc, StandardInvoice standardInvoice)
118 | {
119 | xmlDoc.SetNodeValue(LineItemIdXpath, standardInvoice.InvoiceLineItem.ID);
120 | xmlDoc.SetNodeValue(LineItemInvoicedQuantityXpath, standardInvoice.InvoiceLineItem.InvoicedQuantity);
121 | xmlDoc.SetCurrencyNodeValue(LineItemLineExtensionAmountXpath, standardInvoice.InvoiceLineItem.LineExtensionAmount);
122 | xmlDoc.SetCurrencyNodeValue(LineItemTaxAmountXpath, standardInvoice.InvoiceLineItem.TaxAmount);
123 | xmlDoc.SetCurrencyNodeValue(LineItemRoundingAmountXpath, standardInvoice.InvoiceLineItem.RoundingAmount);
124 | xmlDoc.SetNodeValue(LineItemNameXpath, standardInvoice.InvoiceLineItem.ItemName);
125 | xmlDoc.SetNodeValue(LineItemTaxPercentXpath, standardInvoice.InvoiceLineItem.TaxPercent);
126 | xmlDoc.SetNodeValue(LineItemTaxClassificationIdXpath, standardInvoice.InvoiceLineItem.TaxSchemeId);
127 | xmlDoc.SetNodeValue(LineItemTaxSchemeXpath, standardInvoice.InvoiceLineItem.TaxScheme);
128 | xmlDoc.SetCurrencyNodeValue(LineItemPriceAmountXpath, standardInvoice.InvoiceLineItem.PriceAmount);
129 |
130 | // TODO : implement Allowance charge
131 |
132 | xmlDoc.RemoveNode(LineItemAllowanceXpath);
133 | }
134 |
135 | private static void PopulateTaxTotals(XmlDocument xmlDoc, StandardInvoice standardInvoice)
136 | {
137 | //TODO: Need to handle tax exemption
138 |
139 | xmlDoc.SetCurrencyNodeValue(TaxTotalTaxAmountXpath, standardInvoice.TaxTotal.TaxAmount);
140 | xmlDoc.SetCurrencyNodeValue(TaxTotalTaxAmountSecondTagXpath, standardInvoice.TaxTotal.TaxAmount);
141 | xmlDoc.SetCurrencyNodeValue(TaxTotalSubTotalTaxableAmountXpath,standardInvoice.TaxTotal.TaxableAmount);
142 | xmlDoc.SetCurrencyNodeValue(TaxTotalSubTotalTaxAmountXpath,standardInvoice.TaxTotal.TaxAmount);
143 | xmlDoc.SetNodeValue(TaxTotalSubTotalTaxCategoryIdTaxAmountXpath,standardInvoice.TaxTotal.TaxCategory);
144 | xmlDoc.SetNodeValue(TaxTotalSubTotalTaxPercentTaxAmountXpath,standardInvoice.TaxTotal.Percent);
145 | }
146 |
147 | private static void PopulateDiscountInfo(XmlDocument xmlDoc, StandardInvoice standardInvoice)
148 | {
149 | // TODO : implement Allowance charge
150 |
151 | xmlDoc.RemoveNode(AllowanceXpath);
152 | }
153 |
154 | private static void PopulateCustomerInfo(XmlDocument xmlDoc, StandardInvoice standardInvoice)
155 | {
156 | xmlDoc.SetNodeValue(CustomerStreetNameXpath, standardInvoice.CustomerInfo.PartyPostalAddress.StreetName);
157 | xmlDoc.SetNodeValue(CustomerBuildingNumberXpath, standardInvoice.CustomerInfo.PartyPostalAddress.BuildingNumber);
158 | xmlDoc.SetNodeValue(CustomerPlotIdentificationXpath, standardInvoice.CustomerInfo.PartyPostalAddress.PlotIdentification);
159 | xmlDoc.SetNodeValue(CustomerCitySubdivisionNameXpath, standardInvoice.CustomerInfo.PartyPostalAddress.CitySubdivisionName);
160 | xmlDoc.SetNodeValue(CustomerCityNameXpath, standardInvoice.CustomerInfo.PartyPostalAddress.CityName);
161 | xmlDoc.SetNodeValue(CustomerPostalZoneXpath, standardInvoice.CustomerInfo.PartyPostalAddress.PostalZone);
162 | xmlDoc.SetNodeValue(CustomerCountrySubEntityXpath, standardInvoice.CustomerInfo.PartyPostalAddress.CountrySubentity);
163 | xmlDoc.SetNodeValue(CustomerCountryXpath, standardInvoice.CustomerInfo.PartyPostalAddress.Country);
164 | xmlDoc.SetNodeValue(CustomerRegistrationNameXpath, standardInvoice.CustomerInfo.RegistrationName);
165 | xmlDoc.SetNodeValue(CustomerNatXpath, standardInvoice.CustomerInfo.NAT);
166 | xmlDoc.SetNodeValue(CustomerTaxSchemeXpath, "VAT");
167 | }
168 |
169 | private static void PopulateSupplierInfo(XmlDocument xmlDoc, StandardInvoice standardInvoice)
170 | {
171 | xmlDoc.SetNodeValue(SupplierStreetNameXpath, standardInvoice.SupplierInfo.PartyPostalAddress.StreetName);
172 | xmlDoc.SetNodeValue(SupplierBuildingNumberXpath, standardInvoice.SupplierInfo.PartyPostalAddress.BuildingNumber);
173 | xmlDoc.SetNodeValue(SupplierPlotIdentificationXpath, standardInvoice.SupplierInfo.PartyPostalAddress.PlotIdentification);
174 | xmlDoc.SetNodeValue(SupplierCitySubdivisionNameXpath, standardInvoice.SupplierInfo.PartyPostalAddress.CitySubdivisionName);
175 | xmlDoc.SetNodeValue(SupplierCityNameXpath, standardInvoice.SupplierInfo.PartyPostalAddress.CityName);
176 | xmlDoc.SetNodeValue(SupplierPostalZoneXpath, standardInvoice.SupplierInfo.PartyPostalAddress.PostalZone);
177 | xmlDoc.SetNodeValue(SupplierCountrySubEntityXpath, standardInvoice.SupplierInfo.PartyPostalAddress.CountrySubentity);
178 | xmlDoc.SetNodeValue(SupplierCountryXpath, standardInvoice.SupplierInfo.PartyPostalAddress.Country);
179 | xmlDoc.SetNodeValue(SupplierRegistrationNameXpath, standardInvoice.SupplierInfo.RegistrationName);
180 | xmlDoc.SetNodeValue(SupplierTaxSchemeXpath, "VAT");
181 | xmlDoc.SetNodeValue(SupplierCrnXpath, standardInvoice.SupplierInfo.CRN);
182 | }
183 |
184 | private static void PopulateInvoiceBasicInfo(XmlDocument xmlDoc, StandardInvoice standardInvoice)
185 | {
186 | xmlDoc.SetNodeValue(InvoiceIdXpath, standardInvoice.Id);
187 | xmlDoc.SetNodeValue(InvoiceUuidXpath, standardInvoice.UUID);
188 | xmlDoc.SetNodeValue(InvoiceActualDeliveryDateXpath, standardInvoice.ActualDeliveryDate);
189 | xmlDoc.SetNodeValue(InvoiceDocumentCurrencyCodeXpath, standardInvoice.DocumentCurrencyCode);
190 | xmlDoc.SetNodeValue(InvoiceTaxCurrencyCodeXpath, standardInvoice.TaxCurrencyCode);
191 | xmlDoc.SetNodeValue(InvoiceIssueDateXpath, standardInvoice.IssuedDate);
192 | xmlDoc.SetNodeValue(InvoiceIssueTimeXpath, standardInvoice.IssuedTime);
193 | xmlDoc.SetNodeValue(InvoiceLatestDeliveryDateXpath, standardInvoice.LatestDeliveryDate);
194 | xmlDoc.SetNodeValue(InvoicePreviousInvoiceHashXpath, standardInvoice.PreviousInvoiceHash);
195 | }
196 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Util/EmbeddedResourceHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Bee.ZatcaHelper.Util;
2 |
3 | public static class EmbeddedResourceHelper
4 | {
5 | public static string? GetFileContent(this Type type,string fileName)
6 | {
7 | var fullyQualifiedName = type.Namespace + "." + fileName;
8 | using var stream = typeof(Bee.ZatcaHelper.StandardInvoiceXmlGenerator).Assembly.GetManifestResourceStream(fullyQualifiedName);
9 | if (stream == null) return null;
10 | using var reader = new StreamReader(stream);
11 | var fileContent = reader.ReadToEnd();
12 | return fileContent;
13 | }
14 |
15 | public static StreamReader? GetFileContentAsStream(this Type type,string fileName)
16 | {
17 | var fullyQualifiedName = type.Namespace + "." + fileName;
18 | var stream = typeof(Bee.ZatcaHelper.StandardInvoiceXmlGenerator).Assembly.GetManifestResourceStream(fullyQualifiedName);
19 | return stream == null ? null : new StreamReader(stream);
20 | }
21 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Util/InvoiceHashHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography;
2 | using System.Security.Cryptography.Xml;
3 | using System.Text;
4 | using System.Xml;
5 | using System.Xml.Xsl;
6 | using Serilog;
7 |
8 | namespace Bee.ZatcaHelper.Util;
9 |
10 | public static class InvoiceHashHelper
11 | {
12 | private const string StandardInvoiceHashingXslTemplate = "StandardInvoiceHashingTemplate.xsl";
13 |
14 | public static string? GenerateEInvoiceHashing(string xmlString)
15 | {
16 | try
17 | {
18 | var xslStream = typeof(InvoiceHashHelper).GetFileContentAsStream(StandardInvoiceHashingXslTemplate);
19 | string transformedXml;
20 | try
21 | {
22 | transformedXml = ApplyXslt(xmlString, xslStream);
23 | }
24 | catch
25 | {
26 | Log.Error("Can not apply XSL file");
27 | return null;
28 | }
29 |
30 | if (string.IsNullOrEmpty(transformedXml))
31 | {
32 | Log.Error("Error In applying XSL file");
33 | return null;
34 | }
35 |
36 | var canonicalizedXml = CanonicalizeXml(transformedXml);
37 | var hashValue = GetSha256Hash(Encoding.UTF8.GetString(canonicalizedXml?.ToArray() ?? Array.Empty()));
38 | if (hashValue.Any()) return Convert.ToBase64String(hashValue);
39 | Log.Error("Hashing Error");
40 | return null;
41 | }
42 | catch (Exception ex)
43 | {
44 | Log.Error(ex,ex.Message);
45 | return null;
46 | }
47 | }
48 |
49 | private static MemoryStream? CanonicalizeXml(string transformedXml)
50 | {
51 | using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(transformedXml));
52 | //converting xml to standard format
53 | //https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.xml.xmldsigc14ntransform?view=dotnet-plat-ext-6.0
54 | var canonicalizer = new XmlDsigC14NTransform(false);
55 | canonicalizer.LoadInput(memoryStream);
56 | var canonicalizedXml = canonicalizer.GetOutput() as MemoryStream;
57 | return canonicalizedXml;
58 | }
59 |
60 |
61 | private static byte[] GetSha256Hash(string value)
62 | {
63 | using var shA256 = SHA256.Create();
64 | var utF8 = Encoding.UTF8;
65 | return shA256.ComputeHash(utF8.GetBytes(value));
66 | }
67 |
68 | private static string ApplyXslt(string xmlContent, StreamReader? xslFile)
69 | {
70 | var xmlReader = XmlReader.Create(new StringReader(xmlContent));
71 | var output = new StringBuilder();
72 | using (var results = XmlWriter.Create(output, new XmlWriterSettings()
73 | {
74 | OmitXmlDeclaration = true,
75 | Encoding = Encoding.UTF8,
76 | Indent = false
77 | }))
78 | {
79 | if (xslFile != null)
80 | {
81 | var stylesheet = XmlReader.Create(xslFile);
82 |
83 | var compiledTransform = new XslCompiledTransform();
84 | compiledTransform.Load(stylesheet);
85 | compiledTransform.Transform(xmlReader, results);
86 | }
87 | }
88 |
89 | xslFile?.Close();
90 | return output.ToString();
91 | }
92 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Util/StandardInvoiceHashingTemplate.xsl:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Util/WebClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Net.Http.Headers;
3 | using System.Net.Http.Json;
4 | using System.Text;
5 | using Serilog;
6 |
7 | namespace Bee.ZatcaHelper.Util;
8 |
9 | public class WebClient
10 | {
11 | private readonly HttpClient _client = new();
12 |
13 | public WebClient(string baseUrl, Dictionary customHeaders)
14 | {
15 | _client.BaseAddress = new Uri(baseUrl);
16 | _client.DefaultRequestHeaders.Accept.Clear();
17 | _client.DefaultRequestHeaders.Accept.Add(
18 | new MediaTypeWithQualityHeaderValue("application/json"));
19 | _client.DefaultRequestHeaders.Add("Accept-Version", "V2");
20 |
21 | foreach (var (header, value) in customHeaders.Select( x => (x.Key, x.Value) ))
22 | {
23 | _client.DefaultRequestHeaders.Add(header, value);
24 | }
25 | }
26 |
27 | public WebClient(string baseUrl, Dictionary customHeaders, string userName, string password)
28 | : this(baseUrl, customHeaders)
29 | {
30 | var credentials = Convert.ToBase64String(
31 | Encoding.ASCII.GetBytes(userName + ":" + password));
32 | _client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Basic {credentials}");
33 | }
34 |
35 | public async Task PostAsJsonAsync(string controller, object data)
36 | {
37 | HttpResponseMessage response = new();
38 |
39 | try
40 | {
41 | response = await _client.PostAsJsonAsync(controller, data).ConfigureAwait(false);
42 |
43 | response.EnsureSuccessStatusCode();
44 | }
45 | catch (Exception ex)
46 | {
47 | Log.Error(ex, "Exception occured - {0}", ex.Message);
48 | }
49 |
50 | return response;
51 | }
52 | }
--------------------------------------------------------------------------------
/Bee.ZatcaHelper/Util/XmlHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Xml;
2 | using Bee.ZatcaHelper.Model;
3 |
4 | namespace Bee.ZatcaHelper.Util;
5 |
6 | public static class XmlHelper
7 | {
8 | public static void SetNodeValue(this XmlDocument xmlDocument, string xPath, string? value)
9 | {
10 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
11 | if (xmlNode == null)
12 | return;
13 | if (value != null) xmlNode.InnerText = value;
14 | }
15 |
16 | public static void RemoveNode(this XmlDocument xmlDocument, string xPath)
17 | {
18 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
19 | xmlNode?.ParentNode?.RemoveChild(xmlNode);
20 | }
21 |
22 | public static void SetAttributeValue(this XmlDocument xmlDocument, string xPath, string attributeName,
23 | string? value)
24 | {
25 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
26 | if (xmlNode?.Attributes == null) return;
27 | foreach (XmlAttribute attribute in xmlNode.Attributes)
28 | {
29 | if (attribute.Name == attributeName)
30 | attribute.Value = value;
31 | }
32 | }
33 |
34 | public static void SetCurrencyNodeValue(this XmlDocument xmlDocument, string xPath, string? value, string? attributeValue)
35 | {
36 | const string currencyIdAttributeName = "currencyID";
37 |
38 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
39 | if (xmlNode?.Attributes == null) return;
40 | foreach (XmlAttribute attribute in xmlNode.Attributes)
41 | {
42 | if (attribute.Name == currencyIdAttributeName)
43 | attribute.Value = attributeValue;
44 | }
45 |
46 | if (value != null) xmlNode.InnerText = value;
47 | }
48 |
49 | public static void SetCurrencyNodeValue(this XmlDocument xmlDocument, string xPath, Money money)
50 | {
51 | const string currencyIdAttributeName = "currencyID";
52 |
53 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
54 | if (xmlNode?.Attributes == null) return;
55 | foreach (XmlAttribute attribute in xmlNode.Attributes)
56 | {
57 | if (attribute.Name == currencyIdAttributeName)
58 | attribute.Value = money.CurrencyCode;
59 | }
60 |
61 | xmlNode.InnerText = money.GetAmountString();
62 | }
63 |
64 | public static string? GetNodeValue(this XmlDocument xmlDocument, string xPath)
65 | {
66 | var xmlNode = xmlDocument.SelectSingleNode(xPath);
67 | return xmlNode?.InnerText;
68 | }
69 | }
--------------------------------------------------------------------------------
/Logo/Devbee-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devbee-engineering/ZatcaHelper/1ef567cd6f1775a7ba98f4e7d18ac4d3198bb7a7/Logo/Devbee-Logo.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Zatca | Fatoora - Integeration Helper
3 |
4 | This project helps in integertation of systems to Zatca systems.
5 | Currently tested using their sandbox APIs.
6 |
7 | > 🚀 Check [Pro feature list](https://github.com/devbee-engineering/ZatcaHelper/blob/main/pro-version-features.md) for the Pro version, we already have more than 10 clients using our pro version.
8 |
9 |
10 | > **_NOTE:_** We have implemented similar solutions for many of our B2B clients in KSA, if you have similar requirements feel free to reach me at +91 - 9003095635
11 |
12 | ## 🔗 Contact
13 | [](https://www.linkedin.com/in/fasilmarshooq)
14 |
15 | Write to me at **fasil@dev-bee.com**
16 |
17 | ## 🔗 NuGet Package
18 | [](https://www.nuget.org/packages/Bee.ZatcaHelper/)
19 |
20 |
21 | ## Follow below shell commands to generate CSR
22 |
23 | we need a csr token to initiate compliance API request , more about what is CSR here -> https://en.wikipedia.org/wiki/Certificate_signing_request
24 |
25 | ```bash
26 | #generate private key using below command
27 | openssl ecparam -name secp256k1 -genkey -noout -out privatekey.pem
28 |
29 | #generate csr using below command
30 | openssl req -new -sha256 -key privatekey.pem -extensions v3_req -config cert.cnf -out generatedCSR.csr
31 |
32 | #convert to base64
33 | echo -n `cat generatedCSR.csr` | openssl enc -base64 -a -A >> csr_Output.txt
34 | ```
35 | > **_NOTE:_** Above script is bash/shell script, if you are running from windows cmd setup openssl and execute the commands or simply dowload the git bash (https://git-scm.com/download/win) which can help in execution
36 | ## API Reference
37 |
38 | **Note:** Global variables is a dependecy for all the classes , its parameters are described below
39 |
40 | | Parameter | Type | Description |
41 | | :-------- | :------- | :-------------------------------- |
42 | | `BaseUrl` | `string` | **Required**. Base url of zatca [Eg: https://gw-apic-gov.gazt.gov.sa] |
43 | | `ComplianceCsidEndpoint` | `string` | **Required**. Compliance Csid endpoint [Eg: /e-invoicing/developer-portal/compliance] |
44 | | `ProdCsidEndpoint` | `string` | **Required**. Prod csid endpoint [Eg : /e-invoicing/developer-portal/production/csids] |
45 | | `InvoiceClearanceEndPoint` | `string` | **Required**. clearance endpoint [Eg: /e-invoicing/developer-portal/invoices/clearance/single] |
46 |
47 | #### Get Compliance CSR token
48 |
49 | ```http
50 | ComplianceCsrApiClient(GlobalVariables).GetToken(ComplianceCsrRequest)
51 | ```
52 |
53 | | ComplianceCsrRequest Parameter | Type | Description |
54 | | :-------- | :------- | :------------------------- |
55 | | `Otp` | `string` | **Required**. OTP obatined from tax portal |
56 | | `Csr` | `string` | **Required**. CSR token generated using above mentioned step |
57 |
58 | #### Get Prod CSID
59 |
60 | ```http
61 | ProdCsidApiClient(GlobalVariables).GetToken(ProdCsidOnboardingRequest)
62 | ```
63 |
64 | | ProdCsidOnboardingRequest Parameter | Type | Description |
65 | | :-------- | :------- | :-------------------------------- |
66 | | `ComplianceRequestId` | `string` | **Required**. RequestId from the response of above endpoint |
67 | | `CsrBinaryToken` | `string` | **Required**. Binary token from the response of above endpoint |
68 | | `CsrSecret` | `string` | **Required**. Secret from the response of above endpoint |
69 |
70 | #### Generate xml
71 |
72 | ```http
73 | StandardInvoiceXmlGenerator.Generate(StandardInvoice)
74 | ```
75 |
76 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoice.cs
77 |
78 | #### Clear Invoice
79 |
80 | ```http
81 | StandardInvoiceClearanceApiClient(GlobalVariables).ClearInvoice(InvoiceClearanceRequest)
82 | ```
83 |
84 | | InvoiceClearanceRequest Parameter | Type | Description |
85 | | :-------- | :------- | :-------------------------------- |
86 | | `Body` | `string` | **Required**. response of above endpoint |
87 | | `BinaryToken` | `string` | **Required**. Binary token from the response of Get Prod CSID endpoint |
88 | | `Secret` | `string` | **Required**. Secret from the response of Get Prod CSID endpoint |
89 |
90 | #### Generate xml for Debit Note
91 |
92 | ```http
93 | StandardInvoiceXmlGenerator.GenerateForDebitNote(StandardInvoiceAdjustment)
94 | ```
95 |
96 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoiceAdjustment.cs
97 |
98 | > **_NOTE:_** use above clear Invoice endpoint to clear Debit Note
99 |
100 | #### Generate xml for Credit Note
101 |
102 | ```http
103 | StandardInvoiceXmlGenerator.GenerateForCreditNote(StandardInvoiceAdjustment)
104 | ```
105 |
106 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoiceAdjustment.cs
107 |
108 | > **_NOTE:_** use above clear Invoice endpoint to clear Credit Note
109 | ## Roadmap
110 |
111 | - implement Allowance charge / Discount
112 |
113 | - implement reporting api for simplified tax invoices
114 |
115 | ## License
116 |
117 | [MIT](https://choosealicense.com/licenses/mit/)
118 |
119 | ## Trouble Shooting
120 |
121 | - If you facing Schema validation execption without any error message please refer -> https://stackoverflow.com/questions/73388407/schema-validation-failed-xml-does-not-comply-with-ubl-2-1-standards-in-line-wit
122 |
123 | - UBL Schema repo -> http://www.datypic.com/sc/ubl21/e-ns39_Invoice.html
124 |
125 |
--------------------------------------------------------------------------------
/Zatca-Standard-Invoice-Integration-Client.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bee.ZatcaHelper", "Bee.ZatcaHelper\Bee.ZatcaHelper.csproj", "{DE9A3E6E-9E5E-41EF-ABCC-AB8D81BE323D}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bee.ZatcaHelper.IntegrationTests", "Bee.ZatcaHelper.IntegrationTests\Bee.ZatcaHelper.IntegrationTests.csproj", "{9DC352F7-577D-4180-97C5-36320D0794B4}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bee.ZatcaHelper.UnitTests", "Bee.ZatcaHelper.UnitTests\Bee.ZatcaHelper.UnitTests.csproj", "{61240BFD-01B7-4EA0-B48F-206A658C1732}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {DE9A3E6E-9E5E-41EF-ABCC-AB8D81BE323D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {DE9A3E6E-9E5E-41EF-ABCC-AB8D81BE323D}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {DE9A3E6E-9E5E-41EF-ABCC-AB8D81BE323D}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {DE9A3E6E-9E5E-41EF-ABCC-AB8D81BE323D}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {9DC352F7-577D-4180-97C5-36320D0794B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {9DC352F7-577D-4180-97C5-36320D0794B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {9DC352F7-577D-4180-97C5-36320D0794B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {9DC352F7-577D-4180-97C5-36320D0794B4}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {61240BFD-01B7-4EA0-B48F-206A658C1732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {61240BFD-01B7-4EA0-B48F-206A658C1732}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {61240BFD-01B7-4EA0-B48F-206A658C1732}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {61240BFD-01B7-4EA0-B48F-206A658C1732}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/pro-version-features.md:
--------------------------------------------------------------------------------
1 |
2 | # Zatca | Fatoora - Integeration Helper
3 |
4 | This project helps in integertation of systems to Zatca systems.
5 | Currently tested using their sandbox APIs.
6 |
7 | ---
8 |
9 |

10 |
CSR generation
11 |
B2C Simplified Invoice Generation is available Now
12 |
Compliance API
13 |
Reporting API
14 |
15 |
16 | ---
17 |
18 | ## Follow [This guide](CSR_Generation.md) to generate csr token.
19 |
20 | ## API Reference
21 |
22 | **Note:** Global variables is a dependecy for all the classes , its parameters are described below
23 |
24 | | Parameter | Type | Description |
25 | | :-------- | :------- | :-------------------------------- |
26 | | `BaseUrl` | `string` | **Required**. Base url of zatca [Eg: https://gw-apic-gov.gazt.gov.sa] |
27 | | `ComplianceCsidEndpoint` | `string` | **Required**. Compliance Csid endpoint [Eg: /e-invoicing/developer-portal/compliance] |
28 | | `ProdCsidEndpoint` | `string` | **Required**. Prod csid endpoint [Eg : /e-invoicing/developer-portal/production/csids] |
29 | | `InvoiceClearanceEndPoint` | `string` | **Required**. clearance endpoint [Eg: /e-invoicing/developer-portal/invoices/clearance/single] |
30 |
31 | #### Get Compliance CSR token
32 |
33 | ```http
34 | ComplianceCsrApiClient(GlobalVariables).GetToken(ComplianceCsrRequest)
35 | ```
36 |
37 | | ComplianceCsrRequest Parameter | Type | Description |
38 | | :-------- | :------- | :------------------------- |
39 | | `Otp` | `string` | **Required**. OTP obatined from tax portal |
40 | | `Csr` | `string` | **Required**. CSR token generated using above mentioned step |
41 |
42 | #### Get Prod CSID
43 |
44 | ```http
45 | ProdCsidApiClient(GlobalVariables).GetToken(ProdCsidOnboardingRequest)
46 | ```
47 |
48 | | ProdCsidOnboardingRequest Parameter | Type | Description |
49 | | :-------- | :------- | :-------------------------------- |
50 | | `ComplianceRequestId` | `string` | **Required**. RequestId from the response of above endpoint |
51 | | `CsrBinaryToken` | `string` | **Required**. Binary token from the response of above endpoint |
52 | | `CsrSecret` | `string` | **Required**. Secret from the response of above endpoint |
53 |
54 | #### Generate xml
55 |
56 | ```http
57 | StandardInvoiceXmlGenerator.Generate(StandardInvoice)
58 | ```
59 |
60 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoice.cs
61 |
62 | #### Clear Invoice
63 |
64 | ```http
65 | StandardInvoiceClearanceApiClient(GlobalVariables).ClearInvoice(InvoiceClearanceRequest)
66 | ```
67 |
68 | | InvoiceClearanceRequest Parameter | Type | Description |
69 | | :-------- | :------- | :-------------------------------- |
70 | | `Body` | `string` | **Required**. response of above endpoint |
71 | | `BinaryToken` | `string` | **Required**. Binary token from the response of Get Prod CSID endpoint |
72 | | `Secret` | `string` | **Required**. Secret from the response of Get Prod CSID endpoint |
73 |
74 | #### Generate xml for Debit Note
75 |
76 | ```http
77 | StandardInvoiceXmlGenerator.GenerateForDebitNote(StandardInvoiceAdjustment)
78 | ```
79 |
80 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoiceAdjustment.cs
81 |
82 | > **_NOTE:_** use above clear Invoice endpoint to clear Debit Note
83 |
84 | #### Generate xml for Credit Note
85 |
86 | ```http
87 | StandardInvoiceXmlGenerator.GenerateForCreditNote(StandardInvoiceAdjustment)
88 | ```
89 |
90 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoiceAdjustment.cs
91 |
92 | > **_NOTE:_** use above clear Invoice endpoint to clear Credit Note
93 |
94 | #### Generate xml for Simplified Invoice
95 |
96 | ```http
97 | StandardInvoiceXmlGenerator.GenerateForSimplifiedInvoice(StandardInvoiceAdjustment,Certificate,PrivateKey)
98 | ```
99 |
100 | For parameters refer -> https://github.com/fasilmarshooq/Zatca_Integration_Helper/blob/main/Zatca-Standard-Invoice-Integration-Client/Model/StandardInvoice.cs
101 |
102 | - Certificate : Binary token received using Get Prod CSID API
103 | - PrivateKey : your private key generated using above openSsl commands
104 | - feel free to use certifcate and private key from test project for testing purpose
105 | - [Certificate for test](Bee.ZatcaHelper.UnitTests/TestXmls/SimpleInvoice_Cert.pem)
106 | - [Private Key for test](Bee.ZatcaHelper.UnitTests/TestXmls/SimpleInvoice_privateKey.pem)
107 |
108 | > **_NOTE:_** reporting api is still under developement it has a bug in zatca sandbox till then feel free to use compliance API
109 |
110 |
111 | ---
112 |
113 |

114 |
Rest API supprt for non .net apps
115 |
Zatca Reporting API bug fixes
116 |
117 |
118 | ---
119 | ## License
120 |
121 | [MIT](https://choosealicense.com/licenses/mit/)
122 |
123 | ## Trouble Shooting
124 |
125 | - If you facing Schema validation execption without any error message please refer -> https://stackoverflow.com/questions/73388407/schema-validation-failed-xml-does-not-comply-with-ubl-2-1-standards-in-line-wit
126 |
127 | - UBL Schema repo -> http://www.datypic.com/sc/ubl21/e-ns39_Invoice.html
128 |
129 |
--------------------------------------------------------------------------------