geti 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/Gemfile +20 -0
- data/Gemfile.lock +78 -0
- data/Guardfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +22 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/config/test_credentials.yml.example +2 -0
- data/doc/.DS_Store +0 -0
- data/doc/Application Gateway Doc.pdf +0 -0
- data/doc/Auth Gateway Doc.pdf +0 -0
- data/doc/Auth Gateway Implementation Guide.pdf +0 -0
- data/doc/implementation_notes.md +29 -0
- data/doc/readme.txt +28 -0
- data/lib/geti.rb +7 -0
- data/lib/geti/app_client.rb +277 -0
- data/lib/geti/auth_client.rb +133 -0
- data/lib/geti/client.rb +42 -0
- data/lib/geti/response.rb +24 -0
- data/lib/geti/terminal_settings.rb +7 -0
- data/spec/geti_app_client_spec.rb +166 -0
- data/spec/geti_auth_client_spec.rb +28 -0
- data/spec/helper.rb +37 -0
- data/spec/remote/geti_app_client_spec.rb +50 -0
- data/spec/remote/geti_auth_client_spec.rb +99 -0
- metadata +193 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
class Geti::AuthClient < Geti::Client
|
2
|
+
def initialize(auth, terminal_opts, env='test')
|
3
|
+
super
|
4
|
+
|
5
|
+
@sec_code = terminal_opts[:sec_code]
|
6
|
+
@verify_check = terminal_opts[:verify].include? :check
|
7
|
+
@verify_id = terminal_opts[:verify].include? :identity
|
8
|
+
@dl_required = terminal_opts[:verify].include? :dl
|
9
|
+
end
|
10
|
+
|
11
|
+
# Loads terminal settings for the configured terminal. Returns a
|
12
|
+
# TerminalSettings object that can be used to confirm requested
|
13
|
+
# verification features, terminal ID, and XSD/XML templates.
|
14
|
+
def get_terminal_settings
|
15
|
+
Geti::TerminalSettings.new(soap_request("GetCertificationTerminalSettings"))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Used to verify that an XML request is valid for use on the
|
19
|
+
# terminal. The returned Result will have a validation response
|
20
|
+
# but no authorization.
|
21
|
+
# NOTE: CERTIFICATION SERVER ONLY
|
22
|
+
def validate(opts)
|
23
|
+
response = soap_request("AuthGatewayCertification") do |xml|
|
24
|
+
data_packet(xml, opts)
|
25
|
+
end
|
26
|
+
Geti::Response.new(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates an authorization for funds transfer. Returns a Result
|
30
|
+
# with both validation and (if valid) authorization responses.
|
31
|
+
def process(opts)
|
32
|
+
response = soap_request("ProcessSingleCertificationCheck") do |xml|
|
33
|
+
data_packet(xml, opts)
|
34
|
+
end
|
35
|
+
Geti::Response.new(response)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def data_packet(xml, opts)
|
40
|
+
xml.AUTH_GATEWAY do # has an optional REQUEST_ID attribute for later lookups
|
41
|
+
xml.TRANSACTION do
|
42
|
+
xml.TRANSACTION_ID
|
43
|
+
xml.MERCHANT do
|
44
|
+
xml.TERMINAL_ID terminal_id
|
45
|
+
end
|
46
|
+
xml.PACKET do
|
47
|
+
xml.IDENTIFIER identifier(opts[:type])
|
48
|
+
xml.ACCOUNT do
|
49
|
+
xml.ROUTING_NUMBER opts[:routing_number]
|
50
|
+
xml.ACCOUNT_NUMBER opts[:account_number]
|
51
|
+
xml.ACCOUNT_TYPE opts[:account_type]
|
52
|
+
end
|
53
|
+
xml.CONSUMER do
|
54
|
+
xml.FIRST_NAME opts[:first_name]
|
55
|
+
xml.LAST_NAME opts[:last_name]
|
56
|
+
xml.ADDRESS1
|
57
|
+
xml.ADDRESS2
|
58
|
+
xml.CITY
|
59
|
+
xml.STATE
|
60
|
+
xml.ZIP
|
61
|
+
xml.PHONE_NUMBER
|
62
|
+
xml.DL_STATE
|
63
|
+
xml.DL_NUMBER
|
64
|
+
xml.COURTESY_CARD_ID
|
65
|
+
if @verify_id
|
66
|
+
xml.IDENTITY do
|
67
|
+
xml.SSN4
|
68
|
+
xml.DOB_YEAR
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
xml.CHECK do
|
73
|
+
xml.CHECK_AMOUNT("%.2d" % ((opts[:amount]||0)/100.0))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def identifier(name)
|
81
|
+
{ :authorize => 'A',
|
82
|
+
:void => 'V',
|
83
|
+
:override => 'O',
|
84
|
+
:payroll => 'P',
|
85
|
+
:recurring => 'R'
|
86
|
+
}[name]
|
87
|
+
end
|
88
|
+
|
89
|
+
def service_address
|
90
|
+
"https://demo.eftchecks.com/webservices/AuthGateway.asmx?WSDL"
|
91
|
+
end
|
92
|
+
|
93
|
+
def soap_header
|
94
|
+
{ "AuthGatewayHeader" => {
|
95
|
+
"UserName" => @user,
|
96
|
+
"Password" => @pass,
|
97
|
+
"TerminalID" => terminal_id.to_s
|
98
|
+
},
|
99
|
+
:attributes! => { 'AuthGatewayHeader' => {'xmlns'=>"http://tempuri.org/GETI.eMagnus.WebServices/AuthGateway"}}
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def terminal_id
|
104
|
+
base = {
|
105
|
+
# Guaranteed
|
106
|
+
'PPD' => 1010, # Debit-only
|
107
|
+
'POP' => 1110,
|
108
|
+
'TEL' => 1210,
|
109
|
+
'C21' => 1610,
|
110
|
+
'CCD' => 1710, # Debit only
|
111
|
+
'PPD_cr' => 1810, # Debit and Credit
|
112
|
+
'CCD_cr' => 1910, # Debit and Credit
|
113
|
+
# Non-Guaranteed
|
114
|
+
'PPD_ng' => 2010,
|
115
|
+
'TEL_ng' => 2210,
|
116
|
+
'WEB' => 2310, # Web is always non-guaranteed
|
117
|
+
'CCD_ng' => 2710,
|
118
|
+
'PPD_ng_cr' => 2810,
|
119
|
+
'CCD_ng_cr' => 2910
|
120
|
+
}[@sec_code]
|
121
|
+
offset = {
|
122
|
+
[false, false, false] => 0,
|
123
|
+
[true, false, false] => 1,
|
124
|
+
[false, true, true ] => 2,
|
125
|
+
[true, true, true ] => 3,
|
126
|
+
[false, true, false] => 4,
|
127
|
+
[true, true, false] => 5,
|
128
|
+
[false, false, true ] => 6,
|
129
|
+
[true, false, true ] => 7
|
130
|
+
}[[@dl_required, @verify_check, @verify_id]]
|
131
|
+
base + offset
|
132
|
+
end
|
133
|
+
end
|
data/lib/geti/client.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'savon'
|
2
|
+
require 'httpi'
|
3
|
+
|
4
|
+
class Geti::Client
|
5
|
+
def initialize(auth, terminal_opts, env='test')
|
6
|
+
@user = auth[:user]
|
7
|
+
@pass = auth[:pass]
|
8
|
+
@env = env
|
9
|
+
end
|
10
|
+
|
11
|
+
def soap_client
|
12
|
+
@soap_client ||= Savon.client(service_address)
|
13
|
+
end
|
14
|
+
|
15
|
+
def soap_request(operation, op_key=nil)
|
16
|
+
operation.sub!('Certification','') unless certification?
|
17
|
+
response = soap_client.request operation do
|
18
|
+
http.headers.delete('SOAPAction')
|
19
|
+
config.soap_header = soap_header
|
20
|
+
xml = Builder::XmlMarkup.new
|
21
|
+
xml.instruct!
|
22
|
+
yield xml if block_given?
|
23
|
+
content = xml.target!
|
24
|
+
soap.body = {"DataPacket" => content}
|
25
|
+
end
|
26
|
+
|
27
|
+
op_key ||= operation.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
28
|
+
operation.sub!('_certification','') unless certification?
|
29
|
+
response_key = (op_key+'_response').to_sym
|
30
|
+
result_key = (op_key+'_result').to_sym
|
31
|
+
|
32
|
+
xml_parser.parse(response.body[response_key][result_key])
|
33
|
+
end
|
34
|
+
|
35
|
+
def certification?
|
36
|
+
@env != 'production'
|
37
|
+
end
|
38
|
+
|
39
|
+
def xml_parser
|
40
|
+
@xml_parser or Nori
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class Geti::Response
|
4
|
+
attr_reader :validation, :authorization, :exception
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@validation = OpenStruct.new(response[:response][:validation_message])
|
8
|
+
@authorization = OpenStruct.new(response[:response][:authorization_message])
|
9
|
+
@exception = OpenStruct.new(response[:response][:exception])
|
10
|
+
end
|
11
|
+
|
12
|
+
def errors
|
13
|
+
err = []
|
14
|
+
Array(@validation.validation_error).each do |e|
|
15
|
+
err << e[:message]
|
16
|
+
end
|
17
|
+
err << @exception.message
|
18
|
+
err.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
def success?
|
22
|
+
validation.result == "Passed"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Geti::AppClient do
|
4
|
+
def mock_soap!(client, parsed_response, operation, op_key=nil)
|
5
|
+
op_key ||= operation.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
6
|
+
response_key = (op_key+'_response').to_sym
|
7
|
+
result_key = (op_key+'_result').to_sym
|
8
|
+
|
9
|
+
data = OpenStruct.new(:body => {response_key => {result_key => :encoded_xml}})
|
10
|
+
|
11
|
+
client.soap_client.should_receive(:request).with(operation).and_return(data)
|
12
|
+
client.xml_parser.should_receive(:parse).with(:encoded_xml).and_return(parsed_response)
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_payload
|
16
|
+
{
|
17
|
+
:id => 123456,
|
18
|
+
:name => "Cogsley's Cogs",
|
19
|
+
:industry => "Metal_Fabricators",
|
20
|
+
:address => "123 Main St",
|
21
|
+
:city => "Vancouver",
|
22
|
+
:state => "WA",
|
23
|
+
:zip => "10120",
|
24
|
+
:phone => "5555551234",
|
25
|
+
:business_type => "Corporation",
|
26
|
+
:days_in_business => 404,
|
27
|
+
|
28
|
+
:contact_name => 'George Jetson',
|
29
|
+
:physical_address => "123 Main St",
|
30
|
+
:physical_city => "Vancouver",
|
31
|
+
:physical_state => "WA",
|
32
|
+
:physical_zip => "10120",
|
33
|
+
:physical_phone => "5555551234",
|
34
|
+
|
35
|
+
:principal_first_name => "Carl",
|
36
|
+
:principal_last_name => "Cogsley",
|
37
|
+
:principal_title => 'President',
|
38
|
+
:principal_address => "123 Main St",
|
39
|
+
:principal_city => "Vancouver",
|
40
|
+
:principal_state => "WA",
|
41
|
+
:principal_zip => "10120",
|
42
|
+
:principal_dob => "1965-04-28",
|
43
|
+
:principal_ssn => '111222123',
|
44
|
+
|
45
|
+
:average_amount => "4000",
|
46
|
+
:max_amount => "7600",
|
47
|
+
|
48
|
+
:taxpayer_name => "Carl Cogsley",
|
49
|
+
:taxpayer_id => "123456789",
|
50
|
+
|
51
|
+
:routing_number => "490000018",
|
52
|
+
:account_number => "123456789",
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def success_response
|
57
|
+
{:response=>{
|
58
|
+
:status=>"Approved",
|
59
|
+
:message=>"1 merchant(s) created.\n0 merchant(s) not created due to errors.\n\n-----------------------\nMerchants Created:\nCogsley's Cogs (ISO ID: 9999, CrossRef: 123456, Status: AppApprovedandActivated)\n\n",
|
60
|
+
:app_data=>{
|
61
|
+
:merchant=>{
|
62
|
+
:@name=>"Cogsley's Cogs",
|
63
|
+
:@active=>"1",
|
64
|
+
:@type=>"Merchant",
|
65
|
+
:@cross_ref_id=>"123456",
|
66
|
+
:@id=>"20",
|
67
|
+
:location=>{
|
68
|
+
:terminal=>{
|
69
|
+
:@manual_entry=>"N",
|
70
|
+
:@name=>"Lipman Nurit 3000-01 (111163) ",
|
71
|
+
:@active=>"1",
|
72
|
+
:@type=>"Terminal",
|
73
|
+
:@cross_ref_id=>"41680",
|
74
|
+
:@id=>"111163",
|
75
|
+
:@mid=>"101-111163-606"},
|
76
|
+
:@name=>"Cogsley's Cogs ",
|
77
|
+
:@active=>"1",
|
78
|
+
:@ach_name=>"COGSLEYSCOGS",
|
79
|
+
:@type=>"Location",
|
80
|
+
:@cross_ref_id=>"123456",
|
81
|
+
:@id=>"31"},
|
82
|
+
:poc1=>{
|
83
|
+
:@password=>"UGPRDGIX",
|
84
|
+
:@user_name=>"CCogsley",
|
85
|
+
:@last_name=>"Cogsley",
|
86
|
+
:@first_name=>"Carl"}}},
|
87
|
+
:"@xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
|
88
|
+
:"@xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
89
|
+
:validation_message=>{:result=>"Passed", :schema_file_path=>nil}}}
|
90
|
+
end
|
91
|
+
|
92
|
+
def error_response
|
93
|
+
{:response=>{
|
94
|
+
:"@xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
|
95
|
+
:"@xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
96
|
+
:validation_message=>
|
97
|
+
{:result=>"Failed",
|
98
|
+
:schema_file_path=>
|
99
|
+
"http://demo.eftchecks.com/WebServices/schemas/app/NewMerchApp_ACH.xsd",
|
100
|
+
:validation_error=>
|
101
|
+
[{:@line_number=>"1",
|
102
|
+
:severity=>"Error",
|
103
|
+
:message=>"The 'pocAddress1' attribute is not declared.",
|
104
|
+
:@line_position=>"1193"},
|
105
|
+
{:@line_number=>"1",
|
106
|
+
:severity=>"Error",
|
107
|
+
:message=>"The required attribute 'pocAddress' is missing.",
|
108
|
+
:@line_position=>"1020"}]}}}
|
109
|
+
end
|
110
|
+
|
111
|
+
def repeat_response
|
112
|
+
{:response=>{
|
113
|
+
:status=>"Pending",
|
114
|
+
:message=>
|
115
|
+
"1 merchant(s) created.\n0 merchant(s) not created due to errors.\n\n-----------------------\nMerchants Created:\nCogsley's Cogs (ISO ID: 9999, CrossRef: 123456, Status: PendingInput)\n\n",
|
116
|
+
:validation_message=>{:result=>"Passed", :schema_file_path=>nil},
|
117
|
+
:"@xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
|
118
|
+
:"@xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
119
|
+
:app_data=>{:merchant=>{:@id=>"21"}}}}
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#board_merchant_ach' do
|
123
|
+
it 'calls BoardCertificationMerchant_ACH' do
|
124
|
+
client = Geti::AppClient.new(test_credentials, {})
|
125
|
+
mock_soap!(client, success_response, "BoardCertificationMerchant_ACH", "board_certification_merchant_ach")
|
126
|
+
client.board_merchant_ach(request_payload)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'calls BoardMerchant_ACH in production' do
|
130
|
+
client = Geti::AppClient.new(test_credentials, {}, 'production')
|
131
|
+
mock_soap!(client, success_response, "BoardMerchant_ACH", "board_certification_merchant_ach")
|
132
|
+
client.board_merchant_ach(request_payload)
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'response on success' do
|
136
|
+
let(:client) {
|
137
|
+
Geti::AppClient.new(test_credentials, {}).tap{|c|
|
138
|
+
mock_soap!(c, response, "BoardCertificationMerchant_ACH", "board_certification_merchant_ach")
|
139
|
+
}
|
140
|
+
}
|
141
|
+
subject { client.board_merchant_ach(request_payload) }
|
142
|
+
|
143
|
+
describe 'on success' do
|
144
|
+
let(:response) { success_response }
|
145
|
+
its([:success]) { should be_true }
|
146
|
+
its([:status]) { should eq("Approved") }
|
147
|
+
|
148
|
+
it 'normalizes (nested) keys' do
|
149
|
+
subject[:app_data][:merchant][:id].should eq("20")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe 'on repeat' do
|
154
|
+
let(:response) { repeat_response }
|
155
|
+
its([:success]) { should be_true }
|
156
|
+
its([:status]) { should eq("Pending") }
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'on error' do
|
160
|
+
let(:response) { error_response }
|
161
|
+
its([:success]) { should be_false }
|
162
|
+
its([:status]) { should be_nil }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Geti::AuthClient do
|
4
|
+
def mock_soap!(client, parsed_response, operation, op_key=nil)
|
5
|
+
op_key ||= operation.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
6
|
+
response_key = (op_key+'_response').to_sym
|
7
|
+
result_key = (op_key+'_result').to_sym
|
8
|
+
|
9
|
+
data = OpenStruct.new(:body => {response_key => {result_key => :encoded_xml}})
|
10
|
+
|
11
|
+
client.soap_client.should_receive(:request).with(operation).and_return(data)
|
12
|
+
client.xml_parser.should_receive(:parse).with(:encoded_xml).and_return(parsed_response)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#get_terminal_settings' do
|
16
|
+
it 'calls GetCertificationTerminalSettings' do
|
17
|
+
client = Geti::AuthClient.new(test_credentials, {:sec_code => 'WEB', :verify => []})
|
18
|
+
mock_soap!(client, {}, "GetCertificationTerminalSettings")
|
19
|
+
client.get_terminal_settings
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'calls GetTerminalSettings in production' do
|
23
|
+
client = Geti::AuthClient.new(test_credentials, {:sec_code => 'WEB', :verify => []}, 'production')
|
24
|
+
mock_soap!(client, {}, "GetTerminalSettings")
|
25
|
+
client.get_terminal_settings
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development, :test)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rspec'
|
12
|
+
require 'rspec/mocks'
|
13
|
+
require 'ostruct'
|
14
|
+
class OpenStruct
|
15
|
+
def inspect
|
16
|
+
"<OpenStruct \"#{@table.inspect}\">"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
21
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
22
|
+
|
23
|
+
require 'pp'
|
24
|
+
require 'geti'
|
25
|
+
|
26
|
+
|
27
|
+
Savon.configure do |config|
|
28
|
+
config.log = HTTPI.log = false unless ENV['SOAP_DEBUG']
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_credentials
|
32
|
+
YAML.load(File.read('config/test_credentials.yml'))
|
33
|
+
end
|
34
|
+
|
35
|
+
def xit(*args, &block)
|
36
|
+
# noop, disabled spec.
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Geti::AppClient do
|
4
|
+
describe '#board_merchant_ach' do
|
5
|
+
it 'has a successful response' do
|
6
|
+
t = Time.now.to_i
|
7
|
+
client = Geti::AppClient.new(test_credentials, {})
|
8
|
+
response = client.board_merchant_ach({
|
9
|
+
:id => t,
|
10
|
+
:name => "Cogsley's Cogs %d" % t,
|
11
|
+
:industry => "Metal_Fabricators",
|
12
|
+
:address => "123 Main St",
|
13
|
+
:city => "Vancouver",
|
14
|
+
:state => "WA",
|
15
|
+
:zip => "10120",
|
16
|
+
:phone => "5555551234",
|
17
|
+
:business_type => "Corporation",
|
18
|
+
:days_in_business => 404,
|
19
|
+
|
20
|
+
:contact_name => 'George Jetson',
|
21
|
+
:physical_address => "123 Main St",
|
22
|
+
:physical_city => "Vancouver",
|
23
|
+
:physical_state => "WA",
|
24
|
+
:physical_zip => "10120",
|
25
|
+
:physical_phone => "5555551234",
|
26
|
+
|
27
|
+
:principal_first_name => "Carl",
|
28
|
+
:principal_last_name => "Cogsley",
|
29
|
+
:principal_title => 'President',
|
30
|
+
:principal_address => "123 Main St",
|
31
|
+
:principal_city => "Vancouver",
|
32
|
+
:principal_state => "WA",
|
33
|
+
:principal_zip => "10120",
|
34
|
+
:principal_dob => "1965-04-28",
|
35
|
+
:principal_ssn => '111222123',
|
36
|
+
|
37
|
+
:average_amount => "4000",
|
38
|
+
:max_amount => "7600",
|
39
|
+
|
40
|
+
:taxpayer_name => "Carl Cogsley",
|
41
|
+
:taxpayer_id => "123456789",
|
42
|
+
|
43
|
+
:routing_number => "490000018",
|
44
|
+
:account_number => "123456789",
|
45
|
+
})
|
46
|
+
expect(response[:status]).to eq("Approved")
|
47
|
+
expect(response[:message]).to match(/CrossRef: #{t}/)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|