adyen 0.3.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +4 -0
  2. data/.kick +35 -0
  3. data/LICENSE +3 -2
  4. data/README.rdoc +8 -4
  5. data/Rakefile +10 -0
  6. data/TODO +14 -4
  7. data/adyen.gemspec +9 -15
  8. data/lib/adyen.rb +10 -59
  9. data/lib/adyen/api.rb +281 -0
  10. data/lib/adyen/api/cacert.pem +3509 -0
  11. data/lib/adyen/api/payment_service.rb +258 -0
  12. data/lib/adyen/api/recurring_service.rb +126 -0
  13. data/lib/adyen/api/response.rb +54 -0
  14. data/lib/adyen/api/simple_soap_client.rb +118 -0
  15. data/lib/adyen/api/templates/payment_service.rb +103 -0
  16. data/lib/adyen/api/templates/recurring_service.rb +34 -0
  17. data/lib/adyen/api/test_helpers.rb +133 -0
  18. data/lib/adyen/api/xml_querier.rb +94 -0
  19. data/lib/adyen/configuration.rb +139 -0
  20. data/lib/adyen/form.rb +37 -109
  21. data/lib/adyen/formatter.rb +0 -10
  22. data/lib/adyen/matchers.rb +1 -1
  23. data/lib/adyen/notification_generator.rb +30 -0
  24. data/lib/adyen/railtie.rb +13 -0
  25. data/lib/adyen/templates/notification_migration.rb +29 -0
  26. data/lib/adyen/templates/notification_model.rb +70 -0
  27. data/spec/adyen_spec.rb +3 -45
  28. data/spec/api/api_spec.rb +139 -0
  29. data/spec/api/payment_service_spec.rb +439 -0
  30. data/spec/api/recurring_service_spec.rb +105 -0
  31. data/spec/api/response_spec.rb +35 -0
  32. data/spec/api/simple_soap_client_spec.rb +91 -0
  33. data/spec/api/spec_helper.rb +417 -0
  34. data/spec/api/test_helpers_spec.rb +83 -0
  35. data/spec/form_spec.rb +27 -23
  36. data/spec/functional/api_spec.rb +90 -0
  37. data/spec/functional/initializer.rb.sample +3 -0
  38. data/spec/spec_helper.rb +5 -5
  39. data/tasks/github-gem.rake +49 -55
  40. data/yard_extensions.rb +16 -0
  41. metadata +63 -82
  42. data/init.rb +0 -1
  43. data/lib/adyen/notification.rb +0 -151
  44. data/lib/adyen/soap.rb +0 -649
  45. data/spec/notification_spec.rb +0 -97
  46. data/spec/soap_spec.rb +0 -340
@@ -0,0 +1,16 @@
1
+ class ResponseAttrHandler < YARD::Handlers::Ruby::Legacy::Base
2
+ handles 'response_attrs'
3
+ namespace_only
4
+
5
+ def process
6
+ statement.tokens[1..-1].each do |token|
7
+ next unless token.text =~ /^:?(\w+)/
8
+ name = $1
9
+ object = YARD::CodeObjects::MethodObject.new(namespace, name)
10
+ register(object)
11
+ object.dynamic = true
12
+ object.docstring = "@return [String] Returns +:#{name}+ from the {#params}."
13
+ end
14
+ end
15
+ end
16
+
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adyen
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 3
9
- - 8
10
- version: 0.3.8
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Willem van Bergen
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2010-09-23 00:00:00 +02:00
21
+ date: 2011-01-22 00:00:00 -05:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -41,88 +41,44 @@ dependencies:
41
41
  requirement: &id002 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
- - - ">="
44
+ - - ~>
45
45
  - !ruby/object:Gem::Version
46
- hash: 27
46
+ hash: 3
47
47
  segments:
48
- - 1
49
- - 1
50
- - 4
51
- version: 1.1.4
48
+ - 2
49
+ - 0
50
+ version: "2.0"
52
51
  type: :development
53
52
  version_requirements: *id002
54
53
  - !ruby/object:Gem::Dependency
55
- name: git
54
+ name: nokogiri
56
55
  prerelease: false
57
56
  requirement: &id003 !ruby/object:Gem::Requirement
58
57
  none: false
59
58
  requirements:
60
59
  - - ">="
61
60
  - !ruby/object:Gem::Version
62
- hash: 19
61
+ hash: 3
63
62
  segments:
64
- - 1
65
- - 1
66
63
  - 0
67
- version: 1.1.0
64
+ version: "0"
68
65
  type: :development
69
66
  version_requirements: *id003
70
67
  - !ruby/object:Gem::Dependency
71
- name: gemcutter
68
+ name: rails
72
69
  prerelease: false
73
70
  requirement: &id004 !ruby/object:Gem::Requirement
74
71
  none: false
75
72
  requirements:
76
73
  - - ">="
77
74
  - !ruby/object:Gem::Version
78
- hash: 3
75
+ hash: 5
79
76
  segments:
80
- - 0
81
- version: "0"
77
+ - 2
78
+ - 3
79
+ version: "2.3"
82
80
  type: :development
83
81
  version_requirements: *id004
84
- - !ruby/object:Gem::Dependency
85
- name: activerecord
86
- prerelease: false
87
- requirement: &id005 !ruby/object:Gem::Requirement
88
- none: false
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- hash: 3
93
- segments:
94
- - 0
95
- version: "0"
96
- type: :development
97
- version_requirements: *id005
98
- - !ruby/object:Gem::Dependency
99
- name: handsoap
100
- prerelease: false
101
- requirement: &id006 !ruby/object:Gem::Requirement
102
- none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- hash: 3
107
- segments:
108
- - 0
109
- version: "0"
110
- type: :development
111
- version_requirements: *id006
112
- - !ruby/object:Gem::Dependency
113
- name: nokogiri
114
- prerelease: false
115
- requirement: &id007 !ruby/object:Gem::Requirement
116
- none: false
117
- requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- hash: 3
121
- segments:
122
- - 0
123
- version: "0"
124
- type: :development
125
- version_requirements: *id007
126
82
  description: " Package to simplify including the Adyen payments services into a Ruby on Rails application.\n The package provides functionality to create payment forms, handling and storing notifications \n sent by Adyen and consuming the SOAP services provided by Adyen. Moreover, it contains helper\n methods, mocks and matchers to simpify writing tests/specsfor your code.\n"
127
83
  email:
128
84
  - willem@vanbergen.org
@@ -136,28 +92,49 @@ extensions: []
136
92
  extra_rdoc_files:
137
93
  - README.rdoc
138
94
  files:
139
- - spec/spec_helper.rb
140
- - spec/adyen_spec.rb
141
- - lib/adyen/form.rb
142
95
  - .gitignore
143
- - spec/notification_spec.rb
144
- - lib/adyen/soap.rb
96
+ - .kick
145
97
  - LICENSE
146
- - spec/soap_spec.rb
147
- - init.rb
148
- - adyen.gemspec
149
- - Rakefile
150
- - spec/form_spec.rb
151
98
  - README.rdoc
152
- - lib/adyen/notification.rb
153
- - lib/adyen/formatter.rb
154
- - tasks/github-gem.rake
155
- - lib/adyen/encoding.rb
99
+ - Rakefile
156
100
  - TODO
157
- - lib/adyen/matchers.rb
101
+ - adyen.gemspec
158
102
  - lib/adyen.rb
103
+ - lib/adyen/api.rb
104
+ - lib/adyen/api/cacert.pem
105
+ - lib/adyen/api/payment_service.rb
106
+ - lib/adyen/api/recurring_service.rb
107
+ - lib/adyen/api/response.rb
108
+ - lib/adyen/api/simple_soap_client.rb
109
+ - lib/adyen/api/templates/payment_service.rb
110
+ - lib/adyen/api/templates/recurring_service.rb
111
+ - lib/adyen/api/test_helpers.rb
112
+ - lib/adyen/api/xml_querier.rb
113
+ - lib/adyen/configuration.rb
114
+ - lib/adyen/encoding.rb
115
+ - lib/adyen/form.rb
116
+ - lib/adyen/formatter.rb
117
+ - lib/adyen/matchers.rb
118
+ - lib/adyen/notification_generator.rb
119
+ - lib/adyen/railtie.rb
120
+ - lib/adyen/templates/notification_migration.rb
121
+ - lib/adyen/templates/notification_model.rb
122
+ - spec/adyen_spec.rb
123
+ - spec/api/api_spec.rb
124
+ - spec/api/payment_service_spec.rb
125
+ - spec/api/recurring_service_spec.rb
126
+ - spec/api/response_spec.rb
127
+ - spec/api/simple_soap_client_spec.rb
128
+ - spec/api/spec_helper.rb
129
+ - spec/api/test_helpers_spec.rb
130
+ - spec/form_spec.rb
131
+ - spec/functional/api_spec.rb
132
+ - spec/functional/initializer.rb.sample
133
+ - spec/spec_helper.rb
134
+ - tasks/github-gem.rake
135
+ - yard_extensions.rb
159
136
  has_rdoc: true
160
- homepage: http://wiki.github.com/wvanbergen/adyen
137
+ homepage: http://github.com/wvanbergen/adyen/wiki
161
138
  licenses: []
162
139
 
163
140
  post_install_message:
@@ -189,8 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
166
  - 0
190
167
  version: "0"
191
168
  requirements:
192
- - Handsoap is required for accessing the SOAP services. See http://github.com/troelskn/handsoap.
193
- - ActiveRecord is required for storing the notifications in your database.
169
+ - Having Nokogiri installed will speed up XML handling when using the SOAP API.
194
170
  rubyforge_project:
195
171
  rubygems_version: 1.3.7
196
172
  signing_key:
@@ -198,6 +174,11 @@ specification_version: 3
198
174
  summary: Integrate Adyen payment services in your Ruby on Rails application.
199
175
  test_files:
200
176
  - spec/adyen_spec.rb
201
- - spec/notification_spec.rb
202
- - spec/soap_spec.rb
177
+ - spec/api/api_spec.rb
178
+ - spec/api/payment_service_spec.rb
179
+ - spec/api/recurring_service_spec.rb
180
+ - spec/api/response_spec.rb
181
+ - spec/api/simple_soap_client_spec.rb
182
+ - spec/api/test_helpers_spec.rb
203
183
  - spec/form_spec.rb
184
+ - spec/functional/api_spec.rb
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'adyen'
@@ -1,151 +0,0 @@
1
- require 'active_record'
2
-
3
- module Adyen
4
-
5
- # The +Adyen::Notification+ class handles notifications sent by Adyen to your servers.
6
- #
7
- # Because notifications contain important payment status information, you should store
8
- # these notifications in your database. For this reason, +Adyen::Notification+ inherits
9
- # from +ActiveRecord::Base+, and a migration is included to simply create a suitable table
10
- # to store the notifications in.
11
- #
12
- # Adyen can either send notifications to you via HTTP POST requests, or SOAP requests.
13
- # Because SOAP is not really well supported in Rails and setting up a SOAP server is
14
- # not trivial, only handling HTTP POST notifications is currently supported.
15
- #
16
- # @example
17
- # @notification = Adyen::Notification::HttpPost.log(request)
18
- # if @notification.successful_authorisation?
19
- # @invoice = Invoice.find(@notification.merchant_reference)
20
- # @invoice.set_paid!
21
- # end
22
- #
23
- # @see Adyen::Notification::HttpPost.log
24
- class Notification < ActiveRecord::Base
25
-
26
- # The default table name to use for the notifications table.
27
- DEFAULT_TABLE_NAME = :adyen_notifications
28
- set_table_name(DEFAULT_TABLE_NAME)
29
-
30
- # A notification should always include an event_code
31
- validates_presence_of :event_code
32
-
33
- # A notification should always include a psp_reference
34
- validates_presence_of :psp_reference
35
-
36
- # A notification should be unique using the composed key of
37
- # [:psp_reference, :event_code, :success]
38
- validates_uniqueness_of :success, :scope => [:psp_reference, :event_code]
39
-
40
- # Make sure we don't end up with an original_reference with an empty string
41
- before_validation { |notification| notification.original_reference = nil if notification.original_reference.blank? }
42
-
43
- # Logs an incoming notification into the database.
44
- #
45
- # @param [Hash] params The notification parameters that should be stored in the database.
46
- # @return [Adyen::Notification] The initiated and persisted notification instance.
47
- # @raise This method will raise an exception if the notification cannot be stored.
48
- # @see Adyen::Notification::HttpPost.log
49
- def self.log(params)
50
- converted_params = {}
51
- # Convert each attribute from CamelCase notation to under_score notation
52
- # For example, merchantReference will be converted to merchant_reference
53
- params.each do |key, value|
54
- field_name = key.to_s.underscore
55
- converted_params[field_name] = value if self.column_names.include?(field_name)
56
- end
57
- self.create!(converted_params)
58
- end
59
-
60
- # Returns true if this notification is an AUTHORISATION notification
61
- # @return [true, false] true iff event_code == 'AUTHORISATION'
62
- # @see Adyen.notification#successful_authorisation?
63
- def authorisation?
64
- event_code == 'AUTHORISATION'
65
- end
66
-
67
- alias :authorization? :authorisation?
68
-
69
- # Returns true if this notification is an AUTHORISATION notification and
70
- # the success status indicates that the authorization was successfull.
71
- # @return [true, false] true iff the notification is an authorization
72
- # and the authorization was successful according to the success field.
73
- def successful_authorisation?
74
- event_code == 'AUTHORISATION' && success?
75
- end
76
-
77
- alias :successful_authorization? :successful_authorisation?
78
-
79
- # Collect a payment using the recurring contract that was initiated with
80
- # this notification. The payment is collected using a SOAP call to the
81
- # Adyen SOAP service for recurring payments.
82
- # @param [Hash] options The payment parameters.
83
- # @see Adyen::SOAP::RecurringService#submit
84
- def collect_payment_for_recurring_contract!(options)
85
- # Make sure we convert the value to cents
86
- options[:value] = Adyen::Formatter::Price.in_cents(options[:value])
87
- raise "This is not a recurring contract!" unless event_code == 'RECURRING_CONTRACT'
88
- Adyen::SOAP::RecurringService.submit(options.merge(:recurring_reference => self.psp_reference))
89
- end
90
-
91
- # Deactivates the recurring contract that was initiated with this notification.
92
- # The contract is deactivated by sending a SOAP call to the Adyen SOAP service for
93
- # recurring contracts.
94
- # @param [Hash] options The recurring contract parameters.
95
- # @see Adyen::SOAP::RecurringService#deactivate
96
- def deactivate_recurring_contract!(options)
97
- raise "This is not a recurring contract!" unless event_code == 'RECURRING_CONTRACT'
98
- Adyen::SOAP::RecurringService.deactivate(options.merge(:recurring_reference => self.psp_reference))
99
- end
100
-
101
- class HttpPost < Notification
102
-
103
- def self.log(request)
104
- super(request.params)
105
- end
106
-
107
- def live=(value)
108
- super([true, 1, '1', 'true'].include?(value))
109
- end
110
-
111
- def success=(value)
112
- super([true, 1, '1', 'true'].include?(value))
113
- end
114
-
115
- def value=(value)
116
- super(Adyen::Formatter::Price.from_cents(value)) unless value.blank?
117
- end
118
- end
119
-
120
- # An ActiveRecord migration that can be used to create a suitable table
121
- # to store Adyen::Notification instances for your application.
122
- class Migration < ActiveRecord::Migration
123
-
124
- def self.up(table_name = Adyen::Notification::DEFAULT_TABLE_NAME)
125
- create_table(table_name) do |t|
126
- t.boolean :live, :null => false, :default => false
127
- t.string :event_code, :null => false
128
- t.string :psp_reference, :null => false
129
- t.string :original_reference, :null => true
130
- t.string :merchant_reference, :null => false
131
- t.string :merchant_account_code, :null => false
132
- t.datetime :event_date, :null => false
133
- t.boolean :success, :null => false, :default => false
134
- t.string :payment_method, :null => true
135
- t.string :operations, :null => true
136
- t.text :reason
137
- t.string :currency, :null => false, :limit => 3
138
- t.decimal :value, :null => true, :precision => 9, :scale => 2
139
- t.boolean :processed, :null => false, :default => false
140
- t.timestamps
141
- end
142
- add_index table_name, [:psp_reference, :event_code, :success], :unique => true, :name => 'adyen_notification_uniqueness'
143
- end
144
-
145
- def self.down(table_name = Adyen::Notification::DEFAULT_TABLE_NAME)
146
- remove_index(table_name, :name => 'adyen_notification_uniqueness')
147
- drop_table(table_name)
148
- end
149
- end
150
- end
151
- end
@@ -1,649 +0,0 @@
1
- begin
2
- require "handsoap"
3
- rescue LoadError
4
- $stderr.puts "The handsoap gem (>= 1.4.1) is required to use the SOAP clients:"
5
- $stderr.puts "$ (sudo) gem install handsoap --source http://gemcutter.org"
6
- end
7
-
8
- module Adyen
9
-
10
- # The SOAP module contains classes that interact with the Adyen SOAP
11
- # services. The clients are based on the +handsoap+ library and requires at
12
- # least version 1.4.1 of this gem.
13
- #
14
- # Note that you'll need an Adyen notification PSP reference for most SOAP
15
- # calls. Because of this, store all notifications that Adyen sends to you.
16
- # (e.g. using the {Adyen::Notification} ActiveRecord class). Moreover, most
17
- # SOAP calls do not respond that they were successful immediately, but a
18
- # notifications to indicate that will be sent later on.
19
- #
20
- # You'll need to provide a username and password to interact with the Adyen
21
- # SOAP services:
22
- #
23
- # Adyen::SOAP.username = 'ws@Company.MyAccount'
24
- # Adyen::SOAP.password = 'very$ecret'
25
- #
26
- # You can setup default parameters that will be used by every SOAP call by
27
- # using {Adyen::SOAP.default_arguments}. You can override these default
28
- # values by passing another value as parameter to the actual call.
29
- #
30
- # Adyen::SOAP.default_arguments[:merchant_account] = 'MyMerchant'
31
- #
32
- # All SOAP clients are based on the {Adyen::SOAP::Base} class, which sets up
33
- # the Handsoap library to work with the Adyen SOAP services and implements
34
- # shared functionality. Based on this class, the following services are available:
35
- #
36
- # * {Adyen::SOAP::RecurringService} - SOAP service for handling recurring payments.
37
- # * {Adyen::SOAP::PaymentService} - SOAP service for modification to payments. Currently,
38
- # this class is just a stub. Feel free to implement it as you need it.
39
- module SOAP
40
-
41
- class << self
42
-
43
- # Username for the HTTP Basic Authentication that Adyen uses. Your username
44
- # should be something like +ws@Company.MyAccount+
45
- # @return [String]
46
- attr_accessor :username
47
-
48
- # Password for the HTTP Basic Authentication that Adyen uses. You can choose
49
- # your password yourself in the user management tool of the merchant area.
50
- # @return [String]
51
- attr_accessor :password
52
-
53
- # Default arguments that will be used for every SOAP call.
54
- # @return [Hash]
55
- attr_accessor :default_arguments
56
- end
57
-
58
- self.default_arguments = {} # Set default value
59
-
60
- # The base class sets up XML namespaces and the HTTP client
61
- # for all the Adyen SOAP services.
62
- class Base < Handsoap::Service
63
-
64
- # Basic setup for the SOAP endpoint when creating a subclass.
65
- #
66
- # The version must be set to construct the request envelopes, the URI
67
- # wil be set later using the correct {Adyen.environment} value. For now,
68
- # use a bogus value so handsoap will not complain.
69
- def self.inherited(klass) # :nodoc:
70
- klass.endpoint :version => 1, :uri => 'bogus'
71
- end
72
-
73
- # Setup some CURL options to handle redirects correctly.
74
- def on_after_create_http_client(http_client) # :nodoc:
75
- http_client.follow_location = true
76
- http_client.max_redirects = 2
77
- end
78
-
79
- # Setup basic authentication for SOAP requests
80
- # @see Adyen::SOAP.username
81
- # @see Adyen::SOAP.password
82
- def on_after_create_http_request(http_request) # :nodoc:
83
- debug { |logger| logger.puts "Authorization: #{Adyen::SOAP.username}:#{Adyen::SOAP.password}..." }
84
- http_request.set_auth Adyen::SOAP.username, Adyen::SOAP.password
85
- end
86
-
87
- # Sets up XML namespaces for composing the SOAP request body.
88
- def on_create_document(doc) # :nodoc:
89
- doc.alias 'payment', 'http://payment.services.adyen.com'
90
- doc.alias 'recurring', 'http://recurring.services.adyen.com'
91
- doc.alias 'common', 'http://common.services.adyen.com'
92
- end
93
-
94
- # Sets up the XML namespaces for parsing the SOAP response.
95
- def on_response_document(doc) # :nodoc:
96
- doc.add_namespace 'payment', 'http://payment.services.adyen.com'
97
- doc.add_namespace 'recurring', 'http://recurring.services.adyen.com'
98
- doc.add_namespace 'common', 'http://common.services.adyen.com'
99
- end
100
-
101
- # Set endpoint URI before dispatch, so that changes in environment
102
- # are reflected correctly.
103
- def on_before_dispatch
104
- self.class.endpoint(:uri => self.class::ENDPOINT_URI % Adyen.environment.to_s, :version => 1)
105
- end
106
- end
107
-
108
- # SOAP client to interact with the payment modification service of Adyen. This client
109
- # implements the following calls:
110
- #
111
- # * +authorise+ to list recurring contracts for a shopper, using {Adyen::SOAP::PaymentService#authorise}.
112
- # * +cancelOrRefund+ to cancel a payment (or refund if it has been captured), using
113
- # {Adyen::SOAP::PaymentService#cancel_or_refund}.
114
- #
115
- # Before using this service, make sure to set the SOAP username and
116
- # password (see {Adyen::SOAP.username} and {Adyen::SOAP.password}).
117
- class PaymentService < Base
118
-
119
- ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Payment'
120
-
121
- # Submits a recurring payment for authorisation.
122
- #
123
- # @example
124
- # Adyen::SOAP::PaymentService.authorise(
125
- # :merchant_account => 'MyAccount', :selected_recurring_detail_reference => 'LATEST',
126
- # :shopper_reference => user.id, :shopper_email => user.email,
127
- # :reference => invoice.id, :currency => invoice.currency, :value => invoice.amount)
128
- #
129
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
130
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
131
- # is required by the Adyen SOAP service, so please provide a value for all options.
132
- # @option args [String] :selected_recurring_detail_reference ('LATEST') This is the
133
- # recurringDetailReference you want to use for this payment. You can use the
134
- # value "LATEST" to select the most recently used recurring detail, which is the default.
135
- # @option args [String] :merchant_account The merchant account you want to process this payment
136
- # with.
137
- # @option args [String] :currency The currency code (EUR, GBP, USD, etc).
138
- # @option args [Integer] :value The value of the payment in cents.
139
- # @option args [String] :reference Your reference for this payment. This (merchant) reference
140
- # will be used in all communication to you about the status of the payment.
141
- # Although it is a good idea to make sure it is unique, this is not a requirement.
142
- # @option args [String] :shopper_email The email address of the shopper. This does not have to
143
- # match the email address supplied with the initial payment, since it may have
144
- # changed in the mean time.
145
- # @option args [String] :shopper_reference The reference of the shopper. This should be
146
- # the same as the reference that was used to create the recurring contract.
147
- # @option args [Integer] :fraud_offset (optional) An integer that is added to normal fraud score.
148
- # The value can be either positive or negative.
149
- # @option args [String] :shopper_ip (optional) The IP address of the shopper. Used in various risk
150
- # checks (number of payment attempts, location based checks), so it is a good idea to supply
151
- # this.
152
- #
153
- # @return [nil] This action returns nothing of interest. The result of the authorization
154
- # will be communicated using a {Adyen::Notification notification}.
155
- #
156
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
157
- # The Adyen integration manual
158
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=7&nav=0,3
159
- # The Adyen recurring payments manual.
160
- def authorise(args = {})
161
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
162
- invoke_args[:selected_recurring_detail_reference] ||= 'LATEST'
163
-
164
- response = invoke('payment:authorise') do |message|
165
- message.add('payment:paymentRequest') do |req|
166
- req.add('payment:selectedRecurringDetailReference', invoke_args[:selected_recurring_detail_reference])
167
- req.add('payment:recurring') do |recurring|
168
- recurring.add('payment:contract', 'RECURRING')
169
- end
170
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
171
- req.add('payment:amount') do |amount|
172
- amount.add('common:currency', invoke_args[:currency])
173
- amount.add('common:value', invoke_args[:value])
174
- end
175
- req.add('payment:reference', invoke_args[:reference])
176
- req.add('payment:shopperEmail', invoke_args[:shopper_email])
177
- req.add('payment:shopperReference', invoke_args[:shopper_reference])
178
- req.add('payment:shopperInteraction', 'ContAuth')
179
-
180
- # optional fields
181
- req.add('payment:fraudOffset', invoke_args[:fraud_offset]) if(invoke_args[:fraud_offset])
182
- req.add('payment:shopperIP', invoke_args[:shopper_ip]) if(invoke_args[:shopper_ip])
183
- end
184
- end
185
-
186
- parse_authorise(response)
187
- end
188
-
189
- # Submits a direct debit recurring payment.
190
- #
191
- # @example
192
- # Adyen::SOAP::PaymentService.directdebit(
193
- # :merchant_account => 'MyAccount', :selected_recurring_detail_reference => 'LATEST',
194
- # :shopper_reference => user.id, :shopper_email => user.email,
195
- # :reference => invoice.id, :currency => invoice.currency, :value => invoice.amount)
196
- #
197
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
198
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
199
- # is required by the Adyen SOAP service, so please provide a value for all options.
200
- # @option args [String] :selected_recurring_detail_reference ('LATEST') This is the
201
- # recurringDetailReference you want to use for this payment. You can use the
202
- # value "LATEST" to select the most recently used recurring detail, which is the default.
203
- # @option args [String] :merchant_account The merchant account you want to process this payment
204
- # with.
205
- # @option args [String] :currency The currency code (EUR, GBP, USD, etc).
206
- # @option args [Integer] :value The value of the payment in cents.
207
- # @option args [String] :reference Your reference for this payment. This (merchant) reference
208
- # will be used in all communication to you about the status of the payment.
209
- # Although it is a good idea to make sure it is unique, this is not a requirement.
210
- # @option args [String] :shopper_email The email address of the shopper. This does not have to
211
- # match the email address supplied with the initial payment, since it may have
212
- # changed in the mean time.
213
- # @option args [String] :shopper_reference The reference of the shopper. This should be
214
- # the same as the reference that was used to create the recurring contract.
215
- # @option args [String] :shopper_ip (optional) The IP address of the shopper. Used in various risk
216
- # checks (number of payment attempts, location based checks), so it is a good idea to supply
217
- # this.
218
- #
219
- # @return [nil] This action returns nothing of interest. The result of the authorization
220
- # will be communicated using a {Adyen::Notification notification}.
221
- #
222
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
223
- # The Adyen integration manual
224
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=7&nav=0,3
225
- # The Adyen recurring payments manual.
226
- def directdebit(args = {})
227
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
228
- invoke_args[:selected_recurring_detail_reference] ||= 'LATEST'
229
-
230
- response = invoke('payment:directdebit') do |message|
231
- message.add('payment:request') do |req|
232
- req.add('payment:selectedRecurringDetailReference', invoke_args[:selected_recurring_detail_reference])
233
- req.add('payment:recurring') do |recurring|
234
- recurring.add('payment:contract', 'RECURRING')
235
- end
236
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
237
- req.add('payment:amount') do |amount|
238
- amount.add('common:currency', invoke_args[:currency])
239
- amount.add('common:value', invoke_args[:value])
240
- end
241
- req.add('payment:reference', invoke_args[:reference])
242
- req.add('payment:shopperEmail', invoke_args[:shopper_email])
243
- req.add('payment:shopperReference', invoke_args[:shopper_reference])
244
- req.add('payment:shopperInteraction', 'ContAuth')
245
- req.add('payment:shopperIP', invoke_args[:shopper_ip]) if(invoke_args[:shopper_ip])
246
- end
247
- end
248
-
249
- parse_directdebit(response)
250
- end
251
-
252
- # Capture a payment.
253
- #
254
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
255
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
256
- # is required by the Adyen SOAP service, so please provide a value for all options.
257
- # @option args [String] :merchant_account The merchant account to file this payment under.
258
- # @option args [String] :currency The currency code (EUR, GBP, USD, etc).
259
- # @option args [Integer] :value The value of the payment in cents.
260
- # @option args [String] :original_reference The psp_reference of the payment to capture.
261
- #
262
- # @return [nil] This action returns nothing of interest.
263
- #
264
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
265
- # The Adyen integration manual
266
- #
267
- # @todo Parse response object and return something useful
268
- def capture(args = {})
269
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
270
- response = invoke('payment:capture') do |message|
271
- message.add('payment:modificationRequest') do |req|
272
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
273
- req.add('payment:modificationAmount') do |amount|
274
- amount.add('common:currency', invoke_args[:currency])
275
- amount.add('common:value', invoke_args[:value])
276
- end
277
- req.add('payment:originalReference', invoke_args[:original_reference])
278
- end
279
- end
280
-
281
- parse_capture(response)
282
- end
283
-
284
- # Cancel a payment.
285
- #
286
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
287
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
288
- # is required by the Adyen SOAP service, so please provide a value for all options.
289
- # @option args [String] :merchant_account The merchant account to file this payment under.
290
- # @option args [String] :original_reference The psp_reference of the payment to cancel.
291
- #
292
- # @return [nil] This action returns nothing of interest.
293
- #
294
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
295
- # The Adyen integration manual
296
- #
297
- # @todo Parse response object and return something useful
298
- def cancel(args = {})
299
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
300
- response = invoke('payment:cancel') do |message|
301
- message.add('payment:modificationRequest') do |req|
302
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
303
- req.add('payment:originalReference', invoke_args[:original_reference])
304
- end
305
- end
306
-
307
- parse_cancel(response)
308
- end
309
-
310
- # Refund a payment.
311
- #
312
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
313
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
314
- # is required by the Adyen SOAP service, so please provide a value for all options.
315
- # @option args [String] :merchant_account The merchant account to file this payment under.
316
- # @option args [String] :currency The currency code (EUR, GBP, USD, etc).
317
- # @option args [Integer] :value The value of the refund in cents.
318
- # @option args [String] :original_reference The psp_reference of the payment to refund.
319
- #
320
- # @return [nil] This action returns nothing of interest.
321
- #
322
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
323
- # The Adyen integration manual
324
- #
325
- # @todo Parse response object and return something useful
326
- def refund(args = {})
327
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
328
- response = invoke('payment:refund') do |message|
329
- message.add('payment:modificationRequest') do |req|
330
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
331
- req.add('payment:modificationAmount') do |amount|
332
- amount.add('common:currency', invoke_args[:currency])
333
- amount.add('common:value', invoke_args[:value])
334
- end
335
- req.add('payment:originalReference', invoke_args[:original_reference])
336
- end
337
- end
338
-
339
- parse_refund(response)
340
- end
341
-
342
- # Cancel or refund a payment.
343
- #
344
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
345
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
346
- # is required by the Adyen SOAP service, so please provide a value for all options.
347
- # @option args [String] :merchant_account The merchant account to file this payment under.
348
- # @option args [String] :original_reference The psp_reference of the payment to cancel or refund.
349
- #
350
- # @return [nil] This action returns nothing of interest.
351
- #
352
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
353
- # The Adyen integration manual
354
- #
355
- # @todo Parse response object and return something useful
356
- def cancel_or_refund(args = {})
357
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
358
- response = invoke('payment:cancelOrRefund') do |message|
359
- message.add('payment:modificationRequest') do |req|
360
- req.add('payment:merchantAccount', invoke_args[:merchant_account])
361
- req.add('payment:originalReference', invoke_args[:original_reference])
362
- end
363
- end
364
-
365
- parse_cancel_or_refund(response)
366
- end
367
-
368
- private
369
-
370
- def parse_authorise(response)
371
- response = response.xpath('//payment:authoriseResponse/payment:paymentResult')
372
- {
373
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
374
- :result_code => response.xpath('./payment:resultCode/text()').to_s,
375
- :auth_code => response.xpath('./payment:authCode/text()').to_s,
376
- :refusal_reason => response.xpath('./payment:refusalReason/text()').to_s
377
- }
378
- end
379
-
380
- def parse_directdebit(response)
381
- response = response.xpath('//payment:directdebitResponse/payment:response')
382
- {
383
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
384
- :result_code => response.xpath('./payment:resultCode/text()').to_s,
385
- :auth_code => response.xpath('./payment:authCode/text()').to_s,
386
- :refusal_reason => response.xpath('./payment:refusalReason/text()').to_s
387
- }
388
- end
389
-
390
- def parse_capture(response)
391
- response = response.xpath('//payment:captureResponse/payment:captureResult')
392
- {
393
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
394
- :response => response.xpath('./payment:response/text()').to_s
395
- }
396
- end
397
-
398
- def parse_cancel(response)
399
- response = response.xpath('//payment:cancelResponse/payment:cancelResult')
400
- {
401
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
402
- :response => response.xpath('./payment:response/text()').to_s
403
- }
404
- end
405
-
406
- def parse_refund(response)
407
- response = response.xpath('//payment:refundResponse/payment:refundResult')
408
- {
409
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
410
- :response => response.xpath('./payment:response/text()').to_s
411
- }
412
- end
413
-
414
- def parse_cancel_or_refund(response)
415
- response = response.xpath('//payment:cancelOrRefundResponse/payment:cancelOrRefundResult')
416
- {
417
- :psp_reference => response.xpath('./payment:pspReference/text()').to_s,
418
- :response => response.xpath('./payment:response/text()').to_s
419
- }
420
- end
421
-
422
- end
423
-
424
- # SOAP client to interact with the recurring payment service of Adyen. This clients
425
- # implements the following calls:
426
- #
427
- # * +listRecurring+ to list recurring contracts for a shopper, using {Adyen::SOAP::RecurringService#list}.
428
- # * +submitRecurring+ to submit a recurring payment for a shopper, using {Adyen::SOAP::RecurringService#submit}.
429
- # * +deactivateRecurring+ to cancel a recurring contract, using {Adyen::SOAP::RecurringService#deactivate}.
430
- #
431
- # Before using this service, make sure to set the SOAP username and
432
- # password (see {Adyen::SOAP.username} and {Adyen::SOAP.password}).
433
- #
434
- # The recurring service requires shoppers to have a recurring contract.
435
- # Such a contract can be set up when creating the initial payment using
436
- # the {Adyen::Form} methods. After the payment has been authorized, a
437
- # {Adyen::Notification RECURRING_CONTRACT notification} will be sent. The
438
- # PSP reference of this notification should be used as
439
- # +:recurring_reference+ parameters in these calls.
440
- #
441
- # @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=7&nav=0,3
442
- # The Adyen recurring payments manual.
443
- class RecurringService < Base
444
-
445
- # The endpoint URI for this SOAP service, in which test or live should be filled in as
446
- # environment.
447
- # @see Adyen.environment
448
- ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Recurring'
449
-
450
- # Submits a recurring payment for a recurring contract to Adyen.
451
- #
452
- # @deprecated This method has been replaced by {Adyen::SOAP::PaymentService.authorise}.
453
- #
454
- # @example
455
- # Adyen::SOAP::RecurringService.submit(
456
- # :merchant_account => 'MyAccount',
457
- # :shopper_reference => user.id, :shopper_email => user.email,
458
- # :recurring_reference => user.contract_notification.psp_reference,
459
- # :reference => invoice.id, :currency => invoice.currency, :value => invoice.amount)
460
- #
461
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
462
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
463
- # is required by the Adyen SOAP service, so please provide a value for all options.
464
- # @option args [String] :merchant_account The merchant account to file this payment under.
465
- # @option args [String] :currency The currency code (EUR, GBP, USD, etc).
466
- # @option args [Integer] :value The value of the payment in cents.
467
- # @option args [Integer] :recurring_reference The psp_reference of the RECURRING_CONTRACT
468
- # notification that was sent after the initial payment.
469
- # @option args [String] :reference Your reference for this payment. This (merchant) reference
470
- # will be used in all communication to you about the status of the payment.
471
- # Although it is a good idea to make sure it is unique, this is not a requirement.
472
- # @option args [String] :shopper_email The email address of the shopper. This does not have to
473
- # match the email address supplied with the initial payment, since it may have
474
- # changed in the mean time.
475
- # @option args [String] :shopper_reference The reference of the shopper. This should be
476
- # the same as the reference that was used to create the recurring contract.
477
- #
478
- # @return [nil] This method does not return anything. The result of the payment request will
479
- # be communicated with an {Adyen::Notification}.
480
- # @see Adyen::Notification#collect_payment_for_recurring_contract!
481
- def submit(args = {})
482
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
483
- response = invoke('recurring:submitRecurring') do |message|
484
- message.add('recurring:recurringRequest') do |req|
485
- req.add('recurring:amount') do |amount|
486
- amount.add('common:currency', invoke_args[:currency])
487
- amount.add('common:value', invoke_args[:value])
488
- end
489
- req.add('recurring:merchantAccount', invoke_args[:merchant_account])
490
- req.add('recurring:recurringReference', invoke_args[:recurring_reference])
491
- req.add('recurring:reference', invoke_args[:reference])
492
- req.add('recurring:shopperEmail', invoke_args[:shopper_email])
493
- req.add('recurring:shopperReference', invoke_args[:shopper_reference])
494
- end
495
- end
496
- end
497
-
498
- # Retrieves the recurring contracts for a shopper.
499
- #
500
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
501
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
502
- # is required by the Adyen SOAP service, so please provide a value for all options.
503
- # @option args [String] :merchant_account The merchant account to file this payment under.
504
- # @option args [String] :shopper_reference The refrence of the shopper. This should be
505
- # the same as the reference that was used to create the recurring contract.
506
- #
507
- # @return [Hash] This method returns a hash representation of the
508
- # listRecurringDetailsResponse.
509
- #
510
- def list(args = {})
511
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
512
- response = invoke('recurring:listRecurringDetails') do |message|
513
- message.add('recurring:request') do |req|
514
- req.add('recurring:recurring') do |recurring|
515
- recurring.add('recurring:contract', 'RECURRING')
516
- end
517
- req.add('recurring:merchantAccount', invoke_args[:merchant_account])
518
- req.add('recurring:shopperReference', invoke_args[:shopper_reference])
519
- end
520
- end
521
-
522
- parse_list_recurring_details(response)
523
- end
524
-
525
- # Disables a recurring payment contract. Requires the following arguments:
526
- #
527
- # @example
528
- # Adyen::SOAP::RecurringService.disable(
529
- # :merchant_account => 'MyAccount', :shopper_reference => user.id,
530
- # :recurring_detail_reference => user.contract_notification.psp_reference)
531
- #
532
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
533
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
534
- # is required by the Adyen SOAP service, so please provide a value for all options.
535
- # @option args [String] :merchant_account Your merchant account.
536
- # @option args [String] :shopper_reference The reference to the shopper. This shopperReference
537
- # must be the same as the shopperReference used in the initial payment.
538
- # @option args [String] :recurring_detail_reference The recurringDetailReference of the
539
- # details you wish to disable. If you do not supply this field, all details for the shopper
540
- # will be disabled, including the contract! This means that you can not add new details
541
- # anymore.
542
- def disable(args = {})
543
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
544
- response = invoke('recurring:disable') do |message|
545
- message.add('recurring:request') do |req|
546
- req.add('recurring:merchantAccount', invoke_args[:merchant_account])
547
- req.add('recurring:shopperReference', invoke_args[:shopper_reference])
548
- req.add('recurring:recurringDetailReference', invoke_args[:recurring_detail_reference])
549
- end
550
- end
551
-
552
- parse_disable(response)
553
- end
554
-
555
- # Deactivates a recurring payment contract. Requires the following arguments:
556
- #
557
- # @deprecated This method has been replaced by the {#disable} method.
558
- #
559
- # @example
560
- # Adyen::SOAP::RecurringService.deactivate(
561
- # :merchant_account => 'MyAccount', :shopper_reference => user.id,
562
- # :recurring_reference => user.contract_notification.psp_reference,
563
- # :reference => "Terminated account #{user.account.id}")
564
- #
565
- # @param [Hash] args The paramaters to use for this call. These will be merged by any default
566
- # parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
567
- # is required by the Adyen SOAP service, so please provide a value for all options.
568
- # @option args [String] :merchant_account The merchant account to file this payment under.
569
- # @option args [String] :shopper_reference The refrence of the shopper. This should be
570
- # the same as the reference that was used to create the recurring contract.
571
- # @option args [Integer] :recurring_reference The psp_reference of the RECURRING_CONTRACT
572
- # notification that was sent after the initial payment.
573
- # @option args [String] :reference The (merchant) reference for this contract deactivation.
574
- # Use any string you like that helps you identify this contract deactivation.
575
- #
576
- # @return [nil] This method does not return anything.
577
- # @see Adyen::Notification#deactivate_recurring_contract!
578
- def deactivate(args = {})
579
- invoke_args = Adyen::SOAP.default_arguments.merge(args)
580
- response = invoke('recurring:deactivateRecurring') do |message|
581
- message.add('recurring:recurringRequest') do |req|
582
- req.add('recurring:merchantAccount', invoke_args[:merchant_account])
583
- req.add('recurring:recurringReference', invoke_args[:recurring_reference])
584
- req.add('recurring:reference', invoke_args[:reference])
585
- req.add('recurring:shopperReference', invoke_args[:shopper_reference])
586
- end
587
- end
588
- end
589
-
590
- private
591
-
592
- def parse_list_recurring_details(response)
593
- response = response.xpath('//recurring:listRecurringDetailsResponse/recurring:result')
594
- {
595
- :creation_date => response.xpath('./recurring:creationDate/text()').to_date,
596
- :details => response.xpath('.//recurring:RecurringDetail').map { |node| parse_recurring_detail(node) },
597
- :last_known_shopper_email => response.xpath('./recurring:lastKnownShopperEmail/text()').to_s,
598
- :shopper_reference => response.xpath('./recurring:shopperReference/text()').to_s
599
- }
600
- end
601
-
602
- # @todo add support for elv
603
- def parse_recurring_detail(node)
604
- result = if(not node.xpath('./recurring:card').to_s.nil?)
605
- parse_card(node)
606
- elsif(not node.xpath('./recurring:bank').to_s.nil?)
607
- parse_bank(node)
608
- end
609
-
610
- result.merge({
611
- :recurring_detail_reference => node.xpath('./recurring:recurringDetailReference/text()').to_s,
612
- :variant => node.xpath('./recurring:variant/text()').to_s,
613
- :creation_date => node.xpath('./recurring:creationDate/text()').to_date
614
- })
615
- end
616
-
617
- def parse_card(node)
618
- {
619
- :card => {
620
- :expiry_date => Date.new(node.xpath('./recurring:card/payment:expiryYear/text()').to_i, node.xpath('recurring:card/payment:expiryMonth').to_i, -1),
621
- :holder_name => node.xpath('./recurring:card/payment:holderName/text()').to_s,
622
- :number => node.xpath('./recurring:card/payment:number/text()').to_s
623
- }
624
- }
625
- end
626
-
627
- def parse_bank(node)
628
- {
629
- :bank => {
630
- :bank_account_number => node.xpath('./recurring:bank/payment:bankAccountNumber/text()').to_s,
631
- :bank_location_id => node.xpath('./recurring:bank/payment:bankLocationId/text()').to_s,
632
- :bank_name => node.xpath('./recurring:bank/payment:bankName/text()').to_s,
633
- :bic => node.xpath('./recurring:bank/payment:bic/text()').to_s,
634
- :country_code => node.xpath('./recurring:bank/payment:countryCode/text()').to_s,
635
- :iban => node.xpath('./recurring:bank/payment:iban/text()').to_s,
636
- :owner_name => node.xpath('./recurring:bank/payment:ownerName/text()').to_s
637
- }
638
- }
639
- end
640
-
641
- def parse_disable(response)
642
- response = response.xpath('//recurring:disableResponse/recurring:result')
643
- {
644
- :response => response.xpath('./recurring:response/text()').to_s
645
- }
646
- end
647
- end
648
- end
649
- end