nihaopay-ruby 0.1.0

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.
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