orangedata 0.0.1
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 +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: []
|