adyen 2.0.0.pre2 → 2.0.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: 65289d682bc415347ec65ea427c0d6e7943cb3d3
4
- data.tar.gz: e3a11edb4ac752b412721f3bdf61a0a04b4bf85e
3
+ metadata.gz: 98769e9e8772583d0d35be23a09352f10654095b
4
+ data.tar.gz: 8e8fefa15a40bcd9f871ddf203acb415351df55d
5
5
  SHA512:
6
- metadata.gz: fb6eb78b5a9748124fa432b7105f88d1b0d358ad7fe13817d36ae97036968ebb9514e7986aa8ee071a0210331d16782b67db5179ef90a04733c9d08835b07fcb
7
- data.tar.gz: db40c3ff10543755bbe055f6f2ac677cbba179aea097ea175389d0c39b9958e6434118ff646deba9cb2ce2dd5096bca23ae869ae1ce45766a738e79f4c5ca4bb
6
+ metadata.gz: c3273f3cfb9c26b6e399254e92e068843dd7a75eedd66205166eee82b3cb2761848dc05fec7935d0e08d1d5b45ad00d4903f280c69c00a540dd1b52f35666d56
7
+ data.tar.gz: 629c089709b330bf3bfb9accd30f4ee7a4a8db5d687662eb6c7b2047b4b2d973ab676c9d8ebaa099cfe1790e6d9cec5f4793db9932dc038b1fd920b5a5607567
@@ -26,6 +26,7 @@ end
26
26
  require 'adyen/version'
27
27
  require 'adyen/configuration'
28
28
  require 'adyen/util'
29
+ require 'adyen/hpp/signature'
29
30
  require 'adyen/form'
30
31
  require 'adyen/api'
31
32
  require 'adyen/rest'
@@ -120,10 +120,18 @@ module Adyen
120
120
  parameters[:billing_address_sig] = calculate_billing_address_signature(parameters, shared_secret)
121
121
  end
122
122
 
123
+ if parameters[:delivery_address]
124
+ parameters[:delivery_address_sig] = calculate_delivery_address_signature(parameters, shared_secret)
125
+ end
126
+
123
127
  if parameters[:shopper]
124
128
  parameters[:shopper_sig] = calculate_shopper_signature(parameters, shared_secret)
125
129
  end
126
130
 
131
+ if parameters[:openinvoicedata]
132
+ parameters[:openinvoicedata][:sig] = calculate_open_invoice_signature(parameters, shared_secret)
133
+ end
134
+
127
135
  return parameters
128
136
  end
129
137
 
@@ -225,14 +233,15 @@ module Adyen
225
233
  # @return [String] The string for which the siganture is calculated.
226
234
  def calculate_signature_string(parameters)
227
235
  merchant_sig_string = ""
228
- merchant_sig_string << parameters[:payment_amount].to_s << parameters[:currency_code].to_s <<
229
- parameters[:ship_before_date].to_s << parameters[:merchant_reference].to_s <<
230
- parameters[:skin_code].to_s << parameters[:merchant_account].to_s <<
231
- parameters[:session_validity].to_s << parameters[:shopper_email].to_s <<
232
- parameters[:shopper_reference].to_s << parameters[:recurring_contract].to_s <<
233
- parameters[:allowed_methods].to_s << parameters[:blocked_methods].to_s <<
234
- parameters[:shopper_statement].to_s << parameters[:merchant_return_data].to_s <<
235
- parameters[:billing_address_type].to_s << parameters[:offset].to_s
236
+ merchant_sig_string << parameters[:payment_amount].to_s << parameters[:currency_code].to_s <<
237
+ parameters[:ship_before_date].to_s << parameters[:merchant_reference].to_s <<
238
+ parameters[:skin_code].to_s << parameters[:merchant_account].to_s <<
239
+ parameters[:session_validity].to_s << parameters[:shopper_email].to_s <<
240
+ parameters[:shopper_reference].to_s << parameters[:recurring_contract].to_s <<
241
+ parameters[:allowed_methods].to_s << parameters[:blocked_methods].to_s <<
242
+ parameters[:shopper_statement].to_s << parameters[:merchant_return_data].to_s <<
243
+ parameters[:billing_address_type].to_s << parameters[:delivery_address_type].to_s <<
244
+ parameters[:shopper_type].to_s << parameters[:offset].to_s
236
245
  end
237
246
 
238
247
  # Calculates the payment request signature for the given payment parameters.
@@ -265,6 +274,16 @@ module Adyen
265
274
  end.join
266
275
  end
267
276
 
277
+ # Generates the string that is used to calculate the request signature. This signature
278
+ # is used by Adyen to check whether the request is genuinely originating from you.
279
+ # @param [Hash] parameters The parameters that will be included in the delivery address request.
280
+ # @return [String] The string for which the siganture is calculated.
281
+ def calculate_delivery_address_signature_string(parameters)
282
+ %w(street house_number_or_name city postal_code state_or_province country).map do |key|
283
+ parameters[key.to_sym]
284
+ end.join
285
+ end
286
+
268
287
  # Calculates the billing address request signature for the given billing address parameters.
269
288
  #
270
289
  # This signature is used by Adyen to check whether the request is
@@ -285,6 +304,26 @@ module Adyen
285
304
  Adyen::Util.hmac_base64(shared_secret, calculate_billing_address_signature_string(parameters[:billing_address]))
286
305
  end
287
306
 
307
+ # Calculates the delivery address request signature for the given delivery address parameters.
308
+ #
309
+ # This signature is used by Adyen to check whether the request is
310
+ # genuinely originating from you. The resulting signature should be
311
+ # included in the delivery address request parameters as the +deliveryAddressSig+
312
+ # parameter; the shared secret should of course not be included.
313
+ #
314
+ # @param [Hash] parameters The delivery address parameters for which to calculate
315
+ # the delivery address request signature.
316
+ # @param [String] shared_secret The shared secret to use for this signature.
317
+ # It should correspond with the skin_code parameter. This parameter can be
318
+ # left out if the shared_secret is included as key in the parameters.
319
+ # @return [String] The signature of the delivery address request
320
+ # @raise [ArgumentError] Thrown if shared_secret is empty
321
+ def calculate_delivery_address_signature(parameters, shared_secret = nil)
322
+ shared_secret ||= parameters.delete(:shared_secret)
323
+ raise ArgumentError, "Cannot calculate delivery address request signature with empty shared_secret" if shared_secret.to_s.empty?
324
+ Adyen::Util.hmac_base64(shared_secret, calculate_delivery_address_signature_string(parameters[:delivery_address]))
325
+ end
326
+
288
327
  # shopperSig: shopper.firstName + shopper.infix + shopper.lastName + shopper.gender + shopper.dateOfBirthDayOfMonth + shopper.dateOfBirthMonth + shopper.dateOfBirthYear + shopper.telephoneNumber
289
328
  # (Note that you can send only shopper.firstName and shopper.lastName if you like. Do NOT include shopperSocialSecurityNumber in the shopperSig!)
290
329
  def calculate_shopper_signature_string(parameters)
@@ -299,6 +338,19 @@ module Adyen
299
338
  Adyen::Util.hmac_base64(shared_secret, calculate_shopper_signature_string(parameters[:shopper]))
300
339
  end
301
340
 
341
+ def calculate_open_invoice_signature_string(merchant_sig, parameters)
342
+ flattened = Adyen::Util.flatten(:merchant_sig => merchant_sig, :openinvoicedata => parameters)
343
+ pairs = flattened.to_a.sort
344
+ pairs.transpose.map { |it| it.join(':') }.join('|')
345
+ end
346
+
347
+ def calculate_open_invoice_signature(parameters, shared_secret = nil)
348
+ shared_secret ||= parameters.delete(:shared_secret)
349
+ raise ArgumentError, "Cannot calculate open invoice request signature with empty shared_secret" if shared_secret.to_s.empty?
350
+ merchant_sig = calculate_signature(parameters, shared_secret)
351
+ Adyen::Util.hmac_base64(shared_secret, calculate_open_invoice_signature_string(merchant_sig, parameters[:openinvoicedata]))
352
+ end
353
+
302
354
  ######################################################
303
355
  # REDIRECT SIGNATURE CHECKING
304
356
  ######################################################
@@ -0,0 +1,66 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Adyen
5
+ module HPP
6
+ # The Signature module can sign and verify HMAC SHA-256 signatures for Hosted Payment Pages
7
+ module Signature
8
+ extend self
9
+
10
+ # Sign the parameters with the given shared secret
11
+ # @param [Hash] params The set of parameters to sign
12
+ # @param [String] shared_secret The shared secret for signing/verification. Can also be sent in the
13
+ # params hash with the `sharedSecret` key.
14
+ # @return [Hash] params The params that were passed in plus a new `merchantSig` param
15
+ def sign(params, shared_secret = nil)
16
+ shared_secret ||= params.delete['sharedSecret']
17
+ raise ArgumentError, "Cannot verify a signature without a shared secret" unless shared_secret
18
+ sig = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), Array(shared_secret).pack("H*"), string_to_sign(params))
19
+ params.merge('merchantSig' => Base64.encode64(sig).strip)
20
+ end
21
+
22
+ # Verify the parameters with the given shared secret
23
+ # @param [Hash] params The set of parameters to verify. Must include a `merchantSig`
24
+ # param that will be compared to the signature we calculate.
25
+ # @param [String] shared_secret The shared secret for signing/verification. Can also be sent in the
26
+ # params hash with the `sharedSecret` key.
27
+ # @return [Boolean] true if the `merchantSig` in the params matches our calculated signature
28
+ def verify(params, shared_secret = nil)
29
+ their_sig = params.delete('merchantSig')
30
+ raise ArgumentError, "params must include 'merchantSig' for verification" if their_sig.empty?
31
+ our_sig = sign(params, shared_secret)['merchantSig']
32
+ secure_compare(their_sig, our_sig)
33
+ end
34
+
35
+ private
36
+
37
+ def string_to_sign(params)
38
+ (sorted_keys(params) + sorted_values(params)).map{ |el| escape_value(el) }.join(':')
39
+ end
40
+
41
+ def sorted_keys(hash)
42
+ hash.sort.map{ |el| el[0] }
43
+ end
44
+
45
+ def sorted_values(hash)
46
+ hash.sort.map{ |el| el[1] }
47
+ end
48
+
49
+ def escape_value(value)
50
+ value.gsub(':', '\\:').gsub('\\', '\\\\')
51
+ end
52
+
53
+ # Constant-time compare for two fixed-length strings
54
+ # Stolen from https://github.com/rails/rails/commit/c8c660002f4b0e9606de96325f20b95248b6ff2d
55
+ def secure_compare(a, b)
56
+ return false unless a.bytesize == b.bytesize
57
+
58
+ l = a.unpack "C#{a.bytesize}"
59
+
60
+ res = 0
61
+ b.each_byte { |byte| res |= byte ^ l.shift }
62
+ res == 0
63
+ end
64
+ end
65
+ end
66
+ end
@@ -16,7 +16,6 @@
16
16
  # @invoice.set_paid!
17
17
  # end
18
18
  class AdyenNotification < ActiveRecord::Base
19
-
20
19
  # A notification should always include an event_code
21
20
  validates_presence_of :event_code
22
21
 
@@ -37,8 +36,6 @@ class AdyenNotification < ActiveRecord::Base
37
36
  # @raise This method will raise an exception if the notification cannot be stored.
38
37
  # @see Adyen::Notification::HttpPost.log
39
38
  def self.log(params)
40
- converted_params = {}
41
-
42
39
  # Assign explicit each attribute from CamelCase notation to notification
43
40
  # For example, merchantReference will be converted to merchant_reference
44
41
  self.new.tap do |notification|
@@ -46,6 +43,7 @@ class AdyenNotification < ActiveRecord::Base
46
43
  setter = "#{key.to_s.underscore}="
47
44
  notification.send(setter, value) if notification.respond_to?(setter)
48
45
  end
46
+
49
47
  notification.save!
50
48
  end
51
49
  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.0.0.pre2"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -29,11 +29,32 @@ class FormTest < Minitest::Test
29
29
  :state_or_province => 'Berlin',
30
30
  :country => 'Germany',
31
31
  },
32
+ :delivery_address => {
33
+ :street => 'Pecunialaan',
34
+ :house_number_or_name => '316',
35
+ :city => 'Geldrop',
36
+ :state_or_province => 'None',
37
+ :postal_code => '1234 AB',
38
+ :country => 'Netherlands',
39
+ },
32
40
  :shopper => {
33
41
  :telephone_number => '1234512345',
34
42
  :first_name => 'John',
35
43
  :last_name => 'Doe',
36
44
  :social_security_number => '123-45-1234'
45
+ },
46
+ :openinvoicedata => {
47
+ :number_of_lines => 1,
48
+ :line1 => {
49
+ :number_of_items => 2,
50
+ :item_amount => 4000,
51
+ :currency_code => 'GBP',
52
+ :item_vat_amount => 1000,
53
+ :item_vat_percentage => 2500,
54
+ :item_vat_category => 'High',
55
+ :description => 'Product Awesome'
56
+ },
57
+ :refund_description => 'Refund for 12345'
37
58
  }
38
59
  }
39
60
 
@@ -122,6 +143,12 @@ class FormTest < Minitest::Test
122
143
 
123
144
  signature_string = Adyen::Form.calculate_signature_string(@recurring_payment_attributes)
124
145
  assert_equal "10000GBP2007-10-20Internet Order 12345sk1nC0deOtherMerchant2007-10-11T11:00:00Zgras.shopper@somewhere.orggrasshopper52DEFAULT", signature_string
146
+
147
+ signature_string = Adyen::Form.calculate_signature_string(@payment_attributes.merge(:billing_address_type => '1', :delivery_address_type => '2'))
148
+ assert_equal "10000GBP2007-10-20Internet Order 123454aD37dJATestMerchant2007-10-11T11:00:00Z12", signature_string
149
+
150
+ signature_string = Adyen::Form.calculate_signature_string(@payment_attributes.merge(:delivery_address_type => '2', :shopper_type => '1'))
151
+ assert_equal "10000GBP2007-10-20Internet Order 123454aD37dJATestMerchant2007-10-11T11:00:00Z21", signature_string
125
152
  end
126
153
 
127
154
  def test_redirect_signature
@@ -150,10 +177,58 @@ class FormTest < Minitest::Test
150
177
  assert_raises(ArgumentError) { Adyen::Form.calculate_billing_address_signature(@payment_attributes) }
151
178
  end
152
179
 
153
- def test_billing_address_and_shopper_signature_in_redirect_url
180
+ def test_delivery_address_signature
181
+ signature_string = Adyen::Form.calculate_delivery_address_signature_string(@payment_attributes[:delivery_address])
182
+ assert_equal "Pecunialaan316Geldrop1234 ABNoneNetherlands", signature_string
183
+ assert_equal 'g8wPEWYrDPatkGXzuQbN1++JVbE=', Adyen::Form.calculate_delivery_address_signature(@payment_attributes)
184
+
185
+ @payment_attributes.delete(:shared_secret)
186
+ assert_raises(ArgumentError) { Adyen::Form.calculate_delivery_address_signature(@payment_attributes) }
187
+ end
188
+
189
+ def test_open_invoice_signature
190
+ merchant_sig = Adyen::Form.calculate_signature(@payment_attributes, @payment_attributes[:shared_secret])
191
+ signature_string = Adyen::Form.calculate_open_invoice_signature_string(merchant_sig, @payment_attributes[:openinvoicedata])
192
+ expected_string =
193
+ [
194
+ 'merchantSig',
195
+ 'openinvoicedata.line1.currencyCode',
196
+ 'openinvoicedata.line1.description',
197
+ 'openinvoicedata.line1.itemAmount',
198
+ 'openinvoicedata.line1.itemVatAmount',
199
+ 'openinvoicedata.line1.itemVatCategory',
200
+ 'openinvoicedata.line1.itemVatPercentage',
201
+ 'openinvoicedata.line1.numberOfItems',
202
+ 'openinvoicedata.numberOfLines',
203
+ 'openinvoicedata.refundDescription'
204
+ ].join(':') +
205
+ '|' +
206
+ [
207
+ merchant_sig,
208
+ 'GBP',
209
+ 'Product Awesome',
210
+ 4000,
211
+ 1000,
212
+ 'High',
213
+ 2500,
214
+ 2,
215
+ 1,
216
+ 'Refund for 12345'
217
+ ].join(':')
218
+
219
+ assert_equal expected_string, signature_string
220
+ assert_equal 'OI71VGB7G3vKBRrtE6Ibv+RWvYY=', Adyen::Form.calculate_open_invoice_signature(@payment_attributes)
221
+
222
+ @payment_attributes.delete(:shared_secret)
223
+ assert_raises(ArgumentError) { Adyen::Form.calculate_open_invoice_signature(@payment_attributes) }
224
+ end
225
+
226
+ def test_billing_signatures_in_redirect_url
154
227
  get_params = CGI.parse(URI(Adyen::Form.redirect_url(@payment_attributes)).query)
155
228
  assert_equal '5KQb7VJq4cz75cqp11JDajntCY4=', get_params['billingAddressSig'].first
229
+ assert_equal 'g8wPEWYrDPatkGXzuQbN1++JVbE=', get_params['deliveryAddressSig'].first
156
230
  assert_equal 'rb2GEs1kGKuLh255a3QRPBYXmsQ=', get_params['shopperSig'].first
231
+ assert_equal 'OI71VGB7G3vKBRrtE6Ibv+RWvYY=', get_params['openinvoicedata.sig'].first
157
232
  end
158
233
 
159
234
  def test_redirect_signature_check
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class SignatureTest < Minitest::Test
4
+ def setup
5
+ # values from https://docs.adyen.com/pages/viewpage.action?pageId=5376964
6
+ @shared_secret = "4468D9782DEF54FCD706C9100C71EC43932B1EBC2ACF6BA0560C05AAA7550C48"
7
+
8
+ @expected_sig = 'GJ1asjR5VmkvihDJxCd8yE2DGYOKwWwJCBiV3R51NFg='
9
+
10
+ @raw_params = {
11
+ 'merchantAccount' => 'TestMerchant',
12
+ 'currencyCode' => 'EUR',
13
+ 'paymentAmount' => '199',
14
+ 'sessionValidity' => '2015-06-25T10:31:06Z',
15
+ 'shipBeforeDate' => '2015-07-01',
16
+ 'shopperLocale' => 'en_GB',
17
+ 'merchantReference' => 'SKINTEST-1435226439255',
18
+ 'skinCode' => 'X7hsNDWp',
19
+ }
20
+ end
21
+
22
+ def test_sign
23
+ signed_params = Adyen::HPP::Signature.sign(@raw_params, @shared_secret)
24
+ assert_equal @expected_sig, signed_params['merchantSig']
25
+ end
26
+
27
+ def test_verify_succeeds_with_same_secret
28
+ signed_params = @raw_params.merge('merchantSig' => @expected_sig)
29
+ assert_equal true, Adyen::HPP::Signature.verify(signed_params, @shared_secret)
30
+ end
31
+
32
+ def test_verification_fails_with_different_secret
33
+ signed_params = @raw_params.merge('merchantSig' => @expected_sig)
34
+ assert_equal false, Adyen::HPP::Signature.verify(signed_params, '12345')
35
+ end
36
+ 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.0.0.pre2
4
+ version: 2.0.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: 2015-03-06 00:00:00.000000000 Z
14
+ date: 2016-05-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rake
@@ -165,6 +165,7 @@ files:
165
165
  - lib/adyen/api/xml_querier.rb
166
166
  - lib/adyen/configuration.rb
167
167
  - lib/adyen/form.rb
168
+ - lib/adyen/hpp/signature.rb
168
169
  - lib/adyen/matchers.rb
169
170
  - lib/adyen/notification_generator.rb
170
171
  - lib/adyen/railtie.rb
@@ -204,6 +205,7 @@ files:
204
205
  - test/helpers/views/index.erb
205
206
  - test/helpers/views/pay.erb
206
207
  - test/helpers/views/redirect_shopper.erb
208
+ - test/hpp/signature_test.rb
207
209
  - test/integration/hpp_integration_test.rb
208
210
  - test/integration/payment_using_3d_secure_integration_test.rb
209
211
  - test/integration/payment_with_client_side_encryption_integration_test.rb
@@ -234,13 +236,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
234
236
  version: 1.9.3
235
237
  required_rubygems_version: !ruby/object:Gem::Requirement
236
238
  requirements:
237
- - - '>'
239
+ - - '>='
238
240
  - !ruby/object:Gem::Version
239
- version: 1.3.1
241
+ version: '0'
240
242
  requirements:
241
243
  - Having Nokogiri installed will speed up XML handling when using the SOAP API.
242
244
  rubyforge_project:
243
- rubygems_version: 2.0.14
245
+ rubygems_version: 2.0.14.1
244
246
  signing_key:
245
247
  specification_version: 4
246
248
  summary: Integrate Adyen payment services in your Ruby on Rails application.
@@ -269,6 +271,7 @@ test_files:
269
271
  - test/helpers/views/index.erb
270
272
  - test/helpers/views/pay.erb
271
273
  - test/helpers/views/redirect_shopper.erb
274
+ - test/hpp/signature_test.rb
272
275
  - test/integration/hpp_integration_test.rb
273
276
  - test/integration/payment_using_3d_secure_integration_test.rb
274
277
  - test/integration/payment_with_client_side_encryption_integration_test.rb