sat_mx 0.2.0 → 0.4.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 +84 -24
- data/lib/sat_mx/authentication.rb +2 -0
- data/lib/sat_mx/body.rb +1 -0
- data/lib/sat_mx/client.rb +12 -0
- data/lib/sat_mx/configuration.rb +1 -0
- data/lib/sat_mx/download_petition.rb +3 -2
- data/lib/sat_mx/download_petition_body.rb +1 -0
- data/lib/sat_mx/download_request.rb +3 -2
- data/lib/sat_mx/download_request_body.rb +1 -0
- data/lib/sat_mx/download_request_received.rb +110 -0
- data/lib/sat_mx/download_request_received_body.rb +112 -0
- data/lib/sat_mx/result.rb +1 -0
- data/lib/sat_mx/signer.rb +1 -0
- data/lib/sat_mx/verify_request.rb +3 -2
- data/lib/sat_mx/verify_request_body.rb +1 -0
- data/lib/sat_mx/version.rb +1 -1
- data/lib/sat_mx.rb +202 -3
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6f23a9065a4facc1ea12963a7a27587c8055775cbb729e3070c0a5a2a8144cd
|
|
4
|
+
data.tar.gz: c043c861ed94a4cb049b3664f37b6a3b13e502917e28dd7b6f07608f15212485
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a4ca5be6421a81e686b084207556f320fc4294d0d9731d9febd8e4d303a49bf10dcbfde496d86d2a8eef72fb172d69b7948ae2812e79f9c950d6749e038c8d8
|
|
7
|
+
data.tar.gz: 6dfb7a7364d74260cb0eb5e804b19b2e113e3fbfd8a5a61baee671e22cb15a109618decae7c7851b941e07901e61b93ec9ba12ede074b736e08fe355717bc227
|
data/README.md
CHANGED
|
@@ -1,39 +1,99 @@
|
|
|
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 of received CFDI
|
|
37
|
+
result = SatMx.download_request_received(
|
|
38
|
+
start_date: Time.new(2024, 1, 1),
|
|
39
|
+
end_date: Time.new(2024, 1, 31),
|
|
40
|
+
request_type: :cfdi,
|
|
41
|
+
recipient_rfc: "ABC010101ABC",
|
|
42
|
+
requester_rfc: "ABC010101ABC",
|
|
43
|
+
issuing_rfc: "XYZ020202XYZ",
|
|
44
|
+
document_status: "Vigente",
|
|
45
|
+
access_token: token
|
|
46
|
+
)
|
|
47
|
+
raise "Request failed" unless result.success?
|
|
48
|
+
|
|
49
|
+
request_id = result.value
|
|
50
|
+
|
|
51
|
+
# 3. Verify status (poll until ready)
|
|
52
|
+
loop do
|
|
53
|
+
result = SatMx.verify_request(
|
|
54
|
+
request_id: request_id,
|
|
55
|
+
requester_rfc: "ABC010101ABC",
|
|
56
|
+
access_token: token
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
case result.value[:request_status]
|
|
60
|
+
when :finished
|
|
61
|
+
break result.value[:package_ids]
|
|
62
|
+
when :error, :rejected, :expired
|
|
63
|
+
raise "Request failed: #{result.value}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
sleep 5
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# 4. Download packages
|
|
70
|
+
package_ids.each do |package_id|
|
|
71
|
+
result = SatMx.download_petition(
|
|
72
|
+
package_id: package_id,
|
|
73
|
+
requester_rfc: "ABC010101ABC",
|
|
74
|
+
access_token: token
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
File.write("#{package_id}.zip", result.value) if result.success?
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
| Method | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `SatMx.configure` | Configure certificate and private key |
|
|
86
|
+
| `SatMx.configuration` | Get current configuration |
|
|
87
|
+
| `SatMx.authenticate` | Get access token |
|
|
88
|
+
| `SatMx.download_request_received` | Request download of received CFDI |
|
|
89
|
+
| `SatMx.verify_request` | Check request status |
|
|
90
|
+
| `SatMx.download_petition` | Download package |
|
|
91
|
+
|
|
92
|
+
All methods return a `Result` object with:
|
|
93
|
+
- `success?` - Boolean indicating success
|
|
94
|
+
- `value` - Data on success, error hash `{:cod_estatus, :mensaje}` on failure
|
|
95
|
+
- `xml` - Raw XML response
|
|
32
96
|
|
|
33
97
|
## License
|
|
34
98
|
|
|
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).
|
|
99
|
+
MIT
|
|
@@ -3,6 +3,7 @@ require "time"
|
|
|
3
3
|
require "base64"
|
|
4
4
|
|
|
5
5
|
module SatMx
|
|
6
|
+
# @api private
|
|
6
7
|
class Authentication
|
|
7
8
|
def self.authenticate(certificate:, private_key:, uuid: SecureRandom.uuid)
|
|
8
9
|
new(
|
|
@@ -43,6 +44,7 @@ module SatMx
|
|
|
43
44
|
attr_reader :xml_auth_body, :client
|
|
44
45
|
end
|
|
45
46
|
|
|
47
|
+
# @api private
|
|
46
48
|
class XmlAuthBody
|
|
47
49
|
def initialize(certificate:, uuid:)
|
|
48
50
|
@certificate = certificate
|
data/lib/sat_mx/body.rb
CHANGED
data/lib/sat_mx/client.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module SatMx
|
|
2
|
+
# @api private
|
|
2
3
|
class Client
|
|
3
4
|
HEADERS = {
|
|
4
5
|
"content-type" => "text/xml; charset=utf-8",
|
|
@@ -32,6 +33,17 @@ module SatMx
|
|
|
32
33
|
)
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def download_request_received(payload)
|
|
37
|
+
HTTPX.post(
|
|
38
|
+
"https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/SolicitaDescargaService.svc",
|
|
39
|
+
headers: {
|
|
40
|
+
"SOAPAction" => "http://DescargaMasivaTerceros.sat.gob.mx/ISolicitaDescargaService/SolicitaDescargaRecibidos"
|
|
41
|
+
}.merge(authorization)
|
|
42
|
+
.merge(HEADERS),
|
|
43
|
+
body: sign(payload)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
35
47
|
def verify_request(payload)
|
|
36
48
|
HTTPX.post(
|
|
37
49
|
"https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/VerificaSolicitudDescargaService.svc",
|
data/lib/sat_mx/configuration.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module SatMx
|
|
2
|
+
# @api private
|
|
2
3
|
class DownloadPetition
|
|
3
4
|
def self.call(package_id:, requester_rfc:, access_token:, certificate:, private_key:)
|
|
4
5
|
new(
|
|
@@ -41,8 +42,8 @@ module SatMx
|
|
|
41
42
|
success?: false,
|
|
42
43
|
xml: response.xml,
|
|
43
44
|
value: {
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
cod_estatus: response_tag["CodEstatus"],
|
|
46
|
+
mensaje: response_tag["Mensaje"]
|
|
46
47
|
}
|
|
47
48
|
)
|
|
48
49
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module SatMx
|
|
2
|
+
# @api private
|
|
2
3
|
class DownloadRequest
|
|
3
4
|
def self.call(start_date:,
|
|
4
5
|
end_date:,
|
|
@@ -56,8 +57,8 @@ module SatMx
|
|
|
56
57
|
Result.new(
|
|
57
58
|
success?: false,
|
|
58
59
|
value: {
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
cod_estatus: download_result_tag.attr("CodEstatus").value,
|
|
61
|
+
mensaje: download_result_tag.attr("Mensaje").value
|
|
61
62
|
},
|
|
62
63
|
xml:
|
|
63
64
|
)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require_relative "body"
|
|
2
|
+
|
|
3
|
+
module SatMx
|
|
4
|
+
# @api private
|
|
5
|
+
class DownloadRequestReceived
|
|
6
|
+
def self.call(start_date:,
|
|
7
|
+
end_date:,
|
|
8
|
+
request_type:,
|
|
9
|
+
recipient_rfc:,
|
|
10
|
+
access_token:,
|
|
11
|
+
certificate:,
|
|
12
|
+
private_key:,
|
|
13
|
+
requester_rfc: nil,
|
|
14
|
+
issuing_rfc: nil,
|
|
15
|
+
complement: nil,
|
|
16
|
+
document_status: nil)
|
|
17
|
+
new(
|
|
18
|
+
download_request_received_body: DownloadRequestReceivedBody.new(
|
|
19
|
+
start_date:,
|
|
20
|
+
end_date:,
|
|
21
|
+
request_type:,
|
|
22
|
+
recipient_rfc:,
|
|
23
|
+
requester_rfc:,
|
|
24
|
+
certificate:,
|
|
25
|
+
issuing_rfc:,
|
|
26
|
+
complement:,
|
|
27
|
+
document_status:
|
|
28
|
+
),
|
|
29
|
+
client: Client.new(private_key:, access_token:)
|
|
30
|
+
).call
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def initialize(download_request_received_body:, client:)
|
|
34
|
+
@download_request_received_body = download_request_received_body
|
|
35
|
+
@client = client
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def call
|
|
39
|
+
if (validation_error = validate_complement!)
|
|
40
|
+
return validation_error
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if (validation_error = validate_document_status!)
|
|
44
|
+
return validation_error
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
response = client.download_request_received(download_request_received_body.generate)
|
|
48
|
+
|
|
49
|
+
case response.status
|
|
50
|
+
when 200..299
|
|
51
|
+
check_body_status response.xml
|
|
52
|
+
when 400..599
|
|
53
|
+
Result.new(success?: false, value: nil, xml: response.xml)
|
|
54
|
+
else
|
|
55
|
+
Error
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
attr_reader :download_request_received_body, :client
|
|
62
|
+
|
|
63
|
+
def validate_complement!
|
|
64
|
+
complement = download_request_received_body.complement
|
|
65
|
+
return unless complement
|
|
66
|
+
return if DownloadRequestReceivedBody::COMPLEMENT_TYPES.include?(complement)
|
|
67
|
+
|
|
68
|
+
Result.new(
|
|
69
|
+
success?: false,
|
|
70
|
+
value: {cod_estatus: "INVALID_COMPLEMENT", mensaje: "Invalid complement type: #{complement}"},
|
|
71
|
+
xml: nil
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def validate_document_status!
|
|
76
|
+
document_status = download_request_received_body.document_status
|
|
77
|
+
return unless document_status
|
|
78
|
+
return if DownloadRequestReceivedBody::DOCUMENT_STATUS.include?(document_status)
|
|
79
|
+
|
|
80
|
+
Result.new(
|
|
81
|
+
success?: false,
|
|
82
|
+
value: {cod_estatus: "INVALID_DOCUMENT_STATUS", mensaje: "Invalid document status: #{document_status}"},
|
|
83
|
+
xml: nil
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def check_body_status(xml)
|
|
88
|
+
download_result_tag = xml.xpath(
|
|
89
|
+
"//xmlns:SolicitaDescargaRecibidosResult",
|
|
90
|
+
xmlns: Body::NAMESPACE
|
|
91
|
+
)
|
|
92
|
+
if download_result_tag.attr("CodEstatus").value == "5000"
|
|
93
|
+
Result.new(
|
|
94
|
+
success?: true,
|
|
95
|
+
value: download_result_tag.attr("IdSolicitud").value,
|
|
96
|
+
xml:
|
|
97
|
+
)
|
|
98
|
+
else
|
|
99
|
+
Result.new(
|
|
100
|
+
success?: false,
|
|
101
|
+
value: {
|
|
102
|
+
cod_estatus: download_result_tag.attr("CodEstatus").value,
|
|
103
|
+
mensaje: download_result_tag.attr("Mensaje").value
|
|
104
|
+
},
|
|
105
|
+
xml:
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
module SatMx
|
|
4
|
+
# @api private
|
|
5
|
+
class DownloadRequestReceivedBody
|
|
6
|
+
include Body
|
|
7
|
+
|
|
8
|
+
REQUEST_TYPES = {
|
|
9
|
+
cfdi: "CFDI",
|
|
10
|
+
metadata: "Metadata"
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
DOCUMENT_STATUS = %w[
|
|
14
|
+
Todos
|
|
15
|
+
Cancelado
|
|
16
|
+
Vigente
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
COMPLEMENT_TYPES = %w[
|
|
20
|
+
acreditamientoieps10
|
|
21
|
+
aerolineas
|
|
22
|
+
certificadodedestruccion
|
|
23
|
+
cfdiregistrofiscal
|
|
24
|
+
comercioexterior10
|
|
25
|
+
comercioexterior11
|
|
26
|
+
comprobante
|
|
27
|
+
consumodecombustibles
|
|
28
|
+
consumodecombustibles11
|
|
29
|
+
detallista
|
|
30
|
+
divisas
|
|
31
|
+
donat11
|
|
32
|
+
ecc11
|
|
33
|
+
ecc12
|
|
34
|
+
gastoshidrocarbonos10
|
|
35
|
+
iedu
|
|
36
|
+
implocal
|
|
37
|
+
ine11
|
|
38
|
+
ingresoshidrocarbonos
|
|
39
|
+
leyendasfisc
|
|
40
|
+
nomina11
|
|
41
|
+
nomina12
|
|
42
|
+
notariospublicos
|
|
43
|
+
obrasarteantiguedades
|
|
44
|
+
pagoenespecie
|
|
45
|
+
pagos10
|
|
46
|
+
pfic
|
|
47
|
+
renovacionysustitucionvehiculos
|
|
48
|
+
servicioparcialconstruccion
|
|
49
|
+
spei
|
|
50
|
+
terceros11
|
|
51
|
+
turistapasajeroextranjero
|
|
52
|
+
valesdedespensa
|
|
53
|
+
vehiculousado
|
|
54
|
+
ventavehiculos11
|
|
55
|
+
].freeze
|
|
56
|
+
|
|
57
|
+
def initialize(
|
|
58
|
+
start_date:,
|
|
59
|
+
end_date:,
|
|
60
|
+
request_type:,
|
|
61
|
+
recipient_rfc:,
|
|
62
|
+
requester_rfc:,
|
|
63
|
+
certificate:,
|
|
64
|
+
issuing_rfc: nil,
|
|
65
|
+
complement: nil,
|
|
66
|
+
document_status: nil
|
|
67
|
+
)
|
|
68
|
+
@start_date = start_date
|
|
69
|
+
@end_date = end_date
|
|
70
|
+
@request_type = request_type
|
|
71
|
+
@recipient_rfc = recipient_rfc
|
|
72
|
+
@requester_rfc = requester_rfc
|
|
73
|
+
@certificate = certificate
|
|
74
|
+
@issuing_rfc = issuing_rfc
|
|
75
|
+
@complement = complement
|
|
76
|
+
@document_status = document_status
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def generate
|
|
80
|
+
envelope do |xml|
|
|
81
|
+
xml[Body::DES].SolicitaDescargaRecibidos do
|
|
82
|
+
attrs = {
|
|
83
|
+
"FechaInicial" => start_date,
|
|
84
|
+
"FechaFinal" => end_date,
|
|
85
|
+
"TipoSolicitud" => request_type,
|
|
86
|
+
"RfcReceptor" => recipient_rfc
|
|
87
|
+
}
|
|
88
|
+
attrs["RfcEmisor"] = issuing_rfc if issuing_rfc
|
|
89
|
+
attrs["RfcSolicitante"] = requester_rfc if requester_rfc
|
|
90
|
+
attrs["Complemento"] = complement if complement
|
|
91
|
+
attrs["EstadoComprobante"] = document_status if document_status
|
|
92
|
+
|
|
93
|
+
xml[Body::DES].solicitud(attrs) do
|
|
94
|
+
signature(xml)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
attr_reader :complement, :document_status
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
attr_reader :recipient_rfc, :requester_rfc, :certificate, :issuing_rfc
|
|
105
|
+
|
|
106
|
+
def start_date = @start_date.strftime("%Y-%m-%dT%H:%M:%S")
|
|
107
|
+
|
|
108
|
+
def end_date = @end_date.strftime("%Y-%m-%dT%H:%M:%S")
|
|
109
|
+
|
|
110
|
+
def request_type = REQUEST_TYPES.fetch(@request_type)
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/sat_mx/result.rb
CHANGED
data/lib/sat_mx/signer.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module SatMx
|
|
2
|
+
# @api private
|
|
2
3
|
class VerifyRequest
|
|
3
4
|
STATUS = {
|
|
4
5
|
"1" => :accepted,
|
|
@@ -49,8 +50,8 @@ module SatMx
|
|
|
49
50
|
Result.new(
|
|
50
51
|
success?: false,
|
|
51
52
|
value: {
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
cod_status: download_result_tag.attr("CodEstatus").value,
|
|
54
|
+
mensaje: download_result_tag.attr("Mensaje").value
|
|
54
55
|
},
|
|
55
56
|
xml:
|
|
56
57
|
)
|
data/lib/sat_mx/version.rb
CHANGED
data/lib/sat_mx.rb
CHANGED
|
@@ -7,6 +7,8 @@ module SatMx
|
|
|
7
7
|
autoload(:Authentication, "sat_mx/authentication")
|
|
8
8
|
autoload(:DownloadRequest, "sat_mx/download_request")
|
|
9
9
|
autoload(:DownloadRequestBody, "sat_mx/download_request_body")
|
|
10
|
+
autoload(:DownloadRequestReceived, "sat_mx/download_request_received")
|
|
11
|
+
autoload(:DownloadRequestReceivedBody, "sat_mx/download_request_received_body")
|
|
10
12
|
autoload(:VerifyRequest, "sat_mx/verify_request")
|
|
11
13
|
autoload(:VerifyRequestBody, "sat_mx/verify_request_body")
|
|
12
14
|
autoload(:DownloadPetition, "sat_mx/download_petition")
|
|
@@ -46,6 +48,9 @@ module SatMx
|
|
|
46
48
|
# puts "Authentication failed"
|
|
47
49
|
# end
|
|
48
50
|
#
|
|
51
|
+
# @param certificate [OpenSSL::X509::Certificate, nil] Certificate object (uses configuration if nil)
|
|
52
|
+
# @param private_key [OpenSSL::PKey::RSA, nil] Private key object (uses configuration if nil)
|
|
53
|
+
#
|
|
49
54
|
# @return [SatMx::Result] A Result object containing:
|
|
50
55
|
# - success?: [Boolean] whether the authentication was successful
|
|
51
56
|
# - value: [String, nil] the authentication token if successful, nil otherwise
|
|
@@ -53,10 +58,204 @@ module SatMx
|
|
|
53
58
|
#
|
|
54
59
|
# @see SatMx::Authentication
|
|
55
60
|
# @see SatMx::Result
|
|
56
|
-
def authenticate
|
|
61
|
+
def authenticate(certificate: nil, private_key: nil)
|
|
62
|
+
cert = certificate || configuration.certificate
|
|
63
|
+
key = private_key || configuration.private_key
|
|
57
64
|
Authentication.authenticate(
|
|
58
|
-
certificate:
|
|
59
|
-
private_key:
|
|
65
|
+
certificate: cert,
|
|
66
|
+
private_key: key
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Requests a download of CFDI documents from the SAT web service.
|
|
71
|
+
#
|
|
72
|
+
# result = SatMx.download_request(
|
|
73
|
+
# start_date: Time.new(2024, 1, 1),
|
|
74
|
+
# end_date: Time.new(2024, 1, 31),
|
|
75
|
+
# request_type: :cfdi,
|
|
76
|
+
# issuing_rfc: "ABC010101ABC",
|
|
77
|
+
# recipient_rfcs: ["XYZ020202XYZ"],
|
|
78
|
+
# requester_rfc: "ABC010101ABC",
|
|
79
|
+
# access_token: "your_access_token"
|
|
80
|
+
# )
|
|
81
|
+
# if result.success?
|
|
82
|
+
# puts "Request ID: #{result.value}"
|
|
83
|
+
# else
|
|
84
|
+
# puts "Request failed: #{result.value}"
|
|
85
|
+
# end
|
|
86
|
+
#
|
|
87
|
+
# @param start_date [Time] Start date for the search range
|
|
88
|
+
# @param end_date [Time] End date for the search range
|
|
89
|
+
# @param request_type [Symbol] Type of request (:cfdi or :retentions)
|
|
90
|
+
# @param issuing_rfc [String] RFC of the issuer
|
|
91
|
+
# @param recipient_rfcs [Array<String>] RFCs of the recipients
|
|
92
|
+
# @param requester_rfc [String] RFC of the requester
|
|
93
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
94
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
95
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
96
|
+
#
|
|
97
|
+
# @return [SatMx::Result] A Result object containing:
|
|
98
|
+
# - success?: [Boolean] whether the request was successful
|
|
99
|
+
# - value: [String, nil] the request ID if successful, or {cod_estatus:, mensaje:} on failure
|
|
100
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
101
|
+
#
|
|
102
|
+
# @see SatMx::DownloadRequest
|
|
103
|
+
# @see SatMx::Result
|
|
104
|
+
def download_request(start_date:, end_date:, request_type:, issuing_rfc:, recipient_rfcs:, requester_rfc:, access_token:, **options)
|
|
105
|
+
certificate = options[:certificate] || configuration.certificate
|
|
106
|
+
private_key = options[:private_key] || configuration.private_key
|
|
107
|
+
DownloadRequest.call(
|
|
108
|
+
start_date:,
|
|
109
|
+
end_date:,
|
|
110
|
+
request_type:,
|
|
111
|
+
issuing_rfc:,
|
|
112
|
+
recipient_rfcs:,
|
|
113
|
+
requester_rfc:,
|
|
114
|
+
access_token:,
|
|
115
|
+
certificate:,
|
|
116
|
+
private_key:
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Requests a download of received CFDI documents from the SAT web service SolicitaDescargaService with SOAPAction SolicitaDescargaRecibidos
|
|
121
|
+
#
|
|
122
|
+
# result = SatMx.download_request_received(
|
|
123
|
+
# start_date: Time.new(2024, 1, 1),
|
|
124
|
+
# end_date: Time.new(2024, 1, 31),
|
|
125
|
+
# request_type: :cfdi,
|
|
126
|
+
# recipient_rfc: "ABC010101ABC",
|
|
127
|
+
# requester_rfc: "ABC010101ABC",
|
|
128
|
+
# access_token: "your_access_token"
|
|
129
|
+
# )
|
|
130
|
+
# if result.success?
|
|
131
|
+
# puts "Request ID: #{result.value}"
|
|
132
|
+
# else
|
|
133
|
+
# puts "Request failed: #{result.value}"
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
# @param start_date [Time] Start date for the search range
|
|
137
|
+
# @param end_date [Time] End date for the search range
|
|
138
|
+
# @param request_type [Symbol] Type of request (:cfdi or :metadata)
|
|
139
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
140
|
+
# @param recipient_rfc [String] RFC of the receiver (required)
|
|
141
|
+
# @param requester_rfc [String, nil] RFC of the requester (optional)
|
|
142
|
+
# @param issuing_rfc [String, nil] RFC of the issuer (optional)
|
|
143
|
+
# @param complement [String, nil] Complement type to filter (e.g., "nomina12", "pagos10")
|
|
144
|
+
# This value is optional. If omitted, the SAT web service will return all documents.
|
|
145
|
+
# @param document_status [String, nil] Document status filter (Todos, Cancelados, Vigentes) defaults to Todos
|
|
146
|
+
# @param certificate [String, nil] Certificate object (uses configuration if nil)
|
|
147
|
+
# @param private_key [String, nil] Private key object (uses configuration if nil)
|
|
148
|
+
#
|
|
149
|
+
# @return [SatMx::Result] A Result object containing:
|
|
150
|
+
# - success?: [Boolean] whether the request was successful
|
|
151
|
+
# - value: [String, nil] the request ID if successful, or {cod_estatus:, mensaje:} on failure
|
|
152
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
153
|
+
#
|
|
154
|
+
# @see SatMx::DownloadRequestReceived
|
|
155
|
+
# @see SatMx::Result
|
|
156
|
+
def download_request_received(
|
|
157
|
+
access_token:,
|
|
158
|
+
start_date:,
|
|
159
|
+
end_date:,
|
|
160
|
+
request_type:,
|
|
161
|
+
recipient_rfc:,
|
|
162
|
+
issuing_rfc: nil,
|
|
163
|
+
requester_rfc: nil,
|
|
164
|
+
document_status: nil,
|
|
165
|
+
complement: nil,
|
|
166
|
+
**options
|
|
167
|
+
)
|
|
168
|
+
certificate = options[:certificate] || configuration.certificate
|
|
169
|
+
private_key = options[:private_key] || configuration.private_key
|
|
170
|
+
DownloadRequestReceived.call(
|
|
171
|
+
start_date:,
|
|
172
|
+
end_date:,
|
|
173
|
+
request_type:,
|
|
174
|
+
recipient_rfc:,
|
|
175
|
+
requester_rfc:,
|
|
176
|
+
issuing_rfc:,
|
|
177
|
+
complement:,
|
|
178
|
+
document_status:,
|
|
179
|
+
access_token:,
|
|
180
|
+
certificate:,
|
|
181
|
+
private_key:
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Verifies the status of a previously submitted download request.
|
|
186
|
+
#
|
|
187
|
+
# result = SatMx.verify_request(
|
|
188
|
+
# request_id: "606c5667-345a-4630-8979-0769734ac80b",
|
|
189
|
+
# requester_rfc: "ABC010101ABC",
|
|
190
|
+
# access_token: "your_access_token"
|
|
191
|
+
# )
|
|
192
|
+
# if result.success?
|
|
193
|
+
# puts "Status: #{result.value[:request_status]}"
|
|
194
|
+
# puts "Packages: #{result.value[:package_ids]}"
|
|
195
|
+
# else
|
|
196
|
+
# puts "Verification failed: #{result.value}"
|
|
197
|
+
# end
|
|
198
|
+
#
|
|
199
|
+
# @param request_id [String] The ID returned from SatMx.download_request
|
|
200
|
+
# @param requester_rfc [String] RFC of the requester
|
|
201
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
202
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
203
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
204
|
+
#
|
|
205
|
+
# @return [SatMx::Result] A Result object containing:
|
|
206
|
+
# - success?: [Boolean] whether the verification was successful
|
|
207
|
+
# - value: [Hash, nil] containing :request_status and :package_ids if successful, or {cod_estatus:, mensaje:} on failure
|
|
208
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
209
|
+
#
|
|
210
|
+
# @see SatMx::VerifyRequest
|
|
211
|
+
# @see SatMx::Result
|
|
212
|
+
def verify_request(request_id:, requester_rfc:, access_token:, **options)
|
|
213
|
+
certificate = options[:certificate] || configuration.certificate
|
|
214
|
+
private_key = options[:private_key] || configuration.private_key
|
|
215
|
+
VerifyRequest.call(
|
|
216
|
+
request_id:,
|
|
217
|
+
requester_rfc:,
|
|
218
|
+
access_token:,
|
|
219
|
+
certificate:,
|
|
220
|
+
private_key:
|
|
221
|
+
)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Downloads a package of CFDI documents from the SAT web service.
|
|
225
|
+
#
|
|
226
|
+
# result = SatMx.download_petition(
|
|
227
|
+
# package_id: "18015570-C084-4BE8-BE36-476F5D46A133_01",
|
|
228
|
+
# requester_rfc: "ABC010101ABC",
|
|
229
|
+
# access_token: "your_access_token"
|
|
230
|
+
# )
|
|
231
|
+
# if result.success?
|
|
232
|
+
# File.write("package.zip", result.value)
|
|
233
|
+
# else
|
|
234
|
+
# puts "Download failed: #{result.value}"
|
|
235
|
+
# end
|
|
236
|
+
#
|
|
237
|
+
# @param package_id [String] The package ID from SatMx.verify_request
|
|
238
|
+
# @param requester_rfc [String] RFC of the requester
|
|
239
|
+
# @param access_token [String] Authentication token from SatMx.authenticate
|
|
240
|
+
# @param certificate [String, nil] Path to certificate file (uses configuration if nil)
|
|
241
|
+
# @param private_key [String, nil] Path to private key file (uses configuration if nil)
|
|
242
|
+
#
|
|
243
|
+
# @return [SatMx::Result] A Result object containing:
|
|
244
|
+
# - success?: [Boolean] whether the download was successful
|
|
245
|
+
# - value: [String, nil] Base64 encoded ZIP content if successful, or {cod_estatus:, mensaje:} on failure
|
|
246
|
+
# - xml: [Nokogiri::XML::Document] the raw XML response from the service
|
|
247
|
+
#
|
|
248
|
+
# @see SatMx::DownloadPetition
|
|
249
|
+
# @see SatMx::Result
|
|
250
|
+
def download_petition(package_id:, requester_rfc:, access_token:, **options)
|
|
251
|
+
certificate = options[:certificate] || configuration.certificate
|
|
252
|
+
private_key = options[:private_key] || configuration.private_key
|
|
253
|
+
DownloadPetition.call(
|
|
254
|
+
package_id:,
|
|
255
|
+
requester_rfc:,
|
|
256
|
+
access_token:,
|
|
257
|
+
certificate:,
|
|
258
|
+
private_key:
|
|
60
259
|
)
|
|
61
260
|
end
|
|
62
261
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sat_mx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oscar Rivas
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: xmldsig
|
|
@@ -131,6 +130,8 @@ files:
|
|
|
131
130
|
- lib/sat_mx/download_petition_body.rb
|
|
132
131
|
- lib/sat_mx/download_request.rb
|
|
133
132
|
- lib/sat_mx/download_request_body.rb
|
|
133
|
+
- lib/sat_mx/download_request_received.rb
|
|
134
|
+
- lib/sat_mx/download_request_received_body.rb
|
|
134
135
|
- lib/sat_mx/result.rb
|
|
135
136
|
- lib/sat_mx/signer.rb
|
|
136
137
|
- lib/sat_mx/verify_request.rb
|
|
@@ -143,7 +144,6 @@ licenses:
|
|
|
143
144
|
metadata:
|
|
144
145
|
homepage_uri: https://github.com/kadru/sat_mx
|
|
145
146
|
changelog_uri: https://github.com/kadru/sat_mx/CHANGELOG.md
|
|
146
|
-
post_install_message:
|
|
147
147
|
rdoc_options: []
|
|
148
148
|
require_paths:
|
|
149
149
|
- lib
|
|
@@ -158,8 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
159
|
version: '0'
|
|
160
160
|
requirements: []
|
|
161
|
-
rubygems_version:
|
|
162
|
-
signing_key:
|
|
161
|
+
rubygems_version: 4.0.4
|
|
163
162
|
specification_version: 4
|
|
164
163
|
summary: a client to connect to SAT web services
|
|
165
164
|
test_files: []
|