ruby_fints 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|