Class: ISO8583::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/iso8583/message.rb

Overview

The class ‘Message` defines functionality to describe classes representing different type of messages, or message families. A message family consists of a number of possible message types that are allowed, and a way of naming and encoding the bitmaps allowed in the messages.

To create your own message, start by subclassing Message:

class MyMessage < Message
   (...)
end

the subtyped message should be told how the MTI is encoded:

class MyMessage < Message
   mti_format N, :length => 4
   (...)
end

‘N` above is an instance of Field which encodes numbers into their ASCII representations in a fixed length field. The option `length=>4` indicates the length of the fixed field.

Next, the allowed message types are specified:

class MyMessage < Message
   (...)
   mti 1100, "Authorization Request Acquirer Gateway"
   mti 1110, "Authorization Request Response Issuer Gateway"
   (...)
end

This basically defines to message types, 1100 and 1110 which may be accessed later either via their name or value:

mes = MyMessage.new 1100

or

mes = MyMessage.new "Authorization Request Acquirer Gateway"

or

mes = MyMessage.new
mes.mti = 1110 # or Auth. Req. Acq. Gateway ...

Finally the allowed bitmaps, their names and the encoding rules are specified:

class MyMessage < Message
   (...)
   bmp  2, "Primary Account Number (PAN)",               LLVAR_N,   :max    => 19
   bmp  3,  "Processing Code",                           N,         :length =>  6
   bmp  4,  "Amount (Transaction)",                      N,         :length => 12
   bmp  6,  "Amount, Cardholder Billing" ,               N,         :length => 12
   (...)
end

The example above defines four bitmaps (2,3,4 and 6), and provides their bitmap number and description. The PAN field is variable length encoded (LL length indicator, ASCII, contents numeric, ASCII) and the maximum length of the field is limited to 19 using options.

The other fields are fixed length numeric ASCII fields (the length of the fields is indicated by the ‘:length` options.)

This message may be used as follows in order to interpret a received message.:

mes = MyMessage.parse inputData
puts mes[2] # prints the PAN from the message.

Constructing own messages works as follows:

mes = MyMessage.new 1100
mes[2]= 474747474747
# Alternatively
mes["Primary Account Number (PAN)"]= 4747474747
mes[3] = 1234 # padding is added by the Field en/decoder
mes["Amount (Transaction)"] = 100
mes[6] = 200

the convenience method bmp_alias may be used in defining the class in order to provide direct access to fields using methods:

class MyMessage < Message
   (...)
   bmp  2, "Primary Account Number (PAN)",               LLVAR_N,   :max    => 19
   (...)
   bmp_alias 2, :pan
end

this allows accessing fields in the following manner:

mes = MyMessage.new 1100
mes.pan = 474747474747
puts mes.pan
# Identical functionality to:
mes[2]= 474747474747
# or:
mes["Primary Account Number (PAN)"]= 4747474747

Most of the work in implementing a new set of message type lays in figuring out the correct fields to use defining the Message class via bmp.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mti = nil, use_hex_bitmap = false, ignore_mti = false, bitmap_size = 64) ⇒ Message

Instantiate a new instance of this type of Message optionally specifying an mti.



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/iso8583/message.rb', line 129

def initialize(mti = nil, use_hex_bitmap = false, ignore_mti = false, bitmap_size = 64)
  # values is an internal field used to collect all the
  # bmp number | bmp name | field en/decoders | values
  # which are set in this message.
  @values = {}

  self.mti = mti if mti
  @use_hex_bitmap = use_hex_bitmap
  @ignore_mti = ignore_mti
  @bitmap_size = bitmap_size
end

Instance Attribute Details

#bitmap_sizeObject (readonly)

bitmap_size define the size of bitmap to be used, in number of bits. It should be a multiple of 8 (a byte of 8 bits)



125
126
127
# File 'lib/iso8583/message.rb', line 125

def bitmap_size
  @bitmap_size
end

#ignore_mtiObject (readonly)

ignore_mti allow use of Message without mti. Useful for fields with variable subfields



121
122
123
# File 'lib/iso8583/message.rb', line 121

def ignore_mti
  @ignore_mti
end

#mtiObject

The value of the MTI (Message Type Indicator) of this message.



115
116
117
# File 'lib/iso8583/message.rb', line 115

def mti
  @mti
end

#use_hex_bitmapObject (readonly)

ISO8583 allows hex or binary bitmap, so it should be configurable



118
119
120
# File 'lib/iso8583/message.rb', line 118

def use_hex_bitmap
  @use_hex_bitmap
end

Class Method Details

._definitionsObject

Access the field definitions of this class, this is a hash containing [bmp_number, BMP] and [bitmap_name, BMP] pairs.



408
409
410
# File 'lib/iso8583/message.rb', line 408

def _definitions
  @defs
end

._handle_opts(field, opts) ⇒ Object

Modifies the field definitions of the fields passed in through the ‘bmp` and `mti_format` class methods.



425
426
427
428
429
430
431
432
433
434
# File 'lib/iso8583/message.rb', line 425

def _handle_opts(field, opts)
  opts.each_pair {|key, value|
    key = (key.to_s+"=").to_sym
    if field.respond_to?(key)
      field.send(key, value)
    else
      warn "unknown option #{key} for #{field.name}"
    end
  }
end

._mti_definitionsObject

access the mti definitions applicable to the Message

returns a pair of hashes containing:

mti_value => mti_name

mti_name => mti_value



400
401
402
# File 'lib/iso8583/message.rb', line 400

def _mti_definitions
  [@mtis_v, @mtis_n]
end

._mti_formatObject

Returns the field definition to format the mti.



413
414
415
# File 'lib/iso8583/message.rb', line 413

def _mti_format
  @mti_format
end

.bmp(bmp, name, field, opts = nil) ⇒ Object

Define a bitmap in the message

Params:

  • bmp : bitmap number

  • name : human readable form

  • field : field for encoding/decoding

  • opts : options to pass to the field, e.g. length for fxed len fields.

Example

class MyMessage < Message
  bmp 2, "PAN", LLVAR_N, :max =>19
  (...)
end

creates a class MyMessage that allows for a bitmap 2 which is named “PAN” and encoded by an LLVAR_N Field. The maximum length of the value is 19. This class may be used as follows:

mes = MyMessage.new
mes[2] = 474747474747 # or mes["PAN"] = 4747474747


325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/iso8583/message.rb', line 325

def bmp(bmp, name, field, opts = nil)
  @defs ||= {}

  field = field.dup
  field.name = name
  field.bmp  = bmp
  _handle_opts(field, opts) if opts

  bmp_def = BMP.new bmp, name, field

  @defs[bmp]  = bmp_def
  @defs[name] = bmp_def
end

.bmp_alias(bmp, aliaz) ⇒ Object

Create an alias to access bitmaps directly using a method. Example:

class MyMessage < Message
    (...)
    bmp 2, "PAN", LLVAR_N
    (...)
    bmp_alias 2, :pan
end #class

would allow you to access the PAN like this:

mes.pan = 1234
puts mes.pan

instead of:

mes[2] = 1234


357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/iso8583/message.rb', line 357

def bmp_alias(bmp, aliaz)
  define_method (aliaz) {
    bmp_ = @values[bmp]
    bmp_ ? bmp_.value : nil
  }

  define_method ("#{aliaz}=") {|value|
    self[bmp] = value
    # bmp_def = _get_definition(bmp)
    # bmp_def.value= value
    # @values[bmp] = bmp_def
  }
end

.mti(value, name) ⇒ Object

Defines the message types allowed for this type of message and gives them names

Example

class MyMessage < Message
  (...)
  mti 1100, "Authorization Request Acquirer Gateway"
end

mes = MyMessage.new
mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"

See Also: mti_format



297
298
299
300
301
302
# File 'lib/iso8583/message.rb', line 297

def mti(value, name)
  @mtis_v ||= {}
  @mtis_n ||= {}
  @mtis_v[value] = name
  @mtis_n[name]  = value
end

.mti_format(field, opts) ⇒ Object

Defines how the message type indicator is encoded into bytes.

Params:

  • field : the decoder/encoder for the MTI

  • opts : the options to pass to this field

Example

class MyMessage < Message
  mti_format N, :length =>4
  (...)
end

encodes the mti of this message using the ‘N` field (fixed length, plain ASCII) and sets the fixed lengh to 4 bytes.

See also: mti



278
279
280
281
282
# File 'lib/iso8583/message.rb', line 278

def mti_format(field, opts)
  f = field.dup
  _handle_opts(f, opts)
  @mti_format = f
end

.parse(str, use_hex_bitmap = false, bitmap_size = 64) ⇒ Object

Parse the bytes ‘str` returning a message of the defined type.



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/iso8583/message.rb', line 372

def parse(str, use_hex_bitmap = false, bitmap_size = 64)
  message = self.new(nil, use_hex_bitmap, false, bitmap_size)

  message.mti, rest = _mti_format.parse(str)

  bmp, rest = Bitmap.parse(rest, use_hex_bitmap, bitmap_size)

  bmp.each {|bit|
    bmp_def      = _definitions[bit]

    unless bmp_def
      raise ISO8583ParseException.new "The message contains fields not defined"
    end

    value, rest  = bmp_def.field.parse(rest)
    message[bit] = value
  }
  message
end

Instance Method Details

#[](key) ⇒ Object

Retrieve the decoded value of the contents of a bitmap described either by the bitmap number or name.

Example

mes = BlaBlaMessage.parse someMessageBytes
mes[2] # bmp 2 is generally the PAN
mes["Primary Account Number"] # if thats what you called the field in Message.bmp.


185
186
187
188
189
# File 'lib/iso8583/message.rb', line 185

def [](key)
  bmp_def = _get_definition key
  bmp     = @values[bmp_def.bmp]
  bmp ? bmp.value : nil
end

#[]=(key, value) ⇒ Object

Set a field in this message, ‘key` is either the bmp number or it’s name.

Example

mes = BlaBlaMessage.new
mes[2]=47474747                          # bmp 2 is generally the PAN
mes["Primary Account Number"]=47474747   # if thats what you called the field in Message.bmp.


167
168
169
170
171
172
173
174
175
# File 'lib/iso8583/message.rb', line 167

def []=(key, value)
  if value.nil?
    @values.delete(key)
  else
    bmp_def              = _get_definition key
    bmp_def.value        = value
    @values[bmp_def.bmp] = bmp_def
  end
end

#_bodyObject

Returns an array of two byte arrays:

bitmap_bytes, message_bytes


224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/iso8583/message.rb', line 224

def _body
  bitmap  = Bitmap.new(nil, use_hex_bitmap, bitmap_size)
  message = String.new("", encoding: 'ASCII-8BIT')
  @values.keys.sort.each do |bmp_num|
    bitmap.set(bmp_num)
    enc_value = @values[bmp_num].encode
    message << enc_value
  end

  if use_hex_bitmap
    [bitmap.to_hex, message]
  else
    [bitmap.to_bytes, message]
  end
end

#_get_definition(key) ⇒ Object

:nodoc:



240
241
242
243
244
245
246
# File 'lib/iso8583/message.rb', line 240

def _get_definition(key) #:nodoc:
  b = self.class._definitions[key]
  unless b
    raise ISO8583Exception.new "no definition for field: #{key}"
  end
  b.dup
end

#_get_mti_definition(key) ⇒ Object

return [mti_num, mti_value] for key being either mti_num or mti_value



250
251
252
253
254
255
256
257
258
259
# File 'lib/iso8583/message.rb', line 250

def _get_mti_definition(key)
  num_hash,name_hash = self.class._mti_definitions
  if num_hash[key]
    [key, num_hash[key]]
  elsif name_hash[key]
    [name_hash[key], key]
  else
    raise ISO8583Exception.new("MTI: #{key} not allowed!")
  end
end

#to_bObject

Retrieve the byte representation of the bitmap.



192
193
194
195
196
197
198
199
# File 'lib/iso8583/message.rb', line 192

def to_b
  raise ISO8583Exception.new "no MTI set!" if !(ignore_mti || mti)

  return _body.join if ignore_mti

  mti_enc = self.class._mti_format.encode(mti)
  mti_enc << _body.join
end

#to_sObject

Returns a nicely formatted representation of this message.



203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/iso8583/message.rb', line 203

def to_s
  _mti_name = _get_mti_definition(mti)[1]
  str = "MTI:#{mti} (#{_mti_name})\n\n"
  _max = @values.values.max {|a,b|
    a.name.length <=> b.name.length
  }
  _max_name = _max.name.length

  @values.keys.sort.each{|bmp_num|
    _bmp = @values[bmp_num]
    str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
  }
  str
end