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.

Direct Known Subclasses

BerlinMessage

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mti = nil) ⇒ Message

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



117
118
119
120
121
122
123
# File 'lib/iso8583/message.rb', line 117

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

Instance Attribute Details

#mtiObject

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



113
114
115
# File 'lib/iso8583/message.rb', line 113

def mti
  @mti
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.



374
375
376
# File 'lib/iso8583/message.rb', line 374

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.



391
392
393
394
395
396
397
398
399
400
# File 'lib/iso8583/message.rb', line 391

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



366
367
368
# File 'lib/iso8583/message.rb', line 366

def _mti_definitions
  [@mtis_v, @mtis_n]
end

._mti_formatObject

Returns the field definition to format the mti.



379
380
381
# File 'lib/iso8583/message.rb', line 379

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


299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/iso8583/message.rb', line 299

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


331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/iso8583/message.rb', line 331

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



271
272
273
274
275
276
# File 'lib/iso8583/message.rb', line 271

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



252
253
254
255
256
# File 'lib/iso8583/message.rb', line 252

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

.parse(str) ⇒ Object

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



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

def parse(str)
  message = self.new
  message.mti, rest = _mti_format.parse(str)
  bmp,rest = Bitmap.parse(rest)
  bmp.each {|bit|
    bmp_def      = _definitions[bit]
    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.


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

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.


149
150
151
152
153
154
155
156
157
# File 'lib/iso8583/message.rb', line 149

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


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

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
  end
  [ bitmap.to_bytes, message ]
end

#_get_definition(key) ⇒ Object

:nodoc:



214
215
216
217
218
219
220
# File 'lib/iso8583/message.rb', line 214

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



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

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.



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

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

#to_sObject

Returns a nicely formatted representation of this message.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/iso8583/message.rb', line 182

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