Processing ACK/NAK
Model and API to process SWIFT ACK notifications
The acknowledge (ACK) or non-acknowledge (NAK) are service messages sent by the SWIFT interface to the user application to notify an outgoing message was accepted or not. The acceptance mainly depends on the message being standard compliant.
Note that receiving an ACK does not mean the message was effectively delivered to the receiver, it is just a notification indicating if the SWIFT interface accepted the message as valid and entered the message in the network.
In order to handle the acknowledges, for instance to change the message status in your application, you have to do some matching of the received notifications and the original sent message. This matching process is not provided as an out-of-the-box feature because it involves querying the application database or file system to find candidates. Anyway, the building blocks to accomplish this task is well supported by the API.
ACK structure
The structure of a service message is similar to the structure of the regular user message, with a different service id and the block 4 composed of inner sub-blocks instead of fields in lines. Within the service messages the ACK/NAK notifications are identified with service id 21.
The following example is the basic structure of a an ACK message:
{1:F21LITEBEBBAXXX0066000079}{4:{177:1104180901}{451:0}}
Field 451 in the system message indicates whether the message was acked (0) or nacked (1).
When nacked the field 405 will also contain a field indicating the error code. For example:
{405:T33002}
The SwiftParser
can seamlessly parse this messages and the SwiftMessage
objects provides the same API to retrieve the
service message fields as in a regular user message.
The methods SwiftMessage#isAck()
and SwiftMessage#isNack()
can be used to determine in the message is a notification
and if it is positive or not.
Depending on the SWIFT interface, the file containing the notification can be different. However, in all cases it is composed of the service message plus some reference of the original message. This reference can be for the original message MUR or a full copy of the original message.
Matching ACK with full copy of original message
The most common structure is the system message with the ACK/NAK followed by a full copy of the original message as in this example:
{1:F21LITEBEBBAXXX0066000079}{4:{177:1104180901}{451:0}}{1:F01LITEBEBBAXXX0066000079}{2:I999LITEBEBBXXXXN}{4:
:20:TESTREF1
:79:This is text line 1
-}{5:{CHK:7602B010CF31}{TNG:}}
The following section describe the normal procedure to detect and pair the acknowledge with the original message and the convenient API to use for the task.
First step would be to determine if a received message is and ACK/NAK. Once the message is parsed as SwiftMessage, this methods can be use to verify if it is a regular FIN user to user message or if it is an ACK/NAK notification:
Second step would be to split the ACK/NAK information from the identification of the original message. This example uses the most common format which is the ACK/NAK message followed by the full copy of the original message (this is the for example the format used by SAA AFT, or SA Lite autoclient). To parse both the ACK/NAK and the original copy:
Swiftparser parser = new SwiftParser()
SwiftMessage ack = parser.parse(msg)
SwiftMessage original = parser.parse(ack.getUnparsedTexts().getAsFINString())
Finally, to match the acked/nacked message with its original message, a query on candidates must be done (usually searching for messages for the same day and same message type). Finding candidates is not covered here because it involves searching the application database or reading the sent messages from a file system directory. Once the candidates are retrieved, the matching can be accomplished like this:
AckMessageComparator comparator = new AckMessageComparator();
for (SwiftMessage candidate : candidates) {
if (comparator.compare(original, candidate) == 0) {
//candidate is the message acked/nacked
}
}
Matching ACK with MUR
Depending on the acknowledge notification source the structure of the message may differ. For instance the full copy of the original message could be missing. However, other kind of reference to the original acked/nacked message must be there, for example the MUR (Message User Reference) like this:
{1:F21LITEBEBBAXXX0066000079}{4:{177:1104180901}{451:0}{108:FOO16101900001}}
So to match the original message, instead of using the full message copy and the AckMessageComparator
, a message with
the same MUR must be found:
for (SwiftMessage candidate : candidates) {
if (StringUtils.equals(ack.getMUR(), candidate.getMUR())) {
candidate is the message acked/nacked
}
}
Matching ACK with UUID
If neither the original message copy nor the MUR are present in the ACK/NAK, the UUID (Unique Identifier) may be present. In this scenario the first step is to strip relevant information from the UUID such as the receiver address, message type and the reference. This fields should then be enough to perform a direct query in the application database to retrieve the unique original message matching the criteria.
Related API documentation can be found online at SwiftMessage
.