ach_client 0.5.1
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.
- 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
|