mandarin-api 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 +17 -0
- data/.rspec +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +99 -0
- data/LICENSE +21 -0
- data/README.md +66 -0
- data/lib/mandarin-api.rb +2 -0
- data/lib/mandarin_api.rb +44 -0
- data/lib/mandarin_api/card_manager.rb +12 -0
- data/lib/mandarin_api/configuration.rb +5 -0
- data/lib/mandarin_api/payment_manager.rb +24 -0
- data/lib/mandarin_api/responder.rb +40 -0
- data/lib/mandarin_api/wrapper.rb +65 -0
- data/mandarin-api.gemspec +27 -0
- data/plantuml/callbacks.puml +18 -0
- data/spec/lib/mandarin_api/card_manager_spec.rb +23 -0
- data/spec/lib/mandarin_api/configuration_spec.rb +21 -0
- data/spec/lib/mandarin_api/payment_manager_spec.rb +31 -0
- data/spec/lib/mandarin_api/responder_spec.rb +93 -0
- data/spec/lib/mandarin_api/wrapper_spec.rb +67 -0
- data/spec/lib/mandarin_api_spec.rb +21 -0
- data/spec/mocks.rb +50 -0
- data/spec/spec_helper.rb +20 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 15f0096f101bdda10a4a47882006909f52ef444f
|
4
|
+
data.tar.gz: b4fa4f3db47d56941cb6f4e3f5dfd4bed2585c46
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2be9a637c30f3b11e8e22de6de3b0e22c2f3c9816709e23431d26be138aedc65a6c31ad2c8aeca7b0c04cd2a097c4057722f650ab256d0d29f6699d824b9765c
|
7
|
+
data.tar.gz: 60c27b365ca7db424245972da36f246ac8bb34605c5008f8384587e6049782be68c1886cd665b842fa625b2c0b3f7a5ac33ab9ce6f79add886b5d17e885e2bee
|
data/.gitignore
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
log/*
|
2
|
+
!/log/.keep
|
3
|
+
/tmp
|
4
|
+
#/config/*.yml
|
5
|
+
#/config/unicorn.rb
|
6
|
+
.idea
|
7
|
+
.versions.conf
|
8
|
+
public/assets
|
9
|
+
public/system
|
10
|
+
vendor/bundle
|
11
|
+
#!public/favicon.ico
|
12
|
+
*.DS_Store
|
13
|
+
.env
|
14
|
+
rspec_results.html
|
15
|
+
backup/.*
|
16
|
+
coverage
|
17
|
+
.rubocop.yml
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mandarin-api (0.0.1)
|
5
|
+
dry-configurable (>= 0.1.0, < 1.0)
|
6
|
+
rest-client (>= 2.0, < 3.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.4.0)
|
12
|
+
ast (2.3.0)
|
13
|
+
coderay (1.1.1)
|
14
|
+
concurrent-ruby (1.0.2)
|
15
|
+
crack (0.4.3)
|
16
|
+
safe_yaml (~> 1.0.0)
|
17
|
+
diff-lcs (1.2.5)
|
18
|
+
domain_name (0.5.20160826)
|
19
|
+
unf (>= 0.0.5, < 1.0.0)
|
20
|
+
dry-configurable (0.1.7)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
faker (1.6.6)
|
23
|
+
i18n (~> 0.5)
|
24
|
+
hashdiff (0.3.0)
|
25
|
+
http-cookie (1.0.3)
|
26
|
+
domain_name (~> 0.5)
|
27
|
+
i18n (0.7.0)
|
28
|
+
json (1.8.3)
|
29
|
+
method_source (0.8.2)
|
30
|
+
mime-types (3.1)
|
31
|
+
mime-types-data (~> 3.2015)
|
32
|
+
mime-types-data (3.2016.0521)
|
33
|
+
netrc (0.11.0)
|
34
|
+
parser (2.3.1.4)
|
35
|
+
ast (~> 2.2)
|
36
|
+
powerpack (0.1.1)
|
37
|
+
pry (0.10.4)
|
38
|
+
coderay (~> 1.1.0)
|
39
|
+
method_source (~> 0.8.1)
|
40
|
+
slop (~> 3.4)
|
41
|
+
pry-doc (0.9.0)
|
42
|
+
pry (~> 0.9)
|
43
|
+
yard (~> 0.8)
|
44
|
+
rainbow (2.1.0)
|
45
|
+
rake (11.3.0)
|
46
|
+
rdoc (4.2.2)
|
47
|
+
json (~> 1.4)
|
48
|
+
rest-client (2.0.0)
|
49
|
+
http-cookie (>= 1.0.2, < 2.0)
|
50
|
+
mime-types (>= 1.16, < 4.0)
|
51
|
+
netrc (~> 0.8)
|
52
|
+
rspec (3.5.0)
|
53
|
+
rspec-core (~> 3.5.0)
|
54
|
+
rspec-expectations (~> 3.5.0)
|
55
|
+
rspec-mocks (~> 3.5.0)
|
56
|
+
rspec-core (3.5.4)
|
57
|
+
rspec-support (~> 3.5.0)
|
58
|
+
rspec-expectations (3.5.0)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.5.0)
|
61
|
+
rspec-mocks (3.5.0)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.5.0)
|
64
|
+
rspec-support (3.5.0)
|
65
|
+
rubocop (0.44.1)
|
66
|
+
parser (>= 2.3.1.1, < 3.0)
|
67
|
+
powerpack (~> 0.1)
|
68
|
+
rainbow (>= 1.99.1, < 3.0)
|
69
|
+
ruby-progressbar (~> 1.7)
|
70
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
71
|
+
ruby-progressbar (1.8.1)
|
72
|
+
safe_yaml (1.0.4)
|
73
|
+
slop (3.6.0)
|
74
|
+
unf (0.1.4)
|
75
|
+
unf_ext
|
76
|
+
unf_ext (0.0.7.2)
|
77
|
+
unicode-display_width (1.1.1)
|
78
|
+
webmock (2.1.0)
|
79
|
+
addressable (>= 2.3.6)
|
80
|
+
crack (>= 0.3.2)
|
81
|
+
hashdiff
|
82
|
+
yard (0.9.5)
|
83
|
+
|
84
|
+
PLATFORMS
|
85
|
+
ruby
|
86
|
+
|
87
|
+
DEPENDENCIES
|
88
|
+
faker (>= 1.6, < 1.7)
|
89
|
+
mandarin-api!
|
90
|
+
pry (~> 0)
|
91
|
+
pry-doc (~> 0)
|
92
|
+
rake
|
93
|
+
rdoc (>= 2.4.2, < 5.0)
|
94
|
+
rspec (~> 3.0)
|
95
|
+
rubocop (~> 0)
|
96
|
+
webmock (~> 2.1)
|
97
|
+
|
98
|
+
BUNDLED WITH
|
99
|
+
1.12.5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Bogaevsky
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# mandarin-api-rb
|
2
|
+
mandarinpay.com api wrapper for ruby
|
3
|
+
[](https://codeclimate.com/github/vbogaevsky/mandarin-api-rb)
|
4
|
+
**mandarin-api** provides necessary API to interact with **Mandarin**.
|
5
|
+
##**Usage:**
|
6
|
+
To use mandarin-api you will need to specify config file with `merchant_id`, `secret`, and `request_url`.
|
7
|
+
###**Example of config:**
|
8
|
+
```
|
9
|
+
MandarinApi.configure do |config|
|
10
|
+
config.merchant_id = 123
|
11
|
+
config.secret = 'secret'
|
12
|
+
config.request_url = 'https://secure.mandarinpay.com'
|
13
|
+
end
|
14
|
+
```
|
15
|
+
###**Example of assigning a card:**
|
16
|
+
```
|
17
|
+
MandarinApi.assign_card user
|
18
|
+
```
|
19
|
+
`user` should be an instance or a Struct, and should respond to `#email` and `#phone` methods
|
20
|
+
`#phone` should be serialized, for example '+79091234567' is correctly serialized number.
|
21
|
+
`#assign_card` will return a hash.
|
22
|
+
###**Example:**
|
23
|
+
```
|
24
|
+
{
|
25
|
+
'id' => '0eb51e74-e704-4c36-b5cb-8f0227621518',
|
26
|
+
'userWebLink' => "https://secure.mandarinpay.com/CardBindings/New?' \
|
27
|
+
'id=0eb51e74-e704-4c36-b5cb-8f0227621518"
|
28
|
+
}
|
29
|
+
```
|
30
|
+
Keep id, it will be used for pay/payouts requests. Use userWebLink to redirect user to Mandarin page for card data input.
|
31
|
+
|
32
|
+
###**Example of performing payout:**
|
33
|
+
```
|
34
|
+
# order_id - id of order/bill, etc. in your system.
|
35
|
+
# amount - sum of payout
|
36
|
+
# assigned_card_uuid - the id you received assigning the card
|
37
|
+
MandarinApi.payout(order_id, amount, assigned_card_uuid)
|
38
|
+
```
|
39
|
+
#payout will return a hash with transaction id.
|
40
|
+
###**Example:**
|
41
|
+
```
|
42
|
+
{ 'id' => '721a5185314740aaa304278fb1d8ee63' }
|
43
|
+
```
|
44
|
+
You will have to provide a link to receive callbacks from Mandarin.
|
45
|
+
MandarinApi.process_callback takes as arguments body of a callback serialized
|
46
|
+
to hash with symbolized keys and an instance with `#success` and `#failure` methods,
|
47
|
+
`#success` and `#failure` methods should take hash with symbolized keys as an argument.
|
48
|
+
###**Example:**
|
49
|
+
```
|
50
|
+
class Application
|
51
|
+
def assign_card(user)
|
52
|
+
MandarinApi.assign_card(user)
|
53
|
+
end
|
54
|
+
def transit(request)
|
55
|
+
MandarinApi.process_callback(request, ResponseHandler.new)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
class ResponseHandler
|
59
|
+
def success(result)
|
60
|
+
# your code here
|
61
|
+
end
|
62
|
+
def failure(result)
|
63
|
+
# your code here
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
data/lib/mandarin-api.rb
ADDED
data/lib/mandarin_api.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-configurable'
|
4
|
+
# Main module
|
5
|
+
module MandarinApi
|
6
|
+
class << self
|
7
|
+
attr_accessor :config
|
8
|
+
end
|
9
|
+
def self.assign_card(user)
|
10
|
+
MandarinApi::CardManager.new.assign_card user
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.pay(_client, _amount)
|
14
|
+
# call api
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.payout(order_id, amount, assigned_card_uuid)
|
18
|
+
params = {
|
19
|
+
order_id: order_id, amount: amount,
|
20
|
+
assigned_card_uuid: assigned_card_uuid
|
21
|
+
}
|
22
|
+
MandarinApi::PaymentManager.new.perform_payout params
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.process_callback(request_params, response_handler)
|
26
|
+
response = MandarinApi::Responder.new.process(request_params)
|
27
|
+
response_handler.success(response.data) if response.success
|
28
|
+
response_handler.failure(response.data) if response.failure
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.config
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.configure
|
36
|
+
yield(config)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'mandarin_api/card_manager'
|
41
|
+
require 'mandarin_api/payment_manager'
|
42
|
+
require 'mandarin_api/responder'
|
43
|
+
require 'mandarin_api/wrapper'
|
44
|
+
require 'mandarin_api/configuration'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module MandarinApi
|
3
|
+
# Manages cards assignment
|
4
|
+
class CardManager
|
5
|
+
def assign_card(user)
|
6
|
+
params = { customer_info: { email: user.email, phone: user.phone } }
|
7
|
+
MandarinApi::Wrapper.new(merchant_id: MandarinApi.config.merchant_id,
|
8
|
+
secret: MandarinApi.config.secret)
|
9
|
+
.request('/api/card-bindings', params)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module MandarinApi
|
3
|
+
# Pergorms payouts and payins
|
4
|
+
class PaymentManager
|
5
|
+
def perform_payout(params)
|
6
|
+
MandarinApi::Wrapper.new(merchant_id: MandarinApi.config.merchant_id,
|
7
|
+
secret: MandarinApi.config.secret)
|
8
|
+
.request('/api/transactions', reques_body(params))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def reques_body(params)
|
14
|
+
{
|
15
|
+
payment: {
|
16
|
+
order_id: params[:order_id], action: 'payout', price: params[:amount]
|
17
|
+
},
|
18
|
+
target: {
|
19
|
+
card: params[:assigned_card_uuid]
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module MandarinApi
|
3
|
+
# Processes callbacks from mandarinpay
|
4
|
+
class Responder
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
@secret = MandarinApi.config.secret
|
8
|
+
if sign_is_valid
|
9
|
+
@process_status = data[:status]
|
10
|
+
else
|
11
|
+
@process_status = 'failed'
|
12
|
+
@data = { sign: 'Wrong signature!' }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def success
|
17
|
+
@process_status == 'success'
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure
|
21
|
+
@process_status == 'failed'
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def sign_is_valid
|
27
|
+
return false if !(@data.nil? || @data.empty?) && @data.class != Hash
|
28
|
+
sign = @data.delete(:sign)
|
29
|
+
Digest::SHA256.hexdigest(temp_string) == sign
|
30
|
+
end
|
31
|
+
|
32
|
+
def temp_string
|
33
|
+
temp_string = ''
|
34
|
+
@data.sort.to_h.keys.each do |key|
|
35
|
+
temp_string += @data[key].to_s + '-'
|
36
|
+
end
|
37
|
+
temp_string + @secret
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
module MandarinApi
|
5
|
+
# Wraps request sending
|
6
|
+
class Wrapper
|
7
|
+
def initialize(merchant_id:, secret:)
|
8
|
+
@merchant_id = merchant_id
|
9
|
+
@secret = secret
|
10
|
+
end
|
11
|
+
|
12
|
+
def request(endpoint, params = {})
|
13
|
+
url = URI.join(MandarinApi.config.request_url, endpoint).to_s
|
14
|
+
RestClient.post(url, json(params), header) do |response|
|
15
|
+
case response.code
|
16
|
+
when 200
|
17
|
+
JSON.parse response.body
|
18
|
+
else
|
19
|
+
{ 'status' => response.status, 'error' => 'Invalid request' }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def header
|
27
|
+
{
|
28
|
+
content_type: :json,
|
29
|
+
x_auth: generate_x_auth_header(@merchant_id, @secret)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_x_auth_header(merchant_id, secret)
|
34
|
+
request_id = SecureRandom.uuid
|
35
|
+
hash = Digest::SHA256.hexdigest "#{merchant_id}-#{request_id}-#{secret}"
|
36
|
+
"#{merchant_id}-#{hash}-#{request_id}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def json(params = {})
|
40
|
+
JSON.generate(key_transform(params))
|
41
|
+
end
|
42
|
+
|
43
|
+
def key_transform(hash)
|
44
|
+
new_hash = {}
|
45
|
+
hash.keys.each do |key|
|
46
|
+
new_hash[camelize(key.to_s)] = if hash[key].class == Hash
|
47
|
+
key_transform hash[key]
|
48
|
+
else
|
49
|
+
hash[key]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
new_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
def camelize(str)
|
56
|
+
str.split('_').map.with_index(0) do |letter, index|
|
57
|
+
if index.zero?
|
58
|
+
letter
|
59
|
+
else
|
60
|
+
letter.capitalize
|
61
|
+
end
|
62
|
+
end.join
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'mandarin-api'
|
4
|
+
s.version = '0.0.1'
|
5
|
+
s.authors = ['Vladimir Bogaevsky', 'Boris Kraportov']
|
6
|
+
s.email = 'gitvbogaevsky@gmail.com'
|
7
|
+
s.licenses = ['MIT']
|
8
|
+
s.summary = 'mandarinpay.com api wrapper for ruby'
|
9
|
+
s.homepage = 'https://github.com/vbogaevsky/mandarin-api-rb'
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
12
|
+
s.extra_rdoc_files = ['README.md', 'plantuml/callbacks.puml']
|
13
|
+
s.require_paths = ['lib']
|
14
|
+
|
15
|
+
s.add_development_dependency('webmock', '~> 2.1')
|
16
|
+
s.add_development_dependency('rspec', '~> 3.0')
|
17
|
+
s.add_development_dependency('pry', '~> 0')
|
18
|
+
s.add_development_dependency('pry-doc', '~> 0')
|
19
|
+
s.add_development_dependency('rdoc', '>= 2.4.2', '< 5.0')
|
20
|
+
s.add_development_dependency('rubocop', '~> 0')
|
21
|
+
s.add_development_dependency('faker', '>= 1.6', '< 1.7')
|
22
|
+
|
23
|
+
s.add_dependency 'rest-client', '>= 2.0', '< 3.0'
|
24
|
+
s.add_dependency 'dry-configurable', '>= 0.1.0', '< 1.0'
|
25
|
+
|
26
|
+
s.required_ruby_version = '>= 2.2.0'
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
@startuml
|
2
|
+
|
3
|
+
boundary Mandarin
|
4
|
+
|
5
|
+
boundary Application
|
6
|
+
entity ResultProcessor
|
7
|
+
control ResponseHandler
|
8
|
+
boundary MandarinApiGem
|
9
|
+
control Responder
|
10
|
+
|
11
|
+
Mandarin -> Application : HttpRequest
|
12
|
+
Application -> MandarinApiGem : RequestParams
|
13
|
+
MandarinApiGem -> Responder : RequestParams
|
14
|
+
Responder -> ResponseHandler : "<font color=green><b>SuccessResponse"
|
15
|
+
Responder -> ResponseHandler : "<font color=red><b>FailureResponse"
|
16
|
+
ResponseHandler -> ResultProcessor : Result
|
17
|
+
|
18
|
+
@enduml
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RSpec.describe MandarinApi::CardManager do
|
2
|
+
describe 'assign_card' do
|
3
|
+
let(:card_manager) { MandarinApi::CardManager.new }
|
4
|
+
let(:merchant_id) { 234 }
|
5
|
+
let(:secret) { 'secret' }
|
6
|
+
let(:params) do
|
7
|
+
{ customer_info: { email: 'ololo@gmail.com', phone: '8-909-999-88-77' } }
|
8
|
+
end
|
9
|
+
it 'calls wrapper instance with args' do
|
10
|
+
User = Struct.new('User', :email, :phone)
|
11
|
+
user = User.new('ololo@gmail.com', '8-909-999-88-77')
|
12
|
+
allow(MandarinApi).to \
|
13
|
+
receive_message_chain(:config, :merchant_id).and_return(merchant_id)
|
14
|
+
allow(MandarinApi).to \
|
15
|
+
receive_message_chain(:config, :secret).and_return(merchant_id)
|
16
|
+
allow_any_instance_of(MandarinApi::Wrapper).to receive(:request)
|
17
|
+
.with('/api/card-bindings', params)
|
18
|
+
expect_any_instance_of(MandarinApi::Wrapper).to receive(:request)
|
19
|
+
.with('/api/card-bindings', params)
|
20
|
+
card_manager.assign_card user
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.describe MandarinApi::Configuration do
|
2
|
+
let(:config) { MandarinApi::Configuration.new }
|
3
|
+
describe '#merchant_id=' do
|
4
|
+
it 'can set value' do
|
5
|
+
config.merchant_id = 123
|
6
|
+
expect(config.merchant_id).to eq 123
|
7
|
+
end
|
8
|
+
end
|
9
|
+
describe '#secret=' do
|
10
|
+
it 'can set value' do
|
11
|
+
config.secret = 'secret'
|
12
|
+
expect(config.secret).to eq 'secret'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
describe '#request_url=' do
|
16
|
+
it 'can set value' do
|
17
|
+
config.request_url = 'www.google.com'
|
18
|
+
expect(config.request_url).to eq 'www.google.com'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
RSpec.describe MandarinApi::PaymentManager do
|
3
|
+
describe '#perform_payout' do
|
4
|
+
let(:payment_manager) { MandarinApi::PaymentManager.new }
|
5
|
+
let(:merchant_id) { 234 }
|
6
|
+
let(:secret) { 'secret' }
|
7
|
+
let(:params) do
|
8
|
+
{
|
9
|
+
order_id: 123_321, amount: 35_000,
|
10
|
+
assigned_card_uuid: '0eb51e74-e704-4c36-b5cb-8f0227621518'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
let(:request_body) do
|
14
|
+
{
|
15
|
+
payment: { order_id: 123_321, action: 'payout', price: 35_000 },
|
16
|
+
target: { card: '0eb51e74-e704-4c36-b5cb-8f0227621518' }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
it 'calls wrapper instance with args' do
|
20
|
+
allow(MandarinApi).to \
|
21
|
+
receive_message_chain(:config, :merchant_id).and_return(merchant_id)
|
22
|
+
allow(MandarinApi).to \
|
23
|
+
receive_message_chain(:config, :secret).and_return(merchant_id)
|
24
|
+
allow_any_instance_of(MandarinApi::Wrapper).to receive(:request)
|
25
|
+
.with('/api/transactions', request_body)
|
26
|
+
expect_any_instance_of(MandarinApi::Wrapper).to receive(:request)
|
27
|
+
.with('/api/transactions', request_body)
|
28
|
+
payment_manager.perform_payout params
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
RSpec.describe MandarinApi::Responder do
|
3
|
+
let(:data) { unsign_data.merge(sign: sign) }
|
4
|
+
let(:responder) { MandarinApi::Responder.new(data) }
|
5
|
+
describe '#success' do
|
6
|
+
context 'success' do
|
7
|
+
let(:unsign_data) do
|
8
|
+
{
|
9
|
+
card_holder: 'VASILY PUPKIN',
|
10
|
+
card_number: '123456XXXXXX7890',
|
11
|
+
card_expiration_year: 18,
|
12
|
+
card_expiration_month: 9,
|
13
|
+
status: 'success'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
let(:sign) do
|
17
|
+
Digest::SHA256.hexdigest(
|
18
|
+
'9-18-VASILY PUPKIN-123456XXXXXX7890-success-secret'
|
19
|
+
)
|
20
|
+
end
|
21
|
+
it 'returns true' do
|
22
|
+
allow(MandarinApi).to \
|
23
|
+
receive_message_chain(:config, :secret).and_return('secret')
|
24
|
+
expect(responder.success).to eq true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context 'failure' do
|
28
|
+
let(:unsign_data) do
|
29
|
+
{
|
30
|
+
card_holder: 'VASILY PUPKIN',
|
31
|
+
card_number: '123456XXXXXX7890',
|
32
|
+
card_expiration_year: 18,
|
33
|
+
card_expiration_month: 9,
|
34
|
+
status: 'failed'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
let(:sign) do
|
38
|
+
Digest::SHA256.hexdigest(
|
39
|
+
'9-18-VASILY PUPKIN-123456XXXXXX7890-failed-secret'
|
40
|
+
)
|
41
|
+
end
|
42
|
+
it 'returns true' do
|
43
|
+
allow(MandarinApi).to \
|
44
|
+
receive_message_chain(:config, :secret).and_return('secret')
|
45
|
+
expect(responder.success).to eq false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
describe '#failure' do
|
50
|
+
context 'success' do
|
51
|
+
let(:unsign_data) do
|
52
|
+
{
|
53
|
+
card_holder: 'VASILY PUPKIN',
|
54
|
+
card_number: '123456XXXXXX7890',
|
55
|
+
card_expiration_year: 18,
|
56
|
+
card_expiration_month: 9,
|
57
|
+
status: 'success'
|
58
|
+
}
|
59
|
+
end
|
60
|
+
let(:sign) do
|
61
|
+
Digest::SHA256.hexdigest(
|
62
|
+
'9-18-VASILY PUPKIN-123456XXXXXX7890-success-secret'
|
63
|
+
)
|
64
|
+
end
|
65
|
+
it 'returns true' do
|
66
|
+
allow(MandarinApi).to \
|
67
|
+
receive_message_chain(:config, :secret).and_return('secret')
|
68
|
+
expect(responder.failure).to eq false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
context 'failure' do
|
72
|
+
let(:unsign_data) do
|
73
|
+
{
|
74
|
+
card_holder: 'VASILY PUPKIN',
|
75
|
+
card_number: '123456XXXXXX7890',
|
76
|
+
card_expiration_year: 18,
|
77
|
+
card_expiration_month: 9,
|
78
|
+
status: 'failed'
|
79
|
+
}
|
80
|
+
end
|
81
|
+
let(:sign) do
|
82
|
+
Digest::SHA256.hexdigest(
|
83
|
+
'9-18-VASILY PUPKIN-123456XXXXXX7890-failed-secret'
|
84
|
+
)
|
85
|
+
end
|
86
|
+
it 'returns true' do
|
87
|
+
allow(MandarinApi).to \
|
88
|
+
receive_message_chain(:config, :secret).and_return('secret')
|
89
|
+
expect(responder.failure).to eq true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe MandarinApi::Wrapper do
|
4
|
+
include_context 'mocks'
|
5
|
+
|
6
|
+
let(:wrapper) do
|
7
|
+
MandarinApi::Wrapper
|
8
|
+
.new(merchant_id: rand(1000), secret: SecureRandom.hex(5))
|
9
|
+
end
|
10
|
+
describe 'request' do
|
11
|
+
let(:params) do
|
12
|
+
{
|
13
|
+
customer_info: {
|
14
|
+
email: 'ololo@gmail.com', phone: '8-909-999-88-77'
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
let(:x_auth) { 'x-auth' }
|
19
|
+
let(:mandarin_adress) { 'https://secure.mandarinpay.com/' }
|
20
|
+
let(:expected) do
|
21
|
+
{
|
22
|
+
'id' => '0eb51e74-e704-4c36-b5cb-8f0227621518',
|
23
|
+
'userWebLink' => 'https://secure.mandarinpay.com/CardBindings/New' \
|
24
|
+
'?id=0eb51e74-e704-4c36-b5cb-8f0227621518'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
it 'sends request with passed action' do
|
28
|
+
allow(wrapper).to receive(:generate_x_auth_header).and_return(x_auth)
|
29
|
+
allow(MandarinApi).to \
|
30
|
+
receive_message_chain(:config, :request_url).and_return(mandarin_adress)
|
31
|
+
expect(wrapper.request('api/card-bindings', params)).to eq expected
|
32
|
+
end
|
33
|
+
end
|
34
|
+
describe 'camelize' do
|
35
|
+
let(:input) { 's_tri_ng' }
|
36
|
+
let(:output) { 'sTriNg' }
|
37
|
+
it 'input: underscored string, output: camelized string' do
|
38
|
+
expect(wrapper.send(:camelize, input)).to eq output
|
39
|
+
end
|
40
|
+
end
|
41
|
+
describe 'key_transform' do
|
42
|
+
let(:input) do
|
43
|
+
{
|
44
|
+
big_key_one: {
|
45
|
+
key_one: 1, key_two: 2, key_three: 3
|
46
|
+
},
|
47
|
+
big_key_two: {
|
48
|
+
key_a: 'a', key_b: 'b'
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
let(:output) do
|
53
|
+
{
|
54
|
+
'bigKeyOne' => {
|
55
|
+
'keyOne' => 1, 'keyTwo' => 2, 'keyThree' => 3
|
56
|
+
},
|
57
|
+
'bigKeyTwo' => {
|
58
|
+
'keyA' => 'a', 'keyB' => 'b'
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
it 'input: multilevel hash with underscored keys-symbols, ' \
|
63
|
+
'output: multilevel hash with camelized keys-strings' do
|
64
|
+
expect(wrapper.send(:key_transform, input)).to eq output
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe MandarinApi do
|
5
|
+
describe '#configure' do
|
6
|
+
let(:wrapper) do
|
7
|
+
MandarinApi::Wrapper.new(merchant_id: MandarinApi.config.merchant_id,
|
8
|
+
secret: MandarinApi.config.secret)
|
9
|
+
end
|
10
|
+
before do
|
11
|
+
MandarinApi.configure do |config|
|
12
|
+
config.merchant_id = 123
|
13
|
+
config.secret = 'secret'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
it 'calls MandarinApi::CardManager' do
|
17
|
+
expect(wrapper.instance_variable_get(:@merchant_id)).to eq 123
|
18
|
+
expect(wrapper.instance_variable_get(:@secret)).to eq 'secret'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/mocks.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
RSpec.shared_context 'mocks', shared_contex: :metadata do
|
3
|
+
before :each do
|
4
|
+
user_agent = "rest-client/#{Gem.loaded_specs['rest-client'].version} " \
|
5
|
+
"(#{RbConfig::CONFIG['host_os']} #{RbConfig::CONFIG['host_cpu']}) " \
|
6
|
+
"ruby/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}"
|
7
|
+
card_assign_body = JSON.generate(customerInfo: { email: 'ololo@gmail.com',
|
8
|
+
phone: '8-909-999-88-77' })
|
9
|
+
payout_body =
|
10
|
+
JSON.generate(
|
11
|
+
payment: { orderId: 123_321, action: 'payout', price: 35_000 },
|
12
|
+
target: { card: '0eb51e74-e704-4c36-b5cb-8f0227621518' }
|
13
|
+
)
|
14
|
+
header = {
|
15
|
+
'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate',
|
16
|
+
'Content-Length' => '70', 'Content-Type' => 'application/json',
|
17
|
+
'Host' => 'secure.mandarinpay.com',
|
18
|
+
'User-Agent' => user_agent,
|
19
|
+
'X-Auth' => 'x-auth'
|
20
|
+
}
|
21
|
+
stub_request(:post, 'https://secure.mandarinpay.com/api/card-bindings')
|
22
|
+
.with(
|
23
|
+
body: card_assign_body,
|
24
|
+
headers: header
|
25
|
+
)
|
26
|
+
.to_return(
|
27
|
+
status: 200,
|
28
|
+
body:
|
29
|
+
JSON.generate(
|
30
|
+
id: '0eb51e74-e704-4c36-b5cb-8f0227621518',
|
31
|
+
userWebLink: 'https://secure.mandarinpay.com/CardBindings/' \
|
32
|
+
'New?id=0eb51e74-e704-4c36-b5cb-8f0227621518'
|
33
|
+
),
|
34
|
+
headers: {}
|
35
|
+
)
|
36
|
+
stub_request(:post, 'https://secure.mandarinpay.com/api/transactions')
|
37
|
+
.with(
|
38
|
+
body: payout_body,
|
39
|
+
headers: header
|
40
|
+
)
|
41
|
+
.to_return(
|
42
|
+
status: 200,
|
43
|
+
body:
|
44
|
+
JSON.generate(
|
45
|
+
id: '43913ddc000c4d3990fddbd3980c1725'
|
46
|
+
),
|
47
|
+
headers: {}
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'mocks'
|
4
|
+
require 'faker'
|
5
|
+
|
6
|
+
Dir['./lib/**/*.rb'].each { |f| require f }
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
# This option will default to `true` in RSpec 4.
|
11
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
12
|
+
end
|
13
|
+
|
14
|
+
config.mock_with :rspec do |mocks|
|
15
|
+
# Prevents you from mocking or stubbing a method that does not exist on a
|
16
|
+
# real object. This is generally recommended, and will default to `true`
|
17
|
+
# in RSpec 4.
|
18
|
+
mocks.verify_partial_doubles = true
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mandarin-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vladimir Bogaevsky
|
8
|
+
- Boris Kraportov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-10-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: webmock
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.1'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.1'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: pry
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry-doc
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rdoc
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 2.4.2
|
77
|
+
- - "<"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '5.0'
|
80
|
+
type: :development
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 2.4.2
|
87
|
+
- - "<"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.0'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rubocop
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: faker
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
111
|
+
- - "<"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '1.7'
|
114
|
+
type: :development
|
115
|
+
prerelease: false
|
116
|
+
version_requirements: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '1.6'
|
121
|
+
- - "<"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.7'
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: rest-client
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '2.0'
|
131
|
+
- - "<"
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '3.0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '2.0'
|
141
|
+
- - "<"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '3.0'
|
144
|
+
- !ruby/object:Gem::Dependency
|
145
|
+
name: dry-configurable
|
146
|
+
requirement: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: 0.1.0
|
151
|
+
- - "<"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '1.0'
|
154
|
+
type: :runtime
|
155
|
+
prerelease: false
|
156
|
+
version_requirements: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 0.1.0
|
161
|
+
- - "<"
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '1.0'
|
164
|
+
description:
|
165
|
+
email: gitvbogaevsky@gmail.com
|
166
|
+
executables: []
|
167
|
+
extensions: []
|
168
|
+
extra_rdoc_files:
|
169
|
+
- README.md
|
170
|
+
- plantuml/callbacks.puml
|
171
|
+
files:
|
172
|
+
- ".gitignore"
|
173
|
+
- ".rspec"
|
174
|
+
- Gemfile
|
175
|
+
- Gemfile.lock
|
176
|
+
- LICENSE
|
177
|
+
- README.md
|
178
|
+
- lib/mandarin-api.rb
|
179
|
+
- lib/mandarin_api.rb
|
180
|
+
- lib/mandarin_api/card_manager.rb
|
181
|
+
- lib/mandarin_api/configuration.rb
|
182
|
+
- lib/mandarin_api/payment_manager.rb
|
183
|
+
- lib/mandarin_api/responder.rb
|
184
|
+
- lib/mandarin_api/wrapper.rb
|
185
|
+
- mandarin-api.gemspec
|
186
|
+
- plantuml/callbacks.puml
|
187
|
+
- spec/lib/mandarin_api/card_manager_spec.rb
|
188
|
+
- spec/lib/mandarin_api/configuration_spec.rb
|
189
|
+
- spec/lib/mandarin_api/payment_manager_spec.rb
|
190
|
+
- spec/lib/mandarin_api/responder_spec.rb
|
191
|
+
- spec/lib/mandarin_api/wrapper_spec.rb
|
192
|
+
- spec/lib/mandarin_api_spec.rb
|
193
|
+
- spec/mocks.rb
|
194
|
+
- spec/spec_helper.rb
|
195
|
+
homepage: https://github.com/vbogaevsky/mandarin-api-rb
|
196
|
+
licenses:
|
197
|
+
- MIT
|
198
|
+
metadata: {}
|
199
|
+
post_install_message:
|
200
|
+
rdoc_options: []
|
201
|
+
require_paths:
|
202
|
+
- lib
|
203
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: 2.2.0
|
208
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
209
|
+
requirements:
|
210
|
+
- - ">="
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: '0'
|
213
|
+
requirements: []
|
214
|
+
rubyforge_project:
|
215
|
+
rubygems_version: 2.6.7
|
216
|
+
signing_key:
|
217
|
+
specification_version: 4
|
218
|
+
summary: mandarinpay.com api wrapper for ruby
|
219
|
+
test_files:
|
220
|
+
- spec/lib/mandarin_api/card_manager_spec.rb
|
221
|
+
- spec/lib/mandarin_api/configuration_spec.rb
|
222
|
+
- spec/lib/mandarin_api/payment_manager_spec.rb
|
223
|
+
- spec/lib/mandarin_api/responder_spec.rb
|
224
|
+
- spec/lib/mandarin_api/wrapper_spec.rb
|
225
|
+
- spec/lib/mandarin_api_spec.rb
|
226
|
+
- spec/mocks.rb
|
227
|
+
- spec/spec_helper.rb
|