adyen_jpiqueras 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +30 -0
  4. data/CHANGELOG.md +128 -0
  5. data/CONTRIBUTING.md +85 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +21 -0
  8. data/README.md +31 -0
  9. data/Rakefile +54 -0
  10. data/adyen_jpiqueras.gemspec +44 -0
  11. data/config.ru +5 -0
  12. data/lib/adyen.rb +16 -0
  13. data/lib/adyen/api.rb +424 -0
  14. data/lib/adyen/api/cacert.pem +3894 -0
  15. data/lib/adyen/api/payment_service.rb +374 -0
  16. data/lib/adyen/api/recurring_service.rb +188 -0
  17. data/lib/adyen/api/response.rb +61 -0
  18. data/lib/adyen/api/simple_soap_client.rb +134 -0
  19. data/lib/adyen/api/templates/payment_service.rb +159 -0
  20. data/lib/adyen/api/templates/recurring_service.rb +71 -0
  21. data/lib/adyen/api/test_helpers.rb +133 -0
  22. data/lib/adyen/api/xml_querier.rb +137 -0
  23. data/lib/adyen/base.rb +17 -0
  24. data/lib/adyen/configuration.rb +179 -0
  25. data/lib/adyen/form.rb +419 -0
  26. data/lib/adyen/hpp.rb +27 -0
  27. data/lib/adyen/hpp/request.rb +192 -0
  28. data/lib/adyen/hpp/response.rb +52 -0
  29. data/lib/adyen/hpp/signature.rb +34 -0
  30. data/lib/adyen/matchers.rb +92 -0
  31. data/lib/adyen/notification_generator.rb +30 -0
  32. data/lib/adyen/railtie.rb +13 -0
  33. data/lib/adyen/rest.rb +67 -0
  34. data/lib/adyen/rest/authorise_payment.rb +234 -0
  35. data/lib/adyen/rest/authorise_recurring_payment.rb +46 -0
  36. data/lib/adyen/rest/client.rb +127 -0
  37. data/lib/adyen/rest/errors.rb +33 -0
  38. data/lib/adyen/rest/modify_payment.rb +89 -0
  39. data/lib/adyen/rest/payout.rb +89 -0
  40. data/lib/adyen/rest/request.rb +104 -0
  41. data/lib/adyen/rest/response.rb +80 -0
  42. data/lib/adyen/rest/signature.rb +27 -0
  43. data/lib/adyen/signature.rb +76 -0
  44. data/lib/adyen/templates/notification_migration.rb +29 -0
  45. data/lib/adyen/templates/notification_model.rb +69 -0
  46. data/lib/adyen/util.rb +147 -0
  47. data/lib/adyen/version.rb +5 -0
  48. data/spec/api/api_spec.rb +231 -0
  49. data/spec/api/payment_service_spec.rb +505 -0
  50. data/spec/api/recurring_service_spec.rb +236 -0
  51. data/spec/api/response_spec.rb +59 -0
  52. data/spec/api/simple_soap_client_spec.rb +133 -0
  53. data/spec/api/spec_helper.rb +463 -0
  54. data/spec/api/test_helpers_spec.rb +84 -0
  55. data/spec/functional/api_spec.rb +117 -0
  56. data/spec/functional/initializer.rb.ci +3 -0
  57. data/spec/functional/initializer.rb.sample +3 -0
  58. data/spec/spec_helper.rb +8 -0
  59. data/test/form_test.rb +303 -0
  60. data/test/functional/payment_authorisation_api_test.rb +107 -0
  61. data/test/functional/payment_modification_api_test.rb +58 -0
  62. data/test/functional/payout_api_test.rb +93 -0
  63. data/test/helpers/capybara.rb +12 -0
  64. data/test/helpers/configure_adyen.rb +6 -0
  65. data/test/helpers/example_server.rb +136 -0
  66. data/test/helpers/public/adyen.encrypt.js +679 -0
  67. data/test/helpers/public/adyen.encrypt.min.js +14 -0
  68. data/test/helpers/test_cards.rb +20 -0
  69. data/test/helpers/views/authorized.erb +7 -0
  70. data/test/helpers/views/hpp.erb +20 -0
  71. data/test/helpers/views/index.erb +6 -0
  72. data/test/helpers/views/pay.erb +36 -0
  73. data/test/helpers/views/redirect_shopper.erb +18 -0
  74. data/test/hpp/signature_test.rb +37 -0
  75. data/test/hpp_test.rb +250 -0
  76. data/test/integration/hpp_integration_test.rb +52 -0
  77. data/test/integration/payment_using_3d_secure_integration_test.rb +41 -0
  78. data/test/integration/payment_with_client_side_encryption_integration_test.rb +26 -0
  79. data/test/rest/signature_test.rb +36 -0
  80. data/test/rest_list_recurring_details_response_test.rb +22 -0
  81. data/test/rest_request_test.rb +43 -0
  82. data/test/rest_response_test.rb +19 -0
  83. data/test/signature_test.rb +76 -0
  84. data/test/test_helper.rb +45 -0
  85. data/test/util_test.rb +78 -0
  86. data/yard_extensions.rb +16 -0
  87. metadata +308 -0
@@ -0,0 +1,236 @@
1
+ # encoding: UTF-8
2
+ require 'api/spec_helper'
3
+ require 'date'
4
+
5
+ describe Adyen::API::RecurringService do
6
+ include APISpecHelper
7
+
8
+ before do
9
+ @params = {
10
+ :reference => 'order-id',
11
+ :amount => {
12
+ :currency => 'EUR',
13
+ :value => '1234',
14
+ },
15
+ :shopper => {
16
+ :email => 's.hopper@example.com',
17
+ :reference => 'user-id',
18
+ :ip => '61.294.12.12',
19
+ :statement => 'invoice number 123456'
20
+ },
21
+ :card => {
22
+ :expiry_month => 12,
23
+ :expiry_year => 2012,
24
+ :holder_name => 'Simon わくわく Hopper',
25
+ :number => '4444333322221111',
26
+ :cvc => '737',
27
+ # Maestro UK/Solo only
28
+ #:issue_number => ,
29
+ #:start_month => ,
30
+ #:start_year => ,
31
+ },
32
+ # German's Direct Debit (Elektronisches Lastschriftverfahren)
33
+ :elv => {
34
+ :holder_name => 'Simon わくわく Hopper',
35
+ :number => '1234567890',
36
+ :bank_location => 'Berlin',
37
+ :bank_location_id => '12345678',
38
+ :bank_name => 'TestBank',
39
+ }
40
+ }
41
+ @recurring = @object = Adyen::API::RecurringService.new(@params)
42
+ end
43
+
44
+ describe_request_body_of :list, '//recurring:listRecurringDetails/recurring:request' do
45
+ it_should_validate_request_parameters :merchant_account,
46
+ :shopper => [:reference]
47
+
48
+ it "includes the merchant account handle" do
49
+ text('./recurring:merchantAccount').should == 'SuperShopper'
50
+ end
51
+
52
+ it "includes the shopper’s reference" do
53
+ text('./recurring:shopperReference').should == 'user-id'
54
+ end
55
+
56
+ it "includes the type of contract, which is always `RECURRING'" do
57
+ text('./recurring:recurring/payment:contract').should == 'RECURRING'
58
+ end
59
+ end
60
+
61
+ describe_response_from :list, LIST_RESPONSE, 'listRecurringDetails' do
62
+ it_should_return_params_for_each_xml_backend({
63
+ :creation_date => DateTime.parse('2009-10-27T11:26:22.203+01:00'),
64
+ :last_known_shopper_email => 's.hopper@example.com',
65
+ :shopper_reference => 'user-id',
66
+ :details => [
67
+ {
68
+ :card => {
69
+ :expiry_date => Date.new(2012, 12, 31),
70
+ :holder_name => 'S. Hopper',
71
+ :number => '1111'
72
+ },
73
+ :recurring_detail_reference => 'RecurringDetailReference1',
74
+ :variant => 'mc',
75
+ :creation_date => DateTime.parse('2009-10-27T11:50:12.178+01:00')
76
+ },
77
+ {
78
+ :bank => {
79
+ :number => '123456789',
80
+ :bank_location_id => 'bank-location-id',
81
+ :bank_name => 'AnyBank',
82
+ :bic => 'BBBBCCLLbbb',
83
+ :country_code => 'NL',
84
+ :iban => 'NL69PSTB0001234567',
85
+ :holder_name => 'S. Hopper'
86
+ },
87
+ :recurring_detail_reference => 'RecurringDetailReference2',
88
+ :variant => 'IDEAL',
89
+ :creation_date => DateTime.parse('2009-10-27T11:26:22.216+01:00')
90
+ },
91
+ {
92
+ :elv => {
93
+ :holder_name => 'S. Hopper',
94
+ :number => '1234567890',
95
+ :bank_location => 'Berlin',
96
+ :bank_location_id => '12345678',
97
+ :bank_name => 'TestBank',
98
+ },
99
+ :recurring_detail_reference => 'RecurringDetailReference3',
100
+ :variant => 'elv',
101
+ :creation_date => DateTime.parse('2009-10-27T11:26:22.216+01:00')
102
+ }
103
+ ],
104
+ })
105
+
106
+ it "returns an array with just the detail references" do
107
+ @response.references.should == %w{ RecurringDetailReference1 RecurringDetailReference2 RecurringDetailReference3 }
108
+ end
109
+ end
110
+
111
+ describe_response_from :list, LIST_EMPTY_RESPONSE, 'listRecurringDetails' do
112
+ it "returns an empty hash when there are no details" do
113
+ @recurring.list.params.should == {}
114
+ end
115
+
116
+ it "returns an empty array when there are no references" do
117
+ @response.references.should == []
118
+ end
119
+ end
120
+
121
+ describe_request_body_of :disable, '//recurring:disable/recurring:request' do
122
+ it_should_validate_request_parameters :merchant_account,
123
+ :shopper => [:reference]
124
+
125
+ it "includes the merchant account handle" do
126
+ text('./recurring:merchantAccount').should == 'SuperShopper'
127
+ end
128
+
129
+ it "includes the shopper’s reference" do
130
+ text('./recurring:shopperReference').should == 'user-id'
131
+ end
132
+
133
+ it "includes the shopper’s recurring detail reference if it is given" do
134
+ xpath('./recurring:recurringDetailReference').should be_empty
135
+ @recurring.params[:recurring_detail_reference] = 'RecurringDetailReference1'
136
+ text('./recurring:recurringDetailReference').should == 'RecurringDetailReference1'
137
+ end
138
+ end
139
+
140
+ describe_response_from :disable, (DISABLE_RESPONSE % '[detail-successfully-disabled]'), 'disable' do
141
+ it "returns whether or not it was disabled" do
142
+ @response.should be_success
143
+ @response.should be_disabled
144
+
145
+ stub_net_http(DISABLE_RESPONSE % '[all-details-successfully-disabled]')
146
+ @response = @recurring.disable
147
+ @response.should be_success
148
+ @response.should be_disabled
149
+ end
150
+
151
+ it_should_return_params_for_each_xml_backend(:response => '[detail-successfully-disabled]')
152
+ end
153
+
154
+ describe_request_body_of :store_token, '//recurring:storeToken/recurring:request' do
155
+ it_should_validate_request_parameters :merchant_account,
156
+ :shopper => [:email, :reference]
157
+
158
+ it "includes the merchant account handle" do
159
+ text('./recurring:merchantAccount').should == 'SuperShopper'
160
+ end
161
+
162
+ it "includes the shopper’s reference" do
163
+ text('./recurring:shopperReference').should == 'user-id'
164
+ end
165
+
166
+ it "includes the shopper’s email" do
167
+ text('./recurring:shopperEmail').should == 's.hopper@example.com'
168
+ end
169
+
170
+ it "includes the creditcard details" do
171
+ xpath('./recurring:card') do |card|
172
+ # there's no reason why Nokogiri should escape these characters, but as long as they're correct
173
+ card.text('./payment:holderName').should == 'Simon わくわく Hopper'
174
+ card.text('./payment:number').should == '4444333322221111'
175
+ card.text('./payment:cvc').should == '737'
176
+ card.text('./payment:expiryMonth').should == '12'
177
+ card.text('./payment:expiryYear').should == '2012'
178
+ end
179
+ end
180
+
181
+ it "formats the creditcard’s expiry month as a two digit number" do
182
+ @recurring.params[:card][:expiry_month] = 6
183
+ text('./recurring:card/payment:expiryMonth').should == '06'
184
+ end
185
+
186
+ it "includes the necessary recurring and one-click contract info if the `:recurring' param is truthful" do
187
+ text('./recurring:recurring/payment:contract').should == 'RECURRING'
188
+ end
189
+ end
190
+
191
+ describe_request_body_of :store_token, '//recurring:storeToken/recurring:request' do
192
+ it_should_validate_request_parameters :merchant_account,
193
+ :shopper => [:email, :reference]
194
+
195
+ it "includes the merchant account handle" do
196
+ text('./recurring:merchantAccount').should == 'SuperShopper'
197
+ end
198
+
199
+ it "includes the shopper’s reference" do
200
+ text('./recurring:shopperReference').should == 'user-id'
201
+ end
202
+
203
+ it "includes the shopper’s email" do
204
+ text('./recurring:shopperEmail').should == 's.hopper@example.com'
205
+ end
206
+
207
+ it "includes the ELV details" do
208
+ xpath('./recurring:elv') do |elv|
209
+ # there's no reason why Nokogiri should escape these characters, but as long as they're correct
210
+ elv.text('./payment:accountHolderName').should == 'Simon わくわく Hopper'
211
+ elv.text('./payment:bankAccountNumber').should == '1234567890'
212
+ elv.text('./payment:bankLocation').should == 'Berlin'
213
+ elv.text('./payment:bankLocationId').should == '12345678'
214
+ elv.text('./payment:bankName').should == 'TestBank'
215
+ end
216
+ end
217
+
218
+ it "includes the necessary recurring and one-click contract info if the `:recurring' param is truthful" do
219
+ text('./recurring:recurring/payment:contract').should == 'RECURRING'
220
+ end
221
+ end
222
+
223
+ describe_response_from :disable, (DISABLE_RESPONSE % '[detail-successfully-disabled]'), 'disable' do
224
+ it "returns whether or not it was disabled" do
225
+ @response.should be_success
226
+ @response.should be_disabled
227
+
228
+ stub_net_http(DISABLE_RESPONSE % '[all-details-successfully-disabled]')
229
+ @response = @recurring.disable
230
+ @response.should be_success
231
+ @response.should be_disabled
232
+ end
233
+
234
+ it_should_return_params_for_each_xml_backend(:response => '[detail-successfully-disabled]')
235
+ end
236
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+ require 'api/spec_helper'
3
+
4
+ describe Adyen::API::Response do
5
+ before do
6
+ http_response = Net::HTTPOK.new('1.1', '200', 'OK')
7
+ http_response.add_field('Content-type', 'text/xml')
8
+ http_response.stub(:body).and_return(AUTHORISE_RESPONSE)
9
+ @response = Adyen::API::Response.new(http_response)
10
+ end
11
+
12
+ it "returns a XMLQuerier instance with the response body" do
13
+ @response.xml_querier.should be_instance_of(Adyen::API::XMLQuerier)
14
+ @response.xml_querier.to_s.rstrip.should == AUTHORISE_RESPONSE.rstrip
15
+ end
16
+
17
+ describe "with a successful HTTP response" do
18
+ it "returns that the (HTTP) request was a success" do
19
+ @response.should_not be_a_http_failure
20
+ @response.should be_a_success
21
+ end
22
+ end
23
+
24
+ describe "with a failed HTTP response" do
25
+ before do
26
+ http_response = Net::HTTPBadRequest.new('1.1', '400', 'Bad request')
27
+ @response = Adyen::API::Response.new(http_response)
28
+ end
29
+
30
+ it "returns that the (HTTP) request was not a success" do
31
+ @response.should be_a_http_failure
32
+ @response.should_not be_a_success
33
+ end
34
+ end
35
+
36
+ describe "with a server error HTTP response and _no_ SOAP fault message" do
37
+ before do
38
+ http_response = Net::HTTPServerError.new('1.1', '500', 'Internal Server Error')
39
+ http_response.stub(:body).and_return(%{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body></soap:Body></soap:Envelope>})
40
+ @response = Adyen::API::Response.new(http_response)
41
+ end
42
+
43
+ it "`server_error?` returns that the (HTTP) request did cause a server error" do
44
+ @response.server_error?.should be true
45
+ end
46
+ end
47
+
48
+ describe "with a server error HTTP response _and_ SOAP fault message" do
49
+ before do
50
+ http_response = Net::HTTPServerError.new('1.1', '500', 'Internal Server Error')
51
+ http_response.stub(:body).and_return(%{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Illegal argument. For input string: "100.0"</faultstring></soap:Fault></soap:Body></soap:Envelope>})
52
+ @response = Adyen::API::Response.new(http_response)
53
+ end
54
+
55
+ it "`server_error?` returns that the (HTTP) request did not cause a server error" do
56
+ @response.server_error?.should be false
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,133 @@
1
+ # encoding: UTF-8
2
+ require 'api/spec_helper'
3
+
4
+ module APISpecHelper
5
+ class SOAPClient < Adyen::API::SimpleSOAPClient
6
+ ENDPOINT_URI = 'https://%s.example.com/soap/Action'
7
+ end
8
+ end
9
+
10
+ describe Adyen::API::SimpleSOAPClient do
11
+ include APISpecHelper
12
+
13
+ before do
14
+ @client = APISpecHelper::SOAPClient.new(:reference => 'order-id')
15
+ end
16
+
17
+ it "returns the endpoint, for the current environment, from the ENDPOINT_URI constant" do
18
+ uri = APISpecHelper::SOAPClient.endpoint
19
+ uri.scheme.should == 'https'
20
+ uri.host.should == 'test.example.com'
21
+ uri.path.should == '/soap/Action'
22
+ end
23
+
24
+ it "initializes with the given parameters" do
25
+ @client.params[:reference].should == 'order-id'
26
+ end
27
+
28
+ it "merges the default parameters with the given ones" do
29
+ @client.params[:merchant_account].should == 'SuperShopper'
30
+ end
31
+
32
+ describe "call_webservice_action" do
33
+ before do
34
+ stub_net_http(AUTHORISE_RESPONSE)
35
+ @response = @client.call_webservice_action('Action', '<bananas>Yes, please</bananas>', Adyen::API::Response)
36
+ @request, @post = Net::HTTP.posted
37
+ end
38
+
39
+ after do
40
+ Net::HTTP.stubbing_enabled = false
41
+ end
42
+
43
+ it "posts to the class's endpoint" do
44
+ endpoint = APISpecHelper::SOAPClient.endpoint
45
+ @request.host.should == endpoint.host
46
+ @request.port.should == endpoint.port
47
+ @post.path.should == endpoint.path
48
+ end
49
+
50
+ it "makes a request over SSL" do
51
+ @request.use_ssl?.should be true
52
+ end
53
+
54
+ it "verifies certificates" do
55
+ File.should exist(Adyen::API::SimpleSOAPClient::CACERT)
56
+ @request.ca_file.should == Adyen::API::SimpleSOAPClient::CACERT
57
+ @request.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
58
+ end
59
+
60
+ it "uses basic-authentication with the credentials set on the Adyen::API module" do
61
+ username, password = @post.assigned_basic_auth
62
+ username.should == 'SuperShopper'
63
+ password.should == 'secret'
64
+ end
65
+
66
+ it "sends the proper headers" do
67
+ @post.header.should include(
68
+ 'accept' => ['text/xml'],
69
+ 'content-type' => ['text/xml; charset=utf-8'],
70
+ 'soapaction' => ['Action']
71
+ )
72
+ end
73
+
74
+ it "returns an Adyen::API::Response instance" do
75
+ @response.should be_instance_of(Adyen::API::Response)
76
+ @response.xml_querier.to_s.rstrip.should == AUTHORISE_RESPONSE.rstrip
77
+ end
78
+
79
+ [
80
+ [
81
+ "[401 Bad request] A client",
82
+ Net::HTTPBadRequest.new('1.1', '401', 'Bad request'),
83
+ Adyen::API::SimpleSOAPClient::ClientError
84
+ ]
85
+ ].each do |label, response, expected_exception|
86
+ it "raises when the HTTP response is a subclass of #{response.class.name}" do
87
+ response.stub(:body).and_return(%{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Illegal argument. For input string: "100.0"</faultstring></soap:Fault></soap:Body></soap:Envelope>})
88
+ Net::HTTP.stubbed_response = response
89
+
90
+ exception = nil
91
+ begin
92
+ @client.call_webservice_action('Action', '<bananas>Yes, please</bananas>', Adyen::API::Response)
93
+ rescue expected_exception => e
94
+ exception = e
95
+ end
96
+ exception.message.should == %{#{label} error occurred while calling SOAP action `Action' on endpoint `https://test.example.com/soap/Action'. Fault message: Illegal argument. For input string: "100.0".}
97
+ end
98
+ end
99
+
100
+ describe 'server error' do
101
+ [
102
+ ["[500 Internal Server Error] A server", Net::HTTPBadGateway.new('1.1', '500', 'Internal Server Error')],
103
+ ["[501 Not Implemented] A server", Net::HTTPBadGateway.new('1.1', '501', 'Not Implemented')],
104
+ ["[502 Bad Gateway] A server", Net::HTTPBadGateway.new('1.1', '502', 'Bad Gateway')],
105
+ ["[503 Service Unavailable] A server", Net::HTTPBadGateway.new('1.1', '503', 'Service Unavailable')],
106
+ ["[504 Gateway Timeout] A server", Net::HTTPBadGateway.new('1.1', '504', 'Gateway Timeout')],
107
+ ["[505 HTTP Version Not Supported] A server", Net::HTTPBadGateway.new('1.1', '505', 'HTTP Version Not Supported')],
108
+ ].each do |label, response|
109
+ it "is raised when the HTTP response is a `real` server error by status code" do
110
+ response.stub(:body).and_return(%{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body></soap:Body></soap:Envelope>})
111
+ Net::HTTP.stubbed_response = response
112
+
113
+ exception = nil
114
+ begin
115
+ @client.call_webservice_action('Action', '<bananas>Yes, please</bananas>', Adyen::API::Response)
116
+ rescue Adyen::API::SimpleSOAPClient::ServerError => e
117
+ exception = e
118
+ end
119
+ exception.message.should == %{#{label} error occurred while calling SOAP action `Action' on endpoint `https://test.example.com/soap/Action'.}
120
+ end
121
+ end
122
+
123
+ it "is not raised when the HTTP response has a 500 status code with a fault message" do
124
+ response = Net::HTTPServerError.new('1.1', '500', 'Internal Server Error')
125
+ response.stub(:body).and_return(%{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Illegal argument. For input string: "100.0"</faultstring></soap:Fault></soap:Body></soap:Envelope>})
126
+
127
+ lambda do
128
+ @client.call_webservice_action('Action', '<bananas>Yes, please</bananas>', Adyen::API::Response)
129
+ end.should_not raise_error
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,463 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ require 'adyen/api'
5
+
6
+ require 'nokogiri'
7
+ require 'rexml/document'
8
+
9
+ Adyen.configuration.default_api_params = { :merchant_account => 'SuperShopper' }
10
+ Adyen.configuration.api_username = 'SuperShopper'
11
+ Adyen.configuration.api_password = 'secret'
12
+
13
+ module Net
14
+ class HTTP
15
+ class Post
16
+ attr_reader :header
17
+ attr_reader :assigned_basic_auth
18
+
19
+ alias old_basic_auth basic_auth
20
+ def basic_auth(username, password)
21
+ if Net::HTTP.stubbing_enabled
22
+ @assigned_basic_auth = [username, password]
23
+ else
24
+ old_basic_auth(username, password)
25
+ end
26
+ end
27
+
28
+ def soap_action
29
+ header['soapaction'].first
30
+ end
31
+ end
32
+
33
+ class << self
34
+ attr_accessor :stubbing_enabled, :posted, :stubbed_response
35
+
36
+ def stubbing_enabled=(enabled)
37
+ reset! if @stubbing_enabled = enabled
38
+ end
39
+
40
+ def reset!
41
+ @posted = nil
42
+ @stubbed_response = nil
43
+ end
44
+ end
45
+
46
+ def host
47
+ @address
48
+ end
49
+
50
+ alias old_start start
51
+ def start(&block)
52
+ Net::HTTP.stubbing_enabled ? yield(self) : old_start(&block)
53
+ end
54
+
55
+ alias old_request request
56
+ def request(request)
57
+ if Net::HTTP.stubbing_enabled
58
+ self.class.posted = [self, request]
59
+ self.class.stubbed_response
60
+ else
61
+ old_request(request)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ module Adyen
68
+ module API
69
+ class PaymentService
70
+ public :authorise_payment_request_body,
71
+ :authorise_recurring_payment_request_body,
72
+ :authorise_one_click_payment_request_body,
73
+ :capture_request_body, :refund_request_body,
74
+ :cancel_or_refund_request_body,
75
+ :cancel_request_body
76
+ end
77
+
78
+ class RecurringService
79
+ public :list_request_body, :disable_request_body
80
+ end
81
+ end
82
+ end
83
+
84
+ module APISpecHelper
85
+ def node_for_current_object_and_method
86
+ Adyen::API::XMLQuerier.xml(@object.send(@method))
87
+ end
88
+
89
+ def xpath(query, &block)
90
+ node_for_current_method.xpath(query, &block)
91
+ end
92
+
93
+ def text(query)
94
+ node_for_current_method.text(query)
95
+ end
96
+
97
+ def stub_net_http(response_body)
98
+ Net::HTTP.stubbing_enabled = true
99
+ response = Net::HTTPOK.new('1.1', '200', 'OK')
100
+ response.stub(:body).and_return(response_body)
101
+ Net::HTTP.stubbed_response = response
102
+ end
103
+
104
+ def self.included(klass)
105
+ klass.extend ClassMethods
106
+ end
107
+
108
+ module ClassMethods
109
+ def for_each_xml_backend(&block)
110
+ backends = [Adyen::API::XMLQuerier::NokogiriBackend, Adyen::API::XMLQuerier::REXMLBackend]
111
+ backends.each do |xml_backend|
112
+ describe "with a #{xml_backend} backend" do
113
+ before { Adyen::API::XMLQuerier.stub(:default_backend => xml_backend.new) }
114
+ instance_eval(&block)
115
+ end
116
+ end
117
+ end
118
+
119
+ def it_should_have_shortcut_methods_for_params_on_the_response
120
+ it "provides shortcut methods, on the response object, for all entries in the #params hash" do
121
+ @response.params.each do |key, value|
122
+ @response.send(key).should == value
123
+ end
124
+ end
125
+ end
126
+
127
+ def it_should_return_params_for_each_xml_backend(params)
128
+ for_each_xml_backend do
129
+ it "returns a hash with parsed response details" do
130
+ @object.send(@method).params.should == params
131
+ end
132
+ end
133
+ end
134
+
135
+ def it_should_validate_request_parameters(*params)
136
+ params.each do |param|
137
+ case param
138
+ when Symbol
139
+ it_should_validate_request_param(param) { @object.params[param] = '' }
140
+ when Hash
141
+ param.each do |name, attrs|
142
+ it_should_validate_request_param(name) { @object.params[name] = nil }
143
+ attrs.each do |attr|
144
+ it_should_validate_request_param("#{name} => :#{attr}") { @object.params[name][attr] = nil }
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def it_should_validate_request_param(name, &block)
152
+ it "validates the `#{name}' request parameter" do
153
+ instance_eval &block
154
+ lambda { @object.send(@method) }.should raise_error(ArgumentError)
155
+ end
156
+ end
157
+
158
+ def describe_response_from(method, response, soap_action = 'authorise', &block)
159
+ describe(method) do
160
+ before do
161
+ stub_net_http(response)
162
+ @method = method
163
+ @object.params.merge!(:psp_reference => '9876543210987654')
164
+ @response = @object.send(@method)
165
+ @request, @post = Net::HTTP.posted
166
+ end
167
+
168
+ after do
169
+ Net::HTTP.stubbing_enabled = false
170
+ end
171
+
172
+ it "posts the body generated for the given parameters" do
173
+ @post.body.should == Adyen::API::SimpleSOAPClient::ENVELOPE % @object.send("#{@method}_request_body")
174
+ end
175
+
176
+ it "posts to the correct SOAP action" do
177
+ @post.soap_action.should == soap_action
178
+ end
179
+
180
+ it_should_have_shortcut_methods_for_params_on_the_response
181
+
182
+ instance_eval(&block)
183
+ end
184
+ end
185
+
186
+ def describe_request_body_of(method, xpath = nil, &block)
187
+ method = "#{method}_request_body"
188
+ describe(method) do
189
+ before { @method = method }
190
+ if xpath
191
+ define_method(:node_for_current_method) do
192
+ node_for_current_object_and_method.xpath(xpath)
193
+ end
194
+ end
195
+ instance_eval(&block)
196
+ end
197
+ end
198
+
199
+ def describe_modification_request_body_of(method, camelized_method = nil, &block)
200
+ describe_request_body_of method, "//payment:#{camelized_method || method}/payment:modificationRequest" do
201
+ before do
202
+ @payment.params[:psp_reference] = 'original-psp-reference'
203
+ end
204
+
205
+ it "includes the merchant account" do
206
+ text('./payment:merchantAccount').should == 'SuperShopper'
207
+ end
208
+
209
+ it "includes the payment (PSP) reference of the payment to refund" do
210
+ text('./payment:originalReference').should == 'original-psp-reference'
211
+ end
212
+
213
+ instance_eval(&block) if block_given?
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+ AUTHORISE_RESPONSE = <<EOS
220
+ <?xml version="1.0" encoding="UTF-8"?>
221
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
222
+ <soap:Body>
223
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
224
+ <ns1:paymentResult>
225
+ <additionalData xmlns="http://payment.services.adyen.com">
226
+ <entry>
227
+ <key xsi:type="xsd:string">cardSummary</key>
228
+ <value xsi:type="xsd:string">1111</value>
229
+ </entry>
230
+ </additionalData>
231
+ <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
232
+ <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
233
+ <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
234
+ <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
235
+ <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
236
+ <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
237
+ <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
238
+ <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
239
+ <refusalReason xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
240
+ <resultCode xmlns="http://payment.services.adyen.com">Authorised</resultCode>
241
+ </ns1:paymentResult>
242
+ </ns1:authoriseResponse>
243
+ </soap:Body>
244
+ </soap:Envelope>
245
+ EOS
246
+
247
+ AUTHORISATION_DECLINED_RESPONSE = <<EOS
248
+ <?xml version="1.0" encoding="UTF-8"?>
249
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
250
+ <soap:Body>
251
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
252
+ <ns1:paymentResult>
253
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
254
+ <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
255
+ <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
256
+ <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
257
+ <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
258
+ <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
259
+ <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
260
+ <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
261
+ <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
262
+ <refusalReason xmlns="http://payment.services.adyen.com">You need to actually own money.</refusalReason>
263
+ <resultCode xmlns="http://payment.services.adyen.com">Refused</resultCode>
264
+ </ns1:paymentResult>
265
+ </ns1:authoriseResponse>
266
+ </soap:Body>
267
+ </soap:Envelope>
268
+ EOS
269
+
270
+ AUTHORISE_REQUEST_INVALID_RESPONSE = <<EOS
271
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
272
+ <soap:Body>
273
+ <soap:Fault>
274
+ <faultcode>soap:Server</faultcode>
275
+ <faultstring>%s</faultstring>
276
+ </soap:Fault>
277
+ </soap:Body>
278
+ </soap:Envelope>
279
+ EOS
280
+
281
+ AUTHORISE_REQUEST_REFUSED_RESPONSE = <<EOS
282
+ <?xml version="1.0" encoding="UTF-8"?>
283
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
284
+ <soap:Body>
285
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
286
+ <ns1:paymentResult>
287
+ <refusalReason xmlns="http://payment.services.adyen.com">You need to actually own money.</refusalReason>
288
+ <resultCode xmlns="http://payment.services.adyen.com">Refused</resultCode>
289
+ </ns1:paymentResult>
290
+ </ns1:authoriseResponse>
291
+ </soap:Body>
292
+ </soap:Envelope>
293
+ EOS
294
+
295
+
296
+ LIST_RESPONSE = <<EOS
297
+ <?xml version="1.0"?>
298
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
299
+ <soap:Body>
300
+ <ns1:listRecurringDetailsResponse xmlns:ns1="http://recurring.services.adyen.com">
301
+ <ns1:result xmlns:ns2="http://payment.services.adyen.com">
302
+ <ns1:creationDate>2009-10-27T11:26:22.203+01:00</ns1:creationDate>
303
+ <details xmlns="http://recurring.services.adyen.com">
304
+ <RecurringDetail>
305
+ <bank xsi:nil="true"/>
306
+ <card>
307
+ <cvc xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
308
+ <expiryMonth xmlns="http://payment.services.adyen.com">12</expiryMonth>
309
+ <expiryYear xmlns="http://payment.services.adyen.com">2012</expiryYear>
310
+ <holderName xmlns="http://payment.services.adyen.com">S. Hopper</holderName>
311
+ <issueNumber xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
312
+ <number xmlns="http://payment.services.adyen.com">1111</number>
313
+ <startMonth xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
314
+ <startYear xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
315
+ </card>
316
+ <creationDate>2009-10-27T11:50:12.178+01:00</creationDate>
317
+ <elv xsi:nil="true"/>
318
+ <name/>
319
+ <recurringDetailReference>RecurringDetailReference1</recurringDetailReference>
320
+ <variant>mc</variant>
321
+ </RecurringDetail>
322
+ <RecurringDetail>
323
+ <bank>
324
+ <bankAccountNumber xmlns="http://payment.services.adyen.com">123456789</bankAccountNumber>
325
+ <bankLocationId xmlns="http://payment.services.adyen.com">bank-location-id</bankLocationId>
326
+ <bankName xmlns="http://payment.services.adyen.com">AnyBank</bankName>
327
+ <bic xmlns="http://payment.services.adyen.com">BBBBCCLLbbb</bic>
328
+ <countryCode xmlns="http://payment.services.adyen.com">NL</countryCode>
329
+ <iban xmlns="http://payment.services.adyen.com">NL69PSTB0001234567</iban>
330
+ <ownerName xmlns="http://payment.services.adyen.com">S. Hopper</ownerName>
331
+ </bank>
332
+ <card xsi:nil="true"/>
333
+ <creationDate>2009-10-27T11:26:22.216+01:00</creationDate>
334
+ <elv xsi:nil="true"/>
335
+ <name/>
336
+ <recurringDetailReference>RecurringDetailReference2</recurringDetailReference>
337
+ <variant>IDEAL</variant>
338
+ </RecurringDetail>
339
+ <RecurringDetail>
340
+ <card xsi:nil="true"/>
341
+ <bank xsi:nil="true"/>
342
+ <elv>
343
+ <accountHolderName xmlns="http://payment.services.adyen.com">S. Hopper</accountHolderName>
344
+ <bankAccountNumber xmlns="http://payment.services.adyen.com">1234567890</bankAccountNumber>
345
+ <bankLocation xmlns="http://payment.services.adyen.com">Berlin</bankLocation>
346
+ <bankLocationId xmlns="http://payment.services.adyen.com">12345678</bankLocationId>
347
+ <bankName xmlns="http://payment.services.adyen.com">TestBank</bankName>
348
+ </elv>
349
+ <creationDate>2009-10-27T11:26:22.216+01:00</creationDate>
350
+ <name/>
351
+ <recurringDetailReference>RecurringDetailReference3</recurringDetailReference>
352
+ <variant>elv</variant>
353
+ </RecurringDetail>
354
+ </details>
355
+ <ns1:lastKnownShopperEmail>s.hopper@example.com</ns1:lastKnownShopperEmail>
356
+ <ns1:shopperReference>user-id</ns1:shopperReference>
357
+ </ns1:result>
358
+ </ns1:listRecurringDetailsResponse>
359
+ </soap:Body>
360
+ </soap:Envelope>
361
+ EOS
362
+
363
+ LIST_EMPTY_RESPONSE = <<EOS
364
+ <?xml version="1.0"?>
365
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
366
+ <soap:Body>
367
+ <ns1:listRecurringDetailsResponse xmlns:ns1="http://recurring.services.adyen.com">
368
+ <ns1:result>
369
+ <details xmlns="http://recurring.services.adyen.com"/>
370
+ <lastKnownShopperEmail xmlns="http://recurring.services.adyen.com" xsi:nil="true"/>
371
+ <shopperReference xmlns="http://recurring.services.adyen.com" xsi:nil="true"/>
372
+ </ns1:result>
373
+ </ns1:listRecurringDetailsResponse>
374
+ </soap:Body>
375
+ </soap:Envelope>
376
+ EOS
377
+
378
+ DISABLE_RESPONSE = <<EOS
379
+ <?xml version="1.0"?>
380
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
381
+ <soap:Body>
382
+ <ns1:disableResponse xmlns:ns1="http://recurring.services.adyen.com">
383
+ <ns1:result>
384
+ <response xmlns="http://recurring.services.adyen.com">
385
+ %s
386
+ </response>
387
+ </ns1:result>
388
+ </ns1:disableResponse>
389
+ </soap:Body>
390
+ </soap:Envelope>
391
+ EOS
392
+
393
+ REFUND_RESPONSE = <<EOS
394
+ <?xml version="1.0"?>
395
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
396
+ <soap:Body>
397
+ <ns1:refundResponse xmlns:ns1="http://payment.services.adyen.com">
398
+ <ns1:refundResult>
399
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
400
+ <pspReference xmlns="http://payment.services.adyen.com">8512865475512126</pspReference>
401
+ <response xmlns="http://payment.services.adyen.com">%s</response>
402
+ </ns1:refundResult>
403
+ </ns1:refundResponse>
404
+ </soap:Body>
405
+ </soap:Envelope>
406
+ EOS
407
+
408
+ CANCEL_OR_REFUND_RESPONSE = <<EOS
409
+ <?xml version="1.0"?>
410
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
411
+ <soap:Body>
412
+ <ns1:cancelOrRefundResponse xmlns:ns1="http://payment.services.adyen.com">
413
+ <ns1:cancelOrRefundResult>
414
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
415
+ <pspReference xmlns="http://payment.services.adyen.com">8512865521218306</pspReference>
416
+ <response xmlns="http://payment.services.adyen.com">%s</response>
417
+ </ns1:cancelOrRefundResult>
418
+ </ns1:cancelOrRefundResponse>
419
+ </soap:Body>
420
+ </soap:Envelope>
421
+ EOS
422
+
423
+ CANCEL_RESPONSE = <<EOS
424
+ <?xml version="1.0"?>
425
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
426
+ <soap:Body>
427
+ <ns1:cancelResponse xmlns:ns1="http://payment.services.adyen.com">
428
+ <ns1:cancelResult>
429
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
430
+ <pspReference xmlns="http://payment.services.adyen.com">8612865544848013</pspReference>
431
+ <response xmlns="http://payment.services.adyen.com">%s</response>
432
+ </ns1:cancelResult>
433
+ </ns1:cancelResponse>
434
+ </soap:Body>
435
+ </soap:Envelope>
436
+ EOS
437
+
438
+ CAPTURE_RESPONSE = <<EOS
439
+ <?xml version="1.0"?>
440
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
441
+ <soap:Body>
442
+ <ns1:captureResponse xmlns:ns1="http://payment.services.adyen.com">
443
+ <ns1:captureResult>
444
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
445
+ <pspReference xmlns="http://payment.services.adyen.com">8512867956198946</pspReference>
446
+ <response xmlns="http://payment.services.adyen.com">%s</response>
447
+ </ns1:captureResult>
448
+ </ns1:captureResponse>
449
+ </soap:Body>
450
+ </soap:Envelope>
451
+ EOS
452
+
453
+ BILLET_RECEIVED_RESPONSE = <<EOS
454
+ <?xml version="1.0"?>
455
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
456
+ <soap:Body>
457
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com"><ns1:paymentResult><additionalData xmlns="http://payment.services.adyen.com"><entry><key xsi:type="xsd:string">boletobancario.url</key><value xsi:type="xsd:string">https://test.adyen.com/hpp/generationBoleto.shtml?data=AgABAQBdYDe9OqdseA79Rfexm2Lz8fRQ1bWqkLhBCf1fHhQEif7bsRKi0otq%2B1ekMAdMIZUiVXeR3QFrAOA8Zy4tpiNLhkMq6f7W2zFqYhVWrByqxQnbQTYuX2FWI7tsu7Vb0MnyOvFfFdFtaxzImZYCli%2BMrqaAJ5HI9ap3egeqBQIsRI%2Fj0zWsu2EGN16lGbwFOLyxl%2By0Pc5jazTo8rnBA7OVPGDIu7Qt%2F2DYIMcB6PXou5W3aJoTC4SldhNdobVqgWUtES8NsWdOYbLGa6I%2BjSwEFXvxyTXwtw4J2E%2BE7ux1UhBiZRj66lMbcvaYlfnR2xWbA%2BKmdLrVvuXTroEHKQ%2B1C%2FuyGuiOk3SmGq6TMgOyCEt%2BmG%2Bq6z5jDi%2BnYLtlLQU4ccMOujgWMfGkViC%2FXDUlqYjKbn8NHwPwoPcelpf1zCDCe%2Fvu6NBTVQbEXbE0oV0j2MT1tLlMdf08iUsDThuQ3MlJbE8VbTMlttOFqoyXhBjepQ42C1eXfswSz1gsZlHanBCTiw1pB69vkvfWPf5IdUSx1cpEr9LJ9PSz%2FeHxEhq%2B8ZdWzrybXqRbEl2mUjLeyhMNuiE%3D</value></entry><entry><key xsi:type="xsd:string">boletobancario.data</key><value xsi:type="xsd:string">AgABAQBdYDe9OqdseA79Rfexm2Lz8fRQ1bWqkLhBCf1fHhQEif7bsRKi0otq+1ekMAdMIZUiVXeR3QFrAOA8Zy4tpiNLhkMq6f7W2zFqYhVWrByqxQnbQTYuX2FWI7tsu7Vb0MnyOvFfFdFtaxzImZYCli+MrqaAJ5HI9ap3egeqBQIsRI/j0zWsu2EGN16lGbwFOLyxl+y0Pc5jazTo8rnBA7OVPGDIu7Qt/2DYIMcB6PXou5W3aJoTC4SldhNdobVqgWUtES8NsWdOYbLGa6I+jSwEFXvxyTXwtw4J2E+E7ux1UhBiZRj66lMbcvaYlfnR2xWbA+KmdLrVvuXTroEHKQ+1C/uyGuiOk3SmGq6TMgOyCEt+mG+q6z5jDi+nYLtlLQU4ccMOujgWMfGkViC/XDUlqYjKbn8NHwPwoPcelpf1zCDCe/vu6NBTVQbEXbE0oV0j2MT1tLlMdf08iUsDThuQ3MlJbE8VbTMlttOFqoyXhBjepQ42C1eXfswSz1gsZlHanBCTiw1pB69vkvfWPf5IdUSx1cpEr9LJ9PSz/eHxEhq+8ZdWzrybXqRbEl2mUjLeyhMNuiE=</value></entry><entry><key xsi:type="xsd:string">boletobancario.expirationDate</key><value xsi:type="xsd:string">2014-07-17</value></entry><entry><key xsi:type="xsd:string">boletobancario.dueDate</key><value xsi:type="xsd:string">2014-07-02</value></entry></additionalData><authCode xmlns="http://payment.services.adyen.com" xsi:nil="true" /><dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true" /><dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true" /><fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true" /><issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true" /><md xmlns="http://payment.services.adyen.com" xsi:nil="true" /><paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true" /><pspReference xmlns="http://payment.services.adyen.com">8814038837489129</pspReference><refusalReason xmlns="http://payment.services.adyen.com" xsi:nil="true" /><resultCode xmlns="http://payment.services.adyen.com">Received</resultCode></ns1:paymentResult></ns1:authoriseResponse></soap:Body></soap:Envelope>
458
+ EOS
459
+
460
+ BILLET_REFUSED_RESPONSE = <<EOS
461
+ <?xml version="1.0"?>
462
+ <soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><soap:Body><ns1:authoriseResponse xmlns:ns1='http://payment.services.adyen.com'><ns1:paymentResult><additionalData xmlns='http://payment.services.adyen.com' xsi:nil='true' /><authCode xmlns='http://payment.services.adyen.com' xsi:nil='true' /><dccAmount xmlns='http://payment.services.adyen.com' xsi:nil='true' /><dccSignature xmlns='http://payment.services.adyen.com' xsi:nil='true' /><fraudResult xmlns='http://payment.services.adyen.com' xsi:nil='true' /><issuerUrl xmlns='http://payment.services.adyen.com' xsi:nil='true' /><md xmlns='http://payment.services.adyen.com' xsi:nil='true' /><paRequest xmlns='http://payment.services.adyen.com' xsi:nil='true' /><pspReference xmlns='http://payment.services.adyen.com'>8514038928235061</pspReference><refusalReason xmlns='http://payment.services.adyen.com'>102 Unable to determine variant</refusalReason><resultCode xmlns='http://payment.services.adyen.com'>Refused</resultCode></ns1:paymentResult></ns1:authoriseResponse></soap:Body></soap:Envelope>
463
+ EOS