ideal-payment 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Ideal
4
+ VERSION = '1.0.2'
5
+ end
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ # This improves performance since the Google DNS is much quicker than the local Vagrant Virtualbox combination
4
+ printf "nameserver 8.8.8.8\nnameserver 8.8.4.4" > /etc/resolv.conf
5
+
6
+ echo "Updating the Operating System..."
7
+ sudo apt-get update >> /tmp/provision.log 2>&1
8
+ sudo apt-get upgrade -y >> /tmp/provision.log 2>&1
9
+
10
+ echo "Installing extra packages..."
11
+ sudo apt-get install curl wget git g++ libreadline6-dev zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 autoconf libgdbm-dev libncurses5-dev automake libtool bison pkg-config libffi-dev -y >> /tmp/provision.log 2>&1
12
+
13
+ echo "Installing rvm..."
14
+ curl -sSL https://get.rvm.io | bash -s stable --ruby >> /tmp/provision.log 2>&1
15
+ source /usr/local/rvm/scripts/rvm
16
+
17
+ echo "Installing Ruby-2.1.2..."
18
+ rvm install ruby-2.1.2 >> /tmp/provision.log 2>&1
19
+ rvm use ruby-2.1.2 --default >> /tmp/provision.log 2>&1
20
+
21
+ cd /vagrant && bundle install >> /tmp/provision.log 2>&1
22
+
23
+ echo ""
24
+ echo "And we are done! Check /tmp/provision.log for a full log file \`vagrant ssh -c \"cat /tmp/provision.log\"\`"
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
3
+ <createDateTimestamp>created_at_timestamp</createDateTimestamp>
4
+ <Merchant>
5
+ <merchantID>002054205</merchantID>
6
+ <subID>0</subID>
7
+ </Merchant>
8
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
9
+ <SignedInfo>
10
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
11
+ <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
12
+ <Reference URI="">
13
+ <Transforms>
14
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
15
+ </Transforms>
16
+ <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
17
+ <DigestValue>digest_value</DigestValue>
18
+ </Reference>
19
+ </SignedInfo>
20
+ <SignatureValue>signature_value</SignatureValue>
21
+ <KeyInfo>
22
+ <KeyName>fingerprint</KeyName>
23
+ </KeyInfo>
24
+ </Signature>
25
+ </DirectoryReq>
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <AcquirerStatusReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
3
+ <createDateTimestamp>created_at_timestamp</createDateTimestamp>
4
+ <Merchant>
5
+ <merchantID>002054205</merchantID>
6
+ <subID>0</subID>
7
+ </Merchant>
8
+ <Transaction>
9
+ <transactionID>transaction_id</transactionID>
10
+ </Transaction>
11
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
12
+ <SignedInfo>
13
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
14
+ <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
15
+ <Reference URI="">
16
+ <Transforms>
17
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
18
+ </Transforms>
19
+ <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
20
+ <DigestValue>digest_value</DigestValue>
21
+ </Reference>
22
+ </SignedInfo>
23
+ <SignatureValue>signature_value</SignatureValue>
24
+ <KeyInfo>
25
+ <KeyName>fingerprint</KeyName>
26
+ </KeyInfo>
27
+ </Signature>
28
+ </AcquirerStatusReq>
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <AcquirerTrxReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
3
+ <createDateTimestamp>created_at_timestamp</createDateTimestamp>
4
+ <Issuer>
5
+ <issuerID>issuer_id</issuerID>
6
+ </Issuer>
7
+ <Merchant>
8
+ <merchantID>002054205</merchantID>
9
+ <subID>0</subID>
10
+ <merchantReturnURL>return_url</merchantReturnURL>
11
+ </Merchant>
12
+ <Transaction>
13
+ <purchaseID>purchase_id</purchaseID>
14
+ <amount>amount</amount>
15
+ <currency>EUR</currency>
16
+ <expirationPeriod>expiration_period</expirationPeriod>
17
+ <language>nl</language>
18
+ <description>description</description>
19
+ <entranceCode>entrance_code</entranceCode>
20
+ </Transaction>
21
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
22
+ <SignedInfo>
23
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
24
+ <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
25
+ <Reference URI="">
26
+ <Transforms>
27
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
28
+ </Transforms>
29
+ <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
30
+ <DigestValue>digest_value</DigestValue>
31
+ </Reference>
32
+ </SignedInfo>
33
+ <SignatureValue>signature_value</SignatureValue>
34
+ <KeyInfo>
35
+ <KeyName>fingerprint</KeyName>
36
+ </KeyInfo>
37
+ </Signature>
38
+ </AcquirerTrxReq>
@@ -0,0 +1,7 @@
1
+ default:
2
+ acquirer: :rabobank
3
+ merchant_id: '002054205'
4
+ passphrase: wachtwoord
5
+ private_key_file: ../certs/bestandsnaam.key
6
+ private_certificate_file: ../certs/bestandsnaam.cer
7
+ ideal_certificate_file: ../certs/ideal.cer
@@ -0,0 +1,205 @@
1
+ # encoding: utf-8
2
+ require 'coveralls'
3
+
4
+ Coveralls.wear!
5
+
6
+ require 'rubygems'
7
+ require 'ideal'
8
+ require 'yaml'
9
+ require 'mocha'
10
+
11
+ class Idealtest
12
+ describe Ideal do
13
+
14
+ before(:each) do
15
+ setup_ideal_gateway(fixtures('default'))
16
+ Ideal::Gateway.environment = :test
17
+
18
+ @gateway = Ideal::Gateway.new
19
+
20
+ @@issuer ||= {id: 'RABONL2U'}
21
+
22
+ @valid_options = {
23
+ :issuer_id => @@issuer[:id],
24
+ :expiration_period => 'PT10M',
25
+ :return_url => 'http://return_to.example.com',
26
+ :order_id => '123456789012',
27
+ :currency => 'EUR',
28
+ :description => 'A classic Dutch windmill',
29
+ :entrance_code => '1234'
30
+ }
31
+ end
32
+
33
+
34
+ describe 'requests' do
35
+ it 'should go to the test environment' do
36
+ expect(@gateway.issuers.test?).to be true
37
+ end
38
+ end
39
+
40
+ describe 'valid requests' do
41
+ it 'should proceed' do
42
+ response = @gateway.setup_purchase(550, @valid_options)
43
+ expect(response.success?).to be true
44
+ expect(response.service_url).not_to be nil
45
+ expect(response.transaction_id).not_to be nil
46
+ expect(response.order_id).to eq(@valid_options[:order_id])
47
+ expect(response.verified?).to be true
48
+ end
49
+ end
50
+
51
+ describe 'invalid requests' do
52
+ it 'should be rejected' do
53
+ response = @gateway.setup_purchase('0;5', @valid_options)
54
+
55
+ expect(response.success?).to eq false
56
+ expect(response.error_code).to eq('BR1210')
57
+ expect(response.error_message).not_to be nil
58
+ expect(response.consumer_error_message).not_to be nil
59
+ end
60
+ end
61
+
62
+ describe 'valid requests but incorrectly signed' do
63
+ it 'should be rejected' do
64
+ allow_any_instance_of(Xmldsig::SignedDocument).to receive(:validate).and_return(false)
65
+ response = capture_transaction(:success)
66
+
67
+ expect(response.verified?).to be false
68
+ end
69
+ end
70
+
71
+ ###
72
+ #
73
+ # These are the 7 integration tests of ING which need to be ran sucessfuly
74
+ # _before_ you'll get access to the live environment.
75
+ #
76
+ # See test_transaction_id for info on how the remote tests are ran.
77
+ #
78
+
79
+ describe '#issuers' do
80
+ it 'return a list of iDeal issuers' do
81
+ issuer_list = @gateway.issuers.list
82
+ expect(issuer_list.length).to eq(2)
83
+ expect(issuer_list[0][:id]).to eq('INGBNL2A')
84
+ end
85
+ end
86
+
87
+
88
+ describe 'successful transaction' do
89
+ it 'should be successful' do
90
+ res = capture_transaction(:success)
91
+ expect(res.success?).to be true
92
+ expect(res.status).to eq(:success)
93
+ expect(res.verified?).to be true
94
+ expect(res.consumer_iban).to eq('NL17RABO0213698412')
95
+ expect(res.consumer_name).to eq('Hr E G H Küppers en/of MW M.J. Küppers-Veeneman')
96
+ expect(res.consumer_bic).to eq('RABONL2U')
97
+ end
98
+ end
99
+
100
+ describe 'cancelled transaction' do
101
+ it 'should be cancelled' do
102
+ res = capture_transaction(:cancelled)
103
+ expect(res.success?).to be false
104
+ expect(res.status).to eq(:cancelled)
105
+ expect(res.verified?).to be true
106
+ end
107
+ end
108
+
109
+ describe 'expired transaction' do
110
+ it 'should be expired' do
111
+ res = capture_transaction(:expired)
112
+ expect(res.success?).to be false
113
+ expect(res.status).to eq(:expired)
114
+ expect(res.verified?).to be true
115
+ end
116
+ end
117
+
118
+ describe 'open transaction' do
119
+ it 'should be open' do
120
+ res = capture_transaction(:open)
121
+ expect(res.success?).to be false
122
+ expect(res.status).to eq(:open)
123
+ expect(res.verified?).to be true
124
+ end
125
+ end
126
+
127
+ describe 'failed transaction' do
128
+ it 'should be failure' do
129
+ res = capture_transaction(:failure)
130
+ expect(res.success?).to be false
131
+ expect(res.status).to eq(:failure)
132
+ expect(res.verified?).to be true
133
+ end
134
+ end
135
+
136
+ describe 'server_error transaction' do
137
+ it 'should be server_error' do
138
+ res = capture_transaction(:server_error)
139
+ expect(res.success?).to be false
140
+ expect(res.verified?).to be true
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ # Shortcut method which does a #setup_purchase through #test_transaction and
147
+ # captures the resulting transaction and returns the capture response.
148
+ def capture_transaction(type)
149
+ @gateway.capture test_transaction(type).transaction_id
150
+ end
151
+
152
+ # Calls #setup_purchase with the amount corresponding to the named test and
153
+ # returns the response. Before returning an assertion will be ran to test
154
+ # whether or not the transaction was successful.
155
+ def test_transaction(type)
156
+ amount = case type
157
+ when :success then
158
+ 1.00
159
+ when :cancelled then
160
+ 2.00
161
+ when :expired then
162
+ 3.00
163
+ when :open then
164
+ 4.00
165
+ when :failure then
166
+ 5.00
167
+ when :server_error then
168
+ 7.00
169
+ end
170
+
171
+ response = @gateway.setup_purchase(amount, @valid_options)
172
+ expect(response.success?).to be true
173
+
174
+ log('RESP', response.service_url)
175
+
176
+ response
177
+ end
178
+
179
+ def fixtures(key)
180
+ file = File.join(File.dirname(__FILE__), 'fixtures.yml')
181
+ fixtures ||= YAML.load(File.read(file))
182
+ @fixture = fixtures[key] || raise(StandardError, "No fixture data was found for key '#{key}'")
183
+ @fixture['private_key_file'] = File.join(File.dirname(__FILE__), @fixture['private_key_file'])
184
+ @fixture['private_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['private_certificate_file'])
185
+ @fixture['ideal_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['ideal_certificate_file'])
186
+ @fixture
187
+ end
188
+
189
+ # Setup the gateway by providing a hash of attributes and values.
190
+ def setup_ideal_gateway(fixture)
191
+ fixture = fixture.dup
192
+ # The passphrase needs to be set first, otherwise the key won't initialize properly
193
+ if passphrase = fixture.delete('passphrase')
194
+ Ideal::Gateway.passphrase = passphrase
195
+ end
196
+ fixture.each { |key, value| Ideal::Gateway.send("#{key}=", value) }
197
+ Ideal::Gateway.live_url = nil
198
+ end
199
+
200
+ end
201
+ end
202
+
203
+ def log(a, b)
204
+ #$stderr.write("#{a}: #{b}")
205
+ end
@@ -0,0 +1,500 @@
1
+ require 'coveralls'
2
+
3
+ Coveralls.wear!
4
+
5
+ require 'rubygems'
6
+ require 'ideal'
7
+ require 'yaml'
8
+ require 'mocha'
9
+
10
+
11
+ def strip_whitespace(str)
12
+ str.gsub('/\s/m', '')
13
+ end
14
+
15
+ class GeneralMethodTest
16
+ describe Ideal do
17
+
18
+ before(:all) do
19
+ file = File.join(File.dirname(__FILE__), 'fixtures.yml')
20
+ fixtures ||= YAML.load(File.read(file))
21
+ @fixture = fixtures['default'] || raise(StandardError, 'No fixture data was found for key "default"')
22
+ @fixture['private_key_file'] = File.join(File.dirname(__FILE__), @fixture['private_key_file'])
23
+ @fixture['private_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['private_certificate_file'])
24
+ @fixture['ideal_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['ideal_certificate_file'])
25
+ # The passphrase needs to be set first, otherwise the key won't initialize properly
26
+ if (passphrase = @fixture.delete('passphrase')) # Yes the = is intended, not ==
27
+ Ideal::Gateway.passphrase = passphrase
28
+ end
29
+ @fixture.each { |key, value| Ideal::Gateway.send("#{key}=", value) }
30
+ Ideal::Gateway.live_url = nil
31
+ end
32
+
33
+ describe '#merchant_id' do
34
+ it 'returns the set merchant id' do
35
+ expect(Ideal::Gateway.merchant_id).to eq('002054205')
36
+ end
37
+ end
38
+
39
+ describe '#url' do
40
+ it 'returns the set test url from the fixture' do
41
+ expect(Ideal::Gateway.test_url).to eq('https://idealtest.rabobank.nl/ideal/iDEALv3')
42
+ end
43
+ end
44
+
45
+ describe '#url' do
46
+ it 'returns the ideal url for issuer ING' do
47
+ Ideal::Gateway.acquirer = :ing
48
+ expect(Ideal::Gateway.live_url).to eq('https://ideal.secure-ing.com/ideal/iDEALv3')
49
+ end
50
+ end
51
+
52
+ describe '#url' do
53
+ it 'returns the ideal url for issuer Rabobank' do
54
+ Ideal::Gateway.acquirer = :rabobank
55
+ expect(Ideal::Gateway.live_url).to eq('https://ideal.rabobank.nl/ideal/iDEALv3')
56
+ end
57
+ end
58
+
59
+ describe '#url' do
60
+ it 'returns the ideal url for issuer ABN Amro' do
61
+ Ideal::Gateway.acquirer = :abnamro
62
+ expect(Ideal::Gateway.live_url).to eq('https://abnamro.ideal-payment.de/ideal/iDeal')
63
+ end
64
+ end
65
+
66
+ describe '#acquirer' do
67
+ it 'throws an error if the acquirer is non existent' do
68
+ expect { Ideal::Gateway.acquirer = :nonexistent }.to raise_error(ArgumentError)
69
+ end
70
+ end
71
+
72
+ describe '#private_certificate' do
73
+ it 'returns the currently loaded private certificate' do
74
+ private_cert_file = File.read(@fixture['private_certificate_file'])
75
+ private_cert = OpenSSL::X509::Certificate.new(private_cert_file)
76
+ expect(Ideal::Gateway.private_certificate.to_text).to eq(private_cert.to_text)
77
+ end
78
+ end
79
+
80
+ describe '#' do
81
+ it 'returns the private key' do
82
+ private_key_file = File.read(@fixture['private_key_file'])
83
+ private_key = OpenSSL::PKey::RSA.new(private_key_file, Ideal::Gateway.passphrase)
84
+ expect(Ideal::Gateway.private_key.to_text).to eq(private_key.to_text)
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ class GeneralTest
92
+ describe Ideal do
93
+
94
+ before(:each) do
95
+ file = File.join(File.dirname(__FILE__), 'fixtures.yml')
96
+ fixtures ||= YAML.load(File.read(file))
97
+ @fixture = fixtures['default'] || raise(StandardError, 'No fixture data was found for key "default"')
98
+ @fixture['private_key_file'] = File.join(File.dirname(__FILE__), @fixture['private_key_file'])
99
+ @fixture['private_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['private_certificate_file'])
100
+ @fixture['ideal_certificate_file'] = File.join(File.dirname(__FILE__), @fixture['ideal_certificate_file'])
101
+ # The passphrase needs to be set first, otherwise the key won't initialize properly
102
+ if (passphrase = @fixture.delete('passphrase')) # Yes the = is intended, not ==
103
+ Ideal::Gateway.passphrase = passphrase
104
+ end
105
+ @fixture.each { |key, value| Ideal::Gateway.send("#{key}=", value) }
106
+ Ideal::Gateway.live_url = nil
107
+ @gateway = Ideal::Gateway.new
108
+ end
109
+
110
+ describe '#initialize' do
111
+ it 'has a sub_id of 0 when just initialized or picks the supplied value' do
112
+ expect(Ideal::Gateway.new.sub_id).to eq(0)
113
+ expect(Ideal::Gateway.new(:sub_id => 1).sub_id).to eq(1)
114
+ end
115
+ end
116
+
117
+ describe '#send' do
118
+ it 'returns the test_url if the test mode is active' do
119
+ gateway = Ideal::Gateway.new
120
+ Ideal::Gateway.acquirer = :rabobank
121
+ Ideal::Gateway.environment = :test
122
+ expect(gateway.send(:request_url)).to eq(Ideal::Gateway.test_url)
123
+ end
124
+ end
125
+
126
+ describe '#send' do
127
+ it 'returns the live_url if the test mode is inactive' do
128
+ gateway = Ideal::Gateway.new
129
+ Ideal::Gateway.acquirer = :rabobank
130
+ Ideal::Gateway.environment = :live
131
+ expect(gateway.send(:request_url)).to eq(Ideal::Gateway.live_url)
132
+ end
133
+ end
134
+
135
+ describe '#send' do
136
+ it 'sends the correct created at datestamp' do
137
+ timestamp = '2011-11-28T16:30:00.000Z' # Exact time inventid was founded
138
+ allow_any_instance_of(Time).to receive(:gmtime).and_return(DateTime.parse(timestamp))
139
+
140
+ expect(@gateway.send(:created_at_timestamp)).to eq(timestamp)
141
+ end
142
+ end
143
+
144
+ describe '#send' do
145
+ it 'generates a correct digest' do
146
+ sha256 = OpenSSL::Digest::SHA256.new
147
+ allow_any_instance_of(OpenSSL::Digest::SHA256).to receive(:new).and_return(sha256)
148
+ xml = Nokogiri::XML::Builder.new do |xml|
149
+ xml.request do |innerxml|
150
+ xml.content 'digest test'
151
+ @gateway.send(:sign!, innerxml)
152
+ end
153
+ end
154
+ digest_value = xml.doc.at_xpath('//xmlns:DigestValue', 'xmlns' => 'http://www.w3.org/2000/09/xmldsig#').text
155
+ xml.doc.at_xpath('//xmlns:Signature', 'xmlns' => 'http://www.w3.org/2000/09/xmldsig#').remove
156
+ canonical = xml.doc.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
157
+ digest = sha256.digest canonical
158
+ expected_digest_value = strip_whitespace(Base64.encode64(digest.strip)) # .strip does not remove the \n
159
+ expect(expected_digest_value).to eq(digest_value)
160
+ end
161
+ end
162
+
163
+ #=begin
164
+ describe '#send' do
165
+ it 'generates a correct signature' do
166
+ sha256 = OpenSSL::Digest::SHA256.new
167
+ allow_any_instance_of(OpenSSL::Digest::SHA256).to receive(:new).and_return(sha256)
168
+ xml1 = Nokogiri::XML::Builder.new do |xml|
169
+ xml.request do |innerxml|
170
+ xml.content 'signature test'
171
+ @gateway.send(:sign!, innerxml)
172
+ end
173
+ end
174
+ signature_value = @gateway.send(:signature_value, xml1.doc)
175
+ xml2 = xml1.doc.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
176
+ signature = Ideal::Gateway.private_key.sign(sha256, xml2)
177
+ expected_signature_value = strip_whitespace(Base64.encode64(signature))
178
+ expect(signature_value).to eq(expected_signature_value)
179
+ end
180
+ end
181
+ #=end
182
+
183
+ describe '#send' do
184
+ it 'generates a correct fingerprint' do
185
+ certificate_file = File.read(@fixture['private_certificate_file'])
186
+ expected_token = Digest::SHA1.hexdigest(OpenSSL::X509::Certificate.new(certificate_file).to_der)
187
+ expect(@gateway.send(:fingerprint)).to eq(expected_token)
188
+ end
189
+ end
190
+
191
+ describe '#send' do
192
+ it 'performs an SSL POST and returns the correct response' do
193
+ Ideal::Gateway.environment = :test
194
+ allow_any_instance_of(Ideal::Response).to receive(:new).with('response', :test => true)
195
+ allow(@gateway).to receive(:ssl_post).with(@gateway.request_url, 'data').and_return('<x>response</x>')
196
+ @gateway.send(:post_data, @gateway.request_url, 'data', Ideal::Response)
197
+ end
198
+ end
199
+
200
+ end
201
+ end
202
+
203
+ class XmlTest
204
+ describe Ideal do
205
+
206
+ before(:each) do
207
+ @gateway = Ideal::Gateway.new
208
+ allow(@gateway).to receive(:created_at_timestamp).and_return('created_at_timestamp')
209
+ allow(@gateway).to receive(:digest_value).and_return('digest_value')
210
+ allow(@gateway).to receive(:signature_value).and_return('signature_value')
211
+ allow(@gateway).to receive(:fingerprint).and_return('fingerprint')
212
+ end
213
+
214
+ describe Ideal do
215
+ it 'creates a correct TransactionRequestXml' do
216
+ options = {
217
+ issuer_id: 'issuer_id',
218
+ return_url: 'return_url',
219
+ order_id: 'purchase_id',
220
+ expiration_period: 'expiration_period',
221
+ description: 'description',
222
+ entrance_code: 'entrance_code'
223
+ }
224
+ xml = @gateway.send(:build_transaction_request, 'amount', options)
225
+ expected_response = Nokogiri::XML.parse(File.read(File.join(File.dirname(__FILE__), 'expectation_xml/transaction_request.xml')))
226
+ actual_response = Nokogiri::XML.parse(xml)
227
+ expect(actual_response.to_s).to eq(expected_response.to_s)
228
+ end
229
+ end
230
+
231
+ describe Ideal do
232
+ it 'creates a correct StatusRequestXml' do
233
+ options = {
234
+ transaction_id: 'transaction_id'
235
+ }
236
+ xml = @gateway.send(:build_status_request, options)
237
+ expected_response = Nokogiri::XML.parse(File.read(File.join(File.dirname(__FILE__), 'expectation_xml/status_request.xml')))
238
+ actual_response = Nokogiri::XML.parse(xml)
239
+ expect(actual_response.to_s).to eq(expected_response.to_s)
240
+ end
241
+ end
242
+
243
+ describe Ideal do
244
+ it 'creates a correct DirectoryRequestXml' do
245
+ xml = @gateway.send(:build_directory_request)
246
+ expected_response = Nokogiri::XML.parse(File.read(File.join(File.dirname(__FILE__), 'expectation_xml/directory_request.xml')))
247
+ actual_response = Nokogiri::XML.parse(xml)
248
+ expect(actual_response.to_s).to eq(expected_response.to_s)
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ class ErrorHandlingTest
255
+ describe Ideal do
256
+
257
+ before(:each) do
258
+ @gateway = Ideal::Gateway.new
259
+ allow(@gateway).to receive(:created_at_timestamp).and_return('created_at_timestamp')
260
+ allow(@gateway).to receive(:digest_value).and_return('digest_value')
261
+ allow(@gateway).to receive(:signature_value).and_return('signature_value')
262
+ allow(@gateway).to receive(:fingerprint).and_return('fingerprint')
263
+
264
+ @transaction_id = '1234567890123456'
265
+ end
266
+
267
+ describe '#send' do
268
+ it 'accepts a valid request with valid options' do
269
+ expect(@gateway.send(:build_transaction_request, 4321, VALID_PURCHASE_OPTIONS)).to be_truthy
270
+ end
271
+ end
272
+
273
+ describe '#send' do
274
+ it 'should error on erroneously long fields in a transaction request' do
275
+ expect { @gateway.send(:build_transaction_request, 1234567890123, VALID_PURCHASE_OPTIONS) }.to raise_error(ArgumentError) # 13 chars
276
+
277
+ [
278
+ [:order_id, '12345678901234567'], # 17 chars,
279
+ [:description, '123456789012345678901234567890123'], # 33 chars
280
+ [:entrance_code, '12345678901234567890123456789012345678901'] # 41
281
+ ].each do |key, value|
282
+ options = VALID_PURCHASE_OPTIONS.dup
283
+ options[key] = value
284
+
285
+ expect { @gateway.send(:build_transaction_request, 4321, options) }.to raise_error(ArgumentError)
286
+ end
287
+ end
288
+ end
289
+
290
+ describe '#send' do
291
+ it 'should error on a missing required field in a transaction request' do
292
+ options = VALID_PURCHASE_OPTIONS.dup
293
+ options.keys.each do |key|
294
+ options.delete(key)
295
+
296
+ expect { @gateway.send(:build_transaction_request, 100, options) }.to raise_error(ArgumentError)
297
+ end
298
+ end
299
+ end
300
+
301
+ describe '#send' do
302
+ it 'should error on inappropriate characters for a transaction request' do
303
+ expect { @gateway.send(:build_transaction_request, 'graphème', VALID_PURCHASE_OPTIONS) }.to raise_error(ArgumentError)
304
+
305
+ [:order_id, :description, :entrance_code].each do |key, value|
306
+ options = VALID_PURCHASE_OPTIONS.dup
307
+ options[key] = 'graphème'
308
+
309
+ expect { @gateway.send(:build_transaction_request, 4321, options) }.to raise_error(ArgumentError)
310
+ end
311
+ end
312
+ end
313
+
314
+ describe '#send' do
315
+ it 'should error on missing required options for a status request' do
316
+ expect { @gateway.send(:build_status_request, {}) }.to raise_error(ArgumentError)
317
+ end
318
+ end
319
+
320
+ end
321
+ end
322
+
323
+ class ResponseTest
324
+ describe Ideal do
325
+
326
+ before(:each) do
327
+ file = File.join(File.dirname(__FILE__), 'test_xml/large_directory_response.xml')
328
+ @xml = File.read(file)
329
+ @response = Ideal::Response.new(@xml)
330
+ end
331
+
332
+ describe '#test' do
333
+ it 'should set the environment correctly on instantiation or use the default' do
334
+ file = File.join(File.dirname(__FILE__), 'test_xml/large_directory_response.xml')
335
+ xml = File.read(file)
336
+ expect(Ideal::Response.new(xml, :test => true).test?).to be true
337
+ expect(Ideal::Response.new(xml, :test => false).test?).to be false
338
+ expect(Ideal::Response.new(xml).test?).to be false
339
+ end
340
+ end
341
+
342
+ describe 'response' do
343
+ it 'return a correct response body' do
344
+ file = File.join(File.dirname(__FILE__), 'test_xml/large_directory_response.xml')
345
+ xml = File.read(file)
346
+ response = Ideal::Response.new(xml)
347
+ expect(response.instance_variable_get(:@response).to_s).to eq(Nokogiri::XML.parse(xml).remove_namespaces!.root.to_s)
348
+ expect(response.success?).to be true
349
+ expect(response.error_message).to be nil
350
+ expect(response.error_code).to be nil
351
+ end
352
+ end
353
+
354
+ describe 'error' do
355
+ it 'gives an error if the response is incorrect' do
356
+ file = File.join(File.dirname(__FILE__), 'test_xml/error_response.xml')
357
+ xml = File.read(file)
358
+ response = Ideal::Response.new(xml)
359
+
360
+ expect(response.success?).to be false
361
+ expect(response.error_message).to eq('Failure in system')
362
+ expect(response.error_details).to eq('System generating error: issuer')
363
+ expect(response.consumer_error_message).to eq('Betalen met iDEAL is nu niet mogelijk.')
364
+ expect(response.error_code).to eq('SO1000')
365
+ [
366
+ ['IX1000', :xml],
367
+ ['SO1000', :system],
368
+ ['SE2000', :security],
369
+ ['BR1200', :value],
370
+ ['AP1000', :application]
371
+ ].each do |code, type|
372
+ allow(response).to receive(:error_code).and_return(code)
373
+ expect(response.error_type).to eq(type)
374
+ end
375
+
376
+ end
377
+ end
378
+
379
+ describe 'directory' do
380
+ it 'returns the list of issuers' do
381
+ gateway = Ideal::Gateway.new
382
+
383
+ file = File.join(File.dirname(__FILE__), 'test_xml/small_directory_response.xml')
384
+ xml = File.read(file)
385
+
386
+ allow(gateway).to receive(:build_directory_request).and_return('the request body')
387
+ expect(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(xml)
388
+
389
+ expected_issuers = [{:id => '1006', :country => 'RogierIsGaafLand', :name => 'ABN AMRO Bank'}]
390
+
391
+ directory_response = gateway.issuers
392
+ expect(directory_response).to be_a Ideal::DirectoryResponse
393
+ expect(directory_response.list).to eq(expected_issuers)
394
+
395
+ file = File.join(File.dirname(__FILE__), 'test_xml/large_directory_response.xml')
396
+ xml = File.read(file)
397
+
398
+ expected_issuers = [
399
+ {:id => '1006', :country => 'JoostIsCool', :name => 'ABN AMRO Bank'},
400
+ {:id => '1003', :country => 'JoostIsCool', :name => 'Postbank'},
401
+ {:id => '1005', :country => 'JoostIsCool', :name => 'Rabobank'},
402
+ {:id => '1017', :country => 'XmlIsCoolOhNee', :name => 'Asr bank'},
403
+ {:id => '1023', :country => 'XmlIsCoolOhNee', :name => 'Van Lanschot'}
404
+ ]
405
+
406
+ expect(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(xml)
407
+
408
+ directory_response = gateway.issuers
409
+ expect(directory_response).to be_a Ideal::DirectoryResponse
410
+ expect(directory_response.list).to eq(expected_issuers)
411
+ end
412
+ end
413
+
414
+ describe 'purchase' do
415
+ it 'should return a valid transaction response' do
416
+ gateway = Ideal::Gateway.new
417
+ file = File.join(File.dirname(__FILE__), 'test_xml/transaction_response.xml')
418
+ xml = File.read(file)
419
+
420
+ allow(gateway).to receive(:build_transaction_request).with(4321, VALID_PURCHASE_OPTIONS).and_return('the request body')
421
+ expect(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(xml)
422
+
423
+ setup_purchase_response = gateway.setup_purchase(4321, VALID_PURCHASE_OPTIONS)
424
+
425
+ expect(setup_purchase_response).to be_a Ideal::TransactionResponse
426
+
427
+ expect(setup_purchase_response.service_url).to eq('https://ideal.example.com/long_service_url?X009=BETAAL&X010=20')
428
+
429
+ expect(setup_purchase_response.transaction_id).to eq('0001023456789112')
430
+ expect(setup_purchase_response.order_id).to eq('iDEAL-aankoop 21')
431
+ end
432
+ end
433
+
434
+ end
435
+
436
+ describe 'purchase' do
437
+ it 'should start a transaction at the acquirer' do
438
+
439
+ gateway = Ideal::Gateway.new
440
+ file = File.join(File.dirname(__FILE__), 'test_xml/transaction_response.xml')
441
+ xml = File.read(file)
442
+
443
+ allow(gateway).to receive(:build_transaction_request).with(4321, VALID_PURCHASE_OPTIONS).and_return('the request body')
444
+ expect(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(xml)
445
+
446
+ setup_purchase_response = gateway.setup_purchase(4321, VALID_PURCHASE_OPTIONS)
447
+
448
+ expect(setup_purchase_response).to be_a(Ideal::TransactionResponse)
449
+
450
+ expect(setup_purchase_response.service_url).to eq('https://ideal.example.com/long_service_url?X009=BETAAL&X010=20')
451
+
452
+ expect(setup_purchase_response.transaction_id).to eq('0001023456789112')
453
+ expect(setup_purchase_response.order_id).to eq('iDEAL-aankoop 21')
454
+ end
455
+ end
456
+
457
+ describe 'capturePurchase' do
458
+ it 'should do something correctly and not wrong' do
459
+ gateway = Ideal::Gateway.new
460
+
461
+ allow(gateway).to receive(:build_status_request).
462
+ with(:transaction_id => '0001023456789112').and_return('the request body')
463
+
464
+ allow(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(File.read(File.join(File.dirname(__FILE__), 'test_xml/status_response_succeeded.xml')))
465
+ expect(gateway.capture('0001023456789112')).to be_a Ideal::StatusResponse
466
+
467
+ allow_any_instance_of(Ideal::StatusResponse).to receive(:verified?).and_return(true)
468
+ capture_response = gateway.capture('0001023456789112')
469
+ expect(capture_response.success?).to be true
470
+ end
471
+ end
472
+
473
+ describe 'capturePurchase' do
474
+ it 'tololol' do
475
+
476
+ gateway = Ideal::Gateway.new
477
+ allow(gateway).to receive(:build_status_request).
478
+ with(:transaction_id => '0001023456789112').and_return('the request body')
479
+
480
+ allow(gateway).to receive(:ssl_post).with(gateway.request_url, 'the request body').and_return(File.read(File.join(File.dirname(__FILE__), 'test_xml/status_response_succeeded_incorrect.xml')))
481
+ allow_any_instance_of(Ideal::StatusResponse).to receive(:verified?).and_return(false)
482
+ capture_response = gateway.capture('0001023456789112')
483
+ expect(capture_response.verified?).to be false
484
+ expect(capture_response.success?).to be false
485
+ end
486
+
487
+
488
+ end
489
+ end
490
+
491
+ # Defaults
492
+
493
+ VALID_PURCHASE_OPTIONS = {
494
+ :issuer_id => '99999IBAN',
495
+ :expiration_period => 'PT10M',
496
+ :return_url => 'https://example.inventid.net',
497
+ :order_id => '1234567890',
498
+ :description => 'A beautiful Eticket',
499
+ :entrance_code => '1234'
500
+ }