ideal-payment 1.0.2

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,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
+ }