adyen 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -4
- data/lib/adyen.rb +3 -7
- data/lib/adyen/configuration.rb +8 -3
- data/lib/adyen/hpp.rb +27 -0
- data/lib/adyen/hpp/request.rb +192 -0
- data/lib/adyen/hpp/response.rb +52 -0
- data/lib/adyen/hpp/signature.rb +7 -6
- data/lib/adyen/matchers.rb +1 -1
- data/lib/adyen/signature.rb +1 -1
- data/lib/adyen/version.rb +1 -1
- data/test/form_test.rb +2 -2
- data/test/helpers/configure_adyen.rb +1 -0
- data/test/helpers/example_server.rb +26 -1
- data/test/helpers/views/hpp.erb +3 -2
- data/test/hpp_test.rb +250 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79d7e1124e75e3e161c514b3d4fe8eab7058bec4
|
4
|
+
data.tar.gz: 97a72df3756f9d5ab4385b17f9db517c0c67d762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 870d40ed54714492339b6945c61acf0069d4d90a82e547e8b0c0fc69075e49b2c568b0ab25504e8efdddb7a61126fe4934c776e2003340279970167ad987f46b
|
7
|
+
data.tar.gz: 61da208663797528ecd1c8e2beaf1fd9c3bf895dead0a13833bf27b4114880a799ece1e1fb84dc77356e937fcf01a34817506c2f665099e8f32c88d2ad882ebf
|
data/CHANGELOG.md
CHANGED
@@ -4,12 +4,24 @@ The following changes have been made to the library over the years. Pleae add an
|
|
4
4
|
|
5
5
|
#### Unrelease changes
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
Nothing yet!
|
8
|
+
|
9
|
+
#### Version 2.2.0
|
10
|
+
|
11
|
+
- Add `Adyen::HPP` to integrate with Adyen's Hosted Payment Pages. `Adyen::HPP` supports the new HMAC-256 signing mechanism. `Adyen::Form` should be considered deprecated and will be removed from a future release.
|
12
|
+
|
13
|
+
#### Version 2.1.0
|
14
|
+
|
15
|
+
- Create syntax sugar for signature responses
|
16
|
+
- Various code cleanups.
|
17
|
+
|
18
|
+
#### Version 2.0.0
|
19
|
+
|
20
|
+
- Add `Adyen::REST` to intereact with Adyen's webservices. `Adyen::API` should be considered deprecated and will be removed from a future release.
|
9
21
|
- Make client-side encryption a first class citizen.
|
10
22
|
- Add integration test suite that uses a functional example app.
|
11
|
-
-
|
12
|
-
-
|
23
|
+
- Documentation updates and improvements.
|
24
|
+
- Drop support for Ruby 1.9
|
13
25
|
|
14
26
|
#### Version 1.6.0
|
15
27
|
|
data/lib/adyen.rb
CHANGED
@@ -3,18 +3,14 @@
|
|
3
3
|
# configuration methods.
|
4
4
|
#
|
5
5
|
# The most important submodules are:
|
6
|
-
# * {Adyen::
|
7
|
-
#
|
8
|
-
# * {Adyen::API} for communicating with the Adyen SOAP services for issuing
|
9
|
-
# (recurring) payments and recurring contract maintenance.
|
6
|
+
# * {Adyen::HPP} for interacting with Adyen's Hosted Payment Pages.
|
7
|
+
# * {Adyen::REST} for communicating with the Adyen REST webservices.
|
10
8
|
require 'adyen/base'
|
11
9
|
require 'adyen/version'
|
12
10
|
|
13
11
|
require 'adyen/form'
|
14
12
|
require 'adyen/api'
|
15
13
|
require 'adyen/rest'
|
16
|
-
|
17
|
-
# TODO: Move into main hpp file once it exists
|
18
|
-
require 'adyen/hpp/signature'
|
14
|
+
require 'adyen/hpp'
|
19
15
|
|
20
16
|
require 'adyen/railtie' if defined?(::Rails) && ::Rails::VERSION::MAJOR >= 3
|
data/lib/adyen/configuration.rb
CHANGED
@@ -86,14 +86,19 @@ class Adyen::Configuration
|
|
86
86
|
# @return [String]
|
87
87
|
attr_accessor :cse_public_key
|
88
88
|
|
89
|
-
# Default arguments that will be used
|
89
|
+
# Default arguments that will be used in every HTML form.
|
90
90
|
#
|
91
91
|
# @example
|
92
|
-
# Adyen.configuration.default_form_params[:
|
92
|
+
# Adyen.configuration.default_form_params[:merchant_account] = 'SuperShop'
|
93
93
|
#
|
94
94
|
# @return [Hash]
|
95
95
|
attr_accessor :default_form_params
|
96
96
|
|
97
|
+
# Name of the default skin for HPP requests.
|
98
|
+
#
|
99
|
+
# @return [String]
|
100
|
+
attr_accessor :default_skin
|
101
|
+
|
97
102
|
# Username that's set in Notification settings screen in Adyen PSP system and used by notification service to
|
98
103
|
# authenticate instant payment notification requests.
|
99
104
|
#
|
@@ -152,7 +157,7 @@ class Adyen::Configuration
|
|
152
157
|
@form_skins[skin_name.to_sym]
|
153
158
|
end
|
154
159
|
|
155
|
-
# Returns skin information by
|
160
|
+
# Returns skin information by skin code.
|
156
161
|
#
|
157
162
|
# @param [String] skin_code The code of the skin.
|
158
163
|
#
|
data/lib/adyen/hpp.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'adyen/hpp/signature'
|
2
|
+
require 'adyen/hpp/request'
|
3
|
+
require 'adyen/hpp/response'
|
4
|
+
|
5
|
+
module Adyen
|
6
|
+
module HPP
|
7
|
+
|
8
|
+
# The DOMAIN of the Adyen payment system that still requires the current
|
9
|
+
# Adyen enviroment.
|
10
|
+
HPP_DOMAIN = "%s.adyen.com"
|
11
|
+
|
12
|
+
# The URL of the Adyen payment system that still requires the current
|
13
|
+
# domain and payment flow to be filled.
|
14
|
+
HPP_URL = "https://%s/hpp/%s.shtml"
|
15
|
+
|
16
|
+
class Error < Adyen::Error
|
17
|
+
end
|
18
|
+
|
19
|
+
class ForgedResponse < Adyen::HPP::Error
|
20
|
+
end
|
21
|
+
|
22
|
+
class Notification
|
23
|
+
def initialize(request)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'adyen/hpp/signature'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Adyen
|
5
|
+
module HPP
|
6
|
+
|
7
|
+
class Request
|
8
|
+
attr_accessor :parameters
|
9
|
+
attr_writer :skin, :environment, :shared_secret
|
10
|
+
|
11
|
+
# Initialize the HPP request
|
12
|
+
#
|
13
|
+
# @param [Hash] parameters The payment parameters
|
14
|
+
# You must not provide the +:merchant_sig+ parameter: it will be calculated automatically.
|
15
|
+
# @param [Hash|String] skin A skin hash in the same format that is returned by
|
16
|
+
# Adyen::Configuration.register_form_skin, or the name of a registered skin.
|
17
|
+
# When not set, the default skin specified in the configuration will be used.
|
18
|
+
# @param [String] environment The Adyen environment to use.
|
19
|
+
# When not set, the environment specified in the configuration will be used.
|
20
|
+
# @param [String] shared_secret The shared secret to use for signing the request.
|
21
|
+
# When not set, the shared secret of the skin will be used.
|
22
|
+
def initialize(parameters, skin: nil, environment: nil, shared_secret: nil)
|
23
|
+
@parameters, @skin, @environment, @shared_secret = parameters, skin, environment, shared_secret
|
24
|
+
@skin = Adyen.configuration.form_skin_by_name(@skin) unless skin.nil? || skin.is_a?(Hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the Adyen skin to use for the request, in the same format that is
|
28
|
+
# returned by Adyen::Configuration.register_form_skin
|
29
|
+
#
|
30
|
+
# @return [Hash] skin if set, configuration default otherwise
|
31
|
+
def skin
|
32
|
+
@skin || Adyen.configuration.form_skin_by_name(Adyen.configuration.default_skin) || {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the Adyen environment the request will be directed to
|
36
|
+
#
|
37
|
+
# @return [String] environment if set, configuration default otherwise
|
38
|
+
def environment
|
39
|
+
@environment || Adyen.configuration.environment
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the shared secret to use for signing the request
|
43
|
+
#
|
44
|
+
# @return [String] shared secret if set, the skin's shared secret otherwise
|
45
|
+
def shared_secret
|
46
|
+
@shared_secret || skin[:shared_secret]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the DOMAIN of the Adyen payment system, adjusted for an Adyen environment.
|
50
|
+
#
|
51
|
+
# @return [String] The domain of the Adyen payment system that can be used
|
52
|
+
# for payment forms or redirects.
|
53
|
+
# @see Adyen::HPP::Request.redirect_url
|
54
|
+
def domain
|
55
|
+
(Adyen.configuration.payment_flow_domain || HPP_DOMAIN) % [environment.to_s]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the URL of the Adyen payment system, adjusted for an Adyen environment.
|
59
|
+
#
|
60
|
+
# @param [String] payment_flow The Adyen payment type to use. This parameter can be
|
61
|
+
# left out, in which case the default payment type will be used.
|
62
|
+
# @return [String] The absolute URL of the Adyen payment system that can be used
|
63
|
+
# for payment forms or redirects.
|
64
|
+
# @see Adyen::HPP::Request.domain
|
65
|
+
# @see Adyen::HPP::Request.redirect_url
|
66
|
+
# @see Adyen::HPP::Request.payment_methods_url
|
67
|
+
def url(payment_flow = nil)
|
68
|
+
payment_flow ||= Adyen.configuration.payment_flow
|
69
|
+
HPP_URL % [domain, payment_flow.to_s]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Transforms the payment parameters hash to be in the correct format. It will also
|
73
|
+
# include the Adyen::Configuration#default_form_params hash and it will
|
74
|
+
# include the +:skin_code+ parameter and the default attributes of the skin
|
75
|
+
# Any default parameter value will be overrided if another value is provided in the request.
|
76
|
+
#
|
77
|
+
# @return [Hash] Completed and formatted payment parameters.
|
78
|
+
# @raise [ArgumentError] Thrown if some parameter health check fails.
|
79
|
+
def formatted_parameters
|
80
|
+
raise ArgumentError, "Cannot generate request: parameters should be a hash!" unless parameters.is_a?(Hash)
|
81
|
+
|
82
|
+
formatted_parameters = parameters
|
83
|
+
default_form_parameters = Adyen.configuration.default_form_params
|
84
|
+
unless skin.empty?
|
85
|
+
formatted_parameters[:skin_code] ||= skin[:skin_code]
|
86
|
+
default_form_parameters = default_form_parameters.merge(skin[:default_form_params] || {})
|
87
|
+
end
|
88
|
+
formatted_parameters = default_form_parameters.merge(formatted_parameters)
|
89
|
+
|
90
|
+
raise ArgumentError, "Cannot generate request: :currency code attribute not found!" unless formatted_parameters[:currency_code]
|
91
|
+
raise ArgumentError, "Cannot generate request: :payment_amount code attribute not found!" unless formatted_parameters[:payment_amount]
|
92
|
+
raise ArgumentError, "Cannot generate request: :merchant_account attribute not found!" unless formatted_parameters[:merchant_account]
|
93
|
+
raise ArgumentError, "Cannot generate request: :skin_code attribute not found!" unless formatted_parameters[:skin_code]
|
94
|
+
|
95
|
+
formatted_parameters[:recurring_contract] = 'RECURRING' if formatted_parameters.delete(:recurring) == true
|
96
|
+
formatted_parameters[:order_data] = Adyen::Util.gzip_base64(formatted_parameters.delete(:order_data_raw)) if formatted_parameters[:order_data_raw]
|
97
|
+
formatted_parameters[:ship_before_date] = Adyen::Util.format_date(formatted_parameters[:ship_before_date])
|
98
|
+
formatted_parameters[:session_validity] = Adyen::Util.format_timestamp(formatted_parameters[:session_validity])
|
99
|
+
formatted_parameters
|
100
|
+
end
|
101
|
+
|
102
|
+
# Transforms and flattens payment parameters to be in the correct format which is understood and accepted by adyen
|
103
|
+
#
|
104
|
+
# @return [Hash] The payment parameters, with camelized and prefixed key, stringified values and
|
105
|
+
# the +:merchant_signature+ parameter set.
|
106
|
+
def flat_payment_parameters
|
107
|
+
Adyen::HPP::Signature.sign(Adyen::Util.flatten(formatted_parameters), shared_secret)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns an absolute URL to the Adyen payment system, with the payment parameters included
|
111
|
+
# as GET parameters in the URL. The URL also depends on the Adyen enviroment
|
112
|
+
#
|
113
|
+
# Note that Internet Explorer has a maximum length for URLs it can handle (2083 characters).
|
114
|
+
# Make sure that the URL is not longer than this limit if you want your site to work in IE.
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
#
|
118
|
+
# def pay
|
119
|
+
# # Generate a URL to redirect to Adyen's payment system.
|
120
|
+
# payment_parameters = {
|
121
|
+
# :currency_code => 'USD',
|
122
|
+
# :payment_amount => 1000,
|
123
|
+
# :merchant_account => 'MyMerchant',
|
124
|
+
# ...
|
125
|
+
# }
|
126
|
+
# hpp_request = Adyen::HPP::Request.new(payment_parameters, skin: :my_skin, environment: :test)
|
127
|
+
#
|
128
|
+
# respond_to do |format|
|
129
|
+
# format.html { redirect_to(hpp_request.redirect_url) }
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# @return [String] An absolute URL to redirect to the Adyen payment system.
|
134
|
+
def redirect_url
|
135
|
+
url + '?' + flat_payment_parameters.map { |(k, v)|
|
136
|
+
"#{CGI.escape(k)}=#{CGI.escape(v)}"
|
137
|
+
}.join('&')
|
138
|
+
end
|
139
|
+
|
140
|
+
# @see Adyen::HPP::Request.redirect_url
|
141
|
+
#
|
142
|
+
# Returns an absolute URL very similar to the one returned by Adyen::HPP::Request.redirect_url
|
143
|
+
# except that it uses the directory.shtml call which returns a list of all available
|
144
|
+
# payment methods
|
145
|
+
#
|
146
|
+
# @return [String] An absolute URL to redirect to the Adyen payment system.
|
147
|
+
def payment_methods_url
|
148
|
+
url(:directory) + '?' + flat_payment_parameters.map { |(k, v)|
|
149
|
+
"#{CGI.escape(k)}=#{CGI.escape(v)}"
|
150
|
+
}.join('&')
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns a HTML snippet of hidden INPUT tags with the provided payment parameters.
|
154
|
+
# The snippet can be included in a payment form that POSTs to the Adyen payment system.
|
155
|
+
#
|
156
|
+
# The payment parameters that are provided to this method will be merged with the
|
157
|
+
# {Adyen::Configuration#default_form_params} hash. The default parameter values will be
|
158
|
+
# overrided if another value is provided to this method.
|
159
|
+
#
|
160
|
+
# You do not have to provide the +:merchant_sig+ parameter: it will be calculated automatically.
|
161
|
+
#
|
162
|
+
# @example
|
163
|
+
#
|
164
|
+
# <%
|
165
|
+
# payment_parameters = {
|
166
|
+
# :currency_code => 'USD',
|
167
|
+
# :payment_amount => 1000,
|
168
|
+
# :merchant_account => 'MyMerchant',
|
169
|
+
# ...
|
170
|
+
# }
|
171
|
+
# hpp_request = Adyen::HPP::Request.new(payment_parameters, skin: :my_skin, environment: :test)
|
172
|
+
# %>
|
173
|
+
#
|
174
|
+
# <%= form_tag(hpp_request.url, authenticity_token: false, enforce_utf8: false) do %>
|
175
|
+
# <%= hpp_request.hidden_fields %>
|
176
|
+
# <%= submit_tag("Pay invoice")
|
177
|
+
# <% end %>
|
178
|
+
#
|
179
|
+
# @return [String] An HTML snippet that can be included in a form that POSTs to the
|
180
|
+
# Adyen payment system.
|
181
|
+
def hidden_fields
|
182
|
+
|
183
|
+
# Generate a hidden input tag per parameter, join them by newlines.
|
184
|
+
form_str = flat_payment_parameters.map { |key, value|
|
185
|
+
"<input type=\"hidden\" name=\"#{CGI.escapeHTML(key)}\" value=\"#{CGI.escapeHTML(value)}\" />"
|
186
|
+
}.join("\n")
|
187
|
+
|
188
|
+
form_str.respond_to?(:html_safe) ? form_str.html_safe : form_str
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Adyen
|
2
|
+
module HPP
|
3
|
+
|
4
|
+
class Response
|
5
|
+
attr_reader :params, :shared_secret
|
6
|
+
|
7
|
+
# Initialize the HPP response
|
8
|
+
#
|
9
|
+
# @param [Hash] params params A hash of HTTP GET parameters for the redirect request. This
|
10
|
+
# should include the +:merchantSig+ parameter, which contains the signature.
|
11
|
+
# @param [String] shared_secret Optional shared secret; if not provided, the shared secret
|
12
|
+
# of the skin determined by params['skinCode'] will be used
|
13
|
+
def initialize(params, shared_secret: nil)
|
14
|
+
raise ArgumentError, "params should be a Hash" unless params.is_a?(Hash)
|
15
|
+
raise ArgumentError, "params should contain :merchantSig" unless params.key?('merchantSig')
|
16
|
+
|
17
|
+
@params = params
|
18
|
+
skin = Adyen.configuration.form_skin_by_code(params['skinCode']) || {}
|
19
|
+
@shared_secret = shared_secret || skin[:shared_secret]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Checks the redirect signature for this request by calculating the signature from
|
23
|
+
# the provided parameters, and comparing it to the signature provided in the +merchantSig+
|
24
|
+
# parameter.
|
25
|
+
#
|
26
|
+
# If this method returns false, the request could be a forgery and should not be handled.
|
27
|
+
# Therefore, you should include this check in a +before_filter+, and raise an error of the
|
28
|
+
# signature check fails.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# class PaymentsController < ApplicationController
|
32
|
+
# before_filter :check_signature, :only => [:return_from_adyen]
|
33
|
+
#
|
34
|
+
# def return_from_adyen
|
35
|
+
# @invoice = Invoice.find(params[:merchantReference])
|
36
|
+
# @invoice.set_paid! if params[:authResult] == 'AUTHORISED'
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# private
|
40
|
+
#
|
41
|
+
# def check_signature
|
42
|
+
# raise "Forgery!" unless Adyen::HPP::Response.new(params).has_valid_signature?
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @return [true, false] Returns true only if the signature in the parameters is correct.
|
47
|
+
def has_valid_signature?
|
48
|
+
Adyen::HPP::Signature.verify(params, shared_secret)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/adyen/hpp/signature.rb
CHANGED
@@ -8,10 +8,11 @@ module Adyen
|
|
8
8
|
|
9
9
|
# Sign the parameters with the given shared secret
|
10
10
|
# @param [Hash] params The set of parameters to sign.
|
11
|
-
# @param [String] shared_secret The shared secret for signing
|
12
|
-
# params hash with the `sharedSecret` key.
|
11
|
+
# @param [String] shared_secret The shared secret for signing.
|
13
12
|
# @return [Hash] params The params that were passed in plus a new `merchantSig` param
|
14
|
-
def sign(params, shared_secret
|
13
|
+
def sign(params, shared_secret)
|
14
|
+
params = params.dup
|
15
|
+
params.delete('merchantSig')
|
15
16
|
params["sharedSecret"] ||= shared_secret
|
16
17
|
params.merge('merchantSig' => Adyen::Signature.sign(params))
|
17
18
|
end
|
@@ -19,10 +20,10 @@ module Adyen
|
|
19
20
|
# Verify the parameters with the given shared secret
|
20
21
|
# @param [Hash] params The set of parameters to verify. Must include a `merchantSig`
|
21
22
|
# param that will be compared to the signature we calculate.
|
22
|
-
# @param [String] shared_secret The shared secret for
|
23
|
-
# params hash with the `sharedSecret` key.
|
23
|
+
# @param [String] shared_secret The shared secret for verification.
|
24
24
|
# @return [Boolean] true if the `merchantSig` in the params matches our calculated signature
|
25
|
-
def verify(params, shared_secret
|
25
|
+
def verify(params, shared_secret)
|
26
|
+
params = params.dup
|
26
27
|
params["sharedSecret"] ||= shared_secret
|
27
28
|
their_sig = params.delete('merchantSig')
|
28
29
|
raise ArgumentError, "params must include 'merchantSig' for verification" if their_sig.empty?
|
data/lib/adyen/matchers.rb
CHANGED
@@ -7,7 +7,7 @@ module Adyen
|
|
7
7
|
|
8
8
|
def self.build_xpath_query(checks)
|
9
9
|
# Start by finding the check for the Adyen form tag
|
10
|
-
xpath_query = "//form[@
|
10
|
+
xpath_query = "//form[@id='adyen']"
|
11
11
|
|
12
12
|
# Add recurring/single check if specified
|
13
13
|
recurring = checks.delete(:recurring)
|
data/lib/adyen/signature.rb
CHANGED
@@ -13,7 +13,7 @@ module Adyen
|
|
13
13
|
# @return [String] The signature
|
14
14
|
def sign(params, type = :hpp)
|
15
15
|
shared_secret = params.delete('sharedSecret')
|
16
|
-
raise ArgumentError,
|
16
|
+
raise ArgumentError, "Cannot sign parameters without a shared secret" unless shared_secret
|
17
17
|
sig = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), Array(shared_secret).pack("H*"), string_to_sign(params, type))
|
18
18
|
Base64.encode64(sig).strip
|
19
19
|
end
|
data/lib/adyen/version.rb
CHANGED
data/test/form_test.rb
CHANGED
@@ -270,7 +270,7 @@ class FormTest < Minitest::Test
|
|
270
270
|
|
271
271
|
def test_hidden_payment_form_fields
|
272
272
|
payment_snippet = <<-HTML
|
273
|
-
<form action="#{CGI.escapeHTML(Adyen::Form.url)}" method="post">
|
273
|
+
<form id="adyen" action="#{CGI.escapeHTML(Adyen::Form.url)}" method="post">
|
274
274
|
#{Adyen::Form.hidden_fields(@payment_attributes)}
|
275
275
|
</form>
|
276
276
|
HTML
|
@@ -286,7 +286,7 @@ class FormTest < Minitest::Test
|
|
286
286
|
|
287
287
|
def test_hidden_recurring_payment_form_fields
|
288
288
|
recurring_snippet = <<-HTML
|
289
|
-
<form action="#{CGI.escapeHTML(Adyen::Form.url)}" method="post">
|
289
|
+
<form id="adyen" action="#{CGI.escapeHTML(Adyen::Form.url)}" method="post">
|
290
290
|
#{Adyen::Form.hidden_fields(@recurring_payment_attributes)}
|
291
291
|
</form>
|
292
292
|
HTML
|
@@ -3,3 +3,4 @@ Adyen.configuration.api_username = 'ws@Company.VanBergen'
|
|
3
3
|
Adyen.configuration.api_password = '7phtHzbfnzsp'
|
4
4
|
Adyen.configuration.cse_public_key = '10001|AC2CEF7D1BE904AF35B76467927A7044CCFC470E725B4D5327CC143BC5245983A56A4E5B0AC969F7F706F4B4A81B184DE2ECEA229CDBB943E6F7D6AD1623604F66640D1F2FAE1E4AE80EE1E5D4486AA8F553F6CE47BF57C8EFEF745E731AE27DD7B74F8895D41DA8339CC32677E4BD35288EC2FB9A18D46E7E3DFF5C7DD6D756F2223BED29427E5899A5877F0E9D1FAA50C17F8C96BA96DFA79BD04B1116366FFEE33F1BD18B8C3694BACBF8BFC7E2045CB9FFFEFA49AAB18EF1D9E9710A16B7DC67433ABD3A4FD3661CD9F5B1967753E4DC744DD3A1F8C8BED87827EED443CBED06A0D5C86C329406BE452B9A6EB997EE43A9FA7241A74E87AC3FC898F327CD'
|
5
5
|
Adyen.configuration.register_form_skin(:testing, 'tifSfXeX', 'testing123', :merchant_account => 'VanBergenORG')
|
6
|
+
Adyen.configuration.default_skin = :testing
|
@@ -12,6 +12,31 @@ class Adyen::ExampleServer < Sinatra::Base
|
|
12
12
|
end
|
13
13
|
|
14
14
|
get '/hpp' do
|
15
|
+
@payment = {
|
16
|
+
:currency_code => 'EUR',
|
17
|
+
:payment_amount => 4321,
|
18
|
+
:merchant_reference => params[:merchant_reference] || 'HPP test order',
|
19
|
+
:ship_before_date => (Date.today + 1).strftime('%F'),
|
20
|
+
:session_validity => (Time.now.utc + 30*60).strftime('%FT%TZ'),
|
21
|
+
:billing_address => {
|
22
|
+
:street => 'Alexanderplatz',
|
23
|
+
:house_number_or_name => '0815',
|
24
|
+
:city => 'Berlin',
|
25
|
+
:postal_code => '10119',
|
26
|
+
:state_or_province => 'Berlin',
|
27
|
+
:country => 'Germany',
|
28
|
+
},
|
29
|
+
:shopper => {
|
30
|
+
:telephone_number => '123-4512-345',
|
31
|
+
:first_name => 'John',
|
32
|
+
:last_name => 'Doe',
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
erb :hpp
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/form' do
|
15
40
|
@payment = {
|
16
41
|
:skin => :testing,
|
17
42
|
:currency_code => 'EUR',
|
@@ -38,7 +63,7 @@ class Adyen::ExampleServer < Sinatra::Base
|
|
38
63
|
end
|
39
64
|
|
40
65
|
get '/hpp/result' do
|
41
|
-
raise "Forgery!" unless Adyen::
|
66
|
+
raise "Forgery!" unless Adyen::HPP::Response(params).has_valid_signature?
|
42
67
|
|
43
68
|
case params['authResult']
|
44
69
|
when 'AUTHORISED'
|
data/test/helpers/views/hpp.erb
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
<title> HPP Payment</title>
|
4
4
|
</head>
|
5
5
|
<body>
|
6
|
-
|
6
|
+
<% hpp_client = Adyen::HPP::Client.new(:test, :testing) %>
|
7
|
+
<form action="<%= hpp_client.url %>" method="post">
|
7
8
|
<p> Price: <strong>EUR 43.21</strong>. </p>
|
8
9
|
<p>
|
9
|
-
<%=
|
10
|
+
<%= hpp_client.new_request.hidden_fields(@payment) %>
|
10
11
|
<input type="submit" value="Pay" />
|
11
12
|
</p>
|
12
13
|
</form>
|
data/test/hpp_test.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'adyen/hpp'
|
3
|
+
|
4
|
+
class HppTest < Minitest::Test
|
5
|
+
include Adyen::Matchers
|
6
|
+
include Adyen::Test::EachXMLBackend
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@skin_code1 = 'abcdefgh'
|
10
|
+
@skin_code2 = 'ijklmnop'
|
11
|
+
@shared_secret_skin1 = '4468D9782DEF54FCD706C9100C71EC43932B1EBC2ACF6BA0560C05AAA7550C48'
|
12
|
+
@shared_secret_skin2 = '21F58626031F08A30F6BD07BB8AC12C19B56F6C99C6E1DA991A52A1C64A4C010'
|
13
|
+
|
14
|
+
Adyen.configuration.default_form_params[:merchant_account] = 'TestMerchant'
|
15
|
+
Adyen.configuration.register_form_skin(:skin1, @skin_code1, @shared_secret_skin1)
|
16
|
+
Adyen.configuration.register_form_skin(:skin2, @skin_code2, @shared_secret_skin2, merchant_account: 'OtherMerchant')
|
17
|
+
|
18
|
+
# Use autodetection for the environment unless otherwise specified
|
19
|
+
Adyen.configuration.environment = nil
|
20
|
+
Adyen.configuration.payment_flow = :select
|
21
|
+
Adyen.configuration.payment_flow_domain = nil
|
22
|
+
Adyen.configuration.default_skin = :skin1
|
23
|
+
|
24
|
+
@payment_attributes = {
|
25
|
+
:currency_code => 'GBP',
|
26
|
+
:payment_amount => 10000,
|
27
|
+
:merchant_reference => 'Internet Order 12345',
|
28
|
+
:ship_before_date => '2007-10-20',
|
29
|
+
:session_validity => '2007-10-11T11:00:00Z',
|
30
|
+
:billing_address => {
|
31
|
+
:street => 'Alexanderplatz',
|
32
|
+
:house_number_or_name => '0815',
|
33
|
+
:city => 'Berlin',
|
34
|
+
:postal_code => '10119',
|
35
|
+
:state_or_province => 'Berlin',
|
36
|
+
:country => 'Germany',
|
37
|
+
},
|
38
|
+
:shopper => {
|
39
|
+
:telephone_number => '1234512345',
|
40
|
+
:first_name => 'John',
|
41
|
+
:last_name => 'Doe',
|
42
|
+
:social_security_number => '123-45-1234'
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
@recurring_payment_attributes = @payment_attributes.merge(
|
47
|
+
:recurring_contract => 'DEFAULT',
|
48
|
+
:shopper_reference => 'grasshopper52',
|
49
|
+
:shopper_email => 'gras.shopper@somewhere.org'
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_autodetected_redirect_url
|
54
|
+
request = Adyen::HPP::Request.new(@payment_attributes)
|
55
|
+
assert_equal 'https://test.adyen.com/hpp/select.shtml', request.url
|
56
|
+
|
57
|
+
Adyen.configuration.stubs(:autodetect_environment).returns('live')
|
58
|
+
assert_equal 'https://live.adyen.com/hpp/select.shtml', request.url
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_explicit_redirect_url
|
62
|
+
assert_equal 'https://test.adyen.com/hpp/select.shtml',
|
63
|
+
Adyen::HPP::Request.new(@payment_attributes, skin: :skin1, environment: :test).url
|
64
|
+
assert_equal 'https://live.adyen.com/hpp/select.shtml',
|
65
|
+
Adyen::HPP::Request.new(@payment_attributes, skin: :skin1, environment: :live).url
|
66
|
+
assert_equal 'https://test.adyen.com/hpp/select.shtml',
|
67
|
+
Adyen::HPP::Request.new(@payment_attributes, skin: :skin2, environment: :test).url
|
68
|
+
assert_equal 'https://live.adyen.com/hpp/select.shtml',
|
69
|
+
Adyen::HPP::Request.new(@payment_attributes, skin: :skin2, environment: :live).url
|
70
|
+
assert_equal 'https://test.adyen.com/hpp/select.shtml',
|
71
|
+
Adyen::HPP::Request.new(@payment_attributes, environment: :test).url
|
72
|
+
assert_equal 'https://live.adyen.com/hpp/select.shtml',
|
73
|
+
Adyen::HPP::Request.new(@payment_attributes, environment: :live).url
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_redirect_url_for_different_payment_flows
|
77
|
+
request = Adyen::HPP::Request.new(@payment_attributes, environment: :test)
|
78
|
+
|
79
|
+
Adyen.configuration.payment_flow = :select
|
80
|
+
assert_equal 'https://test.adyen.com/hpp/select.shtml', request.url
|
81
|
+
|
82
|
+
Adyen.configuration.payment_flow = :pay
|
83
|
+
assert_equal 'https://test.adyen.com/hpp/pay.shtml', request.url
|
84
|
+
|
85
|
+
Adyen.configuration.payment_flow = :details
|
86
|
+
assert_equal 'https://test.adyen.com/hpp/details.shtml', request.url
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_redirect_url_for_custom_domain
|
90
|
+
request = Adyen::HPP::Request.new(@payment_attributes, environment: :test)
|
91
|
+
|
92
|
+
Adyen.configuration.payment_flow_domain = "checkout.mydomain.com"
|
93
|
+
assert_equal 'https://checkout.mydomain.com/hpp/select.shtml', request.url
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_redirect_url_generation
|
97
|
+
attributes = {
|
98
|
+
:currency_code => 'GBP', :payment_amount => 10000, :ship_before_date => Date.parse('2015-10-26'),
|
99
|
+
:merchant_reference => 'Internet Order 12345', :session_validity => Time.parse('2015-10-26 10:30')
|
100
|
+
}
|
101
|
+
|
102
|
+
request = Adyen::HPP::Request.new(attributes)
|
103
|
+
|
104
|
+
processed_attributes = {
|
105
|
+
'currencyCode' => 'GBP', 'paymentAmount' => '10000', 'shipBeforeDate' => '2015-10-26',
|
106
|
+
'merchantReference' => 'Internet Order 12345', 'sessionValidity' => '2015-10-26T10:30:00Z',
|
107
|
+
'merchantAccount' => 'TestMerchant', 'skinCode' => @skin_code1, 'merchantSig' => 'ewDgqa+m3rMO6MOZfQ0ugWdwsu+otvRVBVujqGfgvb8='
|
108
|
+
}
|
109
|
+
|
110
|
+
redirect_uri = URI(request.redirect_url)
|
111
|
+
assert_match %r[^#{request.url}], redirect_uri.to_s
|
112
|
+
|
113
|
+
params = CGI.parse(redirect_uri.query)
|
114
|
+
processed_attributes.each do |key, value|
|
115
|
+
assert_equal value, params[key].first
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_redirect_url_generation_explicit_skin_code_and_shared_secret
|
120
|
+
attributes = {
|
121
|
+
:currency_code => 'GBP', :payment_amount => 10000, :ship_before_date => Date.parse('2015-10-26'),
|
122
|
+
:merchant_reference => 'Internet Order 12345', :session_validity => Time.parse('2015-10-26 10:30'),
|
123
|
+
:skin_code => @skin_code1
|
124
|
+
}
|
125
|
+
|
126
|
+
request = Adyen::HPP::Request.new(attributes, shared_secret: @shared_secret_skin1)
|
127
|
+
|
128
|
+
processed_attributes = {
|
129
|
+
'currencyCode' => 'GBP', 'paymentAmount' => '10000', 'shipBeforeDate' => '2015-10-26',
|
130
|
+
'merchantReference' => 'Internet Order 12345', 'sessionValidity' => '2015-10-26T10:30:00Z',
|
131
|
+
'merchantAccount' => 'TestMerchant', 'skinCode' => @skin_code1, 'merchantSig' => 'ewDgqa+m3rMO6MOZfQ0ugWdwsu+otvRVBVujqGfgvb8='
|
132
|
+
}
|
133
|
+
|
134
|
+
redirect_uri = URI(request.redirect_url)
|
135
|
+
assert_match %r[^#{request.url}], redirect_uri.to_s
|
136
|
+
|
137
|
+
params = CGI.parse(redirect_uri.query)
|
138
|
+
processed_attributes.each do |key, value|
|
139
|
+
assert_equal value, params[key].first
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_redirect_url_generation_with_direct_skin_details
|
144
|
+
attributes = {
|
145
|
+
:currency_code => 'GBP', :payment_amount => 10000, :ship_before_date => Date.parse('2015-10-26'),
|
146
|
+
:merchant_reference => 'Internet Order 12345', :session_validity => Time.parse('2015-10-26 10:30'),
|
147
|
+
:merchant_account => 'OtherMerchant'
|
148
|
+
}
|
149
|
+
|
150
|
+
request = Adyen::HPP::Request.new(attributes, skin: :skin2)
|
151
|
+
|
152
|
+
processed_attributes = {
|
153
|
+
'currencyCode' => 'GBP', 'paymentAmount' => '10000', 'shipBeforeDate' => '2015-10-26',
|
154
|
+
'merchantReference' => 'Internet Order 12345', 'sessionValidity' => '2015-10-26T10:30:00Z',
|
155
|
+
'merchantAccount' => 'OtherMerchant', 'skinCode' => @skin_code2, 'merchantSig' => 'uS/tdqapxD8rQKBRzKQ9wOIiOFRmcOR3HsC8CO15Zto='
|
156
|
+
}
|
157
|
+
|
158
|
+
redirect_uri = URI(request.redirect_url)
|
159
|
+
assert_match %r[^#{request.url}], redirect_uri.to_s
|
160
|
+
|
161
|
+
params = CGI.parse(redirect_uri.query)
|
162
|
+
processed_attributes.each do |key, value|
|
163
|
+
assert_equal value, params[key].first
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_payment_methods_url_generation
|
168
|
+
attributes = {
|
169
|
+
:currency_code => 'GBP', :payment_amount => 10000, :ship_before_date => Date.parse('2015-10-26'),
|
170
|
+
:merchant_reference => 'Internet Order 12345', :session_validity => Time.parse('2015-10-26 10:30')
|
171
|
+
}
|
172
|
+
|
173
|
+
request = Adyen::HPP::Request.new(attributes)
|
174
|
+
|
175
|
+
processed_attributes = {
|
176
|
+
'currencyCode' => 'GBP', 'paymentAmount' => '10000', 'shipBeforeDate' => '2015-10-26',
|
177
|
+
'merchantReference' => 'Internet Order 12345', 'sessionValidity' => '2015-10-26T10:30:00Z',
|
178
|
+
'merchantAccount' => 'TestMerchant', 'skinCode' => @skin_code1, 'merchantSig' => 'ewDgqa+m3rMO6MOZfQ0ugWdwsu+otvRVBVujqGfgvb8='
|
179
|
+
}
|
180
|
+
|
181
|
+
payment_methods_uri = URI(request.payment_methods_url)
|
182
|
+
assert_match %r[^#{request.url(:directory)}], payment_methods_uri.to_s
|
183
|
+
|
184
|
+
params = CGI.parse(payment_methods_uri.query)
|
185
|
+
processed_attributes.each do |key, value|
|
186
|
+
assert_equal value, params[key].first
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_has_valid_signature
|
191
|
+
params = {
|
192
|
+
'authResult' => 'AUTHORISED', 'pspReference' => '1211992213193029',
|
193
|
+
'merchantReference' => 'Internet Order 12345', 'skinCode' => @skin_code1,
|
194
|
+
'merchantSig' => 'owrLGxBP/l5xej5VZn8FKS1exn0qOgk0P9kmRdBBw9Q='
|
195
|
+
}
|
196
|
+
|
197
|
+
correct_secret = @shared_secret_skin1
|
198
|
+
incorrect_secret = @shared_secret_skin2
|
199
|
+
|
200
|
+
assert Adyen::HPP::Response.new(params).has_valid_signature?
|
201
|
+
assert Adyen::HPP::Response.new(params, shared_secret: correct_secret).has_valid_signature?
|
202
|
+
|
203
|
+
refute Adyen::HPP::Response.new(params.merge('skinCode' => @skin_code2)).has_valid_signature?
|
204
|
+
refute Adyen::HPP::Response.new(params, shared_secret: incorrect_secret).has_valid_signature?
|
205
|
+
|
206
|
+
refute Adyen::HPP::Response.new(params.merge('pspReference' => 'tampered')).has_valid_signature?
|
207
|
+
refute Adyen::HPP::Response.new(params.merge('merchantSig' => 'tampered')).has_valid_signature?
|
208
|
+
|
209
|
+
assert_raises(ArgumentError) { Adyen::HPP::Response.new(nil).has_valid_signature? }
|
210
|
+
assert_raises(ArgumentError) { Adyen::HPP::Response.new({}).has_valid_signature? }
|
211
|
+
assert_raises(ArgumentError) { Adyen::HPP::Response.new(params.delete(:skinCode)).has_valid_signature? }
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_hidden_payment_form_fields
|
215
|
+
request = Adyen::HPP::Request.new(@payment_attributes, skin: :skin1, environment: :test)
|
216
|
+
|
217
|
+
payment_snippet = <<-HTML
|
218
|
+
<form id="adyen" action="#{CGI.escapeHTML(request.url)}" method="post">
|
219
|
+
#{request.hidden_fields}
|
220
|
+
</form>
|
221
|
+
HTML
|
222
|
+
|
223
|
+
for_each_xml_backend do
|
224
|
+
assert_adyen_single_payment_form payment_snippet,
|
225
|
+
merchantAccount: 'TestMerchant',
|
226
|
+
currencyCode: 'GBP',
|
227
|
+
paymentAmount: '10000',
|
228
|
+
skinCode: @skin_code1
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_hidden_recurring_payment_form_fields
|
233
|
+
request = Adyen::HPP::Request.new(@recurring_payment_attributes, skin: :skin2, environment: :live)
|
234
|
+
|
235
|
+
recurring_snippet = <<-HTML
|
236
|
+
<form id="adyen" action="#{CGI.escapeHTML(request.url)}" method="post">
|
237
|
+
#{request.hidden_fields}
|
238
|
+
</form>
|
239
|
+
HTML
|
240
|
+
|
241
|
+
for_each_xml_backend do
|
242
|
+
assert_adyen_recurring_payment_form recurring_snippet,
|
243
|
+
merchantAccount: 'OtherMerchant',
|
244
|
+
currencyCode: 'GBP',
|
245
|
+
paymentAmount: '10000',
|
246
|
+
recurringContract: 'DEFAULT',
|
247
|
+
skinCode: @skin_code2
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
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: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2016-06-
|
14
|
+
date: 2016-06-23 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rake
|
@@ -166,6 +166,9 @@ files:
|
|
166
166
|
- lib/adyen/base.rb
|
167
167
|
- lib/adyen/configuration.rb
|
168
168
|
- lib/adyen/form.rb
|
169
|
+
- lib/adyen/hpp.rb
|
170
|
+
- lib/adyen/hpp/request.rb
|
171
|
+
- lib/adyen/hpp/response.rb
|
169
172
|
- lib/adyen/hpp/signature.rb
|
170
173
|
- lib/adyen/matchers.rb
|
171
174
|
- lib/adyen/notification_generator.rb
|
@@ -210,6 +213,7 @@ files:
|
|
210
213
|
- test/helpers/views/pay.erb
|
211
214
|
- test/helpers/views/redirect_shopper.erb
|
212
215
|
- test/hpp/signature_test.rb
|
216
|
+
- test/hpp_test.rb
|
213
217
|
- test/integration/hpp_integration_test.rb
|
214
218
|
- test/integration/payment_using_3d_secure_integration_test.rb
|
215
219
|
- test/integration/payment_with_client_side_encryption_integration_test.rb
|
@@ -279,6 +283,7 @@ test_files:
|
|
279
283
|
- test/helpers/views/pay.erb
|
280
284
|
- test/helpers/views/redirect_shopper.erb
|
281
285
|
- test/hpp/signature_test.rb
|
286
|
+
- test/hpp_test.rb
|
282
287
|
- test/integration/hpp_integration_test.rb
|
283
288
|
- test/integration/payment_using_3d_secure_integration_test.rb
|
284
289
|
- test/integration/payment_with_client_side_encryption_integration_test.rb
|