adyen 2.2.0 → 2.3.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.
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