ach_client 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +18 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +1156 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/README.md +388 -0
- data/Rakefile +10 -0
- data/ach_client.gemspec +58 -0
- data/bin/console +93 -0
- data/bin/setup +8 -0
- data/config/return_codes.yml +761 -0
- data/lib/ach_client/abstract/abstract_method_error.rb +3 -0
- data/lib/ach_client/helpers/dollars_to_cents.rb +15 -0
- data/lib/ach_client/logging/log_provider_job.rb +36 -0
- data/lib/ach_client/logging/log_providers/log_provider.rb +22 -0
- data/lib/ach_client/logging/log_providers/null_log_provider.rb +14 -0
- data/lib/ach_client/logging/log_providers/stdout_log_provider.rb +14 -0
- data/lib/ach_client/logging/logging.rb +76 -0
- data/lib/ach_client/logging/savon_observer.rb +26 -0
- data/lib/ach_client/objects/account_types.rb +32 -0
- data/lib/ach_client/objects/responses/ach_response.rb +15 -0
- data/lib/ach_client/objects/responses/corrected_ach_response.rb +18 -0
- data/lib/ach_client/objects/responses/processing_ach_response.rb +6 -0
- data/lib/ach_client/objects/responses/returned_ach_response.rb +16 -0
- data/lib/ach_client/objects/responses/settled_ach_response.rb +5 -0
- data/lib/ach_client/objects/return_code.rb +20 -0
- data/lib/ach_client/objects/return_codes.rb +33 -0
- data/lib/ach_client/objects/transaction_types.rb +16 -0
- data/lib/ach_client/providers/abstract/ach_batch.rb +23 -0
- data/lib/ach_client/providers/abstract/ach_status_checker.rb +21 -0
- data/lib/ach_client/providers/abstract/ach_transaction.rb +70 -0
- data/lib/ach_client/providers/abstract/company_info.rb +28 -0
- data/lib/ach_client/providers/abstract/response_record_processor.rb +15 -0
- data/lib/ach_client/providers/abstract/transformer.rb +38 -0
- data/lib/ach_client/providers/sftp/account_type_transformer.rb +20 -0
- data/lib/ach_client/providers/sftp/ach_batch.rb +97 -0
- data/lib/ach_client/providers/sftp/ach_status_checker.rb +146 -0
- data/lib/ach_client/providers/sftp/ach_transaction.rb +62 -0
- data/lib/ach_client/providers/sftp/nacha_provider.rb +30 -0
- data/lib/ach_client/providers/sftp/sftp_provider.rb +124 -0
- data/lib/ach_client/providers/sftp/transaction_type_transformer.rb +19 -0
- data/lib/ach_client/providers/soap/ach_works/account_type_transformer.rb +17 -0
- data/lib/ach_client/providers/soap/ach_works/ach_batch.rb +89 -0
- data/lib/ach_client/providers/soap/ach_works/ach_status_checker.rb +86 -0
- data/lib/ach_client/providers/soap/ach_works/ach_transaction.rb +80 -0
- data/lib/ach_client/providers/soap/ach_works/ach_works.rb +57 -0
- data/lib/ach_client/providers/soap/ach_works/company_info.rb +87 -0
- data/lib/ach_client/providers/soap/ach_works/correction_details_processor.rb +92 -0
- data/lib/ach_client/providers/soap/ach_works/date_formatter.rb +33 -0
- data/lib/ach_client/providers/soap/ach_works/response_record_processor.rb +91 -0
- data/lib/ach_client/providers/soap/ach_works/transaction_type_transformer.rb +18 -0
- data/lib/ach_client/providers/soap/i_check_gateway/account_type_transformer.rb +20 -0
- data/lib/ach_client/providers/soap/i_check_gateway/ach_batch.rb +12 -0
- data/lib/ach_client/providers/soap/i_check_gateway/ach_status_checker.rb +35 -0
- data/lib/ach_client/providers/soap/i_check_gateway/ach_transaction.rb +53 -0
- data/lib/ach_client/providers/soap/i_check_gateway/company_info.rb +51 -0
- data/lib/ach_client/providers/soap/i_check_gateway/i_check_gateway.rb +39 -0
- data/lib/ach_client/providers/soap/i_check_gateway/response_record_processor.rb +59 -0
- data/lib/ach_client/providers/soap/i_check_gateway/transaction_type_transformer.rb +8 -0
- data/lib/ach_client/providers/soap/soap_provider.rb +47 -0
- data/lib/ach_client/version.rb +4 -0
- data/lib/ach_client.rb +38 -0
- metadata +346 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Utility classes shared by all providers
|
3
|
+
class Helpers
|
4
|
+
|
5
|
+
# Turns dollars into cents
|
6
|
+
class DollarsToCents
|
7
|
+
|
8
|
+
# @param dollars [Float | BigDecimal | Fixnum | Integer]
|
9
|
+
# @return [Fixnum] cents
|
10
|
+
def self.dollars_to_cents(dollars)
|
11
|
+
(dollars.to_f.round(2) * 100).to_i
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Logging
|
3
|
+
# Worker to asynchronously invoke the log provider
|
4
|
+
class LogProviderJob
|
5
|
+
include SuckerPunch::Job
|
6
|
+
|
7
|
+
## Prettifies the xml and sends it asynchronously to the log provider
|
8
|
+
# @param xml [String] xml body to log
|
9
|
+
# @param name [String] title for the log
|
10
|
+
def perform(body:, name:)
|
11
|
+
# Savon logger does a nice job of XML pretty print
|
12
|
+
# Takes: message, list of filters, pretty print boolean
|
13
|
+
AchClient::Logging.log_provider.send_logs(
|
14
|
+
body: maybe_encrypt_message(
|
15
|
+
message: Savon::LogMessage.new(
|
16
|
+
body,
|
17
|
+
AchClient::Logging.log_filters,
|
18
|
+
true
|
19
|
+
).to_s
|
20
|
+
),
|
21
|
+
name: name
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def maybe_encrypt_message(message:)
|
27
|
+
# Only encrypt if the client provided a password and a salt
|
28
|
+
if AchClient::Logging.should_use_encryption?
|
29
|
+
AchClient::Logging.codec.encrypt_and_sign(message)
|
30
|
+
else
|
31
|
+
message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Logging
|
3
|
+
# Base class for log providers
|
4
|
+
# Extending classes must implement send_logs
|
5
|
+
# The consumer may implement their own log provider and assign it to
|
6
|
+
# AchClient:
|
7
|
+
# ```ruby
|
8
|
+
# class MyCustomLogger < AchClient::Logging::LogProvider
|
9
|
+
# def self.send_logs(body:, name:)
|
10
|
+
# # Do whatever you want, like send the log data to S3, or whatever
|
11
|
+
# # logging service you choose
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# AchClient::Logging.log_provider = MyCustomLogger
|
15
|
+
# ```
|
16
|
+
class LogProvider
|
17
|
+
def self.send_logs(body:, name:)
|
18
|
+
raise AbstractMethodError, "#{body}#{name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Logging
|
3
|
+
# Logs to stdout (using puts)
|
4
|
+
class StdoutLogProvider < LogProvider
|
5
|
+
# Prints the name and then the body
|
6
|
+
# @param body [String] the log body
|
7
|
+
# @param name [String] the log name
|
8
|
+
def self.send_logs(body:, name:)
|
9
|
+
puts name
|
10
|
+
puts body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative './savon_observer.rb'
|
2
|
+
require_relative './log_providers/log_provider.rb'
|
3
|
+
|
4
|
+
module AchClient
|
5
|
+
# Base class for all things logging
|
6
|
+
class Logging
|
7
|
+
|
8
|
+
# Register our observer with Savon
|
9
|
+
Savon.observers << AchClient::Logging::SavonObserver.new
|
10
|
+
|
11
|
+
# @return [Class] type of the log provider
|
12
|
+
class_attribute :_log_provider_type
|
13
|
+
|
14
|
+
# @return [String] password to use for encrypting logs
|
15
|
+
class_attribute :encryption_password
|
16
|
+
|
17
|
+
# @return [String] salt to use for encrypting logs
|
18
|
+
class_attribute :encryption_salt
|
19
|
+
|
20
|
+
# Defaults to AchClient::Logging::NullLogProvider
|
21
|
+
# @return [Class] The Log provider to use.
|
22
|
+
def self.log_provider
|
23
|
+
self._log_provider_type || AchClient::Logging::NullLogProvider
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set the log provider
|
27
|
+
# Must extend AchClient::Logging::LogProvider
|
28
|
+
# @param [Class] the new log provider
|
29
|
+
def self.log_provider=(log_provider)
|
30
|
+
if log_provider.is_a?(Class) &&
|
31
|
+
log_provider < AchClient::Logging::LogProvider
|
32
|
+
self._log_provider_type = log_provider
|
33
|
+
else
|
34
|
+
raise 'Must be subclass of AchClient::Logging::LogProvider'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<String>] List of XML nodes to scrub
|
39
|
+
class_attribute :_log_filters
|
40
|
+
|
41
|
+
# @return [Array<String>] List of XML nodes to scrub
|
42
|
+
def self.log_filters
|
43
|
+
self._log_filters || []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Updates the log filters
|
47
|
+
# @param filters [Array<String>] List of XML nodes to scrub from the logs
|
48
|
+
def self.log_filters=(filters)
|
49
|
+
self._log_filters = filters
|
50
|
+
end
|
51
|
+
|
52
|
+
# Creates a codec (does encryption and decryption) using the provided
|
53
|
+
# password and salt
|
54
|
+
# @return [ActiveSupport::MessageEncryptor] the encryptor
|
55
|
+
def self.codec
|
56
|
+
ActiveSupport::MessageEncryptor.new(
|
57
|
+
ActiveSupport::KeyGenerator.new(
|
58
|
+
AchClient::Logging.encryption_password
|
59
|
+
).generate_key(AchClient::Logging.encryption_salt)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns whether the client has enabled encryption - if both a username and
|
64
|
+
# salt have been provided
|
65
|
+
# @return [Boolean] is encryption enabled?
|
66
|
+
def self.should_use_encryption?
|
67
|
+
encryption_password && encryption_salt
|
68
|
+
end
|
69
|
+
|
70
|
+
# Decrypt encrypted log file
|
71
|
+
# @param gibberish [String] the log body to decrypt
|
72
|
+
def self.decrypt_log(gibberish)
|
73
|
+
codec.decrypt_and_verify(gibberish)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Logging
|
3
|
+
# Hooks into every savon request.
|
4
|
+
# #notify is called before the request is made
|
5
|
+
class SavonObserver
|
6
|
+
|
7
|
+
##
|
8
|
+
# Hooks into every SOAP request and sends the XML body to be logged.
|
9
|
+
# @param operation_name [Symbol] name of SOAP operation being exectuted
|
10
|
+
# @param builder [Savon::Builder] Savon wrapper for the request
|
11
|
+
# @param globals [Savon::GlobalOptions] Savon's global options
|
12
|
+
# @param locals [Savon::LocalOptions] Savon's global options
|
13
|
+
# @return [NilClass] returns nothing so the request is not mutated
|
14
|
+
def notify(operation_name, builder, _globals, _locals)
|
15
|
+
# Send the xml body to the logger job
|
16
|
+
AchClient::Logging::LogProviderJob.perform_async(
|
17
|
+
body: builder.to_s,
|
18
|
+
name: "request-#{operation_name}-#{DateTime.now}.xml"
|
19
|
+
)
|
20
|
+
|
21
|
+
# Must return nil so the request is unaltered
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AchClient
|
2
|
+
## Representations of merchant account types (checking or savings)
|
3
|
+
module AccountTypes
|
4
|
+
## Base class for all account types
|
5
|
+
class AccountType
|
6
|
+
end
|
7
|
+
|
8
|
+
## Represents a checking account
|
9
|
+
class Checking < AchClient::AccountTypes::AccountType
|
10
|
+
end
|
11
|
+
|
12
|
+
## Represents a savings account
|
13
|
+
class Savings < AchClient::AccountTypes::AccountType
|
14
|
+
end
|
15
|
+
|
16
|
+
## Represents a business checking account
|
17
|
+
class BusinessChecking < AchClient::AccountTypes::Checking
|
18
|
+
end
|
19
|
+
|
20
|
+
## Represents a business savings account
|
21
|
+
class BusinessSavings < AchClient::AccountTypes::Savings
|
22
|
+
end
|
23
|
+
|
24
|
+
## Represents a personal checking account
|
25
|
+
class PersonalChecking < AchClient::AccountTypes::Checking
|
26
|
+
end
|
27
|
+
|
28
|
+
## Represents a personal savings account
|
29
|
+
class PersonalSavings < AchClient::AccountTypes::Savings
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Abstract Response wrapper for the various ACH response statuses
|
3
|
+
class AchResponse
|
4
|
+
|
5
|
+
|
6
|
+
attr_reader :amount, # Amount of the processed ACH
|
7
|
+
:date # Date the Ach was in this status
|
8
|
+
|
9
|
+
def initialize(amount:, date:)
|
10
|
+
raise AbstractMethodError if self.class == AchClient::AchResponse
|
11
|
+
@amount = amount
|
12
|
+
@date = date
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative './returned_ach_response.rb'
|
2
|
+
|
3
|
+
module AchClient
|
4
|
+
# Representation of an Ach return that contains a correction
|
5
|
+
class CorrectedAchResponse < ReturnedAchResponse
|
6
|
+
attr_reader :corrections
|
7
|
+
|
8
|
+
##
|
9
|
+
# @param date [DateTime] date of correction return
|
10
|
+
# @param return_code [AchClient::ReturnCode] Ach Return code for the
|
11
|
+
# correction (ie AchClient::ReturnCodes.find_by(code: 'C01'))
|
12
|
+
# @param corrections [Hash] A hash of corrected attributes and their values
|
13
|
+
def initialize(amount:, date:, return_code:, corrections:)
|
14
|
+
@corrections = corrections
|
15
|
+
super(amount: amount, date: date, return_code: return_code)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Representation of an Ach return that failed
|
3
|
+
class ReturnedAchResponse < AchResponse
|
4
|
+
|
5
|
+
attr_reader :return_code
|
6
|
+
|
7
|
+
##
|
8
|
+
# @param date [DateTime] date of correction return
|
9
|
+
# @param return_code [AchClient::ReturnCode] Ach Return code for the
|
10
|
+
# correction (ie AchClient::ReturnCodes.find_by(code: 'C01'))
|
11
|
+
def initialize(amount:, date:, return_code:)
|
12
|
+
@return_code = return_code
|
13
|
+
super(amount: amount, date: date)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Represents an Ach Return code. Consult NACHA documentation for a full list
|
3
|
+
# See config/return_codes.yml for our list.
|
4
|
+
class ReturnCode
|
5
|
+
|
6
|
+
attr_accessor :code,
|
7
|
+
:description,
|
8
|
+
:reason
|
9
|
+
|
10
|
+
# Constructs a Ach return code
|
11
|
+
# @param code [String] the 3 char code identifier (ie 'R01')
|
12
|
+
# @param description [String] full explanation of the return
|
13
|
+
# @param reason [String] shorter explanation of the return
|
14
|
+
def initialize(code:, description:, reason: nil)
|
15
|
+
@code = code
|
16
|
+
@description = description
|
17
|
+
@reason = reason
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Finding and listing all Ach return codes
|
3
|
+
class ReturnCodes
|
4
|
+
|
5
|
+
# The path to the file where the return codes are enumerated
|
6
|
+
RETURN_CODES_YAML = '../../../config/return_codes.yml'
|
7
|
+
|
8
|
+
class_attribute :_return_codes
|
9
|
+
|
10
|
+
# @return [Array<AchClient::ReturnCode>] A list of all return codes.
|
11
|
+
def self.all
|
12
|
+
self._return_codes ||= YAML.load_file(
|
13
|
+
File.expand_path(File.join(File.dirname(__FILE__), RETURN_CODES_YAML))
|
14
|
+
).map do |code|
|
15
|
+
ReturnCode.new(
|
16
|
+
code: code['code'],
|
17
|
+
description: code['description'],
|
18
|
+
reason: code['reason']
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finds the first ReturnCode with the given code, or raises an exception.
|
24
|
+
# @param [String] 3 char code identifier for a return code
|
25
|
+
# @param [AchClient::ReturnCode] The ReturnCode object with that code
|
26
|
+
def self.find_by(code:)
|
27
|
+
self.all.find do |return_code|
|
28
|
+
return_code.code == code
|
29
|
+
# For some reason || is bad syntax in this context
|
30
|
+
end or raise "Could not find return code #{code}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AchClient
|
2
|
+
## Representations of Ach transaction types (debit or credit)
|
3
|
+
module TransactionTypes
|
4
|
+
## Base class for all transaction types
|
5
|
+
class TransactionType
|
6
|
+
end
|
7
|
+
|
8
|
+
## Representation of a credit transaction type
|
9
|
+
class Credit < AchClient::TransactionTypes::TransactionType
|
10
|
+
end
|
11
|
+
|
12
|
+
## Representation of a debit transaction type
|
13
|
+
class Debit < AchClient::TransactionTypes::TransactionType
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AchClient
|
2
|
+
# Abstract Ach provider, which all other provider's inherit from
|
3
|
+
class Abstract
|
4
|
+
|
5
|
+
# Base class for sending batched ACH transactions to various providers
|
6
|
+
class AchBatch
|
7
|
+
|
8
|
+
##
|
9
|
+
# @param ach_transactions [Array] List of AchTransaction objects to batch
|
10
|
+
def initialize(ach_transactions: [])
|
11
|
+
@ach_transactions = ach_transactions
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Sends the batch to the provider, and returns a tracking identifier
|
16
|
+
# @return [Array<String>] Identifiers to use to poll for result of batch
|
17
|
+
# processing later on.
|
18
|
+
def send_batch
|
19
|
+
raise AbstractMethodError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Abstract
|
3
|
+
|
4
|
+
# Interface for polling provider for ACH status responses
|
5
|
+
class AchStatusChecker
|
6
|
+
# Wrapper for a provider's "most recent bucket"
|
7
|
+
# @return [Hash{String => AchClient::AchResponse}] Hash with provider's
|
8
|
+
# external ACH id as the key, AchResponse objects as values
|
9
|
+
def self.most_recent
|
10
|
+
raise AbstractMethodError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Wrapper for a providers range response endpoint
|
14
|
+
# @return [Hash{String => AchClient::AchResponse}] Hash with provider's
|
15
|
+
# external ACH id as the key, AchResponse objects as values
|
16
|
+
def self.in_range(*)
|
17
|
+
raise AbstractMethodError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Abstract
|
3
|
+
|
4
|
+
# Generic representation of a single Ach transaction
|
5
|
+
class AchTransaction
|
6
|
+
|
7
|
+
##
|
8
|
+
# @return [Array] A list of arguments to use in the initializer, and as
|
9
|
+
# instance attributes
|
10
|
+
def self.arguments
|
11
|
+
[
|
12
|
+
:account_number,
|
13
|
+
:account_type,
|
14
|
+
:amount,
|
15
|
+
:effective_entry_date,
|
16
|
+
:external_ach_id,
|
17
|
+
:memo,
|
18
|
+
:merchant_name,
|
19
|
+
:originator_name,
|
20
|
+
:routing_number,
|
21
|
+
:sec_code,
|
22
|
+
:transaction_type
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader(*arguments)
|
27
|
+
|
28
|
+
##
|
29
|
+
# @param account_number [String] Merchant's account number
|
30
|
+
# @param account_type [AchClient::AccountTypes::AccountType] Merchant's account type
|
31
|
+
# (debit or credit), must be an instance of AchClient::AccountTypes::AccountType
|
32
|
+
# @param amount [BigDecimal] Amount of the ACH transaction
|
33
|
+
# @param effective_entry_date [Date] The date the transaction should be enacted
|
34
|
+
# @param external_ach_id [String] Tracking string you would like the
|
35
|
+
# provider to use with your transaction
|
36
|
+
# @param memo [String] Ach memo thing
|
37
|
+
# @param merchant_name [String] Name associated with merchantaccount we
|
38
|
+
# are ACHing with
|
39
|
+
# @param originator_name [String] String identifying you, will appear on
|
40
|
+
# merchants bank statement
|
41
|
+
# @param routing_number [String] Routing number of the merchant's account
|
42
|
+
# @param sec_code [String] CCD, PPD, WEB, etc.
|
43
|
+
# See: https://en.wikipedia.org/wiki/Automated_Clearing_House#SEC_codes
|
44
|
+
# @param transaction_type [AchClient::TransactionTypes::TransactionType] debit or
|
45
|
+
def initialize(*arguments)
|
46
|
+
args = arguments.extract_options!
|
47
|
+
self.class.arguments.each do |param|
|
48
|
+
self.instance_variable_set(
|
49
|
+
"@#{param}".to_sym,
|
50
|
+
args[param]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Boolean] true if transaction is a debit
|
56
|
+
def debit?
|
57
|
+
transaction_type == AchClient::TransactionTypes::Debit
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Boolean] true if transaction is a credit
|
61
|
+
def credit?
|
62
|
+
transaction_type == AchClient::TransactionTypes::Credit
|
63
|
+
end
|
64
|
+
|
65
|
+
def send
|
66
|
+
raise AbstractMethodError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Abstract
|
3
|
+
# Interface for storing company credentials used with a provider
|
4
|
+
class CompanyInfo
|
5
|
+
|
6
|
+
##
|
7
|
+
# @return [CompanyInfo] instance built from configuration values
|
8
|
+
def self.build
|
9
|
+
raise AbstractMethodError
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Build a hash to send to provider
|
14
|
+
# @return [Hash] hash to send to provider
|
15
|
+
def to_hash
|
16
|
+
raise AbstractMethodError
|
17
|
+
end
|
18
|
+
|
19
|
+
private_class_method def self.build_from_config(args)
|
20
|
+
self.new(
|
21
|
+
args.map do |arg|
|
22
|
+
{arg => self.to_s.deconstantize.constantize.send(arg)}
|
23
|
+
end.reduce(&:merge)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Abstract
|
3
|
+
## Interface for turning a Provider's response representation of an ACH
|
4
|
+
# status into an instance of AchClient::Response
|
5
|
+
class ResponseRecordProcessor
|
6
|
+
## Interface for turning a Provider's response representation of an ACH
|
7
|
+
# status into an instance of AchClient::Response
|
8
|
+
# @param record [Object] the record from the provider
|
9
|
+
# @return [AchClient::AchResponse] our representation of the response
|
10
|
+
def self.process_response_record(_record)
|
11
|
+
raise AbstractMethodError
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AchClient
|
2
|
+
##
|
3
|
+
# Transforms between client class and the strings that the provider expects
|
4
|
+
# For example, the TransactionType classes are serialized as 'C' or 'D' for
|
5
|
+
# AchWorks
|
6
|
+
class Transformer
|
7
|
+
|
8
|
+
##
|
9
|
+
# Convert provider's string representation to AchClient class
|
10
|
+
# representation
|
11
|
+
# @param string [String] string to turn into class
|
12
|
+
# @return [Class] for example AchClient::AccountTypes::Checking or
|
13
|
+
# AchClient::AccountTypes::Savings
|
14
|
+
def self.deserialize_provider_value(string)
|
15
|
+
self.transformer[string] or raise(
|
16
|
+
"Unknown #{self} string #{string}"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Convert client class to string representation used by provider
|
22
|
+
# @param type [Class] the type to convert to a string
|
23
|
+
# @return [String] string serialization of the input class
|
24
|
+
def self.serialize_to_provider_value(type)
|
25
|
+
self.transformer.find do |_, v|
|
26
|
+
type <= v
|
27
|
+
end.try(:first) or raise(
|
28
|
+
"type must be one of #{self.transformer.values.join(', ')}"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Mapping of classes to strings, to be overridden
|
33
|
+
# @return [Hash {String => Class}] the mapping
|
34
|
+
def self.transformer
|
35
|
+
raise AbstractMethodError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AchClient
|
2
|
+
class Sftp
|
3
|
+
##
|
4
|
+
# Transforms AccountTypes between AchClient class and the string
|
5
|
+
# that NACHA expects
|
6
|
+
class AccountTypeTransformer < AchClient::Transformer
|
7
|
+
|
8
|
+
# '2' means Checking, '3' means Savings
|
9
|
+
# The account type string is the first character in the transaction_code
|
10
|
+
# field.
|
11
|
+
# @return [Hash {String => Class}] the mapping
|
12
|
+
def self.transformer
|
13
|
+
{
|
14
|
+
'3' => AchClient::AccountTypes::Savings,
|
15
|
+
'2' => AchClient::AccountTypes::Checking
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|