dcas-ruby 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 +22 -0
- data/API.rdoc +44 -0
- data/HISTORY.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/dcas-ruby.gemspec +87 -0
- data/lib/dcas/ach_response.rb +83 -0
- data/lib/dcas/ach_return.rb +133 -0
- data/lib/dcas/payment.rb +147 -0
- data/lib/dcas/response.rb +123 -0
- data/lib/dcas.rb +140 -0
- data/lib/net/ftps_implicit.rb +225 -0
- data/spec/dcas/response_spec.rb +11 -0
- data/spec/dcas_spec.rb +50 -0
- data/spec/fixtures/ach_first_response.csv.sample +0 -0
- data/spec/fixtures/ach_payments.csv +11 -0
- data/spec/fixtures/ach_payments.yml +81 -0
- data/spec/fixtures/ach_second_response.csv.sample +0 -0
- data/spec/fixtures/cc_response.csv.sample +0 -0
- data/spec/fixtures/clients.yml.sample +15 -0
- data/spec/fixtures/credit_card_payments.csv +11 -0
- data/spec/fixtures/credit_card_payments.yml +71 -0
- data/spec/fixtures/test_upload0.txt +0 -0
- data/spec/fixtures/test_upload1.txt +0 -0
- data/spec/fixtures/test_upload2.txt +0 -0
- data/spec/fixtures/test_upload3.txt +0 -0
- data/spec/fixtures/test_upload4.txt +0 -0
- data/spec/fixtures/test_upload5.txt +0 -0
- data/spec/fixtures/test_upload6.txt +0 -0
- data/spec/fixtures/test_upload7.txt +0 -0
- data/spec/fixtures/test_upload8.txt +0 -0
- data/spec/fixtures/test_upload9.txt +0 -0
- data/spec/ftps_implicit_spec.rb +48 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +18 -0
- metadata +115 -0
data/.document
ADDED
data/.gitignore
ADDED
data/API.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= Functions of DCAS:
|
2
|
+
|
3
|
+
* Generate a file
|
4
|
+
* Upload a file
|
5
|
+
* Check for response files
|
6
|
+
* Receive response files
|
7
|
+
* Parse response files
|
8
|
+
|
9
|
+
=== First you must set up the login info and a cache location
|
10
|
+
angola = DCAS::Client.new(:username => 'angola', :password => '4ng014', :cache_location => './EFT')
|
11
|
+
|
12
|
+
=== Create a Payment Batch
|
13
|
+
ach_payments = angola.new_batch(batch_id)
|
14
|
+
cc_payments = angola.new_batch(batch_id)
|
15
|
+
|
16
|
+
=== Populate it with payments
|
17
|
+
ach_payments << DCAS::AchPayment.new(client_id, client_name, amount, account_type, routing_number, account_number, check_number)
|
18
|
+
cc_payments << DCAS::CreditCardPayment.new(client_id, client_name, amount, card_type, credit_card_number, expiration)
|
19
|
+
|
20
|
+
=== You can generate a payments file from a collection of DCAS::Payment objects
|
21
|
+
File.open(filename, 'w') {|f| f << cc_payments.to_csv }
|
22
|
+
File.open(ach_filename, 'w') {|f| f << ach_payments.to_csv }
|
23
|
+
|
24
|
+
=== Upload a generated file to the payments folder of a DCAS bucket
|
25
|
+
angola.submit_payments_file!(filename)
|
26
|
+
|
27
|
+
=== Simpler method, just submit batches in one go
|
28
|
+
angola.submit_batches!
|
29
|
+
|
30
|
+
=== Check for response file availability and return the available filenames
|
31
|
+
response_filenames = angola.available_response_files
|
32
|
+
response_filename = response_filenames.first
|
33
|
+
|
34
|
+
=== Download all response files
|
35
|
+
angola.download_response_files!
|
36
|
+
response_file_content = File.read(angola.cache_location + '/' + response_filename)
|
37
|
+
|
38
|
+
=== Return an array of DCAS::Response objects parsed from the response file/content
|
39
|
+
responses = DCAS.parse_response_file(response_filename)
|
40
|
+
responses = DCAS.parse_response_file(response_file_content)
|
41
|
+
|
42
|
+
=== Run the block for each DCAS::Response in the response file/content
|
43
|
+
angola.each_response_in(response_filename) { |response| ... }
|
44
|
+
angola.each_response_in(response_file_content) { |response| ... }
|
data/HISTORY.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 BehindLogic
|
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,18 @@
|
|
1
|
+
= dcas-ruby
|
2
|
+
|
3
|
+
Ruby codebase for creating payment batch files for DCAS, uploading them, and receiving response files from DCAS.
|
4
|
+
For the Ruby API, see API.
|
5
|
+
|
6
|
+
== Note on Patches/Pull Requests
|
7
|
+
|
8
|
+
* Fork the project.
|
9
|
+
* Make your feature addition or bug fix.
|
10
|
+
* Add tests for it. This is important so I don't break it in a
|
11
|
+
future version unintentionally.
|
12
|
+
* Commit, do not mess with rakefile, version, or history.
|
13
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
14
|
+
* Send me a pull request. Bonus points for topic branches.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2010 BehindLogic. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "dcas-ruby"
|
8
|
+
gem.summary = %Q{Ruby codebase for managing payments with DCAS.}
|
9
|
+
gem.description = %Q{Ruby codebase for creating payment batch files for DCAS, uploading them, and receiving response files from DCAS.}
|
10
|
+
gem.email = "gems@behindlogic.com"
|
11
|
+
gem.homepage = "http://github.com/dcparker/dcas-ruby"
|
12
|
+
gem.authors = ["BehindLogic"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_dependency "fastercsv"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "dcas-ruby #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/dcas-ruby.gemspec
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{dcas-ruby}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["BehindLogic"]
|
12
|
+
s.date = %q{2010-01-15}
|
13
|
+
s.description = %q{Ruby codebase for creating payment batch files for DCAS, uploading them, and receiving response files from DCAS.}
|
14
|
+
s.email = %q{gems@behindlogic.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"API.rdoc",
|
23
|
+
"HISTORY.rdoc",
|
24
|
+
"LICENSE",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"dcas-ruby.gemspec",
|
29
|
+
"lib/dcas.rb",
|
30
|
+
"lib/dcas/ach_response.rb",
|
31
|
+
"lib/dcas/ach_return.rb",
|
32
|
+
"lib/dcas/payment.rb",
|
33
|
+
"lib/dcas/response.rb",
|
34
|
+
"lib/net/ftps_implicit.rb",
|
35
|
+
"spec/dcas/response_spec.rb",
|
36
|
+
"spec/dcas_spec.rb",
|
37
|
+
"spec/fixtures/ach_first_response.csv.sample",
|
38
|
+
"spec/fixtures/ach_payments.csv",
|
39
|
+
"spec/fixtures/ach_payments.yml",
|
40
|
+
"spec/fixtures/ach_second_response.csv.sample",
|
41
|
+
"spec/fixtures/cc_response.csv.sample",
|
42
|
+
"spec/fixtures/clients.yml.sample",
|
43
|
+
"spec/fixtures/credit_card_payments.csv",
|
44
|
+
"spec/fixtures/credit_card_payments.yml",
|
45
|
+
"spec/fixtures/test_upload0.txt",
|
46
|
+
"spec/fixtures/test_upload1.txt",
|
47
|
+
"spec/fixtures/test_upload2.txt",
|
48
|
+
"spec/fixtures/test_upload3.txt",
|
49
|
+
"spec/fixtures/test_upload4.txt",
|
50
|
+
"spec/fixtures/test_upload5.txt",
|
51
|
+
"spec/fixtures/test_upload6.txt",
|
52
|
+
"spec/fixtures/test_upload7.txt",
|
53
|
+
"spec/fixtures/test_upload8.txt",
|
54
|
+
"spec/fixtures/test_upload9.txt",
|
55
|
+
"spec/ftps_implicit_spec.rb",
|
56
|
+
"spec/spec.opts",
|
57
|
+
"spec/spec_helper.rb"
|
58
|
+
]
|
59
|
+
s.homepage = %q{http://github.com/dcparker/dcas-ruby}
|
60
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
61
|
+
s.require_paths = ["lib"]
|
62
|
+
s.rubygems_version = %q{1.3.5}
|
63
|
+
s.summary = %q{Ruby codebase for managing payments with DCAS.}
|
64
|
+
s.test_files = [
|
65
|
+
"spec/dcas/response_spec.rb",
|
66
|
+
"spec/dcas_spec.rb",
|
67
|
+
"spec/ftps_implicit_spec.rb",
|
68
|
+
"spec/spec_helper.rb"
|
69
|
+
]
|
70
|
+
|
71
|
+
if s.respond_to? :specification_version then
|
72
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
73
|
+
s.specification_version = 3
|
74
|
+
|
75
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
76
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
77
|
+
s.add_runtime_dependency(%q<fastercsv>, [">= 0"])
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
80
|
+
s.add_dependency(%q<fastercsv>, [">= 0"])
|
81
|
+
end
|
82
|
+
else
|
83
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
84
|
+
s.add_dependency(%q<fastercsv>, [">= 0"])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module DCAS
|
2
|
+
class AchResponse < Response
|
3
|
+
DESCRIPTIONS = {
|
4
|
+
'00' => 'Processing',
|
5
|
+
'02' => 'Account Closed',
|
6
|
+
'03' => 'Account Closed',
|
7
|
+
'04' => 'Stop Payment',
|
8
|
+
'05' => 'Stop Payment',
|
9
|
+
'10' => 'No Debits Allowed',
|
10
|
+
'11' => 'No Checks Allowed',
|
11
|
+
'12' => 'Account Closed',
|
12
|
+
'14' => 'Account Closed',
|
13
|
+
'20' => 'Insufficient Funds',
|
14
|
+
'21' => 'Insufficient Funds',
|
15
|
+
'22' => 'Insufficient Funds',
|
16
|
+
'30' => 'Insufficient Funds',
|
17
|
+
'31' => 'Insufficient Funds',
|
18
|
+
'32' => 'Insufficient Funds',
|
19
|
+
'33' => 'Insufficient Funds',
|
20
|
+
'37' => 'Account Restricted',
|
21
|
+
'80' => 'Non-DDA Participant Credit Card',
|
22
|
+
'81' => 'Non-DDA Participant Line of Credit',
|
23
|
+
'82' => 'Non-DDA Participant Home Equity',
|
24
|
+
'83' => 'Non-DDA Non-Participant Credit Card / LOC',
|
25
|
+
'84' => 'Non-DDA Participant Broker Check',
|
26
|
+
'85' => 'Non-DDA Non-Participant Broker Check',
|
27
|
+
'96' => 'Non-Participant',
|
28
|
+
'97' => 'Non-Participant',
|
29
|
+
'98' => 'Non-DDA',
|
30
|
+
'99' => 'Account Not Located',
|
31
|
+
'128' => 'WARNING: Authentication Required',
|
32
|
+
'256' => 'WARNING: Account Validation Declined',
|
33
|
+
'257' => 'WARNING: Insufficient Funds',
|
34
|
+
'258' => 'WARNING: Account - Invalid Card',
|
35
|
+
'259' => 'WARNING: Account - Expired Card',
|
36
|
+
'260' => 'WARNING: Referral',
|
37
|
+
'261' => 'WARNING: Authentication Failed',
|
38
|
+
'262' => 'WARNING: Authentication Server Not Available',
|
39
|
+
'1026' => 'WARNING: Account Validation Error',
|
40
|
+
'1027' => 'WARNING: Account Validation Failure',
|
41
|
+
'1028' => 'WARNING: Account Duplicate',
|
42
|
+
'1029' => 'WARNING: Account - Invalid Merchant',
|
43
|
+
'1034' => 'WARNING: Account - Invalid Transaction',
|
44
|
+
'9001' => 'WARNING; Bank ABA not Verified',
|
45
|
+
'9033' => 'WARNING: Insufficient Funds',
|
46
|
+
'9097' => 'WARNING: ABA Not Valid',
|
47
|
+
'9098' => 'WARNING: Batch not closed',
|
48
|
+
'9099' => 'WARNING: No Batch on file',
|
49
|
+
'9999' => 'Unspecified Error'
|
50
|
+
}
|
51
|
+
|
52
|
+
def initialize(batch_id,attrs={})
|
53
|
+
new_attrs = {}
|
54
|
+
nattrs = attrs.dup
|
55
|
+
if nattrs.is_a?(Hash) # Is xml-hash
|
56
|
+
nattrs.stringify_keys!
|
57
|
+
# status, order_number, transacted_at, transaction_id, description
|
58
|
+
new_attrs = nattrs
|
59
|
+
elsif nattrs.respond_to?('[]') # Is csv row
|
60
|
+
# GotoBilling: MerchantID,FirstName,LastName,CustomerID,Amount,SentDate,SettleDate,TransactionID,TransactionType,Status,Description
|
61
|
+
# DCAS: RT,BankABA,AccountNumber,CheckNumber,Amount,ReturnCode,Description,CustTraceCode
|
62
|
+
# ret could be 0 (denied), 1 (approved), 2 (call for authorization), or 99 (error)
|
63
|
+
new_attrs = {
|
64
|
+
:status => nattrs[3][0..0] == 'A' ? 'A' : 'D',
|
65
|
+
:description => DESCRIPTIONS[nattrs[3].match(/(\d+)/)[1]] + " / " + nattrs[3].split(/-/)[1], # "Processing" if everything's good
|
66
|
+
:ach_submitted => true,
|
67
|
+
:client_id => nattrs[4][4..-1],
|
68
|
+
:account_number => nattrs[2]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
self.attributes = new_attrs
|
72
|
+
self.batch_id = batch_id
|
73
|
+
end
|
74
|
+
|
75
|
+
def record_to_transaction!
|
76
|
+
return unless transaction.status != status && transaction.description != description
|
77
|
+
transaction.status = status
|
78
|
+
transaction.description = description
|
79
|
+
transaction.ach_submitted = true if ach_submitted
|
80
|
+
transaction.save
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module DCAS
|
2
|
+
class AchReturn < Response
|
3
|
+
DESCRIPTIONS = {
|
4
|
+
'C01' => 'Incorrect DFI account number',
|
5
|
+
'C02' => 'Incorrect T/R number',
|
6
|
+
'C03' => 'Incorrect T/R number and incorrect DFI account number',
|
7
|
+
'C04' => 'Incorrect indvidual name/receiving company name',
|
8
|
+
'C05' => 'Incorrect transaction codes',
|
9
|
+
'C06' => 'Incorrect DFI account number and incorrect transaction code',
|
10
|
+
'C07' => 'Incorrect T/R number, incorrect DFI number and incorrect transaction code',
|
11
|
+
'C08' => 'Reserved',
|
12
|
+
'C09' => 'Incorrect individual identification number',
|
13
|
+
'C10' => 'Incorrect company name',
|
14
|
+
'C11' => 'Incorrect',
|
15
|
+
'C12' => 'Incorrect company name and company identification',
|
16
|
+
'C13' => 'Addenda format error',
|
17
|
+
'C61' => 'Misrouted notification change',
|
18
|
+
'C62' => 'Incorrect trace number',
|
19
|
+
'C63' => 'Incorrect company identification number',
|
20
|
+
'C64' => 'Incorrect individual identification number/identification number',
|
21
|
+
'C65' => 'Incorrectly formatted corrected data',
|
22
|
+
'C66' => 'Incorrect discretionary data',
|
23
|
+
'C67' => 'Routing number not from original entry detail record',
|
24
|
+
'C68' => 'DFI account number not from original entry detail record',
|
25
|
+
'C69' => 'Incorrect transaction code',
|
26
|
+
'R01' => 'Insufficient funds',
|
27
|
+
'R02' => 'Account closed',
|
28
|
+
'R03' => 'No account/Unable to locate account',
|
29
|
+
'R04' => 'Invalid account number',
|
30
|
+
'R05' => 'Reserved',
|
31
|
+
'R06' => 'Returned per Originating DFIs request',
|
32
|
+
'R07' => 'Authorization revoked by customer',
|
33
|
+
'R08' => 'Payment stopped',
|
34
|
+
'R09' => 'Uncollected Funds',
|
35
|
+
'R10' => 'Customer advises not authorized',
|
36
|
+
'R11' => 'Check truncation entry return',
|
37
|
+
'R12' => 'Branch sold to another DFI',
|
38
|
+
'R13' => 'Receiving DFI not qualified to participate',
|
39
|
+
'R14' => 'Account-holder deceased',
|
40
|
+
'R15' => 'Beneficiary deceased',
|
41
|
+
'R16' => 'Account frozen',
|
42
|
+
'R17' => 'File record edit criteria',
|
43
|
+
'R18' => 'Improper effective entry date',
|
44
|
+
'R19' => 'Amount field error',
|
45
|
+
'R20' => 'Non-transaction account',
|
46
|
+
'R21' => 'Invalid company identification',
|
47
|
+
'R22' => 'Invalid individual ID number',
|
48
|
+
'R23' => 'Credit entry refused by receiver',
|
49
|
+
'R24' => 'Duplicate entry',
|
50
|
+
'R25' => 'Addenda error',
|
51
|
+
'R26' => 'Mandatory field error',
|
52
|
+
'R27' => 'Trace number error',
|
53
|
+
'R28' => 'Transit/Routing check digit error',
|
54
|
+
'R29' => 'Corporate customer advises not authorized',
|
55
|
+
'R30' => 'Receiving DFI not participant in check truncation program',
|
56
|
+
'R31' => 'Permissible return entry',
|
57
|
+
'R32' => 'RDFI - non-settlement',
|
58
|
+
'R33' => 'Return for XCK',
|
59
|
+
'R34' => 'Limited participation DFI',
|
60
|
+
'900' => 'Edit Reject',
|
61
|
+
'901' => 'Non-Sufficient Funds',
|
62
|
+
'902' => 'Cannot Trace',
|
63
|
+
'903' => 'Payment Stopped/Recalled',
|
64
|
+
'904' => 'Post/Stale Dated',
|
65
|
+
'905' => 'Account Closed',
|
66
|
+
'906' => 'Account Transferred',
|
67
|
+
'907' => 'No Checquing Privileges',
|
68
|
+
'908' => 'Funds Not Cleared',
|
69
|
+
'910' => 'Payer/Payee Deceased',
|
70
|
+
'911' => 'Account Frozen',
|
71
|
+
'912' => 'Invalid/Incorrect Account Number',
|
72
|
+
'914' => 'Incorrect Payer/Payee Name',
|
73
|
+
'915' => 'Refused By Payer/Payee',
|
74
|
+
'990' => 'Institution In Default',
|
75
|
+
'998' => 'No Agreement For Returns'
|
76
|
+
}
|
77
|
+
|
78
|
+
ACH_RET_CODES = {
|
79
|
+
'A' => 'G',
|
80
|
+
'C' => 'I',
|
81
|
+
'R' => 'D',
|
82
|
+
'9' => 'D'
|
83
|
+
}
|
84
|
+
def initialize(batch_id,attrs={})
|
85
|
+
new_attrs = {}
|
86
|
+
nattrs = attrs.dup
|
87
|
+
if nattrs.is_a?(Hash) # Is xml-hash
|
88
|
+
nattrs.stringify_keys!
|
89
|
+
# status, order_number, transacted_at, transaction_id, description
|
90
|
+
new_attrs = nattrs
|
91
|
+
elsif nattrs.respond_to?('[]') # Is csv row
|
92
|
+
# GotoBilling: MerchantID,FirstName,LastName,CustomerID,Amount,SentDate,SettleDate,TransactionID,TransactionType,Status,Description
|
93
|
+
# DCAS: RT,BankABA,AccountNumber,CheckNumber,Amount,ReturnCode,Description,CustTraceCode
|
94
|
+
# ret could be 0 (denied), 1 (approved), 2 (call for authorization), or 99 (error)
|
95
|
+
status = ACH_RET_CODES[nattrs[5][0..0]]
|
96
|
+
new_attrs = {
|
97
|
+
:account_number => nattrs[2],
|
98
|
+
:check_number => nattrs[3],
|
99
|
+
:client_id => nattrs[7] ? nattrs[7][4..-1] : nil
|
100
|
+
}
|
101
|
+
if status == 'I'
|
102
|
+
new_attrs[:information] = "#{DESCRIPTIONS[nattrs[5]]} (#{nattrs[6]})"
|
103
|
+
else
|
104
|
+
new_attrs[:status] = status
|
105
|
+
new_attrs[:description] = "#{DESCRIPTIONS[nattrs[5]]} (#{nattrs[6]})"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
self.attributes = new_attrs
|
109
|
+
self.batch_id = batch_id
|
110
|
+
end
|
111
|
+
|
112
|
+
def transaction
|
113
|
+
@transaction ||= GotoTransaction.find_by_batch_id_and_client_id(self.batch_id, self.client_id)
|
114
|
+
@transaction ||= GotoTransaction.find_by_batch_id_and_bank_account_number_and_check_number(self.batch_id, self.account_number, self.check_number)
|
115
|
+
@transaction
|
116
|
+
end
|
117
|
+
|
118
|
+
def record_to_transaction!
|
119
|
+
return unless transaction.status != status && transaction.description != description
|
120
|
+
|
121
|
+
transaction.transaction_id = 0 if transaction.transaction_id.to_i != 0 && transaction.status == 'G' && status == 'D' # Was accepted, now declined.
|
122
|
+
|
123
|
+
if status == 'I'
|
124
|
+
transaction.information = information
|
125
|
+
else
|
126
|
+
transaction.description = description
|
127
|
+
end
|
128
|
+
transaction.status = status
|
129
|
+
transaction.ach_submitted = ach_submitted if ach_submitted
|
130
|
+
transaction.save
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/dcas/payment.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'fastercsv'
|
2
|
+
|
3
|
+
module DCAS
|
4
|
+
class PaymentBatch
|
5
|
+
def initialize(client, batch_id)
|
6
|
+
@client = client
|
7
|
+
@batch_id = batch_id
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :batch_id
|
11
|
+
|
12
|
+
def payments
|
13
|
+
@payments ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(payment)
|
17
|
+
raise ArgumentError, "payment should be instance of Payment" unless payment.is_a?(Payment)
|
18
|
+
type = payments.first.class
|
19
|
+
raise ArgumentError, "payment added to a #{type} batch should be a #{type} but was #{payment.class.name}!" if !payments.empty? && !payment.is_a?(type)
|
20
|
+
payment.batch = self
|
21
|
+
payments << payment
|
22
|
+
end
|
23
|
+
|
24
|
+
def type
|
25
|
+
payments.first.class.name.gsub(/.*::/,'').downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
# Generates a payment batch file and returns its contents.
|
29
|
+
def to_csv
|
30
|
+
FasterCSV.generate do |csv|
|
31
|
+
csv << [ 'HD', @client.company_alias, @client.company_username, @client.company_password, 'Check' ]
|
32
|
+
payments.each do |payment|
|
33
|
+
csv << payment.to_csv_data if payment.batch == self # Safety net in case the same payment was applied to more than one batch. It will only go through in the last batch it was added to.
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Payment
|
40
|
+
attr_accessor :batch
|
41
|
+
|
42
|
+
def initialize(client_id, client_name, amount, *args)
|
43
|
+
@client_id = client_id
|
44
|
+
@client_name = client_name
|
45
|
+
@amount = amount
|
46
|
+
@txn_type = 'Debit'
|
47
|
+
return args
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class AchPayment < Payment
|
52
|
+
# Arguments: client_id, client_name, amount, account_type, routing_number, account_number, check_number
|
53
|
+
def initialize(*args)
|
54
|
+
@account_type, @routing_number, @account_number, @check_number = *super
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_csv_data(options={})
|
58
|
+
# Example from DCAS:
|
59
|
+
# HD,CompanyName,UserName,Password,Check
|
60
|
+
# CA,111000753,1031103,42676345,50.99,,Darwin Rogers,1409 N AVE,,,75090,,,,,2919,,,,,Checking,,,,,,200
|
61
|
+
# CC,VISA,4118000000981234,04/2009,19.99,N,,162078,JACLYN ,545 Sheridan Ave,,,07203,,,,9872,,,2,3,1
|
62
|
+
[ # This is for bank account transactions
|
63
|
+
'CA',
|
64
|
+
@routing_number,
|
65
|
+
@account_number,
|
66
|
+
@check_number, # check number field can be used to prevent duplicates
|
67
|
+
@amount,
|
68
|
+
nil, # invoice number
|
69
|
+
@client_name,
|
70
|
+
nil, # address - API says required, but it's really not.
|
71
|
+
nil, # city
|
72
|
+
nil, # state
|
73
|
+
nil, # zip
|
74
|
+
nil, # phone number
|
75
|
+
nil, # driver license number
|
76
|
+
nil, # driver license state
|
77
|
+
nil, # third party check? 1=yes, 0=no
|
78
|
+
"#{@batch.batch_id}#{@client_id}", # CustTraceCode
|
79
|
+
nil, # image name
|
80
|
+
nil, # back image name
|
81
|
+
@txn_type, # Credit/Debit (default Debit)
|
82
|
+
nil, # Internal Account Number: date (in 4 digits: YYMM) + client id
|
83
|
+
@account_type,
|
84
|
+
#, nil, # ECC - Default Entry Class Code (??)
|
85
|
+
# nil, nil, nil, nil, # Deposit info
|
86
|
+
# nil, # CPA Code
|
87
|
+
# nil, nil, # scanned MICR info
|
88
|
+
# nil, nil # endorsement and image
|
89
|
+
]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class AchRefund < AchPayment
|
94
|
+
# Arguments: client_id, client_name, amount, account_type, routing_number, account_number, check_number
|
95
|
+
def initialize(*args)
|
96
|
+
super
|
97
|
+
@txn_type = 'Credit'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class CreditCardPayment < Payment
|
102
|
+
# Arguments: client_id, client_name, amount, card_type, credit_card_number, expiration
|
103
|
+
def initialize(*args)
|
104
|
+
@card_type, @credit_card_number, @expiration = *super
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_csv_data(options={})
|
108
|
+
# DCAS Example:
|
109
|
+
# HD,CompanyName,UserName,Password,CHECK
|
110
|
+
# CA,111000753,1031103,42676345,50.99,,Darwin Rogers,1409 N AVE,,,75090,,,,,2919,,,,,Checking,,,,,,200
|
111
|
+
# CC,VISA,4118000000981234,04/2009,19.99,N,,162078,JACLYN ,545 Sheridan Ave,,,07203,,,,9872,,,2,3,1
|
112
|
+
[ # This is for credit card transactions
|
113
|
+
'CC',
|
114
|
+
@card_type, # Card Type
|
115
|
+
@credit_card_number, # Account Number
|
116
|
+
@expiration, # Expiration date (MM/YYYY)
|
117
|
+
@amount, # Amount (00.00)
|
118
|
+
'N', # Card Present
|
119
|
+
nil, # Card verification (if present)
|
120
|
+
nil, # invoice number
|
121
|
+
@client_name, # name # Larry Cummings @ DCAS Support: (972) 239-2327, ext 153 #OR# (972) 392-4654
|
122
|
+
nil, # address
|
123
|
+
nil, # city
|
124
|
+
nil, # state
|
125
|
+
nil, # zip
|
126
|
+
nil, # phone number
|
127
|
+
nil, # driver license number
|
128
|
+
nil, # driver license state
|
129
|
+
"#{@batch.batch_id}#{@client_id}", # CustTraceCode
|
130
|
+
@txn_type, # Credit/Debit (default Debit)
|
131
|
+
nil,
|
132
|
+
2,
|
133
|
+
3,
|
134
|
+
1,
|
135
|
+
nil
|
136
|
+
]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class CreditCardRefund < Payment
|
141
|
+
# Arguments: client_id, client_name, amount, card_type, credit_card_number, expiration
|
142
|
+
def initialize(*args)
|
143
|
+
super
|
144
|
+
@txn_type = 'Credit'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|