zatca-sdk 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.md +60 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/zatca/client.rb +211 -0
- data/lib/zatca/hacks.rb +45 -0
- data/lib/zatca/hashing.rb +18 -0
- data/lib/zatca/qr_code_extractor.rb +31 -0
- data/lib/zatca/qr_code_generator.rb +28 -0
- data/lib/zatca/signing/certificate.rb +78 -0
- data/lib/zatca/signing/csr.rb +220 -0
- data/lib/zatca/signing/ecdsa.rb +59 -0
- data/lib/zatca/tag.rb +44 -0
- data/lib/zatca/tags.rb +46 -0
- data/lib/zatca/tags_schema.rb +22 -0
- data/lib/zatca/types.rb +7 -0
- data/lib/zatca/ubl/base_component.rb +142 -0
- data/lib/zatca/ubl/builder.rb +166 -0
- data/lib/zatca/ubl/common_aggregate_components/allowance_charge.rb +64 -0
- data/lib/zatca/ubl/common_aggregate_components/classified_tax_category.rb +25 -0
- data/lib/zatca/ubl/common_aggregate_components/delivery.rb +27 -0
- data/lib/zatca/ubl/common_aggregate_components/invoice_line.rb +63 -0
- data/lib/zatca/ubl/common_aggregate_components/item.rb +21 -0
- data/lib/zatca/ubl/common_aggregate_components/legal_monetary_total.rb +59 -0
- data/lib/zatca/ubl/common_aggregate_components/party.rb +28 -0
- data/lib/zatca/ubl/common_aggregate_components/party_identification.rb +25 -0
- data/lib/zatca/ubl/common_aggregate_components/party_legal_entity.rb +19 -0
- data/lib/zatca/ubl/common_aggregate_components/party_tax_scheme.rb +30 -0
- data/lib/zatca/ubl/common_aggregate_components/postal_address.rb +59 -0
- data/lib/zatca/ubl/common_aggregate_components/price.rb +20 -0
- data/lib/zatca/ubl/common_aggregate_components/tax_category.rb +56 -0
- data/lib/zatca/ubl/common_aggregate_components/tax_total.rb +58 -0
- data/lib/zatca/ubl/common_aggregate_components.rb +2 -0
- data/lib/zatca/ubl/invoice.rb +481 -0
- data/lib/zatca/ubl/invoice_subtype_builder.rb +50 -0
- data/lib/zatca/ubl/signing/cert.rb +48 -0
- data/lib/zatca/ubl/signing/invoice_signed_data_reference.rb +44 -0
- data/lib/zatca/ubl/signing/key_info.rb +25 -0
- data/lib/zatca/ubl/signing/object.rb +20 -0
- data/lib/zatca/ubl/signing/qualifying_properties.rb +27 -0
- data/lib/zatca/ubl/signing/signature.rb +50 -0
- data/lib/zatca/ubl/signing/signature_information.rb +19 -0
- data/lib/zatca/ubl/signing/signature_properties_reference.rb +26 -0
- data/lib/zatca/ubl/signing/signed_info.rb +21 -0
- data/lib/zatca/ubl/signing/signed_properties.rb +81 -0
- data/lib/zatca/ubl/signing/signed_signature_properties.rb +23 -0
- data/lib/zatca/ubl/signing/ubl_document_signatures.rb +25 -0
- data/lib/zatca/ubl/signing/ubl_extension.rb +22 -0
- data/lib/zatca/ubl/signing/ubl_extensions.rb +17 -0
- data/lib/zatca/ubl/signing.rb +2 -0
- data/lib/zatca/ubl.rb +2 -0
- data/lib/zatca/version.rb +5 -0
- data/lib/zatca.rb +48 -0
- data/zatca_sdk.gemspec +52 -0
- metadata +301 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
class ZATCA::UBL::Signing::KeyInfo < ZATCA::UBL::BaseComponent
|
2
|
+
# <ds:KeyInfo>
|
3
|
+
# <ds:X509Data>
|
4
|
+
# <ds:X509Certificate>MIID9jCCA5ugAwIBAgITbwAAeCy9aKcLA99HrAABAAB4LDAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMDQxOTIwNDkwOVoXDTI0MDQxODIwNDkwOVowWTELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjMxMjM0NTY3ODkxDDAKBgNVBAsTA1RTVDEnMCUGA1UEAxMeVFNULS05NzA1NjAwNDAtMzEyMzQ1Njc4OTAwMDAzMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYYMMoOaFYAhMO/steotfZyavr6p11SSlwsK9azmsLY7b1b+FLhqMArhB2dqHKboxqKNfvkKDePhpqjui5hcn0aOCAjkwggI1MIGaBgNVHREEgZIwgY+kgYwwgYkxOzA5BgNVBAQMMjEtVFNUfDItVFNUfDMtNDdmMTZjMjYtODA2Yi00ZTE1LWIyNjktN2E4MDM4ODRiZTljMR8wHQYKCZImiZPyLGQBAQwPMzEyMzQ1Njc4OTAwMDAzMQ0wCwYDVQQMDAQxMTAwMQwwCgYDVQQaDANUU1QxDDAKBgNVBA8MA1RTVDAdBgNVHQ4EFgQUO5ZiU7NakU3eejVa3I2S1B2sDwkwHwYDVR0jBBgwFoAUdmCM+wagrGdXNZ3PmqynK5k1tS8wTgYDVR0fBEcwRTBDoEGgP4Y9aHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2EvQ2VydEVucm9sbC9UU1pFSU5WT0lDRS1TdWJDQS0xLmNybDCBrQYIKwYBBQUHAQEEgaAwgZ0wbgYIKwYBBQUHMAGGYmh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRWludm9pY2VTQ0ExLmV4dGdhenQuZ292LmxvY2FsX1RTWkVJTlZPSUNFLVN1YkNBLTEoMSkuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9vY3NwMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwMwJwYJKwYBBAGCNxUKBBowGDAKBggrBgEFBQcDAjAKBggrBgEFBQcDAzAKBggqhkjOPQQDAgNJADBGAiEA7mHT6yg85jtQGWp3M7tPT7Jk2+zsvVHGs3bU5Z7YE68CIQD60ebQamYjYvdebnFjNfx4X4dop7LsEBFCNSsLY0IFaQ==</ds:X509Certificate>
|
5
|
+
# </ds:X509Data>
|
6
|
+
# </ds:KeyInfo>
|
7
|
+
|
8
|
+
def initialize(certificate:)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@certificate = certificate
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
"ds:KeyInfo"
|
16
|
+
end
|
17
|
+
|
18
|
+
def elements
|
19
|
+
[
|
20
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:X509Data", elements: [
|
21
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:X509Certificate", value: @certificate)
|
22
|
+
])
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ZATCA::UBL::Signing::Object < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signing_time:, cert_digest_value:, cert_issuer_name:, cert_serial_number:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signing_time = signing_time
|
6
|
+
@cert_digest_value = cert_digest_value
|
7
|
+
@cert_issuer_name = cert_issuer_name
|
8
|
+
@cert_serial_number = cert_serial_number
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
"ds:Object"
|
13
|
+
end
|
14
|
+
|
15
|
+
def elements
|
16
|
+
[
|
17
|
+
ZATCA::UBL::Signing::QualifyingProperties.new(signing_time: @signing_time, cert_digest_value: @cert_digest_value, cert_issuer_name: @cert_issuer_name, cert_serial_number: @cert_serial_number)
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ZATCA::UBL::Signing::QualifyingProperties < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signing_time:, cert_digest_value:, cert_issuer_name:, cert_serial_number:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signing_time = signing_time
|
6
|
+
@cert_digest_value = cert_digest_value
|
7
|
+
@cert_issuer_name = cert_issuer_name
|
8
|
+
@cert_serial_number = cert_serial_number
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
"xades:QualifyingProperties"
|
13
|
+
end
|
14
|
+
|
15
|
+
def attributes
|
16
|
+
{
|
17
|
+
"Target" => "signature",
|
18
|
+
"xmlns:xades" => "http://uri.etsi.org/01903/v1.3.2#"
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def elements
|
23
|
+
[
|
24
|
+
ZATCA::UBL::Signing::SignedProperties.new(signing_time: @signing_time, cert_digest_value: @cert_digest_value, cert_issuer_name: @cert_issuer_name, cert_serial_number: @cert_serial_number)
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class ZATCA::UBL::Signing::Signature < ZATCA::UBL::BaseComponent
|
2
|
+
attr_reader :signing_time, :cert_digest_value, :cert_issuer_name, :cert_serial_number
|
3
|
+
|
4
|
+
def initialize(
|
5
|
+
invoice_hash:, signed_properties_hash:, signature_value:,
|
6
|
+
certificate:, signing_time:, cert_digest_value:, cert_issuer_name:,
|
7
|
+
cert_serial_number:
|
8
|
+
)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@invoice_digest = invoice_hash
|
12
|
+
@signed_properties_hash = signed_properties_hash
|
13
|
+
@signature_value = signature_value
|
14
|
+
@certificate = certificate
|
15
|
+
@signing_time = signing_time
|
16
|
+
@cert_digest_value = cert_digest_value
|
17
|
+
@cert_issuer_name = cert_issuer_name
|
18
|
+
@cert_serial_number = cert_serial_number
|
19
|
+
end
|
20
|
+
|
21
|
+
def attributes
|
22
|
+
{
|
23
|
+
"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#",
|
24
|
+
"Id" => "signature"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def name
|
29
|
+
"ds:Signature"
|
30
|
+
end
|
31
|
+
|
32
|
+
def elements
|
33
|
+
[
|
34
|
+
ZATCA::UBL::Signing::SignedInfo.new(
|
35
|
+
invoice_digest: @invoice_digest,
|
36
|
+
signed_properties_hash: @signed_properties_hash
|
37
|
+
),
|
38
|
+
|
39
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:SignatureValue", value: @signature_value),
|
40
|
+
ZATCA::UBL::Signing::KeyInfo.new(certificate: @certificate),
|
41
|
+
|
42
|
+
ZATCA::UBL::Signing::Object.new(
|
43
|
+
signing_time: @signing_time,
|
44
|
+
cert_digest_value: @cert_digest_value,
|
45
|
+
cert_issuer_name: @cert_issuer_name,
|
46
|
+
cert_serial_number: @cert_serial_number
|
47
|
+
)
|
48
|
+
]
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class ZATCA::UBL::Signing::SignatureInformation < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signature:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signature = signature
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
"sac:SignatureInformation"
|
10
|
+
end
|
11
|
+
|
12
|
+
def elements
|
13
|
+
[
|
14
|
+
ZATCA::UBL::BaseComponent.new(name: "cbc:ID", value: "urn:oasis:names:specification:ubl:signature:1"),
|
15
|
+
ZATCA::UBL::BaseComponent.new(name: "sbc:ReferencedSignatureID", value: "urn:oasis:names:specification:ubl:signature:Invoice"),
|
16
|
+
@signature
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class ZATCA::UBL::Signing::SignaturePropertiesReference < ZATCA::UBL::BaseComponent
|
2
|
+
attr_accessor :digest_value
|
3
|
+
|
4
|
+
def initialize(digest_value:)
|
5
|
+
super()
|
6
|
+
@digest_value = digest_value
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
"ds:Reference"
|
11
|
+
end
|
12
|
+
|
13
|
+
def attributes
|
14
|
+
{
|
15
|
+
"Type" => "http://www.w3.org/2000/09/xmldsig#SignatureProperties",
|
16
|
+
"URI" => "#xadesSignedProperties"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def elements
|
21
|
+
[
|
22
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:DigestMethod", attributes: {"Algorithm" => "http://www.w3.org/2001/04/xmlenc#sha256"}),
|
23
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:DigestValue", value: @digest_value)
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ZATCA::UBL::Signing::SignedInfo < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(invoice_digest:, signed_properties_hash:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@invoice_digest = invoice_digest
|
6
|
+
@signed_properties_hash = signed_properties_hash
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
"ds:SignedInfo"
|
11
|
+
end
|
12
|
+
|
13
|
+
def elements
|
14
|
+
[
|
15
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:CanonicalizationMethod", attributes: {"Algorithm" => "http://www.w3.org/2006/12/xml-c14n11"}),
|
16
|
+
ZATCA::UBL::BaseComponent.new(name: "ds:SignatureMethod", attributes: {"Algorithm" => "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"}),
|
17
|
+
ZATCA::UBL::Signing::InvoiceSignedDataReference.new(digest_value: @invoice_digest),
|
18
|
+
ZATCA::UBL::Signing::SignaturePropertiesReference.new(digest_value: @signed_properties_hash)
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class ZATCA::UBL::Signing::SignedProperties < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(
|
3
|
+
signing_time:,
|
4
|
+
cert_digest_value:,
|
5
|
+
cert_issuer_name:,
|
6
|
+
cert_serial_number:
|
7
|
+
)
|
8
|
+
super()
|
9
|
+
|
10
|
+
@signing_time = signing_time
|
11
|
+
@cert_digest_value = cert_digest_value
|
12
|
+
@cert_issuer_name = cert_issuer_name
|
13
|
+
@cert_serial_number = cert_serial_number
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
"xades:SignedProperties"
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
{
|
22
|
+
"Id" => "xadesSignedProperties"
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def elements
|
27
|
+
[
|
28
|
+
ZATCA::UBL::Signing::SignedSignatureProperties.new(
|
29
|
+
signing_time: @signing_time,
|
30
|
+
cert_digest_value: @cert_digest_value,
|
31
|
+
cert_issuer_name: @cert_issuer_name,
|
32
|
+
cert_serial_number: @cert_serial_number
|
33
|
+
)
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_hash
|
38
|
+
ZATCA::Hashing.generate_hashes(zatca_whitespaced_xml_for_hashing)[:hexdigest_base64]
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# rubocop:disable Layout/HeredocIndentation
|
44
|
+
# rubocop:disable Layout/ClosingHeredocIndentation
|
45
|
+
#
|
46
|
+
# We need this version of the XML with this exact whitespacing because ZATCA
|
47
|
+
# did not opt for canonicalization before hashing (unlike the invoice hash),
|
48
|
+
# and also because they have XML attributes that are only added the moment you
|
49
|
+
# generate the hash but are expected to be removed in all other instances.
|
50
|
+
#
|
51
|
+
# We will use this version of the XML to generate the hash only, and expect
|
52
|
+
# the output of `build_xml` to generate the non-signing version of the XML.
|
53
|
+
# See:
|
54
|
+
# - https://zatca1.discourse.group/t/what-do-signed-properties-look-like-when-hashing
|
55
|
+
# - https://web.archive.org/web/20230925182417/https://zatca1.discourse.group/t/what-do-signed-properties-look-like-when-hashing/717
|
56
|
+
|
57
|
+
def zatca_whitespaced_xml_for_hashing
|
58
|
+
# Note: chomp is used to get rid of the final newline character
|
59
|
+
<<-XML.chomp
|
60
|
+
<xades:SignedProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Id="xadesSignedProperties">
|
61
|
+
<xades:SignedSignatureProperties>
|
62
|
+
<xades:SigningTime>#{@signing_time}</xades:SigningTime>
|
63
|
+
<xades:SigningCertificate>
|
64
|
+
<xades:Cert>
|
65
|
+
<xades:CertDigest>
|
66
|
+
<ds:DigestMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
67
|
+
<ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{@cert_digest_value}</ds:DigestValue>
|
68
|
+
</xades:CertDigest>
|
69
|
+
<xades:IssuerSerial>
|
70
|
+
<ds:X509IssuerName xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{@cert_issuer_name}</ds:X509IssuerName>
|
71
|
+
<ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{@cert_serial_number}</ds:X509SerialNumber>
|
72
|
+
</xades:IssuerSerial>
|
73
|
+
</xades:Cert>
|
74
|
+
</xades:SigningCertificate>
|
75
|
+
</xades:SignedSignatureProperties>
|
76
|
+
</xades:SignedProperties>
|
77
|
+
XML
|
78
|
+
end
|
79
|
+
# rubocop:enable Layout/HeredocIndentation
|
80
|
+
# rubocop:enable Layout/ClosingHeredocIndentation
|
81
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ZATCA::UBL::Signing::SignedSignatureProperties < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signing_time:, cert_digest_value:, cert_issuer_name:, cert_serial_number:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signing_time = signing_time
|
6
|
+
@cert_digest_value = cert_digest_value
|
7
|
+
@cert_issuer_name = cert_issuer_name
|
8
|
+
@cert_serial_number = cert_serial_number
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
"xades:SignedSignatureProperties"
|
13
|
+
end
|
14
|
+
|
15
|
+
def elements
|
16
|
+
[
|
17
|
+
ZATCA::UBL::BaseComponent.new(name: "xades:SigningTime", value: @signing_time),
|
18
|
+
ZATCA::UBL::BaseComponent.new(name: "xades:SigningCertificate", elements: [
|
19
|
+
ZATCA::UBL::Signing::Cert.new(cert_digest_value: @cert_digest_value, cert_issuer_name: @cert_issuer_name, cert_serial_number: @cert_serial_number)
|
20
|
+
])
|
21
|
+
]
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class ZATCA::UBL::Signing::UBLDocumentSignatures < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signature:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signature = signature
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
"sig:UBLDocumentSignatures"
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes
|
13
|
+
{
|
14
|
+
"xmlns:sig" => "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2",
|
15
|
+
"xmlns:sac" => "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2",
|
16
|
+
"xmlns:sbc" => "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def elements
|
21
|
+
[
|
22
|
+
ZATCA::UBL::Signing::SignatureInformation.new(signature: @signature)
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ZATCA::UBL::Signing::UBLExtension < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signature:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signature = signature
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
"ext:UBLExtension"
|
10
|
+
end
|
11
|
+
|
12
|
+
def elements
|
13
|
+
[
|
14
|
+
ZATCA::UBL::BaseComponent.new(name: "ext:ExtensionURI", value: "urn:oasis:names:specification:ubl:dsig:enveloped:xades"),
|
15
|
+
ZATCA::UBL::BaseComponent.new(name: "ext:ExtensionContent", elements: [
|
16
|
+
ZATCA::UBL::Signing::UBLDocumentSignatures.new(signature: @signature)
|
17
|
+
])
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ZATCA::UBL::Signing::UBLExtensions < ZATCA::UBL::BaseComponent
|
2
|
+
def initialize(signature:)
|
3
|
+
super()
|
4
|
+
|
5
|
+
@signature = signature
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
"ext:UBLExtensions"
|
10
|
+
end
|
11
|
+
|
12
|
+
def elements
|
13
|
+
[
|
14
|
+
ZATCA::UBL::Signing::UBLExtension.new(signature: @signature)
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
data/lib/zatca/ubl.rb
ADDED
data/lib/zatca.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "zatca/version"
|
4
|
+
require_relative "zatca/types"
|
5
|
+
|
6
|
+
require "active_support/all"
|
7
|
+
require "zeitwerk"
|
8
|
+
require "dry-initializer"
|
9
|
+
require "dry-types"
|
10
|
+
|
11
|
+
loader = Zeitwerk::Loader.for_gem
|
12
|
+
loader.inflector.inflect(
|
13
|
+
"zatca" => "ZATCA",
|
14
|
+
"qr_code_generator" => "QRCodeGenerator",
|
15
|
+
"qr_code_extractor" => "QRCodeExtractor",
|
16
|
+
"ubl" => "UBL",
|
17
|
+
"csr" => "CSR",
|
18
|
+
"ubl_document_signature" => "UBLDocumentSignature",
|
19
|
+
"ubl_document_signatures" => "UBLDocumentSignatures",
|
20
|
+
"ubl_extension" => "UBLExtension",
|
21
|
+
"ubl_extensions" => "UBLExtensions",
|
22
|
+
"id" => "ID",
|
23
|
+
"ecdsa" => "ECDSA"
|
24
|
+
)
|
25
|
+
loader.setup
|
26
|
+
|
27
|
+
module ZATCA
|
28
|
+
extend self
|
29
|
+
|
30
|
+
class Error < StandardError; end
|
31
|
+
|
32
|
+
def self.extract_qr_base64_from_base64_invoice(base64_invoice)
|
33
|
+
QRCodeExtractor.new(invoice_base64: base64_invoice).extract
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.render_qr_code(tags: nil, base64: nil)
|
37
|
+
if tags.present?
|
38
|
+
tags = ZATCA::Tags.new(tags) if tags.is_a?(Hash)
|
39
|
+
QRCodeGenerator.new(tags: tags).render
|
40
|
+
elsif base64.present?
|
41
|
+
QRCodeGenerator.new(base64: base64).render
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Either tags or base64 must be provided"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
loader.eager_load
|
data/zatca_sdk.gemspec
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/zatca/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "zatca-sdk"
|
7
|
+
spec.version = ZATCA::VERSION
|
8
|
+
spec.authors = ["Omar Bahareth"]
|
9
|
+
spec.email = ["omar@omar.engineer"]
|
10
|
+
|
11
|
+
spec.summary = "A library for generating QR Codes for the e-invoice standard by ZATCA in Saudi Arabia."
|
12
|
+
spec.description = "A library for generating QR Codes for the e-invoice standard by ZATCA in Saudi Arabia."
|
13
|
+
spec.homepage = "https://github.com/obahareth/zatca"
|
14
|
+
spec.required_ruby_version = ">= 2.5.0"
|
15
|
+
|
16
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/obahareth/zatca"
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/obahareth/zatca/releases"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features|einvoicing-sdk)/|\.(?:git|travis|circleci)|appveyor)})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
# Dependencies
|
34
|
+
spec.add_dependency "zeitwerk", "~> 2.4"
|
35
|
+
spec.add_dependency "dry-schema", ">= 1.5"
|
36
|
+
spec.add_dependency "rqrcode", "~> 2.1.0"
|
37
|
+
spec.add_dependency "httpx", "~> 0.21.0"
|
38
|
+
spec.add_dependency "nokogiri", ">= 1.12"
|
39
|
+
spec.add_dependency "activesupport", ">= 5.2"
|
40
|
+
spec.add_dependency "dry-initializer", ">= 3.0"
|
41
|
+
spec.add_dependency "starkbank-ecdsa", "~> 2.0"
|
42
|
+
|
43
|
+
# Development Dependencies
|
44
|
+
spec.add_development_dependency "standard", "~> 1.3"
|
45
|
+
spec.add_development_dependency "byebug", "~> 9.0"
|
46
|
+
spec.add_development_dependency "rspec", "~> 3.10"
|
47
|
+
spec.add_development_dependency "vcr", "~> 6.0.0"
|
48
|
+
spec.add_development_dependency "webmock", "~> 3.18.1"
|
49
|
+
spec.add_development_dependency "amazing_print", "~> 1.4.0"
|
50
|
+
# For more information and examples about making a new gem, checkout our
|
51
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
52
|
+
end
|