SWIFT messages processing and testing with Java WIFE

This document provides an introduction to the SWIFT protocol and messages, and gives a guideline explaining how to process swift messages using the Java WIFE library

SWIFT Overview 

SWIFT or Society for Worldwide Interbank Financial Telecommunication provides a network to allow financial and non-financial institutions (e.g. corporates) to transfer financial transactions through a 'financial message'.

Message structure

SWIFT messages consist of five blocks of data including three headers, message content, and a trailer.

  1. {1: Basic Header Block}
  2. {2: Application Header Block}
  3. {3: User Header Block}
  4. {4: Text Block or body}
  5. {5: Trailer Block}

- All blocks have the same basic format:{n:...}
- The curly braces ({}) indicate the beginning and end of a block.
- n is the block identifier, in this case a single integer between 1 and 5.
- Each block identifier is associated with a particular part of the message.
- Blocs 3 and 5 are optional.

Field structure

A field is a logical subdivision of a message block, which consists of a sequence of components with a starting field tag and delimiters.
A field is always prefaced by a field tag that consists of two digits followed, optionally, by an alphabetic character. The alphabetic character is referred to as an option.
For example, 16R is a tag (16) with an option (R) that indicates the start of a block; 16S is a tag (16) with an option (S) that indicates the end of a block. A field is always terminated by a field delimiter. The delimiter depends on the type of field used in a message block.

Message sample

{1:F01ANASCH20AXXX0527012180}{2:O5020750040609LRLRXXXX4A0400004386330406090954U}{3:{108:MT502 001 OF 008}}{4:
:16R:GENL
:20C::SEME//01116
:23G:NEWM/CODU
:22F::TRTR//BASK
:16R:LINK
:20C::PREV//x
:16S:LINK
:16S:GENL
:16R:ORDRDET
:16R:PRIC
:90A::LIMI//DISC/1200
:16S:PRIC
:22H::BUSE//SWIT
:22F::TOOR//ALNO
:98A::EXPI//19991231
:16R:TRADPRTY
:95P::BUYR//P005
:16S:TRADPRTY
:19A::ORDR//NUSD1,34
:36B::ORDR//UNIT/7500
:35B:ISIN MA0000011058
:16S:ORDRDET
:16R:SETDET
:22F::SETR//COLL
:16R:SETPRTY
:95R::REAG/A2C4E6G8/34x
:16S:SETPRTY
:16R:SETPRTY
:95R::BUYR/A2C4E6G8/34x
:16S:SETPRTY
:16R:SETPRTY
:95R::RECU/A2C4E6G8/34x
:16S:SETPRTY
:16R:SETPRTY
:95R::DEAG/A2C4E6G8/34x
:16S:SETPRTY
:16R:SETPRTY
:95P::PSET//DATW
:16S:SETPRTY
:16S:SETDET
-}{5:{MAC:307F606D}{CHK:521A0E3826D3}{TNG:}}

WIFE Java API

Java WIFE library is an open source Java library for SWIFT messages parsing, writing and processing. The component is heavily tested and running in production environments since 2006.
Its main features are:


All SWIFT message categories (MT0xx to MT9xx) are supported, including System and Service message. The current version is compliant with ISO 15022.


Pre-Requisites

The following jar files, representing the Java WIFE API should be used.


SWIFT Parsing example with WIFE

The following example shows how to:


package com.sungard.isb.swift.test;

import java.math.BigDecimal;
import java.util.Calendar;

import junit.framework.TestCase;
import net.sourceforge.wife.services.ConversionService;
import net.sourceforge.wife.services.IConversionService;
import net.sourceforge.wife.swift.model.SwiftMessage;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import com.prowidesoftware.swift.model.MIR;
import com.prowidesoftware.swift.model.field.Field32A;
import com.prowidesoftware.swift.model.field.Field59;


public class TestSwiftParsing extends TestCase {

      private String swiftStr;
      private String xml;
      private SwiftMessage msg;
      private IConversionService service;

      private static final Logger logger = Logger
                  .getLogger(TestSwiftGeneration.class);

      /** 
       * set up the swift message and the service
      */
      @Before
      public void setUp() throws Exception {

            service = new ConversionService();

            swiftStr = "{1:F01BANKDEFMAXXX2039063581}"
                        + "{2:O1031609050901BANKDEFXAXXX89549829458949811609N}"
                        + "{3:{108:00750532785315}}{4:\n" + ":20:007505327853\n"
                        + ":23B:CRED\n" 
                        + ":32A:050902JPY3520000\n"
                        + ":33B:JPY3520000,\n" 
                        + ":50K:EUROXXXEI\n" 
                        + ":52A:FEBXXXM1\n"
                        + ":53A:MHCXXXJT\n" 
                        + ":54A:FOOBICXX\n" 
                        + ":59:/13212312\n"
                        + "RECEIVER NAME S.A\n" 
                        + ":70:FUTURES\n" 
                        + ":71A:SHA\n"
                        + ":71F:EUR12,00\n" 
                        + ":71F:EUR2,34\n" + "-}";
            
            msg = service.getMessageFromFIN(swiftStr);
            xml = service.getXml(msg); //convert to xml

      }

      /**
      * Test parse swift message
      */
      @Test
      public void testParseSwiftMessage() {
                      
            assertEquals(swiftStr.length(), 313);
            assertEquals(xml.length(), 1623);
            assertEquals(msg.getBlock1().getApplicationId(), "F");
            assertEquals(msg.getBlock1().getServiceId(), "01");
            assertEquals(msg.getBlock1().getLogicalTerminal(), "BANKDEFMAXXX");
            assertEquals(msg.getBlock1().getSessionNumber(), "2039");
            assertEquals(msg.getBlock1().getSequenceNumber(), "063581");
       }
      
      
      /**
      * Test Block1
      */
      @Test
      public void testBlock1() {
            
            assertEquals(msg.getBlock1().getValue(), "F01BANKDEFMAXXX2039063581");
            assertEquals(msg.getBlock1().getApplicationId(), "F");
            assertEquals(msg.getBlock1().getServiceId(), "01");
            assertEquals(msg.getBlock1().getLogicalTerminal(), "BANKDEFMAXXX");
            assertEquals(msg.getBlock1().getSessionNumber(), "2039");
            assertEquals(msg.getBlock1().getSequenceNumber(), "063581");
      }
      
      /**
      * Test Block2
      */
      @Test
      public void testBlock2() {
            
            assertEquals(msg.getBlock2().getValue(), 
                     "O1031609050901BANKDEFXAXXX89549829458949811609N");
            assertNull(msg.getBlock2().getBlockType());
            assertEquals(msg.getBlock2().getMessagePriority(), "N");
            assertEquals(msg.getBlock2().getMessageType(), "103");
            
            MIR mir = new MIR(msg.getBlock2().getValue().substring(8, 8+28));
            assertEquals(mir.getDate(), "050901");
            assertEquals(mir.getLogicalTerminal(), "BANKDEFXAXXX");
            assertEquals(mir.getSessionNumber(), "8954");

      }
      
      /**
      * Test Block3
      */
      @Test
      public void testBlock3() {
            
            assertEquals(msg.getBlock3().getName(), "3");
            assertNull(msg.getBlock3().getBlockType());
            assertNull(msg.getBlock3().getId());
            assertEquals(msg.getBlock3().getTagValue("108"), "00750532785315");
      
      }
      
      /**
      * Test Block4
      */
      @Test
      public void testBlock4() {

            assertEquals(msg.getBlock4().getTagCount(),13);
            assertEquals(msg.getBlock4().getTagCount("23B"),1);
            assertEquals(msg.getBlock4().getTagValue("23B"), "CRED");

      }
      /**
      * Test Fields 71F
      */
      @Test
      public void testFieldList71F() {
            String[] list71 = msg.getBlock4().getTagValues("71F");
            assertEquals("71F: Size is incorrect", list71.length,2);
            assertEquals("71F: Value(1) is incorrect", list71[0],"EUR12,00");
            assertEquals("71F: Value(2) is incorrect", list71[1],"EUR2,34");
      }
      /**
      * Test 32A field parsing
      */
      @Test
      public void testField32A() {

            Field32A f32A = new Field32A(msg.getBlock4().getTagValue("32A"));

            assertEquals("32A: Component not correct",f32A.getValue(), "050902JPY3520000");
            assertEquals("32A: Date not correct",f32A.getComponent1(), "050902");
            assertEquals("32A: Year is incorrect", 
                        f32A.getComponent1AsCalendar().get(Calendar.YEAR), 2005);
            assertEquals("32A: Currency is incorrect", f32A.getComponent2(),"JPY");
            assertEquals("32A: Currency is incorrect", f32A.getComponent2AsCurrency()
                                 .getSymbol(),"JPY");
            assertEquals("32A: Amount is incorrect", 
                            f32A.getComponent3AsNumber().doubleValue(),3520000.0);
      }
}


SWIFT generation example with WIFE

The following example shows how to generate SWIFT MT messages from SwiftMessage Java objects.

package com.sungard.isb.swift.test;

import junit.framework.TestCase;
import net.sourceforge.wife.services.ConversionService;
import net.sourceforge.wife.services.IConversionService;
import net.sourceforge.wife.swift.model.SwiftBlock1;
import net.sourceforge.wife.swift.model.SwiftBlock4;
import net.sourceforge.wife.swift.model.SwiftMessage;
import net.sourceforge.wife.swift.model.Tag;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

public class TestSwiftGeneration extends TestCase {

      private String swiftStr;
      private SwiftMessage msg;
      private IConversionService service;
      private SwiftBlock1 b1;

      private static final Logger logger = Logger
                  .getLogger(TestSwiftGeneration.class);

      /**
      * set up the swift message object and the service
      * @throws java.lang.Exception
      */
      @Before
      public void setUp() throws Exception {

            service = new ConversionService();
            msg = new SwiftMessage();

            /**
            * Block 1
            */
            b1 = new SwiftBlock1();
            b1.setApplicationId("F");
            b1.setServiceId("21");
            b1.setLogicalTerminal("ABNAGB2PAXXX");
            b1.setSessionNumber("2766");
            b1.setSequenceNumber("797510");
            msg.setBlock1(b1);

            /**
            * Block4
            */

            msg.setBlock4(new SwiftBlock4());
            SwiftBlock4 b4 = msg.getBlock4();
            
            b4.addTag(new Tag("16R:GENL"));
            b4.addTag(new Tag("20C:CORP//1255"));
            b4.addTag(new Tag("23G:NEWM"));
            b4.addTag(new Tag("22F:CAEV//RHTS"));
            b4.addTag(new Tag("98C::PREP//20010115101205"));
            b4.addTag(new Tag("16S:GENL"));
            
            b4.addTag(new Tag("16R:USECU"));
            b4.addTag(new Tag("35B:ISIN//FR0000077844"));
            b4.addTag(new Tag("16S:USECU"));
            
            b4.addTag(new Tag("16R:ACCTINFO"));
            b4.addTag(new Tag("97A::SAFE//AC1234"));
            b4.addTag(new Tag("93B::ELIG//UNIT/2500"));
            b4.addTag(new Tag("16S:ACCTINFO"));
            
            b4.addTag(new Tag("16R:CAINST"));
            b4.addTag(new Tag("13A::CAON//UNS"));
            b4.addTag(new Tag("22H::CAOP//CASH"));
            b4.addTag(new Tag("36B::QINS//UNIT/10"));
            b4.addTag(new Tag("92A::TAXB//30"));
            b4.addTag(new Tag("70E::INST//ABCDEFGH"));
            b4.addTag(new Tag("16S:CAINST"));
      }

      /**
      * Test generating swift message
      */
      @Test
      public void testGenerateSwiftMessage() {

           swiftStr = service.getFIN(msg);
           assertEquals(swiftStr, "{1:F21ABNAGB2PAXXX2766797510}{4:{16R:GENL}{20C:CORP//1255}{23G:NEWM}
{22F:CAEV//RHTS}{98C::PREP//20010115101205}{16S:GENL}{16R:USECU}
{35B:ISIN//FR0000077844}{16S:USECU}{16R:ACCTINFO}{97A::SAFE//AC1234}
{93B::ELIG//UNIT/2500}{16S:ACCTINFO}{16R:CAINST}{13A::CAON//UNS}
{22H::CAOP//CASH}{36B::QINS//UNIT/10}{92A::TAXB//30}{70E::INST//ABCDEFGH}
{16S:CAINST}}");
            assertEquals(swiftStr.length(), 350);
            assertEquals(b1.getApplicationId(), "F");
            assertEquals(b1.getServiceId(), "21");
            assertEquals(b1.getLogicalTerminal(), "ABNAGB2PAXXX");
            assertEquals(b1.getSessionNumber(), "2766");
            assertEquals(b1.getSequenceNumber(), "797510");
      }
}