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) ⇒ Message

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



122
123
124
125
126
127
128
129
130
131
# File 'lib/iso8583/message.rb', line 122

def initialize(mti = nil, use_hex_bitmap = false)
  # 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

end

Instance Attribute Details

#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.



395
396
397
# File 'lib/iso8583/message.rb', line 395

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.



412
413
414
415
416
417
418
419
420
421
# File 'lib/iso8583/message.rb', line 412

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



387
388
389
# File 'lib/iso8583/message.rb', line 387

def _mti_definitions
  [@mtis_v, @mtis_n]
end

._mti_formatObject

Returns the field definition to format the mti.



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

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


312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/iso8583/message.rb', line 312

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


344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/iso8583/message.rb', line 344

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



284
285
286
287
288
289
# File 'lib/iso8583/message.rb', line 284

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



265
266
267
268
269
# File 'lib/iso8583/message.rb', line 265

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

.parse(str, use_hex_bitmap = false) ⇒ Object

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



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/iso8583/message.rb', line 359

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

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

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

  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.


175
176
177
178
179
# File 'lib/iso8583/message.rb', line 175

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.


157
158
159
160
161
162
163
164
165
# File 'lib/iso8583/message.rb', line 157

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


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/iso8583/message.rb', line 211

def _body
  bitmap  = Bitmap.new
  message = ""
  @values.keys.sort.each do |bmp_num|
    bitmap.set(bmp_num)
    enc_value = @values[bmp_num].encode
    message << enc_value.force_encoding('UTF-8')
  end

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

#_get_definition(key) ⇒ Object

:nodoc:



227
228
229
230
231
232
233
# File 'lib/iso8583/message.rb', line 227

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



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

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.



182
183
184
185
186
# File 'lib/iso8583/message.rb', line 182

def to_b
  raise ISO8583Exception.new "no MTI set!" unless mti
  mti_enc = self.class._mti_format.encode(mti).force_encoding('UTF-8')
  mti_enc << _body.join.force_encoding('UTF-8')
end

#to_sObject

Returns a nicely formatted representation of this message.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/iso8583/message.rb', line 190

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