adyen_jpiqueras 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +30 -0
  4. data/CHANGELOG.md +128 -0
  5. data/CONTRIBUTING.md +85 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +21 -0
  8. data/README.md +31 -0
  9. data/Rakefile +54 -0
  10. data/adyen_jpiqueras.gemspec +44 -0
  11. data/config.ru +5 -0
  12. data/lib/adyen.rb +16 -0
  13. data/lib/adyen/api.rb +424 -0
  14. data/lib/adyen/api/cacert.pem +3894 -0
  15. data/lib/adyen/api/payment_service.rb +374 -0
  16. data/lib/adyen/api/recurring_service.rb +188 -0
  17. data/lib/adyen/api/response.rb +61 -0
  18. data/lib/adyen/api/simple_soap_client.rb +134 -0
  19. data/lib/adyen/api/templates/payment_service.rb +159 -0
  20. data/lib/adyen/api/templates/recurring_service.rb +71 -0
  21. data/lib/adyen/api/test_helpers.rb +133 -0
  22. data/lib/adyen/api/xml_querier.rb +137 -0
  23. data/lib/adyen/base.rb +17 -0
  24. data/lib/adyen/configuration.rb +179 -0
  25. data/lib/adyen/form.rb +419 -0
  26. data/lib/adyen/hpp.rb +27 -0
  27. data/lib/adyen/hpp/request.rb +192 -0
  28. data/lib/adyen/hpp/response.rb +52 -0
  29. data/lib/adyen/hpp/signature.rb +34 -0
  30. data/lib/adyen/matchers.rb +92 -0
  31. data/lib/adyen/notification_generator.rb +30 -0
  32. data/lib/adyen/railtie.rb +13 -0
  33. data/lib/adyen/rest.rb +67 -0
  34. data/lib/adyen/rest/authorise_payment.rb +234 -0
  35. data/lib/adyen/rest/authorise_recurring_payment.rb +46 -0
  36. data/lib/adyen/rest/client.rb +127 -0
  37. data/lib/adyen/rest/errors.rb +33 -0
  38. data/lib/adyen/rest/modify_payment.rb +89 -0
  39. data/lib/adyen/rest/payout.rb +89 -0
  40. data/lib/adyen/rest/request.rb +104 -0
  41. data/lib/adyen/rest/response.rb +80 -0
  42. data/lib/adyen/rest/signature.rb +27 -0
  43. data/lib/adyen/signature.rb +76 -0
  44. data/lib/adyen/templates/notification_migration.rb +29 -0
  45. data/lib/adyen/templates/notification_model.rb +69 -0
  46. data/lib/adyen/util.rb +147 -0
  47. data/lib/adyen/version.rb +5 -0
  48. data/spec/api/api_spec.rb +231 -0
  49. data/spec/api/payment_service_spec.rb +505 -0
  50. data/spec/api/recurring_service_spec.rb +236 -0
  51. data/spec/api/response_spec.rb +59 -0
  52. data/spec/api/simple_soap_client_spec.rb +133 -0
  53. data/spec/api/spec_helper.rb +463 -0
  54. data/spec/api/test_helpers_spec.rb +84 -0
  55. data/spec/functional/api_spec.rb +117 -0
  56. data/spec/functional/initializer.rb.ci +3 -0
  57. data/spec/functional/initializer.rb.sample +3 -0
  58. data/spec/spec_helper.rb +8 -0
  59. data/test/form_test.rb +303 -0
  60. data/test/functional/payment_authorisation_api_test.rb +107 -0
  61. data/test/functional/payment_modification_api_test.rb +58 -0
  62. data/test/functional/payout_api_test.rb +93 -0
  63. data/test/helpers/capybara.rb +12 -0
  64. data/test/helpers/configure_adyen.rb +6 -0
  65. data/test/helpers/example_server.rb +136 -0
  66. data/test/helpers/public/adyen.encrypt.js +679 -0
  67. data/test/helpers/public/adyen.encrypt.min.js +14 -0
  68. data/test/helpers/test_cards.rb +20 -0
  69. data/test/helpers/views/authorized.erb +7 -0
  70. data/test/helpers/views/hpp.erb +20 -0
  71. data/test/helpers/views/index.erb +6 -0
  72. data/test/helpers/views/pay.erb +36 -0
  73. data/test/helpers/views/redirect_shopper.erb +18 -0
  74. data/test/hpp/signature_test.rb +37 -0
  75. data/test/hpp_test.rb +250 -0
  76. data/test/integration/hpp_integration_test.rb +52 -0
  77. data/test/integration/payment_using_3d_secure_integration_test.rb +41 -0
  78. data/test/integration/payment_with_client_side_encryption_integration_test.rb +26 -0
  79. data/test/rest/signature_test.rb +36 -0
  80. data/test/rest_list_recurring_details_response_test.rb +22 -0
  81. data/test/rest_request_test.rb +43 -0
  82. data/test/rest_response_test.rb +19 -0
  83. data/test/signature_test.rb +76 -0
  84. data/test/test_helper.rb +45 -0
  85. data/test/util_test.rb +78 -0
  86. data/yard_extensions.rb +16 -0
  87. metadata +308 -0
@@ -0,0 +1,29 @@
1
+ # @private
2
+ class CreateAdyenNotifications < ActiveRecord::Migration
3
+
4
+ def self.up
5
+ create_table :adyen_notifications do |t|
6
+ t.boolean :live, :null => false, :default => false
7
+ t.string :event_code, :null => false, :limit => 40
8
+ t.string :psp_reference, :null => false, :limit => 50
9
+ t.string :original_reference, :null => true
10
+ t.string :merchant_reference, :null => false
11
+ t.string :merchant_account_code, :null => false
12
+ t.datetime :event_date, :null => false
13
+ t.boolean :success, :null => false, :default => false
14
+ t.string :payment_method, :null => true
15
+ t.string :operations, :null => true
16
+ t.text :reason, :null => true
17
+ t.string :currency, :null => true, :limit => 3
18
+ t.integer :value, :null => true
19
+ t.boolean :processed, :null => false, :default => false
20
+ t.timestamps
21
+ end
22
+
23
+ add_index :adyen_notifications, [:merchant_account_code, :psp_reference, :event_code, :success], :unique => true, :name => 'adyen_notification_uniqueness'
24
+ end
25
+
26
+ def self.down
27
+ drop_table :adyen_notifications
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ # The +AdyenNotification+ class handles notifications sent by Adyen to your servers.
2
+ #
3
+ # Because notifications contain important payment status information, you should store
4
+ # these notifications in your database. For this reason, +AdyenNotification+ inherits
5
+ # from +ActiveRecord::Base+, and a migration is included to simply create a suitable table
6
+ # to store the notifications in.
7
+ #
8
+ # Adyen can either send notifications to you via HTTP POST requests, or SOAP requests.
9
+ # Because SOAP is not really well supported in Rails and setting up a SOAP server is
10
+ # not trivial, only handling HTTP POST notifications is currently supported.
11
+ #
12
+ # @example
13
+ # @notification = AdyenNotification.log(request)
14
+ # if @notification.successful_authorisation?
15
+ # @invoice = Invoice.find(@notification.merchant_reference)
16
+ # @invoice.set_paid!
17
+ # end
18
+ class AdyenNotification < ActiveRecord::Base
19
+ # A notification should always include an event_code
20
+ validates_presence_of :event_code
21
+
22
+ # A notification should always include a psp_reference
23
+ validates_presence_of :psp_reference
24
+
25
+ # A notification should be unique using the composed key of
26
+ # [:merchant_account_code, :psp_reference, :event_code, :success]
27
+ validates_uniqueness_of :success, :scope => [:merchant_account_code, :psp_reference, :event_code]
28
+
29
+ # Make sure we don't end up with an original_reference with an empty string
30
+ before_validation { |notification| notification.original_reference = nil if notification.original_reference.blank? }
31
+
32
+ # Logs an incoming notification into the database.
33
+ #
34
+ # @param [Hash] params The notification parameters that should be stored in the database.
35
+ # @return [Adyen::Notification] The initiated and persisted notification instance.
36
+ # @raise This method will raise an exception if the notification cannot be stored.
37
+ # @see Adyen::Notification::HttpPost.log
38
+ def self.log(params)
39
+ # Assign explicit each attribute from CamelCase notation to notification
40
+ # For example, merchantReference will be converted to merchant_reference
41
+ self.new.tap do |notification|
42
+ params.each do |key, value|
43
+ setter = "#{key.to_s.underscore}="
44
+ notification.send(setter, value) if notification.respond_to?(setter)
45
+ end
46
+
47
+ notification.save!
48
+ end
49
+ end
50
+
51
+ # Returns true if this notification is an AUTHORISATION notification
52
+ # @return [true, false] true iff event_code == 'AUTHORISATION'
53
+ # @see Adyen.notification#successful_authorisation?
54
+ def authorisation?
55
+ event_code == 'AUTHORISATION'
56
+ end
57
+
58
+ alias_method :authorization?, :authorisation?
59
+
60
+ # Returns true if this notification is an AUTHORISATION notification and
61
+ # the success status indicates that the authorization was successfull.
62
+ # @return [true, false] true iff the notification is an authorization
63
+ # and the authorization was successful according to the success field.
64
+ def successful_authorisation?
65
+ authorisation? && success?
66
+ end
67
+
68
+ alias_method :successful_authorization?, :successful_authorisation?
69
+ end
data/lib/adyen/util.rb ADDED
@@ -0,0 +1,147 @@
1
+ require 'date'
2
+ require 'openssl'
3
+ require 'base64'
4
+
5
+ module Adyen
6
+ module Util
7
+ extend self
8
+
9
+ # Returns a valid Adyen string representation for a date
10
+ def format_date(date)
11
+ case date
12
+ when Date, DateTime, Time
13
+ date.strftime('%Y-%m-%d')
14
+ when String
15
+ raise ArgumentError, "Invalid date notation: #{date.inspect}!" unless /^\d{4}-\d{2}-\d{2}$/ =~ date
16
+ date
17
+ else
18
+ raise ArgumentError, "Cannot convert #{date.inspect} to date!"
19
+ end
20
+ end
21
+
22
+ # Returns a valid Adyen string representation for a timestamp
23
+ def format_timestamp(time)
24
+ case time
25
+ when Date, DateTime, Time
26
+ time.strftime('%Y-%m-%dT%H:%M:%SZ')
27
+ when String
28
+ raise ArgumentError, "Invalid timestamp notation: #{time.inspect}!" unless /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z$/ =~ time
29
+ time
30
+ else
31
+ raise ArgumentError, "Cannot convert #{time.inspect} to timestamp!"
32
+ end
33
+ end
34
+
35
+ # Returns a base64-encoded signature for a message
36
+ # @param [String] hmac_key The secret key to use for the HMAC signature.
37
+ # @param [String] message The message to sign.
38
+ # @return [String] The signature, base64-encoded.
39
+ def hmac_base64(hmac_key, message)
40
+ digest = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), hmac_key, message)
41
+ Base64.strict_encode64(digest).strip
42
+ end
43
+
44
+ # Retuns a message gzip-compressed and base64-encoded.
45
+ # @param [String] message The message to compress and encode.
46
+ # @return [String] The compressed and encoded version of the message
47
+ def gzip_base64(message)
48
+ sio = StringIO.new
49
+ gz = Zlib::GzipWriter.new(sio)
50
+ gz.write(message)
51
+ gz.close
52
+ Base64.strict_encode64(sio.string)
53
+ end
54
+
55
+ # Returns the camelized version of a string.
56
+ # @param [:to_s] identifier The identifier to turn to camelcase.
57
+ # @return [String] The camelcase version of the identifier provided.
58
+ def camelize(identifier)
59
+ CAMELCASE_EXCEPTIONS[identifier.to_s] || identifier.to_s.gsub(/_+(.)/) { $1.upcase }
60
+ end
61
+
62
+ # Returns the underscore version of a string.
63
+ # @param [:to_s] identifier The identifier to turn to underscore notation.
64
+ # @return [String] The underscore version of the identifier provided.
65
+ def underscore(identifier)
66
+ UNDERSCORE_EXCEPTIONS[identifier.to_s] || identifier.to_s
67
+ .gsub(/([A-Z]{2,})([A-Z])/) { "#{$1.downcase}#{$2}" }
68
+ .gsub(/(?!\A)([A-Z][a-z]*)/, '_\1')
69
+ .downcase
70
+ end
71
+
72
+ # Transforms the nested parameters Hash into a 'flat' Hash which is understood by adyen. This is:
73
+ # * all keys are camelized
74
+ # * all keys are stringified
75
+ # * nested hash is flattened, keys are prefixed with root key
76
+ #
77
+ # @example
78
+ # flatten {:billing_address => { :street => 'My Street'}}
79
+ #
80
+ # # resolves in:
81
+ # {'billingAddress.street' => 'My Street'}
82
+ #
83
+ # @param [Hash] nested_hash The nested hash to transform
84
+ # @param [String] prefix The prefix to add to the key
85
+ # @param [Hash] return_hash The new hash which is retruned (needed for recursive calls)
86
+ # @return [Hash] The return hash filled with camelized and prefixed key, stringified value
87
+ def flatten(nested_hash, prefix = "", return_hash = {})
88
+ nested_hash ||= {}
89
+ nested_hash.inject(return_hash) do |hash, (key, value)|
90
+ key = "#{prefix}#{camelize(key)}"
91
+ if value.is_a?(Hash)
92
+ flatten(value, "#{key}.", return_hash)
93
+ else
94
+ hash[key] = value.to_s
95
+ end
96
+ hash
97
+ end
98
+ end
99
+
100
+ # Transforms a flat hash into a nested hash structure.
101
+ # * all keys are underscored
102
+ # * all keys are stringified
103
+ # * flattened hash is deflattened, using . as namespace separator
104
+ #
105
+ # @example
106
+ # deflatten {'billingAddress.street' => 'My Street'}
107
+ #
108
+ # # resolves in:
109
+ # {'billing_address' => { 'street' => 'My Street'}}
110
+ #
111
+ # @param [Hash] flattened_hash The flat hash to transform
112
+ # @param [Hash] return_hash The new hash which will be returned (needed for recursive calls)
113
+ # @return [Hash] A nested hash structure, using strings as key.
114
+ def deflatten(flattened_hash, return_hash = {})
115
+ return return_hash if flattened_hash.nil?
116
+ flattened_hash.each do |key, value|
117
+ deflatten_pair(key, value, return_hash)
118
+ end
119
+ return_hash
120
+ end
121
+
122
+ private
123
+
124
+ def deflatten_pair(key, value, return_hash)
125
+ head, rest = key.split('.', 2)
126
+ key = underscore(head)
127
+ if rest.nil?
128
+ raise ArgumentError, "Duplicate key in flattened hash." if return_hash.key?(key)
129
+ return_hash[key] = value
130
+ else
131
+ return_hash[key] ||= {}
132
+ raise ArgumentError, "Key nesting conflict in flattened hash." unless return_hash[key].is_a?(Hash)
133
+ deflatten_pair(rest, value, return_hash[key])
134
+ end
135
+ end
136
+
137
+ # This hash contains exceptions to the standard underscore to camelcase conversion rules.
138
+ CAMELCASE_EXCEPTIONS = {
139
+ 'shopper_ip' => 'shopperIP'
140
+ }
141
+
142
+ # This hash contains exceptions to the standard camelcase to underscore conversion rules.
143
+ UNDERSCORE_EXCEPTIONS = CAMELCASE_EXCEPTIONS.invert
144
+
145
+ private_constant :CAMELCASE_EXCEPTIONS, :UNDERSCORE_EXCEPTIONS
146
+ end
147
+ end
@@ -0,0 +1,5 @@
1
+ module Adyen
2
+ # Version constant for the Adyen plugin.
3
+ # Set it & commit the change before running rake release.
4
+ VERSION = "2.3.0"
5
+ end
@@ -0,0 +1,231 @@
1
+ # encoding: UTF-8
2
+ require 'api/spec_helper'
3
+
4
+ describe Adyen::API do
5
+ include APISpecHelper
6
+
7
+ describe "shortcut methods" do
8
+ describe "for the PaymentService" do
9
+ before do
10
+ @payment = double('PaymentService')
11
+ end
12
+
13
+ def should_map_shortcut_to(method, params)
14
+ Adyen::API::PaymentService.should_receive(:new).with(params).and_return(@payment)
15
+ @payment.should_receive(method)
16
+ end
17
+
18
+ it "performs a `authorise payment' request without enabling :recurring" do
19
+ should_map_shortcut_to(:authorise_payment,
20
+ :reference => 'order-id',
21
+ :amount => { :currency => 'EUR', :value => 1234 },
22
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
23
+ :card => { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' },
24
+ :recurring => false,
25
+ :fraud_offset => nil,
26
+ :instant_capture => false
27
+ )
28
+ Adyen::API.authorise_payment('order-id',
29
+ { :currency => 'EUR', :value => 1234 },
30
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
31
+ { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' }
32
+ )
33
+ end
34
+
35
+ it "performs a `authorise payment' request with additional :fraud_offset" do
36
+ should_map_shortcut_to(:authorise_payment,
37
+ :reference => 'order-id',
38
+ :amount => { :currency => 'EUR', :value => 1234 },
39
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
40
+ :card => { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' },
41
+ :recurring => false,
42
+ :fraud_offset => -100,
43
+ :instant_capture => false
44
+ )
45
+ Adyen::API.authorise_payment('order-id',
46
+ { :currency => 'EUR', :value => 1234 },
47
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
48
+ { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' },
49
+ false,
50
+ -100,
51
+ false
52
+ )
53
+ end
54
+
55
+ it "performs a `authorise payment' request with enabling :recurring" do
56
+ should_map_shortcut_to(:authorise_payment,
57
+ :reference => 'order-id',
58
+ :amount => { :currency => 'EUR', :value => 1234 },
59
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
60
+ :card => { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' },
61
+ :recurring => true,
62
+ :fraud_offset => nil,
63
+ :instant_capture => false,
64
+ )
65
+ Adyen::API.authorise_payment('order-id',
66
+ { :currency => 'EUR', :value => 1234 },
67
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
68
+ { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111', :cvc => '737' },
69
+ true,
70
+ nil,
71
+ false
72
+ )
73
+ end
74
+
75
+ it "performs a `authorise recurring payment' request without specific detail" do
76
+ should_map_shortcut_to(:authorise_recurring_payment,
77
+ :reference => 'order-id',
78
+ :amount => { :currency => 'EUR', :value => 1234 },
79
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
80
+ :recurring_detail_reference => 'LATEST',
81
+ :instant_capture => false,
82
+ :fraud_offset => nil
83
+ )
84
+ Adyen::API.authorise_recurring_payment('order-id',
85
+ { :currency => 'EUR', :value => 1234 },
86
+ { :reference => 'user-id', :email => 's.hopper@example.com' }
87
+ )
88
+ end
89
+
90
+ it "performs a `authorise recurring payment' request with specific detail" do
91
+ should_map_shortcut_to(:authorise_recurring_payment,
92
+ :reference => 'order-id',
93
+ :amount => { :currency => 'EUR', :value => 1234 },
94
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
95
+ :recurring_detail_reference => 'recurring-detail-reference',
96
+ :instant_capture => false,
97
+ :fraud_offset => nil
98
+ )
99
+ Adyen::API.authorise_recurring_payment('order-id',
100
+ { :currency => 'EUR', :value => 1234 },
101
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
102
+ 'recurring-detail-reference'
103
+ )
104
+ end
105
+
106
+ it "performs a `authorise recurring payment' request with specific detail and fraud offset" do
107
+ should_map_shortcut_to(:authorise_recurring_payment,
108
+ :reference => 'order-id',
109
+ :amount => { :currency => 'EUR', :value => 1234 },
110
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
111
+ :recurring_detail_reference => 'recurring-detail-reference',
112
+ :fraud_offset => 50,
113
+ :instant_capture => false
114
+ )
115
+ Adyen::API.authorise_recurring_payment('order-id',
116
+ { :currency => 'EUR', :value => 1234 },
117
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
118
+ 'recurring-detail-reference',
119
+ 50,
120
+ false
121
+ )
122
+ end
123
+
124
+ it "performs a `authorise one-click payment' request with specific detail" do
125
+ should_map_shortcut_to(:authorise_one_click_payment,
126
+ :reference => 'order-id',
127
+ :amount => { :currency => 'EUR', :value => 1234 },
128
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
129
+ :card => { :cvc => '737' },
130
+ :recurring_detail_reference => 'recurring-detail-reference',
131
+ :fraud_offset => nil,
132
+ :instant_capture => false
133
+ )
134
+ Adyen::API.authorise_one_click_payment('order-id',
135
+ { :currency => 'EUR', :value => 1234 },
136
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
137
+ { :cvc => '737' },
138
+ 'recurring-detail-reference'
139
+ )
140
+ end
141
+
142
+ it "performs a `authorise one-click payment' request with specific detail and fraud offset" do
143
+ should_map_shortcut_to(:authorise_one_click_payment,
144
+ :reference => 'order-id',
145
+ :amount => { :currency => 'EUR', :value => 1234 },
146
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
147
+ :card => { :cvc => '737' },
148
+ :recurring_detail_reference => 'recurring-detail-reference',
149
+ :fraud_offset => -10,
150
+ :instant_capture => false,
151
+ )
152
+ Adyen::API.authorise_one_click_payment('order-id',
153
+ { :currency => 'EUR', :value => 1234 },
154
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
155
+ { :cvc => '737' },
156
+ 'recurring-detail-reference',
157
+ -10,
158
+ false
159
+ )
160
+ end
161
+
162
+ it "performs a `capture' request" do
163
+ should_map_shortcut_to(:capture, :psp_reference => 'original-psp-reference', :amount => { :currency => 'EUR', :value => '1234' })
164
+ Adyen::API.capture_payment('original-psp-reference', { :currency => 'EUR', :value => '1234' })
165
+ end
166
+
167
+ it "performs a `refund payment' request" do
168
+ should_map_shortcut_to(:refund, :psp_reference => 'original-psp-reference', :amount => { :currency => 'EUR', :value => '1234' })
169
+ Adyen::API.refund_payment('original-psp-reference', { :currency => 'EUR', :value => '1234' })
170
+ end
171
+
172
+ it "performs a `cancel or refund payment' request" do
173
+ should_map_shortcut_to(:cancel_or_refund, :psp_reference => 'original-psp-reference')
174
+ Adyen::API.cancel_or_refund_payment('original-psp-reference')
175
+ end
176
+
177
+ it "performs a `cancel payment' request" do
178
+ should_map_shortcut_to(:cancel, :psp_reference => 'original-psp-reference')
179
+ Adyen::API.cancel_payment('original-psp-reference')
180
+ end
181
+ end
182
+
183
+ describe "for the RecurringService" do
184
+ before do
185
+ @recurring = double('RecurringService')
186
+ end
187
+
188
+ def should_map_shortcut_to(method, params)
189
+ Adyen::API::RecurringService.should_receive(:new).with(params).and_return(@recurring)
190
+ @recurring.should_receive(method)
191
+ end
192
+
193
+ it "performs a `tokenize creditcard details' request" do
194
+ should_map_shortcut_to(:store_token,
195
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
196
+ :card => { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111' }
197
+ )
198
+ Adyen::API.store_recurring_token(
199
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
200
+ { :expiry_month => 12, :expiry_year => 2012, :holder_name => "Simon Hopper", :number => '4444333322221111' }
201
+ )
202
+ end
203
+
204
+ it "performs a `tokenize ELV details' request" do
205
+ should_map_shortcut_to(:store_token,
206
+ :shopper => { :reference => 'user-id', :email => 's.hopper@example.com' },
207
+ :elv => { :bank_location => "Berlin", :bank_name => "TestBank", :bank_location_id => "12345678", :holder_name => "Simon Hopper", :number => "1234567890" }
208
+ )
209
+ Adyen::API.store_recurring_token(
210
+ { :reference => 'user-id', :email => 's.hopper@example.com' },
211
+ { :bank_location => "Berlin", :bank_name => "TestBank", :bank_location_id => "12345678", :holder_name => "Simon Hopper", :number => "1234567890" }
212
+ )
213
+ end
214
+
215
+ it "preforms a `list recurring details' request" do
216
+ should_map_shortcut_to(:list, :shopper => { :reference => 'user-id' })
217
+ Adyen::API.list_recurring_details('user-id')
218
+ end
219
+
220
+ it "performs a `disable recurring contract' request for all details" do
221
+ should_map_shortcut_to(:disable, :shopper => { :reference => 'user-id' }, :recurring_detail_reference => nil)
222
+ Adyen::API.disable_recurring_contract('user-id')
223
+ end
224
+
225
+ it "performs a `disable recurring contract' request for a specific detail" do
226
+ should_map_shortcut_to(:disable, :shopper => { :reference => 'user-id' }, :recurring_detail_reference => 'detail-id')
227
+ Adyen::API.disable_recurring_contract('user-id', 'detail-id')
228
+ end
229
+ end
230
+ end
231
+ end