adyen 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +12 -5
  4. data/CHANGELOG.md +5 -2
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile +4 -0
  7. data/README.md +0 -2
  8. data/Rakefile +2 -8
  9. data/adyen.gemspec +4 -5
  10. data/lib/adyen/api.rb +14 -10
  11. data/lib/adyen/api/payment_service.rb +35 -4
  12. data/lib/adyen/api/templates/payment_service.rb +5 -0
  13. data/lib/adyen/form.rb +1 -1
  14. data/lib/adyen/hpp/request.rb +8 -6
  15. data/lib/adyen/rest.rb +21 -8
  16. data/lib/adyen/rest/client.rb +13 -0
  17. data/lib/adyen/templates/notification_migration.rb +1 -1
  18. data/lib/adyen/templates/notification_model.rb +2 -2
  19. data/lib/adyen/version.rb +1 -1
  20. data/{spec/functional/api_spec.rb → test/functional/api_test.rb} +38 -33
  21. data/{spec → test}/functional/initializer.rb.ci +0 -0
  22. data/{spec → test}/functional/initializer.rb.sample +0 -0
  23. data/test/helpers/views/hpp.erb +15 -15
  24. data/test/test_helper.rb +1 -0
  25. data/{spec/api/api_spec.rb → test/unit/api/api_test.rb} +7 -7
  26. data/{spec/api/payment_service_spec.rb → test/unit/api/payment_service_test.rb} +149 -117
  27. data/{spec/api/recurring_service_spec.rb → test/unit/api/recurring_service_test.rb} +49 -46
  28. data/{spec/api/response_spec.rb → test/unit/api/response_test.rb} +12 -12
  29. data/{spec/api/simple_soap_client_spec.rb → test/unit/api/simple_soap_client_test.rb} +27 -30
  30. data/{spec/api/spec_helper.rb → test/unit/api/test_helper.rb} +44 -35
  31. data/{spec/api/test_helpers_spec.rb → test/unit/api/test_helpers_test.rb} +11 -11
  32. data/test/{form_test.rb → unit/form_test.rb} +0 -0
  33. data/test/unit/hpp/request_test.rb +68 -0
  34. data/test/{hpp → unit/hpp}/signature_test.rb +0 -0
  35. data/test/{hpp_test.rb → unit/hpp_test.rb} +0 -0
  36. data/test/{rest → unit/rest}/signature_test.rb +0 -0
  37. data/test/{rest_list_recurring_details_response_test.rb → unit/rest_list_recurring_details_response_test.rb} +0 -0
  38. data/test/{rest_request_test.rb → unit/rest_request_test.rb} +0 -0
  39. data/test/{rest_response_test.rb → unit/rest_response_test.rb} +0 -0
  40. data/test/{signature_test.rb → unit/signature_test.rb} +0 -0
  41. data/test/{util_test.rb → unit/util_test.rb} +0 -0
  42. metadata +53 -61
  43. data/spec/spec_helper.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79d7e1124e75e3e161c514b3d4fe8eab7058bec4
4
- data.tar.gz: 97a72df3756f9d5ab4385b17f9db517c0c67d762
3
+ metadata.gz: f8ef547799ff5fa479e0adb223c8e325b394e064
4
+ data.tar.gz: 964b4e93c00f7448a073aa9423497713ee480dfd
5
5
  SHA512:
6
- metadata.gz: 870d40ed54714492339b6945c61acf0069d4d90a82e547e8b0c0fc69075e49b2c568b0ab25504e8efdddb7a61126fe4934c776e2003340279970167ad987f46b
7
- data.tar.gz: 61da208663797528ecd1c8e2beaf1fd9c3bf895dead0a13833bf27b4114880a799ece1e1fb84dc77356e937fcf01a34817506c2f665099e8f32c88d2ad882ebf
6
+ metadata.gz: 6c0d7a296dd0697404034dbc655a7b53fb1e3cba9443fe6a821203db33fe307ec4b21b58b1196c853d486eec05b8bf6d15c5f9152078a939d5fca0779664b010
7
+ data.tar.gz: 62422891b4307c4b6e1a872cfe17223bb323c1163df1ce47920f77bd0990ce4432fd5189ad9e55463dcd10a99d769956c1f4a3f0114134b6c2b80d6322ff1210
data/.gitignore CHANGED
@@ -10,5 +10,6 @@ adyen-*.gem
10
10
  *.swo
11
11
  .DS_Store
12
12
  t.rb
13
- spec/functional/initializer.rb
13
+ test/functional/initializer.rb
14
14
  Gemfile.lock
15
+ .ruby-*
@@ -1,30 +1,37 @@
1
+ sudo: false
2
+ dist: trusty
1
3
  language: ruby
2
4
  cache: bundler
3
- script: bundle exec rake
4
5
  rvm:
5
6
  - "2.0"
6
7
  - "2.1"
7
8
  - "2.2"
8
9
  - "2.3.1"
10
+ - "2.4.1"
9
11
  - "jruby-9.0"
12
+ - "jruby-9.1"
10
13
  - "ruby-head"
11
- - "rbx-2"
14
+ - "rubinius-3"
12
15
  - "jruby-head"
13
16
  matrix:
14
17
  allow_failures:
15
18
  - rvm: jruby-head
16
19
  - rvm: ruby-head
17
- - rvm: rbx-2
20
+ - rvm: rubinius-3
21
+
18
22
  before_install:
19
23
  - bundle --version || gem install bundler
24
+ - gem update bundler
20
25
  before_script:
21
- - cp spec/functional/initializer.rb.ci spec/functional/initializer.rb
26
+ - cp test/functional/initializer.rb.ci test/functional/initializer.rb
27
+ script: bundle exec rake
28
+
22
29
  env:
23
30
  global:
24
31
  - ADYEN_MERCHANT_ACCOUNT: "VanBergenORG"
25
32
  - ADYEN_API_USERNAME: "ws@Company.VanBergen"
26
33
  - ADYEN_API_PASSWORD: "7phtHzbfnzsp"
27
- sudo: false
34
+
28
35
  branches:
29
36
  only:
30
37
  - master
@@ -2,9 +2,12 @@
2
2
 
3
3
  The following changes have been made to the library over the years. Pleae add an entry to this file as part of your pull requests.
4
4
 
5
- #### Unrelease changes
5
+ #### Version 2.3.0
6
6
 
7
- Nothing yet!
7
+ - Add `shopper_statement` option to `Adyen::APP`, allowing to generate Billets with custom payment instructions.
8
+ - Make sure the unique index created by the notification model generator includes `merchant_account_code`
9
+ - Make refusal_reson availble as a response field for failed requests.
10
+ - Updates to the testing in frastructure
8
11
 
9
12
  #### Version 2.2.0
10
13
 
@@ -6,7 +6,7 @@ This projects welcomes outside contributions from anyone.
6
6
 
7
7
  Please report bugs as a [Github issue](https://github.com/wvanbergen/adyen/issues/new).
8
8
 
9
- - We are not associated with Adyen. Please contact Adyen youself if you are having
9
+ - We are not associated with Adyen. Please contact Adyen yourself if you are having
10
10
  trouble with your integration.
11
11
  - This library supports several features that are not supported by default on a new
12
12
  Adyen account. You may have to contact Adyen if you are receiving a
data/Gemfile CHANGED
@@ -1,6 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ ruby RUBY_VERSION
5
+
6
+ gem 'pry'
7
+
4
8
  platform :rbx do
5
9
  gem 'rubysl'
6
10
  gem 'racc'
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Package to simplify including Adyen payments services into a Ruby on Rails application.
4
4
 
5
- > Note: this library is corrently undergoing a major rewrite. Information in this README is about the still unreleased version of this library. If you want information about the last released version, check out version [1.6.0](https://github.com/wvanbergen/adyen/tree/v1.6.0).
6
-
7
5
  Adyen integration relies on three modes of communication between Adyen, your server and your client/customer:
8
6
 
9
7
  - Client-to-Adyen communication using Hosted Payment Pages (HPP).
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
2
  require "rake/testtask"
4
3
 
5
4
  namespace(:test) do
@@ -12,7 +11,7 @@ namespace(:test) do
12
11
  Rake::TestTask.new(:unit) do |t|
13
12
  t.description = "Run unit tests"
14
13
  t.libs << "test"
15
- t.test_files = FileList['test/*_test.rb']
14
+ t.test_files = FileList['test/unit/**/*_test.rb']
16
15
  end
17
16
 
18
17
  Rake::TestTask.new(:functional) do |t|
@@ -31,11 +30,6 @@ end
31
30
  desc "Run unit and functional tests"
32
31
  task :test => %w{test:unit test:functional}
33
32
 
34
- RSpec::Core::RakeTask.new(:spec) do |task|
35
- task.pattern = "./spec/**/*_spec.rb"
36
- task.rspec_opts = ['--color']
37
- end
38
-
39
33
  CACERT_PATH = 'lib/adyen/api/cacert.pem'
40
34
 
41
35
  desc 'Update CA root certificates for the simple SOAP client'
@@ -51,4 +45,4 @@ end
51
45
  # sh "git diff-index --quiet HEAD #{CACERT_PATH} || (git add #{CACERT_PATH} && git commit -m '[API] Update CA root certificates file.')"
52
46
  # end
53
47
 
54
- task :default => %w{test spec}
48
+ task :default => %w{test}
@@ -24,14 +24,13 @@ Gem::Specification.new do |s|
24
24
  s.license = 'MIT'
25
25
 
26
26
  s.add_development_dependency('rake')
27
- s.add_development_dependency('rspec', '~> 2.14')
28
- s.add_development_dependency('minitest', '~> 5')
27
+ s.add_development_dependency('minitest', '~> 5.0')
29
28
  s.add_development_dependency('mocha')
30
29
  s.add_development_dependency('sinatra')
31
30
  s.add_development_dependency('poltergeist')
32
31
 
33
- s.add_development_dependency('rails', '>= 3.2')
34
- s.add_development_dependency('nokogiri', '>= 1.6.1')
32
+ s.add_development_dependency('railties', '>= 3.2', '< 5.2')
33
+ s.add_development_dependency('nokogiri', '>= 1.6.8')
35
34
 
36
35
  s.requirements << 'Having Nokogiri installed will speed up XML handling when using the SOAP API.'
37
36
 
@@ -39,5 +38,5 @@ Gem::Specification.new do |s|
39
38
  s.extra_rdoc_files = ['README.md', 'CHANGELOG.md', 'CONTRIBUTING.md']
40
39
 
41
40
  s.files = `git ls-files`.split($/)
42
- s.test_files = s.files.grep(%r{^(test|spec|features)/})
41
+ s.test_files = s.files.grep(%r{^test/})
43
42
  end
@@ -49,7 +49,7 @@ module Adyen
49
49
  module API
50
50
  extend self
51
51
 
52
- # Generate a Billet - *Brazian users only*
52
+ # Generate a Billet - *Brazillian users only*
53
53
  #
54
54
  # Billet (Boleto Bancário), often simply referred to as Boleto, is an
55
55
  # offline payment method used in Brazil . The consumer will take the Boleto form to
@@ -66,17 +66,20 @@ module Adyen
66
66
  # { currency: "BRL", value: (invoice.amount).to_i },
67
67
  # { first_name: "Simon", last_name: "Hopper" },
68
68
  # document_number,
69
- # selected_brand
69
+ # selected_brand,
70
+ # "2016-10-29T23:00:00.000Z",
71
+ # "Please send payment email to payment@example.com after payment."
70
72
  # )
71
73
  # response.success? # => true
72
74
  #
73
75
  #
74
- # @param [Numeric,String] reference Your reference (ID) for this payment.
75
- # @param [Hash] amount A hash describing the money to charge.
76
- # @param [Hash] shopper A hash describing the shopper.
77
- # @param [String] document_number Social Security
78
- # number(CPF in Brazil)
79
- # @param [String] selected_brand Billet brand
76
+ # @param [Numeric,String] reference Your reference (ID) for this payment.
77
+ # @param [Hash] amount A hash describing the money to charge.
78
+ # @param [Hash] shopper_name A hash describing the shopper.
79
+ # @param [String] document_number Social Security number (CPF in Brazil)
80
+ # @param [String] selected_brand Billet brand
81
+ # @param [String] delivery_date Payment date limit in ISO8601 format
82
+ # @param optional [String] shopper_statement Payment instructions to Shopper
80
83
  #
81
84
  # @option amount [String] :currency The ISO currency code (EUR, GBP, USD, etc).
82
85
  # @option amount [Integer] :value The value of the payment in discrete cents,
@@ -88,13 +91,14 @@ module Adyen
88
91
  #
89
92
  # @return [PaymentService::BilletResponse] The response object which holds the billet url.
90
93
  #
91
- def generate_billet(reference, amount, shopper_name, social_security_number, selected_brand, delivery_date)
94
+ def generate_billet(reference, amount, shopper_name, social_security_number, selected_brand, delivery_date, shopper_statement = nil)
92
95
  params = { :reference => reference,
93
96
  :amount => amount,
94
97
  :shopper_name => shopper_name,
95
98
  :social_security_number => social_security_number,
96
99
  :selected_brand => selected_brand,
97
- :delivery_date => delivery_date }
100
+ :delivery_date => delivery_date,
101
+ :shopper_statement => shopper_statement }
98
102
  PaymentService.new(params).generate_billet
99
103
  end
100
104
 
@@ -126,6 +126,7 @@ module Adyen
126
126
  content << shopper_name_partial if @params[:shopper_name]
127
127
  content << delivery_date_partial if @params[:delivery_date]
128
128
  content << selected_brand_partial if @params[:selected_brand]
129
+ content << shopper_statement_partial if @params[:shopper_statement]
129
130
  LAYOUT % [@params[:merchant_account], @params[:reference], content]
130
131
  end
131
132
 
@@ -210,6 +211,12 @@ module Adyen
210
211
  @params[:shopper].map { |k, v| SHOPPER_PARTIALS[k] % v }.join("\n")
211
212
  end
212
213
 
214
+ def shopper_statement_partial
215
+ if @params[:shopper_statement]
216
+ SHOPPER_STATEMENT % @params[:shopper_statement]
217
+ end
218
+ end
219
+
213
220
  def fraud_offset_partial
214
221
  validate_parameters!(:fraud_offset)
215
222
  FRAUD_OFFSET_PARTIAL % @params[:fraud_offset]
@@ -222,7 +229,9 @@ module Adyen
222
229
  class BilletResponse < Response
223
230
  RECEIVED = "Received"
224
231
 
225
- response_attrs :result_code, :billet_url, :psp_reference
232
+ response_attrs :result_code, :billet_url, :psp_reference,
233
+ :barcode, :due_date, :expiration_date,
234
+ :refusal_reason
226
235
 
227
236
  def success?
228
237
  super && params[:result_code] == RECEIVED
@@ -230,10 +239,24 @@ module Adyen
230
239
 
231
240
  def params
232
241
  @params ||= xml_querier.xpath('//payment:authoriseResponse/payment:paymentResult') do |result|
242
+ result_code = result.text('./payment:resultCode')
243
+ attributes = Hash.new('')
244
+
245
+ if result_code == RECEIVED
246
+ attributes = result.children[0].children.map do |child|
247
+ { child.children[0].text => child.children[1].text }
248
+ end
249
+ attributes = attributes.reduce({}, :merge)
250
+ end
251
+
233
252
  {
234
- :psp_reference => result.text('./payment:pspReference'),
235
- :result_code => result_code = result.text('./payment:resultCode'),
236
- :billet_url => (result_code == RECEIVED) ? result.children[0].children[0].children[1].text : ""
253
+ :psp_reference => result.text('./payment:pspReference'),
254
+ :result_code => result_code,
255
+ :billet_url => attributes['boletobancario.url'],
256
+ :barcode => attributes['boletobancario.barCodeReference'],
257
+ :due_date => convert_to_date(attributes['boletobancario.dueDate']),
258
+ :expiration_date => convert_to_date(attributes['boletobancario.expirationDate']),
259
+ :refusal_reason => (invalid_request? ? fault_message : result.text('./payment:refusalReason'))
237
260
  }
238
261
  end
239
262
  end
@@ -241,6 +264,14 @@ module Adyen
241
264
  def invalid_request?
242
265
  !fault_message.nil?
243
266
  end
267
+
268
+ private
269
+
270
+ def convert_to_date(value)
271
+ Date.parse(value)
272
+ rescue ArgumentError
273
+ nil
274
+ end
244
275
  end
245
276
 
246
277
  class AuthorisationResponse < Response
@@ -106,6 +106,11 @@ module Adyen
106
106
  </payment:shopperName>
107
107
  EOXML
108
108
 
109
+ # @private
110
+ SHOPPER_STATEMENT = <<-EOXML
111
+ <payment:shopperStatement>%s</payment:shopperStatement>
112
+ EOXML
113
+
109
114
  # @private
110
115
  ENCRYPTED_CARD_PARTIAL = <<-EOXML
111
116
  <additionalAmount xmlns="http://payment.services.adyen.com" xsi:nil="true" />
@@ -47,7 +47,7 @@ module Adyen
47
47
  # @see Adyen::Form.redirect_url
48
48
  def domain(environment = nil)
49
49
  environment ||= Adyen.configuration.environment
50
- (Adyen.configuration.payment_flow_domain || ACTION_DOMAIN) % [environment.to_s]
50
+ Adyen.configuration.payment_flow_domain || ACTION_DOMAIN % [environment.to_s]
51
51
  end
52
52
 
53
53
  # Returns the URL of the Adyen payment system, adjusted for an Adyen environment.
@@ -1,4 +1,5 @@
1
1
  require 'adyen/hpp/signature'
2
+ require 'adyen/util'
2
3
  require 'cgi'
3
4
 
4
5
  module Adyen
@@ -8,6 +9,8 @@ module Adyen
8
9
  attr_accessor :parameters
9
10
  attr_writer :skin, :environment, :shared_secret
10
11
 
12
+ MANDATORY_ATTRIBUTES = %i(currency_code payment_amount merchant_account skin_code ship_before_date session_validity).freeze
13
+
11
14
  # Initialize the HPP request
12
15
  #
13
16
  # @param [Hash] parameters The payment parameters
@@ -52,7 +55,7 @@ module Adyen
52
55
  # for payment forms or redirects.
53
56
  # @see Adyen::HPP::Request.redirect_url
54
57
  def domain
55
- (Adyen.configuration.payment_flow_domain || HPP_DOMAIN) % [environment.to_s]
58
+ Adyen.configuration.payment_flow_domain || HPP_DOMAIN % [environment.to_s]
56
59
  end
57
60
 
58
61
  # Returns the URL of the Adyen payment system, adjusted for an Adyen environment.
@@ -87,10 +90,9 @@ module Adyen
87
90
  end
88
91
  formatted_parameters = default_form_parameters.merge(formatted_parameters)
89
92
 
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]
93
+ MANDATORY_ATTRIBUTES.each do |attribute|
94
+ raise ArgumentError, "Cannot generate request: :#{attribute} attribute not found!" unless formatted_parameters[attribute]
95
+ end
94
96
 
95
97
  formatted_parameters[:recurring_contract] = 'RECURRING' if formatted_parameters.delete(:recurring) == true
96
98
  formatted_parameters[:order_data] = Adyen::Util.gzip_base64(formatted_parameters.delete(:order_data_raw)) if formatted_parameters[:order_data_raw]
@@ -189,4 +191,4 @@ module Adyen
189
191
  end
190
192
  end
191
193
  end
192
- end
194
+ end
@@ -9,7 +9,11 @@ module Adyen
9
9
  # The primary method here is {Adyen::REST.session}, which will yield a
10
10
  # {Adyen::REST::Client} which you can use to send API requests.
11
11
  #
12
- # @example
12
+ # If you need more than one client instance, for instance because you
13
+ # have multiple acounts set up with different permissions, you can instantiate
14
+ # clients yourself using {Adyen::REST::Client.new}
15
+ #
16
+ # @example Using the singleton Client instance
13
17
  #
14
18
  # Adyen::REST.session do |client|
15
19
  # client.http.read_timeout = 5
@@ -17,14 +21,26 @@ module Adyen
17
21
  # # ...
18
22
  # end
19
23
  #
24
+ # @example Using a your own Client instance
25
+ #
26
+ # Adyen::REST::Client.new('test', 'username', 'password').session do |client|
27
+ # client.http.read_timeout = 5
28
+ # response = client.api_request(...)
29
+ # # ...
30
+ # end
31
+ #
32
+ #
20
33
  # @see Adyen::REST.session Use Adyen::REST.session to run code against the API.
21
34
  # @see Adyen::REST::Client Adyen::REST::Client implements the actual API calls.
22
35
  module REST
23
36
 
24
- # Provides a REST API client this is configured using the values in <tt>Adyen.configuration</tt>.
37
+ # Provides a singelton REST API client this is configured using the values in
38
+ # <tt>Adyen.configuration</tt>.
39
+ #
25
40
  # @param options [Hash] (see Adyen::REST::Client#initialize)
26
41
  # @return [Adyen::REST::Client] A configured client instance
27
42
  # @see .session
43
+ # @see Adyen::REST::Client.new To instantiate Clients yourself, in case you need more than one.
28
44
  def self.client
29
45
  Adyen::REST::Client.new(
30
46
  Adyen.configuration.environment,
@@ -43,12 +59,9 @@ module Adyen
43
59
  # the provided client. The client will be closed after the block returns.
44
60
  # @yieldparam client [Adyen::REST::Client] The REST client to use for the session.
45
61
  # @return [void]
46
- # @see Adyen::REST::Client
47
- def self.session(client = nil)
48
- client ||= self.client
49
- yield(client)
50
- ensure
51
- client.close
62
+ # @see Adyen::REST::Client#session
63
+ def self.session(client = self.client, &block)
64
+ client.session(&block)
52
65
  end
53
66
  end
54
67
  end
@@ -42,6 +42,19 @@ module Adyen
42
42
  @http = nil
43
43
  end
44
44
 
45
+ # Runs a client session against the Adyen REST service for the duration of the block,
46
+ # and closes the connection afterwards.
47
+ #
48
+ # @yield The provided block will be called in which you can interact with the API using
49
+ # the provided client. The client will be closed after the block returns.
50
+ # @return [void]
51
+ def session
52
+ yield(self)
53
+ ensure
54
+ close
55
+ end
56
+
57
+
45
58
  # The underlying <tt>Net::HTTP</tt> instance that is used to execute HTTP
46
59
  # request against the API.
47
60
  #
@@ -20,7 +20,7 @@ class CreateAdyenNotifications < ActiveRecord::Migration
20
20
  t.timestamps
21
21
  end
22
22
 
23
- add_index :adyen_notifications, [:psp_reference, :event_code, :success], :unique => true, :name => 'adyen_notification_uniqueness'
23
+ add_index :adyen_notifications, [:merchant_account_code, :psp_reference, :event_code, :success], :unique => true, :name => 'adyen_notification_uniqueness'
24
24
  end
25
25
 
26
26
  def self.down