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.



377
378
379
# File 'lib/iso8583/message.rb', line 377

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.



394
395
396
397
398
399
400
401
402
403
# File 'lib/iso8583/message.rb', line 394

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



369
370
371
# File 'lib/iso8583/message.rb', line 369

def _mti_definitions
  [@mtis_v, @mtis_n]
end

._mti_formatObject

Returns the field definition to format the mti.



382
383
384
# File 'lib/iso8583/message.rb', line 382

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


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

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


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

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



273
274
275
276
277
278
# File 'lib/iso8583/message.rb', line 273

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



254
255
256
257
258
# File 'lib/iso8583/message.rb', line 254

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.



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

def parse(str)
  str = str.force_encoding('ASCII-8BIT')
  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


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

def _body
  bitmap  = Bitmap.new
  message = "".force_encoding('ASCII-8BIT')
  @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:



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

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



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

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
179
180
# 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) 
  str_body="".force_encoding('ASCII-8BIT')
  _body.map {|b| str_body+=b.force_encoding('ASCII-8BIT')} 
  mti_enc << str_body
end

#to_sObject

Returns a nicely formatted representation of this message.



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

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