nihaopay-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE +22 -0
  4. data/README.md +344 -0
  5. data/lib/nihaopay-ruby.rb +29 -0
  6. data/lib/nihaopay/configure.rb +14 -0
  7. data/lib/nihaopay/credit_card.rb +27 -0
  8. data/lib/nihaopay/errors.rb +6 -0
  9. data/lib/nihaopay/merchant.rb +47 -0
  10. data/lib/nihaopay/mixins/api.rb +41 -0
  11. data/lib/nihaopay/mixins/queryable.rb +41 -0
  12. data/lib/nihaopay/query.rb +61 -0
  13. data/lib/nihaopay/secure_pay/ali_pay.rb +11 -0
  14. data/lib/nihaopay/secure_pay/base.rb +48 -0
  15. data/lib/nihaopay/secure_pay/union_pay.rb +11 -0
  16. data/lib/nihaopay/secure_pay/we_chat_pay.rb +11 -0
  17. data/lib/nihaopay/transactions/authorize.rb +37 -0
  18. data/lib/nihaopay/transactions/base.rb +78 -0
  19. data/lib/nihaopay/transactions/cancel.rb +28 -0
  20. data/lib/nihaopay/transactions/capture.rb +29 -0
  21. data/lib/nihaopay/transactions/purchase.rb +11 -0
  22. data/lib/nihaopay/transactions/refund.rb +31 -0
  23. data/lib/nihaopay/transactions/release.rb +28 -0
  24. data/lib/nihaopay/util/hash_util.rb +30 -0
  25. data/lib/nihaopay/version.rb +3 -0
  26. data/spec/credit_card_spec.rb +56 -0
  27. data/spec/merchant_spec.rb +142 -0
  28. data/spec/mixins/api_spec.rb +81 -0
  29. data/spec/mixins/queryable_spec.rb +95 -0
  30. data/spec/query_spec.rb +129 -0
  31. data/spec/secure_pay/ali_pay_spec.rb +112 -0
  32. data/spec/secure_pay/union_pay_spec.rb +13 -0
  33. data/spec/secure_pay/we_chat_pay_spec.rb +13 -0
  34. data/spec/spec_helper.rb +7 -0
  35. data/spec/transactions/authorize_spec.rb +115 -0
  36. data/spec/transactions/base_spec.rb +184 -0
  37. data/spec/transactions/cancel_spec.rb +83 -0
  38. data/spec/transactions/capture_spec.rb +85 -0
  39. data/spec/transactions/purchase_spec.rb +7 -0
  40. data/spec/transactions/refund_spec.rb +98 -0
  41. data/spec/transactions/release_spec.rb +83 -0
  42. data/spec/util/hash_util_spec.rb +22 -0
  43. metadata +187 -0
@@ -0,0 +1,11 @@
1
+ module Nihaopay
2
+ module Transactions
3
+ class Purchase < Authorize
4
+ class << self
5
+ def capture_param
6
+ true
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module Nihaopay
2
+ module Transactions
3
+ class Refund < Base
4
+ VALID_OPTIONS = %i(reason).freeze
5
+
6
+ attr_accessor :refunded, :refund_transaction_id
7
+
8
+ class << self
9
+ def start(transaction_id, amount, currency, options = {})
10
+ @token = options.delete(:token)
11
+ url = request_url(transaction_id)
12
+ params = Nihaopay::HashUtil.slice(options, *VALID_OPTIONS).merge(amount: amount, currency: currency)
13
+ response = HTTParty.post(url, headers: request_headers, body: request_body(params))
14
+ build_from_response!(response)
15
+ end
16
+
17
+ def request_url(transaction_id)
18
+ "#{base_url}/transactions/#{transaction_id}/refund"
19
+ end
20
+
21
+ def valid_attributes
22
+ %i(transaction_id status refunded refund_transaction_id)
23
+ end
24
+
25
+ def response_keys_map
26
+ { id: :transaction_id, transaction_id: :refund_transaction_id }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ module Nihaopay
2
+ module Transactions
3
+ class Release < Base
4
+ attr_accessor :released, :release_transaction_id
5
+
6
+ class << self
7
+ def start(transaction_id, options = {})
8
+ @token = options.delete(:token)
9
+ url = request_url(transaction_id)
10
+ response = HTTParty.post(url, headers: request_headers, body: request_body)
11
+ build_from_response!(response)
12
+ end
13
+
14
+ def request_url(transaction_id)
15
+ "#{base_url}/transactions/#{transaction_id}/release"
16
+ end
17
+
18
+ def valid_attributes
19
+ %i(transaction_id status released release_transaction_id)
20
+ end
21
+
22
+ def response_keys_map
23
+ { id: :transaction_id, transaction_id: :release_transaction_id }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # Emulates Hash methods from ActiveSupport without monkey-patching the Hash class directly.
2
+ module Nihaopay
3
+ module HashUtil
4
+ class << self
5
+ def symbolize_keys(hash)
6
+ result = {}
7
+ hash.each_key do |key|
8
+ result[(key.to_sym rescue key)] = hash[key]
9
+ end
10
+ result
11
+ end
12
+
13
+ def stringify_keys(hash)
14
+ result = {}
15
+ hash.each_key do |key|
16
+ result[(key.to_s rescue key)] = hash[key]
17
+ end
18
+ result
19
+ end
20
+
21
+ def slice(hash, *keys)
22
+ result = {}
23
+ keys.each do |key|
24
+ result[key] = hash[key] if hash.key?(key)
25
+ end
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Nihaopay
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Nihaopay::CreditCard do
4
+ let(:number) { '6221558812340000' }
5
+ let(:attributes) { { number: number, expiry_year: 17, expiry_month: 11, cvv: '123' } }
6
+ let(:cc) { described_class.new(attributes) }
7
+
8
+ describe '#attr_accessor' do
9
+ subject { described_class.new }
10
+ it { is_expected.to respond_to :number= }
11
+ it { is_expected.to respond_to :number }
12
+ it { is_expected.to respond_to :expiry_year= }
13
+ it { is_expected.to respond_to :expiry_year }
14
+ it { is_expected.to respond_to :expiry_month= }
15
+ it { is_expected.to respond_to :expiry_month }
16
+ it { is_expected.to respond_to :cvv= }
17
+ it { is_expected.to respond_to :cvv }
18
+ end
19
+
20
+ describe '#initialize' do
21
+ context 'when attributes not passed' do
22
+ subject { described_class.new }
23
+ it { is_expected.to be_a Nihaopay::CreditCard }
24
+ end
25
+
26
+ context 'when attributes passed as stringified hash' do
27
+ subject { described_class.new(Nihaopay::HashUtil.stringify_keys(attributes)) }
28
+ it { expect(subject.number).to eq number }
29
+ it { expect(subject.expiry_year).to eq 17 }
30
+ it { expect(subject.expiry_month).to eq 11 }
31
+ it { expect(subject.cvv).to eq '123' }
32
+ end
33
+
34
+ context 'with invalid attributes' do
35
+ subject { described_class.new(foo: 'bar') }
36
+ it { is_expected.to be_a Nihaopay::CreditCard }
37
+ it { expect(subject.number).to be_nil }
38
+ it { expect(subject.expiry_year).to be_nil }
39
+ it { expect(subject.expiry_month).to be_nil }
40
+ it { expect(subject.cvv).to be_nil }
41
+ end
42
+
43
+ context 'with valid attributes' do
44
+ subject { cc }
45
+ it { expect(subject.number).to eq number }
46
+ it { expect(subject.expiry_year).to eq 17 }
47
+ it { expect(subject.expiry_month).to eq 11 }
48
+ it { expect(subject.cvv).to eq '123' }
49
+ end
50
+ end
51
+
52
+ describe '#to_params_hash' do
53
+ let(:expected) { { card_number: number, card_exp_year: 17, card_exp_month: 11, card_cvv: '123' } }
54
+ it { expect(cc.to_params_hash).to eq expected }
55
+ end
56
+ end
@@ -0,0 +1,142 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Nihaopay::Merchant do
4
+ let(:token) { '6c4dc4828474fa73c5f438a9eb2' }
5
+ let(:merchant) { described_class.new(token) }
6
+
7
+ describe '#attr_accessor' do
8
+ subject { merchant }
9
+ it { is_expected.to respond_to :token= }
10
+ it { is_expected.to respond_to :token }
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it { expect(merchant.token).to eq token }
15
+ end
16
+
17
+ describe '#union_pay' do
18
+ context 'when options not passed' do
19
+ it { expect(Nihaopay::SecurePay::UnionPay).to receive(:start).with(100, 'JPY', token: token) }
20
+ after { merchant.union_pay(100, 'JPY') }
21
+ end
22
+
23
+ context 'when options passed' do
24
+ let(:options) do
25
+ { reference: '3461fcc31aec471780ad1a4dc6111947',
26
+ ipn_url: 'http://website.com/ipn',
27
+ callback_url: 'http://website.com/callback' }
28
+ end
29
+ let(:expected_opts) { options.merge(token: token) }
30
+ it { expect(Nihaopay::SecurePay::UnionPay).to receive(:start).with(100, 'JPY', expected_opts) }
31
+ after { merchant.union_pay(100, 'JPY', options) }
32
+ end
33
+ end
34
+
35
+ describe '#ali_pay' do
36
+ context 'when options not passed' do
37
+ it { expect(Nihaopay::SecurePay::AliPay).to receive(:start).with(100, 'JPY', token: token) }
38
+ after { merchant.ali_pay(100, 'JPY') }
39
+ end
40
+
41
+ context 'when options passed' do
42
+ let(:options) do
43
+ { reference: '3461fcc31aec471780ad1a4dc6111947',
44
+ ipn_url: 'http://website.com/ipn',
45
+ callback_url: 'http://website.com/callback' }
46
+ end
47
+ let(:expected_opts) { options.merge(token: token) }
48
+ it { expect(Nihaopay::SecurePay::AliPay).to receive(:start).with(100, 'JPY', expected_opts) }
49
+ after { merchant.ali_pay(100, 'JPY', options) }
50
+ end
51
+ end
52
+
53
+ describe '#wechat_pay' do
54
+ context 'when options not passed' do
55
+ it { expect(Nihaopay::SecurePay::WeChatPay).to receive(:start).with(100, 'JPY', token: token) }
56
+ after { merchant.wechat_pay(100, 'JPY') }
57
+ end
58
+
59
+ context 'when options passed' do
60
+ let(:options) do
61
+ { reference: '3461fcc31aec471780ad1a4dc6111947',
62
+ ipn_url: 'http://website.com/ipn',
63
+ callback_url: 'http://website.com/callback' }
64
+ end
65
+ let(:expected_opts) { options.merge(token: token) }
66
+ it { expect(Nihaopay::SecurePay::WeChatPay).to receive(:start).with(100, 'JPY', expected_opts) }
67
+ after { merchant.wechat_pay(100, 'JPY', options) }
68
+ end
69
+ end
70
+
71
+ describe '#authorize' do
72
+ let(:cc_attrs) { { number: '6221558812340000', expiry_year: 17, expiry_month: 11, cvv: '123' } }
73
+ let(:cc) { Nihaopay::CreditCard.new(cc_attrs) }
74
+
75
+ context 'when options not passed' do
76
+ it { expect(Nihaopay::Transactions::Authorize).to receive(:start).with(100, cc, token: token) }
77
+ after { merchant.authorize(100, cc) }
78
+ end
79
+
80
+ context 'when options passed' do
81
+ let(:options) do
82
+ { currency: 'USD',
83
+ description: 'Lorem Ipsum',
84
+ reference: '3461fcc31aec471780ad1a4dc6111947' }
85
+ end
86
+ let(:expected_opts) { options.merge(token: token) }
87
+ it { expect(Nihaopay::Transactions::Authorize).to receive(:start).with(100, cc, expected_opts) }
88
+ after { merchant.authorize(100, cc, options) }
89
+ end
90
+ end
91
+
92
+ describe '#capture' do
93
+ it do
94
+ expect(Nihaopay::Transactions::Capture).to receive(:start).with('20160718111604002633',
95
+ 100, 'JPY', token: token)
96
+ end
97
+ after { merchant.capture('20160718111604002633', 100, 'JPY') }
98
+ end
99
+
100
+ describe '#purchase' do
101
+ let(:cc_attrs) { { number: '6221558812340000', expiry_year: 17, expiry_month: 11, cvv: '123' } }
102
+ let(:cc) { Nihaopay::CreditCard.new(cc_attrs) }
103
+
104
+ context 'when options not passed' do
105
+ it { expect(Nihaopay::Transactions::Purchase).to receive(:start).with(100, cc, token: token) }
106
+ after { merchant.purchase(100, cc) }
107
+ end
108
+
109
+ context 'when options passed' do
110
+ let(:options) do
111
+ { currency: 'USD',
112
+ description: 'Lorem Ipsum',
113
+ reference: '3461fcc31aec471780ad1a4dc6111947' }
114
+ end
115
+ let(:expected_opts) { options.merge(token: token) }
116
+ it { expect(Nihaopay::Transactions::Purchase).to receive(:start).with(100, cc, expected_opts) }
117
+ after { merchant.purchase(100, cc, options) }
118
+ end
119
+ end
120
+
121
+ describe '#release' do
122
+ let(:txn_id) { '20160718111604002633' }
123
+ it { expect(Nihaopay::Transactions::Release).to receive(:start).with(txn_id, token: token) }
124
+ after { merchant.release(txn_id) }
125
+ end
126
+
127
+ describe '#refund' do
128
+ let(:txn_id) { '20160718111604002633' }
129
+
130
+ context 'when options not passed' do
131
+ it { expect(Nihaopay::Transactions::Refund).to receive(:start).with(txn_id, 100, 'JPY', token: token) }
132
+ after { merchant.refund(txn_id, 100, 'JPY') }
133
+ end
134
+
135
+ context 'when options passed' do
136
+ let(:options) { { reason: 'Out of stock' } }
137
+ let(:expected_opts) { options.merge(token: token) }
138
+ it { expect(Nihaopay::Transactions::Refund).to receive(:start).with(txn_id, 100, 'JPY', expected_opts) }
139
+ after { merchant.refund(txn_id, 100, 'JPY', options) }
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,81 @@
1
+ require_relative '../spec_helper'
2
+
3
+ class ApiTest
4
+ include Nihaopay::Api
5
+ end
6
+
7
+ describe Nihaopay::Api do
8
+ let(:token) { '6c4dc4828474fa73c5f438a9eb2f' }
9
+
10
+ before do
11
+ Nihaopay.test_mode = true
12
+ Nihaopay.token = token
13
+ end
14
+
15
+ describe 'ClassMethods' do
16
+ describe '.host' do
17
+ context 'when test mode' do
18
+ it { expect(ApiTest.host).to eq 'http://api.test.nihaopay.com' }
19
+ end
20
+
21
+ context 'when not test mode' do
22
+ before { Nihaopay.test_mode = false }
23
+ it { expect(ApiTest.host).to eq 'https://api.nihaopay.com' }
24
+ end
25
+ end
26
+
27
+ describe '.base_url' do
28
+ it { expect(ApiTest.base_url).to eq 'http://api.test.nihaopay.com/v1.1' }
29
+ end
30
+
31
+ describe '.authorization' do
32
+ let(:expected) { { 'Authorization' => "Bearer #{token}" } }
33
+ it { expect(ApiTest.authorization).to eq expected }
34
+ end
35
+
36
+ describe '.merchant_token' do
37
+ context 'when local' do
38
+ let(:token_2) { 'bf3092e441d9f2ea5fbcf93b51a2' }
39
+ before { ApiTest.instance_variable_set(:@token, token_2) }
40
+ it { expect(ApiTest.merchant_token).to eq token_2 }
41
+ after { ApiTest.instance_variable_set(:@token, nil) }
42
+ end
43
+
44
+ context 'when global' do
45
+ it { expect(ApiTest.merchant_token).to eq token }
46
+ end
47
+ end
48
+
49
+ describe '.validate_resource!' do
50
+ before { allow(response).to receive(:parsed_response) { parsed_response } }
51
+ let(:response) { Object.new }
52
+ let(:parsed_response) { nil }
53
+
54
+ context 'when parsed_response not contains :id' do
55
+ let(:parsed_response) { { 'code' => 200 } }
56
+ it { expect { ApiTest.validate_resource!(response) }.to raise_error(Nihaopay::TransactionError) }
57
+ end
58
+
59
+ context 'when parsed_response contains :id' do
60
+ let(:parsed_response) { { 'id' => '20160714132438002485', 'code' => 200 } }
61
+ it { expect { ApiTest.validate_resource!(response) }.to_not raise_error(Nihaopay::TransactionError) }
62
+ end
63
+ end
64
+
65
+ describe '.validate_collection!' do
66
+ before { allow(response).to receive(:parsed_response) { parsed_response } }
67
+ let(:response) { Object.new }
68
+ let(:parsed_response) { nil }
69
+
70
+ context 'when parsed_response not contains :transactions' do
71
+ let(:parsed_response) { { 'code' => 200 } }
72
+ it { expect { ApiTest.validate_collection!(response) }.to raise_error(Nihaopay::TransactionError) }
73
+ end
74
+
75
+ context 'when parsed_response contains :transactions' do
76
+ let(:parsed_response) { { 'transactions' => [{ 'id' => '20160714132438002485', 'code' => 200 }] } }
77
+ it { expect { ApiTest.validate_collection!(response) }.to_not raise_error(Nihaopay::TransactionError) }
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,95 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Nihaopay::Queryable do
4
+ describe 'ClassMethods' do
5
+ describe '.delegate' do
6
+ before { allow(Nihaopay::Transactions::Base).to receive(:q) { q } }
7
+ let(:q) { Nihaopay::Query.new }
8
+
9
+ describe '.limit' do
10
+ it { expect(q).to receive(:limit) }
11
+ after { Nihaopay::Transactions::Base.limit(1) }
12
+ end
13
+
14
+ describe '.before' do
15
+ it { expect(q).to receive(:before) }
16
+ after { Nihaopay::Transactions::Base.before(Time.now) }
17
+ end
18
+
19
+ describe '.after' do
20
+ it { expect(q).to receive(:after) }
21
+ after { Nihaopay::Transactions::Base.after(Time.now) }
22
+ end
23
+ end
24
+
25
+ describe '.find' do
26
+ before do
27
+ allow(response).to receive(:parsed_response) { parsed_response }
28
+ allow(HTTParty).to receive(:get) { response }
29
+ end
30
+ let(:response) { Object.new }
31
+ let(:parsed_response) { nil }
32
+
33
+ context 'when response does not contain transaction details' do
34
+ let(:parsed_response) do
35
+ { 'status' => 'success',
36
+ 'type' => 'charge',
37
+ 'amount' => 1000 }
38
+ end
39
+ it 'should raise an error' do
40
+ err = Nihaopay::TransactionLookUpError
41
+ expect { Nihaopay::Transactions::Base.find('20160714132438002485') }.to raise_error(err)
42
+ end
43
+ end
44
+
45
+ context 'when response contains transaction details' do
46
+ let(:parsed_response) do
47
+ { 'id' => '20160714132438002485',
48
+ 'status' => 'success',
49
+ 'type' => 'charge',
50
+ 'amount' => 1000 }
51
+ end
52
+ it 'should return a transaction object' do
53
+ txn = Nihaopay::Transactions::Base.find('20160714132438002485')
54
+ expect(txn).to be_a Nihaopay::Transactions::Base
55
+ expect(txn.transaction_id).to eq '20160714132438002485'
56
+ expect(txn.status).to eq 'success'
57
+ expect(txn.type).to eq 'charge'
58
+ expect(txn.amount).to eq 1000
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '.fetch' do
64
+ before { allow(Nihaopay::Transactions::Base).to receive(:q) { q } }
65
+ let(:q) { Nihaopay::Query.new }
66
+
67
+ context 'when :after present in options' do
68
+ let(:options) { { after: '2016-06-01T01:00:00Z', limit: 5 } }
69
+ it { expect(q).to receive(:fetch).with(starting_after: '2016-06-01T01:00:00Z', limit: 5) }
70
+ after { Nihaopay::Transactions::Base.fetch(options) }
71
+ end
72
+
73
+ context 'when :before present in options' do
74
+ let(:options) { { before: '2016-06-01T01:00:00Z', limit: 5 } }
75
+ it { expect(q).to receive(:fetch).with(ending_before: '2016-06-01T01:00:00Z', limit: 5) }
76
+ after { Nihaopay::Transactions::Base.fetch(options) }
77
+ end
78
+
79
+ context 'when :after and :before not present in options' do
80
+ let(:options) { { limit: 5 } }
81
+ it { expect(q).to receive(:fetch).with(limit: 5) }
82
+ after { Nihaopay::Transactions::Base.fetch(options) }
83
+ end
84
+
85
+ context 'when options not passed' do
86
+ it { expect(q).to receive(:fetch).with({}) }
87
+ after { Nihaopay::Transactions::Base.fetch }
88
+ end
89
+ end
90
+
91
+ describe '.q' do
92
+ it { expect(Nihaopay::Transactions::Base.send(:q)).to be_a Nihaopay::Query }
93
+ end
94
+ end
95
+ end