buckaroo-ideal 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.
@@ -0,0 +1,54 @@
1
+ require 'digest/md5'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ module Buckaroo
5
+ module Ideal
6
+ class ResponseSignature
7
+
8
+ # @return [String] The configured merchant_key in +Buckaroo::Ideal::Config+
9
+ delegate :merchant_key, to: Config
10
+
11
+ # @return [String] The configured secret_key in +Buckaroo::Ideal::Config+
12
+ delegate :secret_key, to: Config
13
+
14
+ # @return [String] The signature that was given in the response
15
+ attr_reader :signature
16
+
17
+ # @return [Buckaroo::Ideal::Response] The response that was signed.
18
+ attr_reader :response
19
+
20
+ # @return [String] The secret key that is used to sign the order.
21
+ attr_reader :secret
22
+
23
+ def initialize(response, signature = '')
24
+ @response = response
25
+ @signature = signature
26
+ end
27
+
28
+ def valid?
29
+ signature == generate_signature
30
+ end
31
+
32
+ def generate_signature
33
+ salt = [
34
+ response.transaction_id,
35
+ response.timestamp,
36
+ merchant_key,
37
+ response.invoice_number,
38
+ response.reference,
39
+ response.currency,
40
+ to_cents(response.amount),
41
+ response.status.code,
42
+ to_numeric_boolean(response.test_mode),
43
+ secret_key
44
+ ].join
45
+
46
+ Digest::MD5.hexdigest(salt)
47
+ end
48
+
49
+ private
50
+
51
+ include Util
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ require 'csv'
2
+
3
+ module Buckaroo
4
+ module Ideal
5
+ class Status
6
+ class UnknownStatusCode < StandardError; end
7
+
8
+ STATES = {
9
+ completed: %w(071 121 151 171 190 242 243 244 245 246 247 254 255 301
10
+ 401 461 462 463 464 551 601 701 801),
11
+ pending: %w(000 001 070 090 091 100 105 120 126 135 136 150 156 157
12
+ 170 176 177 253 300 400 460 500 550 600 700 710 790 791
13
+ 792 793 800 811 814 815 831 834),
14
+ failed: %w(072 073 074 075 076 101 102 103 104 106 122 123 124 125
15
+ 137 138 139 152 153 155 158 159 172 173 175 178 179 201
16
+ 203 204 205 206 207 251 252 260 261 262 302 303 304 305
17
+ 306 309 402 409 410 411 414 421 422 425 466 468 490 491
18
+ 492 501 552 553 554 555 556 560 581 590 602 605 609 610
19
+ 612 690 702 703 704 705 706 707 708 711 712 720 721 802
20
+ 803 804 810 812 813 816 820 821 822 823 824 830 833 835
21
+ 836 890 891 900 901 910 931 932 933 934 935 940 941 942
22
+ 943 944 945 946 947 948 949 950 951 952 953 954 955 956
23
+ 960 961 962 963 964 971 972 973 974 975 976 977 978 980
24
+ 981 982 983 990 991 992 993 999)
25
+ }
26
+
27
+ CSV_FILE = File.expand_path('../../../files/statuscodes.csv', __FILE__)
28
+
29
+ attr_reader :code, :message
30
+
31
+ def self.status_codes(csv_file = CSV_FILE)
32
+ codes = {}
33
+
34
+ CSV.foreach(csv_file, col_sep: ';') do |row|
35
+ codes[row[0]] = row[1]
36
+ end
37
+
38
+ codes
39
+ end
40
+
41
+ def initialize(code)
42
+ if message = self.class.status_codes[code]
43
+ @code = code
44
+ @message = message
45
+ else
46
+ raise UnknownStatusCode
47
+ end
48
+ end
49
+
50
+ def state
51
+ STATES.each do |state, codes|
52
+ return state if codes.include? @code
53
+ end
54
+
55
+ :unknown
56
+ end
57
+
58
+ def completed?; state == :completed; end
59
+ def pending?; state == :pending; end
60
+ def failed?; state == :failed; end
61
+
62
+ def ==(other)
63
+ other.respond_to?(:code) && other.code == code
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,41 @@
1
+ require 'transliterator'
2
+
3
+ module Buckaroo
4
+ module Ideal
5
+ module Util
6
+ extend self
7
+
8
+ def to_normalized_string(string)
9
+ Transliterator.asciify(string.to_s)
10
+ end
11
+
12
+ def to_cents(amount)
13
+ (amount.to_f * 100).round
14
+ end
15
+
16
+ def from_cents(cents)
17
+ cents.to_f / 100
18
+ end
19
+
20
+ def to_numeric_boolean(boolean)
21
+ boolean ? 1 : 0
22
+ end
23
+
24
+ def from_numeric_boolean(number)
25
+ number.to_i == 1
26
+ end
27
+
28
+ def compact(subject)
29
+ subject = subject.dup
30
+
31
+ if subject.is_a?(Hash)
32
+ subject.each do |key, value|
33
+ subject.delete(key) unless value
34
+ end
35
+ end
36
+
37
+ subject
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module Buckaroo
2
+ module Ideal
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buckaroo::Ideal::Config do
4
+ before do
5
+ Buckaroo::Ideal::Config.configure(
6
+ merchant_key: 'merchant_key',
7
+ secret_key: 'secret_key',
8
+ test_mode: true,
9
+ success_url: 'http://example.com/transaction/success',
10
+ reject_url: 'http://example.com/transaction/reject',
11
+ error_url: 'http://example.com/transaction/error',
12
+ return_method: 'GET',
13
+ style: 'POPUP',
14
+ autoclose_popup: true
15
+ )
16
+ end
17
+
18
+ subject { Buckaroo::Ideal::Config }
19
+
20
+ it 'has a gateway_url' do
21
+ subject.gateway_url.should == 'https://payment.buckaroo.nl/gateway/payment.asp'
22
+ end
23
+
24
+ it 'has a merchant_key' do
25
+ subject.merchant_key.should == 'merchant_key'
26
+ end
27
+
28
+ it 'has a secret_key' do
29
+ subject.secret_key.should == 'secret_key'
30
+ end
31
+
32
+ it 'has a test_mode' do
33
+ subject.test_mode.should be_true
34
+ end
35
+
36
+ it 'has a success_url' do
37
+ subject.success_url.should == 'http://example.com/transaction/success'
38
+ end
39
+
40
+ it 'has a reject_url' do
41
+ subject.reject_url.should == 'http://example.com/transaction/reject'
42
+ end
43
+
44
+ it 'has a error_url' do
45
+ subject.error_url.should == 'http://example.com/transaction/error'
46
+ end
47
+
48
+ it 'has a return_method' do
49
+ subject.return_method.should == 'GET'
50
+ end
51
+
52
+ it 'has a style' do
53
+ subject.style.should == 'POPUP'
54
+ end
55
+
56
+ it 'has a autoclose_popup setting' do
57
+ subject.autoclose_popup.should be_true
58
+ end
59
+
60
+ it 'can be reset to default values' do
61
+ subject.reset
62
+ subject.merchant_key.should be_nil
63
+ subject.secret_key.should be_nil
64
+ subject.test_mode.should be_false
65
+ subject.success_url.should be_nil
66
+ subject.reject_url.should be_nil
67
+ subject.error_url.should be_nil
68
+ subject.return_method.should == 'POST'
69
+ subject.style.should == 'PAGE'
70
+ subject.autoclose_popup.should be_false
71
+ end
72
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buckaroo::Ideal::Order do
4
+ let(:order) { Buckaroo::Ideal::Order.new }
5
+
6
+ before do
7
+ Buckaroo::Ideal::Config.configure(
8
+ merchant_key: 'merchant_key',
9
+ secret_key: 'secret_key',
10
+ test_mode: true,
11
+ success_url: 'http://example.com/transaction/success',
12
+ reject_url: 'http://example.com/transaction/reject',
13
+ error_url: 'http://example.com/transaction/error',
14
+ return_method: 'GET',
15
+ style: 'POPUP',
16
+ autoclose_popup: true
17
+ )
18
+ end
19
+
20
+ it 'has a default currency' do
21
+ order.currency.should == 'EUR'
22
+ end
23
+
24
+ it 'does not have a default amount' do
25
+ order.amount.should be_nil
26
+ end
27
+
28
+ it 'does not have a default bank' do
29
+ order.bank.should be_nil
30
+ end
31
+
32
+ it 'does not have a default description' do
33
+ order.description.should be_nil
34
+ end
35
+
36
+ it 'does not have a default reference' do
37
+ order.reference.should be_nil
38
+ end
39
+
40
+ it 'does not have a default invoice_number' do
41
+ order.invoice_number.should be_nil
42
+ end
43
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buckaroo::Ideal::RequestSignature do
4
+ it 'generates a signature for the given order' do
5
+ order = mock invoice_number: 'EETNU-12345',
6
+ amount: 12.50,
7
+ currency: 'EUR'
8
+
9
+ Buckaroo::Ideal::Config.stub(:test_mode)
10
+ .and_return(true)
11
+
12
+ Buckaroo::Ideal::Config.stub(:merchant_key)
13
+ .and_return('merchant_key')
14
+
15
+ Buckaroo::Ideal::Config.stub(:secret_key)
16
+ .and_return('secret_key')
17
+
18
+ signature = Buckaroo::Ideal::RequestSignature.new(order)
19
+
20
+ expected_salt = [
21
+ 'merchant_key', # config.merchant_key
22
+ 'EETNU-12345', # order.invoice_number
23
+ 1250, # order.amount in cents
24
+ 'EUR', # order.currency
25
+ 1, # config.test_mode
26
+ 'secret_key' # config.secret_key
27
+ ].join
28
+
29
+ Digest::MD5.should_receive(:hexdigest)
30
+ .with(expected_salt)
31
+
32
+ signature.to_s
33
+ end
34
+ end
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buckaroo::Ideal::Request do
4
+ let(:order) { Buckaroo::Ideal::Order.new }
5
+ let(:request) { Buckaroo::Ideal::Request.new(order) }
6
+
7
+ before do
8
+ Buckaroo::Ideal::Config.configure(
9
+ merchant_key: 'merchant_key',
10
+ secret_key: 'secret_key',
11
+ test_mode: true,
12
+ success_url: 'http://example.com/transaction/success',
13
+ reject_url: 'http://example.com/transaction/reject',
14
+ error_url: 'http://example.com/transaction/error',
15
+ return_method: 'GET',
16
+ style: 'POPUP',
17
+ autoclose_popup: true
18
+ )
19
+ end
20
+
21
+ it 'has a default language' do
22
+ request.language.should == 'NL'
23
+ end
24
+
25
+ it 'has a default success_url from the configuration' do
26
+ request.success_url.should == 'http://example.com/transaction/success'
27
+ end
28
+
29
+ it 'has a default reject_url from the configuration' do
30
+ request.reject_url.should == 'http://example.com/transaction/reject'
31
+ end
32
+
33
+ it 'has a default error_url from the configuration' do
34
+ request.error_url.should == 'http://example.com/transaction/error'
35
+ end
36
+
37
+ it 'has a default return_method from the configuration' do
38
+ request.return_method.should == 'GET'
39
+ end
40
+
41
+ it 'has a default style from the configuration' do
42
+ request.style.should == 'POPUP'
43
+ end
44
+
45
+ it 'has a default autoclose_popup from the configuration' do
46
+ request.autoclose_popup.should be_true
47
+ end
48
+
49
+ describe '#gateway_url' do
50
+ it 'returns the configured gateway_url' do
51
+ request.gateway_url.should == Buckaroo::Ideal::Config.gateway_url
52
+ end
53
+ end
54
+
55
+ describe '#parameters' do
56
+ def parameters; request.parameters; end
57
+
58
+ it 'has a BPE_Merchant with the configured merchant_key' do
59
+ parameters['BPE_Merchant'].should == 'merchant_key'
60
+
61
+ Buckaroo::Ideal::Config.merchant_key = 'new_merchant_key'
62
+ parameters['BPE_Merchant'].should == 'new_merchant_key'
63
+ end
64
+
65
+ it "has a BPE_Amount with the order's amount in cents" do
66
+ order.amount = 19.95
67
+ parameters['BPE_Amount'].should == 1995
68
+ end
69
+
70
+ it "has a BPE_Currency with the order's currency" do
71
+ parameters['BPE_Currency'].should == 'EUR'
72
+
73
+ order.currency = 'BHT'
74
+ parameters['BPE_Currency'].should == 'BHT'
75
+ end
76
+
77
+ it "has a BPE_Invoice with the order's invoice_number" do
78
+ order.invoice_number = 'INV001'
79
+ parameters['BPE_Invoice'].should == 'INV001'
80
+ end
81
+
82
+ it 'has a BPE_Return_Method with the return_method' do
83
+ parameters['BPE_Return_Method'].should == 'GET'
84
+
85
+ request.return_method = 'POST'
86
+ parameters['BPE_Return_Method'].should == 'POST'
87
+ end
88
+
89
+ it 'has a BPE_Style if the style is set' do
90
+ parameters['BPE_Style'].should == 'POPUP'
91
+
92
+ request.style = 'PAGE'
93
+ parameters['BPE_Style'].should == 'PAGE'
94
+ end
95
+
96
+ it 'has a BPE_Autoclose_Popup if autoclose_popup is set' do
97
+ parameters['BPE_Autoclose_Popup'].should == 1
98
+
99
+ request.autoclose_popup = false
100
+ parameters['BPE_Autoclose_Popup'].should == 0
101
+ end
102
+
103
+ it 'has a generated BPE_Signature2' do
104
+ parameters['BPE_Signature2'].length.should == 32
105
+
106
+ request.stub(:signature).and_return('signature')
107
+
108
+ parameters['BPE_Signature2'].should == 'signature'
109
+ end
110
+
111
+ it 'has a BPE_Language with the language' do
112
+ parameters['BPE_Language'].should == 'NL'
113
+
114
+ request.language = 'DE'
115
+ parameters['BPE_Language'].should == 'DE'
116
+ end
117
+
118
+ it 'has a BPE_Mode with the configured test_mode' do
119
+ parameters['BPE_Mode'].should == 1
120
+
121
+ Buckaroo::Ideal::Config.test_mode = false
122
+ parameters['BPE_Mode'].should == 0
123
+ end
124
+
125
+ it "has a BPE_Issuer if the order's bank is set" do
126
+ parameters.keys.should_not include 'BPE_Issuer'
127
+
128
+ order.bank = 'ABNAMRO'
129
+ parameters['BPE_Issuer'].should == 'ABNAMRO'
130
+ end
131
+
132
+ it "has a BPE_Description if the order's description is set" do
133
+ parameters.keys.should_not include 'BPE_Description'
134
+
135
+ order.description = 'Your Order Description'
136
+ parameters['BPE_Description'].should == 'Your Order Description'
137
+ end
138
+
139
+ it 'has a BPE_Reference if the reference is set' do
140
+ parameters.keys.should_not include 'BPE_Reference'
141
+
142
+ order.reference = 'Reference'
143
+ parameters['BPE_Reference'].should == 'Reference'
144
+ end
145
+
146
+ it 'has a BPE_Return_Success if the success_url is set' do
147
+ request.success_url = nil
148
+ parameters.keys.should_not include 'BPE_Return_Success'
149
+
150
+ request.success_url = 'http://example.org/'
151
+ parameters['BPE_Return_Success'].should == 'http://example.org/'
152
+ end
153
+
154
+ it 'has a BPE_Return_Reject if the reject_url is set' do
155
+ request.reject_url = nil
156
+ parameters.keys.should_not include 'BPE_Return_Reject'
157
+
158
+ request.reject_url = 'http://example.org/'
159
+ parameters['BPE_Return_Reject'].should == 'http://example.org/'
160
+ end
161
+
162
+ it 'has a BPE_Return_Error if the error_url is set' do
163
+ request.error_url = nil
164
+ parameters.keys.should_not include 'BPE_Return_Error'
165
+
166
+ request.error_url = 'http://example.org/'
167
+ parameters['BPE_Return_Error'].should == 'http://example.org/'
168
+ end
169
+ end
170
+ end