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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecf0432037eb033ade4de9f89e9d9edfcd4a7174391758d2c1dc47c5771e44b0
4
- data.tar.gz: 0aaf19d6965d16cc6c28047964fe4d00be5c925e9cc921f37f3e86985f50993e
3
+ metadata.gz: a6f23a9065a4facc1ea12963a7a27587c8055775cbb729e3070c0a5a2a8144cd
4
+ data.tar.gz: c043c861ed94a4cb049b3664f37b6a3b13e502917e28dd7b6f07608f15212485
5
5
  SHA512:
6
- metadata.gz: 21ce855b92d37223820e13056c03468880473bd0a791987d2ba8adc65c7d6da11f5f058a52a7d2f3ca26b1cd4d2434c1308cd9e3f10235add7d6ab9a1b7c5a98
7
- data.tar.gz: 8a5b7724f51b3698d1a71b15da5e75fa905082ce7b631d261e2c20fec9f0cc47fb4ea4f2992012437afc94759ae419b34b4aa565f79bace0ca1f8632eed97084
6
+ metadata.gz: 7a4ca5be6421a81e686b084207556f320fc4294d0d9731d9febd8e4d303a49bf10dcbfde496d86d2a8eef72fb172d69b7948ae2812e79f9c950d6749e038c8d8
7
+ data.tar.gz: 6dfb7a7364d74260cb0eb5e804b19b2e113e3fbfd8a5a61baee671e22cb15a109618decae7c7851b941e07901e61b93ec9ba12ede074b736e08fe355717bc227
data/README.md CHANGED
@@ -1,39 +1,99 @@
1
1
  # SatMx
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
7
+ ```bash
8
+ gem install sat_mx
9
+ ```
10
10
 
11
- Install the gem and add to the application's Gemfile by executing:
11
+ Or add to your Gemfile:
12
12
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
13
+ ```ruby
14
+ gem "sat_mx"
15
+ ```
14
16
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
17
+ ## Configuration
16
18
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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
- TODO: Write usage instructions here
22
-
23
- ## Development
24
-
25
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
-
27
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
-
29
- ## Contributing
30
-
31
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sat_mx. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/sat_mx/blob/master/CODE_OF_CONDUCT.md).
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
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
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
@@ -1,6 +1,7 @@
1
1
  require "base64"
2
2
 
3
3
  module SatMx
4
+ # @api private
4
5
  module Body
5
6
  S11 = "S11"
6
7
  XMLNS = "xmlns"
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",
@@ -1,4 +1,5 @@
1
1
  module SatMx
2
+ # @api private
2
3
  Configuration = Data.define(:certificate, :private_key) do
3
4
  def initialize(certificate:, private_key:, password:)
4
5
  super(
@@ -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
- CodEstatus: response_tag["CodEstatus"],
45
- Mensaje: response_tag["Mensaje"]
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 DownloadPetitionBody
3
4
  include Body
4
5
 
@@ -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
- CodEstatus: download_result_tag.attr("CodEstatus").value,
60
- Mensaje: download_result_tag.attr("Mensaje").value
60
+ cod_estatus: download_result_tag.attr("CodEstatus").value,
61
+ mensaje: download_result_tag.attr("Mensaje").value
61
62
  },
62
63
  xml:
63
64
  )
@@ -1,6 +1,7 @@
1
1
  require "time"
2
2
 
3
3
  module SatMx
4
+ # @api private
4
5
  class DownloadRequestBody
5
6
  include Body
6
7
 
@@ -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
@@ -1,3 +1,4 @@
1
1
  module SatMx
2
+ # @api private
2
3
  Result = Data.define(:success?, :value, :xml)
3
4
  end
data/lib/sat_mx/signer.rb CHANGED
@@ -2,6 +2,7 @@ require "openssl"
2
2
  require "xmldsig"
3
3
 
4
4
  module SatMx
5
+ # @api private
5
6
  class Signer
6
7
  def self.sign(document:, private_key:)
7
8
  new(document:, private_key:).sign
@@ -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
- CodEstatus: download_result_tag.attr("CodEstatus").value,
53
- Mensaje: download_result_tag.attr("Mensaje").value
53
+ cod_status: download_result_tag.attr("CodEstatus").value,
54
+ mensaje: download_result_tag.attr("Mensaje").value
54
55
  },
55
56
  xml:
56
57
  )
@@ -1,4 +1,5 @@
1
1
  module SatMx
2
+ # @api private
2
3
  class VerifyRequestBody
3
4
  include Body
4
5
 
@@ -1,3 +1,3 @@
1
1
  module SatMx
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
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: configuration.certificate,
59
- private_key: configuration.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.2.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: 2025-03-17 00:00:00.000000000 Z
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: 3.5.23
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: []