adyen 2.1.0 → 2.2.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.
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