ruby_fints 0.0.2
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/.gitignore +58 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +71 -0
- data/Rakefile +10 -0
- data/lib/fints/client.rb +144 -0
- data/lib/fints/connection_error.rb +3 -0
- data/lib/fints/dialog.rb +104 -0
- data/lib/fints/helper.rb +17 -0
- data/lib/fints/https_connection.rb +20 -0
- data/lib/fints/message.rb +67 -0
- data/lib/fints/mt535_miniparser.rb +107 -0
- data/lib/fints/pin_tan_client.rb +22 -0
- data/lib/fints/response.rb +160 -0
- data/lib/fints/segment/base_segment.rb +30 -0
- data/lib/fints/segment/hkend.rb +22 -0
- data/lib/fints/segment/hkidn.rb +22 -0
- data/lib/fints/segment/hkkaz.rb +31 -0
- data/lib/fints/segment/hkspa.rb +26 -0
- data/lib/fints/segment/hksyn.rb +26 -0
- data/lib/fints/segment/hkvvb.rb +26 -0
- data/lib/fints/segment/hkwpd.rb +30 -0
- data/lib/fints/segment/hnhbk.rb +27 -0
- data/lib/fints/segment/hnhbs.rb +22 -0
- data/lib/fints/segment/hnsha.rb +27 -0
- data/lib/fints/segment/hnshk.rb +38 -0
- data/lib/fints/segment/hnvsd.rb +30 -0
- data/lib/fints/segment/hnvsk.rb +34 -0
- data/lib/fints/segment_not_found_error.rb +3 -0
- data/lib/fints/version.rb +4 -0
- data/lib/ruby_fints.rb +29 -0
- data/ruby_fints.gemspec +27 -0
- data/test/fixtures/bpd-allowedgv.txt +1 -0
- data/test/pin_tan_client_test.rb +24 -0
- data/test/ruby_fints_test.rb +7 -0
- data/test/segment_test.rb +90 -0
- data/test/test_helper.rb +10 -0
- metadata +196 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKIDN (Identifikation)
|
4
|
+
# Section C.3.1.2
|
5
|
+
class HKIDN < BaseSegment
|
6
|
+
def initialize(segment_number, blz, username, system_id=0, customerid=1)
|
7
|
+
data = ["#{country_code}:#{blz}", Helper.fints_escape(username), system_id, customerid]
|
8
|
+
super(segment_number, data)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def type
|
14
|
+
'HKIDN'
|
15
|
+
end
|
16
|
+
|
17
|
+
def version
|
18
|
+
2
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKKAZ (Kontoumsätze)
|
4
|
+
# Refs: http://www.hbci-zka.de/dokumente/spezifikation_deutsch/fintsv3/FinTS_3.0_Messages_Geschaeftsvorfaelle_2015-08-07_final_version.pdf
|
5
|
+
# Section C.2.1.1.1.2
|
6
|
+
class HKKAZ < BaseSegment
|
7
|
+
def initialize(segno, version, account, date_start, date_end, touchdown)
|
8
|
+
@version = version
|
9
|
+
data = [
|
10
|
+
account,
|
11
|
+
'N',
|
12
|
+
date_start.strftime('%Y%m%d'),
|
13
|
+
date_end.strftime('%Y%m%d'),
|
14
|
+
'',
|
15
|
+
touchdown ? Helper.fints_escape(touchdown) : ''
|
16
|
+
]
|
17
|
+
super(segno, data)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def type
|
23
|
+
'HKKAZ'
|
24
|
+
end
|
25
|
+
|
26
|
+
def version
|
27
|
+
@version
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKSPA (SEPA-Kontoverbindung anfordern)
|
4
|
+
# Section C.10.1.3
|
5
|
+
class HKSPA < BaseSegment
|
6
|
+
def initialize(segno, accno, subaccfeature, blz)
|
7
|
+
data = if accno.nil?
|
8
|
+
['']
|
9
|
+
else
|
10
|
+
[[accno, subaccfeature, country_code, blz].join(':')]
|
11
|
+
end
|
12
|
+
super(segno, data)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def type
|
18
|
+
'HKSPA'
|
19
|
+
end
|
20
|
+
|
21
|
+
def version
|
22
|
+
1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKSYN (Synchronisation)
|
4
|
+
# Section C.8.1.2
|
5
|
+
class HKSYN < BaseSegment
|
6
|
+
SYNC_MODE_NEW_CUSTOMER_ID = 0
|
7
|
+
SYNC_MODE_LAST_MSG_NUMBER = 1
|
8
|
+
SYNC_MODE_SIGNATURE_ID = 2
|
9
|
+
|
10
|
+
def initialize(segment_no, mode: SYNC_MODE_NEW_CUSTOMER_ID)
|
11
|
+
data = [mode]
|
12
|
+
super(segment_no, data)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def type
|
18
|
+
'HKSYN'
|
19
|
+
end
|
20
|
+
|
21
|
+
def version
|
22
|
+
3
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKVVB (Verarbeitungsvorbereitung)
|
4
|
+
# Section C.3.1.3
|
5
|
+
class HKVVB < BaseSegment
|
6
|
+
LANG_DE = 1
|
7
|
+
LANG_EN = 2
|
8
|
+
LANG_FR = 3
|
9
|
+
|
10
|
+
def initialize(segment_no, lang: LANG_DE)
|
11
|
+
data = [0, 0, lang, Helper.fints_escape(FinTS::GEM_NAME), Helper.fints_escape(FinTS::VERSION)]
|
12
|
+
super(segment_no, data)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def type
|
18
|
+
'HKVVB'
|
19
|
+
end
|
20
|
+
|
21
|
+
def version
|
22
|
+
3
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HKWPD (Depotaufstellung anfordern)
|
4
|
+
# Section C.4.3.1
|
5
|
+
# Example: HKPWD:3:7+23456::280:10020030+USD+2'
|
6
|
+
class HKWPD < BaseSegment
|
7
|
+
def initialize(segno, version, account)
|
8
|
+
# The spec allows the specification of currency and
|
9
|
+
# quality of valuations, as shown in the docstring above.
|
10
|
+
# However, both 1822direkt and CortalConsors reject the
|
11
|
+
# call if these two are present, claiming an invalid input.
|
12
|
+
# 'EUR' # Währung der Depotaufstellung"
|
13
|
+
# 2 # Kursqualität
|
14
|
+
data = [account]
|
15
|
+
super(segno, data)
|
16
|
+
@version = version
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def type
|
22
|
+
'HKPWD'
|
23
|
+
end
|
24
|
+
|
25
|
+
def version
|
26
|
+
@version
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNHBK (Nachrichtenkopf)
|
4
|
+
# Section B.5.2
|
5
|
+
class HNHBK < BaseSegment
|
6
|
+
HEADER_LENGTH = 29
|
7
|
+
|
8
|
+
def initialize(msglen, dialog_id, msg_no)
|
9
|
+
if msglen.to_s.length != 12
|
10
|
+
msglen = (msglen.to_i + HEADER_LENGTH + dialog_id.to_s.length + msg_no.to_s.length).to_s.rjust(12, '0')
|
11
|
+
end
|
12
|
+
data = [msglen, 300, dialog_id, msg_no]
|
13
|
+
super(1, data)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def type
|
19
|
+
'HNHBK'
|
20
|
+
end
|
21
|
+
|
22
|
+
def version
|
23
|
+
3
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNHBS (Nachrichtenabschluss)
|
4
|
+
# Section B.5.3
|
5
|
+
class HNHBS < BaseSegment
|
6
|
+
def initialize(segno, msgno)
|
7
|
+
data = [msgno.to_s]
|
8
|
+
super(segno, data)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def type
|
14
|
+
'HNHBS'
|
15
|
+
end
|
16
|
+
|
17
|
+
def version
|
18
|
+
1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNSHA (Signaturabschluss)
|
4
|
+
# Section B.5.2
|
5
|
+
class HNSHA < BaseSegment
|
6
|
+
SECURITY_FUNC = 999
|
7
|
+
SECURITY_BOUNDARY = 1 # SHM
|
8
|
+
SECURITY_SUPPLIER_ROLE = 1 # ISS
|
9
|
+
PINTAN_VERSION = 1 # 1-step
|
10
|
+
|
11
|
+
def initialize(segno, secref, pin)
|
12
|
+
data = [secref, '', Helper.fints_escape(pin)]
|
13
|
+
super(segno, data)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def type
|
19
|
+
'HNSHA'
|
20
|
+
end
|
21
|
+
|
22
|
+
def version
|
23
|
+
2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNSHK (Signaturkopf)
|
4
|
+
# Section B.5.1
|
5
|
+
class HNSHK < BaseSegment
|
6
|
+
SECURITY_FUNC = 999
|
7
|
+
SECURITY_BOUNDARY = 1 # SHM
|
8
|
+
SECURITY_SUPPLIER_ROLE = 1 # ISS
|
9
|
+
|
10
|
+
def initialize(segno, secref, blz, username, system_id, profile_version, security_function=SECURITY_FUNC)
|
11
|
+
data = [
|
12
|
+
['PIN', profile_version.to_s].join(':'),
|
13
|
+
security_function,
|
14
|
+
secref,
|
15
|
+
SECURITY_BOUNDARY,
|
16
|
+
SECURITY_SUPPLIER_ROLE,
|
17
|
+
['1', '', system_id.to_s].join(':'),
|
18
|
+
1,
|
19
|
+
['1', Time.now.strftime('%Y%m%d'), Time.now.strftime('%H%M%S')].join(':'),
|
20
|
+
['1', '999', '1'].join(':'), # Negotiate hash algorithm
|
21
|
+
['6', '10', '16'].join(':'), # RSA mode
|
22
|
+
[country_code.to_s, blz, Helper.fints_escape(username), 'S', '0', '0'].join(':')
|
23
|
+
]
|
24
|
+
super(segno, data)
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def type
|
30
|
+
'HNSHK'
|
31
|
+
end
|
32
|
+
|
33
|
+
def version
|
34
|
+
4
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNVSD (Verschlüsselte Daten)
|
4
|
+
# Section B.5.4
|
5
|
+
class HNVSD < BaseSegment
|
6
|
+
attr_reader :encoded_data
|
7
|
+
|
8
|
+
def initialize(segno, encoded_data)
|
9
|
+
@encoded_data = encoded_data
|
10
|
+
data = ["@#{encoded_data.length}@#{encoded_data}"]
|
11
|
+
super(segno, data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_data(encoded_data)
|
15
|
+
@encoded_data = encoded_data
|
16
|
+
@data = ["@#{encoded_data.length}@#{encoded_data}"]
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def type
|
22
|
+
'HNVSD'
|
23
|
+
end
|
24
|
+
|
25
|
+
def version
|
26
|
+
1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module FinTS
|
2
|
+
module Segment
|
3
|
+
# HNVSK (Verschlüsselungskopf)
|
4
|
+
# Section B.5.3
|
5
|
+
class HNVSK < BaseSegment
|
6
|
+
COMPRESSION_NONE = 0
|
7
|
+
SECURITY_SUPPLIER_ROLE = 1 # ISS
|
8
|
+
|
9
|
+
def initialize(segno, blz, username, system_id, profile_version)
|
10
|
+
data = [
|
11
|
+
['PIN', profile_version.to_s].join(':'),
|
12
|
+
998,
|
13
|
+
SECURITY_SUPPLIER_ROLE,
|
14
|
+
['1', '', system_id.to_s].join(':'),
|
15
|
+
['1', Time.now.strftime('%Y%m%d'), Time.now.strftime('%H%M%S')].join(':'),
|
16
|
+
['2', '2', '13', '@8@00000000', '5', '1'].join(':'),
|
17
|
+
[country_code.to_s, blz, Helper.fints_escape(username), 'S', '0', '0'].join(':'),
|
18
|
+
COMPRESSION_NONE
|
19
|
+
]
|
20
|
+
super(segno, data)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def type
|
26
|
+
'HNVSK'
|
27
|
+
end
|
28
|
+
|
29
|
+
def version
|
30
|
+
3
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/ruby_fints.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'httparty'
|
3
|
+
require 'cmxl'
|
4
|
+
require 'logger'
|
5
|
+
require_relative 'fints/client'
|
6
|
+
require_relative 'fints/connection_error'
|
7
|
+
require_relative 'fints/dialog'
|
8
|
+
require_relative 'fints/helper'
|
9
|
+
require_relative 'fints/https_connection'
|
10
|
+
require_relative 'fints/message'
|
11
|
+
require_relative 'fints/mt535_miniparser'
|
12
|
+
require_relative 'fints/pin_tan_client'
|
13
|
+
require_relative 'fints/response'
|
14
|
+
require_relative 'fints/segment_not_found_error'
|
15
|
+
require_relative 'fints/version'
|
16
|
+
require_relative 'fints/segment/base_segment'
|
17
|
+
require_relative 'fints/segment/hkend'
|
18
|
+
require_relative 'fints/segment/hkidn'
|
19
|
+
require_relative 'fints/segment/hkkaz'
|
20
|
+
require_relative 'fints/segment/hkspa'
|
21
|
+
require_relative 'fints/segment/hksyn'
|
22
|
+
require_relative 'fints/segment/hkvvb'
|
23
|
+
require_relative 'fints/segment/hkwpd'
|
24
|
+
require_relative 'fints/segment/hnhbk'
|
25
|
+
require_relative 'fints/segment/hnhbs'
|
26
|
+
require_relative 'fints/segment/hnsha'
|
27
|
+
require_relative 'fints/segment/hnshk'
|
28
|
+
require_relative 'fints/segment/hnvsd'
|
29
|
+
require_relative 'fints/segment/hnvsk'
|
data/ruby_fints.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'fints/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'ruby_fints'
|
7
|
+
s.version = FinTS::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Marvin Killing']
|
10
|
+
s.email = ['marvin@playtestcloud.com']
|
11
|
+
s.homepage = ''
|
12
|
+
s.summary = %q{Basic FinTS 3.0 implementation in Ruby}
|
13
|
+
s.description = %q{FinTS (formerly known as HBCI) is a protocol to programmatically interface with German banks. This gem is a translation of https://github.com/raphaelm/python-fints into Ruby.}
|
14
|
+
|
15
|
+
s.add_runtime_dependency 'cmxl', '~> 0.2'
|
16
|
+
s.add_runtime_dependency 'httparty', '~> 0.10'
|
17
|
+
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'minitest'
|
19
|
+
s.add_development_dependency 'webmock'
|
20
|
+
s.add_development_dependency 'simplecov'
|
21
|
+
s.add_development_dependency 'delorean'
|
22
|
+
s.add_development_dependency 'mocha'
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
HNHBK:1:3+000000001881+220+1653639025+1+1653639025:1'HNSHK:2:3+900+2+1+1+2::1241978588+0+1:20120316:210842+1:999:1+6:10:16+280::1:S:0:0'HIRMG:3:2+0010::Nachricht entgegengenommen'HIRMS:4:2:4+3920::Auftraege werden nur gemaess Zwei-Schritt-Verfahren (ITAN) angenommen.:900'HIBPA:5:2:4+1+280:33020000+akf Bank Wuppertal+23+1+220'HISHV:6:2:4+N+RDH:1'HIKAZS:7:5:4+1+1+60:N:N'HIUEBS:8:4:4+1+1+14:51:53:54:67:69'HISUBS:9:5:4+1+1+9999:14:51:53:54'HISLAS:10:5:4+1+1+9999:14:04:05'HISALS:11:5:4+1+1'HIAOMS:12:1:4+1+1+J:756;24;23;24;64;15000,;EUR:208;24;23;24;64;15000,;EUR:246;24;23;24;64;15000,;EUR:440;24;23;24;64;15000,;EUR:616;24;23;24;64;15000,;EUR:752;24;23;24;64;15000,;EUR:705;24;23;24;64;15000,;EUR:203;24;23;24;64;15000,;EUR:348;24;23;24;64;15000,;EUR:056;24;23;24;64;15000,;EUR:372;24;23;24;64;15000,;EUR:380;24;23;24;64;15000,;EUR:442;24;23;24;64;15000,;EUR:528;24;23;24;64;15000,;EUR:040;24;23;24;64;15000,;EUR:620;24;23;24;64;15000,;EUR:724;24;23;24;64;15000,;EUR:826;24;23;24;64;15000,;EUR:474;24;23;24;64;15000,;EUR:352;24;23;24;64;15000,;EUR:300;24;23;24;64;15000,;EUR:250;24;23;24;64;15000,;EUR:638;24;23;24;64;15000,;EUR:254;24;23;24;64;15000,;EUR:292;24;23;24;64;15000,;EUR:312;24;23;24;64;15000,;EUR:470;24;23;24;64;15000,;EUR:196;24;23;24;64;15000,;EUR:233;24;23;24;64;15000,;EUR:428;24;23;24;64;15000,;EUR:276;24;23;24;64;15000,;EUR:578;24;23;24;64;15000,;EUR'HILASS:13:4:4+1+1+14:04:05'HIPINS:14:1:4+1+1+1+5:6:6:BENUTZER:KUNDEN:HKUEB:J:HKSUB:J:HKKAZ:N:HKSLA:J:HKTUE:J:HKTUA:J:HKTUB:N:HKTUL:J:HKAOM:J:HKTAN:N'HITANS:15:2:4+1+1+1+N:J:1:900:1:ITAN:ITAN:6:1:TAN:6:1:N:1:0:N:N:N'HIUPA:16:2:4+XXXX+0+0'HIUPD:17:4:4+10023340::280:33020000+XXXX+EUR+Jan Kahl++Jan Kahl++HKKAZ:1+HKUEB:1+HKSUB:1+HKSAL:1+HKTAN:1'HISYN:18:3:5+1241978588'HNSHA:19:1+2++XXXXX'HNHBS:20:1+1'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RubyFintsTest < Minitest::Test
|
4
|
+
|
5
|
+
# this test tries to go as far as it can to retrieve a list of sepa accounts.
|
6
|
+
# currently we don‘t have the fixtures to make this fully test the entire method.
|
7
|
+
def test_it_raises_when_it_gets_a_bad_response
|
8
|
+
Delorean.time_travel_to(Time.new(2017, 4, 20, 17, 17)) do
|
9
|
+
# we have to mock random number generation
|
10
|
+
Kernel.stubs(:rand).with(1000000..9999999).returns(9999999)
|
11
|
+
|
12
|
+
response = File.read(File.join(File.dirname(__FILE__), 'fixtures', 'bpd-allowedgv.txt'))
|
13
|
+
stub_request(:post, "https://banking-bb6.de/fints30")
|
14
|
+
.to_return(status: 200, body: Base64.encode64(response), headers: {})
|
15
|
+
|
16
|
+
f = FinTS::PinTanClient.new('788000111', 'my?user', 'mypw', 'https://banking-bb6.de/fints30')
|
17
|
+
FinTS::Client.logger.level = Logger::ERROR
|
18
|
+
|
19
|
+
assert_raises FinTS::SegmentNotFoundError do
|
20
|
+
f.get_sepa_accounts
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|