├── .gitignore ├── src └── main │ └── java │ └── com │ └── paymentcomponents │ └── mt │ └── demo │ ├── Main.java │ ├── Utils.java │ ├── ConvertMT2XML.java │ ├── BuildMT101_1.java │ ├── BuildMT101_2.java │ ├── ParseValidMT01.java │ └── ParseInvalidMT01.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | target/ -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/Main.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | 6 | ParseValidMT01.execute(); 7 | ParseInvalidMT01.execute(); 8 | ConvertMT2XML.execute(); 9 | BuildMT101_1.execute(); 10 | BuildMT101_2.execute(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/Utils.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.validator.SwiftValidObj; 4 | 5 | public class Utils { 6 | public static void printErrors(SwiftValidObj swiftValidObj) { 7 | swiftValidObj.getValidationErrorList().stream().forEach(error -> { 8 | System.err.println( 9 | "Error Code: " + error.getErrorCode() + "\n" + 10 | "Error Description: " + error.getDescription() + "\n" + 11 | "Tag in error: " + error.getTagName() + "\n" + 12 | "Line number in error inside the tag: " + error.getLine() + "\n" + 13 | "Ocurrency: " + error.getOccurs() //In case the tag is repeated in the message, the occurs property contains the occurency in error 14 | ); 15 | }); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.paymentcomponents 8 | demo-mt 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | paymentcomponents 14 | https://nexus.paymentcomponents.com/repository/public 15 | 16 | 17 | 18 | 19 | 20 | gr.datamation 21 | smv 22 | 24.2.0 23 | demo 24 | 25 | 26 | 27 | 28 | 8 29 | 8 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/ConvertMT2XML.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.common.InvalidMessageFormatException; 4 | import gr.datamation.mt.common.SwiftMessage; 5 | import gr.datamation.mt.processor.SwiftMsgProcessor; 6 | import gr.datamation.mt.swift2xml.XMLWriter; 7 | import gr.datamation.mt.validator.SwiftMsgValidator; 8 | import gr.datamation.mt.validator.SwiftValidObj; 9 | 10 | import java.io.PrintWriter; 11 | 12 | public class ConvertMT2XML { 13 | 14 | public static void execute() { 15 | System.out.println("Convert MT to XML"); 16 | String mt101String = "{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 17 | ":20:00043\n" + 18 | ":28D:1/1\n" + 19 | ":50F:/409074-293-45/786\n" + 20 | "1/George Philips\n" + 21 | "2/High Street 1\n" + 22 | "3/GB/London\n" + 23 | ":30:011231\n" + 24 | ":21:PQR-27ZZ-01\n" + 25 | ":32B:USD2564,50\n" + 26 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 27 | "MynR49R RailRoad Trust\n" + 28 | "Cloudsboro ARTUI\n" + 29 | ":59F:1/Beneficiary Name-1234567891234123\n" + 30 | "2/QWERT\n" + 31 | "3/US/Beneficiary Address Line 21\n" + 32 | "3/Beneficiary Name-1234567891234123\n" + 33 | ":71A:OUR\n" + 34 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"; 35 | SwiftMsgProcessor parser = new SwiftMsgProcessor("\n"); 36 | try { 37 | SwiftMessage smObj = parser.ParseMsgStringToObject(mt101String); 38 | //Validate the message 39 | SwiftValidObj swiftValidObj = new SwiftMsgValidator().validateMsg(smObj); 40 | //Check if it has any errors 41 | if (swiftValidObj.hasErrors()) { 42 | Utils.printErrors(swiftValidObj); 43 | } else { 44 | XMLWriter xmlWriter = new XMLWriter(); 45 | xmlWriter.buildXML(swiftValidObj.getSwiftMessage(), System.out); 46 | PrintWriter printWriter = new PrintWriter("./mt.xml"); 47 | xmlWriter.buildXML(swiftValidObj.getSwiftMessage(), "./", "mt2.xml"); 48 | } 49 | } catch (InvalidMessageFormatException e) { 50 | System.err.println("Message cannot be parsed"); 51 | } catch (Exception e) { 52 | System.err.println("Cannot produce XML for this message"); 53 | } 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/BuildMT101_1.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.common.RepSeq; 4 | import gr.datamation.mt.common.SwiftMessage; 5 | import gr.datamation.mt.common.Tag; 6 | import gr.datamation.mt.processor.SwiftMsgProcessor; 7 | import gr.datamation.mt.validator.SwiftMsgValidator; 8 | import gr.datamation.mt.validator.SwiftValidObj; 9 | 10 | import java.util.Vector; 11 | 12 | public class BuildMT101_1 { 13 | 14 | public static void execute() { 15 | System.out.println("Build MT 101 Way 1"); 16 | SwiftMessage message = new SwiftMessage(); 17 | 18 | // Set Tags for block1 19 | message.setArgApplid("F"); 20 | message.setArgServid("01"); 21 | message.setArgLTaddrBlk1("COPZBEB0AXXX"); 22 | 23 | // Set Tags for block2 24 | message.setArgInoutind("I"); 25 | message.setArgMsgtype("101"); 26 | message.setArgLTaddrBlk2("LRLRXXXX4A11"); 27 | message.setArgMsgprior("N"); 28 | 29 | // Set Tags for block3 30 | 31 | message.getBlock3().add(new Tag("108", new Vector() {{ 32 | add("MT103 005 OF 020"); 33 | }})); 34 | 35 | 36 | // Set Tags for block4 37 | message.getBlock4().add(new Tag("20", new Vector(){{add("5387354");}})); 38 | message.getBlock4().add(new Tag("28D", new Vector(){{add("1/1");}})); 39 | message.getBlock4().add(new Tag("50F", new Vector(){{ 40 | add("/409074-293-45/786"); 41 | add("1/George Philips"); 42 | add("2/High Street 1"); 43 | add("3/GB/London"); 44 | }})); 45 | message.getBlock4().add(new Tag("30", new Vector(){{add("011231");}})); 46 | 47 | RepSeq repSeqB = new RepSeq(); 48 | repSeqB.addTag(new Tag("21", new Vector(){{add("PQR-27ZZ-01");}})); 49 | repSeqB.addTag(new Tag("32B", new Vector(){{add("USD2564,50");}})); 50 | repSeqB.addTag(new Tag("57D", new Vector(){{ 51 | add("/C/Clementine Nuggets-1842-Y"); 52 | add("MynR49R RailRoad Trust"); 53 | add("Cloudsboro ARTUI"); 54 | }})); 55 | repSeqB.addTag(new Tag("59F", new Vector(){{ 56 | add("1/Beneficiary Name-1234567891234123"); 57 | add("2/QWERT"); 58 | add("3/US/Beneficiary Address Line 21"); 59 | add("3/Beneficiary Name-1234567891234123"); 60 | }})); 61 | repSeqB.addTag(new Tag("71A", new Vector(){{add("OUR");}})); 62 | message.getBlock4().add(repSeqB); 63 | 64 | // Set Tags for block5 65 | message.getBlock5().add(new Tag("MAC", new Vector() {{ 66 | add("00000000"); 67 | }})); 68 | 69 | message.getBlock5().add(new Tag("CHK", new Vector() {{ 70 | add("4BCF59104AF9"); 71 | }})); 72 | 73 | SwiftValidObj swiftValidObj = new SwiftMsgValidator().validateMsg(message); 74 | if (swiftValidObj.hasErrors()){ 75 | Utils.printErrors(swiftValidObj); 76 | } else { 77 | try { 78 | System.out.println(new SwiftMsgProcessor().BuildMsgStringFromObject(message)); 79 | } catch (Exception e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/BuildMT101_2.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.common.RepSeq; 4 | import gr.datamation.mt.common.SwiftMessage; 5 | import gr.datamation.mt.common.Tag; 6 | import gr.datamation.mt.processor.SwiftMsgProcessor; 7 | import gr.datamation.mt.validator.SwiftMsgValidator; 8 | import gr.datamation.mt.validator.SwiftValidObj; 9 | 10 | import java.util.Collections; 11 | import java.util.Vector; 12 | 13 | public class BuildMT101_2 { 14 | 15 | public static void execute() { 16 | System.out.println("Build MT 101 - Way2"); 17 | SwiftMessage message = new SwiftMessage(); 18 | 19 | // Set Tags for block1 20 | message.setArgApplid("F"); 21 | message.setArgServid("01"); 22 | message.setArgLTaddrBlk1("COPZBEB0AXXX"); 23 | 24 | // Set Tags for block2 25 | message.setArgInoutind("I"); 26 | message.setArgMsgtype("101"); 27 | message.setArgLTaddrBlk2("LRLRXXXX4A11"); 28 | message.setArgMsgprior("N"); 29 | 30 | message.getBlock3().addAll(createTagList("108:MT101 005 OF 020")); 31 | 32 | message.getBlock4().addAll(createTagList(":20:00043\n" + 33 | ":28D:1/1\n" + 34 | ":50F:/409074-293-45/786\n" + 35 | "1/George Philips\n" + 36 | "2/High Street 1\n" + 37 | "3/GB/London\n" + 38 | ":30:011231")); 39 | message.getBlock4().add(createRepSeq(":21:PQR-27ZZ-01\n" + 40 | ":32B:USD2564,50\n" + 41 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 42 | "MynR49R RailRoad Trust\n" + 43 | "Cloudsboro ARTUI\n" + 44 | ":59F:1/Beneficiary Name-1234567891234123\n" + 45 | "2/QWERT\n" + 46 | "3/US/Beneficiary Address Line 21\n" + 47 | "3/Beneficiary Name-1234567891234123\n" + 48 | ":71A:OUR")); 49 | 50 | message.getBlock5().addAll(createTagList("MAC:00000000", "CHK:4BCF59104AF9")); 51 | SwiftValidObj swiftValidObj = new SwiftMsgValidator().validateMsg(message); 52 | if(swiftValidObj.hasErrors()) { 53 | Utils.printErrors(swiftValidObj); 54 | } else { 55 | try { 56 | System.out.println(new SwiftMsgProcessor().BuildMsgStringFromObject(message)); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | } 62 | 63 | public static RepSeq createRepSeq(String repSeqString) { 64 | return new RepSeq(createTagList(repSeqString.split("\n:"))); 65 | } 66 | 67 | public static Vector createTagList(String block4Text) { 68 | return createTagList(block4Text.split("\n:")); 69 | } 70 | 71 | public static Vector createTagList(String... tagValues) { 72 | Vector tagList = new Vector(); 73 | for (String tag : tagValues) { 74 | tagList.add(createTag(tag)); 75 | } 76 | return tagList; 77 | } 78 | 79 | public static Tag createTag(String tag) { 80 | String tagKey = tag.replaceAll("(?s):?(.*):.*", "$1"); 81 | String tagValueString = tag.replaceAll("(?s):?.*:(.*)", "$1"); 82 | String[] lines = tagValueString.split("\n"); 83 | 84 | Vector tagValue = new Vector(); 85 | Collections.addAll(tagValue, lines); 86 | 87 | return new Tag(tagKey, tagValue); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/ParseValidMT01.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.common.InvalidMessageFormatException; 4 | import gr.datamation.mt.common.SwiftMessage; 5 | import gr.datamation.mt.common.Tag; 6 | import gr.datamation.mt.processor.SwiftMsgProcessor; 7 | import gr.datamation.mt.validator.SwiftMsgValidator; 8 | import gr.datamation.mt.validator.SwiftValidObj; 9 | 10 | public class ParseValidMT01 { 11 | private static final String mt101String = 12 | "{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 13 | ":20:00043\n" + 14 | ":28D:1/1\n" + 15 | ":50F:/409074-293-45/786\n" + 16 | "1/George Philips\n" + 17 | "2/High Street 1\n" + 18 | "3/GB/London\n" + 19 | ":30:011231\n" + 20 | ":21:PQR-27ZZ-01\n" + 21 | ":32B:USD2564,50\n" + 22 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 23 | "MynR49R RailRoad Trust\n" + 24 | "Cloudsboro ARTUI\n" + 25 | ":59F:1/Beneficiary Name-1234567891234123\n" + 26 | "2/QWERT\n" + 27 | "3/US/Beneficiary Address Line 21\n" + 28 | "3/Beneficiary Name-1234567891234123\n" + 29 | ":71A:OUR\n" + 30 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"; 31 | 32 | public static void execute() { 33 | //You can instatiate the SwiftMsgProcessor with the EOL of your choice. 34 | //\n for linux based systems or \r\b for Windows 35 | //If the default constructor is used then the \n will be used by default 36 | SwiftMsgProcessor parser = new SwiftMsgProcessor("\n"); 37 | try { 38 | System.out.println("Parsing a valid MT101 message"); 39 | SwiftMessage smObj = parser.ParseMsgStringToObject(mt101String); 40 | System.out.println("Sender " + smObj.getArgLTaddrBlk1()); 41 | System.out.println("Receiver " + smObj.getArgLTaddrBlk2()); 42 | Tag tag20 = smObj.getTag("20"); 43 | Tag tag50a = smObj.getTag("50F"); 44 | //Get specific tag from the SwiftMessage object 45 | System.out.println("Tag " + tag20.getName() + " " + 46 | " " + tag20.getValueAsString()); 47 | System.out.println("Tag " + tag50a.getName() + " Number of Lines " 48 | + tag50a.getNumberOfDataLines()); 49 | tag50a.getData().stream().forEach(line -> 50 | System.out.println(tag50a.getName() + " line " + line)); 51 | //In the full version of the product you will have access to the tag's 52 | // description as given by swift by calling the following method 53 | //System.out.println("Description: " + 54 | // tag20.getDescription(smObj.getArgMsgtype())); 55 | //Output "Description: Sender's Reference" 56 | 57 | //Validate the message 58 | SwiftValidObj swiftValidObj = new SwiftMsgValidator().validateMsg(smObj); 59 | //Check if it has any errors 60 | if (!swiftValidObj.hasErrors()) { 61 | System.out.println("Message is valid"); 62 | } else { 63 | Utils.printErrors(swiftValidObj); 64 | } 65 | } catch (InvalidMessageFormatException e) { 66 | System.err.println("Message cannot be parsed"); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/com/paymentcomponents/mt/demo/ParseInvalidMT01.java: -------------------------------------------------------------------------------- 1 | package com.paymentcomponents.mt.demo; 2 | 3 | import gr.datamation.mt.common.InvalidMessageFormatException; 4 | import gr.datamation.mt.common.SwiftMessage; 5 | import gr.datamation.mt.processor.SwiftMsgProcessor; 6 | import gr.datamation.mt.validator.SwiftMsgValidator; 7 | import gr.datamation.mt.validator.SwiftValidObj; 8 | 9 | public class ParseInvalidMT01 { 10 | 11 | public static void execute() { 12 | System.out.println("Parsing invalid MT101 message"); 13 | invalidCurrencyCode(); 14 | nonExpectedCharacter(); 15 | invalidFormat(); 16 | mandatoryFieldMissing(); 17 | networkValidationError(); 18 | } 19 | 20 | private static void validate(String mt101String){ 21 | SwiftMsgProcessor parser = new SwiftMsgProcessor("\n"); 22 | try { 23 | SwiftMessage smObj = parser.ParseMsgStringToObject(mt101String); 24 | //Validate the message 25 | SwiftValidObj swiftValidObj = new SwiftMsgValidator().validateMsg(smObj); 26 | //Check if it has any errors 27 | if (swiftValidObj.hasErrors()) { 28 | Utils.printErrors(swiftValidObj); 29 | } 30 | } catch (InvalidMessageFormatException e) { 31 | System.err.println("Message cannot be parsed"); 32 | } 33 | } 34 | 35 | private static void invalidCurrencyCode(){ 36 | validate("{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 37 | ":20:00043\n" + 38 | ":28D:1/1\n" + 39 | ":50F:/409074-293-45/786\n" + 40 | "1/George Philips\n" + 41 | "2/High Street 1\n" + 42 | "3/GB/London\n" + 43 | ":30:011231\n" + 44 | ":21:PQR-27ZZ-01\n" + 45 | ":32B:AAA2564,50\n" + 46 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 47 | "MynR49R RailRoad Trust\n" + 48 | "Cloudsboro ARTUI\n" + 49 | ":59F:1/Beneficiary Name-1234567891234123\n" + 50 | "2/QWERT\n" + 51 | "3/US/Beneficiary Address Line 21\n" + 52 | "3/Beneficiary Name-1234567891234123\n" + 53 | ":71A:OUR\n" + 54 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"); 55 | 56 | } 57 | 58 | private static void nonExpectedCharacter(){ 59 | validate("{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 60 | ":20:00043@\n" + 61 | ":28D:1/1\n" + 62 | ":50F:/409074-293-45/786\n" + 63 | "1/George Philips\n" + 64 | "2/High Street 1\n" + 65 | "3/GB/London\n" + 66 | ":30:011231\n" + 67 | ":21:PQR-27ZZ-01\n" + 68 | ":32B:USD2564,50\n" + 69 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 70 | "MynR49R RailRoad Trust\n" + 71 | "Cloudsboro ARTUI\n" + 72 | ":59F:1/Beneficiary Name-1234567891234123\n" + 73 | "2/QWERT\n" + 74 | "3/US/Beneficiary Address Line 21\n" + 75 | "3/Beneficiary Name-1234567891234123\n" + 76 | ":71A:OUR\n" + 77 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"); 78 | 79 | } 80 | 81 | private static void invalidFormat(){ 82 | validate("{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 83 | ":20:0004322222222222222\n" + 84 | ":28D:1/1\n" + 85 | ":50F:/409074-293-45/786\n" + 86 | "1/George Philips\n" + 87 | "2/High Street 1\n" + 88 | "3/GB/London\n" + 89 | ":30:011231\n" + 90 | ":21:PQR-27ZZ-01\n" + 91 | ":32B:USD2564,50\n" + 92 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 93 | "MynR49R RailRoad Trust\n" + 94 | "Cloudsboro ARTUI\n" + 95 | ":59F:1/Beneficiary Name-1234567891234123\n" + 96 | "2/QWERT\n" + 97 | "3/US/Beneficiary Address Line 21\n" + 98 | "3/Beneficiary Name-1234567891234123\n" + 99 | ":71A:OUR\n" + 100 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"); 101 | 102 | } 103 | 104 | private static void mandatoryFieldMissing(){ 105 | validate("{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 106 | ":28D:1/1\n" + 107 | ":50F:/409074-293-45/786\n" + 108 | "1/George Philips\n" + 109 | "2/High Street 1\n" + 110 | "3/GB/London\n" + 111 | ":30:011231\n" + 112 | ":21:PQR-27ZZ-01\n" + 113 | ":32B:USD2564,50\n" + 114 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 115 | "MynR49R RailRoad Trust\n" + 116 | "Cloudsboro ARTUI\n" + 117 | ":59F:1/Beneficiary Name-1234567891234123\n" + 118 | "2/QWERT\n" + 119 | "3/US/Beneficiary Address Line 21\n" + 120 | "3/Beneficiary Name-1234567891234123\n" + 121 | ":71A:OUR\n" + 122 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"); 123 | 124 | } 125 | 126 | private static void networkValidationError(){ 127 | validate("{1:F01COPZBEB0AXXX0377002843}{2:O1011519110804LRLRXXXX4A1100009044661108041720N}{3:{108:MT101 006 OF 020}{433:/AOK/NO HIT DETECTED }}{4:\n" + 128 | ":20:1234567890123456\n" + 129 | ":28D:1/1\n" + 130 | ":50F:/409074-293-45/786\n" + 131 | "1/George Philips\n" + 132 | "2/High Street 1\n" + 133 | "3/GB/London\n" + 134 | ":30:011231\n" + 135 | ":21:PQR-27ZZ-01\n" + 136 | ":32B:USD2564,50\n" + 137 | ":57D:/C/Clementine Nuggets-1842-Y\n" + 138 | "MynR49R RailRoad Trust\n" + 139 | "Cloudsboro ARTUI\n" + 140 | ":59F:1/Beneficiary Name-1234567891234123\n" + 141 | "2/QWERT\n" + 142 | "3/US/Beneficiary Address Line 21\n" + 143 | "3/Beneficiary Name-1234567891234123\n" + 144 | ":33B:EUR2564,50\n" + 145 | ":71A:OUR\n" + 146 | "-}{5:{MAC:00000000}{CHK:19DA346889CC}{TNG:}}{S:{SAC:}{COP:P}}"); 147 | 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Swift MT Message Validator Demo 6 | 7 | This project is part of the [FINaplo](https://finaplo.paymentcomponents.com) product and is here to demonstrate how our [SDK](https://finaplo.paymentcomponents.com/financial-messages) for Swift MT Messages Validation works. 8 | For our demonstration we are going to use the demo SDK which can parse/validate/generate an MT101 message. 9 | 10 | It's a simple maven project, you can download it and run it, with Java 1.8 or above. 11 | 12 | ## API Specification 13 | 14 | ### Installation 15 | Incorporate the SDK [jar](https://nexus.paymentcomponents.com/repository/public/gr/datamation/smv/24.2.0/smv-24.2.0-demo.jar) 16 | into your project by the regular IDE means. This process will vary depending upon your specific IDE and you should consult your documentation on how to deploy a bean. For example in Eclipse all that needs to be done is to import the jar files into a project. 17 | Alternatively, you can import it as a Maven or Gradle dependency 18 | 19 | #### Maven 20 | Define repository in the repositories section 21 | ```xml 22 | 23 | paymentcomponents 24 | https://nexus.paymentcomponents.com/repository/public 25 | 26 | ``` 27 | 28 | Import the SDK 29 | ```xml 30 | 31 | gr.datamation 32 | smv 33 | 24.2.0 34 | demo 35 | 36 | ``` 37 | 38 | #### Gradle 39 | Define repository in the repositories section 40 | ```groovy 41 | repositories { 42 | maven { 43 | url "https://nexus.paymentcomponents.com/repository/public" 44 | } 45 | } 46 | ``` 47 | 48 | Import the SDK 49 | ```groovy 50 | implementation 'gr.datamation:smv:24.2.0:demo@jar' 51 | ``` 52 | In case you purchase the SDK you will be given a protected Maven repository with a user name and a password. You can configure your project to download the SDK from there. 53 | 54 | #### Other dependencies 55 | There is a dependency in jdom2 library which is used for XML generation. In case it's a maven projects the following dependency it will added to your project, otherwise you need to include the jar manually to the classpath 56 | ```xml 57 | 58 | org.jdom 59 | jdom2 60 | 2.0.6 61 | 62 | ``` 63 | 64 | In this project you can see code for all the basic manipulation of an MT message, like: 65 | - [Parse a valid MT](src/main/java/com/paymentcomponents/mt/demo/demo/ParseValidMT01.java) 66 | - [Parse invalid MTs](src/main/java/com/paymentcomponents/mt/demo/demo/ParseInvalidMT01.java) and get the syntax and network validations error 67 | - [Build an MT - Way 1](src/main/java/com/paymentcomponents/mt/demo/demo/BuildMT101_1.java) 68 | - [Build an MT - Way 2](src/main/java/com/paymentcomponents/mt/demo/demo/BuildMT101_2.java) 69 | - [Convert an MT from text to xml](src/main/java/com/paymentcomponents/mt/demo/demo/ConvertMT2XML.java) 70 | 71 | 72 | 73 | ### SwiftMsgParser object 74 | This object has two methods encapsulating all the functionality needed to handle a SWIFT message 75 | 76 | #### ParseMsgStringToObject method 77 | This is the method used to parse a swift message. It takes as input a string containing a single message as it arrives from the SWIFT network. The method first validates the message for syntax errors, to ensure that the message string was extracted correctly from the SWIFT system. Then parses it producing the SwiftMessage object containing the values of the message tags. The parser uses an efficient and fast algorithm that ensures the correct breakdown of the message to its information in the minimum time. 78 | {1:F01ABCDGRA0AXXX0057000289}{2:O1030919010321DDDDGRA0AXXX00570001710103210920N}{3:{113:ABCD}{111:111}{121:01911b73-1f15-4932-934a-609149301922}}{4:crlf 79 | :20:494930/DEVcrlf 80 | :32A:020527EUR1958,47crlf 81 | :50:BIODATA GJBHcrlf 82 | ZURICHcrlf 83 | :59:S.T JANSSENcrlf 84 | LEDEBOERSTRAAT 29crlf 85 | AMSTERDAMcrlf 86 | -}{5:{MAC:75D138E4}{CHK:DE1B0D71FA96}{TNG:}}{S:{SAC:}{COP}} 87 | 88 | Observe there are carriage-return line-feeds (crlf) separating each line. So when using your own examples, make sure you use the appropriate crlf's. 89 | To parse it, do the following: 90 | 91 | ```java 92 | //Put your SWIFT message in a string 93 | String message = "{1:F01ABCDGRA0AXXX0057000289}{2:O1000919010321DDDDGRA0AXXX00570001710103210920N}{3:{113:ABCD}{111:111}{121:01911b73-1f15-4932-934a-609149301922}}{4:\n:20:494930/DEV\n:32A:020527EUR1958,47\n:50:BIODATA GJBH\nZURICH\n:59:S.T JANSSEN\nLEDEBOERSTRAAT 29\nAMSTERDAM\n}{5:{MAC:75D138E4}{CHK:DE1B0D71FA96}{TNG:}}{S:{SAC:}{COP}}"; 94 | //Create the parser object as below 95 | SwiftMsgProcessor parser = new SwiftMsgProcessor(); 96 | //Create the return object to get back the results 97 | SwiftMessage smObj = parser.ParseMsgStringToObject(Message); 98 | ``` 99 | 100 | You are done! Now the smObj contains the parsed message. To see how to get or manipulate this object read the "SwiftMessage object" section below. 101 | 102 | #### ParseMsgStringsToObject method 103 | Since the version 14.1.0 you can use this method to construct a SwiftMessage object from multiple related SWIFT messages. The parser will undertake the merging process. The message types supported are: 104 | MT700 (with the MT701 sequence messages) 105 | MT710 (with the MT711 sequence messages) 106 | MT720 (with the MT721 sequence messages) 107 | The library will process tags 45B, 46B and 47B in the sequence messages and will assign the concatenated results inside the corresponding master message (i.e. MT700 or MT710 or MT720) in tags 45A, 46A and 47A respectively. Pagination (Field 27 in the resulting message) will be handled automatically. The messages passed as arguments should have correct pagination, but they do not necessarily need to be provided in the correct order as the library will sort them prior to proceeding further. 108 | 109 | Sample parsing multiple messages 110 | ```java 111 | SwiftMsgProcessor processor = new SwiftMsgProcessor(); 112 | 113 | String messageContentMaster = //... mt700 114 | String messageContent1 = //... mt701 115 | String messageContent2 = //... mt701 116 | String messageContent3 = //... mt701 117 | 118 | //the result message will be an MT700 message holding the content of all messages passed as the arguments 119 | SwiftMessage result = processor.ParseMsgStringsToObject(Arrays.asList(messageContentMaster, messageContent1, messageContent2, messageContent3)); 120 | ``` 121 | 122 | #### BuildMsgStringFromObject method 123 | Use this method to construct a SWIFT message before sending it to the SWIFT network. It takes as input a SwiftMessage object that has been constructed by the programmer (see next section) and performs all the necessary formatting to produce a message with valid syntax. In other words programmer just assigns the right values to the tags of the SwiftMessage object and calls the method to build the SWIFT string ready to be transmitted to the SWIFT network. To enforce or check semantic rules such as tag sequence required fields etc, the SwiftMessageValidator, another PaymentComponents component should be called. To build a valid Swift string so that it can be transmitted to the SWIFT network follow the steps below: 124 | 125 | ```java 126 | //Create the component as below 127 | SwiftMsgProcessor SMP = new SwiftMsgProcessor(); 128 | 129 | //Create the "SwiftMessage" object to set its Tags. 130 | SwiftMessage smObj = new SwiftMessage(); 131 | 132 | //Set all the needed tags for the message. Start with block1 and block2 133 | smObj.setArgApplid("F"); 134 | smObj.setArgServid("01"); 135 | smObj.setArgLTaddrBlk1 ("FFFFGRA0AXXX"); 136 | smObj.setArgInoutind("I"); 137 | smObj.setArgMsgtype("103"); 138 | smObj.setArgLTaddrBlk2("UUUUGRA0AXXX"); 139 | smObj.setArgMsgprior("N"); 140 | 141 | smObj.setServiceTypeIdentifier("111"); 142 | smObj.setUniqueEndToEndTrxRef("01911b73-1f15-4932-934a-609149301922"); 143 | smObj.setPaymentControlsInformation("/AAA/someScreeningInfo"); 144 | 145 | //When finished with block1 and block2 continue with block4, 146 | //Create vector to store the line data of tag 20. 147 | java.util.Vector vec20 = new java.util.Vector(); 148 | //add one data line 149 | vec20.addElement("12345678"); 150 | // Create Tag object to store name and data line for tag 20. 151 | Tag tag20 = new Tag("20",vec20); 152 | //add tag 20 to block4 vector 153 | smObj.getBlock4().addElement(tag20); 154 | 155 | //Do the same for 52A 156 | java.util.Vector vec52A = new java.util.Vector(); 157 | vec52A.addElement("/12345"); // add first data line 158 | vec52A.addElement("ABCDE"); // add second data line 159 | Tag tag52A = new Tag("52A", vec52A); 160 | smObj.getBlock4().addElement(tag52A); //add tag 52A to block4 vector 161 | 162 | //Now build the outgoing message in a string format as SWIFT expects it. 163 | String message = SMP.BuildMsgStringFromObject(smObj); 164 | 165 | You are done! Now variable message contains all the Tags of the "SwiftMessage" – including the necessary carriage return linefeeds - and looks like the string below 166 | {1:F01FFFFGRA0AXXX0000000000}{2:I103UUUUGRA0AXXXN}{3:{111:111}{121:01911b73-1f15-4932-934a-609149301922}{434:/AAA/someScreeningInfo}}{4: 167 | :20:12345678 168 | :52A:/12345 169 | ABCDE 170 | -} 171 | ``` 172 | 173 | #### SwiftMessage object 174 | This is the heart of the component. It is the object that the parser populates with the values of the incoming message. This is the object the programmer accesses to get the values. It keeps the message information in vectors of Tag objects (see next section) and provides various methods to access and manipulate the object. There are more than 70 methods available. The programmer will need a small subset of them. Some of those are described below. The rest are straightforward in their usage and can be seen in the JavaDoc. 175 | 176 | ##### clear() 177 | Clears all the fields of the "SwiftMessage". This includes all block vectors and message property fields. 178 | ```java 179 | smobj.clear(); 180 | ``` 181 | ##### displayMessage() 182 | Displays at the standard output a text representation of the SWIFT message filled Tags 183 | ```java 184 | smobj.displayMessage(); 185 | The above will display as the output the following 186 | Applid : F 187 | Servid : 01 188 | LTaddrBlk1: FFFFGRA0AXXX 189 | Sesno : 0000 190 | Osn : 000000 191 | Inoutind : I 192 | Msgtype : 103 193 | LTaddrBlk2 : UUUUGRA0AXXX 194 | Msgprior: N 195 | 113 : ABCD 196 | 20 : 494930/DEV 197 | 32A : 020527EUR1958,47 198 | 21 : 2222222 199 | 50 : BIODATA GJBH 200 | 50 : ZURICH 201 | 59 : S.T JANSSEN 202 | 59 : LEDEBOERSTRAAT 203 | 59 : AMSTERDAM 204 | ``` 205 | 206 | ##### getArgApplid() 207 | Returns "Application Identifier" of the SWIFT message. 208 | ```java 209 | smobj.getArgApplid(); 210 | ``` 211 | The above returns "F". 212 | 213 | ##### getArgLTaddrBlk1() 214 | Returns "LT address" of the SWIFT message (Basic Header Block (1)) 215 | ```java 216 | smobj.getArgLTaddrBlk1(); 217 | ``` 218 | The above returns "FFFFGRA0AXXX". 219 | 220 | ##### getArgMsgtype() 221 | Returns "Message Type" of the SWIFT message 222 | ```java 223 | smobj.getArgMsgtype(); 224 | ``` 225 | The above returns "103". 226 | 227 | ##### getBlockx() 228 | Returns a vector with all the tags of block x of the message. 229 | ```java 230 | smobj.getBlock1(); 231 | ``` 232 | The above returns 233 | ``` 234 | [Applid[0]:F, Servid[0]:01, LTaddrBlk1[0]:FFFFGRA0AXXX, Sesno[0]:0000, Osn[0]:000000] 235 | ``` 236 | 237 | ##### getMessageAsVector() 238 | Returns a vector containing all the tags of the message in their original sequence. 239 | ```java 240 | smobj.getMessageAsVector(); 241 | ``` 242 | The above returns 243 | ``` 244 | [Applid[0]:F, Servid[0]:01, LTaddrBlk1[0]:FFFFGRA0AXXX, Sesno[0]:0000, Osn[0]:000000,Inoutind[0]:I , Msgtype[0]:103, LTaddrBlk2[0]:UUUUGRA0AXXX, Msgprior[0]:N, 3[0]:ABCD , 20[0]:494930/DEV, 32A[0]:020527EUR1958,47, 21[0]:2222222, 50[0]:BIODATA GJBH 50[1]:ZURICH, 59[0]:S.T JANSSEN 59[1]:LEDEBOERSTRAAT 29 59[2]:AMSTERDAM] 245 | ``` 246 | 247 | ##### getNumbertOfTagInstances(String name) 248 | Returns the number of instances for the specific Tag. If tag does not exist then it returns 0. 249 | ```java 250 | smobj.getNumbertOfTagInstances("20"); 251 | ``` 252 | The above returns the integer 1. 253 | 254 | ##### getTag(String name) 255 | ```java 256 | smobj.getTag("20"); 257 | ``` 258 | The above returns a Tag object with all the information of tag 20 of the message. 259 | A tag may exist more than once. This method returns the first instance of the tag. 260 | 261 | ##### getTag(String tagName, int index) 262 | Returns the specified (if exists) instance of the tag with name tagName in the message (i.e. if tag20 exists three times in a message then getTag("20",1) returns the first occurrence of tag20 and getTag("20",3) returns the third. If tag does not exist or index is greater than the maximum number of tag representation then it returns null. 263 | ```java 264 | smobj.getTag("20",1); 265 | ``` 266 | The above returns a Tag object having all the information of tag 20 of the message. 267 | 268 | ##### isblockEmpty(int index) 269 | Returns true when the specific block vector has no Tags. 270 | ```java 271 | isblockEmpty(4); 272 | ``` 273 | The above returns false. 274 | 275 | ##### getUniqueEndToEndTrxRef() 276 | Returns the "Unique end-to-end transaction Reference or UETR (121) " of a SWIFT message 277 | ```java 278 | smobj.getUniqueEndToEndTrxRef(); 279 | ``` 280 | 281 | ##### getPaymentControlsInformation() 282 | Returns the "Payment Controls Info (434) " of a SWIFT message 283 | ```java 284 | smobj.getPaymentControlsInformation(); 285 | ``` 286 | 287 | ##### getServiceTypeIdentifier() 288 | Returns the "Service Type Identifier (111) " of a SWIFT message 289 | ```java 290 | smobj.getServiceTypeIdentifier(); 291 | ``` 292 | 293 | ##### setArgLTaddrBlk1(String newArgLTaddrBlk1) 294 | Sets the text part for "LT address" of the SWIFT message (Basic Header Block (1)) 295 | ```java 296 | smobj.setArgLTaddrBlk1("FFFFGRA0AXXX"); 297 | ``` 298 | The above sets FFFFGRA0AXXX to LTaddrBlk1 property. 299 | 300 | ##### setArgMsgtype(String newArgMsgtype) 301 | Sets the "Message Type" of a SWIFT message 302 | ```java 303 | smobj.setArgMsgtype("103"); 304 | ``` 305 | The above sets 103 to MsgType property. 306 | 307 | ##### setBlockx(Vector newBlockx) 308 | Sets a vector containing all the tags of a specific block in their original sequence. 309 | ```java 310 | java.util.Vector tmpVec = new java.util.Vector(); 311 | tmpVec.addElement("I"); 312 | tmpVec.addElement("103"); 313 | tmpVec.addElement("UUUUGRA0AXXX"); 314 | tmpVec.addElement("N"); 315 | SwiftMessage smobj = new SwiftMessage(); 316 | smobj.setBlock2(tmpVec); 317 | ``` 318 | The above sets tmpVec vector to block2 property. 319 | 320 | ##### setUniqueEndToEndTrxRef(String newUniqueEndToEndTrxRef) 321 | Sets the "Unique end-to-end transaction Reference or UETR (121) " of a SWIFT message 322 | ```java 323 | smobj.setUniqueEndToEndTrxRef("01911b73-1f15-4932-934a-609149301922"); 324 | ``` 325 | The above sets the UETR to the 121 field of MT103. 326 | 327 | ##### setServiceTypeIdentifier(String newServiceTypeIdentifier) 328 | Sets the "Service Type Identifier (111)" of a SWIFT message 329 | ```java 330 | smobj.setServiceTypeIdentifier("111"); 331 | ``` 332 | The above sets the Service Type Identifier to the 111 field of Block 3. 333 | 334 | 335 | ##### setPaymentControlsInformation(String newPaymentControlsInformation) 336 | Sets the "Payment Controls Information (434) " of a SWIFT message 337 | ```java 338 | smobj.setPaymentControlsInformation("/AAA/someScreeningInfo"); 339 | ``` 340 | The above sets the Payment Controls Info to the 434 field of MT103. 341 | 342 | ##### toString() 343 | Returns the String representation of this SWIFT Message 344 | ```java 345 | smobj.toString(); 346 | ``` 347 | The above returns the string 348 | ```java 349 | Applid[0]:F Servid[0]:01 LTaddrBlk1[0]:FFFFGRA0AXXX Sesno[0]:0000 Osn[0]:000000 Inoutind[0]:I Msgtype[0]:103 LTaddrBlk2[0]:UUUUGRA0AXXX Msgprior[0]:N 113[0]:ABCD 20[0]:494930/DEV 32A[0]:020527EUR1958,47 21[0]:2222222 50[0]:BIODATA GJBH 50[1]:ZURICH 59[0]:S.T JANSSEN 59[1]:LEDEBOERSTRAAT 29 59[2]:AMSTERDAM 350 | ``` 351 | 352 | ##### autoReply(final String confirmationId, final String statusCode, final String reasonCode, final String forwardTo, final String settlementCode, final String clearingSystem) 353 | Since release 20.2.0, and in the paid version only, you have the option to create an MT199, with acctepance/rejection/in process status codes, for an MT103, according to the Univeral Confirmations rule book. See the [gist here](https://gist.github.com/pc14-alexandrakis/4ec4ac8fbb8cffcbe9a7ea5605a4747d) 354 | 355 | #### Tag object 356 | This object is used to handle the message tags. Its properties are name and data. Name is a string containing the name of the tag (i.e. 52A). Data is a vector of strings containing the lines of tag information. It is accompanied by a number of utility methods to set and get the values or do other manipulation. Only the most important of these methods are described below. The rest are straight forward in usage and can be seen in the JavaDoc. 357 | 358 | ##### addDataLine(String line) 359 | Adds a string containing the information of a Tag line to Data vector. 360 | ```java 361 | tag59.addDataLine("S.T JANSSEN"); 362 | tag59.addDataLine("LEDEBOERSTRAAT 29"); 363 | tag59.addDataLine("AMSTERDAM"); 364 | ``` 365 | 366 | ##### clearAll() 367 | Clears the Tag properties name and data. 368 | ```java 369 | tag59.clearAll(); 370 | ``` 371 | 372 | ##### getData() 373 | Returns a vector of strings containing the tag information. Each line of information is a vector element. 374 | ```java 375 | Vector tempVec = tag59.getData(); 376 | ``` 377 | Now tempVec vector contains the value: 378 | ```[S.T JANSSEN, LEDEBOERSTRAAT 29, AMSTERDAM]``` 379 | ##### getDataLineAt(int index) 380 | Returns a string containing the information of the specified line of the tag. e.g. Assuming that in the original tag looks like the following 381 | ``` 382 | :59:S.T JANSSEN 383 | LEDEBOERSTRAAT 29 384 | AMSTERDAM 385 | ``` 386 | 387 | ``` 388 | String data59l0 = tag59.getDataLineAt(0); 389 | String data59l2 = tag59.getDataLineAt(2); 390 | ``` 391 | The above will set "S.T JANSSEN" to data59l0 and "AMSTERDAM" to data59l2 variable. 392 | 393 | ##### getName() 394 | Returns a string containing the name of the tag. 395 | ```java 396 | tag59.getName(); 397 | ``` 398 | The above will return "59". 399 | 400 | ##### getNumberOfDataLines() 401 | Returns the number of lines that a tag has. Assuming that in the original tag looks like the following 402 | ``` 403 | :59:S.T JANSSEN 404 | LEDEBOERSTRAAT 29 405 | AMSTERDAM 406 | ``` 407 | ```java 408 | tag59.getNumberOfDataLines(); 409 | ``` 410 | The above returns the integer 3. 411 | 412 | ##### insertDataLineAt(String line, int index ) 413 | Inserts the specified line in Data vector at the specified index. Each line with an index greater or equal to the specified index is shifted upward. 414 | Throws InvalidActionAttemptedException when the index is invalid. Assuming that in the original tag looks like the following 415 | ``` 416 | :59:S.T JANSSEN 417 | LEDEBOERSTRAAT 29 418 | AMSTERDAM 419 | ``` 420 | ```java 421 | tag59.InsertDataLineAt("HOLLAND",2); 422 | ``` 423 | Now tag 59 looks like the following 424 | ``` 425 | :59:S.T JANSSEN 426 | LEDEBOERSTRAAT 29 427 | HOLLAND 428 | AMSTERDAM 429 | ``` 430 | 431 | ##### isEmpty() 432 | Returns true when Tag infromation is empty. 433 | ```java 434 | tag59.isEmpty(); 435 | ``` 436 | 437 | ##### setData(Vector Data) 438 | Sets the Data property (Vector) value. 439 | 440 | ```java 441 | Vector tmpVec = new Vector(); 442 | tmpVec.addElement("S.T JANSSEN"); 443 | tmpVec.addElement("LEDEBOERSTRAAT 29"); 444 | tmpVec.addElement("AMSTERDAM"); 445 | Tag tag59 = new Tag(); 446 | tag59.setName("59"); 447 | tag59.setData(tmpVec); 448 | ``` 449 | 450 | ##### setDataLineAt(String line, int index ) 451 | Inserts the specified line in Data vector at the specified index overwriting the old line. Throws InvalidActionAttemptedException when the index is invalid. 452 | Assuming that in the original tag looks like the following 453 | ``` 454 | :59:S.T JANSSEN 455 | LEDEBOERSTRAAT 29 456 | AMSTERDAM 457 | ``` 458 | ```java 459 | tag59.setDataLineAt("HOLLAND",1); 460 | ``` 461 | Now tag 59 looks like the following 462 | ``` 463 | :59:S.T JANSSEN 464 | HOLLAND 465 | AMSTERDAM 466 | ``` 467 | 468 | ##### setName(String newName) 469 | Sets the Tags Name property (String) value. 470 | ```java 471 | Tag tag59 = new Tag(); 472 | tag59.setName("59"); 473 | ``` 474 | 475 | ##### setTag(String name, Vector data) 476 | Sets Tags properties name and data. 477 | ```java 478 | Vector tmpVec = new Vector(); 479 | tmpVec.addElement("S.T JANSSEN"); 480 | tmpVec.addElement("LEDEBOERSTRAAT 29"); 481 | tmpVec.addElement("AMSTERDAM"); 482 | Tag tag59 = new Tag(); 483 | tag59.setTag("59",tmpVec); 484 | ``` 485 | 486 | ##### toString() 487 | Returns the String representation of this Tag. 488 | ```java 489 | tag59.toString(); 490 | ``` 491 | The above returns 492 | ``` 493 | 59[0]:S.T JANSSEN 59[1]:LEDEBOERSTRAAT 29 59[2]:AMSTERDAM 494 | ``` 495 | 496 | ##### Tag description 497 | **In the paid version**, you can now get each tag's description by using the `getDescription` method of the `Tag` object. So, if you have the tag object and you know the MT message it belongs to, you can use the following code 498 | ```java 499 | Tag tag20 = smObj.getTag("20"); 500 | //Get specific tag from the SwiftMessage object 501 | System.out.println("Tag " + tag20.getName() + " " + 502 | " " + tag20.getValueAsString()); 503 | System.out.println("Description: " + 504 | tag20.getDescription(smObj.getArgMsgtype())); 505 | ``` 506 | and it will return the description of the tag. `MT_MESSAGE_NUMBER` is the mt message e.g. 103, 202, 700 etc. We have created a [gist here](https://gist.github.com/pc14-alexandrakis/067c319e37deec5bb357d526a953ebf4) 507 | 508 | #### The use of Repetitive Sequences in SWIFT messages 509 | Repetitive Sequences are used in certain SWIFT messages to enable groups of tags to be repeated more than once. The component represent these repetitive sequences by means of the `RepSeq` object. This means that a message can contain a combination of simple Tags and Repetitive Sequences, each of which is defined as optional or mandatory. 510 | The RepSeq object includes simple Tag objects (i.e. tag 21 will be included in this RepSeq object as a simple Tag object), or even other (nested) RepSeq objects since there are SWIFT messages that are more complicated and include repetitive sequences inside repetitive sequences. 511 | Check [here](src/main/java/com/paymentcomponents/mt/demo/demo/BuildMT101_1.java) how the Reppetitive Sequnece B, is being added to the Swift Message Object. 512 | ##### Note 513 | If we want to build a "Block" with a repetitive sequence named Rep1 (BlockRepSeq Rep1) which has another repetitive sequence named Rep2 inside it (BlockRepSeq Rep2), then: We create the second (inner) repetitive sequence (BlockRepSeq Rep2), we fill it with values, then we put it inside the first BlockRepSeq calling the addSubSequence method of the BlockRepSeq class, like this: 514 | ```java 515 | Rep1.addSubSequence(Rep2); 516 | ``` 517 | 518 | #### SwiftMsgValidator object 519 | SwiftMsgValidator has just one method encapsulating the entire functionality needed to validate and build a SWIFT MT message. 520 | ##### Validating Objects - validateMsg method 521 | This method takes as input a SwiftMessage object that has been manually constructed by a developer [(as here)](src/main/java/com/paymentcomponents/mt/demo/demo/BuildMT101_1.java), performs SWIFT validations and returns a list of error messages (if the message it correct, then an empty list is returned; null is never returned). The `validateMsg` will also return a SwiftMessage object that might be sligthly different from the given one. For example the SK reorder the tags if they are in a wrong position in the message, so it's recommented to use the returned message object after the validation. 522 | 523 | #### Splitting Oversized Messages 524 | Since version 14.1.0, SwiftMsgValidator is able to split oversized messages if they belong in one of the supported categories (MT700, MT710, MT720). The validator will handle splitting automatically depending on the category of the input message. 525 | In case where the message type falls into one of the supported categories, additional sequence messages will be produced based on the input message type, and based on the following rules (applied in this order): 526 | If one or more of fields 45A, 46A, 47A of the input message have more lines than the allowed limit, according to the SWIFT standard, each field will be broken into multiple chunks, each being encapsulated inside a sequence message. 527 | If the message as a whole has more characters than the allowed limit per message, according to the SWIFT standard, each of the fields 45A, 46A, 47A will begin being transferred into new sequence messages (priority is given to the tags that have the most lines) up until the point where the message is not oversized anymore. 528 | In the case where the message will be split, pagination in field 27 will be handled automatically, and field 20 of the sequence messages will hold the value of field 20 of the original input message. 529 | Maximum amount of sequence messages that the original input message should be split into is 8 (to result in 9 messages along with the input one); this will allow to keep the syntax of the field 27 intact. If the original message is required to be split into a number of sequence messages greater than 8, the library will still proceed with the splitting, but the validation that follows will report errors in all messages regarding field 27, where the allowed format is 1!n/1!n. 530 | After splitting, all messages will be validated, and errors will be reported. 531 | 532 | #### Validation Results 533 | ##### getSwiftMessage() and getSwiftMessages() 534 | Since version 14.1.0, the SwiftValidObj object is able to return multiple message objects as a result of the validation process. The legacy getSwiftMessage method will always return the first message of the resulting array of message objects. The new method `getSwiftMessages` will return an array of SwiftMessages that are the result of the validation process. The validation error list inside the SwiftValidObj will hold errors that concern the entire array of messages. 535 | 536 | ##### getMessageIndex() 537 | Since version 14.1.0, the ValidationError objects have a new method, getMessageIndex which returns an integer indicating the index in the input array of the message this error corresponds to. 538 | 539 | #### XMLWriter object 540 | It is used to serialize the SwiftMessage object to a generic XML document. It does it with the use of buildXML method. It also uses Java Document Object Model (JDOM) that is delivered with the deployment jar. These open source classes can also be downloaded from www.jdom.org. 541 | Note: This article talks about gr.datamation.mt.swift2xml.XMLWriter. Avoid confusing with javax.sql.rowset.spi.XmlWriter. 542 | 543 | ##### buildXML method 544 | It is a method within XMLWriter object, and is the only one used to build a generic XML document. It has three inputs: 545 | 1. The SMP object after it has been created by the SMP parser 546 | 2. String containing the path where to write the XML Document (if the String is null, the current path will be used) 547 | 3. String containing the XML filename to be created. 548 | 549 | The method first validates the file name for syntax errors, to ensure that the XML will have alphanumeric characters. 550 | Example 551 | Check [here](src/main/java/com/paymentcomponents/mt/demo/demo/ConvertMT2XML.java) how to create XMLs from an MT 552 | 553 | ##### Tag61.splitTag() 554 | Since Tag61 includes many pieces of information, library offers a parsing of the Tag's fields. You get a map with fields name and field value. 555 | The below code 556 | ```java 557 | SwiftMessage message = new Msg940(); 558 | 559 | message.getBlock4().clear(); 560 | message.getBlock4().addAll(createTagList( 561 | ":61:1501080109D184,50NCHKNONREF ////D101 100771017\nCHQ WITHDR-DISSE" 562 | )); 563 | for (Object object : message.getBlock4()) { 564 | if (object instanceof Tag) { 565 | Tag tag = (Tag) object; 566 | if (tag.getName().equals("61")) { 567 | System.out.println(Tag61.splitTag(tag.getValueAsString())); 568 | } 569 | 570 | } 571 | } 572 | ``` 573 | results to 574 | ``` 575 | {dcMark=D, accOwnerRef=NONREF //, amount=184,50, entryDate=0109, idCode=CHK, accServRef=D101 100771017, valueDate=150108, supDetails=CHQ WITHDR-DISSE, fundsCode=null, tranType=N} 576 | ``` 577 | 578 | 579 | ### More features are included in the paid version like 580 | 581 | [MT199 according to Universal Confirmations rules ](https://gist.github.com/pc14-alexandrakis/4ec4ac8fbb8cffcbe9a7ea5605a4747d) 582 | 583 | [Tag Descriptions](https://gist.github.com/pc14-alexandrakis/067c319e37deec5bb357d526a953ebf4) 584 | 585 | [Trade Finance Message Build](https://gist.github.com/apolichronopoulos/5039d48fca54791f0952799357482a14) 586 | 587 | [Trade Finance Message Parse](https://gist.github.com/apolichronopoulos/ab184196d3ae26d231b9bb031f1a9c5a) 588 | 589 | ### Error Codes Appendix 590 | 591 | |Code|Error Message| 592 | |---|---| 593 | |SV00|Swift Validator Error| 594 | |SV01|Field length exceeded| 595 | |SV02|Code word must be placed between slashes '/'| 596 | |SV03|Code word length exceeded| 597 | |SV04|Field not valid| 598 | |SV05|Amount must have a comma| 599 | |SV06|Amount must have only one comma| 600 | |SV07|Party Identifier format not valid| 601 | |SV08|Party Identifier must start with a slash '/'| 602 | |SV09|Does not contain a valid code word| 603 | |SV10|- Field must start with a double slash '//' or a '/'code word'/'.
- Field must start with a double slash '//'| 604 | |SV11|Maximum lines for this option are 2| 605 | |SV12|BIC is mandatory| 606 | |SV13|- At least one line must be present.
- Minimum lines for this option are: | 607 | |SV14|Maximum lines for this option are 5| 608 | |SV15|Option must be| 609 | |SV16|Mandatory Tag is missing| 610 | |SV17|Field must start with a '/'code word'/'| 611 | |SV18|Field must start with a double slash '//'| 612 | |SV19|Narrative text must not start with a slash| 613 | |SV20|Account line must start with a slash '/'| 614 | |SV21|Maximum lines for this option| 615 | |SV22|- Use a '/' to separate Message Index and Total fields.
- Use a '/' to separate Statement number and Sequence number| 616 | |SV23|- Message Index field must not be more than 5 digits.
- Statement number must not be more than 5 digits or less than 1 digit| 617 | |SV24|Total field length must be 4 digits| 618 | |SV25|- Message Index field must be numeric.
- Statement number must be numeric| 619 | |SV26|- Total field must be numeric. - Sequence number must be numeric.
- Number of days field is invalid or empty.
- Invalid function code word| 620 | |SV27|Use a '/' to separate Type and Market fields| 621 | |SV28|Use a '/' to separate Market and Data fields| 622 | |SV29|Repetitive sequence must not be present in message type| 623 | |SV30|At least one Repetitive sequence must be present| 624 | |SV31|Narrative text must start with a slash '/'| 625 | |SV32|Narrative text must be present| 626 | |SV33|Rate must have a comma| 627 | |SV34|Rate must have only one comma| 628 | |SV35|Rate must not start with a comma| 629 | |SV36|Field length must be : | 630 | |SV37|Both the account number and BEI line must be present| 631 | |SV38|The second character must be a slash '/'| 632 | |SV39|Invalid Session Number or ISN| 633 | |SV40|Message Type Number and Date fields must be present| 634 | |SV41|Country field is missing or invalid| 635 | |SV42|Account is mandatory| 636 | |SV43|Index field must not be more than 1 digit| 637 | |SV44|Partial Code Line must not be more than 33 digits| 638 | |SV45|Check the format option for field 50a (Instruction Party or Creditor)| 639 | |SV46|Tag 50a must not be present more than 2 times| 640 | |SV47|Second line must be present| 641 | |SV48|Tag 52 must not be present more than 2 times| 642 | |SV49|Check the format option for field 50a (Instruction Party or Ordering Customer)| 643 | |SV50|- Total field length must not be more than 5 digits.
- Sequence number must not be more than 5 digits or less than 1 digit.
- Sequence number must not be more than 2 digits or less than 1 digit| 644 | |SV51|Additional Information must start with a slash '/'| 645 | |SV52|BEI is mandatory| 646 | |SV53|Application Identifier is missing (Basic Header Block)| 647 | |SV54|Application Identifier is invalid (Basic Header Block)| 648 | |SV55|Service Identifier is missing (Basic Header Block)| 649 | |SV56|Service Identifier is invalid (Basic Header Block)| 650 | |SV57|LT Identifier is missing (Basic Header Block)| 651 | |SV58|LT Identifier is invalid (Basic Header Block)| 652 | |SV59|Session Number is invalid (Basic Header Block)| 653 | |SV60|Sequence Number is invalid (Basic Header Block)| 654 | |SV61|Input/Output Identifier must be 'I' or 'O' (Application Header Block)| 655 | |SV62|Message Type is missing (Application Header Block)| 656 | |SV63|Message Type is invalid (Application Header Block)| 657 | |SV64|Receiver Address is missing (Application Header Block)| 658 | |SV65|Receiver Address is invalid (Application Header Block)| 659 | |SV66|Message Priority is missing (Application Header Block)| 660 | |SV67|Message Priority is invalid (Application Header Block)| 661 | |SV68|Delivery Monitoring must be '1' or '3' (Application Header Block)| 662 | |SV69|Delivery Monitoring must be '2' or blank (Application Header Block)| 663 | |SV70|Delivery Monitoring must not be used (Application Header Block)| 664 | |SV71|Obsolescence period must not be used (Application Header Block)| 665 | |SV72|Obsolescence period must be '003' (Application Header Block)| 666 | |SV73|Obsolescence period must be '020' (Application Header Block)| 667 | |SV74|Obsolescence period is missing (Application Header Block).| 668 | |SV75|Swift Validator cannot handle this Type of Message| 669 | |SV76|Message Type is missing from the message| 670 | |SV77|At least one line of the subfield Name & Address is required| 671 | |SV78|Invalid format. Format is :| 672 | |SV78|Invalid format. Field must be empty| 673 | |SV79|Mandatory Subfield is missing or invalid| 674 | |SV79|Subfield is invalid| 675 | |SV80|Field length invalid| 676 | |SV81|Field must start with symbol ' : '| 677 | |SV82|Field after qualifier , must contain double slash '//'| 678 | |SV83|Use a '/' to separate Function and Subfunction fields| 679 | |SV84|Use a '/' to separate Qualifier , Data Source Scheme and Proprietary Code fields| 680 | |SV85|Use a '/' to separate Function and Name & Address fields| 681 | |SV86|Subfield must start with a slash '/'.| 682 | |SV87|Frequency/Timing in Period subfields are missing or invalid| 683 | |SV88|Use a '/' to separate Frequency and Timing in Period subfields| 684 | |SV89|Use a '/' to separate Timing in Period and Day subfields| 685 | |SV90|When subfield Day is present, it must consist of 2 numbers| 686 | |SV93|- Use a '/' to separate Qualifier , Data Source Scheme, Account Type Code and Account Number.
- Use a '/' to separate Code and Function| 687 | |SV94|Qualifier of Tag is Mandatory| 688 | |SV95|Invalid length of Qualifier| 689 | |SV96|Codes must start and end with a slash '/'.| 690 | |SV97|When an ISIN identifier is not used it is strongly recommended that one of the following codes be used at the first four characters of the description of security : The ISO two digit country code or /TS/ or /XX/.| 691 | |SV98|The decimal comma occurs more than one time(s)| 692 | |SV99|In field 72, if the code /INS/ is used at the beginning of a line, it must be followed by a valid financial institution BIC and be the only information on that line| 693 | |SV100|Missing Mandatory Sequence(s)| 694 | |SV101|Number of Days specifies the number of days notice (for example, 07). It must only be used when Function is NOTICE| 695 | |SV102|In field 72, when first line starts with one of the codes /RETN/ or /REJT/, the third line must start with the code /MREF/ and follow the format : 16x| 696 | |SV103|In field 72, when first line starts with one of the codes /RETN/ or /REJT/, code words must not be duplicated| 697 | |SV104|In field 72, when first line starts with one of the codes /RETN/ or /REJT/, code words on the lines 2-6 must be in proper sequence : reason code (format 2!c2!n), MREF, TREF (optional), CHGS (optional), TEXT (optional)| 698 | |SV105|In field 72, when first line starts with one of the codes /RETN/ or /REJT/, the information component following all code words, except for reason code (for example, /AC01/) is mandatory. This component must not be empty, nor consist entirely of blanks| 699 | |SV106|In field 72, information following the code /RETN/ or /REJT/ must consist of the field causing the reject or return, and possibly other message elements (for example, letter option and sequence identification), which may be helpful to the sender in isolating the specific error; format : 2!n[1!a][/2c].| 700 | |SV107|In field 72, when first line starts with one of the codes /RETN/ or /REJT/, each line must begin with the format : /'code word'/.| 701 | |SV108|One of the following codes must be used:| 702 | |SV109|Slash absent or in wrong position| 703 | |SV110|Left part not present| 704 | |SV111|Field must contain the following codes : | 705 | |SV112| must contain the following codes : | 706 | |SV113|Narrative text must not start with a slash and, if used, must begin on a new line and be the last information in the field| 707 | |SV115|At least one of the following codes should be used, placed between slashes \ /\ : | 708 | |SV116|For field in sequence the following values must not be repeated : | 709 | |SV117|At least one of fields 95P, 95Q, 95R must be present in sequence | 710 | 711 | 712 | ### NVD Data Usage 713 | 714 | This software uses data retrieved from the [National Vulnerability Database (NVD)](https://nvd.nist.gov/) API. 715 | **This product uses the NVD API but is not endorsed or certified by the NVD.** 716 | 717 | - NVD content is provided on an "as-is" and "as-available" basis by NIST. 718 | - Data returned by this SDK may be cached, normalized, or transformed before being exposed to clients. 719 | - Users of this SDK are responsible for supplying and managing their own NVD API key in accordance with the [NVD Terms of Use](https://nvd.nist.gov/developers). 720 | - Excessive use without caching may result in rate limiting or blocked access by NVD. 721 | - For enterprise-scale usage, consult NVD’s official guidance on mirroring or bulk data feeds. --------------------------------------------------------------------------------