A working Java simulation of how IBM Operational Decision Manager (ODM) authorizes a credit card payment in real time. Sara taps her RBC Mastercard at a coffee shop and a 4-party conversation — POS terminal, TD Bank, Mastercard network, RBC + ODM — completes the decision in under 2 seconds.
Sara Thompson buys a $4.50 coffee at Java Joe Coffee Shop in Toronto.
She taps her RBC Mastercard. IBM ODM decides: APPROVED.
The scenario is deliberately simple so the architecture stays front and center — not the business logic.
[Sara taps card at POS terminal]
│ ISO 8583 Auth Request
▼
[TD Bank — Acquiring Bank]
│ Routes via Mastercard Banknet
▼
[Mastercard Network — BIN lookup → RBC]
│ Forwards to issuing bank
▼
[RBC — Issuing Bank]
│ Calls IBM ODM
▼
[IBM ODM — Rule Engine] ← THE BRAIN
│ 5 rules run sequentially
│ Returns: APPROVED (code 00) + auth code
▼
[Response flows back up the same chain]
▼
[POS Terminal: APPROVED ✓]
| Party | Role | Entity in Demo |
|---|---|---|
| Cardholder | Pays | Sara Thompson |
| Acquirer | Merchant's bank, sends auth request | TD Bank |
| Network | Routes between banks via BIN lookup | Mastercard Banknet |
| Issuer | Sara's bank, makes the YES/NO call | RBC + IBM ODM |
The ODM engine runs 5 business rules in priority order. A hard-decline from any rule stops processing immediately. After all rules pass, an aggregate risk score is computed — if it hits 70/100 the transaction is still declined.
| # | Rule | What it checks | Hard Decline? |
|---|---|---|---|
| 1 | Card Validity | Expiry date, blocked flag, NFC capability | Yes |
| 2 | Credit Limit | Available credit ≥ transaction amount | Yes |
| 3 | Geographic Risk | Country risk tier, cross-border flags | No (risk pts) |
| 4 | Velocity Check | Transactions per hour / per day | No (risk pts) |
| 5 | Merchant Category | Amount plausibility for MCC 5812 (restaurant) | No (risk pts) |
In real IBM ODM, a business analyst maintains these rules in Decision Center (a web UI) — no Java code changes needed to update a threshold.
src/main/java/com/ibm/odm/payment/
├── Main.java # CLI entry point — runs the Sara scenario
├── PaymentOrchestrator.java # Wires the 4 parties together, prints output
├── model/
│ ├── Card.java # Cardholder card (number, expiry, network)
│ ├── CardholderAccount.java # RBC account (credit limit, monthly spend)
│ ├── Merchant.java # Merchant (MID, MCC, terminal, acquirer)
│ ├── TransactionRequest.java # ISO 8583 auth request
│ ├── RiskAssessment.java # Accumulates risk score from rules
│ └── AuthorizationResponse.java # Final decision + response code
├── rules/
│ ├── Rule.java # Interface all rules implement
│ ├── RuleResult.java # Pass/fail + risk points + explanation
│ ├── ODMRuleEngine.java # Orchestrates rule execution
│ ├── CardValidityRule.java
│ ├── CreditLimitRule.java
│ ├── GeographicRiskRule.java
│ ├── VelocityCheckRule.java
│ └── MerchantCategoryRule.java
├── bank/
│ ├── RBCIssuingBank.java # Issuer: receives request, calls ODM
│ └── TDBankAcquirer.java # Acquirer: receives POS tap, routes to network
├── network/
│ └── MastercardNetwork.java # BIN lookup, routes to issuing bank
└── web/
├── WebMain.java # Web server entry point (port 8080)
├── DynamicRuleEngine.java # Runtime-editable rule engine (JSON rules)
├── RuleDefinition.java # Rule model for the web UI
├── RulesStore.java # In-memory rule store
├── WebSimulationRunner.java # Runs simulation from web request
└── SimulationResult.java # Result model for JSON response
- Java 11+
- Maven 3.6+ (optional — plain
javacalso works)
No external dependencies. The web server uses Java's built-in com.sun.net.httpserver.
# With Maven
mvn compile exec:java -Dexec.mainClass="com.ibm.odm.payment.Main"
# With javac
javac -d out $(find src -name "*.java")
java -cp out com.ibm.odm.payment.Main
# Windows batch script
compile-and-run.batExpected output (truncated):
╔══════════════════════════════════════════════════════════════════════╗
║ IBM ODM — Mastercard Payment Authorization Scenario ║
║ Sara buys a $4.50 coffee at Java Joe Coffee Shop in Toronto. ║
╚══════════════════════════════════════════════════════════════════════╝
PARTY 4: IBM ODM RULE ENGINE (at RBC)
>>> Executing rule: Card Validity Rule | Card must be valid, non-expired, and not blocked
| Result: PASS
>>> Executing rule: Credit Limit Rule | Available credit must cover transaction
| Result: PASS
...
--- Final Risk Score: 5 / 100 (Threshold for decline: 70)
[*] All rules passed. Risk score within acceptable limits. APPROVING.
┌─────────────────────────────────────┐
│ PAYMENT APPROVED ✓ │
│ Amount: $4.50 CAD │
│ Auth: 427831 │
└─────────────────────────────────────┘
# With Maven
mvn compile exec:java -Dexec.mainClass="com.ibm.odm.payment.web.WebMain"
# Windows batch script
run-web.batOpens http://localhost:8080 in your browser automatically.
The web UI includes:
- Simulation tab — run the authorization and see real-time rule-by-rule output
- Rule Editor — edit rule thresholds (credit limit, velocity, risk scores) live, no restart needed
- BOM / XOM Explorer — browse the Business Object Model and Execution Object Model
- Decision Chart — visual flowchart of the authorization decision tree
- What-If Scenarios — run pre-built edge cases (expired card, over limit, high-risk country, velocity fraud)
In a real ODM deployment, rules like "decline if monthly spend > credit limit" live in a Decision Table edited by business analysts in Decision Center. This demo simulates that as Java objects — the architecture is identical, only the editing interface differs.
ODM supports two decline paths:
- Hard decline — a single rule vetoes the transaction outright (e.g., expired card). Processing stops.
- Aggregate risk — each rule contributes risk points. If the total reaches 70/100, the transaction is declined even though no single rule fired a hard decline.
The TransactionRequest models the data that would travel inside a real ISO 8583 message: PAN, merchant ID, MCC, amount, currency code, entry mode (contactless NFC here), and a retrieval reference number.
Mastercard uses the first 6 digits of Sara's card number (541200) to identify RBC as the issuing bank and route the authorization request there. This is modeled in MastercardNetwork.java.
The web UI includes four pre-built scenarios to explore decline paths:
| Scenario | Trigger | Expected Result |
|---|---|---|
| Normal (Sara's coffee) | $4.50, valid card, Toronto | APPROVED |
| Expired card | Expiry date in the past | DECLINED — Card Validity hard decline |
| Over limit | $4,500 charge, $3,800 available | DECLINED — Credit Limit hard decline |
| High-risk country | Transaction in high-risk jurisdiction | DECLINED — aggregate risk score ≥ 70 |
| Velocity fraud | 12 transactions in 1 hour | DECLINED — Velocity Check hard decline |
- Zero external dependencies — deliberate, so the demo runs anywhere Java 11 runs.
- Rule ordering matters — rules execute sequentially; cheaper/more decisive rules run first.
- Audit trail — every
ODMDecisioncarries the full list ofRuleResultobjects so every YES/NO is explainable. - Web server — uses
com.sun.net.httpserver(JDK built-in). No Tomcat, no Spring, no framework. - Dynamic rules — the web UI writes edited rules to an in-memory
RulesStore; theDynamicRuleEnginepicks them up on the next request without a restart.
| This demo | Real IBM ODM |
|---|---|
ODMRuleEngine.java |
Decision Service deployed on ODM Rule Execution Server |
Rule.java interface |
Business rule authored in Decision Center / Rule Designer |
RuleResult audit trail |
ODM execution trace / decision record |
| Web rule editor | ODM Decision Center (business analyst UI) |
RulesStore |
ODM Rule Repository |
| BOM/XOM explorer tab | IBM ODM Business Object Model / Execution Object Model |
This project is a demonstration and educational resource. Not affiliated with or endorsed by IBM.