iso8583 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -0
- data/CHANGELOG +10 -0
- data/README +5 -27
- data/Rakefile +56 -60
- data/lib/8583.rb +12 -0
- data/lib/8583/berlin.rb +82 -0
- data/lib/8583/bitmap.rb +117 -0
- data/lib/8583/codec.rb +189 -0
- data/lib/8583/exception.rb +4 -0
- data/lib/8583/field.rb +90 -0
- data/lib/8583/fields.rb +148 -0
- data/lib/8583/message.rb +419 -0
- data/lib/8583/util.rb +94 -0
- data/lib/iso8583.rb +2 -6
- data/test/BitmapTests.rb +64 -52
- data/test/message_test.rb +50 -11
- data/test/test_codec.rb +16 -9
- data/test/test_fields.rb +37 -13
- data/test/test_seperate_msg.rb +22 -0
- data/test/test_util.rb +32 -0
- metadata +25 -14
- data/lib/berlin.rb +0 -79
- data/lib/bitmap.rb +0 -117
- data/lib/codec.rb +0 -173
- data/lib/exception.rb +0 -5
- data/lib/field.rb +0 -178
- data/lib/fields.rb +0 -130
- data/lib/message.rb +0 -416
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'lib/8583'
|
2
|
+
require 'lib/8583/berlin'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
include ISO8583
|
6
|
+
|
7
|
+
class SeperateTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
def test_test_separate_messages
|
12
|
+
mes1=BerlinMessage.new
|
13
|
+
mes1[2]="1234567890"
|
14
|
+
assert_equal(mes1[2], "1234567890")
|
15
|
+
mes2=BerlinMessage.new
|
16
|
+
mes2[2]="0987654321"
|
17
|
+
assert_equal(mes2[2], "0987654321")
|
18
|
+
# test that the original value of field 2 in mes1 hasn't changed
|
19
|
+
assert_equal(mes1[2], "1234567890") # this will fail, as the field 2 in mes1 has changed value too!!
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/test_util.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'lib/iso8583'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
include ISO8583
|
5
|
+
|
6
|
+
class UtilTest < Test::Unit::TestCase
|
7
|
+
def test_hex2b
|
8
|
+
assert_equal "\xab\xcd\x12", hex2b("abcd12")
|
9
|
+
assert_equal "\xab\xcd\x12", hex2b("a b c d 1 2")
|
10
|
+
assert_equal "\xab\xcd\x12", hex2b("ABCD12")
|
11
|
+
assert_raise(ISO8583Exception){
|
12
|
+
# non hex
|
13
|
+
hex2b("ABCDEFGH")
|
14
|
+
}
|
15
|
+
assert_raise(ISO8583Exception){
|
16
|
+
# odd num digits
|
17
|
+
hex2b("ABCDEF0")
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_b2hex
|
22
|
+
assert_equal "abcd12", b2hex("\xab\xcd\x12")
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_ebcdic2ascii
|
26
|
+
assert_equal "0123456789", ebcdic2ascii("\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9")
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_ascii2ebcdic
|
30
|
+
assert_equal "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9", ascii2ebcdic("0123456789")
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,25 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iso8583
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Becker
|
8
|
+
- Slava Kravchenko
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2010-09-29 00:00:00 +02:00
|
13
14
|
default_executable:
|
14
15
|
dependencies: []
|
15
16
|
|
16
|
-
description:
|
17
|
-
|
17
|
+
description: |
|
18
|
+
Ruby implementation of ISO 8583 financial messaging
|
19
|
+
|
20
|
+
email:
|
21
|
+
- tim.becker@kuriositaet.de
|
22
|
+
- cordawyn@gmail.com
|
18
23
|
executables: []
|
19
24
|
|
20
25
|
extensions: []
|
@@ -22,14 +27,16 @@ extensions: []
|
|
22
27
|
extra_rdoc_files: []
|
23
28
|
|
24
29
|
files:
|
25
|
-
- lib/berlin.rb
|
26
|
-
- lib/bitmap.rb
|
27
|
-
- lib/codec.rb
|
28
|
-
- lib/exception.rb
|
29
|
-
- lib/field.rb
|
30
|
-
- lib/fields.rb
|
30
|
+
- lib/8583/berlin.rb
|
31
|
+
- lib/8583/bitmap.rb
|
32
|
+
- lib/8583/codec.rb
|
33
|
+
- lib/8583/exception.rb
|
34
|
+
- lib/8583/field.rb
|
35
|
+
- lib/8583/fields.rb
|
36
|
+
- lib/8583/message.rb
|
37
|
+
- lib/8583/util.rb
|
38
|
+
- lib/8583.rb
|
31
39
|
- lib/iso8583.rb
|
32
|
-
- lib/message.rb
|
33
40
|
- AUTHORS
|
34
41
|
- CHANGELOG
|
35
42
|
- LICENSE
|
@@ -40,8 +47,12 @@ files:
|
|
40
47
|
- test/message_test.rb
|
41
48
|
- test/test_codec.rb
|
42
49
|
- test/test_fields.rb
|
50
|
+
- test/test_seperate_msg.rb
|
51
|
+
- test/test_util.rb
|
43
52
|
has_rdoc: true
|
44
|
-
homepage: http://
|
53
|
+
homepage: http://github.com/a2800276/8583/
|
54
|
+
licenses: []
|
55
|
+
|
45
56
|
post_install_message:
|
46
57
|
rdoc_options: []
|
47
58
|
|
@@ -62,9 +73,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
73
|
requirements:
|
63
74
|
- none
|
64
75
|
rubyforge_project: iso8583
|
65
|
-
rubygems_version: 1.3.
|
76
|
+
rubygems_version: 1.3.5
|
66
77
|
signing_key:
|
67
|
-
specification_version:
|
78
|
+
specification_version: 3
|
68
79
|
summary: "iso8583: Ruby implementation of ISO 8583 financial messaging"
|
69
80
|
test_files: []
|
70
81
|
|
data/lib/berlin.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
# Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
|
2
|
-
# MIT License, for details, see the LICENSE file accompaning
|
3
|
-
# this distribution
|
4
|
-
|
5
|
-
require 'lib/iso8583'
|
6
|
-
|
7
|
-
|
8
|
-
# Example of a protocol specification based on:
|
9
|
-
# http://www.berlin-group.org/documents/BG_Authorisation_3.0.pdf
|
10
|
-
# The Berlin Groups Authorisation Interface specification.
|
11
|
-
# No gurantees are made that this is an accurate implemenation.
|
12
|
-
# It currently serves as an example only.
|
13
|
-
|
14
|
-
|
15
|
-
class BerlinMessage < Message
|
16
|
-
mti_format N, :length=>4
|
17
|
-
mti 1100, "Authorization Request Acquirer Gateway"
|
18
|
-
mti 1110, "Authorization Request Response Issuer Gateway"
|
19
|
-
mti 1420, "Reversal Advice Acquirer Gateway"
|
20
|
-
mti 1421, "Reversal Advice Repeat Acquirer Gateway"
|
21
|
-
mti 1430, "Reversal Advice Response Issuer Gateway"
|
22
|
-
mti 1804, "Network Management Request Acquirer Gateway or Issuer Gateway"
|
23
|
-
mti 1814, "Network Management Request Response Issuer Gateway or Acquirer Gateway"
|
24
|
-
|
25
|
-
bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
|
26
|
-
bmp 3, "Processing Code", N, :length => 6
|
27
|
-
bmp 4, "Amount (Transaction)", N, :length => 12
|
28
|
-
bmp 6, "Amount, Cardholder Billing" , N, :length => 12
|
29
|
-
bmp 7, "Date and Time, Transmission" , MMDDhhmmss
|
30
|
-
bmp 10, "Conversion Rate, Cardholder Billing", N, :length => 8
|
31
|
-
bmp 11, "System Trace Audit Number (STAN)", N, :length => 6
|
32
|
-
bmp 12, "Date and Time, Local Transaction", YYMMDDhhmmss
|
33
|
-
bmp 14, "Date, Expiration", YYMM
|
34
|
-
bmp 22, "POS Data Code", AN, :length => 12
|
35
|
-
bmp 23, "Card Sequence Number", N, :length => 3
|
36
|
-
bmp 24, "Function Code", N, :length => 3
|
37
|
-
bmp 25, "Message Reason Code", N, :length => 4
|
38
|
-
bmp 26, "Card Acceptor Business Code", N, :length => 4
|
39
|
-
bmp 30, "Amounts, Original", N, :length => 24
|
40
|
-
bmp 32, "Acquiring Institution Identification Code", LLVAR_N, :max => 11
|
41
|
-
bmp 35, "Track 2 Data", LLVAR_Z, :max => 37
|
42
|
-
bmp 37, "Retrieval Reference Number", ANP, :length => 12
|
43
|
-
bmp 38, "Approval Code", ANP, :length => 6
|
44
|
-
bmp 39, "Action Code", N, :length => 3
|
45
|
-
bmp 41, "Card Acceptor Terminal Identification", ANS, :length => 8
|
46
|
-
bmp 42, "Card Acceptor Identification Code", ANS, :length => 15
|
47
|
-
bmp 43, "Card Acceptor Name/Location", LLVAR_ANS, :max => 56
|
48
|
-
bmp 49, "Currency Code, Transaction", N, :length => 3
|
49
|
-
bmp 51, "Currency Code, Cardholder Billing", N, :length => 3
|
50
|
-
bmp 52, "Personal Identification Number (PIN) Data", B, :length => 8
|
51
|
-
bmp 53, "Security Related Control Information", LLVAR_B, :max => 48
|
52
|
-
bmp 54, "Amounts, Additional", LLLVAR_ANS,:max => 40
|
53
|
-
|
54
|
-
bmp 55, "Integrated Circuit Card (ICC) System Related Data", LLLVAR_B, :max => 255
|
55
|
-
bmp 56, "Original Data Elements", LLVAR_N, :max => 35
|
56
|
-
bmp 58, "Authorizing Agent Institution Identification Code", LLVAR_N, :max => 11
|
57
|
-
bmp 59, "Additional Data - Private", LLLVAR_ANS, :max => 67
|
58
|
-
bmp 64, "Message Authentication Code (MAC) Field", B, :length => 8
|
59
|
-
|
60
|
-
bmp_alias 2, :pan
|
61
|
-
bmp_alias 3, :proc_code
|
62
|
-
bmp_alias 4, :amount
|
63
|
-
bmp_alias 12, :exp_date
|
64
|
-
end
|
65
|
-
|
66
|
-
if __FILE__==$0
|
67
|
-
mes = BerlinMessage.new
|
68
|
-
mes.mti = 1110
|
69
|
-
mes[2] = 474747474747
|
70
|
-
mes["Processing Code"] = "123456"
|
71
|
-
|
72
|
-
pan = mes["Primary Account Number (PAN)"]
|
73
|
-
#mes.pan = 47474747474747
|
74
|
-
|
75
|
-
#puts mes.pan
|
76
|
-
puts mes.to_b
|
77
|
-
puts mes.to_s
|
78
|
-
#mes2 = BerlinMessage.parse input
|
79
|
-
end
|
data/lib/bitmap.rb
DELETED
@@ -1,117 +0,0 @@
|
|
1
|
-
# Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
|
2
|
-
# MIT License, for details, see the LICENSE file accompaning
|
3
|
-
# this distribution
|
4
|
-
|
5
|
-
# This class constructs an object for handling bitmaps
|
6
|
-
# with which ISO8583 messages typically begin.
|
7
|
-
# Bitmaps are either 8 or 16 bytes long, an extended length
|
8
|
-
# bitmap is indicated by the first bit being set.
|
9
|
-
# In all likelyhood, you won't be using this class much, it's used
|
10
|
-
# transparently by the Message class.
|
11
|
-
class Bitmap
|
12
|
-
|
13
|
-
|
14
|
-
# create a new Bitmap object. In case an iso message
|
15
|
-
# is passed in, that messages bitmap will be parsed. If
|
16
|
-
# not, this initializes and empty bitmap.
|
17
|
-
|
18
|
-
def initialize message=nil
|
19
|
-
@bmp = Array.new(128,false)
|
20
|
-
if !message
|
21
|
-
|
22
|
-
else
|
23
|
-
initialize_from_message message
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# yield once with the number of each set field.
|
28
|
-
def each #:yields: each bit set in the bitmap.
|
29
|
-
@bmp.each_with_index {|set,i| yield i+i if set}
|
30
|
-
end
|
31
|
-
|
32
|
-
# Returns whether the bit is set or not.
|
33
|
-
def [] i
|
34
|
-
@bmp[i-1]
|
35
|
-
end
|
36
|
-
|
37
|
-
# Set the bit to the indicated value. Only `true` sets the
|
38
|
-
# bit, any other value unsets it.
|
39
|
-
def []= i,value
|
40
|
-
if i > 128
|
41
|
-
raise ISO8583Exception.new("Bits > 128 are not permitted.")
|
42
|
-
elsif i < 2
|
43
|
-
raise ISO8583Exception.new("Bits < 2 are not permitted (continutation bit is set automatically)")
|
44
|
-
end
|
45
|
-
@bmp[i-1]=(value==true)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Sets bit #i
|
49
|
-
def set i
|
50
|
-
self[i]=true
|
51
|
-
end
|
52
|
-
|
53
|
-
# Unsets bit #i
|
54
|
-
def unset i
|
55
|
-
self[i]=false
|
56
|
-
end
|
57
|
-
|
58
|
-
# Generate the bytes representing this bitmap.
|
59
|
-
def to_bytes
|
60
|
-
arr=[self.to_s]
|
61
|
-
# tricky and ugly, setting bit[1] only when generating to_s...
|
62
|
-
count = self[1] ? 128 : 64
|
63
|
-
arr.pack("B#{count}")
|
64
|
-
end
|
65
|
-
alias_method :to_b, :to_bytes
|
66
|
-
|
67
|
-
# Generate a String representation of this bitmap in the form:
|
68
|
-
# 01001100110000011010110110010100100110011000001101011011001010
|
69
|
-
def to_s
|
70
|
-
#check whether any `high` bits are set
|
71
|
-
@bmp[0]=false
|
72
|
-
65.upto(128) { |i|
|
73
|
-
if self[i]
|
74
|
-
# if so, set continuation bit
|
75
|
-
@bmp[0]=true
|
76
|
-
break
|
77
|
-
end
|
78
|
-
}
|
79
|
-
str = ""
|
80
|
-
1.upto(self[1] ? 128 : 64) {|i|
|
81
|
-
str << (self[i] ? "1" : "0")
|
82
|
-
}
|
83
|
-
str
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
def initialize_from_message message
|
88
|
-
bmp = message.unpack("B64")[0]
|
89
|
-
if bmp[0,1] == "1"
|
90
|
-
bmp = message.unpack("B128")[0]
|
91
|
-
end
|
92
|
-
|
93
|
-
0.upto(bmp.length-1) do |i|
|
94
|
-
@bmp[i] = bmp[i,1]=="1"
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
class << self
|
101
|
-
# Parse the bytes in string and return the Bitmap and bytes remaining in `str`
|
102
|
-
# after the bitmap is taken away.
|
103
|
-
def parse str
|
104
|
-
bmp = Bitmap.new(str)
|
105
|
-
rest = bmp[1] ? str[16,str.length] : str[8,str.length]
|
106
|
-
return [bmp,rest]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
if __FILE__==$0
|
113
|
-
|
114
|
-
mp = ISO8583::Bitmap.new
|
115
|
-
20.step(128,7) {|i| mp.set(i)}
|
116
|
-
print mp.to_bytes
|
117
|
-
end
|
data/lib/codec.rb
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
# Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
|
2
|
-
# MIT License, for details, see the LICENSE file accompaning
|
3
|
-
# this distribution
|
4
|
-
|
5
|
-
require 'date'
|
6
|
-
|
7
|
-
# Codec provides functionality to encode and decode values, codecs are
|
8
|
-
# used internally by Field instances in order to do character conversions
|
9
|
-
# and checking for proper values.
|
10
|
-
# Although they are used internally, you will probably need to write
|
11
|
-
# your own Codec sooner or later. The codecs used by Field instances are
|
12
|
-
# typically instances of Codec, it may or may not be usefull to subclass
|
13
|
-
# Codec.
|
14
|
-
#
|
15
|
-
# Say, for example, a text field needs to be encoded in EBCDIC in the
|
16
|
-
# message, this is how a corresponding codec would be constructed:
|
17
|
-
#
|
18
|
-
# EBCDIC_Codec = Codec.new
|
19
|
-
# EBCDIC_Codec.encoder = lambda {|ascii_str|
|
20
|
-
# raise ISO8583Exception.new("String (#{ascii_str})not valid!") unless =~ /someregexp/
|
21
|
-
# ascii2ebcdic ascii_str # implementing ascii_str is left as an excercise
|
22
|
-
# }
|
23
|
-
# EBCDIC_Codec.decode = lambda {|ebcdic_str|
|
24
|
-
# # you may or may not want to raise exceptions at this point ....
|
25
|
-
# # strip removes any padding...
|
26
|
-
# ebcdic2ascii(ebcdic_str).strip
|
27
|
-
# }
|
28
|
-
#
|
29
|
-
# This instance of Codec would then be used be the corresponding Field
|
30
|
-
# encoder/decoder, which may look similar to this:
|
31
|
-
#
|
32
|
-
# EBCDIC = Field.new
|
33
|
-
# EBCDIC.codec = EBCDIC_Codec
|
34
|
-
# EBCDIC.padding = PADDING_LEFT_JUSTIFIED_SPACES
|
35
|
-
#
|
36
|
-
# Notice there is a bit of inconsistancy: the padding is added by the
|
37
|
-
# field, but removed by the codec. I would like to find a better
|
38
|
-
# solution to this...
|
39
|
-
#
|
40
|
-
# See also: Field, link:files/lib/fields_rb.html
|
41
|
-
#
|
42
|
-
# The following codecs are already implemented:
|
43
|
-
# [+ASCII_Number+] encodes either a Number or String representation of
|
44
|
-
# a number to the ASCII represenation of the number,
|
45
|
-
# decodes ASCII numerals to a number
|
46
|
-
# [+AN_Codec+] passes through ASCII string checking they conform to [A-Za-z0-9]
|
47
|
-
# during encoding, no validity check during decoding.
|
48
|
-
# [+ANP_Codec+] passes through ASCII string checking they conform to [A-Za-z0-9 ]
|
49
|
-
# during encoding, no validity check during decoding.
|
50
|
-
# [+ANS_Codec+] passes through ASCII string checking they conform to [\x20-\x7E]
|
51
|
-
# during encoding, no validity check during decoding.
|
52
|
-
# [+Null_Codec+] passes anything along untouched.
|
53
|
-
# [<tt>Track2</tt>] rudimentary check that string conforms to Track2
|
54
|
-
# [+MMDDhhmmssCodec+] encodes Time, Datetime or String to the described date format, checking
|
55
|
-
# that it is a valid date. Decodes to a DateTime instance, decoding and
|
56
|
-
# encoding perform validity checks!
|
57
|
-
# [+YYMMDDhhmmssCodec+] encodes Time, Datetime or String to the described date format, checking
|
58
|
-
# that it is a valid date. Decodes to a DateTime instance, decoding and
|
59
|
-
# encoding perform validity checks!
|
60
|
-
# [+YYMMCodec+] encodes Time, Datetime or String to the described date format (exp date),
|
61
|
-
# checking that it is a valid date. Decodes to a DateTime instance, decoding
|
62
|
-
# and encoding perform validity checks!
|
63
|
-
#
|
64
|
-
class Codec
|
65
|
-
attr_accessor :encoder
|
66
|
-
attr_accessor :decoder
|
67
|
-
|
68
|
-
def decode raw
|
69
|
-
return decoder.call(raw)
|
70
|
-
end
|
71
|
-
|
72
|
-
# length is either a fixnum or a lenth encoder.
|
73
|
-
def encode value
|
74
|
-
return encoder.call(value)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# ASCII_Number
|
79
|
-
ASCII_Number = Codec.new
|
80
|
-
ASCII_Number.encoder= lambda{|num|
|
81
|
-
enc = num.to_s
|
82
|
-
raise ISO8583Exception.new("Invalid value: #{enc} must be numeric!") unless enc =~ /^[0-9]*$/
|
83
|
-
enc
|
84
|
-
}
|
85
|
-
|
86
|
-
ASCII_Number.decoder= lambda{|raw|
|
87
|
-
raw.to_i
|
88
|
-
}
|
89
|
-
|
90
|
-
PASS_THROUGH_DECODER = lambda{|str|
|
91
|
-
str.strip # remove padding
|
92
|
-
}
|
93
|
-
|
94
|
-
AN_Codec = Codec.new
|
95
|
-
AN_Codec.encoder = lambda{|str|
|
96
|
-
raise ISO8583Exception.new("Invalid value: #{str} must be [A-Za-y0-9]") unless str =~ /^[A-Za-z0-9]*$/
|
97
|
-
str
|
98
|
-
}
|
99
|
-
AN_Codec.decoder = PASS_THROUGH_DECODER
|
100
|
-
|
101
|
-
ANP_Codec = Codec.new
|
102
|
-
ANP_Codec.encoder = lambda{|str|
|
103
|
-
raise ISO8583Exception.new("Invalid value: #{str} must be [A-Za-y0-9 ]") unless str =~ /^[A-Za-z0-9 ]*$/
|
104
|
-
str
|
105
|
-
}
|
106
|
-
ANP_Codec.decoder = PASS_THROUGH_DECODER
|
107
|
-
|
108
|
-
ANS_Codec = Codec.new
|
109
|
-
ANS_Codec.encoder = lambda{|str|
|
110
|
-
raise ISO8583Exception.new("Invalid value: #{str} must be [\x20-\x7E]") unless str =~ /^[\x20-\x7E]*$/
|
111
|
-
str
|
112
|
-
}
|
113
|
-
ANS_Codec.decoder = PASS_THROUGH_DECODER
|
114
|
-
|
115
|
-
Null_Codec = Codec.new
|
116
|
-
Null_Codec.encoder = lambda {|str|
|
117
|
-
str
|
118
|
-
}
|
119
|
-
Null_Codec.decoder = lambda {|str|
|
120
|
-
str.gsub(/\000*$/,'')
|
121
|
-
}
|
122
|
-
|
123
|
-
Track2 = Codec.new
|
124
|
-
Track2.encoder = lambda{|track2|
|
125
|
-
#SS | PAN | FS | Expiration Date | Service Code | Discretionary Data | ES | LRC
|
126
|
-
# SS = ;
|
127
|
-
# PAN = up to 19 digits (at least 9?)
|
128
|
-
# FS = '='
|
129
|
-
# Exp Date = YYMM
|
130
|
-
# SC: 3 digits or =
|
131
|
-
# ES = ?
|
132
|
-
# lrc : 1byte
|
133
|
-
raise ISO8583Exception.new("Invalid Track2 data: #{track2}") unless track2 =~ /^;*(\d{9,19})=(.*)\?.$/
|
134
|
-
track2
|
135
|
-
}
|
136
|
-
Track2.decoder = PASS_THROUGH_DECODER
|
137
|
-
|
138
|
-
def _date_codec(fmt)
|
139
|
-
c = Codec.new
|
140
|
-
c.encoder = lambda {|date|
|
141
|
-
enc = case date
|
142
|
-
when Time
|
143
|
-
when DateTime
|
144
|
-
date.strftime(fmt)
|
145
|
-
when String
|
146
|
-
begin
|
147
|
-
dt = DateTime.strptime date, fmt
|
148
|
-
dt.strftime(fmt)
|
149
|
-
rescue
|
150
|
-
raise ISO8583Exception.new("Invalid format encoding: #{date}, must be #{fmt}.")
|
151
|
-
end
|
152
|
-
else
|
153
|
-
raise ISO8583Exception.new("Don't know how to encode: #{date.class} to a time.")
|
154
|
-
end
|
155
|
-
return enc
|
156
|
-
}
|
157
|
-
c.decoder = lambda {|str|
|
158
|
-
dt = begin
|
159
|
-
DateTime.strptime str, fmt
|
160
|
-
rescue
|
161
|
-
raise ISO8583Exception.new("Invalid format decoding: #{str}, must be MMDDhhmmss.")
|
162
|
-
end
|
163
|
-
return dt
|
164
|
-
}
|
165
|
-
|
166
|
-
return c
|
167
|
-
end
|
168
|
-
|
169
|
-
MMDDhhmmssCodec = _date_codec("%m%d%H%M%S")
|
170
|
-
YYMMDDhhmmssCodec = _date_codec("%y%m%d%H%M%S")
|
171
|
-
YYMMCodec = _date_codec("%y%m")
|
172
|
-
|
173
|
-
|