sepafm 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8d907ad1e367406e6f1a98af49893e5f6211b04
4
- data.tar.gz: cc91cebf5cfc771b33e98c4136f948fde2626631
3
+ metadata.gz: 1bcf3a08974b359e66ea01c5c484f143d7416b94
4
+ data.tar.gz: 452856d46a88d78db3f2187d5d7618479a92f1ad
5
5
  SHA512:
6
- metadata.gz: 812949f6537bfd6e1df79f7a58b74033593e0788aa99103016eba8f9e578e2dd073337a11f58ff4fca586812d4b4842690e807f0c692c1f4bdaec9900481b02f
7
- data.tar.gz: e8a469ea127b5003583a99a1c2cfc593fdb53ecfc43b95046bab97c67f1e83d1d9d3844e8442e46b0d78f376fdab2aeb4687a1c5dedb3f9ab931b5af1a2b877e
6
+ metadata.gz: cd9566b7a6bd7237b7620a6d1245d6e42fa55b4c962857f03f8dae76c6a8c4aecc8b0341e87cb88b06527259fcf6debd5e4ff5e82e409047526a5473569aec11
7
+ data.tar.gz: 89f83a46436f0e6ecae783e4c0f964a0d0ce817bc6952e81d65acc1076f84903bc31cd7b9f36c89ecfeb92d71850098a6040ee46678b59c1b43d4544efb0a46d
data/README.md CHANGED
@@ -14,7 +14,7 @@ This project aims to create an open source implementation of SEPA Financial Mess
14
14
 
15
15
  Add this line to your application's Gemfile:
16
16
 
17
- gem 'sepa'
17
+ gem 'sepafm'
18
18
 
19
19
  And then execute:
20
20
 
@@ -22,15 +22,165 @@ And then execute:
22
22
 
23
23
  Or install it yourself as:
24
24
 
25
- $ gem install sepa
25
+ $ gem install sepafm
26
26
 
27
27
  ## Usage
28
28
 
29
+ ### Building the payload
30
+
31
+ * You can optionally have an invoice bundle included in a given transaction. If that is the case, you first have to define the invoices i.e. as follows:
32
+
33
+ invoice_bundle = []
34
+
35
+ invoice_1 = {
36
+
37
+ # Has to be either CINV for normal invoice or CREN for credit note.
38
+ type: 'CINV',
39
+
40
+ # Positive for invoices and negative for credits.
41
+ amount: '700',
42
+
43
+ currency: 'EUR',
44
+
45
+ # You either have to specify an invoice number or a reference.
46
+ invoice_number: '123456'
47
+ }
48
+
49
+ invoice_2 = {
50
+ type: 'CINV',
51
+ amount: '300',
52
+ currency: 'EUR',
53
+ reference: '123456789',
54
+ }
55
+
56
+ invoice_3 = {
57
+ type: 'CREN',
58
+ amount: '-100',
59
+ currency: 'EUR',
60
+ invoice_number: '654321'
61
+ }
62
+
63
+ invoice_4 = {
64
+ type: 'CREN',
65
+ amount: '-500',
66
+ currency: 'EUR',
67
+ reference: '987654321'
68
+ }
69
+
70
+ # All the invoices are pushed to an array which is later included in the
71
+ # transaction's params.
72
+ invoice_bundle.push(invoice_1)
73
+ invoice_bundle.push(invoice_2)
74
+ invoice_bundle.push(invoice_3)
75
+ invoice_bundle.push(invoice_4)
76
+
77
+ 1. Define parameters for the transactions. You need at least one and one payment can have multiple. It is also worth noting that one payload can also have multiple payments. Here's how the parameters are defined:
78
+
79
+ transactions_params = {
80
+
81
+ # Optional id for the transaction. This is returned to the payer only.
82
+ # Max length is 35 characters.
83
+ instruction_id: '70CEF29BEBA8396A1F806005EDA51DEE4CE',
84
+
85
+ # Mandatory id for the transaction. This id is also passed on the the
86
+ # beneficiary. Max length is 35 characters.
87
+ end_to_end_id: '629CADFDAD5246AD915BA24A3C8E9FC3313',
88
+
89
+ # Amount to be transferred. Decimals separated by a period.
90
+ amount: '30.75',
91
+
92
+ # Currency. For Euros EUR etc.
93
+ currency: 'EUR',
94
+
95
+ # Bank's unique BIC.
96
+ bic: 'NDEAFIHH',
97
+
98
+ # Name of the creditor company or person
99
+ name: 'Testi Saaja Oy',
100
+
101
+ # Street and street number of the creditor.
102
+ address: 'Kokeilukatu 66',
103
+
104
+ # Creditor's county code.
105
+ country: 'FI',
106
+
107
+ # Creditor's postcode.
108
+ postcode: '00200',
109
+
110
+ # Creditor's town
111
+ town: 'Helsinki',
112
+
113
+ # Creditor's IBAN.
114
+ iban: 'FI7429501800000014',
115
+
116
+ # Reference number for the transaction. Mandatory if message not given.
117
+ reference: '00000000000000001245',
118
+
119
+ # Message for the transaction. Mandatory if reference not given.
120
+ message: 'Maksu',
121
+
122
+ # Optional set of invoices to be bundled in this transaction. If a bundle
123
+ # is provided amount, currency and reference will be automatically taken
124
+ # from that bundle.
125
+ invoice_bundle: invoice_bundle
126
+ }
127
+
128
+ 2. Create an array of Sepa::Transaction objects in which you put all the transactions of a given payment. You need to create an array for each payment separately.
129
+
130
+ payment_transactions = []
131
+
132
+ payment_transactions.push(Sepa::Transaction.new(transaction_params))
133
+
134
+ 3. Define the parameters for the payment/payments:
135
+
136
+ payment_params = {
137
+
138
+ # Unique id the payment. Max length is 35 characters.
139
+ payment_info_id: 'F56D46DDA136A981F58C05999479E768C92',
140
+
141
+ # Requested executin date for the payment in form YYY-MM-DD.
142
+ execution_date: '2013-08-10',
143
+
144
+ # The array of transactions for this payment
145
+ transactions: payment_transactions
146
+
147
+ # If this is a payment consisting of salary of pension transactions, you
148
+ # need to provide the following flag.
149
+ salary_or_pension: true
150
+ }
151
+
152
+ 4. Define parameters for the debtor as follows:
153
+
154
+ debtor_params = {
155
+ name: 'Testi Maksaja Oy',
156
+ address: 'Testing Street 12',
157
+ country: 'FI',
158
+ postcode: '00100',
159
+ town: 'Helsinki',
160
+ customer_id: '111111111',
161
+ iban: 'FI4819503000000010',
162
+ bic: 'NDEAFIHH'
163
+ }
164
+
165
+ 5. Create an array of Sepa::Payment objects in which you put all the payments that are going to be in the payload.
166
+
167
+ payments = []
168
+
169
+ payments.push(Sepa::Payment.new(debtor_params, payment_params))
170
+
171
+ 6. Create the actual payload object:
172
+
173
+ payload = Sepa::Payload.new(debtor_params, payments)
174
+
175
+ # Will return the payload as an xml document which can be included in the
176
+ # construction of the SOAP message.
177
+ payload.to_xml
178
+
29
179
  ### Communicating with the bank
30
180
 
31
181
  1. Require the gem:
32
182
 
33
- require 'sepa'
183
+ require 'sepafm'
34
184
 
35
185
  2. Define the hash that will be passed to the gem when initializing it:
36
186
 
@@ -45,7 +195,7 @@ Or install it yourself as:
45
195
  target_id: '11111111A1',
46
196
  language: 'FI',
47
197
  file_type: 'TITO',
48
- content: payload,
198
+ content: payload, # You can use the payload you may have constructed earlier. I.e. payload.to_xml
49
199
  file_reference: "11111111A12006030329501800000014"
50
200
  }
51
201
 
@@ -112,7 +262,7 @@ Or install it yourself as:
112
262
 
113
263
  1. Require the gem:
114
264
 
115
- require 'sepa'
265
+ require 'sepafm'
116
266
 
117
267
  2. Define the hash that will be passed to the gem when initializing it:
118
268
 
@@ -136,7 +286,7 @@ Or install it yourself as:
136
286
 
137
287
  1. Require the gem:
138
288
 
139
- require 'sepa'
289
+ require 'sepafm'
140
290
 
141
291
  2. Define the hash that will be passed to the gem when initializing it:
142
292
 
@@ -1,4 +1,4 @@
1
- require 'sepa'
1
+ require 'sepafm'
2
2
 
3
3
  params = {
4
4
  bank: :danske,
@@ -12,4 +12,4 @@ params = {
12
12
 
13
13
  sepa_client = Sepa::Client.new(params)
14
14
 
15
- sepa_client.send
15
+ sepa_client.send
@@ -0,0 +1,2 @@
1
+ class SchemaError < StandardError
2
+ end
@@ -0,0 +1,109 @@
1
+ module Sepa
2
+ class Payload
3
+ def initialize(debtor, payments)
4
+ @debtor_name = debtor.fetch(:name)
5
+ @debtor_address = debtor.fetch(:address)
6
+ @debtor_country = debtor.fetch(:country)
7
+ @debtor_postcode = debtor.fetch(:postcode)
8
+ @debtor_town = debtor.fetch(:town)
9
+ @debtor_customer_id = debtor.fetch(:customer_id)
10
+
11
+ unless @payments = payments
12
+ fail KeyError, 'No payments provided for the payload.'
13
+ end
14
+
15
+ @doc = build
16
+ end
17
+
18
+ def to_xml
19
+ @doc.to_xml
20
+ end
21
+
22
+ # Checks whether the payload validates against the schema.
23
+ def valid?
24
+ @xsd ||= load_schema
25
+ @xsd.valid?(@doc)
26
+ end
27
+
28
+ # Errors that a schema validation of the document produces.
29
+ def errors
30
+ @xsd ||= load_schema
31
+ @xsd.validate(@doc).collect { |e| e.message }
32
+ end
33
+
34
+ private
35
+
36
+ def build
37
+ doc = build_root
38
+ doc = build_group_header(doc)
39
+ add_payments(doc)
40
+ end
41
+
42
+ # Gets the number of transactions in this payload.
43
+ def number_of_transactions
44
+ count = 0
45
+ @payments.each { |payment| count += payment.number_of_transactions }
46
+ count
47
+ end
48
+
49
+ # Builds the root and pain elements with namespace and schema definitions.
50
+ def build_root
51
+ builder = Nokogiri::XML::Builder.new do |xml|
52
+ xml.Document(
53
+ xmlns: 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.02',
54
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
55
+ 'xsi:schemaLocation' => 'urn:iso:std:iso:20022:tech:xsd:pain.001.' \
56
+ '001.02 pain.001.001.02.xsd'
57
+ ) {
58
+ xml.send 'pain.001.001.02'
59
+ }
60
+ end
61
+
62
+ builder.doc
63
+ end
64
+
65
+ # Builds the group header.
66
+ def build_group_header(root_e)
67
+ builder = Nokogiri::XML::Builder.with(root_e.at('Document > *')) do |xml|
68
+ xml.GrpHdr {
69
+ xml.MsgId SecureRandom.hex(17)
70
+ xml.CreDtTm Time.new.iso8601
71
+ xml.BtchBookg 'true'
72
+ xml.NbOfTxs number_of_transactions
73
+ xml.Grpg 'MIXD'
74
+ xml.InitgPty {
75
+ xml.Nm @debtor_name
76
+ xml.PstlAdr {
77
+ xml.AdrLine @debtor_address
78
+ xml.AdrLine "#{@debtor_country}-#{@debtor_postcode}"
79
+ xml.StrtNm @debtor_address
80
+ xml.PstCd "#{@debtor_country}-#{@debtor_postcode}"
81
+ xml.TwnNm @debtor_town
82
+ xml.Ctry @debtor_country
83
+ }
84
+ }
85
+ }
86
+ end
87
+
88
+ builder.doc
89
+ end
90
+
91
+ # Adds all the payments specified in the parameters to the payload.
92
+ def add_payments(root_e)
93
+ @payments.each do |payment|
94
+ root_e.at(
95
+ '/xmlns:Document/xmlns:pain.001.001.02',
96
+ 'xmlns' => 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.02'
97
+ ).add_child(payment.to_node)
98
+ end
99
+
100
+ root_e
101
+ end
102
+
103
+ def load_schema
104
+ schemas_path = File.expand_path('../../../lib/sepa/xml_schemas', __FILE__)
105
+ xsd = Nokogiri::XML::Schema(File.read("#{schemas_path}/pain.001.001.02.xsd"))
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,97 @@
1
+ module Sepa
2
+ class Payment
3
+ def initialize(debtor, params)
4
+ @payment_info_id = params.fetch(:payment_info_id)
5
+ @execution_date = params.fetch(:execution_date)
6
+ @salary_or_pension = params[:salary_or_pension]
7
+
8
+ @debtor_name = debtor.fetch(:name)
9
+ @debtor_address = debtor.fetch(:address)
10
+ @debtor_country = debtor.fetch(:country)
11
+ @debtor_postcode = debtor.fetch(:postcode)
12
+ @debtor_town = debtor.fetch(:town)
13
+ @debtor_customer_id = debtor.fetch(:customer_id)
14
+ @debtor_iban = debtor.fetch(:iban)
15
+ @debtor_bic = debtor.fetch(:bic)
16
+
17
+ @transactions = params.fetch(:transactions)
18
+ end
19
+
20
+ # Returns a Nokogiri::XML::Node of the payment.
21
+ def to_node
22
+ node = build.doc.root
23
+ add_transactions(node)
24
+ end
25
+
26
+ # Returns the number of transactions in this payment.
27
+ def number_of_transactions
28
+ @transactions.count
29
+ end
30
+
31
+ private
32
+
33
+ # Builds the payment.
34
+ def build
35
+ Nokogiri::XML::Builder.new do |xml|
36
+ xml.PmtInf {
37
+ xml.PmtInfId @payment_info_id
38
+ xml.PmtMtd 'TRF'
39
+
40
+ xml.PmtTpInf {
41
+ xml.SvcLvl {
42
+ xml.Cd 'SEPA'
43
+ }
44
+
45
+ # Needs to be specified in case the payment contains salaris or
46
+ # pensions.
47
+ if @salary_or_pension
48
+ xml.CtgyPurp 'SALA'
49
+ end
50
+ }
51
+
52
+ xml.ReqdExctnDt @execution_date
53
+ xml.Dbtr {
54
+ xml.Nm @debtor_name
55
+ xml.PstlAdr {
56
+ xml.AdrLine @debtor_address
57
+ xml.AdrLine "#{@debtor_country}-#{@debtor_postcode} " \
58
+ "#{@debtor_town}"
59
+ xml.Ctry @debtor_country
60
+ }
61
+
62
+ xml.Id {
63
+ xml.OrgId {
64
+ if @debtor_customer_id
65
+ xml.BkPtyId @debtor_customer_id
66
+ end
67
+ }
68
+ }
69
+ }
70
+
71
+ xml.DbtrAcct {
72
+ xml.Id {
73
+ xml.IBAN @debtor_iban
74
+ }
75
+ }
76
+
77
+ xml.DbtrAgt {
78
+ xml.FinInstnId {
79
+ xml.BIC @debtor_bic
80
+ }
81
+ }
82
+
83
+ xml.ChrgBr 'SLEV'
84
+ }
85
+ end
86
+ end
87
+
88
+ # Adds the transactions specified in the params hash to the payment.
89
+ def add_transactions(node)
90
+ @transactions.each do |transaction|
91
+ node.add_child(transaction.to_node)
92
+ end
93
+
94
+ node
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,178 @@
1
+ module Sepa
2
+ class Transaction
3
+ def initialize(params)
4
+ @instruction_id = params[:instruction_id]
5
+ @end_to_end_id = params.fetch(:end_to_end_id)
6
+
7
+ # If the parameters contains an invoice bundle, the amount is taken from
8
+ # them.
9
+ if params[:invoice_bundle]
10
+ @amount = 0
11
+ params[:invoice_bundle].each do |invoice|
12
+ @amount += invoice.fetch(:amount).to_f
13
+ end
14
+ else
15
+ @amount = params.fetch(:amount)
16
+ end
17
+
18
+ @currency = params.fetch(:currency)
19
+ @bic = params.fetch(:bic)
20
+ @name = params.fetch(:name)
21
+ @address = params.fetch(:address)
22
+ @country = params.fetch(:country)
23
+ @postcode = params.fetch(:postcode)
24
+ @town = params.fetch(:town)
25
+ @iban = params.fetch(:iban)
26
+ @reference = params[:reference]
27
+ @message = params[:message]
28
+ @salary = params[:salary]
29
+ @pension = params[:pension]
30
+ @social_security_number = params[:social_security_number]
31
+ @invoice_bundle = params[:invoice_bundle]
32
+ end
33
+
34
+ # Returns a Nokogiri::XML::Node of the transaction.
35
+ def to_node
36
+ build.doc.root
37
+ end
38
+
39
+ private
40
+
41
+ # Builds the transaction
42
+ def build
43
+ Nokogiri::XML::Builder.new do |xml|
44
+ xml.CdtTrfTxInf {
45
+ xml.PmtId {
46
+ if @instruction_id
47
+ xml.InstrId @instruction_id
48
+ end
49
+
50
+ xml.EndToEndId @end_to_end_id
51
+ }
52
+
53
+ xml.Amt {
54
+ xml.InstdAmt(@amount, :Ccy => @currency)
55
+ }
56
+
57
+ xml.CdtrAgt {
58
+ xml.FinInstnId {
59
+ xml.BIC @bic
60
+ }
61
+ }
62
+
63
+ xml.Cdtr {
64
+ xml.Nm @name
65
+ xml.PstlAdr {
66
+ xml.AdrLine @address
67
+ xml.AdrLine("#{@country}-#{@postcode} " \
68
+ "#{@town}")
69
+ xml.StrtNm @address
70
+ xml.PstCd "#{@country}-#{@postcode}"
71
+ xml.TwnNm @town
72
+ xml.Ctry @country
73
+ }
74
+
75
+ # Social security number needs to be added in case the transaction
76
+ # contains a salary.
77
+ if @salary
78
+ xml.Id {
79
+ xml.PrvtId {
80
+ xml.SclSctyNb @social_security_number
81
+ }
82
+ }
83
+ end
84
+ }
85
+
86
+ xml.CdtrAcct {
87
+ xml.Id {
88
+ xml.IBAN @iban
89
+ }
90
+ }
91
+
92
+ # If the transaction contains a pension, this element needs to be
93
+ # specified.
94
+ if @pension
95
+ xml.Purp {
96
+ xml.Cd 'PENS'
97
+ }
98
+ end
99
+
100
+ xml.RmtInf {
101
+
102
+ # In case this transaction contains an invoice bundle, a Strd
103
+ # element is added for each invoice either with an invoice number
104
+ # or a reference.
105
+ if @invoice_bundle
106
+ message = ''
107
+ @invoice_bundle.each do |invoice|
108
+
109
+ if invoice[:amount].to_f < 0
110
+ amount = "#{invoice[:amount].to_f.abs}-"
111
+ else
112
+ amount = invoice[:amount]
113
+ end
114
+
115
+ if invoice[:reference]
116
+ message += "RFS/#{invoice[:reference]}/" \
117
+ "#{invoice[:currency]}#{amount}/"
118
+ elsif invoice[:invoice_number]
119
+ message += "#{invoice[:type]}/#{invoice[:invoice_number]}/" \
120
+ "#{invoice[:currency]}#{amount}/"
121
+ end
122
+ end
123
+
124
+ xml.Ustrd message
125
+
126
+ @invoice_bundle.each do |invoice|
127
+ xml.Strd {
128
+ xml.RfrdDocInf {
129
+ xml.RfrdDocTp {
130
+ xml.Cd invoice.fetch(:type)
131
+ }
132
+
133
+ if invoice[:invoice_number]
134
+ xml.RfrdDocNb invoice[:invoice_number]
135
+ end
136
+ }
137
+ xml.RfrdDocAmt {
138
+ if invoice.fetch(:amount).to_f > 0
139
+ xml.RmtdAmt(invoice[:amount],
140
+ :Ccy => invoice[:currency])
141
+ else
142
+ xml.CdtNoteAmt(invoice[:amount].to_f.abs,
143
+ :Ccy => invoice[:currency])
144
+ end
145
+ }
146
+
147
+ if invoice[:reference]
148
+ xml.CdtrRefInf {
149
+ xml.CdtrRefTp {
150
+ xml.Cd 'SCOR'
151
+ }
152
+
153
+ xml.CdtrRef invoice[:reference]
154
+ }
155
+ end
156
+ }
157
+ end
158
+
159
+ elsif @reference
160
+ xml.Strd {
161
+ xml.CdtrRefInf {
162
+ xml.CdtrRefTp {
163
+ xml.Cd 'SCOR'
164
+ }
165
+
166
+ xml.CdtrRef @reference
167
+ }
168
+ }
169
+
170
+ else
171
+ xml.Ustrd @message
172
+ end
173
+ }
174
+ }
175
+ end
176
+ end
177
+ end
178
+ end
data/lib/sepa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sepa
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end