active_merchant_ideal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.textile +43 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/active_merchant_ideal.gemspec +63 -0
- data/init.rb +1 -0
- data/lib/active_merchant_ideal/ideal.rb +493 -0
- data/lib/active_merchant_ideal/ideal_response.rb +219 -0
- data/lib/active_merchant_ideal.rb +3 -0
- data/test/fixtures.yml +12 -0
- data/test/helper.rb +143 -0
- data/test/remote_ideal_test.rb +138 -0
- data/test/test_active_merchant_ideal.rb +704 -0
- metadata +106 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module ActiveMerchant #:nodoc:
|
6
|
+
module Billing #:nodoc:
|
7
|
+
# The base class for all iDEAL response classes.
|
8
|
+
#
|
9
|
+
# Note that if the iDEAL system is under load it will _not_ allow more
|
10
|
+
# then two retries per request.
|
11
|
+
class IdealResponse < Response
|
12
|
+
def initialize(response_body, options = {})
|
13
|
+
@response = REXML::Document.new(response_body).root
|
14
|
+
@success = !error_occured?
|
15
|
+
@test = options[:test]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a technical error message.
|
19
|
+
def error_message
|
20
|
+
text('//Error/errorMessage') unless success?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a consumer friendly error message.
|
24
|
+
def consumer_error_message
|
25
|
+
text('//Error/consumerMessage') unless success?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns details on the error if available.
|
29
|
+
def error_details
|
30
|
+
text('//Error/errorDetail') unless success?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an error type inflected from the first two characters of the
|
34
|
+
# error code. See error_code for a full list of errors.
|
35
|
+
#
|
36
|
+
# Error code to type mappings:
|
37
|
+
#
|
38
|
+
# * +IX+ - <tt>:xml</tt>
|
39
|
+
# * +SO+ - <tt>:system</tt>
|
40
|
+
# * +SE+ - <tt>:security</tt>
|
41
|
+
# * +BR+ - <tt>:value</tt>
|
42
|
+
# * +AP+ - <tt>:application</tt>
|
43
|
+
def error_type
|
44
|
+
unless success?
|
45
|
+
case error_code[0,2]
|
46
|
+
when 'IX' then :xml
|
47
|
+
when 'SO' then :system
|
48
|
+
when 'SE' then :security
|
49
|
+
when 'BR' then :value
|
50
|
+
when 'AP' then :application
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the code of the error that occured.
|
56
|
+
#
|
57
|
+
# === Codes
|
58
|
+
#
|
59
|
+
# ==== IX: Invalid XML and all related problems
|
60
|
+
#
|
61
|
+
# Such as incorrect encoding, invalid version, or otherwise unreadable:
|
62
|
+
#
|
63
|
+
# * <tt>IX1000</tt> - Received XML not well-formed.
|
64
|
+
# * <tt>IX1100</tt> - Received XML not valid.
|
65
|
+
# * <tt>IX1200</tt> - Encoding type not UTF-8.
|
66
|
+
# * <tt>IX1300</tt> - XML version number invalid.
|
67
|
+
# * <tt>IX1400</tt> - Unknown message.
|
68
|
+
# * <tt>IX1500</tt> - Mandatory main value missing. (Merchant ID ?)
|
69
|
+
# * <tt>IX1600</tt> - Mandatory value missing.
|
70
|
+
#
|
71
|
+
# ==== SO: System maintenance or failure
|
72
|
+
#
|
73
|
+
# The errors that are communicated in the event of system maintenance or
|
74
|
+
# system failure. Also covers the situation where new requests are no
|
75
|
+
# longer being accepted but requests already submitted will be dealt with
|
76
|
+
# (until a certain time):
|
77
|
+
#
|
78
|
+
# * <tt>SO1000</tt> - Failure in system.
|
79
|
+
# * <tt>SO1200</tt> - System busy. Try again later.
|
80
|
+
# * <tt>SO1400</tt> - Unavailable due to maintenance.
|
81
|
+
#
|
82
|
+
# ==== SE: Security and authentication errors
|
83
|
+
#
|
84
|
+
# Incorrect authentication methods and expired certificates:
|
85
|
+
#
|
86
|
+
# * <tt>SE2000</tt> - Authentication error.
|
87
|
+
# * <tt>SE2100</tt> - Authentication method not supported.
|
88
|
+
# * <tt>SE2700</tt> - Invalid electronic signature.
|
89
|
+
#
|
90
|
+
# ==== BR: Field errors
|
91
|
+
#
|
92
|
+
# Extra information on incorrect fields:
|
93
|
+
#
|
94
|
+
# * <tt>BR1200</tt> - iDEAL version number invalid.
|
95
|
+
# * <tt>BR1210</tt> - Value contains non-permitted character.
|
96
|
+
# * <tt>BR1220</tt> - Value too long.
|
97
|
+
# * <tt>BR1230</tt> - Value too short.
|
98
|
+
# * <tt>BR1240</tt> - Value too high.
|
99
|
+
# * <tt>BR1250</tt> - Value too low.
|
100
|
+
# * <tt>BR1250</tt> - Unknown entry in list.
|
101
|
+
# * <tt>BR1270</tt> - Invalid date/time.
|
102
|
+
# * <tt>BR1280</tt> - Invalid URL.
|
103
|
+
#
|
104
|
+
# ==== AP: Application errors
|
105
|
+
#
|
106
|
+
# Errors relating to IDs, account numbers, time zones, transactions:
|
107
|
+
#
|
108
|
+
# * <tt>AP1000</tt> - Acquirer ID unknown.
|
109
|
+
# * <tt>AP1100</tt> - Merchant ID unknown.
|
110
|
+
# * <tt>AP1200</tt> - Issuer ID unknown.
|
111
|
+
# * <tt>AP1300</tt> - Sub ID unknown.
|
112
|
+
# * <tt>AP1500</tt> - Merchant ID not active.
|
113
|
+
# * <tt>AP2600</tt> - Transaction does not exist.
|
114
|
+
# * <tt>AP2620</tt> - Transaction already submitted.
|
115
|
+
# * <tt>AP2700</tt> - Bank account number not 11-proof.
|
116
|
+
# * <tt>AP2900</tt> - Selected currency not supported.
|
117
|
+
# * <tt>AP2910</tt> - Maximum amount exceeded. (Detailed record states the maximum amount).
|
118
|
+
# * <tt>AP2915</tt> - Amount too low. (Detailed record states the minimum amount).
|
119
|
+
# * <tt>AP2920</tt> - Please adjust expiration period. See suggested expiration period.
|
120
|
+
def error_code
|
121
|
+
text('//errorCode') unless success?
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def error_occured?
|
127
|
+
@response.name == 'ErrorRes'
|
128
|
+
end
|
129
|
+
|
130
|
+
def text(path)
|
131
|
+
@response.get_text(path).to_s
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# An instance of IdealTransactionResponse is returned from
|
136
|
+
# IdealGateway#setup_purchase which returns the service_url to where the
|
137
|
+
# user should be redirected to perform the transaction _and_ the
|
138
|
+
# transaction ID.
|
139
|
+
class IdealTransactionResponse < IdealResponse
|
140
|
+
# Returns the URL to the issuer’s page where the consumer should be
|
141
|
+
# redirected to in order to perform the payment.
|
142
|
+
def service_url
|
143
|
+
text('//issuerAuthenticationURL')
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the transaction ID which is needed for requesting the status
|
147
|
+
# of a transaction. See IdealGateway#capture.
|
148
|
+
def transaction_id
|
149
|
+
text('//transactionID')
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the <tt>:order_id</tt> for this transaction.
|
153
|
+
def order_id
|
154
|
+
text('//purchaseID')
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# An instance of IdealStatusResponse is returned from IdealGateway#capture
|
159
|
+
# which returns whether or not the transaction that was started with
|
160
|
+
# IdealGateway#setup_purchase was successful.
|
161
|
+
#
|
162
|
+
# It takes care of checking if the message was authentic by verifying the
|
163
|
+
# the message and its signature against the iDEAL certificate.
|
164
|
+
#
|
165
|
+
# If success? returns +false+ because the authenticity wasn't verified
|
166
|
+
# there will be no error_code, error_message, and error_type. Use verified?
|
167
|
+
# to check if the authenticity has been verified.
|
168
|
+
class IdealStatusResponse < IdealResponse
|
169
|
+
def initialize(response_body, options = {})
|
170
|
+
super
|
171
|
+
@success = transaction_successful?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns the status message, which is one of: <tt>:success</tt>,
|
175
|
+
# <tt>:cancelled</tt>, <tt>:expired</tt>, <tt>:open</tt>, or
|
176
|
+
# <tt>:failure</tt>.
|
177
|
+
def status
|
178
|
+
text('//status').downcase.to_sym
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns whether or not the authenticity of the message could be
|
182
|
+
# verified.
|
183
|
+
def verified?
|
184
|
+
@verified ||= IdealGateway.ideal_certificate.public_key.
|
185
|
+
verify(OpenSSL::Digest::SHA1.new, signature, message)
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
# Checks if no errors occured _and_ if the message was authentic.
|
191
|
+
def transaction_successful?
|
192
|
+
!error_occured? && status == :success && verified?
|
193
|
+
end
|
194
|
+
|
195
|
+
# The message that we need to verify the authenticity.
|
196
|
+
def message
|
197
|
+
text('//createDateTimeStamp') + text('//transactionID') + text('//status') + text('//consumerAccountNumber')
|
198
|
+
end
|
199
|
+
|
200
|
+
def signature
|
201
|
+
Base64.decode64(text('//signatureValue'))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# An instance of IdealDirectoryResponse is returned from
|
206
|
+
# IdealGateway#issuers which returns the list of issuers available at the
|
207
|
+
# acquirer.
|
208
|
+
class IdealDirectoryResponse < IdealResponse
|
209
|
+
# Returns a list of issuers available at the acquirer.
|
210
|
+
#
|
211
|
+
# gateway.issuers.list # => [{ :id => '1006', :name => 'ABN AMRO Bank' }]
|
212
|
+
def list
|
213
|
+
@response.get_elements('//Issuer').map do |issuer|
|
214
|
+
{ :id => issuer.get_text('issuerID').to_s, :name => issuer.get_text('issuerName').to_s }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/test/fixtures.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# You can also paste the contents of the key and certificates here,
|
2
|
+
# if you want to do so remove the “_file” part of the keys.
|
3
|
+
ideal_ing_postbank:
|
4
|
+
test_url: https://idealtest.secure-ing.com:443/ideal/iDeal
|
5
|
+
merchant_id: ID
|
6
|
+
passphrase: PRIVATE KEY PASSPHRASE
|
7
|
+
private_key_file: |--
|
8
|
+
PASTE THE PATH TO YOUR PEM FILE HERE
|
9
|
+
private_certificate_file: |--
|
10
|
+
PASTE THE PATH TO YOUR CERTIFICATE FILE HERE
|
11
|
+
ideal_certificate_file: |--
|
12
|
+
PASTE THE PATH TO THE iDEAL CERTIFICATE FILE HERE
|
data/test/helper.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'mocha'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
require 'active_merchant_ideal'
|
8
|
+
|
9
|
+
ActiveMerchant::Billing::Base.mode = :test
|
10
|
+
|
11
|
+
# Test gateways
|
12
|
+
class SimpleTestGateway < ActiveMerchant::Billing::Gateway
|
13
|
+
end
|
14
|
+
|
15
|
+
class SubclassGateway < SimpleTestGateway
|
16
|
+
end
|
17
|
+
|
18
|
+
class Hash #:nodoc:
|
19
|
+
# Return a new hash with all keys converted to symbols.
|
20
|
+
def symbolize_keys
|
21
|
+
inject({}) do |options, (key, value)|
|
22
|
+
options[(key.to_sym rescue key) || key] = value
|
23
|
+
options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
# Destructively convert all keys to symbols.
|
27
|
+
def symbolize_keys!
|
28
|
+
self.replace(self.symbolize_keys)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ActiveMerchant
|
33
|
+
module Assertions
|
34
|
+
def assert_field(field, value)
|
35
|
+
clean_backtrace do
|
36
|
+
assert_equal value, @helper.fields[field]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Allows the testing of you to check for negative assertions:
|
41
|
+
#
|
42
|
+
# # Instead of
|
43
|
+
# assert !something_that_is_false
|
44
|
+
#
|
45
|
+
# # Do this
|
46
|
+
# assert_false something_that_should_be_false
|
47
|
+
#
|
48
|
+
# An optional +msg+ parameter is available to help you debug.
|
49
|
+
def assert_false(boolean, message = nil)
|
50
|
+
message = build_message message, '<?> is not false or nil.', boolean
|
51
|
+
|
52
|
+
clean_backtrace do
|
53
|
+
assert_block message do
|
54
|
+
not boolean
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# A handy little assertion to check for a successful response:
|
60
|
+
#
|
61
|
+
# # Instead of
|
62
|
+
# assert response.success?
|
63
|
+
#
|
64
|
+
# # DRY that up with
|
65
|
+
# assert_success response
|
66
|
+
#
|
67
|
+
# A message will automatically show the inspection of the response
|
68
|
+
# object if things go afoul.
|
69
|
+
def assert_success(response)
|
70
|
+
clean_backtrace do
|
71
|
+
assert response.success?, "Response failed: #{response.inspect}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The negative of +assert_success+
|
76
|
+
def assert_failure(response)
|
77
|
+
clean_backtrace do
|
78
|
+
assert_false response.success?, "Response expected to fail: #{response.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def assert_valid(validateable)
|
83
|
+
clean_backtrace do
|
84
|
+
assert validateable.valid?, "Expected to be valid"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def assert_not_valid(validateable)
|
89
|
+
clean_backtrace do
|
90
|
+
assert_false validateable.valid?, "Expected to not be valid"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def clean_backtrace(&block)
|
96
|
+
yield
|
97
|
+
rescue Test::Unit::AssertionFailedError => e
|
98
|
+
path = File.expand_path(__FILE__)
|
99
|
+
raise Test::Unit::AssertionFailedError, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
module Test
|
106
|
+
module Unit
|
107
|
+
class TestCase
|
108
|
+
HOME_DIR = RUBY_PLATFORM =~ /mswin32/ ? ENV['HOMEPATH'] : ENV['HOME'] unless defined?(HOME_DIR)
|
109
|
+
LOCAL_CREDENTIALS = File.join(HOME_DIR.to_s, '.active_merchant/fixtures.yml') unless defined?(LOCAL_CREDENTIALS)
|
110
|
+
DEFAULT_CREDENTIALS = File.join(File.dirname(__FILE__), 'fixtures.yml') unless defined?(DEFAULT_CREDENTIALS)
|
111
|
+
|
112
|
+
include ActiveMerchant::Billing
|
113
|
+
include ActiveMerchant::Assertions
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def all_fixtures
|
118
|
+
@@fixtures ||= load_fixtures
|
119
|
+
end
|
120
|
+
|
121
|
+
def fixtures(key)
|
122
|
+
data = all_fixtures[key] || raise(StandardError, "No fixture data was found for '#{key}'")
|
123
|
+
|
124
|
+
data.dup
|
125
|
+
end
|
126
|
+
|
127
|
+
def load_fixtures
|
128
|
+
file = File.exists?(LOCAL_CREDENTIALS) ? LOCAL_CREDENTIALS : DEFAULT_CREDENTIALS
|
129
|
+
yaml_data = YAML.load(File.read(file))
|
130
|
+
symbolize_keys(yaml_data)
|
131
|
+
|
132
|
+
yaml_data
|
133
|
+
end
|
134
|
+
|
135
|
+
def symbolize_keys(hash)
|
136
|
+
return unless hash.is_a?(Hash)
|
137
|
+
|
138
|
+
hash.symbolize_keys!
|
139
|
+
hash.each{|k,v| symbolize_keys(v)}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class IdealTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Base.mode = :test
|
6
|
+
setup_ideal_gateway(fixtures(:ideal_ing_postbank))
|
7
|
+
|
8
|
+
@gateway = IdealGateway.new
|
9
|
+
|
10
|
+
@valid_options = {
|
11
|
+
:issuer_id => '0151',
|
12
|
+
:expiration_period => 'PT10M',
|
13
|
+
:return_url => 'http://return_to.example.com',
|
14
|
+
:order_id => '123456789012',
|
15
|
+
:currency => 'EUR',
|
16
|
+
:description => 'A classic Dutch windmill',
|
17
|
+
:entrance_code => '1234'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_making_test_requests
|
22
|
+
assert @gateway.issuers.test?
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_setup_purchase_with_valid_options
|
26
|
+
response = @gateway.setup_purchase(550, @valid_options)
|
27
|
+
|
28
|
+
assert_success response
|
29
|
+
assert_not_nil response.service_url
|
30
|
+
assert_not_nil response.transaction_id
|
31
|
+
assert_equal @valid_options[:order_id], response.order_id
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_setup_purchase_with_invalid_amount
|
35
|
+
response = @gateway.setup_purchase(0.5, @valid_options)
|
36
|
+
|
37
|
+
assert_failure response
|
38
|
+
assert_equal "BR1210", response.error_code
|
39
|
+
assert_not_nil response.error_message
|
40
|
+
assert_not_nil response.consumer_error_message
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO: Should we raise a SecurityError instead of setting success to false?
|
44
|
+
def test_status_response_with_invalid_signature
|
45
|
+
IdealStatusResponse.any_instance.stubs(:signature).returns('db82/jpJRvKQKoiDvu33X0yoDAQpayJOaW2Y8zbR1qk1i3epvTXi+6g+QVBY93YzGv4w+Va+vL3uNmzyRjYsm2309d1CWFVsn5Mk24NLSvhYfwVHEpznyMqizALEVUNSoiSHRkZUDfXowBAyLT/tQVGbuUuBj+TKblY826nRa7U=')
|
46
|
+
response = capture_transaction(:success)
|
47
|
+
|
48
|
+
assert_failure response
|
49
|
+
assert !response.verified?
|
50
|
+
end
|
51
|
+
|
52
|
+
###
|
53
|
+
#
|
54
|
+
# These are the 7 integration tests of ING which need to be ran sucessfuly
|
55
|
+
# _before_ you'll get access to the live environment.
|
56
|
+
#
|
57
|
+
# See test_transaction_id for info on how the remote tests are ran.
|
58
|
+
#
|
59
|
+
|
60
|
+
def test_retrieval_of_issuers
|
61
|
+
assert_equal [{ :id => '0151', :name => 'Issuer Simulator' }], @gateway.issuers.list
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_successful_transaction
|
65
|
+
assert_success capture_transaction(:success)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_cancelled_transaction
|
69
|
+
captured_response = capture_transaction(:cancelled)
|
70
|
+
|
71
|
+
assert_failure captured_response
|
72
|
+
assert_equal :cancelled, captured_response.status
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_expired_transaction
|
76
|
+
captured_response = capture_transaction(:expired)
|
77
|
+
|
78
|
+
assert_failure captured_response
|
79
|
+
assert_equal :expired, captured_response.status
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_still_open_transaction
|
83
|
+
captured_response = capture_transaction(:open)
|
84
|
+
|
85
|
+
assert_failure captured_response
|
86
|
+
assert_equal :open, captured_response.status
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_failed_transaction
|
90
|
+
captured_response = capture_transaction(:failure)
|
91
|
+
|
92
|
+
assert_failure captured_response
|
93
|
+
assert_equal :failure, captured_response.status
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_internal_server_error
|
97
|
+
captured_response = capture_transaction(:server_error)
|
98
|
+
|
99
|
+
assert_failure captured_response
|
100
|
+
assert_equal 'SO1000', captured_response.error_code
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Shortcut method which does a #setup_purchase through #test_transaction and
|
106
|
+
# captures the resulting transaction and returns the capture response.
|
107
|
+
def capture_transaction(type)
|
108
|
+
@gateway.capture test_transaction(type).transaction_id
|
109
|
+
end
|
110
|
+
|
111
|
+
# Calls #setup_purchase with the amount corresponding to the named test and
|
112
|
+
# returns the response. Before returning an assertion will be ran to test
|
113
|
+
# whether or not the transaction was successful.
|
114
|
+
def test_transaction(type)
|
115
|
+
amount = case type
|
116
|
+
when :success then 100
|
117
|
+
when :cancelled then 200
|
118
|
+
when :expired then 300
|
119
|
+
when :open then 400
|
120
|
+
when :failure then 500
|
121
|
+
when :server_error then 700
|
122
|
+
end
|
123
|
+
|
124
|
+
response = @gateway.setup_purchase(amount, @valid_options)
|
125
|
+
assert response.success?
|
126
|
+
response
|
127
|
+
end
|
128
|
+
|
129
|
+
# Setup the gateway by providing a hash of aatributes and values.
|
130
|
+
def setup_ideal_gateway(fixture)
|
131
|
+
fixture = fixture.dup
|
132
|
+
if passphrase = fixture.delete(:passphrase)
|
133
|
+
IdealGateway.passphrase = passphrase
|
134
|
+
end
|
135
|
+
fixture.each { |key, value| IdealGateway.send("#{key}=", value) }
|
136
|
+
IdealGateway.live_url = nil
|
137
|
+
end
|
138
|
+
end
|