active_merchant_ideal 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|