adyen 0.2.3 → 0.3.0
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.
- data/.gitignore +1 -0
- data/README.rdoc +2 -1
- data/adyen.gemspec +2 -2
- data/lib/adyen.rb +31 -13
- data/lib/adyen/form.rb +213 -22
- data/lib/adyen/notification.rb +54 -2
- data/lib/adyen/soap.rb +23 -8
- metadata +2 -2
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -26,7 +26,8 @@ You can also install it as a Rails plugin (*deprecated*):
|
|
26
26
|
|
27
27
|
== Usage
|
28
28
|
|
29
|
-
See the project wiki on http://wiki.github.com/wvanbergen/adyen to get started.
|
29
|
+
See the project wiki on http://wiki.github.com/wvanbergen/adyen to get started. Complete
|
30
|
+
RDoc documentation for the project can be found on http://rdoc.info/projects/wvanbergen/adyen.
|
30
31
|
|
31
32
|
* For more information about Adyen, see http://www.adyen.com
|
32
33
|
* For more information about integrating Adyen, see their manuals at
|
data/adyen.gemspec
CHANGED
data/lib/adyen.rb
CHANGED
@@ -1,20 +1,28 @@
|
|
1
|
+
# The Adyen module is the container module for all Adyen related functionality,
|
2
|
+
# which is implemented in submodules. This module only contains some global
|
3
|
+
# configuration methods.
|
4
|
+
#
|
5
|
+
# The most important submodules are:
|
6
|
+
# * {Adyen::Form} for generating payment form fields, generating redirect URLs
|
7
|
+
# to the Adyen payment system, and generating and checking of signatures.
|
8
|
+
# * {Adyen::Notification} for handling notifications sent by Adyen to your servers.
|
9
|
+
# * {Adyen::SOAP} for communicating with the Adyen SOAP services for payment
|
10
|
+
# maintenance and issuing recurring payments.
|
1
11
|
module Adyen
|
2
12
|
|
3
13
|
# Loads configuration settings from a Hash.
|
4
14
|
#
|
5
|
-
#
|
6
|
-
#
|
15
|
+
# @param [Hash] hash The (nested Hash) with configuration variables.
|
16
|
+
# @param [Module] mod The current working module. This parameter is used
|
17
|
+
# to recursively traverse the hash for submodules.
|
18
|
+
# @raise [StandardError] An exception is raised of an unkown configuration
|
19
|
+
# setting is encountered in the hash.
|
7
20
|
def self.load_config(hash, mod = Adyen)
|
8
21
|
hash.each do |key, value|
|
9
22
|
if key.to_s =~ /^[a-z]/ && mod.respond_to?(:"#{key}=")
|
10
23
|
mod.send(:"#{key}=", value)
|
11
24
|
elsif key.to_s =~ /^[A-Z]/
|
12
|
-
|
13
|
-
submodule = mod.const_get(key)
|
14
|
-
rescue LoadError => e
|
15
|
-
raise "Unknown Adyen module to configure: #{mod.name}::#{key}"
|
16
|
-
end
|
17
|
-
self.load_config(value, submodule)
|
25
|
+
self.load_config(value, mod.const_get(key))
|
18
26
|
else
|
19
27
|
raise "Unknown configuration variable: '#{key}' for #{mod}"
|
20
28
|
end
|
@@ -25,27 +33,37 @@ module Adyen
|
|
25
33
|
LIVE_RAILS_ENVIRONMENTS = ['production']
|
26
34
|
|
27
35
|
# Setter voor the current Adyen environment.
|
28
|
-
#
|
36
|
+
# @param ['test', 'live'] env The Adyen environment to use
|
29
37
|
def self.environment=(env)
|
30
38
|
@environment = env
|
31
39
|
end
|
32
40
|
|
33
|
-
# Returns the current Adyen environment.
|
34
|
-
#
|
41
|
+
# Returns the current Adyen environment, either test or live.
|
42
|
+
#
|
43
|
+
# It will return the +override+ value if set, it will return the value set
|
44
|
+
# using {Adyen.environment=} otherwise. If this value also isn't set, the
|
45
|
+
# environemtn is determined with {Adyen.autodetect_environment}.
|
46
|
+
#
|
47
|
+
# @param ['test', 'live'] override An environemt to override the default with.
|
48
|
+
# @return ['test', 'live'] The Adyen environment that is currently being used.
|
35
49
|
def self.environment(override = nil)
|
36
50
|
override || @environment || Adyen.autodetect_environment
|
37
51
|
end
|
38
52
|
|
39
|
-
# Autodetects the Adyen environment based on the RAILS_ENV constant
|
53
|
+
# Autodetects the Adyen environment based on the RAILS_ENV constant.
|
54
|
+
# @return ['test', 'live'] The Adyen environment that corresponds to the Rails environment
|
40
55
|
def self.autodetect_environment
|
41
56
|
(defined?(RAILS_ENV) && Adyen::LIVE_RAILS_ENVIRONMENTS.include?(RAILS_ENV.to_s.downcase)) ? 'live' : 'test'
|
42
57
|
end
|
43
58
|
|
44
59
|
# Loads submodules on demand, so that dependencies are not required.
|
60
|
+
# @param [Symbol] sym The name of the submodule
|
61
|
+
# @return [Module] The actual loaded submodule.
|
62
|
+
# @raise [LoadError, NameError] If the submodule cannot be loaded
|
45
63
|
def self.const_missing(sym)
|
46
64
|
require "adyen/#{sym.to_s.downcase}"
|
47
65
|
return Adyen.const_get(sym)
|
48
|
-
rescue Exception
|
66
|
+
rescue Exception
|
49
67
|
super(sym)
|
50
68
|
end
|
51
69
|
end
|
data/lib/adyen/form.rb
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
require 'action_view'
|
2
2
|
|
3
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 a 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
|
4
23
|
module Form
|
5
24
|
|
6
25
|
extend ActionView::Helpers::TagHelper
|
@@ -9,41 +28,75 @@ module Adyen
|
|
9
28
|
# SKINS
|
10
29
|
######################################################
|
11
30
|
|
31
|
+
# Returns all registered skins and their accompanying skin code and shared secret.
|
32
|
+
# @return [Hash] The hash of registered skins.
|
12
33
|
def self.skins
|
13
34
|
@skins ||= {}
|
14
35
|
end
|
15
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
|
16
41
|
def self.skins=(hash)
|
17
42
|
@skins = hash.inject({}) do |skins, (name, skin)|
|
18
43
|
skins[name.to_sym] = skin.merge(:name => name.to_sym)
|
19
44
|
skins
|
20
45
|
end
|
21
46
|
end
|
22
|
-
|
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
|
23
61
|
def self.register_skin(name, skin_code, shared_secret)
|
24
62
|
self.skins[name.to_sym] = {:name => name.to_sym, :skin_code => skin_code, :shared_secret => shared_secret }
|
25
63
|
end
|
26
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.
|
27
68
|
def self.skin_by_name(skin_name)
|
28
69
|
self.skins[skin_name.to_sym]
|
29
70
|
end
|
30
|
-
|
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.
|
31
75
|
def self.skin_by_code(skin_code)
|
32
|
-
self.skins.detect { |(name, skin)| skin[:skin_code] == skin_code }.last rescue nil
|
76
|
+
self.skins.detect { |(name, skin)| skin[:skin_code] == skin_code }.last rescue nil
|
33
77
|
end
|
34
|
-
|
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.
|
35
82
|
def self.lookup_shared_secret(skin_code)
|
36
83
|
skin = skin_by_code(skin_code)[:shared_secret] rescue nil
|
37
|
-
end
|
38
|
-
|
84
|
+
end
|
85
|
+
|
39
86
|
######################################################
|
40
87
|
# DEFAULT FORM / REDIRECT PARAMETERS
|
41
|
-
######################################################
|
88
|
+
######################################################
|
42
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
|
43
93
|
def self.default_parameters
|
44
94
|
@default_arguments ||= {}
|
45
95
|
end
|
46
96
|
|
97
|
+
# Sets the default parameters to use.
|
98
|
+
# @see Adyen::Form.default_parameters
|
99
|
+
# @param [Hash] hash The hash of default parameters
|
47
100
|
def self.default_parameters=(hash)
|
48
101
|
@default_arguments = hash
|
49
102
|
end
|
@@ -52,18 +105,34 @@ module Adyen
|
|
52
105
|
# ADYEN FORM URL
|
53
106
|
######################################################
|
54
107
|
|
108
|
+
# The URL of the Adyen payment system that still requires the current
|
109
|
+
# Adyen enviroment to be filled in.
|
55
110
|
ACTION_URL = "https://%s.adyen.com/hpp/select.shtml"
|
56
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
|
57
120
|
def self.url(environment = nil)
|
58
|
-
environment ||= Adyen.environment
|
121
|
+
environment ||= Adyen.environment
|
59
122
|
Adyen::Form::ACTION_URL % environment.to_s
|
60
123
|
end
|
61
124
|
|
62
|
-
|
63
125
|
######################################################
|
64
126
|
# POSTING/REDIRECTING TO ADYEN
|
65
127
|
######################################################
|
66
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
|
67
136
|
def self.do_parameter_transformations!(parameters = {})
|
68
137
|
raise "YENs are not yet supported!" if parameters[:currency_code] == 'JPY' # TODO: fixme
|
69
138
|
|
@@ -80,26 +149,90 @@ module Adyen
|
|
80
149
|
end
|
81
150
|
end
|
82
151
|
|
83
|
-
|
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)
|
84
164
|
do_parameter_transformations!(parameters)
|
85
165
|
|
86
166
|
raise "Cannot generate form: :currency code attribute not found!" unless parameters[:currency_code]
|
87
167
|
raise "Cannot generate form: :payment_amount code attribute not found!" unless parameters[:payment_amount]
|
88
168
|
raise "Cannot generate form: :merchant_account attribute not found!" unless parameters[:merchant_account]
|
89
169
|
raise "Cannot generate form: :skin_code attribute not found!" unless parameters[:skin_code]
|
90
|
-
raise "Cannot generate form: :shared_secret signing secret not provided!" unless parameters[:shared_secret]
|
91
170
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
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
|
95
177
|
end
|
96
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.
|
97
207
|
def self.redirect_url(parameters = {})
|
98
|
-
self.url + '?' + payment_parameters(parameters).map { |(k, v)|
|
208
|
+
self.url + '?' + payment_parameters(parameters).map { |(k, v)|
|
209
|
+
"#{k.to_s.camelize(:lower)}=#{CGI.escape(v.to_s)}" }.join('&')
|
99
210
|
end
|
100
|
-
|
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.
|
101
233
|
def self.hidden_fields(parameters = {})
|
102
|
-
|
234
|
+
|
235
|
+
# Generate a hidden input tag per parameter, join them by newlines.
|
103
236
|
payment_parameters(parameters).map { |key, value|
|
104
237
|
self.tag(:input, :type => 'hidden', :name => key.to_s.camelize(:lower), :value => value)
|
105
238
|
}.join("\n")
|
@@ -109,6 +242,10 @@ module Adyen
|
|
109
242
|
# MERCHANT SIGNATURE CALCULATION
|
110
243
|
######################################################
|
111
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.
|
112
249
|
def self.calculate_signature_string(parameters)
|
113
250
|
merchant_sig_string = ""
|
114
251
|
merchant_sig_string << parameters[:payment_amount].to_s << parameters[:currency_code].to_s <<
|
@@ -120,26 +257,80 @@ module Adyen
|
|
120
257
|
parameters[:shopper_statement].to_s << parameters[:billing_address_type].to_s
|
121
258
|
end
|
122
259
|
|
123
|
-
|
124
|
-
|
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))
|
125
276
|
end
|
126
277
|
|
127
278
|
######################################################
|
128
279
|
# REDIRECT SIGNATURE CHECKING
|
129
280
|
######################################################
|
130
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.
|
131
285
|
def self.redirect_signature_string(params)
|
132
286
|
params[:authResult].to_s + params[:pspReference].to_s + params[:merchantReference].to_s + params[:skinCode].to_s
|
133
287
|
end
|
134
|
-
|
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
|
135
297
|
def self.redirect_signature(params, shared_secret = nil)
|
136
298
|
shared_secret ||= lookup_shared_secret(params[:skinCode])
|
137
299
|
Adyen::Encoding.hmac_base64(shared_secret, redirect_signature_string(params))
|
138
300
|
end
|
139
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.
|
140
332
|
def self.redirect_signature_check(params, shared_secret = nil)
|
141
333
|
params[:merchantSig] == redirect_signature(params, shared_secret)
|
142
334
|
end
|
143
|
-
|
144
335
|
end
|
145
|
-
end
|
336
|
+
end
|
data/lib/adyen/notification.rb
CHANGED
@@ -1,18 +1,51 @@
|
|
1
1
|
require 'activerecord'
|
2
2
|
|
3
3
|
module Adyen
|
4
|
+
|
5
|
+
# The +Adyen::Notification+ class handles notifications sent by Adyen to your servers.
|
6
|
+
#
|
7
|
+
# Because notifications contain important payment status information, you should store
|
8
|
+
# these notifications in your database. For this reason, +Adyen::Notification+ inherits
|
9
|
+
# from +ActiveRecord::Base+, and a migration is included to simply create a suitable table
|
10
|
+
# to store the notifications in.
|
11
|
+
#
|
12
|
+
# Adyen can either send notifications to you via HTTP POST requests, or SOAP requests.
|
13
|
+
# Because SOAP is not really well supported in Rails and setting up a SOAP server is
|
14
|
+
# not trivial, only handling HTTP POST notifications is currently supported.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# @notification = Adyen::Notification::HttpPost.log(request)
|
18
|
+
# if @notification.successful_authorisation?
|
19
|
+
# @invoice = Invoice.find(@notification.merchant_reference)
|
20
|
+
# @invoice.set_paid!
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @see Adyen::Notification::HttpPost.log
|
4
24
|
class Notification < ActiveRecord::Base
|
5
25
|
|
26
|
+
# The default table name to use for the notifications table.
|
6
27
|
DEFAULT_TABLE_NAME = :adyen_notifications
|
7
28
|
set_table_name(DEFAULT_TABLE_NAME)
|
8
29
|
|
30
|
+
# A notification should always include an event_code
|
9
31
|
validates_presence_of :event_code
|
32
|
+
|
33
|
+
# A notification should always include a psp_reference
|
10
34
|
validates_presence_of :psp_reference
|
35
|
+
|
36
|
+
# A notification should be unique using the composed key of
|
37
|
+
# [:psp_reference, :event_code, :success]
|
11
38
|
validates_uniqueness_of :success, :scope => [:psp_reference, :event_code]
|
12
39
|
|
13
40
|
# Make sure we don't end up with an original_reference with an empty string
|
14
41
|
before_validation { |notification| notification.original_reference = nil if notification.original_reference.blank? }
|
15
42
|
|
43
|
+
# Logs an incoming notification into the database.
|
44
|
+
#
|
45
|
+
# @param [Hash] params The notification parameters that should be stored in the database.
|
46
|
+
# @return [Adyen::Notification] The initiated and persisted notification instance.
|
47
|
+
# @raise This method will raise an exception if the notification cannot be stored.
|
48
|
+
# @see Adyen::Notification::HttpPost.log
|
16
49
|
def self.log(params)
|
17
50
|
converted_params = {}
|
18
51
|
# Convert each attribute from CamelCase notation to under_score notation
|
@@ -24,16 +57,30 @@ module Adyen
|
|
24
57
|
self.create!(converted_params)
|
25
58
|
end
|
26
59
|
|
60
|
+
# Returns true if this notification is an AUTHORISATION notification
|
61
|
+
# @return [true, false] true iff event_code == 'AUTHORISATION'
|
62
|
+
# @see Adyen.notification#successful_authorisation?
|
27
63
|
def authorisation?
|
28
64
|
event_code == 'AUTHORISATION'
|
29
65
|
end
|
30
66
|
|
31
67
|
alias :authorization? :authorisation?
|
32
68
|
|
69
|
+
# Returns true if this notification is an AUTHORISATION notification and
|
70
|
+
# the success status indicates that the authorization was successfull.
|
71
|
+
# @return [true, false] true iff the notification is an authorization
|
72
|
+
# and the authorization was successful according to the success field.
|
33
73
|
def successful_authorisation?
|
34
74
|
event_code == 'AUTHORISATION' && success?
|
35
75
|
end
|
76
|
+
|
77
|
+
alias :successful_authorization? :successful_authorisation?
|
36
78
|
|
79
|
+
# Collect a payment using the recurring contract that was initiated with
|
80
|
+
# this notification. The payment is collected using a SOAP call to the
|
81
|
+
# Adyen SOAP service for recurring payments.
|
82
|
+
# @param [Hash] options The payment parameters.
|
83
|
+
# @see Adyen::SOAP::RecurringService#submit
|
37
84
|
def collect_payment_for_recurring_contract!(options)
|
38
85
|
# Make sure we convert the value to cents
|
39
86
|
options[:value] = Adyen::Formatter::Price.in_cents(options[:value])
|
@@ -41,13 +88,16 @@ module Adyen
|
|
41
88
|
Adyen::SOAP::RecurringService.submit(options.merge(:recurring_reference => self.psp_reference))
|
42
89
|
end
|
43
90
|
|
91
|
+
# Deactivates the recurring contract that was initiated with this notification.
|
92
|
+
# The contract is deactivated by sending a SOAP call to the Adyen SOAP service for
|
93
|
+
# recurring contracts.
|
94
|
+
# @param [Hash] options The recurring contract parameters.
|
95
|
+
# @see Adyen::SOAP::RecurringService#deactivate
|
44
96
|
def deactivate_recurring_contract!(options)
|
45
97
|
raise "This is not a recurring contract!" unless event_code == 'RECURRING_CONTRACT'
|
46
98
|
Adyen::SOAP::RecurringService.deactivate(options.merge(:recurring_reference => self.psp_reference))
|
47
99
|
end
|
48
100
|
|
49
|
-
alias :successful_authorization? :successful_authorisation?
|
50
|
-
|
51
101
|
class HttpPost < Notification
|
52
102
|
|
53
103
|
def self.log(request)
|
@@ -67,6 +117,8 @@ module Adyen
|
|
67
117
|
end
|
68
118
|
end
|
69
119
|
|
120
|
+
# An ActiveRecord migration that can be used to create a suitable table
|
121
|
+
# to store Adyen::Notification instances for your application.
|
70
122
|
class Migration < ActiveRecord::Migration
|
71
123
|
|
72
124
|
def self.up(table_name = Adyen::Notification::DEFAULT_TABLE_NAME)
|
data/lib/adyen/soap.rb
CHANGED
@@ -47,19 +47,18 @@ module Adyen
|
|
47
47
|
klass.endpoint :version => 1, :uri => 'bogus'
|
48
48
|
end
|
49
49
|
|
50
|
-
# Setup
|
50
|
+
# Setup some CURL options to handle redirects correctly.
|
51
51
|
def on_after_create_http_client(http_client)
|
52
|
-
debug { |logger| logger.puts "Authorization: #{Adyen::SOAP.username}:#{Adyen::SOAP.password}..." }
|
53
|
-
# Handsoap BUG: Setting headers does not work, using a Curb specific method for now.
|
54
|
-
# auth = Base64.encode64("#{Adyen::SOAP.username}:#{Adyen::SOAP.password}").chomp
|
55
|
-
# http_client.headers['Authorization'] = "Basic #{auth}"
|
56
|
-
http_client.userpwd = "#{Adyen::SOAP.username}:#{Adyen::SOAP.password}"
|
57
|
-
|
58
|
-
# Setup some CURL options to handle redirects correctly.
|
59
52
|
http_client.follow_location = true
|
60
53
|
http_client.max_redirects = 1
|
61
54
|
end
|
62
55
|
|
56
|
+
# Setup basic authentication
|
57
|
+
def on_after_create_http_request(http_request)
|
58
|
+
debug { |logger| logger.puts "Authorization: #{Adyen::SOAP.username}:#{Adyen::SOAP.password}..." }
|
59
|
+
http_request.set_auth Adyen::SOAP.username, Adyen::SOAP.password
|
60
|
+
end
|
61
|
+
|
63
62
|
# Setup XML namespaces for SOAP request body
|
64
63
|
def on_create_document(doc)
|
65
64
|
doc.alias 'payment', 'http://payment.services.adyen.com'
|
@@ -128,6 +127,22 @@ module Adyen
|
|
128
127
|
end
|
129
128
|
end
|
130
129
|
|
130
|
+
# Retrieves the recurring contracts for a shopper. Requires the following arguments:
|
131
|
+
#
|
132
|
+
# * <tt>:merchent_account</tt> The merchant account under which to place
|
133
|
+
# this payment.
|
134
|
+
# * <tt>:shopper_reference</tt> The refrence of the shopper. This should be
|
135
|
+
# the same as the reference that was used to create the recurring contract.
|
136
|
+
def list(args = {})
|
137
|
+
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
138
|
+
response = invoke('recurring:listRecurringDetails') do |message|
|
139
|
+
message.add('recurring:recurringRequest') do |req|
|
140
|
+
req.add('recurring:merchantAccount', invoke_args[:merchant_account])
|
141
|
+
req.add('recurring:shopperReference', invoke_args[:shopper_reference])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
131
146
|
# Deactivates a recurring payment contract. Requires the following arguments:
|
132
147
|
#
|
133
148
|
# * <tt>:merchent_account</tt> The merchant account under which to place
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adyen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-12-
|
13
|
+
date: 2009-12-14 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|