sat_mx 0.1.0 → 0.3.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 +4 -4
- data/README.md +83 -24
- data/Rakefile +0 -2
- data/lib/sat_mx/{bulk_download.rb → authentication.rb} +35 -41
- data/lib/sat_mx/body.rb +66 -0
- data/lib/sat_mx/client.rb +70 -0
- data/lib/sat_mx/configuration.rb +14 -0
- data/lib/sat_mx/download_petition.rb +65 -0
- data/lib/sat_mx/download_petition_body.rb +29 -0
- data/lib/sat_mx/download_request.rb +68 -0
- data/lib/sat_mx/download_request_body.rb +60 -0
- data/lib/sat_mx/result.rb +4 -0
- data/lib/sat_mx/signer.rb +26 -0
- data/lib/sat_mx/verify_request.rb +70 -0
- data/lib/sat_mx/verify_request_body.rb +29 -0
- data/lib/sat_mx/version.rb +1 -3
- data/lib/sat_mx.rb +190 -3
- metadata +31 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24d8984a200bf9e2d95a2fcb90e10097f54a4ed496e418c3880df1848dd5c302
|
|
4
|
+
data.tar.gz: 01a221e8d1bcab95919dc4990677ca9c63bc775e1e1f03281c5c07be2653805c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e1905940a68f281204884f848fea09806e6cc7ce242389aba92154e95777d5563b0b400dd731832982f824c626f4353bf204138f75a5ca26b1f4db063adabbe
|
|
7
|
+
data.tar.gz: 3535c79dafbb37cc18fc887a3d266a7ce9f1474e4f0d5add1ae6cfae652c88e64dd72b7fc73867c4d9a82b941c5179a2f447943df63170ee8ad01a0c69f1e18c
|
data/README.md
CHANGED
|
@@ -1,39 +1,98 @@
|
|
|
1
1
|
# SatMx
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sat_mx`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
3
|
+
Ruby client for SAT (Mexican Tax Administration) web services to download CFDI invoices.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
```bash
|
|
8
|
+
gem install sat_mx
|
|
9
|
+
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Or add to your Gemfile:
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```ruby
|
|
14
|
+
gem "sat_mx"
|
|
15
|
+
```
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
## Configuration
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
```ruby
|
|
20
|
+
SatMx.configure do |config|
|
|
21
|
+
config[:certificate] = "path/to/certificate.cer"
|
|
22
|
+
config[:private_key] = "path/to/private.key"
|
|
23
|
+
config[:password] = "key_password"
|
|
24
|
+
end
|
|
25
|
+
```
|
|
18
26
|
|
|
19
27
|
## Usage
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
```ruby
|
|
30
|
+
# 1. Authenticate
|
|
31
|
+
result = SatMx.authenticate
|
|
32
|
+
raise "Auth failed" unless result.success?
|
|
33
|
+
|
|
34
|
+
token = result.value
|
|
35
|
+
|
|
36
|
+
# 2. Request download
|
|
37
|
+
result = SatMx.download_request(
|
|
38
|
+
start_date: Time.new(2024, 1, 1),
|
|
39
|
+
end_date: Time.new(2024, 1, 31),
|
|
40
|
+
request_type: :cfdi,
|
|
41
|
+
issuing_rfc: "ABC010101ABC",
|
|
42
|
+
recipient_rfcs: ["XYZ020202XYZ"],
|
|
43
|
+
requester_rfc: "ABC010101ABC",
|
|
44
|
+
access_token: token
|
|
45
|
+
)
|
|
46
|
+
raise "Request failed" unless result.success?
|
|
47
|
+
|
|
48
|
+
request_id = result.value
|
|
49
|
+
|
|
50
|
+
# 3. Verify status (poll until ready)
|
|
51
|
+
loop do
|
|
52
|
+
result = SatMx.verify_request(
|
|
53
|
+
request_id: request_id,
|
|
54
|
+
requester_rfc: "ABC010101ABC",
|
|
55
|
+
access_token: token
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
case result.value[:request_status]
|
|
59
|
+
when :finished
|
|
60
|
+
break result.value[:package_ids]
|
|
61
|
+
when :error, :rejected, :expired
|
|
62
|
+
raise "Request failed: #{result.value}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
sleep 5
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# 4. Download packages
|
|
69
|
+
package_ids.each do |package_id|
|
|
70
|
+
result = SatMx.download_petition(
|
|
71
|
+
package_id: package_id,
|
|
72
|
+
requester_rfc: "ABC010101ABC",
|
|
73
|
+
access_token: token
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
File.write("#{package_id}.zip", result.value) if result.success?
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API
|
|
81
|
+
|
|
82
|
+
| Method | Description |
|
|
83
|
+
|--------|-------------|
|
|
84
|
+
| `SatMx.configure` | Configure certificate and private key |
|
|
85
|
+
| `SatMx.configuration` | Get current configuration |
|
|
86
|
+
| `SatMx.authenticate` | Get access token |
|
|
87
|
+
| `SatMx.download_request` | Request CFDI download |
|
|
88
|
+
| `SatMx.verify_request` | Check request status |
|
|
89
|
+
| `SatMx.download_petition` | Download package |
|
|
90
|
+
|
|
91
|
+
All methods return a `Result` object with:
|
|
92
|
+
- `success?` - Boolean indicating success
|
|
93
|
+
- `value` - Data on success, error hash `{:cod_estatus, :mensaje}` on failure
|
|
94
|
+
- `xml` - Raw XML response
|
|
32
95
|
|
|
33
96
|
## License
|
|
34
97
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## Code of Conduct
|
|
38
|
-
|
|
39
|
-
Everyone interacting in the SatMx project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/sat_mx/blob/master/CODE_OF_CONDUCT.md).
|
|
98
|
+
MIT
|
data/Rakefile
CHANGED
|
@@ -1,43 +1,39 @@
|
|
|
1
|
-
require "openssl"
|
|
2
1
|
require "httpx"
|
|
3
|
-
require "xmldsig"
|
|
4
2
|
require "time"
|
|
3
|
+
require "base64"
|
|
5
4
|
|
|
6
5
|
module SatMx
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
AUTH_URL = "https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/Autenticacion/Autenticacion.svc".freeze
|
|
11
|
-
HEADERS = {
|
|
12
|
-
"content-type" => "text/xml; charset=utf-8",
|
|
13
|
-
"accept" => "text/xml",
|
|
14
|
-
"SOAPAction" => "http://DescargaMasivaTerceros.gob.mx/IAutenticacion/Autentica"
|
|
15
|
-
}.freeze
|
|
16
|
-
private_constant :AUTH_URL
|
|
17
|
-
private_constant :HEADERS
|
|
18
|
-
|
|
19
|
-
def self.authenticate(certificate:, private_key:, id: nil)
|
|
6
|
+
# @api private
|
|
7
|
+
class Authentication
|
|
8
|
+
def self.authenticate(certificate:, private_key:, uuid: SecureRandom.uuid)
|
|
20
9
|
new(
|
|
21
|
-
xml_auth_body: XmlAuthBody.new(
|
|
10
|
+
xml_auth_body: XmlAuthBody.new(
|
|
11
|
+
certificate:,
|
|
12
|
+
uuid:
|
|
13
|
+
),
|
|
14
|
+
client: Client.new(
|
|
15
|
+
private_key:,
|
|
16
|
+
access_token: ""
|
|
17
|
+
)
|
|
22
18
|
).authenticate
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
def initialize(xml_auth_body:)
|
|
21
|
+
def initialize(xml_auth_body:, client:)
|
|
26
22
|
@xml_auth_body = xml_auth_body
|
|
23
|
+
@client = client
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
def authenticate
|
|
30
|
-
response =
|
|
31
|
-
AUTH_URL,
|
|
32
|
-
headers: HEADERS,
|
|
33
|
-
body: xml_auth_body.sign
|
|
34
|
-
)
|
|
27
|
+
response = client.authenticate(xml_auth_body.generate)
|
|
35
28
|
|
|
36
29
|
case response.status
|
|
37
30
|
when 200..299
|
|
38
|
-
Result.new(success?: true,
|
|
31
|
+
Result.new(success?: true,
|
|
32
|
+
value: response.xml.xpath("//xmlns:AutenticaResult",
|
|
33
|
+
xmlns: "http://DescargaMasivaTerceros.gob.mx").inner_text,
|
|
34
|
+
xml: response.xml)
|
|
39
35
|
when 400..599
|
|
40
|
-
Result.new(success?: false, value: response.xml)
|
|
36
|
+
Result.new(success?: false, value: nil, xml: response.xml)
|
|
41
37
|
else
|
|
42
38
|
SatMx::Error
|
|
43
39
|
end
|
|
@@ -45,39 +41,29 @@ module SatMx
|
|
|
45
41
|
|
|
46
42
|
private
|
|
47
43
|
|
|
48
|
-
attr_reader :xml_auth_body
|
|
44
|
+
attr_reader :xml_auth_body, :client
|
|
49
45
|
end
|
|
50
46
|
|
|
47
|
+
# @api private
|
|
51
48
|
class XmlAuthBody
|
|
52
|
-
def initialize(certificate:,
|
|
49
|
+
def initialize(certificate:, uuid:)
|
|
53
50
|
@certificate = certificate
|
|
54
|
-
@
|
|
55
|
-
@id = id
|
|
51
|
+
@uuid = uuid
|
|
56
52
|
end
|
|
57
53
|
|
|
58
|
-
def
|
|
59
|
-
Xmldsig::SignedDocument.new(xml_document).sign do |data|
|
|
60
|
-
private_key.sign(OpenSSL::Digest.new("SHA1"), data)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
attr_reader :private_key, :certificate, :id
|
|
67
|
-
|
|
68
|
-
def xml_document
|
|
54
|
+
def generate
|
|
69
55
|
<<~XML
|
|
70
56
|
<S11:Envelope xmlns:S11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
|
|
71
57
|
<S11:Header>
|
|
72
58
|
<wsse:Security S11:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
|
73
59
|
#{timestamp}
|
|
74
|
-
<wsse:BinarySecurityToken wsu:Id="#{
|
|
60
|
+
<wsse:BinarySecurityToken wsu:Id="#{uuid}" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">#{Base64.strict_encode64(certificate.to_der)}</wsse:BinarySecurityToken>
|
|
75
61
|
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
|
76
62
|
#{signed_info}
|
|
77
63
|
<SignatureValue></SignatureValue>
|
|
78
64
|
<KeyInfo>
|
|
79
65
|
<wsse:SecurityTokenReference>
|
|
80
|
-
<wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="##{
|
|
66
|
+
<wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="##{uuid}" />
|
|
81
67
|
</wsse:SecurityTokenReference>
|
|
82
68
|
</KeyInfo>
|
|
83
69
|
</Signature>
|
|
@@ -90,6 +76,10 @@ module SatMx
|
|
|
90
76
|
XML
|
|
91
77
|
end
|
|
92
78
|
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
attr_reader :certificate
|
|
82
|
+
|
|
93
83
|
def timestamp
|
|
94
84
|
current_time = Time.now.utc
|
|
95
85
|
<<~XML
|
|
@@ -115,5 +105,9 @@ module SatMx
|
|
|
115
105
|
</SignedInfo>
|
|
116
106
|
XML
|
|
117
107
|
end
|
|
108
|
+
|
|
109
|
+
def uuid
|
|
110
|
+
"uuid-#{@uuid}-1"
|
|
111
|
+
end
|
|
118
112
|
end
|
|
119
113
|
end
|
data/lib/sat_mx/body.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
|
|
3
|
+
module SatMx
|
|
4
|
+
# @api private
|
|
5
|
+
module Body
|
|
6
|
+
S11 = "S11"
|
|
7
|
+
XMLNS = "xmlns"
|
|
8
|
+
DES = "des"
|
|
9
|
+
DS = "ds"
|
|
10
|
+
|
|
11
|
+
ENVELOPE_ATTRS = {
|
|
12
|
+
"#{XMLNS}:#{S11}" => "http://schemas.xmlsoap.org/soap/envelope/",
|
|
13
|
+
"#{XMLNS}:#{DES}" => "http://DescargaMasivaTerceros.sat.gob.mx",
|
|
14
|
+
"#{XMLNS}:#{DS}" => "http://www.w3.org/2000/09/xmldsig#"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
NAMESPACE = ENVELOPE_ATTRS["#{XMLNS}:#{DES}"]
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def envelope
|
|
22
|
+
Nokogiri::XML::Builder.new do |xml|
|
|
23
|
+
xml[S11].Envelope(
|
|
24
|
+
ENVELOPE_ATTRS
|
|
25
|
+
) do
|
|
26
|
+
xml[S11].Header
|
|
27
|
+
xml[S11].Body do
|
|
28
|
+
yield xml
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end.doc.root.to_xml
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def signature(xml)
|
|
35
|
+
xml.Signature(XMLNS => "http://www.w3.org/2000/09/xmldsig#") do
|
|
36
|
+
xml.SignedInfo do
|
|
37
|
+
xml.CanonicalizationMethod("Algorithm" => "http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
|
|
38
|
+
xml.SignatureMethod("Algorithm" => "http://www.w3.org/2000/09/xmldsig#rsa-sha1")
|
|
39
|
+
xml.Reference("URI" => "") do
|
|
40
|
+
xml.Transforms do
|
|
41
|
+
xml.Transform("Algorithm" => "http://www.w3.org/2000/09/xmldsig#enveloped-signature")
|
|
42
|
+
end
|
|
43
|
+
xml.DigestMethod("Algorithm" => "http://www.w3.org/2000/09/xmldsig#sha1")
|
|
44
|
+
xml.DigestValue
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
xml.SignatureValue
|
|
48
|
+
xml.KeyInfo do
|
|
49
|
+
xml.X509Data do
|
|
50
|
+
xml.X509IssuerSerial do
|
|
51
|
+
xml.X509IssuerName(certificate_issuer)
|
|
52
|
+
xml.X509SerialNumber(certificate_serial)
|
|
53
|
+
end
|
|
54
|
+
xml.X509Certificate(encoded_certificate)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def certificate_issuer = certificate.issuer.to_s(OpenSSL::X509::Name::RFC2253)
|
|
61
|
+
|
|
62
|
+
def certificate_serial = certificate.serial
|
|
63
|
+
|
|
64
|
+
def encoded_certificate = Base64.strict_encode64(certificate.to_der)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class Client
|
|
4
|
+
HEADERS = {
|
|
5
|
+
"content-type" => "text/xml; charset=utf-8",
|
|
6
|
+
"accept" => "text/xml"
|
|
7
|
+
}.freeze
|
|
8
|
+
private_constant :HEADERS
|
|
9
|
+
|
|
10
|
+
def initialize(private_key:, access_token:)
|
|
11
|
+
@private_key = private_key
|
|
12
|
+
@access_token = access_token
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def authenticate(payload)
|
|
16
|
+
HTTPX.post(
|
|
17
|
+
"https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/Autenticacion/Autenticacion.svc",
|
|
18
|
+
headers: {
|
|
19
|
+
"SOAPAction" => "http://DescargaMasivaTerceros.gob.mx/IAutenticacion/Autentica"
|
|
20
|
+
}.merge(HEADERS),
|
|
21
|
+
body: sign(payload)
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def download_request(payload)
|
|
26
|
+
HTTPX.post(
|
|
27
|
+
"https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/SolicitaDescargaService.svc",
|
|
28
|
+
headers: {
|
|
29
|
+
"SOAPAction" => "http://DescargaMasivaTerceros.sat.gob.mx/ISolicitaDescargaService/SolicitaDescarga"
|
|
30
|
+
}.merge(authorization)
|
|
31
|
+
.merge(HEADERS),
|
|
32
|
+
body: sign(payload)
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def verify_request(payload)
|
|
37
|
+
HTTPX.post(
|
|
38
|
+
"https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/VerificaSolicitudDescargaService.svc",
|
|
39
|
+
headers: {
|
|
40
|
+
"SOAPAction" => "http://DescargaMasivaTerceros.sat.gob.mx/IVerificaSolicitudDescargaService/VerificaSolicitudDescarga"
|
|
41
|
+
}.merge(authorization)
|
|
42
|
+
.merge(HEADERS),
|
|
43
|
+
body: sign(payload)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def download_petition(payload)
|
|
48
|
+
HTTPX.post(
|
|
49
|
+
"https://cfdidescargamasiva.clouda.sat.gob.mx/DescargaMasivaService.svc",
|
|
50
|
+
headers: {
|
|
51
|
+
"SOAPAction" => "http://DescargaMasivaTerceros.sat.gob.mx/IDescargaMasivaTercerosService/Descargar"
|
|
52
|
+
}.merge(authorization)
|
|
53
|
+
.merge(HEADERS),
|
|
54
|
+
body: sign(payload)
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
attr_reader :private_key, :access_token
|
|
61
|
+
|
|
62
|
+
def authorization
|
|
63
|
+
{"Authorization" => "WRAP access_token=\"#{access_token}\""}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def sign(payload)
|
|
67
|
+
Signer.sign(document: payload, private_key:)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
Configuration = Data.define(:certificate, :private_key) do
|
|
4
|
+
def initialize(certificate:, private_key:, password:)
|
|
5
|
+
super(
|
|
6
|
+
certificate: OpenSSL::X509::Certificate.new(File.read(certificate)),
|
|
7
|
+
private_key: OpenSSL::PKey::RSA.new(
|
|
8
|
+
File.read(private_key),
|
|
9
|
+
password
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class DownloadPetition
|
|
4
|
+
def self.call(package_id:, requester_rfc:, access_token:, certificate:, private_key:)
|
|
5
|
+
new(
|
|
6
|
+
body: DownloadPetitionBody.new(
|
|
7
|
+
package_id:,
|
|
8
|
+
requester_rfc:,
|
|
9
|
+
certificate:
|
|
10
|
+
),
|
|
11
|
+
client: Client.new(private_key:, access_token:)
|
|
12
|
+
).call
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(body:, client:)
|
|
16
|
+
@body = body
|
|
17
|
+
@client = client
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
response = client.download_petition(body.generate)
|
|
22
|
+
|
|
23
|
+
case response.status
|
|
24
|
+
when 200..299
|
|
25
|
+
xml = response.xml
|
|
26
|
+
response_tag = xml.xpath(
|
|
27
|
+
"//xmlns:respuesta",
|
|
28
|
+
xmlns: "http://DescargaMasivaTerceros.sat.gob.mx"
|
|
29
|
+
)[0]
|
|
30
|
+
|
|
31
|
+
if response_tag["CodEstatus"] == "5000"
|
|
32
|
+
Result.new(
|
|
33
|
+
success?: true,
|
|
34
|
+
xml: response.xml,
|
|
35
|
+
value: response.xml.xpath(
|
|
36
|
+
"//xmlns:Paquete",
|
|
37
|
+
xmlns: "http://DescargaMasivaTerceros.sat.gob.mx"
|
|
38
|
+
).inner_text
|
|
39
|
+
)
|
|
40
|
+
else
|
|
41
|
+
Result.new(
|
|
42
|
+
success?: false,
|
|
43
|
+
xml: response.xml,
|
|
44
|
+
value: {
|
|
45
|
+
cod_estatus: response_tag["CodEstatus"],
|
|
46
|
+
mensaje: response_tag["Mensaje"]
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
when 400..599
|
|
51
|
+
Result.new(
|
|
52
|
+
success?: false,
|
|
53
|
+
xml: nil,
|
|
54
|
+
value: nil
|
|
55
|
+
)
|
|
56
|
+
else
|
|
57
|
+
SatMx::Error
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
attr_reader :client, :body
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class DownloadPetitionBody
|
|
4
|
+
include Body
|
|
5
|
+
|
|
6
|
+
def initialize(package_id:, requester_rfc:, certificate:)
|
|
7
|
+
@package_id = package_id
|
|
8
|
+
@requester_rfc = requester_rfc
|
|
9
|
+
@certificate = certificate
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate
|
|
13
|
+
envelope do |xml|
|
|
14
|
+
xml[Body::DES].PeticionDescargaMasivaTercerosEntrada do
|
|
15
|
+
xml[Body::DES].peticionDescarga(
|
|
16
|
+
IdPaquete: package_id,
|
|
17
|
+
RfcSolicitante: requester_rfc
|
|
18
|
+
) do
|
|
19
|
+
signature(xml)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :package_id, :requester_rfc, :certificate
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class DownloadRequest
|
|
4
|
+
def self.call(start_date:,
|
|
5
|
+
end_date:,
|
|
6
|
+
request_type:,
|
|
7
|
+
issuing_rfc:,
|
|
8
|
+
recipient_rfcs:,
|
|
9
|
+
requester_rfc:,
|
|
10
|
+
access_token:,
|
|
11
|
+
certificate:,
|
|
12
|
+
private_key:)
|
|
13
|
+
new(
|
|
14
|
+
download_request_body: DownloadRequestBody.new(
|
|
15
|
+
start_date:,
|
|
16
|
+
end_date:,
|
|
17
|
+
request_type:,
|
|
18
|
+
issuing_rfc:,
|
|
19
|
+
recipient_rfcs:,
|
|
20
|
+
requester_rfc:,
|
|
21
|
+
certificate:
|
|
22
|
+
),
|
|
23
|
+
client: Client.new(private_key:, access_token:)
|
|
24
|
+
).call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(download_request_body:, client:)
|
|
28
|
+
@download_request_body = download_request_body
|
|
29
|
+
@client = client
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call
|
|
33
|
+
response = client.download_request(download_request_body.generate)
|
|
34
|
+
|
|
35
|
+
case response.status
|
|
36
|
+
when 200..299
|
|
37
|
+
check_body_status response.xml
|
|
38
|
+
when 400..599
|
|
39
|
+
Result.new(success?: false, value: nil, xml: response.xml)
|
|
40
|
+
else
|
|
41
|
+
SatMx::Error
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
attr_reader :download_request_body, :client
|
|
48
|
+
|
|
49
|
+
def check_body_status(xml)
|
|
50
|
+
download_result_tag = xml.xpath("//xmlns:SolicitaDescargaResult",
|
|
51
|
+
xmlns: Body::NAMESPACE)
|
|
52
|
+
if download_result_tag.attr("CodEstatus").value == "5000"
|
|
53
|
+
Result.new(success?: true,
|
|
54
|
+
value: download_result_tag.attr("IdSolicitud").value,
|
|
55
|
+
xml: xml)
|
|
56
|
+
else
|
|
57
|
+
Result.new(
|
|
58
|
+
success?: false,
|
|
59
|
+
value: {
|
|
60
|
+
cod_estatus: download_result_tag.attr("CodEstatus").value,
|
|
61
|
+
mensaje: download_result_tag.attr("Mensaje").value
|
|
62
|
+
},
|
|
63
|
+
xml:
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
module SatMx
|
|
4
|
+
# @api private
|
|
5
|
+
class DownloadRequestBody
|
|
6
|
+
include Body
|
|
7
|
+
|
|
8
|
+
REQUEST_TYPES = {
|
|
9
|
+
cfdi: "CFDI",
|
|
10
|
+
metadata: "Metadata"
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def initialize(start_date:,
|
|
14
|
+
end_date:,
|
|
15
|
+
request_type:,
|
|
16
|
+
issuing_rfc:,
|
|
17
|
+
recipient_rfcs:,
|
|
18
|
+
requester_rfc:,
|
|
19
|
+
certificate:)
|
|
20
|
+
@start_date = start_date
|
|
21
|
+
@end_date = end_date
|
|
22
|
+
@request_type = request_type
|
|
23
|
+
@issuing_rfc = issuing_rfc
|
|
24
|
+
@recipient_rfcs = recipient_rfcs
|
|
25
|
+
@requester_rfc = requester_rfc
|
|
26
|
+
@certificate = certificate
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def generate
|
|
30
|
+
envelope do |xml|
|
|
31
|
+
xml[Body::DES].SolicitaDescarga do
|
|
32
|
+
xml[Body::DES].solicitud(
|
|
33
|
+
"FechaInicial" => start_date,
|
|
34
|
+
"FechaFinal" => end_date,
|
|
35
|
+
"RfcEmisor" => issuing_rfc,
|
|
36
|
+
"RfcSolicitante" => requester_rfc,
|
|
37
|
+
"TipoSolicitud" => request_type
|
|
38
|
+
) do
|
|
39
|
+
xml[Body::DES].RfcReceptores do
|
|
40
|
+
@recipient_rfcs.each do |rfc|
|
|
41
|
+
xml[Body::DES].RfcReceptor(rfc)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
signature(xml)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
attr_reader :issuing_rfc, :requester_rfc, :certificate
|
|
53
|
+
|
|
54
|
+
def start_date = @start_date.strftime("%Y-%m-%dT%H:%M:%S")
|
|
55
|
+
|
|
56
|
+
def end_date = @end_date.strftime("%Y-%m-%dT%H:%M:%S")
|
|
57
|
+
|
|
58
|
+
def request_type = REQUEST_TYPES.fetch(@request_type)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "openssl"
|
|
2
|
+
require "xmldsig"
|
|
3
|
+
|
|
4
|
+
module SatMx
|
|
5
|
+
# @api private
|
|
6
|
+
class Signer
|
|
7
|
+
def self.sign(document:, private_key:)
|
|
8
|
+
new(document:, private_key:).sign
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(document:, private_key:)
|
|
12
|
+
@unsigned_document = Xmldsig::SignedDocument.new(document)
|
|
13
|
+
@private_key = private_key
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sign
|
|
17
|
+
unsigned_document.sign do |data|
|
|
18
|
+
private_key.sign(OpenSSL::Digest.new("SHA1"), data)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader :unsigned_document, :private_key
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class VerifyRequest
|
|
4
|
+
STATUS = {
|
|
5
|
+
"1" => :accepted,
|
|
6
|
+
"2" => :in_proccess,
|
|
7
|
+
"3" => :finished,
|
|
8
|
+
"4" => :error,
|
|
9
|
+
"5" => :rejected,
|
|
10
|
+
"6" => :expired
|
|
11
|
+
}
|
|
12
|
+
private_constant :STATUS
|
|
13
|
+
|
|
14
|
+
def self.call(request_id:, requester_rfc:, access_token:, private_key:, certificate:)
|
|
15
|
+
new(
|
|
16
|
+
body: VerifyRequestBody.new(request_id:, requester_rfc:, certificate:),
|
|
17
|
+
client: Client.new(private_key:, access_token:)
|
|
18
|
+
).call
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(body:, client:)
|
|
22
|
+
@body = body
|
|
23
|
+
@client = client
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
response = client.verify_request(body.generate)
|
|
28
|
+
case response.status
|
|
29
|
+
when 200..299
|
|
30
|
+
check_body_status(response.xml)
|
|
31
|
+
when 400..599
|
|
32
|
+
Result.new(success?: false, value: nil, xml: response.xml)
|
|
33
|
+
else
|
|
34
|
+
SatMx::Error
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
attr_reader :body, :client
|
|
41
|
+
|
|
42
|
+
def check_body_status(xml)
|
|
43
|
+
download_result_tag = xml.xpath("//xmlns:VerificaSolicitudDescargaResult",
|
|
44
|
+
xmlns: Body::NAMESPACE)
|
|
45
|
+
if download_result_tag.attr("CodEstatus").value == "5000"
|
|
46
|
+
Result.new(success?: true,
|
|
47
|
+
value: value(download_result_tag, xml),
|
|
48
|
+
xml: xml)
|
|
49
|
+
else
|
|
50
|
+
Result.new(
|
|
51
|
+
success?: false,
|
|
52
|
+
value: {
|
|
53
|
+
cod_status: download_result_tag.attr("CodEstatus").value,
|
|
54
|
+
mensaje: download_result_tag.attr("Mensaje").value
|
|
55
|
+
},
|
|
56
|
+
xml:
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def value(tag, xml)
|
|
62
|
+
{
|
|
63
|
+
request_status: STATUS.fetch(
|
|
64
|
+
tag.attribute("EstadoSolicitud").value
|
|
65
|
+
),
|
|
66
|
+
package_ids: xml.xpath("//xmlns:IdsPaquetes", xmlns: Body::NAMESPACE).map(&:inner_text)
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SatMx
|
|
2
|
+
# @api private
|
|
3
|
+
class VerifyRequestBody
|
|
4
|
+
include Body
|
|
5
|
+
|
|
6
|
+
def initialize(certificate:, request_id:, requester_rfc:)
|
|
7
|
+
@certificate = certificate
|
|
8
|
+
@request_id = request_id
|
|
9
|
+
@requester_rfc = requester_rfc
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate
|
|
13
|
+
envelope do |xml|
|
|
14
|
+
xml[Body::DES].VerificaSolicitudDescarga do
|
|
15
|
+
xml[Body::DES].solicitud(
|
|
16
|
+
"IdSolicitud" => request_id,
|
|
17
|
+
"RfcSolicitante" => requester_rfc
|
|
18
|
+
) do
|
|
19
|
+
signature(xml)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :certificate, :request_id, :requester_rfc
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/sat_mx/version.rb
CHANGED
data/lib/sat_mx.rb
CHANGED
|
@@ -1,8 +1,195 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require "httpx"
|
|
3
2
|
require_relative "sat_mx/version"
|
|
4
3
|
|
|
5
4
|
module SatMx
|
|
6
5
|
class Error < StandardError; end
|
|
7
|
-
autoload(:
|
|
6
|
+
autoload(:Configuration, "sat_mx/configuration")
|
|
7
|
+
autoload(:Authentication, "sat_mx/authentication")
|
|
8
|
+
autoload(:DownloadRequest, "sat_mx/download_request")
|
|
9
|
+
autoload(:DownloadRequestBody, "sat_mx/download_request_body")
|
|
10
|
+
autoload(:VerifyRequest, "sat_mx/verify_request")
|
|
11
|
+
autoload(:VerifyRequestBody, "sat_mx/verify_request_body")
|
|
12
|
+
autoload(:DownloadPetition, "sat_mx/download_petition")
|
|
13
|
+
autoload(:DownloadPetitionBody, "sat_mx/download_petition_body")
|
|
14
|
+
autoload(:Body, "sat_mx/body")
|
|
15
|
+
autoload(:Result, "sat_mx/result")
|
|
16
|
+
autoload(:Signer, "sat_mx/signer")
|
|
17
|
+
autoload(:Client, "sat_mx/client")
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
# Configures the gem using a block, its not threadsafe, so its recommended call only when you're initializing
|
|
21
|
+
# your application, e.g. in your initializers directory of your rails app
|
|
22
|
+
# @example
|
|
23
|
+
# SatMx.configure do |config|
|
|
24
|
+
# config[:certificate] = "path/to/certificate.cer"
|
|
25
|
+
# config[:private_key] = "path/to/private.key"
|
|
26
|
+
# config[:password] = "key_password"
|
|
27
|
+
# end
|
|
28
|
+
def configure
|
|
29
|
+
config = {}
|
|
30
|
+
yield(config)
|
|
31
|
+
@configuration = Configuration.new(**config)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def configuration
|
|
35
|
+
@configuration ||= Configuration.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Authenticates with the SAT web service using the configured certificate and private key.
|
|
39
|
+
# This method uses SOAP to communicate with the SAT authentication service and returns
|
|
40
|
+
# a token that can be used for subsequent requests.
|
|
41
|
+
#
|
|
42
|
+
# result = SatMx.authenticate
|
|
43
|
+
# if result.success?
|
|
44
|
+
# puts "Authentication token: #{result.value}"
|
|
45
|
+
# else
|
|
46
|
+
# puts "Authentication failed"
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
# @param certificate [OpenSSL::X509::Certificate, nil] Certificate object (uses configuration if nil)
|
|
50
|
+
# @param private_key [OpenSSL::PKey::RSA, nil] Private key object (uses configuration if nil)
|
|
51
|
+
#
|
|
52
|
+
# @return [SatMx::Result] A Result object containing:
|
|
53
|
+
# - success?: [Boolean] whether the authentication was successful
|
|
54
|
+
# - value: [String, nil] the authentication token if successful, nil otherwise
|
|
55
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
56
|
+
#
|
|
57
|
+
# @see SatMx::Authentication
|
|
58
|
+
# @see SatMx::Result
|
|
59
|
+
def authenticate(certificate: nil, private_key: nil)
|
|
60
|
+
cert = certificate || configuration.certificate
|
|
61
|
+
key = private_key || configuration.private_key
|
|
62
|
+
Authentication.authenticate(
|
|
63
|
+
certificate: cert,
|
|
64
|
+
private_key: key
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Requests a download of CFDI documents from the SAT web service.
|
|
69
|
+
#
|
|
70
|
+
# result = SatMx.download_request(
|
|
71
|
+
# start_date: Time.new(2024, 1, 1),
|
|
72
|
+
# end_date: Time.new(2024, 1, 31),
|
|
73
|
+
# request_type: :cfdi,
|
|
74
|
+
# issuing_rfc: "ABC010101ABC",
|
|
75
|
+
# recipient_rfcs: ["XYZ020202XYZ"],
|
|
76
|
+
# requester_rfc: "ABC010101ABC",
|
|
77
|
+
# access_token: "your_access_token"
|
|
78
|
+
# )
|
|
79
|
+
# if result.success?
|
|
80
|
+
# puts "Request ID: #{result.value}"
|
|
81
|
+
# else
|
|
82
|
+
# puts "Request failed: #{result.value}"
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
# @param start_date [Time] Start date for the search range
|
|
86
|
+
# @param end_date [Time] End date for the search range
|
|
87
|
+
# @param request_type [Symbol] Type of request (:cfdi or :retentions)
|
|
88
|
+
# @param issuing_rfc [String] RFC of the issuer
|
|
89
|
+
# @param recipient_rfcs [Array<String>] RFCs of the recipients
|
|
90
|
+
# @param requester_rfc [String] RFC of the requester
|
|
91
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
92
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
93
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
94
|
+
#
|
|
95
|
+
# @return [SatMx::Result] A Result object containing:
|
|
96
|
+
# - success?: [Boolean] whether the request was successful
|
|
97
|
+
# - value: [String, nil] the request ID if successful, or {cod_estatus:, mensaje:} on failure
|
|
98
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
99
|
+
#
|
|
100
|
+
# @see SatMx::DownloadRequest
|
|
101
|
+
# @see SatMx::Result
|
|
102
|
+
def download_request(start_date:, end_date:, request_type:, issuing_rfc:, recipient_rfcs:, requester_rfc:, access_token:, **options)
|
|
103
|
+
certificate = options[:certificate] || configuration.certificate
|
|
104
|
+
private_key = options[:private_key] || configuration.private_key
|
|
105
|
+
DownloadRequest.call(
|
|
106
|
+
start_date:,
|
|
107
|
+
end_date:,
|
|
108
|
+
request_type:,
|
|
109
|
+
issuing_rfc:,
|
|
110
|
+
recipient_rfcs:,
|
|
111
|
+
requester_rfc:,
|
|
112
|
+
access_token:,
|
|
113
|
+
certificate:,
|
|
114
|
+
private_key:
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Verifies the status of a previously submitted download request.
|
|
119
|
+
#
|
|
120
|
+
# result = SatMx.verify_request(
|
|
121
|
+
# request_id: "606c5667-345a-4630-8979-0769734ac80b",
|
|
122
|
+
# requester_rfc: "ABC010101ABC",
|
|
123
|
+
# access_token: "your_access_token"
|
|
124
|
+
# )
|
|
125
|
+
# if result.success?
|
|
126
|
+
# puts "Status: #{result.value[:request_status]}"
|
|
127
|
+
# puts "Packages: #{result.value[:package_ids]}"
|
|
128
|
+
# else
|
|
129
|
+
# puts "Verification failed: #{result.value}"
|
|
130
|
+
# end
|
|
131
|
+
#
|
|
132
|
+
# @param request_id [String] The ID returned from SatMx.download_request
|
|
133
|
+
# @param requester_rfc [String] RFC of the requester
|
|
134
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
135
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
136
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
137
|
+
#
|
|
138
|
+
# @return [SatMx::Result] A Result object containing:
|
|
139
|
+
# - success?: [Boolean] whether the verification was successful
|
|
140
|
+
# - value: [Hash, nil] containing :request_status and :package_ids if successful, or {cod_estatus:, mensaje:} on failure
|
|
141
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
142
|
+
#
|
|
143
|
+
# @see SatMx::VerifyRequest
|
|
144
|
+
# @see SatMx::Result
|
|
145
|
+
def verify_request(request_id:, requester_rfc:, access_token:, **options)
|
|
146
|
+
certificate = options[:certificate] || configuration.certificate
|
|
147
|
+
private_key = options[:private_key] || configuration.private_key
|
|
148
|
+
VerifyRequest.call(
|
|
149
|
+
request_id:,
|
|
150
|
+
requester_rfc:,
|
|
151
|
+
access_token:,
|
|
152
|
+
certificate:,
|
|
153
|
+
private_key:
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Downloads a package of CFDI documents from the SAT web service.
|
|
158
|
+
#
|
|
159
|
+
# result = SatMx.download_petition(
|
|
160
|
+
# package_id: "18015570-C084-4BE8-BE36-476F5D46A133_01",
|
|
161
|
+
# requester_rfc: "ABC010101ABC",
|
|
162
|
+
# access_token: "your_access_token"
|
|
163
|
+
# )
|
|
164
|
+
# if result.success?
|
|
165
|
+
# File.write("package.zip", result.value)
|
|
166
|
+
# else
|
|
167
|
+
# puts "Download failed: #{result.value}"
|
|
168
|
+
# end
|
|
169
|
+
#
|
|
170
|
+
# @param package_id [String] The package ID from SatMx.verify_request
|
|
171
|
+
# @param requester_rfc [String] RFC of the requester
|
|
172
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
173
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
174
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
175
|
+
#
|
|
176
|
+
# @return [SatMx::Result] A Result object containing:
|
|
177
|
+
# - success?: [Boolean] whether the download was successful
|
|
178
|
+
# - value: [String, nil] Base64 encoded ZIP content if successful, or {cod_estatus:, mensaje:} on failure
|
|
179
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
180
|
+
#
|
|
181
|
+
# @see SatMx::DownloadPetition
|
|
182
|
+
# @see SatMx::Result
|
|
183
|
+
def download_petition(package_id:, requester_rfc:, access_token:, **options)
|
|
184
|
+
certificate = options[:certificate] || configuration.certificate
|
|
185
|
+
private_key = options[:private_key] || configuration.private_key
|
|
186
|
+
DownloadPetition.call(
|
|
187
|
+
package_id:,
|
|
188
|
+
requester_rfc:,
|
|
189
|
+
access_token:,
|
|
190
|
+
certificate:,
|
|
191
|
+
private_key:
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
8
195
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sat_mx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oscar Rivas
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-02-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: xmldsig
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '1.3'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: base64
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "<"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "<"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: standard
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,7 +123,18 @@ files:
|
|
|
109
123
|
- README.md
|
|
110
124
|
- Rakefile
|
|
111
125
|
- lib/sat_mx.rb
|
|
112
|
-
- lib/sat_mx/
|
|
126
|
+
- lib/sat_mx/authentication.rb
|
|
127
|
+
- lib/sat_mx/body.rb
|
|
128
|
+
- lib/sat_mx/client.rb
|
|
129
|
+
- lib/sat_mx/configuration.rb
|
|
130
|
+
- lib/sat_mx/download_petition.rb
|
|
131
|
+
- lib/sat_mx/download_petition_body.rb
|
|
132
|
+
- lib/sat_mx/download_request.rb
|
|
133
|
+
- lib/sat_mx/download_request_body.rb
|
|
134
|
+
- lib/sat_mx/result.rb
|
|
135
|
+
- lib/sat_mx/signer.rb
|
|
136
|
+
- lib/sat_mx/verify_request.rb
|
|
137
|
+
- lib/sat_mx/verify_request_body.rb
|
|
113
138
|
- lib/sat_mx/version.rb
|
|
114
139
|
- sig/sat_mx.rbs
|
|
115
140
|
homepage: https://github.com/kadru/sat_mx
|
|
@@ -118,7 +143,7 @@ licenses:
|
|
|
118
143
|
metadata:
|
|
119
144
|
homepage_uri: https://github.com/kadru/sat_mx
|
|
120
145
|
changelog_uri: https://github.com/kadru/sat_mx/CHANGELOG.md
|
|
121
|
-
post_install_message:
|
|
146
|
+
post_install_message:
|
|
122
147
|
rdoc_options: []
|
|
123
148
|
require_paths:
|
|
124
149
|
- lib
|
|
@@ -134,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
159
|
version: '0'
|
|
135
160
|
requirements: []
|
|
136
161
|
rubygems_version: 3.5.11
|
|
137
|
-
signing_key:
|
|
162
|
+
signing_key:
|
|
138
163
|
specification_version: 4
|
|
139
164
|
summary: a client to connect to SAT web services
|
|
140
165
|
test_files: []
|