coins_paid_api 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +108 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/coins_paid_api.gemspec +15 -0
- data/lib/coins_paid/api.rb +66 -0
- data/lib/coins_paid/api/callback_data.rb +52 -0
- data/lib/coins_paid/api/currencies_list.rb +20 -0
- data/lib/coins_paid/api/currency.rb +17 -0
- data/lib/coins_paid/api/requester.rb +22 -0
- data/lib/coins_paid/api/signature.rb +18 -0
- data/lib/coins_paid/api/take_address.rb +21 -0
- data/lib/coins_paid/api/transport.rb +43 -0
- data/lib/coins_paid/api/types.rb +9 -0
- data/lib/coins_paid/api/withdrawal.rb +22 -0
- data/lib/coins_paid_api.rb +1 -0
- data/spec/callback_spec.rb +125 -0
- data/spec/check_signature_spec.rb +28 -0
- data/spec/currencies_list_spec.rb +69 -0
- data/spec/request_examples.rb +80 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/shared_data.rb +202 -0
- data/spec/support/struct_like.rb +5 -0
- data/spec/take_address_spec.rb +45 -0
- data/spec/withdrawal_spec.rb +50 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 30840fa192fc3b46c33fa213287229f8662f75c03b049a66a70656ec8ce207dd
|
4
|
+
data.tar.gz: c7d3e06a33e8dc791e784e28c155e9b8e2f9d624c68b7f2e81f4a48164055b94
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e758833c389da026a1ca81e19556bfc2a3d1348987988a0c205da2bb9598afc023afac3f2cfbf7df7dccdfa295ecb323b30402c2e94e850ba43f0f86b4ab2a7a
|
7
|
+
data.tar.gz: 5f5561e85f892abb70f6036f3697221f8ded16f2c5a63a6d65a19ba794d1ca6283386f659bf90f2e47ffbba86de2ecfaab60f50cd3b137155ee56e6c3c6e3ce7
|
@@ -0,0 +1,108 @@
|
|
1
|
+
version: 2.0
|
2
|
+
defaults: &defaults
|
3
|
+
docker:
|
4
|
+
- image: circleci/ruby:2.5-stretch-node
|
5
|
+
working_directory: ~/coins_paid_api
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
checkout_code:
|
9
|
+
<<: *defaults
|
10
|
+
steps:
|
11
|
+
- checkout
|
12
|
+
- run: mkdir log
|
13
|
+
- save_cache:
|
14
|
+
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
|
15
|
+
paths:
|
16
|
+
- ~/coins_paid_api
|
17
|
+
|
18
|
+
download_cc_reporter:
|
19
|
+
<<: *defaults
|
20
|
+
steps:
|
21
|
+
- run:
|
22
|
+
name: Download cc-test-reporter
|
23
|
+
command: |
|
24
|
+
mkdir -p cc/
|
25
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc/cc-test-reporter
|
26
|
+
chmod +x ./cc/cc-test-reporter
|
27
|
+
- persist_to_workspace:
|
28
|
+
root: ~/coins_paid_api
|
29
|
+
paths:
|
30
|
+
- cc/cc-test-reporter
|
31
|
+
|
32
|
+
run_bundler:
|
33
|
+
<<: *defaults
|
34
|
+
steps:
|
35
|
+
- restore_cache:
|
36
|
+
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
|
37
|
+
- restore_cache:
|
38
|
+
key: v1-bundle-{{ checksum "Gemfile" }}
|
39
|
+
- run: echo "export rvm_ignore_gemsets_flag=1" >> ~/.rvmrc
|
40
|
+
- run: sudo apt-get update && sudo apt-get install -y libsodium-dev
|
41
|
+
- run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
|
42
|
+
- save_cache:
|
43
|
+
key: v1-bundle-{{ checksum "Gemfile" }}
|
44
|
+
paths:
|
45
|
+
- ~/coins_paid_api/vendor/bundle
|
46
|
+
|
47
|
+
run_rspec_tests:
|
48
|
+
<<: *defaults
|
49
|
+
docker:
|
50
|
+
- image: circleci/ruby:2.5-stretch-node
|
51
|
+
environment:
|
52
|
+
RACK_ENV: "test"
|
53
|
+
CIRCLE_ARTIFACTS: "./tmp"
|
54
|
+
steps:
|
55
|
+
- restore_cache:
|
56
|
+
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
|
57
|
+
- restore_cache:
|
58
|
+
key: v1-bundle-{{ checksum "Gemfile" }}
|
59
|
+
- attach_workspace:
|
60
|
+
at: ~/coins_paid_api
|
61
|
+
- run: sudo apt-get update && sudo apt-get install -y libsodium-dev
|
62
|
+
- run: bundle check --path vendor/bundle
|
63
|
+
- run: bundle exec rspec spec
|
64
|
+
- run: ./cc/cc-test-reporter format-coverage -t simplecov -o cc/codeclimate.rspec.json tmp/coverage/.resultset.json
|
65
|
+
- persist_to_workspace:
|
66
|
+
root: ~/coins_paid_api
|
67
|
+
paths:
|
68
|
+
- cc/codeclimate.rspec.json
|
69
|
+
|
70
|
+
upload_cc_coverage:
|
71
|
+
<<: *defaults
|
72
|
+
steps:
|
73
|
+
- attach_workspace:
|
74
|
+
at: ~/coins_paid_api
|
75
|
+
- run:
|
76
|
+
name: Upload coverage results to Code Climate
|
77
|
+
command: |
|
78
|
+
./cc/cc-test-reporter upload-coverage -i cc/codeclimate.rspec.json
|
79
|
+
|
80
|
+
deploy:
|
81
|
+
<<: *defaults
|
82
|
+
steps:
|
83
|
+
- restore_cache:
|
84
|
+
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
|
85
|
+
- restore_cache:
|
86
|
+
key: v1-bundle-{{ checksum "Gemfile" }}
|
87
|
+
- run: mkdir -p ~/.ssh && ssh-keyscan -H github.com >> ~/.ssh/known_hosts
|
88
|
+
- run: sudo apt-get update && sudo apt-get install -y libsodium-dev
|
89
|
+
- run: bundle check --path vendor/bundle
|
90
|
+
- run: bundle exec rake deploy:branch_or_label
|
91
|
+
|
92
|
+
workflows:
|
93
|
+
version: 2
|
94
|
+
build_test_deploy:
|
95
|
+
jobs:
|
96
|
+
- checkout_code
|
97
|
+
- download_cc_reporter:
|
98
|
+
requires:
|
99
|
+
- checkout_code
|
100
|
+
- run_bundler:
|
101
|
+
requires:
|
102
|
+
- download_cc_reporter
|
103
|
+
- run_rspec_tests:
|
104
|
+
requires:
|
105
|
+
- run_bundler
|
106
|
+
- upload_cc_coverage:
|
107
|
+
requires:
|
108
|
+
- run_rspec_tests
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'coins_paid_api'
|
3
|
+
s.authors = ['Artem Biserov(artembiserov)', 'Oleg Ivanov(morhekil)']
|
4
|
+
s.version = '1.0.1'
|
5
|
+
s.files = `git ls-files`.split("\n")
|
6
|
+
s.summary = 'Coins Paid Integration'
|
7
|
+
s.license = 'Nonstandard'
|
8
|
+
|
9
|
+
s.add_runtime_dependency 'dry-initializer', '~> 3.0'
|
10
|
+
s.add_runtime_dependency 'dry-struct', '~> 1.0'
|
11
|
+
s.add_runtime_dependency 'faraday', '~> 0.12'
|
12
|
+
s.add_runtime_dependency 'faraday_middleware', '~> 0.11'
|
13
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
14
|
+
s.add_development_dependency 'webmock', '~> 3.7'
|
15
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'dry-struct'
|
3
|
+
require 'json'
|
4
|
+
require 'faraday'
|
5
|
+
require 'faraday_middleware'
|
6
|
+
require_relative 'api/types'
|
7
|
+
require_relative 'api/signature'
|
8
|
+
require_relative 'api/requester'
|
9
|
+
require_relative 'api/transport'
|
10
|
+
require_relative 'api/callback_data'
|
11
|
+
require_relative 'api/currencies_list'
|
12
|
+
require_relative 'api/take_address'
|
13
|
+
require_relative 'api/withdrawal'
|
14
|
+
|
15
|
+
module CoinsPaid
|
16
|
+
module API
|
17
|
+
module_function
|
18
|
+
|
19
|
+
Error = Class.new RuntimeError
|
20
|
+
ProcessingError = Class.new Error
|
21
|
+
ConnectionError = Class.new Error
|
22
|
+
InvalidSignatureError = Class.new Error
|
23
|
+
|
24
|
+
URL = 'https://app.coinspaid.com/api/v2/'
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_accessor :public_key
|
28
|
+
attr_accessor :secret_key
|
29
|
+
end
|
30
|
+
|
31
|
+
@public_key = ENV['COINS_PAID_PUBLIC_KEY']
|
32
|
+
@secret_key = ENV['COINS_PAID_SECRET_KEY']
|
33
|
+
|
34
|
+
def configure
|
35
|
+
yield self
|
36
|
+
end
|
37
|
+
|
38
|
+
def take_address(foreign_id:, currency:, convert_to:)
|
39
|
+
Requester.call(
|
40
|
+
TakeAddress,
|
41
|
+
foreign_id: foreign_id, currency: currency, convert_to: convert_to
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def withdraw(foreign_id:, amount:, currency:, convert_to:, address:)
|
46
|
+
Requester.call(
|
47
|
+
Withdrawal,
|
48
|
+
foreign_id: foreign_id, amount: amount, currency: currency, convert_to: convert_to, address: address
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def currencies_list
|
53
|
+
CurrenciesList.call
|
54
|
+
end
|
55
|
+
|
56
|
+
def callback(request_body, headers)
|
57
|
+
Signature.check!(
|
58
|
+
request_body: request_body,
|
59
|
+
key: headers['X-Processing-Key'],
|
60
|
+
signature: headers['X-Processing-Signature']
|
61
|
+
)
|
62
|
+
|
63
|
+
CallbackData.from_json(JSON.parse(request_body, symbolize_names: true))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
class CallbackData < Dry::Struct
|
6
|
+
NOT_CONFIRMED = 'not_confirmed'
|
7
|
+
CANCELLED = 'cancelled'
|
8
|
+
TNX_TYPE_BLOCKCHAIN = 'blockchain'
|
9
|
+
|
10
|
+
attribute :id, Types::Integer
|
11
|
+
attribute :foreign_id, Types::String
|
12
|
+
attribute? :type, Types::String
|
13
|
+
attribute? :status, Types::String
|
14
|
+
attribute? :error, Types::Coercible::String
|
15
|
+
|
16
|
+
attribute :crypto_address do
|
17
|
+
attribute :currency, Types::String
|
18
|
+
end
|
19
|
+
|
20
|
+
attribute? :transactions, Types::Array do
|
21
|
+
attribute :transaction_type, Types::String
|
22
|
+
attribute :id, Types::Integer
|
23
|
+
end
|
24
|
+
|
25
|
+
attribute? :currency_sent do
|
26
|
+
attribute :amount, Types::String
|
27
|
+
end
|
28
|
+
|
29
|
+
attribute? :currency_received do
|
30
|
+
attribute :amount, Types::String
|
31
|
+
attribute? :amount_minus_fee, Types::String
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.from_json(attributes)
|
35
|
+
attributes[:foreign_id] ||= attributes.dig(:crypto_address, :foreign_id)
|
36
|
+
new(attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def blockchain_trx_id
|
40
|
+
transactions.find { |tr| tr.transaction_type == TNX_TYPE_BLOCKCHAIN }.id
|
41
|
+
end
|
42
|
+
|
43
|
+
def pending?
|
44
|
+
status == NOT_CONFIRMED
|
45
|
+
end
|
46
|
+
|
47
|
+
def cancelled?
|
48
|
+
status == CANCELLED
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'currency'
|
3
|
+
|
4
|
+
module CoinsPaid
|
5
|
+
module API
|
6
|
+
module CurrenciesList
|
7
|
+
module_function
|
8
|
+
|
9
|
+
class Response < Dry::Struct
|
10
|
+
attribute :data, Types::Array.of(Currency)
|
11
|
+
end
|
12
|
+
|
13
|
+
PATH = 'currencies/list'
|
14
|
+
|
15
|
+
def call
|
16
|
+
Response.new(data: Transport.post(PATH)).data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
class Currency < Dry::Struct
|
6
|
+
transform_keys(&:to_sym)
|
7
|
+
|
8
|
+
attribute :id, Types::Integer
|
9
|
+
attribute :type, Types::String
|
10
|
+
attribute :currency, Types::String
|
11
|
+
attribute :minimum_amount, Types::JSON::Decimal
|
12
|
+
attribute :deposit_fee_percent, Types::JSON::Decimal
|
13
|
+
attribute :withdrawal_fee_percent, Types::JSON::Decimal
|
14
|
+
attribute :precision, Types::Integer
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
module Requester
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def call(api, data)
|
9
|
+
request_data = api::Request.new(data)
|
10
|
+
Transport.post(api::PATH, request_data.to_hash)
|
11
|
+
.yield_self { |response| parse(response) }
|
12
|
+
.yield_self { |parsed_response| api::Response.new(parsed_response) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parse(response)
|
18
|
+
response.transform_keys { |key| key.to_s == 'id' ? :external_id : key.to_sym }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
module Signature
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def check!(request_body:, key:, signature:)
|
9
|
+
key == API.public_key && signature == generate(request_body) ||
|
10
|
+
raise(InvalidSignatureError, 'Invalid signature')
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate(request_body)
|
14
|
+
OpenSSL::HMAC.hexdigest('SHA512', API.secret_key, request_body)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
class TakeAddress
|
6
|
+
class Request < Dry::Struct
|
7
|
+
attribute :foreign_id, Types::Coercible::String
|
8
|
+
attribute :currency, Types::String
|
9
|
+
attribute :convert_to, Types::String
|
10
|
+
end
|
11
|
+
|
12
|
+
class Response < Dry::Struct
|
13
|
+
attribute :external_id, Types::Integer
|
14
|
+
attribute :address, Types::String
|
15
|
+
attribute :tag, Types::String.optional
|
16
|
+
end
|
17
|
+
|
18
|
+
PATH = 'addresses/take'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
module Transport
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def post(path, params = {})
|
9
|
+
post_request path, params.to_json
|
10
|
+
rescue Faraday::ParsingError => e
|
11
|
+
raise ConnectionError, e.response.body
|
12
|
+
rescue Faraday::Error => e
|
13
|
+
raise ConnectionError, e
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def post_request(path, params)
|
19
|
+
response = http.post(path, params) do |req|
|
20
|
+
req.headers.merge!(
|
21
|
+
'X-Processing-Key' => API.public_key,
|
22
|
+
'X-Processing-Signature' => Signature.generate(params)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
body = response.body
|
27
|
+
response.success? ? body['data'] : raise(ProcessingError, error_message(body))
|
28
|
+
end
|
29
|
+
|
30
|
+
def error_message(response_body)
|
31
|
+
response_body['error'] || response_body['errors'].values.first
|
32
|
+
end
|
33
|
+
|
34
|
+
def http
|
35
|
+
Faraday.new(url: URL) do |conn|
|
36
|
+
conn.request :json
|
37
|
+
conn.response :json
|
38
|
+
conn.adapter Faraday.default_adapter
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoinsPaid
|
4
|
+
module API
|
5
|
+
class Withdrawal
|
6
|
+
class Request < Dry::Struct
|
7
|
+
attribute :foreign_id, Types::Coercible::String
|
8
|
+
attribute :amount, Types::Coercible::String
|
9
|
+
attribute :currency, Types::String
|
10
|
+
attribute :convert_to, Types::String
|
11
|
+
attribute :address, Types::String
|
12
|
+
end
|
13
|
+
|
14
|
+
class Response < Dry::Struct
|
15
|
+
attribute :external_id, Types::Integer
|
16
|
+
attribute :receiver_amount, Types::Coercible::Float
|
17
|
+
end
|
18
|
+
|
19
|
+
PATH = 'withdrawal/crypto'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'coins_paid/api'
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe CoinsPaid::API, '.callback' do
|
4
|
+
include_context 'coins paid callbacks'
|
5
|
+
let(:key) { double 'key' }
|
6
|
+
let(:signature) { double 'signature' }
|
7
|
+
let(:headers) { { 'X-Processing-Key' => key, 'X-Processing-Signature' => signature } }
|
8
|
+
subject(:callback) { described_class.callback(request_body, headers) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(CoinsPaid::API::Signature).to receive(:check!).with(request_body: request_body, key: key, signature: signature)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when signature is invalid' do
|
15
|
+
let(:request_body) { { key: :value }.to_json }
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(CoinsPaid::API::CallbackData).to receive(:new)
|
19
|
+
allow(CoinsPaid::API::Signature).to receive(:check!).with(request_body: request_body, key: key, signature: signature).and_raise('error')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'raises error' do
|
23
|
+
expect { callback }.to raise_error('error')
|
24
|
+
|
25
|
+
expect(CoinsPaid::API::CallbackData).not_to have_received(:new)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when request_body is deposit callback data' do
|
30
|
+
let(:request_body) { deposit_callback_body.to_json }
|
31
|
+
let(:expected_params) do
|
32
|
+
{
|
33
|
+
id: 2686510,
|
34
|
+
foreign_id: '1234',
|
35
|
+
type: 'deposit_exchange',
|
36
|
+
status: 'confirmed',
|
37
|
+
error: '',
|
38
|
+
crypto_address: {
|
39
|
+
currency: 'BTC'
|
40
|
+
},
|
41
|
+
transactions: [
|
42
|
+
{ transaction_type: 'blockchain', id: 714576 },
|
43
|
+
{ transaction_type: 'exchange', id: 714577 },
|
44
|
+
],
|
45
|
+
currency_sent: { amount: '0.01000000' },
|
46
|
+
currency_received: {
|
47
|
+
amount_minus_fee: '90',
|
48
|
+
amount: '84.17070222'
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it { is_expected.to be_struct_with_params(CoinsPaid::API::CallbackData, expected_params) }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when request_body is cancelled deposit callback data' do
|
57
|
+
let(:request_body) { cancelled_deposit_callback_body.to_json }
|
58
|
+
let(:expected_params) do
|
59
|
+
{
|
60
|
+
id: 2686510,
|
61
|
+
foreign_id: '1234',
|
62
|
+
type: 'deposit_exchange',
|
63
|
+
status: 'cancelled',
|
64
|
+
error: 'Invalid params: expected a hex-encoded hash with 0x prefix.',
|
65
|
+
crypto_address: {
|
66
|
+
currency: 'BTC'
|
67
|
+
},
|
68
|
+
transactions: [
|
69
|
+
{ transaction_type: 'blockchain', id: 714576 },
|
70
|
+
{ transaction_type: 'exchange', id: 714577 },
|
71
|
+
]
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
it { is_expected.to be_struct_with_params(CoinsPaid::API::CallbackData, expected_params) }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when request_body is withdrawal callback data' do
|
79
|
+
let(:request_body) { withdrawal_callback_body.to_json }
|
80
|
+
let(:expected_params) do
|
81
|
+
{
|
82
|
+
id: 1,
|
83
|
+
type: 'withdrawal_exchange',
|
84
|
+
status: 'confirmed',
|
85
|
+
foreign_id: '20',
|
86
|
+
error: '',
|
87
|
+
crypto_address: {
|
88
|
+
currency: 'EUR'
|
89
|
+
},
|
90
|
+
transactions: [
|
91
|
+
{ transaction_type: 'exchange', id: 1 },
|
92
|
+
{ transaction_type: 'blockchain', id: 1 },
|
93
|
+
],
|
94
|
+
currency_sent: { amount: '381' },
|
95
|
+
currency_received: {
|
96
|
+
amount: '0.01000000'
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
it { is_expected.to be_struct_with_params(CoinsPaid::API::CallbackData, expected_params) }
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when request_body is cancelled withdrawal callback data' do
|
105
|
+
let(:request_body) { cancelled_withdrawal_callback_body.to_json }
|
106
|
+
let(:expected_params) do
|
107
|
+
{
|
108
|
+
id: 2686510,
|
109
|
+
type: 'withdrawal_exchange',
|
110
|
+
status: 'cancelled',
|
111
|
+
foreign_id: '20',
|
112
|
+
error: 'Invalid params: expected a hex-encoded hash with 0x prefix.',
|
113
|
+
crypto_address: {
|
114
|
+
currency: 'EUR'
|
115
|
+
},
|
116
|
+
transactions: [
|
117
|
+
{ transaction_type: 'exchange', id: 714576 },
|
118
|
+
{ transaction_type: 'blockchain', id: 714577 },
|
119
|
+
]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
it { is_expected.to be_struct_with_params(CoinsPaid::API::CallbackData, expected_params) }
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe CoinsPaid::API::Signature, '#check!' do
|
4
|
+
let(:key) { 'publickey' }
|
5
|
+
let(:signature) { 'd2b3292793cb1f527dab4c9d8128356a0df7635aa1796a4d45276646ce914dcf29bb9244aed750a3a5b7d26aabb44ba560b05ed1233168107bed4ca684522508' }
|
6
|
+
let(:request_body) { { key: :value }.to_json }
|
7
|
+
subject(:check) { described_class.check!(key: key, signature: signature, request_body: request_body) }
|
8
|
+
|
9
|
+
context 'when key and signature are valid' do
|
10
|
+
it { is_expected.to be_truthy }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when key is invalid' do
|
14
|
+
let(:key) { 'invalidkey' }
|
15
|
+
|
16
|
+
it 'raises invalid signature error' do
|
17
|
+
expect { check }.to raise_error(CoinsPaid::API::InvalidSignatureError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when signature is invalid' do
|
22
|
+
let(:signature) { 'invalidsignature' }
|
23
|
+
|
24
|
+
it 'raises invalid signature error' do
|
25
|
+
expect { check }.to raise_error(CoinsPaid::API::InvalidSignatureError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './request_examples'
|
4
|
+
|
5
|
+
describe CoinsPaid::API, '.currencies_list' do
|
6
|
+
endpoint = 'https://app.coinspaid.com/api/v2/currencies/list'
|
7
|
+
include_context 'CoinsPaid API request'
|
8
|
+
|
9
|
+
let(:expected_currencies) do
|
10
|
+
[
|
11
|
+
{
|
12
|
+
currency: 'BTC',
|
13
|
+
deposit_fee_percent: 0.008,
|
14
|
+
id: 1,
|
15
|
+
minimum_amount: 0.0001,
|
16
|
+
precision: 8,
|
17
|
+
type: 'crypto',
|
18
|
+
withdrawal_fee_percent: 0.0
|
19
|
+
},
|
20
|
+
{
|
21
|
+
currency: 'LTC',
|
22
|
+
deposit_fee_percent: 0.008,
|
23
|
+
id: 2,
|
24
|
+
minimum_amount: 0.01,
|
25
|
+
precision: 8,
|
26
|
+
type: 'crypto',
|
27
|
+
withdrawal_fee_percent: 0.0
|
28
|
+
}
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
subject(:response) { described_class.currencies_list }
|
33
|
+
|
34
|
+
let(:response_data) do
|
35
|
+
{
|
36
|
+
'data' => [
|
37
|
+
{
|
38
|
+
'currency' => 'BTC',
|
39
|
+
'deposit_fee_percent' => '0.008',
|
40
|
+
'id' => 1,
|
41
|
+
'minimum_amount' => '0.00010000',
|
42
|
+
'precision' => 8,
|
43
|
+
'type' => 'crypto',
|
44
|
+
'withdrawal_fee_percent' => '0'
|
45
|
+
},
|
46
|
+
{
|
47
|
+
'currency' => 'LTC',
|
48
|
+
'deposit_fee_percent' => '0.008',
|
49
|
+
'id' => 2,
|
50
|
+
'minimum_amount' => '0.01000000',
|
51
|
+
'precision' => 8,
|
52
|
+
'type' => 'crypto',
|
53
|
+
'withdrawal_fee_percent' => '0'
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns valid response if successful' do
|
60
|
+
stub_request(:post, endpoint)
|
61
|
+
.with(body: '{}', headers: request_signature_headers)
|
62
|
+
.to_return(status: 200, body: response_data.to_json)
|
63
|
+
|
64
|
+
currencies = expected_currencies.map { |data| be_struct_with_params(described_class::Currency, data) }
|
65
|
+
expect(response).to match_array currencies
|
66
|
+
end
|
67
|
+
|
68
|
+
it_behaves_like 'CoinsPaid API error handling', endpoint: endpoint
|
69
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_context 'CoinsPaid API request' do |request_data: {}|
|
4
|
+
let(:signature) { 'c01dc0ffee' }
|
5
|
+
let(:request_signature_headers) do
|
6
|
+
{
|
7
|
+
'X-Processing-Key' => described_class.public_key,
|
8
|
+
'X-Processing-Signature' => signature
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(CoinsPaid::API::Signature).to receive(:generate).with(request_data.to_json).and_return signature
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.shared_examples 'CoinsPaid API error handling' do |endpoint:, request_body: '{}'|
|
18
|
+
context 'when coins paid responded with validation errors' do
|
19
|
+
let(:response_data) do
|
20
|
+
{
|
21
|
+
'errors' => {
|
22
|
+
'field' => 'This field is wrong'
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
stub_request(:post, endpoint)
|
29
|
+
.with(body: request_body)
|
30
|
+
.to_return(status: 400, body: response_data.to_json)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raises processing error' do
|
34
|
+
expect { subject }.to raise_error(CoinsPaid::API::ProcessingError, 'This field is wrong')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when coins paid responded with authorization error' do
|
39
|
+
let(:response_data) do
|
40
|
+
{
|
41
|
+
'error' => 'Bad signature header',
|
42
|
+
'code' => 'bad_header_signature'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
before do
|
47
|
+
stub_request(:post, endpoint)
|
48
|
+
.with(body: request_body)
|
49
|
+
.to_return(status: 403, body: response_data.to_json)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'raises processing error' do
|
53
|
+
expect { subject }.to raise_error(CoinsPaid::API::ProcessingError, 'Bad signature header')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when coins paid responds with internal server error' do
|
58
|
+
let(:response_data) { 'Internal server error' }
|
59
|
+
|
60
|
+
before do
|
61
|
+
stub_request(:post, endpoint)
|
62
|
+
.to_return(status: 500, body: response_data)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'raises processing error' do
|
66
|
+
expect { subject }.to raise_error(CoinsPaid::API::ConnectionError, 'Internal server error')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when request timeout' do
|
71
|
+
before do
|
72
|
+
stub_request(:post, endpoint)
|
73
|
+
.to_timeout
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises connection error' do
|
77
|
+
expect { subject }.to raise_error(CoinsPaid::API::ConnectionError, 'execution expired')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
ENV['COINS_PAID_PUBLIC_KEY'] = 'publickey'
|
2
|
+
ENV['COINS_PAID_SECRET_KEY'] = 'secretkey'
|
3
|
+
|
4
|
+
if ENV['CIRCLE_ARTIFACTS']
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'webmock/rspec'
|
10
|
+
require 'pry'
|
11
|
+
require 'coins_paid_api'
|
12
|
+
require_relative 'support/shared_data'
|
13
|
+
require_relative 'support/struct_like'
|
@@ -0,0 +1,202 @@
|
|
1
|
+
shared_context 'coins paid callbacks' do
|
2
|
+
let(:deposit_callback_body) do
|
3
|
+
{
|
4
|
+
'id' => 2686510,
|
5
|
+
'type' => 'deposit_exchange',
|
6
|
+
'crypto_address' => {
|
7
|
+
'id' => 382270,
|
8
|
+
'currency' => 'BTC',
|
9
|
+
'convert_to' => 'EUR',
|
10
|
+
'address' => '123abc',
|
11
|
+
'tag' => nil,
|
12
|
+
'foreign_id' => '1234'
|
13
|
+
},
|
14
|
+
'currency_sent' => {
|
15
|
+
'currency' => 'BTC',
|
16
|
+
'amount' => '0.01000000'
|
17
|
+
},
|
18
|
+
'currency_received' => {
|
19
|
+
'currency' => 'EUR',
|
20
|
+
'amount' => '84.17070222',
|
21
|
+
'amount_minus_fee' => '90'
|
22
|
+
},
|
23
|
+
'transactions' => [
|
24
|
+
{
|
25
|
+
'id' => 714576,
|
26
|
+
'currency' => 'BTC',
|
27
|
+
'transaction_type' => 'blockchain',
|
28
|
+
'type' => 'deposit',
|
29
|
+
'address' => '31vnLqxVJ1iShJ5Ly586q8XKucECx12bZS',
|
30
|
+
'tag' => nil,
|
31
|
+
'amount' => '0.01000000',
|
32
|
+
'txid' => '3a491da90a1ce5a318d0aeff6867ab98a03219abae29ed68d702291703c3538b',
|
33
|
+
'riskscore' => '0.42',
|
34
|
+
'confirmations' => '1'
|
35
|
+
},
|
36
|
+
{
|
37
|
+
'id' => 714577,
|
38
|
+
'currency' => 'BTC',
|
39
|
+
'currency_to' => 'EUR',
|
40
|
+
'transaction_type' => 'exchange',
|
41
|
+
'type' => 'exchange',
|
42
|
+
'amount' => '0.01000000',
|
43
|
+
'amount_to' => '84.17070222'
|
44
|
+
}
|
45
|
+
],
|
46
|
+
'fees' => [
|
47
|
+
{
|
48
|
+
'type' => 'exchange',
|
49
|
+
'currency' => 'EUR',
|
50
|
+
'amount' => '4.20853511'
|
51
|
+
}
|
52
|
+
],
|
53
|
+
'error' => nil,
|
54
|
+
'status' => 'confirmed'
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:cancelled_deposit_callback_body) do
|
59
|
+
{
|
60
|
+
'id' => 2686510,
|
61
|
+
'type' => 'deposit_exchange',
|
62
|
+
'crypto_address' => {
|
63
|
+
'id' => 382270,
|
64
|
+
'currency' => 'BTC',
|
65
|
+
'convert_to' => 'EUR',
|
66
|
+
'address' => '123abc',
|
67
|
+
'tag' => nil,
|
68
|
+
'foreign_id' => '1234'
|
69
|
+
},
|
70
|
+
'transactions' => [
|
71
|
+
{
|
72
|
+
'id' => 714576,
|
73
|
+
'currency' => 'BTC',
|
74
|
+
'transaction_type' => 'blockchain',
|
75
|
+
'type' => 'deposit',
|
76
|
+
'address' => '31vnLqxVJ1iShJ5Ly586q8XKucECx12bZS',
|
77
|
+
'tag' => nil,
|
78
|
+
'amount' => '0.01000000',
|
79
|
+
'txid' => '3a491da90a1ce5a318d0aeff6867ab98a03219abae29ed68d702291703c3538b',
|
80
|
+
'riskscore' => '0.42',
|
81
|
+
'confirmations' => '1'
|
82
|
+
},
|
83
|
+
{
|
84
|
+
'id' => 714577,
|
85
|
+
'currency' => 'BTC',
|
86
|
+
'currency_to' => 'EUR',
|
87
|
+
'transaction_type' => 'exchange',
|
88
|
+
'type' => 'exchange',
|
89
|
+
'amount' => '0.01000000',
|
90
|
+
'amount_to' => '84.17070222'
|
91
|
+
}
|
92
|
+
],
|
93
|
+
'fees' => [
|
94
|
+
{
|
95
|
+
'type' => 'exchange',
|
96
|
+
'currency' => 'EUR',
|
97
|
+
'amount' => '4.20853511'
|
98
|
+
}
|
99
|
+
],
|
100
|
+
'error' => 'Invalid params: expected a hex-encoded hash with 0x prefix.',
|
101
|
+
'status' => 'cancelled'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
let(:withdrawal_callback_body) do
|
106
|
+
{
|
107
|
+
'id' => 1,
|
108
|
+
'foreign_id' => '20',
|
109
|
+
'type' => 'withdrawal_exchange',
|
110
|
+
'crypto_address' => {
|
111
|
+
'id' => 1,
|
112
|
+
'currency' => 'EUR',
|
113
|
+
'convert_to' => 'BTC',
|
114
|
+
'address' => '1k2btnz8cqnfbphaq729mdj8w6g3w2nbbl',
|
115
|
+
'tag' => nil
|
116
|
+
},
|
117
|
+
'currency_sent' => {
|
118
|
+
'currency' => 'EUR',
|
119
|
+
'amount' => '381'
|
120
|
+
},
|
121
|
+
'currency_received' => {
|
122
|
+
'currency' => 'BTC',
|
123
|
+
'amount' => '0.01000000'
|
124
|
+
},
|
125
|
+
'transactions' => [
|
126
|
+
{
|
127
|
+
'id' => 1,
|
128
|
+
'currency' => 'EUR',
|
129
|
+
'currency_to' => 'BTC',
|
130
|
+
'transaction_type' => 'exchange',
|
131
|
+
'type' => 'exchange',
|
132
|
+
'amount' => 381,
|
133
|
+
'amount_to' => 0.108823
|
134
|
+
},
|
135
|
+
{
|
136
|
+
'id' => 1,
|
137
|
+
'currency' => 'BTC',
|
138
|
+
'transaction_type' => 'blockchain',
|
139
|
+
'type' => 'withdrawal',
|
140
|
+
'address' => '1k2btnz8cqnfbphaq729mdj8w6g3w2nbbl',
|
141
|
+
'tag' => nil,
|
142
|
+
'amount' => 0.108823,
|
143
|
+
'txid' => 'aa3345b96389e126f1ce88a670d1b1e38f2c3f73fb3ecfff8d9da1b1ce6964a6',
|
144
|
+
'confirmations' => 3
|
145
|
+
}
|
146
|
+
],
|
147
|
+
'fees' => [
|
148
|
+
{
|
149
|
+
'type' => 'exchange',
|
150
|
+
'currency' => 'EUR',
|
151
|
+
'amount' => '3.04800000'
|
152
|
+
},
|
153
|
+
{
|
154
|
+
'type' => 'mining',
|
155
|
+
'currency' => 'BTC',
|
156
|
+
'amount' => '0.00003990'
|
157
|
+
}
|
158
|
+
],
|
159
|
+
'error' => '',
|
160
|
+
'status' => 'confirmed'
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
let(:cancelled_withdrawal_callback_body) do
|
165
|
+
{
|
166
|
+
'id' => 2686510,
|
167
|
+
'type' => 'withdrawal_exchange',
|
168
|
+
'foreign_id' => '20',
|
169
|
+
'crypto_address' => {
|
170
|
+
'id' => 382270,
|
171
|
+
'currency' => 'EUR',
|
172
|
+
'address' => '1k2btnz8cqnfbphaq729mdj8w6g3w2nbbl',
|
173
|
+
'tag' => nil
|
174
|
+
},
|
175
|
+
'transactions' => [
|
176
|
+
{
|
177
|
+
'id' => 714576,
|
178
|
+
'currency' => 'BTC',
|
179
|
+
'currency_to' => 'EUR',
|
180
|
+
'transaction_type' => 'exchange',
|
181
|
+
'type' => 'exchange',
|
182
|
+
'amount' => '0.01000000',
|
183
|
+
'amount_to' => '84.17070222'
|
184
|
+
},
|
185
|
+
{
|
186
|
+
'id' => 714577,
|
187
|
+
'currency' => 'BTC',
|
188
|
+
'transaction_type' => 'blockchain',
|
189
|
+
'type' => 'withdrawal',
|
190
|
+
'address' => '31vnlqxvj1ishj5ly586q8xkucecx12bzs',
|
191
|
+
'tag' => nil,
|
192
|
+
'amount' => '0.01000000',
|
193
|
+
'txid' => '3a491da90a1ce5a318d0aeff6867ab98a03219abae29ed68d702291703c3538b',
|
194
|
+
'confirmations' => '0'
|
195
|
+
}
|
196
|
+
],
|
197
|
+
'fees' => [],
|
198
|
+
'error' => 'Invalid params: expected a hex-encoded hash with 0x prefix.',
|
199
|
+
'status' => 'cancelled'
|
200
|
+
}
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './request_examples'
|
4
|
+
|
5
|
+
describe CoinsPaid::API, '.take_address' do
|
6
|
+
endpoint = 'https://app.coinspaid.com/api/v2/addresses/take'
|
7
|
+
request_data = {
|
8
|
+
foreign_id: 'user-id:2048',
|
9
|
+
currency: 'BTC',
|
10
|
+
convert_to: 'EUR'
|
11
|
+
}
|
12
|
+
include_context 'CoinsPaid API request', request_data: request_data
|
13
|
+
|
14
|
+
let(:expected_address_attributes) do
|
15
|
+
{
|
16
|
+
external_id: 1,
|
17
|
+
address: '12983h13ro1hrt24it432t',
|
18
|
+
tag: 'tag-123'
|
19
|
+
}
|
20
|
+
end
|
21
|
+
subject(:take_address) { described_class.take_address(request_data) }
|
22
|
+
|
23
|
+
let(:response_data) do
|
24
|
+
{
|
25
|
+
'data' => {
|
26
|
+
'id' => 1,
|
27
|
+
'currency' => 'BTC',
|
28
|
+
'convert_to' => 'EUR',
|
29
|
+
'address' => '12983h13ro1hrt24it432t',
|
30
|
+
'tag' => 'tag-123',
|
31
|
+
'foreign_id' => 'user-id:2048'
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns valid response if successful' do
|
37
|
+
stub_request(:post, endpoint)
|
38
|
+
.with(body: request_data, headers: request_signature_headers)
|
39
|
+
.to_return(status: 201, body: response_data.to_json)
|
40
|
+
|
41
|
+
expect(take_address).to be_struct_with_params(CoinsPaid::API::TakeAddress::Response, expected_address_attributes)
|
42
|
+
end
|
43
|
+
|
44
|
+
it_behaves_like 'CoinsPaid API error handling', endpoint: endpoint, request_body: request_data
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './request_examples'
|
4
|
+
|
5
|
+
describe CoinsPaid::API, '.withdraw' do
|
6
|
+
endpoint = 'https://app.coinspaid.com/api/v2/withdrawal/crypto'
|
7
|
+
request_data = {
|
8
|
+
foreign_id: 'user-id:2048',
|
9
|
+
amount: '0.01',
|
10
|
+
currency: 'EUR',
|
11
|
+
convert_to: 'BTC',
|
12
|
+
address: 'abc123'
|
13
|
+
}
|
14
|
+
include_context 'CoinsPaid API request', request_data: request_data
|
15
|
+
|
16
|
+
let(:response_data) do
|
17
|
+
{
|
18
|
+
'data' => {
|
19
|
+
'id' => 1,
|
20
|
+
'foreign_id' => 'user-id:2048',
|
21
|
+
'type' => 'withdrawal',
|
22
|
+
'status' => 'processing',
|
23
|
+
'amount' => '10.00000000',
|
24
|
+
'sender_amount' => '10.00000000',
|
25
|
+
'sender_currency' => 'EUR',
|
26
|
+
'receiver_amount' => '0.00100000',
|
27
|
+
'receiver_currency' => 'BTC'
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
let(:expected_withdrawal_attributes) do
|
32
|
+
{
|
33
|
+
external_id: 1,
|
34
|
+
receiver_amount: 0.001
|
35
|
+
}
|
36
|
+
end
|
37
|
+
subject(:withdraw) { described_class.withdraw(request_data) }
|
38
|
+
|
39
|
+
context 'when response is successful' do
|
40
|
+
before do
|
41
|
+
stub_request(:post, endpoint)
|
42
|
+
.with(body: request_data, headers: request_signature_headers)
|
43
|
+
.to_return(status: 201, body: response_data.to_json)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns valid response' do
|
47
|
+
expect(withdraw).to be_struct_with_params(CoinsPaid::API::Withdrawal::Response, expected_withdrawal_attributes)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: coins_paid_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Artem Biserov(artembiserov)
|
8
|
+
- Oleg Ivanov(morhekil)
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2019-11-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: dry-initializer
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '3.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: dry-struct
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: faraday
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.12'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.12'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: faraday_middleware
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.11'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.11'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '3.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '3.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: webmock
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '3.7'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.7'
|
98
|
+
description:
|
99
|
+
email:
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".circleci/config.yml"
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- Gemfile
|
108
|
+
- coins_paid_api.gemspec
|
109
|
+
- lib/coins_paid/api.rb
|
110
|
+
- lib/coins_paid/api/callback_data.rb
|
111
|
+
- lib/coins_paid/api/currencies_list.rb
|
112
|
+
- lib/coins_paid/api/currency.rb
|
113
|
+
- lib/coins_paid/api/requester.rb
|
114
|
+
- lib/coins_paid/api/signature.rb
|
115
|
+
- lib/coins_paid/api/take_address.rb
|
116
|
+
- lib/coins_paid/api/transport.rb
|
117
|
+
- lib/coins_paid/api/types.rb
|
118
|
+
- lib/coins_paid/api/withdrawal.rb
|
119
|
+
- lib/coins_paid_api.rb
|
120
|
+
- spec/callback_spec.rb
|
121
|
+
- spec/check_signature_spec.rb
|
122
|
+
- spec/currencies_list_spec.rb
|
123
|
+
- spec/request_examples.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
- spec/support/shared_data.rb
|
126
|
+
- spec/support/struct_like.rb
|
127
|
+
- spec/take_address_spec.rb
|
128
|
+
- spec/withdrawal_spec.rb
|
129
|
+
homepage:
|
130
|
+
licenses:
|
131
|
+
- Nonstandard
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 2.7.6
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: Coins Paid Integration
|
153
|
+
test_files: []
|