listo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 71fa066f9c0d4b8989daa59834aab045bb058876f9b1c729fc0ca9a9111f71ba
4
+ data.tar.gz: 41a1274e380080acb24a3a12b571e810e25578e034080efa1e21370a3bc30633
5
+ SHA512:
6
+ metadata.gz: ec9b96f1ff54f039070c40ed419dbea0b8affa60da5792585ed5897557342ab2d52e7869badc4db622cc8ab7a9d42a3590865d2f483180714f0c66699351d451
7
+ data.tar.gz: cbc4be2daa4f820ec1e6a778199d934a69134b4c1df339c5aa4b351b33f66fb6ddf822ef7a75d55a31018e3e730e51462bc7c15eaa1efccb055a65abe8f79912
data/.editorconfig ADDED
@@ -0,0 +1,11 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ trim_trailing_whitespace = true
7
+ insert_final_newline = false
8
+
9
+ [*.{rb,yml}]
10
+ indent_style = space
11
+ indent_size = 2
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ listo (0.1.0)
5
+ faraday (~> 0.12.2)
6
+ faraday_middleware (~> 0.10.0)
7
+ openssl (~> 2.1, >= 2.1.1)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.5.2)
13
+ public_suffix (>= 2.0.2, < 4.0)
14
+ crack (0.4.3)
15
+ safe_yaml (~> 1.0.0)
16
+ diff-lcs (1.3)
17
+ faraday (0.12.2)
18
+ multipart-post (>= 1.2, < 3)
19
+ faraday_middleware (0.10.1)
20
+ faraday (>= 0.7.4, < 1.0)
21
+ hashdiff (0.3.7)
22
+ multipart-post (2.0.0)
23
+ openssl (2.1.1)
24
+ public_suffix (3.0.2)
25
+ rake (10.5.0)
26
+ rspec (3.7.0)
27
+ rspec-core (~> 3.7.0)
28
+ rspec-expectations (~> 3.7.0)
29
+ rspec-mocks (~> 3.7.0)
30
+ rspec-core (3.7.1)
31
+ rspec-support (~> 3.7.0)
32
+ rspec-expectations (3.7.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.7.0)
35
+ rspec-mocks (3.7.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.7.0)
38
+ rspec-support (3.7.1)
39
+ safe_yaml (1.0.4)
40
+ vcr (3.0.3)
41
+ webmock (2.3.2)
42
+ addressable (>= 2.3.6)
43
+ crack (>= 0.3.2)
44
+ hashdiff
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ bundler (~> 1.16)
51
+ listo!
52
+ rake (~> 10.0)
53
+ rspec (~> 3.0)
54
+ vcr (~> 3.0, >= 3.0.3)
55
+ webmock (~> 2.1)
56
+
57
+ BUNDLED WITH
58
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Listo
2
+
3
+ A Ruby client for the [listo.mx](https://listo.mx/) API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'listo'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install listo
20
+
21
+ ## Usage
22
+
23
+ ### Client setup
24
+
25
+ ```Ruby
26
+ Listo::Client.configure do |client|
27
+ client.token = 'LISTO_API_TOKEN'
28
+ client.cert_file = './path/cert.cer'
29
+ client.pkey_file = './path/pkcs8.pem'
30
+ client.pkey_passphrase = 'passphrase'
31
+ client.debug_mode = false # default
32
+ end
33
+ ```
34
+
35
+ The PKEY is usually provided in a DER format, you can create a copy in a PEM format with the following command:
36
+
37
+ ```sh
38
+ openssl pkcs8 -inform DER -outform PEM -in pkcs8.key -out pkcs8.pem
39
+ ```
40
+
41
+ ### Invoicing
42
+
43
+ ##### Generate XML
44
+
45
+ ```Ruby
46
+ Listo::Invoicing.generate_xml(invoice_data)
47
+ ```
48
+
49
+ ##### Certify XML
50
+
51
+ ```Ruby
52
+ Listo::Invoicing.certify_xml(xml, original_chain, data)
53
+ ```
54
+
55
+ For more details checkout the listo.mx [api docs](https://apidocs.listo.mx).
56
+ ## Development
57
+
58
+ 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.
59
+
60
+ 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
61
+
62
+ ## Contributing
63
+
64
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aliada-mx/listo.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "listo"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,17 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Listo
5
+ class Certificate < SimpleDelegator
6
+ def initialize(cert_file)
7
+ cert_der = File.read(cert_file)
8
+ cert = OpenSSL::X509::Certificate.new(cert_der)
9
+
10
+ super(cert)
11
+ end
12
+
13
+ def to_der64
14
+ Base64.encode64(to_der)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'listo/connection'
2
+ require 'listo/invoicing'
3
+ require 'listo/certificate'
4
+ require 'listo/pkey'
5
+
6
+ module Listo
7
+ class Client
8
+ attr_reader :connection, :cert, :pkey
9
+
10
+ attr_accessor(
11
+ :token,
12
+ :cert_file,
13
+ :pkey_passphrase,
14
+ :pkey_file,
15
+ :debug_mode,
16
+ )
17
+
18
+ def self.current
19
+ @client
20
+ end
21
+
22
+ def self.configure(&config)
23
+ yield @client = new
24
+
25
+ @client
26
+ end
27
+
28
+ def cert
29
+ @cert ||= Listo::Certificate.new(cert_file)
30
+ end
31
+
32
+ def pkey
33
+ @pkey ||= Listo::PKey.new(pkey_file, pkey_passphrase)
34
+ end
35
+
36
+ def connection
37
+ Listo::Connection.new(token, debug_mode: debug_mode)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'listo/middleware/raise_error'
4
+
5
+ module Listo
6
+ class Connection < SimpleDelegator
7
+ def initialize(token, debug_mode: false)
8
+ @debug_mode = debug_mode
9
+
10
+ connection = Faraday.new(url: url) do |faraday|
11
+ faraday.use Listo::Middleware::RaiseError
12
+
13
+ faraday.request :json
14
+ faraday.response :json, content_type: 'application/json'
15
+
16
+ faraday.adapter Faraday.default_adapter
17
+ end
18
+
19
+ connection.headers['Authorization'] = "Token #{token}"
20
+ connection.headers['Content-Type'] = 'application/json'
21
+ connection.headers['Accept'] = 'application/json'
22
+
23
+ super(connection)
24
+ end
25
+
26
+ private
27
+
28
+ def url
29
+ @debug_mode ? 'https://staging.listo.mx/api' : 'https://listo.mx/api'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module Listo
2
+ module Error
3
+ class Error < StandardError
4
+ end
5
+
6
+ class ClientError < Error
7
+ attr_reader :status, :headers, :body
8
+
9
+ def initialize(env)
10
+ @status = env.dig(:status)
11
+ @headers = env.dig(:headers)
12
+ @body = env.dig(:body)
13
+
14
+ super("the server responded with status #{@status}")
15
+ end
16
+ end
17
+
18
+ class StampError < Error
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Listo
5
+ class Invoicing
6
+ class << self
7
+ extend Forwardable
8
+
9
+ def_delegators :client, :cert, :pkey
10
+
11
+ def generate_xml(params = [])
12
+ client.connection.post('invoicing/generate_xml', params).body
13
+ end
14
+
15
+ def certify_xml(xml, original_chain, data)
16
+ params = {
17
+ xml: xml,
18
+ certificate_num: certificate_num,
19
+ certificate: certificate,
20
+ signature: signature(original_chain),
21
+ data: data,
22
+ }
23
+
24
+ response = client.connection.post('invoicing/certify_xml', params).body
25
+
26
+ # warning: listo reponds stamp errors with HTTP 200
27
+ if response.dig('status') == 'error'
28
+ raise Listo::Error::StampError, response.dig('error_description')
29
+ end
30
+
31
+ response
32
+ end
33
+
34
+ private
35
+
36
+ def client
37
+ Listo::Client.current
38
+ end
39
+
40
+ def certificate
41
+ cert.to_der64.delete("\n")
42
+ end
43
+
44
+ def certificate_num
45
+ cert.serial.to_s(2)
46
+ end
47
+
48
+ def signature(original_chain)
49
+ original_chain.gsub!("|#{'0' * 20}|", "|#{certificate_num}|")
50
+ signature = pkey.sign(OpenSSL::Digest::SHA256.new, original_chain)
51
+
52
+ Base64.encode64(signature).delete("\n")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ require 'faraday'
2
+ require 'listo/error'
3
+
4
+ module Listo
5
+ module Middleware
6
+ class RaiseError < Faraday::Response::Middleware
7
+ ClientErrorStatuses = 400...600
8
+
9
+ def on_complete(env)
10
+ case env[:status]
11
+ when ClientErrorStatuses
12
+ raise Listo::Error::ClientError, response_values(env)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def response_values(env)
19
+ {
20
+ status: env.status,
21
+ headers: env.response_headers,
22
+ body: env.body
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/listo/pkey.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'openssl'
2
+
3
+ module Listo
4
+ class PKey < SimpleDelegator
5
+ def initialize(key_file, pass_phrase = nil)
6
+ key_pem = File.read(key_file)
7
+ key = OpenSSL::PKey::RSA.new(key_pem, pass_phrase)
8
+
9
+ super(key)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module Listo
2
+ VERSION = '0.1.0'
3
+ end
data/lib/listo.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'listo/client'
2
+ require 'listo/version'
3
+
4
+ module Listo
5
+ end
data/listo.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'listo/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'listo'
7
+ spec.version = Listo::VERSION
8
+ spec.authors = ['Luis Alfredo Lorenzo']
9
+ spec.email = ['babasbot@gmail.com']
10
+
11
+ spec.summary = 'A Ruby wrapper for the listo.mx API'
12
+ spec.description = 'A Ruby implementation for the listo.mx web API.'
13
+ spec.homepage = 'https://github.com/aliada-mx/listo'
14
+ spec.license = 'Nonstandard'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.16'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'vcr', '~> 3.0', '>= 3.0.3'
27
+
28
+ spec.add_development_dependency 'webmock', '~> 2.1'
29
+
30
+ spec.add_dependency 'faraday', '~> 0.12.2'
31
+ spec.add_dependency 'faraday_middleware', '~> 0.10.0'
32
+ spec.add_dependency 'openssl', '~> 2.1', '>= 2.1.1'
33
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: listo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Luis Alfredo Lorenzo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: vcr
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 3.0.3
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '3.0'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.0.3
75
+ - !ruby/object:Gem::Dependency
76
+ name: webmock
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.1'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.1'
89
+ - !ruby/object:Gem::Dependency
90
+ name: faraday
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.12.2
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.12.2
103
+ - !ruby/object:Gem::Dependency
104
+ name: faraday_middleware
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 0.10.0
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 0.10.0
117
+ - !ruby/object:Gem::Dependency
118
+ name: openssl
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '2.1'
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 2.1.1
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '2.1'
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 2.1.1
137
+ description: A Ruby implementation for the listo.mx web API.
138
+ email:
139
+ - babasbot@gmail.com
140
+ executables: []
141
+ extensions: []
142
+ extra_rdoc_files: []
143
+ files:
144
+ - ".editorconfig"
145
+ - ".gitignore"
146
+ - ".rspec"
147
+ - ".travis.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - README.md
151
+ - Rakefile
152
+ - bin/console
153
+ - bin/setup
154
+ - lib/listo.rb
155
+ - lib/listo/certificate.rb
156
+ - lib/listo/client.rb
157
+ - lib/listo/connection.rb
158
+ - lib/listo/error.rb
159
+ - lib/listo/invoicing.rb
160
+ - lib/listo/middleware/raise_error.rb
161
+ - lib/listo/pkey.rb
162
+ - lib/listo/version.rb
163
+ - listo.gemspec
164
+ homepage: https://github.com/aliada-mx/listo
165
+ licenses:
166
+ - Nonstandard
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.7.3
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: A Ruby wrapper for the listo.mx API
188
+ test_files: []