fingertips-adyen 0.3.7.20100917
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +40 -0
- data/Rakefile +5 -0
- data/adyen.gemspec +30 -0
- data/init.rb +1 -0
- data/lib/adyen.rb +77 -0
- data/lib/adyen/api.rb +343 -0
- data/lib/adyen/encoding.rb +21 -0
- data/lib/adyen/form.rb +336 -0
- data/lib/adyen/formatter.rb +37 -0
- data/lib/adyen/matchers.rb +105 -0
- data/lib/adyen/notification.rb +151 -0
- data/spec/adyen_spec.rb +86 -0
- data/spec/api_spec.rb +562 -0
- data/spec/form_spec.rb +152 -0
- data/spec/notification_spec.rb +97 -0
- data/spec/spec_helper.rb +12 -0
- data/tasks/github-gem.rake +371 -0
- metadata +132 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 - 2009 Willem van Bergen and Michel Barbosa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= Adyen
|
2
|
+
|
3
|
+
Package to simplify including the Adyen payments services into a Ruby on Rails application.
|
4
|
+
|
5
|
+
Adyen integration relies on three modes of communication between Adyen, your server and
|
6
|
+
your client/customer:
|
7
|
+
|
8
|
+
* Client-to-Adyen communication using forms and redirects.
|
9
|
+
* Adyen-to-server communications using notifications.
|
10
|
+
* Server-to-Adyen communication using SOAP services.
|
11
|
+
|
12
|
+
This library aims to ease the implementation of all these modes into your application.
|
13
|
+
Moreover, it provides matchers, assertions and mocks to make it easier to implement an
|
14
|
+
automated test suite to assert the integration is working correctly.
|
15
|
+
|
16
|
+
== Installation
|
17
|
+
|
18
|
+
Add the following line to your <tt>environment.rb</tt> and run <tt>rake gems:install</tt>
|
19
|
+
to make the Adyen functionality available in your Rails project:
|
20
|
+
|
21
|
+
config.gem 'adyen', :source => 'http://gemcutter.org
|
22
|
+
|
23
|
+
You can also install it as a Rails plugin (*deprecated*):
|
24
|
+
|
25
|
+
script/plugin install git://github.com/wvanbergen/adyen.git
|
26
|
+
|
27
|
+
== Usage
|
28
|
+
|
29
|
+
See the project wiki on http://wiki.github.com/wvanbergen/adyen to get started. Complete
|
30
|
+
RDoc documentation for the project can be found on http://rdoc.info/projects/wvanbergen/adyen.
|
31
|
+
|
32
|
+
* For more information about Adyen, see http://www.adyen.com
|
33
|
+
* For more information about integrating Adyen, see their manuals at
|
34
|
+
http://support.adyen.com/links/documentation
|
35
|
+
|
36
|
+
== About
|
37
|
+
|
38
|
+
This package is written by Michel Barbosa and Willem van Bergen for Floorplanner.com, and
|
39
|
+
made public under the MIT license (see LICENSE). It comes without warranty of any kind, so
|
40
|
+
use at your own risk.
|
data/Rakefile
ADDED
data/adyen.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'fingertips-adyen'
|
3
|
+
s.version = "0.3.7.20100917"
|
4
|
+
s.date = "2010-07-21"
|
5
|
+
|
6
|
+
s.summary = "Integrate Adyen payment services in you Ruby on Rails application."
|
7
|
+
s.description = <<-EOS
|
8
|
+
Package to simplify including the Adyen payments services into a Ruby on Rails application.
|
9
|
+
The package provides functionality to create payment forms, handling and storing notifications
|
10
|
+
sent by Adyen and consuming the SOAP services provided by Adyen. Moreover, it contains helper
|
11
|
+
methods, mocks and matchers to simpify writing tests/specsfor your code.
|
12
|
+
EOS
|
13
|
+
|
14
|
+
s.authors = ['Willem van Bergen', 'Michel Barbosa', 'Eloy Duran']
|
15
|
+
s.email = ['willem@vanbergen.org', 'cicaboo@gmail.com', 'eloy.de.enige@gmail.com']
|
16
|
+
s.homepage = 'http://wiki.github.com/wvanbergen/adyen'
|
17
|
+
|
18
|
+
s.add_development_dependency('rspec', '>= 1.1.4')
|
19
|
+
s.add_development_dependency('git', '>= 1.1.0')
|
20
|
+
|
21
|
+
s.requirements << 'Handsoap is required for accessing the SOAP services. See http://github.com/troelskn/handsoap.'
|
22
|
+
s.requirements << 'LibXML is required for using the RSpec matchers.'
|
23
|
+
s.requirements << 'ActiveRecord is required for storing the notifications in your database.'
|
24
|
+
|
25
|
+
s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
|
26
|
+
s.extra_rdoc_files = ['README.rdoc']
|
27
|
+
|
28
|
+
s.files = %w(spec/spec_helper.rb spec/adyen_spec.rb lib/adyen/form.rb .gitignore spec/notification_spec.rb lib/adyen/api.rb LICENSE spec/api_spec.rb init.rb adyen.gemspec Rakefile spec/form_spec.rb README.rdoc lib/adyen/notification.rb lib/adyen/formatter.rb tasks/github-gem.rake lib/adyen/encoding.rb lib/adyen/matchers.rb lib/adyen.rb)
|
29
|
+
s.test_files = %w(spec/adyen_spec.rb spec/notification_spec.rb spec/api_spec.rb spec/form_spec.rb)
|
30
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'adyen'
|
data/lib/adyen.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# The Adyen module is the container module for all Adyen related functionality,
|
2
|
+
# which is implemented in submodules. This module only contains some global
|
3
|
+
# configuration methods.
|
4
|
+
#
|
5
|
+
# The most important submodules are:
|
6
|
+
# * {Adyen::Form} for generating payment form fields, generating redirect URLs
|
7
|
+
# to the Adyen payment system, and generating and checking of signatures.
|
8
|
+
# * {Adyen::Notification} for handling notifications sent by Adyen to your servers.
|
9
|
+
# * {Adyen::SOAP} for communicating with the Adyen SOAP services for payment
|
10
|
+
# maintenance and issuing recurring payments.
|
11
|
+
module Adyen
|
12
|
+
|
13
|
+
# Version constant for the Adyen plugin.
|
14
|
+
# DO NOT CHANGE THIS VALUE BY HAND. It will be updated automatically by
|
15
|
+
# the gem:release rake task.
|
16
|
+
VERSION = "0.3.7.20100917"
|
17
|
+
|
18
|
+
# Loads configuration settings from a Hash.
|
19
|
+
#
|
20
|
+
# @param [Hash] hash The (nested Hash) with configuration variables.
|
21
|
+
# @param [Module] mod The current working module. This parameter is used
|
22
|
+
# to recursively traverse the hash for submodules.
|
23
|
+
# @raise [StandardError] An exception is raised of an unkown configuration
|
24
|
+
# setting is encountered in the hash.
|
25
|
+
def self.load_config(hash, mod = Adyen)
|
26
|
+
hash.each do |key, value|
|
27
|
+
if key.to_s =~ /^[a-z]/ && mod.respond_to?(:"#{key}=")
|
28
|
+
mod.send(:"#{key}=", value)
|
29
|
+
elsif key.to_s =~ /^[A-Z]/
|
30
|
+
self.load_config(value, mod.const_get(key))
|
31
|
+
else
|
32
|
+
raise "Unknown configuration variable: '#{key}' for #{mod}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# The Rails environment for which to use to Adyen "live" environment.
|
38
|
+
LIVE_RAILS_ENVIRONMENTS = ['production']
|
39
|
+
|
40
|
+
# Setter voor the current Adyen environment.
|
41
|
+
# @param ['test', 'live'] env The Adyen environment to use
|
42
|
+
def self.environment=(env)
|
43
|
+
@environment = env
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the current Adyen environment, either test or live.
|
47
|
+
#
|
48
|
+
# It will return the +override+ value if set, it will return the value set
|
49
|
+
# using {Adyen.environment=} otherwise. If this value also isn't set, the
|
50
|
+
# environment is determined with {Adyen.autodetect_environment}.
|
51
|
+
#
|
52
|
+
# @param ['test', 'live'] override An environment to override the default with.
|
53
|
+
# @return ['test', 'live'] The Adyen environment that is currently being used.
|
54
|
+
def self.environment(override = nil)
|
55
|
+
override || @environment || Adyen.autodetect_environment
|
56
|
+
end
|
57
|
+
|
58
|
+
# Autodetects the Adyen environment based on the RAILS_ENV constant.
|
59
|
+
# @return ['test', 'live'] The Adyen environment that corresponds to the Rails environment
|
60
|
+
def self.autodetect_environment
|
61
|
+
(defined?(RAILS_ENV) && Adyen::LIVE_RAILS_ENVIRONMENTS.include?(RAILS_ENV.to_s.downcase)) ? 'live' : 'test'
|
62
|
+
end
|
63
|
+
|
64
|
+
# Loads submodules on demand, so that dependencies are not required.
|
65
|
+
# @param [Symbol] sym The name of the submodule
|
66
|
+
# @return [Module] The actual loaded submodule.
|
67
|
+
# @raise [LoadError, NameError] If the submodule cannot be loaded
|
68
|
+
def self.const_missing(sym)
|
69
|
+
require "adyen/#{sym.to_s.downcase}"
|
70
|
+
return Adyen.const_get(sym)
|
71
|
+
rescue Exception
|
72
|
+
super(sym)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
require 'adyen/encoding'
|
77
|
+
require 'adyen/formatter'
|
data/lib/adyen/api.rb
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
require "net/https"
|
2
|
+
|
3
|
+
module Adyen
|
4
|
+
module API
|
5
|
+
class << self
|
6
|
+
# Username for the HTTP Basic Authentication that Adyen uses. Your username
|
7
|
+
# should be something like +ws@Company.MyAccount+
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :username
|
10
|
+
|
11
|
+
# Password for the HTTP Basic Authentication that Adyen uses. You can choose
|
12
|
+
# your password yourself in the user management tool of the merchant area.
|
13
|
+
# @return [String]
|
14
|
+
attr_accessor :password
|
15
|
+
|
16
|
+
attr_accessor :default_params
|
17
|
+
end
|
18
|
+
|
19
|
+
self.default_params = {}
|
20
|
+
|
21
|
+
#
|
22
|
+
# Shortcut methods
|
23
|
+
#
|
24
|
+
|
25
|
+
def self.authorise_payment(params = {})
|
26
|
+
PaymentService.new(params).authorise_payment
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.authorise_recurring_payment(params = {})
|
30
|
+
PaymentService.new(params).authorise_recurring_payment
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: the rest
|
34
|
+
|
35
|
+
#
|
36
|
+
# The actual classes
|
37
|
+
#
|
38
|
+
|
39
|
+
class SimpleSOAPClient
|
40
|
+
# from http://curl.haxx.se/ca/cacert.pem
|
41
|
+
CACERT = File.expand_path('../../../support/cacert.pem', __FILE__)
|
42
|
+
|
43
|
+
def self.endpoint
|
44
|
+
@endpoint ||= URI.parse(const_get('ENDPOINT_URI') % Adyen.environment)
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :params
|
48
|
+
|
49
|
+
def initialize(params = {})
|
50
|
+
@params = API.default_params.merge(params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def call_webservice_action(action, data)
|
54
|
+
endpoint = self.class.endpoint
|
55
|
+
|
56
|
+
post = Net::HTTP::Post.new(endpoint.path, 'Accept' => 'text/xml', 'Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => action)
|
57
|
+
post.basic_auth(API.username, API.password)
|
58
|
+
post.body = data
|
59
|
+
|
60
|
+
request = Net::HTTP.new(endpoint.host, endpoint.port)
|
61
|
+
request.use_ssl = true
|
62
|
+
request.ca_file = CACERT
|
63
|
+
request.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
64
|
+
|
65
|
+
request.start do |http|
|
66
|
+
response = http.request(post)
|
67
|
+
# TODO: handle not 2xx responses
|
68
|
+
#p response
|
69
|
+
XMLQuerier.new(response.body)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class PaymentService < SimpleSOAPClient
|
75
|
+
ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Payment'
|
76
|
+
|
77
|
+
def authorise_payment
|
78
|
+
make_payment_request(authorise_payment_request_body)
|
79
|
+
end
|
80
|
+
|
81
|
+
def authorise_recurring_payment
|
82
|
+
make_payment_request(authorise_recurring_payment_request_body)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def make_payment_request(data)
|
88
|
+
response = call_webservice_action('authorise', data)
|
89
|
+
response.xpath('//payment:authoriseResponse/payment:paymentResult') do |result|
|
90
|
+
{
|
91
|
+
:psp_reference => result.text('./payment:pspReference'),
|
92
|
+
:result_code => result.text('./payment:resultCode'),
|
93
|
+
:auth_code => result.text('./payment:authCode'),
|
94
|
+
:refusal_reason => result.text('./payment:refusalReason')
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def authorise_payment_request_body
|
100
|
+
content = card_partial
|
101
|
+
content << RECURRING_PARTIAL if @params[:recurring]
|
102
|
+
payment_request_body(content)
|
103
|
+
end
|
104
|
+
|
105
|
+
def authorise_recurring_payment_request_body
|
106
|
+
content = RECURRING_PAYMENT_BODY_PARTIAL % (@params[:recurring_detail_reference] || 'LATEST')
|
107
|
+
payment_request_body(content)
|
108
|
+
end
|
109
|
+
|
110
|
+
def payment_request_body(content)
|
111
|
+
content << amount_partial
|
112
|
+
content << shopper_partial if @params[:shopper]
|
113
|
+
LAYOUT % [@params[:merchant_account], @params[:reference], content]
|
114
|
+
end
|
115
|
+
|
116
|
+
def amount_partial
|
117
|
+
AMOUNT_PARTIAL % @params[:amount].values_at(:currency, :value)
|
118
|
+
end
|
119
|
+
|
120
|
+
def card_partial
|
121
|
+
card = @params[:card].values_at(:holder_name, :number, :cvc, :expiry_year)
|
122
|
+
card << @params[:card][:expiry_month].to_i
|
123
|
+
CARD_PARTIAL % card
|
124
|
+
end
|
125
|
+
|
126
|
+
def shopper_partial
|
127
|
+
@params[:shopper].map { |k, v| SHOPPER_PARTIALS[k] % v }.join("\n")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class RecurringService < SimpleSOAPClient
|
132
|
+
ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Recurring'
|
133
|
+
|
134
|
+
# TODO: rename to list_details and make shortcut method take the only necessary param
|
135
|
+
def list
|
136
|
+
response = call_webservice_action('listRecurringDetails', list_request_body)
|
137
|
+
response.xpath('//recurring:listRecurringDetailsResponse/recurring:result') do |result|
|
138
|
+
{
|
139
|
+
:creation_date => DateTime.parse(result.text('./recurring:creationDate')),
|
140
|
+
:details => result.xpath('.//recurring:RecurringDetail').map { |node| parse_recurring_detail(node) },
|
141
|
+
:last_known_shopper_email => result.text('./recurring:lastKnownShopperEmail'),
|
142
|
+
:shopper_reference => result.text('./recurring:shopperReference')
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def list_request_body
|
150
|
+
LAYOUT % [@params[:merchant_account], @params[:shopper][:reference]]
|
151
|
+
end
|
152
|
+
|
153
|
+
# @todo add support for elv
|
154
|
+
def parse_recurring_detail(node)
|
155
|
+
result = {
|
156
|
+
:recurring_detail_reference => node.text('./recurring:recurringDetailReference'),
|
157
|
+
:variant => node.text('./recurring:variant'),
|
158
|
+
:creation_date => DateTime.parse(node.text('./recurring:creationDate'))
|
159
|
+
}
|
160
|
+
|
161
|
+
card = node.xpath('./recurring:card')
|
162
|
+
if card.children.empty?
|
163
|
+
result[:bank] = parse_bank_details(node.xpath('./recurring:bank'))
|
164
|
+
else
|
165
|
+
result[:card] = parse_card_details(card)
|
166
|
+
end
|
167
|
+
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse_card_details(card)
|
172
|
+
{
|
173
|
+
:expiry_date => Date.new(card.text('./payment:expiryYear').to_i, card.text('./payment:expiryMonth').to_i, -1),
|
174
|
+
:holder_name => card.text('./payment:holderName'),
|
175
|
+
:number => card.text('./payment:number')
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
def parse_bank_details(bank)
|
180
|
+
{
|
181
|
+
:bank_account_number => bank.text('./payment:bankAccountNumber'),
|
182
|
+
:bank_location_id => bank.text('./payment:bankLocationId'),
|
183
|
+
:bank_name => bank.text('./payment:bankName'),
|
184
|
+
:bic => bank.text('./payment:bic'),
|
185
|
+
:country_code => bank.text('./payment:countryCode'),
|
186
|
+
:iban => bank.text('./payment:iban'),
|
187
|
+
:owner_name => bank.text('./payment:ownerName')
|
188
|
+
}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class XMLQuerier
|
193
|
+
NS = {
|
194
|
+
'payment' => 'http://payment.services.adyen.com',
|
195
|
+
'recurring' => 'http://recurring.services.adyen.com',
|
196
|
+
'common' => 'http://common.services.adyen.com'
|
197
|
+
}
|
198
|
+
|
199
|
+
class << self
|
200
|
+
attr_accessor :backend
|
201
|
+
|
202
|
+
def backend=(backend)
|
203
|
+
@backend = backend
|
204
|
+
class_eval do
|
205
|
+
private
|
206
|
+
if backend == :nokogiri
|
207
|
+
def document_for_xml(xml)
|
208
|
+
Nokogiri::XML::Document.parse(xml)
|
209
|
+
end
|
210
|
+
def perform_xpath(query)
|
211
|
+
@node.xpath(query, NS)
|
212
|
+
end
|
213
|
+
else
|
214
|
+
def document_for_xml(xml)
|
215
|
+
REXML::Document.new(xml)
|
216
|
+
end
|
217
|
+
def perform_xpath(query)
|
218
|
+
REXML::XPath.match(@node, query, NS)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
begin
|
226
|
+
require 'nokogiri'
|
227
|
+
self.backend = :nokogiri
|
228
|
+
rescue LoadError
|
229
|
+
require 'rexml/document'
|
230
|
+
self.backend = :rexml
|
231
|
+
end
|
232
|
+
|
233
|
+
def initialize(data)
|
234
|
+
@node = data.is_a?(String) ? document_for_xml(data) : data
|
235
|
+
end
|
236
|
+
|
237
|
+
def xpath(query)
|
238
|
+
result = self.class.new(perform_xpath(query))
|
239
|
+
block_given? ? yield(result) : result
|
240
|
+
end
|
241
|
+
|
242
|
+
def text(query)
|
243
|
+
xpath("#{query}/text()").to_s
|
244
|
+
end
|
245
|
+
|
246
|
+
def children
|
247
|
+
@node.first.children
|
248
|
+
end
|
249
|
+
|
250
|
+
def empty?
|
251
|
+
@node.empty?
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_s
|
255
|
+
@node.to_s
|
256
|
+
end
|
257
|
+
|
258
|
+
def map(&block)
|
259
|
+
@node.map { |n| self.class.new(n) }.map(&block)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
########################
|
266
|
+
#
|
267
|
+
# XML template constants
|
268
|
+
#
|
269
|
+
########################
|
270
|
+
|
271
|
+
module Adyen
|
272
|
+
module API
|
273
|
+
class PaymentService
|
274
|
+
LAYOUT = <<EOS
|
275
|
+
<?xml version="1.0"?>
|
276
|
+
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
277
|
+
<soap:Body>
|
278
|
+
<ns1:authorise xmlns:ns1="http://payment.services.adyen.com">
|
279
|
+
<ns1:paymentRequest>
|
280
|
+
<merchantAccount xmlns="http://payment.services.adyen.com">%s</merchantAccount>
|
281
|
+
<reference xmlns="http://payment.services.adyen.com">%s</reference>
|
282
|
+
%s
|
283
|
+
</ns1:paymentRequest>
|
284
|
+
</ns1:authorise>
|
285
|
+
</soap:Body>
|
286
|
+
</soap:Envelope>
|
287
|
+
EOS
|
288
|
+
|
289
|
+
AMOUNT_PARTIAL = <<EOS
|
290
|
+
<amount xmlns="http://payment.services.adyen.com">
|
291
|
+
<currency xmlns="http://common.services.adyen.com">%s</currency>
|
292
|
+
<value xmlns="http://common.services.adyen.com">%s</value>
|
293
|
+
</amount>
|
294
|
+
EOS
|
295
|
+
|
296
|
+
CARD_PARTIAL = <<EOS
|
297
|
+
<card xmlns="http://payment.services.adyen.com">
|
298
|
+
<holderName>%s</holderName>
|
299
|
+
<number>%s</number>
|
300
|
+
<cvc>%s</cvc>
|
301
|
+
<expiryYear>%s</expiryYear>
|
302
|
+
<expiryMonth>%02d</expiryMonth>
|
303
|
+
</card>
|
304
|
+
EOS
|
305
|
+
|
306
|
+
RECURRING_PARTIAL = <<EOS
|
307
|
+
<recurring xmlns="http://recurring.services.adyen.com">
|
308
|
+
<contract xmlns="http://payment.services.adyen.com">RECURRING</contract>
|
309
|
+
</recurring>
|
310
|
+
EOS
|
311
|
+
|
312
|
+
RECURRING_PAYMENT_BODY_PARTIAL = RECURRING_PARTIAL + <<EOS
|
313
|
+
<ns1:selectedRecurringDetailReference>%s</ns1:selectedRecurringDetailReference>
|
314
|
+
<ns1:shopperInteraction>ContAuth</ns1:shopperInteraction>
|
315
|
+
EOS
|
316
|
+
|
317
|
+
SHOPPER_PARTIALS = {
|
318
|
+
:reference => ' <shopperReference xmlns="http://payment.services.adyen.com">%s</shopperReference>',
|
319
|
+
:email => ' <shopperEmail xmlns="http://payment.services.adyen.com">%s</shopperEmail>',
|
320
|
+
:ip => ' <shopperIP xmlns="http://payment.services.adyen.com">%s</shopperIP>',
|
321
|
+
}
|
322
|
+
end
|
323
|
+
|
324
|
+
class RecurringService
|
325
|
+
LAYOUT = <<EOS
|
326
|
+
<?xml version="1.0"?>
|
327
|
+
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
328
|
+
<soap:Body>
|
329
|
+
<ns1:listRecurringDetails xmlns:ns1="http://recurring.services.adyen.com">
|
330
|
+
<ns1:request>
|
331
|
+
<ns1:recurring>
|
332
|
+
<ns1:contract>RECURRING</ns1:contract>
|
333
|
+
</ns1:recurring>
|
334
|
+
<ns1:merchantAccount>%s</ns1:merchantAccount>
|
335
|
+
<ns1:shopperReference>%s</ns1:shopperReference>
|
336
|
+
</ns1:request>
|
337
|
+
</ns1:listRecurringDetails>
|
338
|
+
</soap:Body>
|
339
|
+
</soap:Envelope>
|
340
|
+
EOS
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|