fingertips-adyen 0.3.7.20100917
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +40 -0
- data/Rakefile +5 -0
- data/adyen.gemspec +30 -0
- data/init.rb +1 -0
- data/lib/adyen.rb +77 -0
- data/lib/adyen/api.rb +343 -0
- data/lib/adyen/encoding.rb +21 -0
- data/lib/adyen/form.rb +336 -0
- data/lib/adyen/formatter.rb +37 -0
- data/lib/adyen/matchers.rb +105 -0
- data/lib/adyen/notification.rb +151 -0
- data/spec/adyen_spec.rb +86 -0
- data/spec/api_spec.rb +562 -0
- data/spec/form_spec.rb +152 -0
- data/spec/notification_spec.rb +97 -0
- data/spec/spec_helper.rb +12 -0
- data/tasks/github-gem.rake +371 -0
- metadata +132 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'stringio'
|
4
|
+
require 'zlib'
|
5
|
+
|
6
|
+
module Adyen
|
7
|
+
module Encoding
|
8
|
+
def self.hmac_base64(hmac_key, message)
|
9
|
+
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), hmac_key, message)
|
10
|
+
Base64.encode64(digest).strip
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.gzip_base64(message)
|
14
|
+
sio = StringIO.new
|
15
|
+
gz = Zlib::GzipWriter.new(sio)
|
16
|
+
gz.write(message)
|
17
|
+
gz.close
|
18
|
+
Base64.encode64(sio.string).gsub("\n", "")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/adyen/form.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
|
3
|
+
module Adyen
|
4
|
+
|
5
|
+
# The Adyen::Form module contains all functionality that is used to send payment requests
|
6
|
+
# to the Adyen payment system, using either a HTML form (see {Adyen::Form.hidden_fields})
|
7
|
+
# or a HTTP redirect (see {Adyen::Form.redirect_url}).
|
8
|
+
#
|
9
|
+
# Moreover, this module contains the method {Adyen::Form.redirect_signature_check} to
|
10
|
+
# check the request that is made to your website after the visitor has made his payment
|
11
|
+
# on the Adyen system for genuinity.
|
12
|
+
#
|
13
|
+
# You can use different skins in Adyen to define different payment environments. You can
|
14
|
+
# register these skins under a custom name in the module. The other methods will automatically
|
15
|
+
# use this information (i.e. the skin code and the shared secret) if it is available.
|
16
|
+
# Otherwise, you have to provide it yourself for every method call you make. See
|
17
|
+
# {Adyen::Form.register_skin} for more information.
|
18
|
+
#
|
19
|
+
# @see Adyen::Form.register_skin
|
20
|
+
# @see Adyen::Form.hidden_fields
|
21
|
+
# @see Adyen::Form.redirect_url
|
22
|
+
# @see Adyen::Form.redirect_signature_check
|
23
|
+
module Form
|
24
|
+
|
25
|
+
extend ActionView::Helpers::TagHelper
|
26
|
+
|
27
|
+
######################################################
|
28
|
+
# SKINS
|
29
|
+
######################################################
|
30
|
+
|
31
|
+
# Returns all registered skins and their accompanying skin code and shared secret.
|
32
|
+
# @return [Hash] The hash of registered skins.
|
33
|
+
def self.skins
|
34
|
+
@skins ||= {}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets the registered skins.
|
38
|
+
# @param [Hash<Symbol, Hash>] hash A hash with the skin name as key and the skin parameter hash
|
39
|
+
# (which should include +:skin_code+ and +:shared_secret+) as value.
|
40
|
+
# @see Adyen::Form.register_skin
|
41
|
+
def self.skins=(hash)
|
42
|
+
@skins = hash.inject({}) do |skins, (name, skin)|
|
43
|
+
skins[name.to_sym] = skin.merge(:name => name.to_sym)
|
44
|
+
skins
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Registers a skin for later use.
|
49
|
+
#
|
50
|
+
# You can store a skin using a self defined symbol. Once the skin is registered,
|
51
|
+
# you can refer to it using this symbol instead of the hard-to-remember skin code.
|
52
|
+
# Moreover, the skin's shared_secret will be looked up automatically for calculting
|
53
|
+
# signatures.
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# Adyen::Form.register_skin(:my_skin, 'dsfH67PO', 'Dfs*7uUln9')
|
57
|
+
# @param [Symbol] name The name of the skin.
|
58
|
+
# @param [String] skin_code The skin code for this skin, as defined by Adyen.
|
59
|
+
# @param [String] shared_secret The shared secret used for signature calculation.
|
60
|
+
# @see Adyen.load_config
|
61
|
+
def self.register_skin(name, skin_code, shared_secret)
|
62
|
+
self.skins[name.to_sym] = {:name => name.to_sym, :skin_code => skin_code, :shared_secret => shared_secret }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns skin information given a skin name.
|
66
|
+
# @param [Symbol] skin_name The name of the skin
|
67
|
+
# @return [Hash, nil] A hash with the skin information, or nil if not found.
|
68
|
+
def self.skin_by_name(skin_name)
|
69
|
+
self.skins[skin_name.to_sym]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns skin information given a skin code.
|
73
|
+
# @param [String] skin_code The skin code of the skin
|
74
|
+
# @return [Hash, nil] A hash with the skin information, or nil if not found.
|
75
|
+
def self.skin_by_code(skin_code)
|
76
|
+
self.skins.detect { |(name, skin)| skin[:skin_code] == skin_code }.last rescue nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the shared secret belonging to a skin code.
|
80
|
+
# @param [String] skin_code The skin code of the skin
|
81
|
+
# @return [String, nil] The shared secret for the skin, or nil if not found.
|
82
|
+
def self.lookup_shared_secret(skin_code)
|
83
|
+
skin = skin_by_code(skin_code)[:shared_secret] rescue nil
|
84
|
+
end
|
85
|
+
|
86
|
+
######################################################
|
87
|
+
# DEFAULT FORM / REDIRECT PARAMETERS
|
88
|
+
######################################################
|
89
|
+
|
90
|
+
# Returns the default parameters to use, unless they are overridden.
|
91
|
+
# @see Adyen::Form.default_parameters
|
92
|
+
# @return [Hash] The hash of default parameters
|
93
|
+
def self.default_parameters
|
94
|
+
@default_arguments ||= {}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Sets the default parameters to use.
|
98
|
+
# @see Adyen::Form.default_parameters
|
99
|
+
# @param [Hash] hash The hash of default parameters
|
100
|
+
def self.default_parameters=(hash)
|
101
|
+
@default_arguments = hash
|
102
|
+
end
|
103
|
+
|
104
|
+
######################################################
|
105
|
+
# ADYEN FORM URL
|
106
|
+
######################################################
|
107
|
+
|
108
|
+
# The URL of the Adyen payment system that still requires the current
|
109
|
+
# Adyen enviroment to be filled in.
|
110
|
+
ACTION_URL = "https://%s.adyen.com/hpp/select.shtml"
|
111
|
+
|
112
|
+
# Returns the URL of the Adyen payment system, adjusted for an Adyen environment.
|
113
|
+
#
|
114
|
+
# @param [String] environment The Adyen environment to use. This parameter can be
|
115
|
+
# left out, in which case the 'current' environment will be used.
|
116
|
+
# @return [String] The absolute URL of the Adyen payment system that can be used
|
117
|
+
# for payment forms or redirects.
|
118
|
+
# @see Adyen::Form.environment
|
119
|
+
# @see Adyen::Form.redirect_url
|
120
|
+
def self.url(environment = nil)
|
121
|
+
environment ||= Adyen.environment
|
122
|
+
Adyen::Form::ACTION_URL % environment.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
######################################################
|
126
|
+
# POSTING/REDIRECTING TO ADYEN
|
127
|
+
######################################################
|
128
|
+
|
129
|
+
# Transforms the payment parameters hash to be in the correct format.
|
130
|
+
# It will also include the default_parameters hash. Finally, switches
|
131
|
+
# the +:skin+ parameter out for the +:skin_code+ and +:shared_secret+
|
132
|
+
# parameter using the list of registered skins.
|
133
|
+
#
|
134
|
+
# @private
|
135
|
+
# @param [Hash] parameters The payment parameters hash to transform
|
136
|
+
def self.do_parameter_transformations!(parameters = {})
|
137
|
+
raise "YENs are not yet supported!" if parameters[:currency_code] == 'JPY' # TODO: fixme
|
138
|
+
|
139
|
+
parameters.replace(default_parameters.merge(parameters))
|
140
|
+
parameters[:recurring_contract] = 'RECURRING' if parameters.delete(:recurring) == true
|
141
|
+
parameters[:order_data] = Adyen::Encoding.gzip_base64(parameters.delete(:order_data_raw)) if parameters[:order_data_raw]
|
142
|
+
parameters[:ship_before_date] = Adyen::Formatter::DateTime.fmt_date(parameters[:ship_before_date])
|
143
|
+
parameters[:session_validity] = Adyen::Formatter::DateTime.fmt_time(parameters[:session_validity])
|
144
|
+
|
145
|
+
if parameters[:skin]
|
146
|
+
skin = Adyen::Form.skin_by_name(parameters.delete(:skin))
|
147
|
+
parameters[:skin_code] ||= skin[:skin_code]
|
148
|
+
parameters[:shared_secret] ||= skin[:shared_secret]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Transforms the payment parameters to be in the correct format and calculates the merchant
|
153
|
+
# signature parameter. It also does some basic health checks on the parameters hash.
|
154
|
+
#
|
155
|
+
# @param [Hash] parameters The payment parameters. The parameters set in the
|
156
|
+
# {Adyen::Form.default_parameters} hash will be included automatically.
|
157
|
+
# @param [String] shared_secret The shared secret that should be used to calculate
|
158
|
+
# the payment request signature. This parameter can be left if the skin that is
|
159
|
+
# used is registered (see {Adyen::Form.register_skin}), or if the shared secret
|
160
|
+
# is provided as the +:shared_secret+ parameter.
|
161
|
+
# @return [Hash] The payment parameters with the +:merchant_signature+ parameter set.
|
162
|
+
# @raise [StandardError] Thrown if some parameter health check fails.
|
163
|
+
def self.payment_parameters(parameters = {}, shared_secret = nil)
|
164
|
+
do_parameter_transformations!(parameters)
|
165
|
+
|
166
|
+
raise "Cannot generate form: :currency code attribute not found!" unless parameters[:currency_code]
|
167
|
+
raise "Cannot generate form: :payment_amount code attribute not found!" unless parameters[:payment_amount]
|
168
|
+
raise "Cannot generate form: :merchant_account attribute not found!" unless parameters[:merchant_account]
|
169
|
+
raise "Cannot generate form: :skin_code attribute not found!" unless parameters[:skin_code]
|
170
|
+
|
171
|
+
# Calculate the merchant signature using the shared secret.
|
172
|
+
shared_secret ||= parameters.delete(:shared_secret)
|
173
|
+
raise "Cannot calculate payment request signature without shared secret!" unless shared_secret
|
174
|
+
parameters[:merchant_sig] = calculate_signature(parameters, shared_secret)
|
175
|
+
|
176
|
+
return parameters
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns an absolute URL to the Adyen payment system, with the payment parameters included
|
180
|
+
# as GET parameters in the URL. The URL also depends on the current Adyen enviroment.
|
181
|
+
#
|
182
|
+
# The payment parameters that are provided to this method will be merged with the
|
183
|
+
# {Adyen::Form.default_parameters} hash. The default parameter values will be overrided
|
184
|
+
# if another value is provided to this method.
|
185
|
+
#
|
186
|
+
# You do not have to provide the +:merchant_sig+ parameter: it will be calculated automatically
|
187
|
+
# if you provide either a registered skin name as the +:skin+ parameter or provide both the
|
188
|
+
# +:skin_code+ and +:shared_secret+ parameters.
|
189
|
+
#
|
190
|
+
# Note that Internet Explorer has a maximum length for URLs it can handle (2083 characters).
|
191
|
+
# Make sure that the URL is not longer than this limit if you want your site to work in IE.
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
#
|
195
|
+
# def pay
|
196
|
+
# # Genarate a URL to redirect to Adyen's payment system.
|
197
|
+
# adyen_url = Adyen::Form.redirect_url(:skin => :my_skin, :currency_code => 'USD',
|
198
|
+
# :payment_amount => 1000, merchant_account => 'MyMerchant', ... )
|
199
|
+
#
|
200
|
+
# respond_to do |format|
|
201
|
+
# format.html { redirect_to(adyen_url) }
|
202
|
+
# end
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
# @param [Hash] parameters The payment parameters to include in the payment request.
|
206
|
+
# @return [String] An absolute URL to redirect to the Adyen payment system.
|
207
|
+
def self.redirect_url(parameters = {})
|
208
|
+
self.url + '?' + payment_parameters(parameters).map { |(k, v)|
|
209
|
+
"#{k.to_s.camelize(:lower)}=#{CGI.escape(v.to_s)}" }.join('&')
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns a HTML snippet of hidden INPUT tags with the provided payment parameters.
|
213
|
+
# The snippet can be included in a payment form that POSTs to the Adyen payment system.
|
214
|
+
#
|
215
|
+
# The payment parameters that are provided to this method will be merged with the
|
216
|
+
# {Adyen::Form.default_parameters} hash. The default parameter values will be overrided
|
217
|
+
# if another value is provided to this method.
|
218
|
+
#
|
219
|
+
# You do not have to provide the +:merchant_sig+ parameter: it will be calculated automatically
|
220
|
+
# if you provide either a registered skin name as the +:skin+ parameter or provide both the
|
221
|
+
# +:skin_code+ and +:shared_secret+ parameters.
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# <% form_tag(Adyen::Form.url) do %>
|
225
|
+
# <%= Adyen::Form.hidden_fields(:skin => :my_skin, :currency_code => 'USD',
|
226
|
+
# :payment_amount => 1000, ...) %>
|
227
|
+
# <%= submit_tag("Pay invoice")
|
228
|
+
# <% end %>
|
229
|
+
#
|
230
|
+
# @param [Hash] parameters The payment parameters to include in the payment request.
|
231
|
+
# @return [String] An HTML snippet that can be included in a form that POSTs to the
|
232
|
+
# Adyen payment system.
|
233
|
+
def self.hidden_fields(parameters = {})
|
234
|
+
|
235
|
+
# Generate a hidden input tag per parameter, join them by newlines.
|
236
|
+
payment_parameters(parameters).map { |key, value|
|
237
|
+
self.tag(:input, :type => 'hidden', :name => key.to_s.camelize(:lower), :value => value)
|
238
|
+
}.join("\n")
|
239
|
+
end
|
240
|
+
|
241
|
+
######################################################
|
242
|
+
# MERCHANT SIGNATURE CALCULATION
|
243
|
+
######################################################
|
244
|
+
|
245
|
+
# Generates the string that is used to calculate the request signature. This signature
|
246
|
+
# is used by Adyen to check whether the request is genuinely originating from you.
|
247
|
+
# @param [Hash] parameters The parameters that will be included in the payment request.
|
248
|
+
# @return [String] The string for which the siganture is calculated.
|
249
|
+
def self.calculate_signature_string(parameters)
|
250
|
+
merchant_sig_string = ""
|
251
|
+
merchant_sig_string << parameters[:payment_amount].to_s << parameters[:currency_code].to_s <<
|
252
|
+
parameters[:ship_before_date].to_s << parameters[:merchant_reference].to_s <<
|
253
|
+
parameters[:skin_code].to_s << parameters[:merchant_account].to_s <<
|
254
|
+
parameters[:session_validity].to_s << parameters[:shopper_email].to_s <<
|
255
|
+
parameters[:shopper_reference].to_s << parameters[:recurring_contract].to_s <<
|
256
|
+
parameters[:allowed_methods].to_s << parameters[:blocked_methods].to_s <<
|
257
|
+
parameters[:shopper_statement].to_s << parameters[:billing_address_type].to_s
|
258
|
+
end
|
259
|
+
|
260
|
+
# Calculates the payment request signature for the given payment parameters.
|
261
|
+
#
|
262
|
+
# This signature is used by Adyen to check whether the request is
|
263
|
+
# genuinely originating from you. The resulting signature should be
|
264
|
+
# included in the payment request parameters as the +merchantSig+
|
265
|
+
# parameter; the shared secret should of course not be included.
|
266
|
+
#
|
267
|
+
# @param [Hash] parameters The payment parameters for which to calculate
|
268
|
+
# the payment request signature.
|
269
|
+
# @param [String] shared_secret The shared secret to use for this signature.
|
270
|
+
# It should correspond with the skin_code parameter. This parameter can be
|
271
|
+
# left out if the shared_secret is included as key in the parameters.
|
272
|
+
# @return [String] The signature of the payment request
|
273
|
+
def self.calculate_signature(parameters, shared_secret = nil)
|
274
|
+
shared_secret ||= parameters.delete(:shared_secret)
|
275
|
+
Adyen::Encoding.hmac_base64(shared_secret, calculate_signature_string(parameters))
|
276
|
+
end
|
277
|
+
|
278
|
+
######################################################
|
279
|
+
# REDIRECT SIGNATURE CHECKING
|
280
|
+
######################################################
|
281
|
+
|
282
|
+
# Generates the string for which the redirect signature is calculated, using the request paramaters.
|
283
|
+
# @param [Hash] params A hash of HTTP GET parameters for the redirect request.
|
284
|
+
# @return [String] The signature string.
|
285
|
+
def self.redirect_signature_string(params)
|
286
|
+
params[:authResult].to_s + params[:pspReference].to_s + params[:merchantReference].to_s + params[:skinCode].to_s
|
287
|
+
end
|
288
|
+
|
289
|
+
# Computes the redirect signature using the request parameters, so that the
|
290
|
+
# redirect can be checked for forgery.
|
291
|
+
#
|
292
|
+
# @param [Hash] params A hash of HTTP GET parameters for the redirect request.
|
293
|
+
# @param [String] shared_secret The shared secret for the Adyen skin that was used for
|
294
|
+
# the original payment form. You can leave this out of the skin is registered
|
295
|
+
# using the {Adyen::Form.register_skin} method.
|
296
|
+
# @return [String] The redirect signature
|
297
|
+
def self.redirect_signature(params, shared_secret = nil)
|
298
|
+
shared_secret ||= lookup_shared_secret(params[:skinCode])
|
299
|
+
Adyen::Encoding.hmac_base64(shared_secret, redirect_signature_string(params))
|
300
|
+
end
|
301
|
+
|
302
|
+
# Checks the redirect signature for this request by calcultating the signature from
|
303
|
+
# the provided parameters, and comparing it to the signature provided in the +merchantSig+
|
304
|
+
# parameter.
|
305
|
+
#
|
306
|
+
# If this method returns false, the request could be a forgery and should not be handled.
|
307
|
+
# Therefore, you should include this check in a +before_filter+, and raise an error of the
|
308
|
+
# signature check fails.
|
309
|
+
#
|
310
|
+
# @example
|
311
|
+
# class PaymentsController < ApplicationController
|
312
|
+
# before_filter :check_signature, :only => [:return_from_adyen]
|
313
|
+
#
|
314
|
+
# def return_from_adyen
|
315
|
+
# @invoice = Invoice.find(params[:merchantReference])
|
316
|
+
# @invoice.set_paid! if params[:authResult] == 'AUTHORISED'
|
317
|
+
# end
|
318
|
+
#
|
319
|
+
# private
|
320
|
+
#
|
321
|
+
# def check_signature
|
322
|
+
# raise "Forgery!" unless Adyen::Form.redirect_signature_check(params)
|
323
|
+
# end
|
324
|
+
# end
|
325
|
+
#
|
326
|
+
# @param [Hash] params params A hash of HTTP GET parameters for the redirect request. This
|
327
|
+
# should include the +:merchantSig+ parameter, which contains the signature.
|
328
|
+
# @param [String] shared_secret The shared secret for the Adyen skin that was used for
|
329
|
+
# the original payment form. You can leave this out of the skin is registered
|
330
|
+
# using the {Adyen::Form.register_skin} method.
|
331
|
+
# @return [true, false] Returns true only if the signature in the parameters is correct.
|
332
|
+
def self.redirect_signature_check(params, shared_secret = nil)
|
333
|
+
params[:merchantSig] == redirect_signature(params, shared_secret)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Adyen
|
2
|
+
module Formatter
|
3
|
+
module DateTime
|
4
|
+
# Returns a valid Adyen string representation for a date
|
5
|
+
def self.fmt_date(date)
|
6
|
+
case date
|
7
|
+
when Date, DateTime, Time
|
8
|
+
date.strftime('%Y-%m-%d')
|
9
|
+
else
|
10
|
+
raise "Invalid date notation: #{date.inspect}!" unless /^\d{4}-\d{2}-\d{2}$/ =~ date
|
11
|
+
date
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a valid Adyen string representation for a timestamp
|
16
|
+
def self.fmt_time(time)
|
17
|
+
case time
|
18
|
+
when Date, DateTime, Time
|
19
|
+
time.strftime('%Y-%m-%dT%H:%M:%SZ')
|
20
|
+
else
|
21
|
+
raise "Invalid timestamp notation: #{time.inspect}!" unless /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z$/ =~ time
|
22
|
+
time
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Price
|
28
|
+
def self.in_cents(price)
|
29
|
+
((price * 100).round).to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.from_cents(price)
|
33
|
+
BigDecimal.new(price.to_s) / 100
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'xml'
|
2
|
+
|
3
|
+
module Adyen
|
4
|
+
module Matchers
|
5
|
+
|
6
|
+
module XPathPaymentFormCheck
|
7
|
+
|
8
|
+
def self.build_xpath_query(checks)
|
9
|
+
# Start by finding the check for the Adyen form tag
|
10
|
+
xpath_query = "//form[@action='#{Adyen::Form.url}']"
|
11
|
+
|
12
|
+
# Add recurring/single check if specified
|
13
|
+
recurring = checks.delete(:recurring)
|
14
|
+
unless recurring.nil?
|
15
|
+
if recurring
|
16
|
+
xpath_query << "[descendant::input[@type='hidden'][@name='recurringContract']]"
|
17
|
+
else
|
18
|
+
xpath_query << "[not(descendant::input[@type='hidden'][@name='recurringContract'])]"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a check for all the other fields specified
|
23
|
+
checks.each do |key, value|
|
24
|
+
condition = "descendant::input[@type='hidden'][@name='#{key.to_s.camelize(:lower)}']"
|
25
|
+
condition << "[@value='#{value}']" unless value == :anything
|
26
|
+
xpath_query << "[#{condition}]"
|
27
|
+
end
|
28
|
+
|
29
|
+
return xpath_query
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.document(subject)
|
33
|
+
if String === subject
|
34
|
+
XML::HTMLParser.string(subject).parse
|
35
|
+
elsif subject.respond_to?(:body)
|
36
|
+
XML::HTMLParser.string(subject.body).parse
|
37
|
+
elsif XML::Node === subject
|
38
|
+
subject
|
39
|
+
elsif XML::Document === subject
|
40
|
+
subject
|
41
|
+
else
|
42
|
+
raise "Cannot handle this XML input type"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.check(subject, checks = {})
|
47
|
+
document(subject).find_first(build_xpath_query(checks))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class HaveAdyenPaymentForm
|
52
|
+
|
53
|
+
def initialize(checks)
|
54
|
+
@checks = checks
|
55
|
+
end
|
56
|
+
|
57
|
+
def matches?(document)
|
58
|
+
Adyen::Matchers::XPathPaymentFormCheck.check(document, @checks)
|
59
|
+
end
|
60
|
+
|
61
|
+
def description
|
62
|
+
"have an adyen payment form"
|
63
|
+
end
|
64
|
+
|
65
|
+
def failure_message
|
66
|
+
"expected to find a valid Adyen form on this page"
|
67
|
+
end
|
68
|
+
|
69
|
+
def negative_failure_message
|
70
|
+
"expected not to find a valid Adyen form on this page"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def have_adyen_payment_form(checks = {})
|
75
|
+
default_checks = {:merchant_sig => :anything, :payment_amount => :anything, :currency_code => :anything, :skin_code => :anything }
|
76
|
+
HaveAdyenPaymentForm.new(default_checks.merge(checks))
|
77
|
+
end
|
78
|
+
|
79
|
+
def have_adyen_recurring_payment_form(checks = {})
|
80
|
+
recurring_checks = { :recurring => true, :shopper_email => :anything, :shopper_reference => :anything }
|
81
|
+
have_adyen_payment_form(recurring_checks.merge(checks))
|
82
|
+
end
|
83
|
+
|
84
|
+
def have_adyen_single_payment_form(checks = {})
|
85
|
+
recurring_checks = { :recurring => false }
|
86
|
+
have_adyen_payment_form(recurring_checks.merge(checks))
|
87
|
+
end
|
88
|
+
|
89
|
+
def assert_adyen_payment_form(subject, checks = {})
|
90
|
+
default_checks = {:merchant_sig => :anything, :payment_amount => :anything, :currency_code => :anything, :skin_code => :anything }
|
91
|
+
assert Adyen::Matchers::XPathPaymentFormCheck.check(subject, default_checks.merge(checks)), 'No Adyen payment form found'
|
92
|
+
end
|
93
|
+
|
94
|
+
def assert_adyen_recurring_payment_form(subject, checks = {})
|
95
|
+
recurring_checks = { :recurring => true, :shopper_email => :anything, :shopper_reference => :anything }
|
96
|
+
assert_adyen_payment_form(subject, recurring_checks.merge(checks))
|
97
|
+
end
|
98
|
+
|
99
|
+
def assert_adyen_single_payment_form(subject, checks = {})
|
100
|
+
recurring_checks = { :recurring => false }
|
101
|
+
assert_adyen_payment_form(subject, recurring_checks.merge(checks))
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|