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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b0487de131168302d566b9dbe912c921ebf4b8a
4
- data.tar.gz: f9913d706f1280146dfc27c5e6bbcb7f2727ea03
3
+ metadata.gz: 79d7e1124e75e3e161c514b3d4fe8eab7058bec4
4
+ data.tar.gz: 97a72df3756f9d5ab4385b17f9db517c0c67d762
5
5
  SHA512:
6
- metadata.gz: 6914750599b1a3ecdabd6d7000dde4ea4c2131f85c691d4d4ad693a6ba8cdbf4b95a2f4cc7a41b54b83ae7351a1bac2b18c6d4eb7fb69f64523e7ee48686994c
7
- data.tar.gz: 7401dcb3c7808508fe3da582a172150af1441a3dba9bf7744da5a91b77b78be94c0b75772ed671153258b9c5a429e5b9bdb786960619d8d7216eb5f609912bac
6
+ metadata.gz: 870d40ed54714492339b6945c61acf0069d4d90a82e547e8b0c0fc69075e49b2c568b0ab25504e8efdddb7a61126fe4934c776e2003340279970167ad987f46b
7
+ data.tar.gz: 61da208663797528ecd1c8e2beaf1fd9c3bf895dead0a13833bf27b4114880a799ece1e1fb84dc77356e937fcf01a34817506c2f665099e8f32c88d2ad882ebf
@@ -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
- - Add `Adyen::REST` to intereact with Adyen's webservices.
8
- - Add `Adyen::HPP` to integrate with Adyen's Hosted Payment Pages.
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
- - Deprecate `Adyen::API` and `Adyen::Form`.
12
- - DOcumentation updates and improvements.
23
+ - Documentation updates and improvements.
24
+ - Drop support for Ruby 1.9
13
25
 
14
26
  #### Version 1.6.0
15
27
 
@@ -3,18 +3,14 @@
3
3
  # configuration methods.
4
4
  #
5
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::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
@@ -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 for in every HTML form.
89
+ # Default arguments that will be used in every HTML form.
90
90
  #
91
91
  # @example
92
- # Adyen.configuration.default_form_params[:shared_secret] = 'secret'
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 code code.
160
+ # Returns skin information by skin code.
156
161
  #
157
162
  # @param [String] skin_code The code of the skin.
158
163
  #
@@ -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
@@ -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/verification. Can also be sent in the
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 = nil)
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 signing/verification. Can also be sent in the
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 = nil)
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?
@@ -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[@action='#{Adyen::Form.url}']"
10
+ xpath_query = "//form[@id='adyen']"
11
11
 
12
12
  # Add recurring/single check if specified
13
13
  recurring = checks.delete(:recurring)
@@ -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, 'Cannot sign without a shared secret' if shared_secret.nil?
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
@@ -1,5 +1,5 @@
1
1
  module Adyen
2
2
  # Version constant for the Adyen plugin.
3
3
  # Set it & commit the change before running rake release.
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.0"
5
5
  end
@@ -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::Form.redirect_signature_check(params)
66
+ raise "Forgery!" unless Adyen::HPP::Response(params).has_valid_signature?
42
67
 
43
68
  case params['authResult']
44
69
  when 'AUTHORISED'
@@ -3,10 +3,11 @@
3
3
  <title> HPP Payment</title>
4
4
  </head>
5
5
  <body>
6
- <form action="<%= Adyen::Form.url(:test) %>" method="post">
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
- <%= Adyen::Form.hidden_fields(@payment) %>
10
+ <%= hpp_client.new_request.hidden_fields(@payment) %>
10
11
  <input type="submit" value="Pay" />
11
12
  </p>
12
13
  </form>
@@ -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.1.0
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-15 00:00:00.000000000 Z
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