Case Study 2: Message Queue Integration for Real-Time Notifications
Background
MidWest Community Bank processes over 200,000 debit card transactions daily through its COBOL-based authorization system running on IBM z/OS. Customers have been requesting real-time transaction alerts -- push notifications on their mobile devices within seconds of a purchase. The current system processes transactions in batch overnight, meaning customers discover unauthorized charges only when they check their statements the next morning.
The fraud team also needs real-time visibility. Their cloud-based fraud analytics platform can detect suspicious patterns across multiple cards, but only if it receives transaction data as it happens, not in overnight batch feeds.
The architecture team selects IBM MQ as the integration backbone. When the COBOL authorization program approves a transaction, it publishes an event message to an MQ topic. Downstream subscribers -- the mobile notification service and the fraud analytics platform -- consume these events independently and in real time. The COBOL program's performance is minimally impacted because MQ put operations are asynchronous and take only 1-2 milliseconds.
This case study implements the complete flow: the COBOL transaction publisher, the JSON message format, the MQ configuration, and a COBOL-based fraud monitoring subscriber.
Problem Statement
Implement an MQ-based real-time transaction notification system where:
- The COBOL authorization program publishes a JSON-formatted event message after each approved transaction.
- Messages are published to an MQ topic so multiple subscribers can receive them independently.
- The message includes transaction details: card number (masked), merchant, amount, location, timestamp, and authorization code.
- A fraud monitoring COBOL program subscribes to the topic and examines each transaction against configurable rules.
- The system handles MQ failures gracefully without disrupting transaction processing.
Message Format Design
The team designs a JSON message format that serves both the notification service and the fraud analytics platform:
{
"eventType": "TXN_AUTHORIZED",
"eventTimestamp": "2025-04-01T14:23:45.123",
"transactionId": "TXN20250401142345001",
"cardNumber": "****1234",
"accountNumber": "1000234567",
"merchantName": "WHOLE FOODS MARKET #10842",
"merchantCategory": "5411",
"transactionAmount": "87.43",
"transactionCurrency": "USD",
"merchantCity": "CHICAGO",
"merchantState": "IL",
"merchantCountry": "US",
"authorizationCode": "A04521",
"channelType": "POS",
"riskScore": 15
}
The Transaction Event Publisher
This COBOL program is called by the main authorization program after a transaction is approved. It builds the JSON message and publishes it to the MQ topic. The design prioritizes minimal impact on transaction processing time.
IDENTIFICATION DIVISION.
PROGRAM-ID. TXNEVTPB.
*================================================================
* PROGRAM: TXNEVTPB - Transaction Event Publisher
* PURPOSE: Publishes approved transaction events to IBM MQ
* for real-time notification and fraud monitoring
* CALLED BY: Main authorization program (AUTHPROC)
* MQ TOPIC: TXN.EVENTS.AUTHORIZED
*================================================================
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY CMQV.
01 WS-PROGRAM-NAME PIC X(8) VALUE "TXNEVTPB".
*--- MQ connection and handle fields ---
01 WS-QM-NAME PIC X(48) VALUE "BANKQM01".
01 WS-TOPIC-STRING PIC X(48)
VALUE "TXN.EVENTS.AUTHORIZED".
01 WS-HCONN PIC S9(9) BINARY VALUE 0.
01 WS-HOBJ PIC S9(9) BINARY VALUE 0.
01 WS-COMP-CODE PIC S9(9) BINARY VALUE 0.
01 WS-REASON PIC S9(9) BINARY VALUE 0.
01 WS-OPEN-OPTIONS PIC S9(9) BINARY VALUE 0.
01 WS-OBJDESC.
COPY CMQODV.
01 WS-MSGDESC.
COPY CMQMDV.
01 WS-PMO.
COPY CMQPMOV.
*--- JSON message construction ---
01 WS-MSG-BUFFER PIC X(2000) VALUE SPACES.
01 WS-MSG-LENGTH PIC S9(9) BINARY VALUE 0.
01 WS-JSON-LENGTH PIC 9(5) VALUE 0.
*--- Transaction event data for JSON generation ---
01 WS-TXN-EVENT.
05 EVENTTYPE PIC X(20)
JSON NAME "eventType".
05 EVENTTIMESTAMP PIC X(23)
JSON NAME "eventTimestamp".
05 TRANSACTIONID PIC X(25)
JSON NAME "transactionId".
05 CARDNUMBER PIC X(16)
JSON NAME "cardNumber".
05 ACCOUNTNUMBER PIC X(10)
JSON NAME "accountNumber".
05 MERCHANTNAME PIC X(40)
JSON NAME "merchantName".
05 MERCHANTCATEGORY PIC X(4)
JSON NAME "merchantCategory".
05 TRANSACTIONAMOUNT PIC X(12)
JSON NAME "transactionAmount".
05 TRANSACTIONCURRENCY PIC X(3)
JSON NAME "transactionCurrency".
05 MERCHANTCITY PIC X(20)
JSON NAME "merchantCity".
05 MERCHANTSTATE PIC X(2)
JSON NAME "merchantState".
05 MERCHANTCOUNTRY PIC X(2)
JSON NAME "merchantCountry".
05 AUTHORIZATIONCODE PIC X(6)
JSON NAME "authorizationCode".
05 CHANNELTYPE PIC X(6)
JSON NAME "channelType".
05 RISKSCORE PIC 9(3)
JSON NAME "riskScore".
*--- Formatting work fields ---
01 WS-FMT-AMOUNT PIC Z(8)9.99.
01 WS-MASKED-CARD PIC X(16).
01 WS-CURRENT-TS PIC X(26).
01 WS-FORMATTED-TS PIC X(23).
*--- Error handling ---
01 WS-MQ-CONNECTED PIC 9 VALUE 0.
88 MQ-IS-CONNECTED VALUE 1.
88 MQ-NOT-CONNECTED VALUE 0.
01 WS-MQ-OPENED PIC 9 VALUE 0.
88 MQ-TOPIC-OPENED VALUE 1.
88 MQ-TOPIC-NOT-OPENED VALUE 0.
01 WS-PUBLISH-OK PIC 9 VALUE 0.
LINKAGE SECTION.
*--- Input from calling authorization program ---
01 LS-TXN-DATA.
05 LS-CARD-NUM PIC X(16).
05 LS-ACCT-NUM PIC X(10).
05 LS-MERCHANT PIC X(40).
05 LS-MERCH-CAT PIC X(4).
05 LS-AMOUNT PIC 9(9)V99.
05 LS-CURRENCY PIC X(3).
05 LS-MERCH-CITY PIC X(20).
05 LS-MERCH-STATE PIC X(2).
05 LS-MERCH-COUNTRY PIC X(2).
05 LS-AUTH-CODE PIC X(6).
05 LS-CHANNEL PIC X(6).
05 LS-RISK-SCORE PIC 9(3).
05 LS-TXN-ID PIC X(25).
01 LS-RETURN-CODE PIC 9.
PROCEDURE DIVISION USING LS-TXN-DATA LS-RETURN-CODE.
0000-MAIN.
MOVE 0 TO LS-RETURN-CODE
PERFORM 1000-BUILD-EVENT-MESSAGE
PERFORM 2000-CONNECT-TO-QM
IF MQ-IS-CONNECTED
PERFORM 3000-OPEN-TOPIC
END-IF
IF MQ-TOPIC-OPENED
PERFORM 4000-PUBLISH-MESSAGE
END-IF
PERFORM 5000-CLEANUP
GOBACK
.
1000-BUILD-EVENT-MESSAGE.
* Populate the event structure
MOVE "TXN_AUTHORIZED" TO EVENTTYPE
* Format timestamp: YYYY-MM-DDTHH:MM:SS.mmm
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TS
STRING WS-CURRENT-TS(1:4) "-"
WS-CURRENT-TS(5:2) "-"
WS-CURRENT-TS(7:2) "T"
WS-CURRENT-TS(9:2) ":"
WS-CURRENT-TS(11:2) ":"
WS-CURRENT-TS(13:2) "."
WS-CURRENT-TS(15:3)
DELIMITED SIZE
INTO WS-FORMATTED-TS
MOVE WS-FORMATTED-TS TO EVENTTIMESTAMP
MOVE LS-TXN-ID TO TRANSACTIONID
* Mask card number: show only last 4 digits
STRING "************"
LS-CARD-NUM(13:4)
DELIMITED SIZE
INTO WS-MASKED-CARD
MOVE WS-MASKED-CARD TO CARDNUMBER
MOVE LS-ACCT-NUM TO ACCOUNTNUMBER
MOVE LS-MERCHANT TO MERCHANTNAME
MOVE LS-MERCH-CAT TO MERCHANTCATEGORY
* Format amount as string with decimal
MOVE LS-AMOUNT TO WS-FMT-AMOUNT
MOVE FUNCTION TRIM(WS-FMT-AMOUNT)
TO TRANSACTIONAMOUNT
MOVE LS-CURRENCY TO TRANSACTIONCURRENCY
MOVE LS-MERCH-CITY TO MERCHANTCITY
MOVE LS-MERCH-STATE TO MERCHANTSTATE
MOVE LS-MERCH-COUNTRY TO MERCHANTCOUNTRY
MOVE LS-AUTH-CODE TO AUTHORIZATIONCODE
MOVE LS-CHANNEL TO CHANNELTYPE
MOVE LS-RISK-SCORE TO RISKSCORE
* Generate JSON from the event structure
JSON GENERATE WS-MSG-BUFFER
FROM WS-TXN-EVENT
COUNT WS-JSON-LENGTH
ON EXCEPTION
DISPLAY WS-PROGRAM-NAME
" JSON GENERATE failed"
MOVE 1 TO LS-RETURN-CODE
END-JSON
MOVE WS-JSON-LENGTH TO WS-MSG-LENGTH
.
2000-CONNECT-TO-QM.
CALL 'MQCONN' USING WS-QM-NAME
WS-HCONN
WS-COMP-CODE
WS-REASON
EVALUATE WS-COMP-CODE
WHEN MQCC-OK
SET MQ-IS-CONNECTED TO TRUE
WHEN MQCC-WARNING
SET MQ-IS-CONNECTED TO TRUE
DISPLAY WS-PROGRAM-NAME
" MQCONN warning. Reason: " WS-REASON
WHEN OTHER
SET MQ-NOT-CONNECTED TO TRUE
DISPLAY WS-PROGRAM-NAME
" MQCONN failed. Reason: " WS-REASON
MOVE 1 TO LS-RETURN-CODE
END-EVALUATE
.
3000-OPEN-TOPIC.
* Configure object descriptor for topic publish
MOVE MQOT-TOPIC TO MQOD-OBJECTTYPE
MOVE WS-TOPIC-STRING TO MQOD-OBJECTSTRING
MOVE FUNCTION LENGTH(
FUNCTION TRIM(WS-TOPIC-STRING))
TO MQOD-OBJECTSTRINGLENGTH
COMPUTE WS-OPEN-OPTIONS =
MQOO-OUTPUT + MQOO-FAIL-IF-QUIESCING
CALL 'MQOPEN' USING WS-HCONN
WS-OBJDESC
WS-OPEN-OPTIONS
WS-HOBJ
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE = MQCC-OK
SET MQ-TOPIC-OPENED TO TRUE
ELSE
SET MQ-TOPIC-NOT-OPENED TO TRUE
DISPLAY WS-PROGRAM-NAME
" MQOPEN topic failed. Reason: " WS-REASON
MOVE 1 TO LS-RETURN-CODE
END-IF
.
4000-PUBLISH-MESSAGE.
* Set message descriptor fields
MOVE MQMT-DATAGRAM TO MQMD-MSGTYPE
MOVE MQFMT-STRING TO MQMD-FORMAT
MOVE MQPER-NOT-PERSISTENT TO MQMD-PERSISTENCE
MOVE MQEI-UNLIMITED TO MQMD-EXPIRY
* Set put-message options
MOVE MQPMO-NO-SYNCPOINT TO MQPMO-OPTIONS
CALL 'MQPUT' USING WS-HCONN
WS-HOBJ
WS-MSGDESC
WS-PMO
WS-MSG-LENGTH
WS-MSG-BUFFER
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE = MQCC-OK
MOVE 1 TO WS-PUBLISH-OK
ELSE
DISPLAY WS-PROGRAM-NAME
" MQPUT failed. Reason: " WS-REASON
MOVE 1 TO LS-RETURN-CODE
END-IF
.
5000-CLEANUP.
IF MQ-TOPIC-OPENED
CALL 'MQCLOSE' USING WS-HCONN
WS-HOBJ
MQCO-NONE
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE NOT = MQCC-OK
DISPLAY WS-PROGRAM-NAME
" MQCLOSE warning. Reason: "
WS-REASON
END-IF
END-IF
IF MQ-IS-CONNECTED
CALL 'MQDISC' USING WS-HCONN
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE NOT = MQCC-OK
DISPLAY WS-PROGRAM-NAME
" MQDISC warning. Reason: "
WS-REASON
END-IF
END-IF
.
The Fraud Monitoring Subscriber
The fraud monitoring program subscribes to the transaction events topic and evaluates each transaction against configurable rules. When a suspicious transaction is detected, it writes an alert record and can optionally put a message on a separate alert queue.
IDENTIFICATION DIVISION.
PROGRAM-ID. FRAUDMON.
*================================================================
* PROGRAM: FRAUDMON - Fraud Monitoring Subscriber
* PURPOSE: Subscribes to transaction events via IBM MQ,
* evaluates fraud rules, generates alerts
* MQ TOPIC: TXN.EVENTS.AUTHORIZED
* RUNS AS: Long-running batch process or CICS task
*================================================================
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY CMQV.
01 WS-PROGRAM-NAME PIC X(8) VALUE "FRAUDMON".
*--- MQ connection fields ---
01 WS-QM-NAME PIC X(48) VALUE "BANKQM01".
01 WS-TOPIC-STRING PIC X(48)
VALUE "TXN.EVENTS.AUTHORIZED".
01 WS-SUB-NAME PIC X(48)
VALUE "FRAUD.MONITOR.SUB".
01 WS-HCONN PIC S9(9) BINARY VALUE 0.
01 WS-HSUB PIC S9(9) BINARY VALUE 0.
01 WS-HOBJ PIC S9(9) BINARY VALUE 0.
01 WS-COMP-CODE PIC S9(9) BINARY VALUE 0.
01 WS-REASON PIC S9(9) BINARY VALUE 0.
01 WS-SUBDESC.
COPY CMQSDV.
01 WS-MSGDESC.
COPY CMQMDV.
01 WS-GMO.
COPY CMQGMOV.
*--- Message buffer ---
01 WS-MSG-BUFFER PIC X(2000) VALUE SPACES.
01 WS-MSG-LENGTH PIC S9(9) BINARY VALUE 2000.
01 WS-DATA-LENGTH PIC S9(9) BINARY VALUE 0.
*--- Parsed transaction event ---
01 WS-TXN-EVENT.
05 EVENTTYPE PIC X(20)
JSON NAME "eventType".
05 EVENTTIMESTAMP PIC X(23)
JSON NAME "eventTimestamp".
05 TRANSACTIONID PIC X(25)
JSON NAME "transactionId".
05 CARDNUMBER PIC X(16)
JSON NAME "cardNumber".
05 ACCOUNTNUMBER PIC X(10)
JSON NAME "accountNumber".
05 MERCHANTNAME PIC X(40)
JSON NAME "merchantName".
05 MERCHANTCATEGORY PIC X(4)
JSON NAME "merchantCategory".
05 TRANSACTIONAMOUNT PIC X(12)
JSON NAME "transactionAmount".
05 TRANSACTIONCURRENCY PIC X(3)
JSON NAME "transactionCurrency".
05 MERCHANTCITY PIC X(20)
JSON NAME "merchantCity".
05 MERCHANTSTATE PIC X(2)
JSON NAME "merchantState".
05 MERCHANTCOUNTRY PIC X(2)
JSON NAME "merchantCountry".
05 AUTHORIZATIONCODE PIC X(6)
JSON NAME "authorizationCode".
05 CHANNELTYPE PIC X(6)
JSON NAME "channelType".
05 RISKSCORE PIC 9(3)
JSON NAME "riskScore".
*--- Fraud detection rules and thresholds ---
01 WS-FRAUD-RULES.
05 WS-HIGH-VALUE-THRESHOLD
PIC 9(9)V99 VALUE 10000.00.
05 WS-RISK-SCORE-THRESHOLD
PIC 9(3) VALUE 70.
05 WS-HIGH-RISK-CATEGORIES.
10 WS-HR-CAT PIC X(4) OCCURS 5 TIMES.
05 WS-HIGH-RISK-COUNTRIES.
10 WS-HR-CTRY PIC X(2) OCCURS 10 TIMES.
05 WS-HR-CAT-COUNT PIC 9 VALUE 3.
05 WS-HR-CTRY-COUNT PIC 9(2) VALUE 5.
*--- Alert tracking ---
01 WS-ALERT-RECORD.
05 WS-ALERT-TXN-ID PIC X(25).
05 WS-ALERT-CARD PIC X(16).
05 WS-ALERT-AMOUNT PIC X(12).
05 WS-ALERT-RULE PIC X(30).
05 WS-ALERT-SEVERITY PIC X(8).
05 WS-ALERT-TIMESTAMP PIC X(23).
*--- Processing counters ---
01 WS-COUNTERS.
05 WS-MSGS-RECEIVED PIC 9(8) VALUE 0.
05 WS-MSGS-CLEAN PIC 9(8) VALUE 0.
05 WS-ALERTS-RAISED PIC 9(8) VALUE 0.
05 WS-PARSE-ERRORS PIC 9(5) VALUE 0.
*--- Control flags ---
01 WS-CONTINUE-FLAG PIC 9 VALUE 1.
88 CONTINUE-PROCESSING VALUE 1.
88 STOP-PROCESSING VALUE 0.
01 WS-ALERT-FOUND PIC 9 VALUE 0.
88 ALERT-DETECTED VALUE 1.
88 NO-ALERT VALUE 0.
*--- Work fields ---
01 WS-TXN-AMOUNT-NUM PIC 9(9)V99 VALUE 0.
01 WS-IDX PIC 9(2).
01 WS-FMT-COUNT PIC ZZ,ZZ9.
PROCEDURE DIVISION.
0000-MAIN.
DISPLAY WS-PROGRAM-NAME " starting..."
PERFORM 1000-INITIALIZE-RULES
PERFORM 2000-CONNECT-AND-SUBSCRIBE
IF WS-COMP-CODE = MQCC-OK
PERFORM 3000-PROCESS-MESSAGES
UNTIL NOT CONTINUE-PROCESSING
END-IF
PERFORM 8000-PRINT-SUMMARY
PERFORM 9000-CLEANUP
DISPLAY WS-PROGRAM-NAME " ended."
STOP RUN
.
1000-INITIALIZE-RULES.
* Load high-risk merchant categories
MOVE "5912" TO WS-HR-CAT(1)
MOVE "5944" TO WS-HR-CAT(2)
MOVE "7995" TO WS-HR-CAT(3)
* Load high-risk countries
MOVE "NG" TO WS-HR-CTRY(1)
MOVE "RO" TO WS-HR-CTRY(2)
MOVE "UA" TO WS-HR-CTRY(3)
MOVE "VN" TO WS-HR-CTRY(4)
MOVE "PH" TO WS-HR-CTRY(5)
DISPLAY " Fraud rules initialized"
DISPLAY " High-value threshold: $"
WS-HIGH-VALUE-THRESHOLD
DISPLAY " Risk score threshold: "
WS-RISK-SCORE-THRESHOLD
.
2000-CONNECT-AND-SUBSCRIBE.
* Connect to queue manager
CALL 'MQCONN' USING WS-QM-NAME
WS-HCONN
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE NOT = MQCC-OK
DISPLAY WS-PROGRAM-NAME
" MQCONN failed. Reason: " WS-REASON
SET STOP-PROCESSING TO TRUE
GOBACK
END-IF
DISPLAY " Connected to " WS-QM-NAME
* Create a durable subscription to the topic
MOVE WS-TOPIC-STRING TO MQSD-OBJECTSTRING
MOVE FUNCTION LENGTH(
FUNCTION TRIM(WS-TOPIC-STRING))
TO MQSD-OBJECTSTRINGLENGTH
MOVE WS-SUB-NAME TO MQSD-SUBNAME
COMPUTE MQSD-OPTIONS =
MQSO-CREATE + MQSO-RESUME
+ MQSO-DURABLE
+ MQSO-FAIL-IF-QUIESCING
CALL 'MQSUB' USING WS-HCONN
WS-SUBDESC
WS-HOBJ
WS-HSUB
WS-COMP-CODE
WS-REASON
IF WS-COMP-CODE = MQCC-OK
DISPLAY " Subscribed to " WS-TOPIC-STRING
ELSE
DISPLAY WS-PROGRAM-NAME
" MQSUB failed. Reason: " WS-REASON
SET STOP-PROCESSING TO TRUE
END-IF
.
3000-PROCESS-MESSAGES.
* Set get-message options
COMPUTE MQGMO-OPTIONS =
MQGMO-WAIT + MQGMO-FAIL-IF-QUIESCING
MOVE 5000 TO MQGMO-WAITINTERVAL
* Reset message descriptor for each get
INITIALIZE WS-MSGDESC
MOVE MQMI-NONE TO MQMD-MSGID
MOVE MQCI-NONE TO MQMD-CORRELID
MOVE 2000 TO WS-MSG-LENGTH
MOVE SPACES TO WS-MSG-BUFFER
CALL 'MQGET' USING WS-HCONN
WS-HOBJ
WS-MSGDESC
WS-GMO
WS-MSG-LENGTH
WS-MSG-BUFFER
WS-DATA-LENGTH
WS-COMP-CODE
WS-REASON
EVALUATE WS-COMP-CODE
WHEN MQCC-OK
ADD 1 TO WS-MSGS-RECEIVED
PERFORM 4000-PARSE-AND-EVALUATE
WHEN MQCC-FAILED
IF WS-REASON = MQRC-NO-MSG-AVAILABLE
CONTINUE
ELSE
DISPLAY WS-PROGRAM-NAME
" MQGET failed. Reason: "
WS-REASON
SET STOP-PROCESSING TO TRUE
END-IF
WHEN OTHER
DISPLAY WS-PROGRAM-NAME
" MQGET unexpected. CC: "
WS-COMP-CODE " Reason: " WS-REASON
END-EVALUATE
.
4000-PARSE-AND-EVALUATE.
INITIALIZE WS-TXN-EVENT
JSON PARSE WS-MSG-BUFFER(1:WS-DATA-LENGTH)
INTO WS-TXN-EVENT
ON EXCEPTION
ADD 1 TO WS-PARSE-ERRORS
DISPLAY WS-PROGRAM-NAME
" JSON parse error for message "
WS-MSGS-RECEIVED
END-JSON
SET NO-ALERT TO TRUE
PERFORM 5000-CHECK-HIGH-VALUE
PERFORM 5100-CHECK-RISK-SCORE
PERFORM 5200-CHECK-HIGH-RISK-CATEGORY
PERFORM 5300-CHECK-HIGH-RISK-COUNTRY
IF NO-ALERT
ADD 1 TO WS-MSGS-CLEAN
END-IF
.
5000-CHECK-HIGH-VALUE.
COMPUTE WS-TXN-AMOUNT-NUM =
FUNCTION NUMVAL(TRANSACTIONAMOUNT)
IF WS-TXN-AMOUNT-NUM > WS-HIGH-VALUE-THRESHOLD
PERFORM 6000-RAISE-ALERT-SETUP
MOVE "HIGH VALUE TRANSACTION"
TO WS-ALERT-RULE
MOVE "HIGH" TO WS-ALERT-SEVERITY
PERFORM 7000-LOG-ALERT
END-IF
.
5100-CHECK-RISK-SCORE.
IF RISKSCORE > WS-RISK-SCORE-THRESHOLD
PERFORM 6000-RAISE-ALERT-SETUP
MOVE "ELEVATED RISK SCORE"
TO WS-ALERT-RULE
MOVE "CRITICAL" TO WS-ALERT-SEVERITY
PERFORM 7000-LOG-ALERT
END-IF
.
5200-CHECK-HIGH-RISK-CATEGORY.
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > WS-HR-CAT-COUNT
IF MERCHANTCATEGORY = WS-HR-CAT(WS-IDX)
PERFORM 6000-RAISE-ALERT-SETUP
MOVE "HIGH RISK MERCHANT CATEGORY"
TO WS-ALERT-RULE
MOVE "MEDIUM" TO WS-ALERT-SEVERITY
PERFORM 7000-LOG-ALERT
END-IF
END-PERFORM
.
5300-CHECK-HIGH-RISK-COUNTRY.
IF MERCHANTCOUNTRY NOT = "US"
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > WS-HR-CTRY-COUNT
IF MERCHANTCOUNTRY = WS-HR-CTRY(WS-IDX)
PERFORM 6000-RAISE-ALERT-SETUP
MOVE "HIGH RISK COUNTRY"
TO WS-ALERT-RULE
MOVE "CRITICAL" TO WS-ALERT-SEVERITY
PERFORM 7000-LOG-ALERT
END-IF
END-PERFORM
END-IF
.
6000-RAISE-ALERT-SETUP.
SET ALERT-DETECTED TO TRUE
ADD 1 TO WS-ALERTS-RAISED
MOVE TRANSACTIONID TO WS-ALERT-TXN-ID
MOVE CARDNUMBER TO WS-ALERT-CARD
MOVE TRANSACTIONAMOUNT TO WS-ALERT-AMOUNT
MOVE EVENTTIMESTAMP TO WS-ALERT-TIMESTAMP
.
7000-LOG-ALERT.
DISPLAY "*** FRAUD ALERT ***"
DISPLAY " Transaction: " WS-ALERT-TXN-ID
DISPLAY " Card: " WS-ALERT-CARD
DISPLAY " Amount: " WS-ALERT-AMOUNT
DISPLAY " Rule: " WS-ALERT-RULE
DISPLAY " Severity: " WS-ALERT-SEVERITY
DISPLAY " Merchant: " MERCHANTNAME
DISPLAY " Location: " MERCHANTCITY " "
MERCHANTSTATE " " MERCHANTCOUNTRY
DISPLAY " Timestamp: " WS-ALERT-TIMESTAMP
DISPLAY "---"
.
8000-PRINT-SUMMARY.
DISPLAY " "
DISPLAY "========================================"
DISPLAY " FRAUD MONITORING SESSION SUMMARY"
DISPLAY "========================================"
MOVE WS-MSGS-RECEIVED TO WS-FMT-COUNT
DISPLAY " Messages received: " WS-FMT-COUNT
MOVE WS-MSGS-CLEAN TO WS-FMT-COUNT
DISPLAY " Clean transactions: " WS-FMT-COUNT
MOVE WS-ALERTS-RAISED TO WS-FMT-COUNT
DISPLAY " Alerts raised: " WS-FMT-COUNT
MOVE WS-PARSE-ERRORS TO WS-FMT-COUNT
DISPLAY " Parse errors: " WS-FMT-COUNT
DISPLAY "========================================"
.
9000-CLEANUP.
IF WS-HSUB NOT = 0
CALL 'MQCLOSE' USING WS-HCONN
WS-HSUB
MQCO-KEEP-SUB
WS-COMP-CODE
WS-REASON
END-IF
IF WS-HOBJ NOT = 0
CALL 'MQCLOSE' USING WS-HCONN
WS-HOBJ
MQCO-NONE
WS-COMP-CODE
WS-REASON
END-IF
IF WS-HCONN NOT = 0
CALL 'MQDISC' USING WS-HCONN
WS-COMP-CODE
WS-REASON
END-IF
.
Solution Walkthrough
Publisher Design Decisions
Non-persistent messages: The publisher uses MQPER-NOT-PERSISTENT for transaction events. This is a deliberate trade-off: non-persistent messages are faster (no disk write) but can be lost if the queue manager restarts. For real-time notifications, a missed alert is acceptable -- the customer will see the transaction on their next statement. If guaranteed delivery is required (e.g., for regulatory audit), persistence should be enabled.
No syncpoint: The publisher uses MQPMO-NO-SYNCPOINT, meaning the message is committed immediately without participating in a CICS unit of work. This ensures that the MQ put cannot cause the transaction authorization to roll back. If the MQ put fails, the transaction is still authorized -- the alert is simply not sent. The alternative (syncpoint integration) would risk rolling back an approved transaction due to an MQ issue, which is unacceptable for an authorization system.
Card masking: The card number is masked before being placed on the message queue. This is a security requirement -- even internal MQ messages should not contain full card numbers. The masking happens in the publisher so that no downstream subscriber ever sees the full number.
Subscriber Design Decisions
Durable subscription: The subscriber uses MQSO-DURABLE combined with MQSO-CREATE + MQSO-RESUME. This means the subscription persists even when the subscriber is not running. Messages published while the subscriber is down are queued and delivered when it reconnects. This prevents message loss during planned maintenance windows.
Wait with timeout: The subscriber uses MQGMO-WAIT with a 5-second interval. This allows the program to periodically check for shutdown signals while waiting for messages. An infinite wait (MQWI-UNLIMITED) would make the program unresponsive to shutdown requests.
Multiple rule checks per message: Each message is evaluated against all fraud rules, not just the first matching rule. A single transaction can trigger multiple alerts (e.g., high value AND high-risk country). This is important for fraud analysis -- the pattern of multiple triggers is more significant than any single trigger.
Message Flow Architecture
+--> Mobile Notification Service
| (Cloud subscriber)
COBOL Auth Program |
(AUTHPROC) |
| |
v |
TXNEVTPB -----> MQ Topic --------+--> FRAUDMON
(Publisher) TXN.EVENTS. | (COBOL Fraud Monitor)
AUTHORIZED |
+--> Analytics Platform
(Cloud subscriber)
Each subscriber receives its own copy of every message. Adding a new subscriber (e.g., a regulatory reporting system) requires only creating a new subscription -- no changes to the publisher or existing subscribers.
JCL for the Fraud Monitor Batch Job
//FRAUDMON JOB (ACCT),'FRAUD MONITOR',
// CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//* Run the fraud monitoring subscriber as a long-running
//* batch job. The program connects to MQ, subscribes to
//* the transaction events topic, and processes messages
//* until manually stopped or the queue manager quiesces.
//*
//STEP01 EXEC PGM=FRAUDMON
//STEPLIB DD DSN=BANK.PROD.LOADLIB,DISP=SHR
// DD DSN=IBM.MQ.SCSQCOBC,DISP=SHR
// DD DSN=IBM.MQ.SCSQLOAD,DISP=SHR
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//ALERTS DD DSN=BANK.FRAUD.ALERTS,
// DISP=MOD,
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
Discussion Questions
-
Reliability vs. performance: The publisher uses non-persistent messages without syncpoint. Under what circumstances should you switch to persistent messages with syncpoint? What is the performance cost, and how would you measure it?
-
Message ordering: If two transactions for the same card are authorized simultaneously on different CICS regions, can the fraud monitor receive them out of order? Does this affect fraud detection accuracy? How would you handle ordering if it matters?
-
Subscriber scaling: During peak hours (Black Friday), transaction volume increases 5x. How would you scale the fraud monitoring subscriber? Can multiple instances of FRAUDMON subscribe to the same topic? What coordination issues arise?
-
Dead letter handling: If the fraud monitor cannot parse a JSON message (corrupt data), the message is currently lost after the parse error counter is incremented. Design a dead-letter queue strategy that preserves unprocessable messages for later analysis.
-
Schema evolution: If a new field (e.g., "deviceFingerprint") is added to the transaction event JSON, what happens to existing subscribers that do not expect this field? How would you manage schema versioning across publishers and subscribers?