orangedata 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +72 -0
- data/LICENSE.txt +21 -0
- data/README.md +120 -0
- data/Rakefile +8 -0
- data/bin/console +8 -0
- data/bin/setup +6 -0
- data/lib/orange_data.rb +21 -0
- data/lib/orange_data/credentials.rb +157 -0
- data/lib/orange_data/credentials_test.yml +101 -0
- data/lib/orange_data/receipt.rb +77 -0
- data/lib/orange_data/transport.rb +134 -0
- data/lib/orange_data/version.rb +3 -0
- data/lib/orangedata.rb +1 -0
- data/orangedata.gemspec +33 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f3099e2e11880ab05a035b7174a3b51d4f23568
|
4
|
+
data.tar.gz: c48823de2b7073f0e19c60903e3c6d574b08b2b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e8449d4a55cebf3da41ec3cae035fccdd16c62a363359ee3b5cc35b2511e84b9ebed8b4147a2d3604ca47096df09a65de39cd35f7c24363cda62402c68013949
|
7
|
+
data.tar.gz: bcf0803eaecc27e05bb5a34305cf3da9092c792309630147e093c084c89bfd01aaa78fd0860a290190c9b7f606c7a310b59d6c0d039909d9888f6d593a1d63a2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
orangedata (0.0.1)
|
5
|
+
faraday (>= 0.15)
|
6
|
+
faraday_middleware
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.5.2)
|
12
|
+
public_suffix (>= 2.0.2, < 4.0)
|
13
|
+
ast (2.4.0)
|
14
|
+
crack (0.4.3)
|
15
|
+
safe_yaml (~> 1.0.0)
|
16
|
+
diff-lcs (1.3)
|
17
|
+
faraday (0.15.2)
|
18
|
+
multipart-post (>= 1.2, < 3)
|
19
|
+
faraday_middleware (0.12.2)
|
20
|
+
faraday (>= 0.7.4, < 1.0)
|
21
|
+
hashdiff (0.3.7)
|
22
|
+
jaro_winkler (1.5.1)
|
23
|
+
multipart-post (2.0.0)
|
24
|
+
parallel (1.12.1)
|
25
|
+
parser (2.5.1.0)
|
26
|
+
ast (~> 2.4.0)
|
27
|
+
powerpack (0.1.2)
|
28
|
+
public_suffix (3.0.3)
|
29
|
+
rainbow (3.0.0)
|
30
|
+
rake (10.5.0)
|
31
|
+
rspec (3.7.0)
|
32
|
+
rspec-core (~> 3.7.0)
|
33
|
+
rspec-expectations (~> 3.7.0)
|
34
|
+
rspec-mocks (~> 3.7.0)
|
35
|
+
rspec-core (3.7.1)
|
36
|
+
rspec-support (~> 3.7.0)
|
37
|
+
rspec-expectations (3.7.0)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.7.0)
|
40
|
+
rspec-mocks (3.7.0)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.7.0)
|
43
|
+
rspec-support (3.7.1)
|
44
|
+
rubocop (0.57.2)
|
45
|
+
jaro_winkler (~> 1.5.1)
|
46
|
+
parallel (~> 1.10)
|
47
|
+
parser (>= 2.5)
|
48
|
+
powerpack (~> 0.1)
|
49
|
+
rainbow (>= 2.2.2, < 4.0)
|
50
|
+
ruby-progressbar (~> 1.7)
|
51
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
52
|
+
ruby-progressbar (1.9.0)
|
53
|
+
safe_yaml (1.0.4)
|
54
|
+
unicode-display_width (1.4.0)
|
55
|
+
webmock (3.4.2)
|
56
|
+
addressable (>= 2.3.6)
|
57
|
+
crack (>= 0.3.2)
|
58
|
+
hashdiff
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
bundler (~> 1.16)
|
65
|
+
orangedata!
|
66
|
+
rake (~> 10.0)
|
67
|
+
rspec
|
68
|
+
rubocop
|
69
|
+
webmock
|
70
|
+
|
71
|
+
BUNDLED WITH
|
72
|
+
1.16.6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Vasily Fedoseyev
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Orangedata Client
|
2
|
+
|
3
|
+
A ruby client for orangedata.ru service.
|
4
|
+
Target service is pretty local to RU, so parts of readme will be in russian.
|
5
|
+
|
6
|
+
Note: This is a Work-in-progress. API might change in the future.
|
7
|
+
|
8
|
+
Умеет:
|
9
|
+
- собственно транспорт с подписью запросов
|
10
|
+
- сгенерировать ключ сразу в нужном виде
|
11
|
+
- планируется DSL для чеков
|
12
|
+
|
13
|
+
## Установка
|
14
|
+
|
15
|
+
Все стандартно. Пишем в Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'orangedata'
|
19
|
+
```
|
20
|
+
|
21
|
+
И давим:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Либо руками:
|
26
|
+
|
27
|
+
$ gem install orangedata
|
28
|
+
|
29
|
+
## Использование
|
30
|
+
|
31
|
+
Для тестового окружения ключики в комплекте - [credentials_test.yml](lib/credentials_test.yml), собрано из родного `File_for_test.zip`, доступны как `OrangeData::Credentials.default_test`
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
transport = OrangeData::Transport.new("https://apip.orangedata.ru:2443/api/v2/", OrangeData::Credentials.default_test)
|
35
|
+
|
36
|
+
receipt = {
|
37
|
+
id: SecureRandom.uuid,
|
38
|
+
inn: '1234567890', key:'1234567890',
|
39
|
+
content: { # тут собрать данные можно по официальному мануалу
|
40
|
+
type: 1,
|
41
|
+
positions:[{ quantity: 1, price: 0.01, tax: 4, text: "Товар на копейку"}],
|
42
|
+
checkClose:{
|
43
|
+
payments: [{ type:2, amount:'0.01' }],
|
44
|
+
taxationSystem: 1
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
transport.post_document(receipt)
|
49
|
+
# wait some time, then
|
50
|
+
transport.get_document("1234567890", receipt[:id])
|
51
|
+
# =>
|
52
|
+
# {
|
53
|
+
# "id"=>"a88b6b30-20ab-47ea-95ca-f12f22ef03d3",
|
54
|
+
# "deviceSN"=>"1400000000001033",
|
55
|
+
# "deviceRN"=>"0000000400054952",
|
56
|
+
# "fsNumber"=>"9999078900001341",
|
57
|
+
# "ofdName"=>"ООО \"Ярус\" (\"ОФД-Я\")",
|
58
|
+
# "ofdWebsite"=>"www.ofd-ya.ru",
|
59
|
+
# "ofdinn"=>"7728699517",
|
60
|
+
# "fnsWebsite"=>"www.nalog.ru",
|
61
|
+
# "companyINN"=>"1234567890",
|
62
|
+
# "companyName"=>"Тест",
|
63
|
+
# "documentNumber"=>5548,
|
64
|
+
# "shiftNumber"=>6072,
|
65
|
+
# "documentIndex"=>3045,
|
66
|
+
# "processedAt"=>"2018-10-22T19:36:00",
|
67
|
+
# "content"=>
|
68
|
+
# {
|
69
|
+
# "type"=>1,
|
70
|
+
# "positions"=>[{"quantity"=>1.0, "price"=>0.01, "tax"=>4, "text"=>"Товар на копейку"}],
|
71
|
+
# "checkClose"=>{
|
72
|
+
# "payments"=>[{"type"=>2, "amount"=>0.01}],
|
73
|
+
# "taxationSystem"=>1
|
74
|
+
# }
|
75
|
+
# },
|
76
|
+
# "change"=>0.0,
|
77
|
+
# "fp"=>"787980846"
|
78
|
+
# }
|
79
|
+
```
|
80
|
+
|
81
|
+
### Получаем сертификаты
|
82
|
+
|
83
|
+
Предполагается, что всякие договоры и прочая фискализация уже успешно пройдена и у вас есть доступ
|
84
|
+
к ЛК orangedata.
|
85
|
+
|
86
|
+
Генерируем себе приватный ключ:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
require 'yaml'
|
90
|
+
c = OrangeData::Credentials.new title:'My production'
|
91
|
+
c.generate_signature_key!(2048) # параметр - длина ключа
|
92
|
+
#=> возвращает публичный ключ в том виде, который хочет ЛК OrangeData:
|
93
|
+
# "<RSAKeyValue><Modulus>(многабукв)==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
|
94
|
+
|
95
|
+
File.open("my_production.yml", "wt"){|f| f.write YAML.dump(c.to_hash) }
|
96
|
+
# (на выходе - yml с приватным ключом и паролем к нему, который надо сохранить и беречь)
|
97
|
+
|
98
|
+
# повторно взять публичный ключ можно так:
|
99
|
+
credentials = OrangeData::Credentials.from_hash(YAML.load_file('my_production.yml'))
|
100
|
+
credentials.signature_public_xml
|
101
|
+
```
|
102
|
+
|
103
|
+
После чего публичный ключ (xml c `RSAKeyValue`) кормим в ЛК. Значение поля `Название ключа` с этого шага отправляется в `signature_key_name`.
|
104
|
+
Далее там выпускаем себе сертификаты и из полученного архива вставляем содержимое `<ИНН>.crt` и `<ИНН>.key`(сертификат и ключ к нему) в yml-файлик аналогично примеру.
|
105
|
+
|
106
|
+
Если все прошло гладко - теперь у вас есть файлик `my_production.yml` со всеми реквизитами доступа к продакшн-кассе. Обращаться с ним стоит как и с любой другой очень чувствительной информацией, например не стоит коммитить его (ну или как минимум, убрать из него поля `signature_key_pass` и `certificate_key_pass` и хранить отдельно)
|
107
|
+
|
108
|
+
## Разработка
|
109
|
+
|
110
|
+
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.
|
111
|
+
|
112
|
+
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).
|
113
|
+
|
114
|
+
## Contributing
|
115
|
+
|
116
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Vasfed/orangedata.
|
117
|
+
|
118
|
+
## License
|
119
|
+
|
120
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). Copyright (c) 2018 Vasily Fedoseyev
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/lib/orange_data.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "orange_data/version"
|
4
|
+
require "orange_data/credentials"
|
5
|
+
require "orange_data/transport"
|
6
|
+
|
7
|
+
module OrangeData
|
8
|
+
|
9
|
+
|
10
|
+
# QR: t=20180518T220500&s=975.88&fn=8710000101125654&i=99456&fp=1250448795&n=1
|
11
|
+
#
|
12
|
+
|
13
|
+
# - t=<date/time - дата и время осуществления расчета в формате ГГГГММДДТЧЧММ>
|
14
|
+
# - s=<сумма расчета в рублях и копейках, разделенных точкой>
|
15
|
+
# - fn=<заводской номер фискального накопителя>
|
16
|
+
# - i=<порядковый номер фискального документа, нулями не дополняется>
|
17
|
+
# - fp=<фискальный признак документа, нулями не дополняется>
|
18
|
+
# - n=<признак расчета>.
|
19
|
+
# Пример строки QR-кода: t=20150720T1638&s=9999999.00&fn=000110000105&i=12345678&fp=123456&n=2
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
require "securerandom"
|
6
|
+
|
7
|
+
# wrapper for keys/certs used for connection auth
|
8
|
+
module OrangeData
|
9
|
+
class Credentials
|
10
|
+
|
11
|
+
# nodoc
|
12
|
+
module KeyEncoding
|
13
|
+
refine OpenSSL::PKey::RSA do
|
14
|
+
def to_xml
|
15
|
+
h_params = to_hash
|
16
|
+
h = { 'Modulus' => :n, 'Exponent' => :e }
|
17
|
+
if private?
|
18
|
+
h.merge!({
|
19
|
+
'P' => :p, 'Q' => :q, 'DP' => :dmp1, 'DQ' => :dmq1, 'InverseQ' => :iqmp, 'D' => :d
|
20
|
+
})
|
21
|
+
end
|
22
|
+
"<RSAKeyValue>#{h.map{|(k,v)| "<#{k}>#{h_params[v.to_s]}</#{k}>"}.join('')}</RSAKeyValue>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_hash
|
26
|
+
params.map{|k,v| v != 0 && [k, Base64.strict_encode64(v.to_s(2))] || nil}.compact.to_h
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
refine OpenSSL::PKey::RSA.singleton_class do
|
31
|
+
def from_xml(xml)
|
32
|
+
require "rexml/document"
|
33
|
+
kv = REXML::Document.new(xml).elements['RSAKeyValue']
|
34
|
+
raise ArgumentError, 'no RSAKeyValue in xml' unless kv&.name == 'RSAKeyValue'
|
35
|
+
|
36
|
+
mapping = {
|
37
|
+
"Modulus"=>:n, "Exponent"=>:e,
|
38
|
+
"D"=>:d, "P"=>:p, "Q"=>:q,
|
39
|
+
"DP"=>:dmp1, "DQ"=>:dmq1, "InverseQ"=>:iqmp
|
40
|
+
}
|
41
|
+
from_hash(
|
42
|
+
kv.elements.each_with_object({}){|k,h| h[mapping[k.name]] = k.text if mapping[k.name] }
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def from_hash hash
|
47
|
+
OpenSSL::PKey::RSA.new.tap do |key|
|
48
|
+
key.params.keys.each{|param|
|
49
|
+
if(v = hash[param] || hash[param.to_sym])
|
50
|
+
key.send(:"#{param}=", OpenSSL::BN.new(Base64.decode64(v), 2))
|
51
|
+
end
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
using KeyEncoding
|
59
|
+
|
60
|
+
attr_accessor :signature_key_name, :signature_key, :certificate, :certificate_key, :title
|
61
|
+
|
62
|
+
def initialize signature_key_name:nil,signature_key:nil,certificate:nil,certificate_key:nil,title:nil
|
63
|
+
raise ArgumentError, "Signature key should be a private key" if signature_key && !signature_key.private?
|
64
|
+
raise ArgumentError, "Certificate key should be a private key" if certificate_key && !certificate_key.private?
|
65
|
+
@signature_key_name = signature_key_name
|
66
|
+
@signature_key = signature_key
|
67
|
+
@certificate = certificate
|
68
|
+
@certificate_key = certificate_key
|
69
|
+
@title = title
|
70
|
+
end
|
71
|
+
|
72
|
+
def valid?
|
73
|
+
signature_key_name &&
|
74
|
+
signature_key && signature_key.private? &&
|
75
|
+
certificate && certificate_key && certificate_key.private?
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.from_hash creds
|
79
|
+
key = nil
|
80
|
+
if creds[:signature_key]
|
81
|
+
key = if creds[:signature_key].is_a?(Hash)
|
82
|
+
OpenSSL::PKey::RSA.from_hash(creds[:signature_key])
|
83
|
+
elsif creds[:signature_key].start_with?('<')
|
84
|
+
OpenSSL::PKey::RSA.from_xml(creds[:signature_key])
|
85
|
+
else
|
86
|
+
OpenSSL::PKey::RSA.new(creds[:signature_key], creds[:signature_key_pass])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
new(
|
90
|
+
signature_key_name: creds[:signature_key_name],
|
91
|
+
signature_key: key,
|
92
|
+
certificate: creds[:certificate] && OpenSSL::X509::Certificate.new(creds[:certificate]),
|
93
|
+
certificate_key: creds[:certificate_key] && OpenSSL::PKey::RSA.new(creds[:certificate_key], creds[:certificate_key_pass]),
|
94
|
+
title: creds[:title]
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_hash(key_pass:nil, save_pass:false)
|
99
|
+
if key_pass.nil?
|
100
|
+
key_pass = SecureRandom.hex
|
101
|
+
save_pass = true
|
102
|
+
elsif key_pass == false
|
103
|
+
key_pass = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
{
|
107
|
+
title: title,
|
108
|
+
signature_key_name: signature_key_name,
|
109
|
+
signature_key: signature_key&.to_pem(OpenSSL::Cipher.new("aes-128-cbc"), key_pass),
|
110
|
+
certificate: certificate&.to_pem,
|
111
|
+
certificate_key: certificate_key&.to_pem(OpenSSL::Cipher.new("aes-128-cbc"), key_pass),
|
112
|
+
}.tap do |h|
|
113
|
+
h.delete(:title) if !title || title == ''
|
114
|
+
if save_pass
|
115
|
+
h[:certificate_key_pass] = key_pass
|
116
|
+
h[:signature_key_pass] = key_pass
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.from_json json
|
122
|
+
require 'json'
|
123
|
+
from_hash(JSON.parse(json, symbolize_names: true))
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_json(key_pass:nil, save_pass:false)
|
127
|
+
to_hash(key_pass:key_pass, save_pass:save_pass).to_json
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
info_fields = {
|
132
|
+
title: (title || 'untitled').inspect,
|
133
|
+
key_name: signature_key_name.inspect,
|
134
|
+
certificate: (certificate&.subject.to_a.select{|ent| ent.first == 'O'}.dig(0, 1)).inspect,
|
135
|
+
}.map{|(k,v)| "#{k}=#{v}"}.join(' ')
|
136
|
+
|
137
|
+
"#<#{self.class.name}:#{object_id} #{info_fields}>"
|
138
|
+
end
|
139
|
+
|
140
|
+
def generate_signature_key!(key_length=2048)
|
141
|
+
self.signature_key = OpenSSL::PKey::RSA.new(key_length)
|
142
|
+
signature_public_xml
|
143
|
+
end
|
144
|
+
|
145
|
+
# публичная часть ключа подписи в формате пригодном для отдачи в ЛК
|
146
|
+
def signature_public_xml
|
147
|
+
signature_key.public_key.to_xml
|
148
|
+
end
|
149
|
+
|
150
|
+
# ключи для тествого окружения
|
151
|
+
def self.default_test
|
152
|
+
require 'yaml'
|
153
|
+
from_hash(YAML.load_file(File.expand_path('../credentials_test.yml', __FILE__)))
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
---
|
2
|
+
# Credentials for test environment at https://apip.orangedata.ru:2443/api/v2/
|
3
|
+
:title: 'Test credentials'
|
4
|
+
:signature_key_name: '1234567890'
|
5
|
+
:signature_key: |
|
6
|
+
<RSAKeyValue>
|
7
|
+
<Modulus>
|
8
|
+
t8nC/Eth8UabQbXu8pdro3v7NqUanV8Y+g92YgT7z1xqkBLRHXZ1guml3Pxr
|
9
|
+
qjNX9AvOmu8R+qaKOyHfJW0PcRDLzCoIUcHNAwpDO/E5j6WAaLIv7gAjTtyr
|
10
|
+
9kJB9rfJaparViJNZu3RSUYGTvVznOmXMf7LTOTMR6HP/5H1TP5n1g4+BbLm
|
11
|
+
C9EhjUf2eNFqwZBqPtzybBb6jaHBRaJ0XdE3lh2OeE9/OF0BtLwiYPDKsVTx
|
12
|
+
IekbNf7l/DREy+YbUOxQLceeHXrvbYLiGWecP0a7CqHGj9ZNY1oJThK3AwrS
|
13
|
+
d4yHa9Wnx/GaZUNtWud1BaP9g3sVX+sRV9xtnI96dw==
|
14
|
+
</Modulus>
|
15
|
+
<Exponent>AQAB</Exponent>
|
16
|
+
<P>
|
17
|
+
3WSb72a1erb6jcLkyZA2Y21VNIipGz+ta1RP+iacs3xnktFsxgTYgqWyt6SW
|
18
|
+
Z2rStp0u4vb/IAHyKhgJPNTUSi2u0G44MOsRxMC/FWTF8zdyrDF4BjPBM4j8
|
19
|
+
4nAmE/FQYv5F8ldDkakc96zEPiTk5Fka3MPeN8mMk6/OA59JdF0=
|
20
|
+
</P>
|
21
|
+
<Q>
|
22
|
+
1IRVid5SsDrOwJQAEKkdT436XEb0sVWe9AcU8JyaCEEMj0NPzownNbIrebPo
|
23
|
+
fMYdDHikopQpr2XqxZYDbb7AneoHkhEV26TfpPVbN4wBJFXih3lAP2n5hqhg
|
24
|
+
qHGp5Wq2Lu7jUS376Ruw3bhwW+MiWpXv1xhMTZ8AtDfnZFFNvOM=
|
25
|
+
</Q>
|
26
|
+
<DP>
|
27
|
+
Fo5KiNCJCtCbpFfH4XVM5UJdXPXTbNBHBdlYMJ9AddTl5IJrt50ExgLFu4oM
|
28
|
+
PMsYXryS61LI2WT5XCqIvmbcnhYbambgWLOKYuZUUYSr2kS67So5FUCunWaG
|
29
|
+
hTdx2bRLQVqwm6kiXDPDnMRAViiCHXWqk/VsrXheVymhLqNK440=
|
30
|
+
</DP>
|
31
|
+
<DQ>
|
32
|
+
mowSWMzhfV+G8+2tjnAt7KjnpSvEzyHhEr4DsGdybQZBR/4/j4nFCfukOkFn
|
33
|
+
lTXN8j/aGpF9Lx0C+uX5YFoUYcLL9qGOL8lbCu+TgnXCbtY2gybeXj+HQzI3
|
34
|
+
+MeQMlLEYqU/ks3KIOAOY2+55ljrpszbOqVk+B3luSnekMm/qtk=
|
35
|
+
</DQ>
|
36
|
+
<InverseQ>
|
37
|
+
aP5e5F1j6s82Pm7dCpH3mRZWnfZIKqoNQIq2BO8vA9/WrdFI2C27uNhxCp2Z
|
38
|
+
DMulRdBZcoeHcwJjnyDzg4I4gBZ2nSKkVdlN1REoTjLBBdlHi8XKiXzxvpIt
|
39
|
+
c2wjNC2AKHaJqj/dnh3bbTAQD1iUAxPmmLJYYkhfZ2i1IrTVxZE=
|
40
|
+
</InverseQ>
|
41
|
+
<D>
|
42
|
+
PUfM+Aq6kZSVWAetsL3EajKAxOuwQCDhVx+ovW4j+DQ8Y+WiTEyfShNV9qVD
|
43
|
+
0PBltz3omch1GjpFhQn6OaRvraeIDH9HXttb3FOjr2zzYG4yrrYbPSRWoYj6
|
44
|
+
3ZWiIP2O7zdl0caGQHezfNcYa2N0NTG99DGc3/q6EnhlvjWQsSbiEjmxcPx8
|
45
|
+
fmV1i4DoflMQ383nsixAFapgrROUAtCgMvhWn1kSeoojKd+e4eKZxa/SNYul
|
46
|
+
sBJWNFkmo1CZH4YTqlPM+IwYeDUOnOUGNxGurRZ3qQdWs2N2ZQhnrvlh+zpz
|
47
|
+
urD2hwAz6gQXP7mxxMR1xHtAD8XQ+w4OiJK6VWjoIQ==
|
48
|
+
</D>
|
49
|
+
</RSAKeyValue>
|
50
|
+
:certificate: |
|
51
|
+
-----BEGIN CERTIFICATE-----
|
52
|
+
MIIDYjCCAkoCAQAwDQYJKoZIhvcNAQELBQAwcTELMAkGA1UEBhMCUlUxDzANBgNV
|
53
|
+
BAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRMwEQYDVQQKDApPcmFuZ2VkYXRh
|
54
|
+
MQ8wDQYDVQQLDAZOZWJ1bGExGjAYBgNVBAMMEXd3dy5vcmFuZ2VkYXRhLnJ1MB4X
|
55
|
+
DTE4MDMxNTE2NDYwMVoXDTI4MDMxMjE2NDYwMVowfTELMAkGA1UEBhMCUlUxDzAN
|
56
|
+
BgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MR8wHQYDVQQKDBZPcmFuZ2Vk
|
57
|
+
YXRhIHRlc3QgY2xpZW50MRMwEQYDVQQLDApFLWNvbW1lcmNlMRYwFAYDVQQDDA1v
|
58
|
+
cmFuZ2VkYXRhLnJ1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7XZ
|
59
|
+
+VUUo9p+Q0zPmlt1eThA8NmVVAgNXkVDZoz3umyEnnm2d4R5Voxf4y6fuesW3Za8
|
60
|
+
/ImKWLbQ3/S/pHZKWiz75ElSfpnYJfMRuLAaqqs0eFfxmHbHi8Mgg9zjAMdILpR6
|
61
|
+
eEaP7qeCNRom3Zb6ziYoWEmDC2ZFFu9995rjkn7CtV3noWZveOCGExjM7WTkql8L
|
62
|
+
v1PX3ee3fXaEC7Kefxl4O/4w7agEceKRHlc0l3iwVJaKittQwAQd3ieUwoqsxzPH
|
63
|
+
dRwB4IU9aI6IjfqteyD51s7xd+ayM/O4j+aJ/HBhJajDHBcGWKytxv0f6YpqPUAc
|
64
|
+
25fRAXVa0Gsei6eY/QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCv/Vcxh2lMt8RV
|
65
|
+
Al0V9xIst0ZdjH22yTOUCOiH9PZgeagqrjTLT3ycWAdbZZUpzcFSdOmPUsgQ7Eqz
|
66
|
+
+TpcY5lmYFInLwJK/Afjqsb5LK2irGKT254p5qzD9rSRlM42wxRzQTA0BWX3mmhi
|
67
|
+
zwdrfLAvyCw1gHBbUZNf3eemBCY+8RRGPRAqD2XbyIya1bX0AHLXbx5dBe9EIOG/
|
68
|
+
F46WbTlrkR7kc06eiacTiGYwNdcywJ2KOcvmnXPup8Os6KOWe197CIathDHeiG2C
|
69
|
+
mQlsQDF/d7W4G/+l6Q66BhfRtuhp99gkT8P8j82X6ChrwbgQ5+vya3SytJ0wmIg2
|
70
|
+
67jOKmGK
|
71
|
+
-----END CERTIFICATE-----
|
72
|
+
:certificate_key: |
|
73
|
+
-----BEGIN PRIVATE KEY-----
|
74
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjtdn5VRSj2n5D
|
75
|
+
TM+aW3V5OEDw2ZVUCA1eRUNmjPe6bISeebZ3hHlWjF/jLp+56xbdlrz8iYpYttDf
|
76
|
+
9L+kdkpaLPvkSVJ+mdgl8xG4sBqqqzR4V/GYdseLwyCD3OMAx0gulHp4Ro/up4I1
|
77
|
+
GibdlvrOJihYSYMLZkUW7333muOSfsK1XeehZm944IYTGMztZOSqXwu/U9fd57d9
|
78
|
+
doQLsp5/GXg7/jDtqARx4pEeVzSXeLBUloqK21DABB3eJ5TCiqzHM8d1HAHghT1o
|
79
|
+
joiN+q17IPnWzvF35rIz87iP5on8cGElqMMcFwZYrK3G/R/pimo9QBzbl9EBdVrQ
|
80
|
+
ax6Lp5j9AgMBAAECggEAL5qkrKT54H+bcZR3Vco8iag68g5DJvFEeeIoLDzXmGUP
|
81
|
+
10lLLsvdwLYG9/fJyHU86+h2QfT4vr1CVa1EwN0I19n20TYk/91ahgZ9Y7gJuREZ
|
82
|
+
q9jeztfTRKfT36Quej54ldrlFe5m0h3xdeGJ5auOeL2Nw8Z0ja8KbhXsCkEG5cTx
|
83
|
+
ZvXB0XlFoAJOp8AZvU3ZNBpmpItFlcl2aBXwRCb72DUjLkpnZf2kFDNorc1wFZ2e
|
84
|
+
DO/pujT6EtQ1r5qb2kUuj4GpCaHffOB/ukz3dg3bBhompTYdhax0RlZs2vNsUusm
|
85
|
+
6oYsUS5nWmJfnrh32Te03Fdzc2U8/XUflJzKL/0QvQKBgQDOpNQvCCxwvthZXART
|
86
|
+
q0fl9NY0fxlSqUpxd1BB4DYCg6Sg5kVvfwf7rdb5bbP4aNCC/9m4MgXTD0DGfEhM
|
87
|
+
FnYPVNKTzwLMBftBQdzDN6766j5lI49evwnh855EFAR5GyaIWh2n7tT3NUOstogp
|
88
|
+
kpwhzsPGH1WkEO1QLcBDyzPI3wKBgQDKz94V8au1EVKuRBR+c5gNJpF+zmUu2t2C
|
89
|
+
ZlPtYIuWaxMbqitmeCmNBQQZK+oLQdSUMkgMvYVpKriPk6AgnY7+1F+OOeg+ezPU
|
90
|
+
G+J4Vi8Yx/kZPhXoBuW745twux+q8WOBwEj2WeMy5p1F/V3qlu70HA3kbsrXdB+R
|
91
|
+
0bFVAxCtowKBgFTtq4M08cbYuORpDCIzGBarvMnQnuC5US43IlYgxzHbVvMGEO2V
|
92
|
+
IPvQY7UZ4EitE11zt9CbRoeLEk1BURlsddMxQmabQwQFRVF5tzjIjvLzCPfaWJdR
|
93
|
+
Hsetr5M9QuVfQkPx/ZRCdWawjoLSdj3X0rGWYCHySOloR5CXbRiv0DWzAoGAF3XW
|
94
|
+
Ldmn0Ckx1EDB0iLS+up0OCPt5m6g4v2tRa8+VmcKbc/Qd2j8/XgQEk1XJHg3+/CZ
|
95
|
+
Dwg5T4IGmW0tP7iaGvY8G3qtV9TumOGk3+CwUACJ2xaoeA+cMZDRoUe0ERUdOpwg
|
96
|
+
lIavVmsA1GDLpWBSQeCg5sS+KBAhur9z8O6K1lsCgYEAj7TLLE0jLNXRRfkfWzy5
|
97
|
+
RsJezMCQS9fjtJrLGB3BbYxqtebP2owp1qjmKMQioW5QjRxRCOyT2KrHjb31hRsp
|
98
|
+
Hk3Wi0OKOEuKNwmAZczbjcPH4caPZPeL6LMDtFFMsFX2BW7TnC8FcoVr2KPO/FG/
|
99
|
+
xs4KtXC9j5rrvBowJ0LbJ2U=
|
100
|
+
-----END PRIVATE KEY-----
|
101
|
+
:certificate_key_pass: '1234'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OrangeData
|
4
|
+
|
5
|
+
class Receipt
|
6
|
+
attr_accessor :id, :inn, :group, :key_name
|
7
|
+
|
8
|
+
def initialize id:SecureRandom.uuid, inn:, group:nil, key_name:nil
|
9
|
+
@id = id
|
10
|
+
@inn = inn
|
11
|
+
@group = group
|
12
|
+
@key_name = key_name
|
13
|
+
yield self if block_given?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ReceiptContent
|
18
|
+
|
19
|
+
module AgentTypeSerializer
|
20
|
+
AGENT_TYPE_BITS = { # 1057 (в чеках/БСО должно соответствовать отчету о (пере)регистрации ККТ)
|
21
|
+
bank_payment_agent: (1 << 0), # банковский платежный агент
|
22
|
+
bank_payment_subagent: (1 << 1), # банковский платежный субагент
|
23
|
+
payment_agent: (1 << 2), # платежный агент
|
24
|
+
payment_subagent: (1 << 3), # платежный субагент
|
25
|
+
attorney: (1 << 4), # поверенный
|
26
|
+
commission_agent: (1 << 5), # комиссионер
|
27
|
+
other_agent: (1 << 6), # иной агент
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def self.load data
|
31
|
+
data = data.to_i
|
32
|
+
AGENT_TYPE_BITS.select{|(k,v)| (data & v) > 0}.map(&:first)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.dump val
|
36
|
+
val = [val] unless val.is_a?(Array)
|
37
|
+
val.map{|v| AGENT_TYPE_BITS[v] || raise "unknown agent_type #{v}"}.reduce(:|)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
RECEIPT_TYPES = { # 1054:
|
43
|
+
|
44
|
+
}.freeze
|
45
|
+
|
46
|
+
FIELDS = {
|
47
|
+
type: {
|
48
|
+
name: 'Признак расчета',
|
49
|
+
tag_num: 1054,
|
50
|
+
mapper: :enum,
|
51
|
+
enum_values: {
|
52
|
+
income: 1, # Приход
|
53
|
+
return_income: 2, # Возврат прихода
|
54
|
+
expense: 3, # Расход
|
55
|
+
return_expense: 4 # Возврат расхода
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
def initialize type
|
63
|
+
@positions = []
|
64
|
+
@payments = []
|
65
|
+
end
|
66
|
+
|
67
|
+
def agent_type
|
68
|
+
AgentTypeSerializer.load(@agent_type)
|
69
|
+
end
|
70
|
+
|
71
|
+
def agent_type= val
|
72
|
+
@agent_type = AgentTypeSerializer.dump(val)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'base64'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
|
8
|
+
# handles low-level http requests to orangedata, including auth
|
9
|
+
module OrangeData
|
10
|
+
class Transport
|
11
|
+
|
12
|
+
def initialize api_url=default_api_url, credentials=Credentials.default_test
|
13
|
+
raise ArgumentError, "Need full credentials for connection" unless credentials.valid?
|
14
|
+
@credentials = credentials
|
15
|
+
@api_url = api_url
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_api_url
|
19
|
+
# production: https://api.orangedata.ru:12003/api/v2/
|
20
|
+
# test: https://apip.orangedata.ru:2443/api/v2/
|
21
|
+
"https://apip.orangedata.ru:2443/api/v2/"
|
22
|
+
end
|
23
|
+
|
24
|
+
class RequestSignatureMiddleware < Faraday::Middleware
|
25
|
+
def initialize(app, signature_key)
|
26
|
+
@app = app
|
27
|
+
@signature_key = signature_key
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
if env.body
|
32
|
+
signature = @signature_key.sign(OpenSSL::Digest::SHA256.new, env.body)
|
33
|
+
env.request_headers['X-Signature'] = Base64.strict_encode64(signature)
|
34
|
+
end
|
35
|
+
@app.call(env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def transport
|
40
|
+
@transport ||= Faraday.new(
|
41
|
+
url: @api_url,
|
42
|
+
ssl: {
|
43
|
+
client_cert: @credentials.certificate,
|
44
|
+
client_key: @credentials.certificate_key,
|
45
|
+
}
|
46
|
+
) do |conn|
|
47
|
+
conn.headers['User-Agent'] = "OrangeDataRuby/#{OrangeData::VERSION}"
|
48
|
+
conn.headers['Accept'] = "application/json"
|
49
|
+
conn.request(:json)
|
50
|
+
conn.use(RequestSignatureMiddleware, @credentials.signature_key)
|
51
|
+
conn.response :json, content_type: /\bjson$/
|
52
|
+
conn.adapter(Faraday.default_adapter)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def raw_post method, data
|
57
|
+
transport.post(method, data)
|
58
|
+
end
|
59
|
+
|
60
|
+
def post_entity sub_url, data
|
61
|
+
res = raw_post(sub_url, data)
|
62
|
+
|
63
|
+
case res.status
|
64
|
+
when 201
|
65
|
+
return true
|
66
|
+
when 401
|
67
|
+
raise 'Unauthorized'
|
68
|
+
when 409
|
69
|
+
raise "Conflict"
|
70
|
+
when 400
|
71
|
+
raise "Invalid doc: #{res.body}"
|
72
|
+
else
|
73
|
+
raise "Unknown code from OD: #{res.status} #{res.reason_phrase} #{res.body}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_entity sub_url
|
78
|
+
res = transport.get(sub_url)
|
79
|
+
|
80
|
+
case res.status
|
81
|
+
when 200
|
82
|
+
return res.body
|
83
|
+
when 400
|
84
|
+
raise "Cannot get doc: #{res.body}"
|
85
|
+
when 401
|
86
|
+
raise 'Unauthorized'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Below actual methods from api
|
91
|
+
|
92
|
+
def ping
|
93
|
+
res = transport.get(''){|r|
|
94
|
+
r.headers['Accept'] = 'text/plain'
|
95
|
+
}
|
96
|
+
|
97
|
+
return res.status == 200 && res.body == "Nebula.Api v2"
|
98
|
+
rescue StandardError => e
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
|
102
|
+
def post_document_validate data
|
103
|
+
res = raw_post 'validateDocument', data
|
104
|
+
|
105
|
+
case res.status
|
106
|
+
when 200
|
107
|
+
return true
|
108
|
+
when 400
|
109
|
+
return res.body
|
110
|
+
when 401
|
111
|
+
raise 'Unauthorized'
|
112
|
+
else
|
113
|
+
raise "Unexpected response: #{res.status} #{res.reason_phrase}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def post_document data
|
118
|
+
post_entity 'documents', data
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_document inn, document_id
|
122
|
+
get_entity "documents/#{inn}/status/#{document_id}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def post_correction data
|
126
|
+
post_entity 'corrections', data
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_correction inn, document_id
|
130
|
+
get_entity "corrections/#{inn}/status/#{document_id}"
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
data/lib/orangedata.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'orange_data'
|
data/orangedata.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "orange_data/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "orangedata"
|
8
|
+
spec.version = OrangeData::VERSION
|
9
|
+
spec.authors = ["Vasily Fedoseyev"]
|
10
|
+
spec.email = ["vasilyfedoseyev@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Ruby client for orangedata.ru service}
|
13
|
+
spec.description = %q{Ruby client for orangedata.ru service}
|
14
|
+
spec.homepage = "https://github.com/Vasfed/orangedata"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# spec.bindir = "exe"
|
22
|
+
# spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_dependency "faraday", ">=0.15"
|
26
|
+
spec.add_dependency "faraday_middleware"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "webmock"
|
32
|
+
spec.add_development_dependency "rubocop"
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: orangedata
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vasily Fedoseyev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.15'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Ruby client for orangedata.ru service
|
112
|
+
email:
|
113
|
+
- vasilyfedoseyev@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- bin/console
|
127
|
+
- bin/setup
|
128
|
+
- lib/orange_data.rb
|
129
|
+
- lib/orange_data/credentials.rb
|
130
|
+
- lib/orange_data/credentials_test.yml
|
131
|
+
- lib/orange_data/receipt.rb
|
132
|
+
- lib/orange_data/transport.rb
|
133
|
+
- lib/orange_data/version.rb
|
134
|
+
- lib/orangedata.rb
|
135
|
+
- orangedata.gemspec
|
136
|
+
homepage: https://github.com/Vasfed/orangedata
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.6.3
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Ruby client for orangedata.ru service
|
160
|
+
test_files: []
|