wire_client 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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +35 -0
- data/.editorconfig +13 -0
- data/.gitignore +11 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.tool-versions-e +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +46 -0
- data/Rakefile +27 -0
- data/bin/setup +8 -0
- data/lib/wire_client/account/account.rb +59 -0
- data/lib/wire_client/account/creditor_account.rb +7 -0
- data/lib/wire_client/account/debitor_account.rb +7 -0
- data/lib/wire_client/base/constants.rb +61 -0
- data/lib/wire_client/base/converters.rb +46 -0
- data/lib/wire_client/base/invalid_wire_transaction_error.rb +4 -0
- data/lib/wire_client/base/invalid_wire_transaction_type_error.rb +3 -0
- data/lib/wire_client/base/validators.rb +95 -0
- data/lib/wire_client/messages/credit_transfer.rb +112 -0
- data/lib/wire_client/messages/direct_debit.rb +148 -0
- data/lib/wire_client/messages/message.rb +215 -0
- data/lib/wire_client/providers/base/wire_batch.rb +120 -0
- data/lib/wire_client/providers/sftp/sftp_provider.rb +39 -0
- data/lib/wire_client/providers/sftp/wire_batch.rb +39 -0
- data/lib/wire_client/transaction/credit_transfer_transaction.rb +20 -0
- data/lib/wire_client/transaction/direct_debit_transaction.rb +49 -0
- data/lib/wire_client/transaction/transaction.rb +87 -0
- data/lib/wire_client/version.rb +4 -0
- data/lib/wire_client.rb +36 -0
- data/schemas/.gitattributes +1 -0
- data/schemas/pain.001.001.03.xsd +921 -0
- data/schemas/pain.008.001.02.xsd +879 -0
- data/wire_client.gemspec +66 -0
- metadata +375 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
require_relative './message'
|
2
|
+
require_relative '../transaction/direct_debit_transaction'
|
3
|
+
|
4
|
+
module WireClient
|
5
|
+
class DirectDebit < Message
|
6
|
+
self.account_class = CreditorAccount
|
7
|
+
self.transaction_class = DirectDebitTransaction
|
8
|
+
self.xml_main_tag = 'CstmrDrctDbtInitn'
|
9
|
+
self.known_schemas = [WireClient::PAIN_008_001_02]
|
10
|
+
|
11
|
+
validate do |record|
|
12
|
+
if record.transactions.map(&:local_instrument).uniq.size > 1
|
13
|
+
errors.add(
|
14
|
+
:base,
|
15
|
+
'CORE, COR1 and B2B must not be mixed in one message!'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Find groups of transactions which share the same values for
|
23
|
+
# selected attributes
|
24
|
+
def transaction_group(transaction)
|
25
|
+
{
|
26
|
+
requested_date: transaction.requested_date,
|
27
|
+
local_instrument: transaction.local_instrument,
|
28
|
+
sequence_type: transaction.sequence_type,
|
29
|
+
batch_booking: transaction.batch_booking,
|
30
|
+
account: transaction.creditor_account || account,
|
31
|
+
service_priority: transaction.service_priority,
|
32
|
+
service_level: transaction.service_level
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_payment_information(builder)
|
37
|
+
# Build a PmtInf block for every group of transactions
|
38
|
+
grouped_transactions.each do |group, transactions|
|
39
|
+
builder.PmtInf do
|
40
|
+
builder.PmtInfId(payment_information_identification(group))
|
41
|
+
builder.PmtMtd('TRF')
|
42
|
+
builder.BtchBookg(group[:batch_booking])
|
43
|
+
builder.NbOfTxs(transactions.length)
|
44
|
+
builder.CtrlSum('%.2f' % amount_total(transactions))
|
45
|
+
builder.PmtTpInf do
|
46
|
+
builder.InstrPrty(group[:service_priority])
|
47
|
+
builder.SvcLvl do
|
48
|
+
builder.Cd(group[:service_level])
|
49
|
+
end
|
50
|
+
builder.LclInstrm do
|
51
|
+
builder.Cd(group[:local_instrument])
|
52
|
+
end
|
53
|
+
builder.SeqTp(group[:sequence_type])
|
54
|
+
end
|
55
|
+
builder.ReqdColltnDt(group[:requested_date].iso8601)
|
56
|
+
builder.Cdtr do
|
57
|
+
builder.Nm(group[:account].name)
|
58
|
+
builder.PstlAdr do
|
59
|
+
builder.CtrySubDvsn(account.country_subdivision_name)
|
60
|
+
builder.Ctry(account.country)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
builder.CdtrAcct do
|
64
|
+
account_id(builder, group[:account])
|
65
|
+
end
|
66
|
+
builder.CdtrAgt do
|
67
|
+
builder.FinInstnId do
|
68
|
+
account_agent_id(builder, group[:account])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if account.charge_bearer
|
73
|
+
builder.ChrgBr(account.charge_bearer)
|
74
|
+
end
|
75
|
+
|
76
|
+
transactions.each do |transaction|
|
77
|
+
build_transaction(builder, transaction)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_amendment_informations(builder, transaction)
|
84
|
+
return unless transaction.original_debtor_account ||
|
85
|
+
transaction.same_mandate_new_debtor_agent
|
86
|
+
builder.AmdmntInd(true)
|
87
|
+
builder.AmdmntInfDtls do
|
88
|
+
if transaction.original_debtor_account
|
89
|
+
builder.OrgnlDbtrAcct do
|
90
|
+
builder.Id do
|
91
|
+
builder.IBAN(transaction.original_debtor_account)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
builder.OrgnlDbtrAgt do
|
96
|
+
builder.FinInstnId do
|
97
|
+
builder.Othr do
|
98
|
+
builder.Id('SMNDA')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_transaction(builder, transaction)
|
107
|
+
builder.DrctDbtTxInf do
|
108
|
+
builder.PmtId do
|
109
|
+
if transaction.instruction.present?
|
110
|
+
builder.InstrId(transaction.instruction)
|
111
|
+
end
|
112
|
+
builder.EndToEndId(transaction.reference)
|
113
|
+
end
|
114
|
+
builder.InstdAmt(
|
115
|
+
'%.2f' % transaction.amount,
|
116
|
+
Ccy: transaction.currency
|
117
|
+
)
|
118
|
+
builder.DrctDbtTx do
|
119
|
+
builder.MndtRltdInf do
|
120
|
+
builder.MndtId(transaction.mandate_id)
|
121
|
+
builder.DtOfSgntr(transaction.mandate_date_of_signature.iso8601)
|
122
|
+
build_amendment_informations(builder, transaction)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
builder.DbtrAgt do
|
126
|
+
builder.FinInstnId do
|
127
|
+
transaction_agent_id(builder, transaction)
|
128
|
+
builder.Nm(transaction.agent_name)
|
129
|
+
builder.PstlAdr do
|
130
|
+
builder.Ctry(transaction.country)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
builder.Dbtr do
|
135
|
+
builder.Nm(transaction.name)
|
136
|
+
end
|
137
|
+
builder.DbtrAcct do
|
138
|
+
transaction_account_id(builder, transaction)
|
139
|
+
end
|
140
|
+
if transaction.remittance_information
|
141
|
+
builder.RmtInf do
|
142
|
+
builder.Ustrd(transaction.remittance_information)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module WireClient
|
2
|
+
class Message
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
attr_reader :account, :grouped_transactions
|
6
|
+
|
7
|
+
validates_presence_of :transactions
|
8
|
+
validate do |record|
|
9
|
+
unless record.account.valid?
|
10
|
+
record.errors.add(:account, record.account.errors.full_messages)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class_attribute :account_class,
|
15
|
+
:transaction_class,
|
16
|
+
:xml_main_tag,
|
17
|
+
:known_schemas
|
18
|
+
|
19
|
+
def initialize(account_options={})
|
20
|
+
@grouped_transactions = {}
|
21
|
+
@account = account_class.new(account_options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_transaction(options)
|
25
|
+
transaction = transaction_class.new(options)
|
26
|
+
unless transaction.valid?
|
27
|
+
raise ArgumentError, transaction.error_messages
|
28
|
+
end
|
29
|
+
@grouped_transactions[transaction_group(transaction)] ||= []
|
30
|
+
@grouped_transactions[transaction_group(transaction)] << transaction
|
31
|
+
end
|
32
|
+
|
33
|
+
def transactions
|
34
|
+
grouped_transactions.values.flatten
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] xml
|
38
|
+
def to_xml(schema_name=self.class.known_schemas.first)
|
39
|
+
raise(RuntimeError, errors.full_messages.join("\n")) unless valid?
|
40
|
+
unless schema_compatible?(schema_name)
|
41
|
+
raise RuntimeError, "Incompatible with schema #{schema_name}!"
|
42
|
+
end
|
43
|
+
|
44
|
+
builder = Builder::XmlMarkup.new indent: 2
|
45
|
+
builder.instruct! :xml
|
46
|
+
builder.Document(xml_schema(schema_name)) do
|
47
|
+
builder.__send__(self.class.xml_main_tag) do
|
48
|
+
build_group_header(builder)
|
49
|
+
build_payment_information(builder)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def amount_total(selected_transactions=transactions)
|
55
|
+
selected_transactions.inject(0) { |sum, t| sum + t.amount }
|
56
|
+
end
|
57
|
+
|
58
|
+
def schema_compatible?(schema_name)
|
59
|
+
unless self.known_schemas.include?(schema_name)
|
60
|
+
raise ArgumentError, "Schema #{schema_name} is unknown!"
|
61
|
+
end
|
62
|
+
|
63
|
+
transactions.all? { |t| t.schema_compatible?(schema_name) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set unique identifer for the message
|
67
|
+
def message_identification=(value)
|
68
|
+
unless value.is_a?(String)
|
69
|
+
raise ArgumentError, 'mesage_identification must be a string!'
|
70
|
+
end
|
71
|
+
|
72
|
+
regex = /\A([A-Za-z0-9]|[\+|\?|\/|\-|\:|\(|\)|\.|\,|\'|\ ]){1,35}\z/
|
73
|
+
unless value.match(regex)
|
74
|
+
raise ArgumentError, "mesage_identification does not match #{regex}!"
|
75
|
+
end
|
76
|
+
|
77
|
+
@message_identification = value
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get unique identifer for the message (with fallback to a random string)
|
81
|
+
def message_identification
|
82
|
+
@message_identification ||= "WIRE/#{SecureRandom.hex(5)}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the id of the batch to which the given transaction belongs
|
86
|
+
# Identified based upon the reference of the transaction
|
87
|
+
def batch_id(transaction_reference)
|
88
|
+
grouped_transactions.each do |group, transactions|
|
89
|
+
selected_transactions = begin
|
90
|
+
transactions.select do |transaction|
|
91
|
+
transaction.reference == transaction_reference
|
92
|
+
end
|
93
|
+
end
|
94
|
+
if selected_transactions.any?
|
95
|
+
return payment_information_identification(group)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def batches
|
101
|
+
grouped_transactions.keys.collect do |group|
|
102
|
+
payment_information_identification(group)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def error_messages
|
107
|
+
errors.full_messages.join("\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# @return {Hash<Symbol=>String>} xml schema information used in output xml
|
113
|
+
def xml_schema(schema_name)
|
114
|
+
urn = "urn:iso:std:iso:20022:tech:xsd:#{schema_name}"
|
115
|
+
{
|
116
|
+
:xmlns => "#{urn}",
|
117
|
+
:'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
118
|
+
:'xsi:schemaLocation' => "#{urn} #{schema_name}.xsd"
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def build_group_header(builder)
|
123
|
+
builder.GrpHdr do
|
124
|
+
builder.MsgId(message_identification)
|
125
|
+
builder.CreDtTm(Time.now.iso8601)
|
126
|
+
builder.NbOfTxs(transactions.length)
|
127
|
+
builder.CtrlSum('%.2f' % amount_total)
|
128
|
+
builder.InitgPty do
|
129
|
+
builder.Nm(account.name)
|
130
|
+
builder.Id do
|
131
|
+
builder.OrgId do
|
132
|
+
builder.Othr do
|
133
|
+
builder.Id(account.identifier)
|
134
|
+
builder.SchmeNm do
|
135
|
+
builder.Cd(account.schema_code)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Unique and consecutive identifier (used for the <PmntInf> blocks)
|
145
|
+
def payment_information_identification(group)
|
146
|
+
"#{message_identification}/#{grouped_transactions.keys.index(group) + 1}"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns a key to determine the group to which the transaction belongs
|
150
|
+
def transaction_group(transaction)
|
151
|
+
transaction
|
152
|
+
end
|
153
|
+
|
154
|
+
def account_agent_id(builder, account)
|
155
|
+
if account.bic
|
156
|
+
builder.BIC(account.bic)
|
157
|
+
else
|
158
|
+
builder.ClrSysMmbId do
|
159
|
+
builder.ClrSysId do
|
160
|
+
builder.Cd(account.clear_system_code)
|
161
|
+
end
|
162
|
+
builder.MmbId(account.wire_routing_number)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def account_id(builder, account)
|
168
|
+
if account.iban
|
169
|
+
builder.Id do
|
170
|
+
builder.IBAN(account.iban)
|
171
|
+
end
|
172
|
+
else
|
173
|
+
builder.Id do
|
174
|
+
builder.Othr do
|
175
|
+
builder.Id(account.account_number)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
builder.Tp do
|
179
|
+
builder.Cd('CACC')
|
180
|
+
end
|
181
|
+
builder.Ccy(account.currency)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def transaction_agent_id(builder, transaction)
|
186
|
+
if transaction.bic
|
187
|
+
builder.BIC(transaction.bic)
|
188
|
+
else
|
189
|
+
builder.ClrSysMmbId do
|
190
|
+
builder.ClrSysId do
|
191
|
+
builder.Cd(transaction.clear_system_code)
|
192
|
+
end
|
193
|
+
builder.MmbId(transaction.wire_routing_number)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def transaction_account_id(builder, transaction)
|
199
|
+
if transaction.iban
|
200
|
+
builder.Id do
|
201
|
+
builder.IBAN(transaction.iban)
|
202
|
+
end
|
203
|
+
else
|
204
|
+
builder.Id do
|
205
|
+
builder.Othr do
|
206
|
+
builder.Id(transaction.account_number)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
builder.Tp do
|
210
|
+
builder.Cd('CACC')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module WireClient
|
2
|
+
# Abstract Wire transfer provider, which all other providers inherit from
|
3
|
+
class Abstract
|
4
|
+
|
5
|
+
# Base class for sending batched Wire transfer transactions to various
|
6
|
+
# providers
|
7
|
+
class WireBatch
|
8
|
+
# An initiator is an entity that initiates the transfer process; it can
|
9
|
+
# be a debtor or a creditor. A receptor, on the other hand, is someone
|
10
|
+
# who will fulfill the transfer, whether receiving or withdrawing the
|
11
|
+
# described amount.
|
12
|
+
|
13
|
+
# @return [String] Debtor's or creditor's name for the one initianting
|
14
|
+
# the transfer
|
15
|
+
class_attribute :initiator_name
|
16
|
+
|
17
|
+
# @return [String] Debtor's or creditor's IBAN (International Bank
|
18
|
+
# Account Number) ID
|
19
|
+
class_attribute :initiator_iban
|
20
|
+
|
21
|
+
# @return [String] Debtor's or creditor's bank SWIFT Code / BIC
|
22
|
+
class_attribute :initiator_bic
|
23
|
+
|
24
|
+
# @return [String] Debtor's or creditor's Account Number
|
25
|
+
class_attribute :initiator_account_number
|
26
|
+
|
27
|
+
# @return [String] Debtor or creditor agent's wire routing number
|
28
|
+
class_attribute :initiator_wire_routing_number
|
29
|
+
|
30
|
+
# @return [String] The initiating party's Identifier
|
31
|
+
class_attribute :initiator_identifier
|
32
|
+
|
33
|
+
# @return [String] The initiating party's country (2 character country
|
34
|
+
# code; default: US)
|
35
|
+
class_attribute :initiator_country
|
36
|
+
|
37
|
+
# @return [String] The initiating party's country subdivision (name or
|
38
|
+
# 2 character code; default: MA)
|
39
|
+
class_attribute :initiator_country_subdivision
|
40
|
+
|
41
|
+
##
|
42
|
+
# @return [Array] A list of arguments to use in the initializer, and as
|
43
|
+
# instance attributes
|
44
|
+
def self.arguments
|
45
|
+
[
|
46
|
+
:transaction_type
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader(*arguments)
|
51
|
+
|
52
|
+
##
|
53
|
+
# @param transaction_type [WireClient::TransactionTypes::TransactionType]
|
54
|
+
# debit or credit
|
55
|
+
def initialize(*arguments)
|
56
|
+
args = arguments.extract_options!
|
57
|
+
self.class.arguments.each do |param|
|
58
|
+
self.instance_variable_set(
|
59
|
+
"@#{param}".to_sym,
|
60
|
+
args[param]
|
61
|
+
)
|
62
|
+
end
|
63
|
+
if @transaction_type == WireClient::TransactionTypes::Credit
|
64
|
+
initialize_payment_initiation(CreditTransfer)
|
65
|
+
elsif @transaction_type == WireClient::TransactionTypes::Debit
|
66
|
+
initialize_payment_initiation(DirectDebit)
|
67
|
+
else
|
68
|
+
raise InvalidWireTransactionTypeError,
|
69
|
+
'Transaction type should be explicitly defined'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_transaction(transaction_options)
|
74
|
+
@payment_initiation.add_transaction(transaction_options)
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Sends the batch to the provider. Useful to check transaction status
|
79
|
+
# before sending any data (consistency, validation, etc.)
|
80
|
+
def send_batch
|
81
|
+
if @payment_initiation.valid?
|
82
|
+
do_send_batch
|
83
|
+
else
|
84
|
+
raise InvalidWireTransactionError,
|
85
|
+
"invalid wire transfer: #{@payment_initiation.error_messages}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Implementation of sending the Wire transfer batch to the provider, to be
|
90
|
+
# implemented by the subclass
|
91
|
+
def do_send_batch
|
92
|
+
raise AbstractMethodError
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def initialize_payment_initiation(klass)
|
98
|
+
if self.class.initiator_iban
|
99
|
+
@payment_initiation = klass.new(
|
100
|
+
name: self.class.initiator_name,
|
101
|
+
bic: self.class.initiator_bic,
|
102
|
+
iban: self.class.initiator_iban,
|
103
|
+
identifier: self.class.initiator_identifier,
|
104
|
+
country: self.class.initiator_country,
|
105
|
+
country_subdivision: self.class.initiator_country_subdivision
|
106
|
+
)
|
107
|
+
else
|
108
|
+
@payment_initiation = klass.new(
|
109
|
+
name: self.class.initiator_name,
|
110
|
+
wire_routing_number: self.class.initiator_wire_routing_number,
|
111
|
+
account_number: self.class.initiator_account_number,
|
112
|
+
identifier: self.class.initiator_identifier,
|
113
|
+
country: self.class.initiator_country,
|
114
|
+
country_subdivision: self.class.initiator_country_subdivision
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module WireClient
|
2
|
+
# Base concern for providers like SVB that use an SFTP system instead of API
|
3
|
+
module SftpProvider
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include AchClient::SftpProvider
|
6
|
+
|
7
|
+
included do
|
8
|
+
# @return [String] Hostname/URL of SVB's "Direct File Transmission" server
|
9
|
+
class_attribute :host
|
10
|
+
|
11
|
+
# @return [String] The username they gave you to login to the server
|
12
|
+
class_attribute :username
|
13
|
+
|
14
|
+
# @return [String] The password they gave you to login to the server
|
15
|
+
class_attribute :password
|
16
|
+
|
17
|
+
# @return [String] The private ssh key that matches the public ssh key you
|
18
|
+
# provided to SVB, ie the output of `cat path/to/private/ssh/key`
|
19
|
+
class_attribute :private_ssh_key
|
20
|
+
|
21
|
+
# @return [String | NilClass] Passphrase for your private ssh key
|
22
|
+
# (if applicable)
|
23
|
+
class_attribute :passphrase
|
24
|
+
|
25
|
+
# @return [String] The path on the remote server to the directory where
|
26
|
+
# you will deposit your outgoing NACHA files
|
27
|
+
class_attribute :outgoing_path
|
28
|
+
|
29
|
+
# @return [String] The path on the remote server to the directory where
|
30
|
+
# the SFTP provider will deposit return/confirmation files
|
31
|
+
class_attribute :incoming_path
|
32
|
+
|
33
|
+
# @return [Proc] A function that defines the filenaming strategy for your
|
34
|
+
# provider. The function should take an optional batch number and return
|
35
|
+
# a filename string
|
36
|
+
class_attribute :file_naming_strategy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module WireClient
|
2
|
+
# Namespace for all things Sftp
|
3
|
+
class Sftp
|
4
|
+
# NACHA representation of an AchBatch
|
5
|
+
class WireBatch < WireClient::Abstract::WireBatch
|
6
|
+
|
7
|
+
def initialize(transaction_type:, batch_number: nil)
|
8
|
+
super(transaction_type: transaction_type)
|
9
|
+
@batch_number = batch_number
|
10
|
+
end
|
11
|
+
|
12
|
+
# The filename used for the batch
|
13
|
+
# @return [String] filename to use
|
14
|
+
def batch_file_name
|
15
|
+
self.class.parent.file_naming_strategy.(@batch_number)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sends the batch to SFTP provider
|
19
|
+
def do_send_batch
|
20
|
+
file_path = File.join(
|
21
|
+
self.class.parent.outgoing_path,
|
22
|
+
batch_file_name
|
23
|
+
)
|
24
|
+
file_body = begin
|
25
|
+
if @transaction_type == WireClient::TransactionTypes::Credit
|
26
|
+
@payment_initiation.to_xml('pain.001.001.03').to_s
|
27
|
+
else
|
28
|
+
@payment_initiation.to_xml('pain.008.001.02').to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
self.class.parent.write_remote_file(
|
32
|
+
file_path: file_path,
|
33
|
+
file_body: file_body
|
34
|
+
)
|
35
|
+
[file_path, file_body]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative './transaction'
|
2
|
+
|
3
|
+
module WireClient
|
4
|
+
class CreditTransferTransaction < Transaction
|
5
|
+
attr_accessor :service_priority, :service_level
|
6
|
+
|
7
|
+
validates_inclusion_of :service_priority, :in => SERVICE_PRIORITY_TYPES
|
8
|
+
validates_inclusion_of :service_level, :in => SERVICE_LEVEL_TYPES
|
9
|
+
|
10
|
+
validate do |t|
|
11
|
+
t.validate_requested_date_after(Time.zone.now.to_date.next)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(attributes = {})
|
15
|
+
super
|
16
|
+
@service_priority ||= 'NORM'
|
17
|
+
@service_level ||= 'URGP'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative './transaction'
|
2
|
+
|
3
|
+
module WireClient
|
4
|
+
class DirectDebitTransaction < Transaction
|
5
|
+
|
6
|
+
attr_accessor :mandate_id,
|
7
|
+
:mandate_date_of_signature,
|
8
|
+
:local_instrument,
|
9
|
+
:sequence_type,
|
10
|
+
:creditor_account,
|
11
|
+
:original_debtor_account,
|
12
|
+
:same_mandate_new_debtor_agent,
|
13
|
+
:service_priority,
|
14
|
+
:service_level
|
15
|
+
|
16
|
+
validates_with MandateIdentifierValidator,
|
17
|
+
field_name: :mandate_id,
|
18
|
+
message: "%{value} is invalid"
|
19
|
+
validates_presence_of :mandate_date_of_signature
|
20
|
+
validates_inclusion_of :local_instrument, in: LOCAL_INSTRUMENTS
|
21
|
+
validates_inclusion_of :sequence_type, in: SEQUENCE_TYPES
|
22
|
+
validates_inclusion_of :service_priority, :in => SERVICE_PRIORITY_TYPES
|
23
|
+
validates_inclusion_of :service_level, :in => SERVICE_LEVEL_TYPES
|
24
|
+
|
25
|
+
validate do |t|
|
26
|
+
t.validate_requested_date_after(Time.zone.now.to_date.next)
|
27
|
+
end
|
28
|
+
|
29
|
+
validate do |t|
|
30
|
+
if creditor_account && !creditor_account.valid?
|
31
|
+
errors.add(:creditor_account, 'is not correct')
|
32
|
+
end
|
33
|
+
|
34
|
+
if t.mandate_date_of_signature.is_a?(Date)
|
35
|
+
if t.mandate_date_of_signature > Time.zone.now.to_date
|
36
|
+
errors.add(:mandate_date_of_signature, 'is in the future')
|
37
|
+
end
|
38
|
+
else
|
39
|
+
errors.add(:mandate_date_of_signature, 'is not a Date')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(attributes = {})
|
44
|
+
super
|
45
|
+
@local_instrument ||= 'B2B'
|
46
|
+
@sequence_type ||= 'OOFF'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|