# Mercury TMS — Mastercard Clearing Interface Operation Manual

**Version:** 0.1 (first edition)
**Date:** 25 August 2026
**Status:** Describes the system as built on branch `feature/mastercard-ipm-clearing-safe`. Every
menu path, screen, field, and behaviour below is real and in the codebase — nothing here is
aspirational. Sections describing work not yet built are explicitly marked **NOT YET BUILT**
rather than written as if they existed; this manual should never need a correction because it
overclaimed.

This document is written in the structure of `way-4-mc-interface.txt` (OpenWay Way4™ Mastercard
Interchange Interface Operation Manual) — supplied as the reference this project was asked to
follow — so a reader already familiar with that manual can map concepts across directly. It
otherwise stands alone: this is Mercury's own manual for Mercury's own system, not a commentary
on Way4.

---

## Contents

1. [Interchange between Mercury TMS and Mastercard](#1-interchange-between-mercury-tms-and-mastercard)
   1.1 [Mastercard Interchange Interface Functionality](#11-mastercard-interchange-interface-functionality)
   1.2 [Principles of Transaction Information Exchange with Mastercard](#12-principles-of-transaction-information-exchange-with-mastercard)
2. [Mastercard Interface Description](#2-mastercard-interface-description)
   2.1 [Description of Mastercard Formats and Services](#21-description-of-mastercard-formats-and-services)
   2.2 [Mastercard File Types](#22-mastercard-file-types)
3. [Processing Incoming Transactions and Exporting Outgoing Transactions](#3-processing-incoming-transactions-and-exporting-outgoing-transactions)
   3.1 [Processing Incoming Transactions](#31-processing-incoming-transactions)
   3.2 [Exporting Outgoing Transactions](#32-exporting-outgoing-transactions)
   3.3 [Monitoring Imported and Exported Files](#33-monitoring-imported-and-exported-files)
   3.4 [Troubleshooting Typical File Import/Export Problems](#34-troubleshooting-typical-file-importexport-problems)
4. [The Member Institution Registry](#4-the-member-institution-registry)
5. [Processing Information from Fraud Prevention Systems and Services](#5-processing-information-from-fraud-prevention-systems-and-services)
6. [Generating Reports](#6-generating-reports)
7. [Configuring Merchant Contracts](#7-configuring-merchant-contracts)
8. [Reference: Where Each Concept Lives in Code](#8-reference-where-each-concept-lives-in-code)

---

### Conventions used in this document

- Screen **field labels** are shown in italics.
- **[Button labels]** are enclosed in square brackets.
- Menu/URL paths are shown as `/admin/clearing/...`.
- Module and function references are shown as `ClearingCore.Mastercard.Ipm.FileReader`.
- ⚠ marks a real constraint or gotcha found while building the system — the equivalent of Way4's
  warning callouts.
- **NOT YET BUILT** marks a capability this manual describes conceptually but that has no
  screen/code behind it yet, with a pointer to where it's tracked.

Reference material this manual was built against:

- *IPM Clearing Formats*, Mastercard, 15 March 2022 — `docs/settlement_recon/ipm-clearing-formats-15-march-2022.pdf`
- *Global Clearing Management System Reference Manual*, Mastercard, 3 June 2025 — `docs/settlement_recon/m-gcmsrefmanual.pdf`
- `docs/settlement_recon/way4-mc-interface-similarity-gap.md` — the full similarity/gap analysis
  this manual's companion document; read that first for *why* things are shaped the way they are,
  read this one for *how to operate what exists*.

---

## 1. Interchange between Mercury TMS and Mastercard

Mercury is a **Mastercard principal member**: it holds its own Customer IDs (ICAs), settles
directly with Mastercard rather than through a sponsor, and is the institution named in the
header of every clearing file it originates. Interchange with Mastercard happens through
special-format files containing clearing, settlement, and additional-information data — the same
IPM (Integrated Product Messages) format Mastercard uses with every member.

This document describes interchange in **file exchange mode**, matching Way4's own scope
statement almost exactly: file-based clearing, not the online authorization channel (which is
handled entirely outside this repository, by `mercury_device_middlelayer`).

### 1.1 Mastercard Interchange Interface Functionality

Mercury's Mastercard Interchange interface is used to:

- **Exchange information on executed transactions** — clearing information (First Presentments,
  Second Presentments), dispute/chargeback cycle messages, and — in principle — settlement
  position and interbank information (see §1.2.3 for what of this is actually built).
- **Exchange information with fraud prevention services** — SAFE (System to Avoid Fraud
  Effectively) case tracking; see §5.
- **Prepare reports** — see §6 for what is built and what remains, mapped against Way4's own
  report list.

Unlike Way4, this interface does **not** yet handle additional-information file types (BIN table
updates, currency rates, stop lists) as native Mastercard file formats — those are covered
separately in `scheme_core`'s own BIN range tooling; see §4.

### 1.2 Principles of Transaction Information Exchange with Mastercard

Mercury exchanges files with Mastercard through inward and outward processing, built around the
real IPM specification rather than convention. Everything the codec does is validated against the
spec's own worked examples — see `ClearingCore.Mastercard.Ipm.MessageCodec`'s test suite, which
reproduces the spec's p. 37–39 example byte for byte.

#### 1.2.1 Support of Mastercard release requirements

**NOT YET BUILT.** Mastercard issues new release requirements twice a year (April/October).
`SchemeCore.Bulletin` exists as a general mandate-notice register (with `implementation_status`
tracking), but nothing yet links a bulletin to the specific DE/PDS catalog version it affects. If
the DE/PDS catalogs (§2.1) ever need a release-driven change, that change is made directly in
`ClearingCore.Mastercard.Ipm.DeCatalog` / `PdsCatalog`, and `Ipm.MessageCodec` will fail loudly
(`{:undefined_data_element, n}`) on any DE it doesn't recognise, rather than silently
misinterpreting a new release's data — the same failure mode a version mismatch would need to be
caught by, until formal linkage is built.

#### 1.2.2 Importing Transaction Information into Mercury TMS

Data imported into Mercury TMS are physical files whose format is regulated by Mastercard. For
each imported physical file, a record is created in the `clearing_batches` table. A physical file
contains one or more logical files, each bounded by its own File Header/1644 and File
Trailer/1644 — Mastercard's own terminology, confirmed real by the GCMS Reference Manual (p. 46).
For each logical file, a record is created in `clearing_logical_files`.

A logical file contains messages (First Presentment, Financial Detail Addendum, First
Chargeback, etc.). For each clearing message, a record is created in the
`mastercard_ipm_records` table — this is Mercury's equivalent of Way4's `ORIGINAL_DOC`, except
every field is stored **named and typed**, not packed: `raw_de_map` and `raw_pds_map` retain
every data element and private data subelement verbatim (so nothing is lost even for
uncatalogued tags), while dozens of named columns (`pan`, `transaction_amount`,
`acquirer_reference_data`, `card_acceptor_business_code`, …) make the common fields directly
queryable — no "Unpack All" step is needed, unlike Way4's packed-blob-plus-unpack-tool design.

```
Physical File (clearing_batches)
  └─ Logical File (clearing_logical_files) — one per File Header/Trailer pair
       ├─ File Header/1644           (not stored as a record — its content
       │                              is lifted onto the logical file row)
       ├─ First Presentment/1240 ─┐
       ├─ Financial Detail        │  each becomes one row in
       │  Addendum/1644           │  mastercard_ipm_records
       ├─ First Chargeback/1442 ──┘
       └─ File Trailer/1644           (likewise lifted, not stored as a record)
```

#### 1.2.3 Exporting Transaction Information to Mastercard

`ClearingCore.Mastercard.Ipm.FileWriter` builds a customer-generated logical file: it owns DE 71
(Message Number) sequencing, the PDS 0301 checksum (hash total of every DE 4 present) and the
PDS 0306 message count, so a caller cannot get any of those wrong. `Ipm.FileSequence` allocates
the PDS 0105 (File ID) sequence number under a database row lock, since Mastercard's own rule is
severe: *"A clearing file rejects in its entirety if it contains a duplicate File ID"* (GCMS
manual, p. 552) — the allocator exists precisely so two concurrent submissions can never collide.

⚠ **What is built is the encoder and the transport (§3.2); what is not built is the *decision* of
which transactions to export.** There is no `"To be Sent"` document status anywhere in
`core_transactions`, no job that selects owed transactions, and no re-export control. See
`docs/settlement_recon/way4-mc-interface-similarity-gap.md` §9.2 #1 for the full statement of
this gap — it is the single largest piece of unbuilt work in the whole interface.

---

## 2. Mastercard Interface Description

### 2.1 Description of Mastercard Formats and Services

Mastercard provides interchange for transaction information through GCMS (Global Clearing
Management System) in IPM (Integrated Product Messages) format, based on ISO 8583-1993 with a
bit-map representation, exactly as the real specification describes. Mercury's implementation of
this format lives entirely under `apps/clearing_core/lib/clearing_core/mastercard/ipm/`:

| Concern | Module |
|---|---|
| Message structure (MTI, bit maps, DE ordering) | `Ipm.MessageCodec`, `Ipm.Bitmap` |
| Data Element catalog | `Ipm.DeCatalog` |
| Private Data Subelement (PDS) tag-length-data | `Ipm.Pds`, `Ipm.PdsCatalog` |
| Character encoding (EBCDIC ↔ ASCII) | `Ipm.Charset` |
| Message classification (type/label/category) | `Ipm.MessageCatalog` |
| DE 31 (Acquirer Reference Data) | `Ipm.AcquirerReferenceData` |
| Physical/logical file structure | `Ipm.Framing`, `Ipm.FileReader`, `Ipm.FileWriter` |
| Database persistence | `Ipm.Ingestor`, `ClearingLogicalFile`, `ClearingCore.Mastercard.IpmRecord` |
| File transport | `Ipm.SftpClient`, `Ipm.InboundPollWorker`, `Ipm.OutboundSubmitter`, `Ipm.Scheduler` |

⚠ **Record framing.** The real spec is explicit about message *content* but is silent on
physical framing, deferring to the GCMS Reference Manual — which, once supplied, confirmed it
directly (p. 54): a 4-byte Record Descriptor Word (RDW), length-inclusive-of-itself, precedes
every IPM message. `Ipm.Framing`'s `:rdw` mode implements exactly this and is the confirmed
default. A `:blocked_1014` mode also exists for a genuinely legacy raw-MIP delivery channel, but
the GCMS manual's own delivery-method table shows that a modern channel (CONNECT:Direct or
equivalent) hands a customer a plain RDW stream, not physical 1014-byte blocks — so `:rdw` is
what to configure once a real delivery channel exists (§3.3).

### 2.2 Mastercard File Types

Way4's Table 1 lists the file types Mastercard exchanges. Mercury's coverage of that same table:

| File type | Mastercard bulk ID | Mercury status |
|---|---|---|
| IPM Generated File (clearing) | T112 / T120 (test) | **Built** — `Ipm.FileReader`/`Ipm.Ingestor`, matched by `Ipm.InboundPollWorker` |
| IPM Customer Generated File | R111 / R119 (test) | **Built** — `Ipm.FileWriter`/`Ipm.OutboundSubmitter` (transport only; see §1.2.3's warning) |
| Early Reconciliation File | T113 / T121 (test) | **Built** — same reader/matcher as T112; Mastercard's early-reconciliation semantics are not specially distinguished |
| IPM MPE Daily Update File | T067 / T167 (test) | Partial — `scheme_core.BinRange` exists and real Mastercard Account Range data has been imported once via `mix load_scheme_bin_ranges`; no *native*, ongoing T067 reader |
| IPM MPE Full File Replacement | T068 / T168 (test) | **NOT YET BUILT** — full-replacement semantics need explicit design (fail-closed upsert ≠ replace-everything) |
| Currency Rate File | T057 / T058 (test) | **NOT YET BUILT** |
| SAFE Transactions / Confirmation Data | R847/T848/T849 | **Partial** — see §5; case tracking only, no file format (no SAFE spec available) |
| Electronic Warning Bulletin (stop list) | T314/T316 | **NOT YET BUILT** |

Production/test bulk IDs both map to Mastercard's own PDS 0122 (Processing Mode) value `P`/`T` —
Mercury reads this per logical file (`ClearingLogicalFile.processing_mode`) and, by default,
**refuses to ingest a test file** unless explicitly told otherwise (§3.4).

---

## 3. Processing Incoming Transactions and Exporting Outgoing Transactions

### 3.1 Processing Incoming Transactions

Incoming Mastercard clearing files are processed through **`/admin/clearing/files`**
(`clearing.view` permission required).

To process an incoming file:

1. Navigate to `/admin/clearing/files`.
2. Under *Upload Visa TC-33 / Mastercard IPM File*, choose a file via [Choose File].
3. Click [Upload & Reconcile].

The screen's own dropzone text — *"Drop a Visa Base II / TC-33 or Mastercard IPM clearing file
here, or choose one. Parsed and reconciled on upload."* — is accurate for both networks: which
parser runs is decided by `detect_network/1` reading the file's own bytes (a physical IPM file's
first message is always a File Header/1644 with MTI `1644`, in either EBCDIC or ASCII — a Visa
TC-33 file, undelimited fixed 168-byte records, satisfies neither test), not by filename.

On upload, `ClearingCore.Mastercard.Ipm.Ingestor.ingest/3` runs: it frames the file, decodes
every message, groups them into logical files, validates the spec's structural rules (§3.4),
stores everything, and immediately reconciles the newly stored records against
`settlement_core.CoreTransaction` via `SettlementCore.ClearingBridge` (matched on DE 31, falling
back to DE 37 — see §8).

The **Clearing File Registry** table on this screen shows, per file: *Filename*, *Network*,
*Direction*, *Batch Date*, *Size*, *Records*, *Matched*, *Status*, *Processed*.

⚠ Mercury has no automatic inbound feed configured yet in any environment — `:mastercard_sftp`
defaults to `host: nil` everywhere until a real Mastercard delivery channel is provisioned. Until
then, this manual upload path is the only way an inbound file reaches the system, exactly the way
Way4 requires an operator to copy a file to a watched directory before running its own Inward
Processing menu item.

### 3.2 Exporting Outgoing Transactions

`Ipm.FileWriter.build/2` (or `build_transmission/2` for several logical files in one physical
transmission) produces a real, spec-valid outbound file from a caller-supplied list of messages.
`Ipm.OutboundSubmitter.submit/2` then writes it locally, records it as an outbound
`clearing_batches` row (`direction: "outbound"`, status `generated → submitted | error`), and
uploads it via `Ipm.SftpClient` if `:mastercard_sftp` is configured.

⚠ As stated in §1.2.3: there is currently **no menu item, button, or job that calls this path**.
Every use of `FileWriter`/`OutboundSubmitter` today is programmatic (from `mix generate_ipm_sample`,
§6, or from a test/console session) — there is no equivalent yet of Way4's *"IPM Outward
Processing"* menu item that collects owed transactions and drives export automatically. This is
the #1 item in the P0 backlog (`way4-mc-interface-similarity-gap.md` §9.2).

### 3.3 Monitoring Imported and Exported Files

#### 3.3.1 Imported Files

**`/admin/clearing/files`** and **`/admin/clearing/batches`** both list files/batches regardless
of direction; `/admin/clearing/batches` is the fuller view, with filters for *Source*,
*Direction*, and *Status*, and a table of *Filename*, *Source*, *Direction*, *Batch Date*,
*Records*, *Matched*, *Status*.

Clicking a batch row opens **`/admin/clearing/batches/:id`** — the equivalent of Way4's "Logical
Files" and "Original Docs" forms combined into one page. It shows batch-level metadata (*Status*,
*Total Records*, *Matched*, *File Size*, *Processed At*) and a table of every decoded record in
the batch: *Clearing ID (RRN/ARN)*, *Transaction ID*, *Merchant*, *Amount*, *Currency*, *Match*.

For a Mastercard record, *Clearing ID* shows DE 31 (Acquirer Reference Data) when present, falling
back to DE 37 (Retrieval Reference Number) — DE 31 is the correct primary identifier (mandatory on
a First Presentment; DE 37 is only conditional), so this column intentionally does not just show
whichever value happens to be first. *Transaction ID* shows DE 63 (Transaction Life Cycle ID) when
present, falling back to the message's own DE 71 sequence number (`Msg #4`) — IPM has no field that
is exactly "transaction ID" the way Way4's document model implies one; this is the closest honest
equivalent.

**NOT YET BUILT:** a dedicated logical-file drill-down screen (Way4's Fig. 5, "Logical Files for
<file>") and a named-field-value grid (Way4's Fig. 7, "Log Fields for Original Data"). The data
for both already exists and is queryable —
`ClearingCore.Context.list_clearing_logical_files/1`, `count_records_by_logical_file/1`,
`DeCatalog.name/1`, `PdsCatalog.name/1` — only the rendering is missing. `mix
generate_ipm_sample`'s `.decoded.txt` output (§6) is, in effect, a preview of exactly what this
screen should look like once built.

#### 3.3.2 Exported Files

Outbound batches appear in the same `/admin/clearing/batches` list, filterable by *Direction:
Outbound*. There is no separate exported-files screen, matching the fact that no export workflow
exists yet to populate one beyond what `Ipm.OutboundSubmitter` writes directly (§3.2).

**NOT YET BUILT:** file export reversal (Way4 §3.3.2.1's rollback, including its own warning that
a re-export reuses the same File ID) and "mark documents paid" (Way4 §3.3.2.2's settlement
confirmation marking). Both require the outbound lifecycle from §1.2.3/§3.2 to exist first.

### 3.4 Troubleshooting Typical File Import/Export Problems

Unlike Way4's free-text process log, every structural problem `Ipm.FileReader` finds becomes a
**typed** `clearing_exceptions` row, reviewable at **`/admin/clearing/exceptions`**
(`/admin/clearing` → [Review Exceptions]). The exception queue supports filtering by *Status*
(defaults to `open`) and *Exception Type*, and a [Resolve] action with a required resolution
note — nothing is silently dropped.

`ClearingCore.ClearingException.file_structure_types/0` lists every structural exception type,
each citing the spec page that states the rule it enforces:

| Exception type | Spec rule (IPM Clearing Formats, 15 March 2022) |
|---|---|
| `missing_file_header` | A logical file must begin with a File Header/1644 (p. 39) |
| `missing_file_trailer` | A logical file must end with a File Trailer/1644 (p. 39) |
| `file_id_mismatch` | Header and trailer PDS 0105 must match (p. 48) |
| `empty_logical_file` | At least one message besides header/trailer is required (p. 48) |
| `bad_first_message_number` | DE 71 must be `00000001` in the File Header (p. 48) |
| `message_number_not_increasing` | DE 71 must strictly increase message to message (p. 48) |
| `message_count_mismatch` | Trailer PDS 0306 must equal the file's actual message count (p. 49) |
| `checksum_mismatch` | Trailer PDS 0301 must equal the hash total of DE 4 across the file (p. 49) |
| `orphan_addendum` | A Financial Detail Addendum/1644 must immediately follow its presentment (p. 39) |
| `dangling_message_exception` | A Message Exception/1644 must be immediately followed by the rejected message (p. 39) |

Reconciliation-side exception types (`unmatched`, `amount_mismatch`, `record_rejected`, …) are
raised by `SettlementCore.ClearingBridge` when a decoded record cannot be matched, or fails to
persist — see `ClearingException.reconciliation_types/0`.

**Test-file handling.** `Ipm.Ingestor.ingest/3` reads PDS 0122 per logical file and refuses to
load a file whose processing mode is `T` (test) unless called with `allow_test_files: true` —
Way4's equivalent is a confirmation prompt ("Do you want to continue?"); Mercury's default
answer is "no," reversible only by an explicit flag, never a click-through.

**Technical/physical file errors** (connection failures, malformed bytes that cannot even be
framed) produce a batch with `status: "error"` and a `processing_error` message describing what
went wrong — visible directly on the batch list, no separate error-code lookup needed.

---

## 4. The Member Institution Registry

Way4 assumes an ICA/Member ID registry exists — its own import validation checks incoming
messages against one (`"Member ID <member> not on file"`). Mercury's did not exist until this
build; it now does, at **`/admin/schemes/member-institutions`** (`scheme.config.view`
permission), backed by `SchemeCore.MemberInstitution`.

This screen is **multi-scheme by construction** — every row belongs to a `scheme_masters` entry
(Visa, Mastercard, JCB, and the rest are already seeded), so registering a Visa or JCB principal's
ICA is the same schema and the same screen, not a redesign. It is also **multi-instance** — an
operator adds another acquiring identity by filling in the form, not by editing code.

Fields on the New/Edit Institution form:

| Field | Feeds |
|---|---|
| *ICA / Customer ID* | DE 32 (Acquiring Institution), DE 33 (Forwarding Institution), DE 94 (Transaction Originator), PDS 0105 subfield 3 (Processor ID) |
| *Scheme* | Which network this ICA belongs to |
| *Member role* | principal / affiliate / association / processor / counterparty |
| *Capacity* | acquiring / issuing / both — see below |
| *Ownership* | "Ours" vs. counterparty (resolves DE 93/DE 100 on inbound messages to a name) |
| *Acquirer BIN (DE 31 sf 2)* | DE 31 (Acquirer Reference Data) subfield 2 — the 6 digits every outbound clearing identifier is built from. Confirmed by the GCMS manual (p. 39, p. 203) to be the same field the manual itself calls "Acquirer Reference ID." |
| *Issuer / card BIN* | A **different** concept — an actual card account-range prefix, not the acquiring institution's own BIN. Do not confuse the two; see the ⚠ below. |
| *GCMS endpoint* | The 7-digit delivery-location identifier the GCMS manual defines (p. 38) |
| *Settlement Service ID* | PDS 0159 subfield 4 — how an inbound Settlement Position Detail/1644 is attributed to a position |
| *Processing mode* | PDS 0122 (`P`/`T`) for that ICA's own files |
| *Status* | draft / active / suspended / inactive |

⚠ **Acquirer BIN ≠ card BIN.** A real, easy-to-make mistake: Mercury's real Acquirer BIN
(`957382`, 6 digits, DE 31 subfield 2) and its real card account-range BIN (`53255811`, 8 digits)
look similar but are different tables — the Acquirer BIN lives on the institution row above; the
card BIN belongs in `SchemeCore.BinRange` (`/admin/schemes/bin-ranges`), a wholly separate
screen. Conflating them would repeat the exact category error the original pre-spec build made
conflating DE 31 with DE 37.

**Capacity, and why an ICA can be both.** The GCMS manual's own worked example states outright:
*"Customer ID 001234 is both an issuer and an acquirer"* — acquirer/issuer is a **per-transaction
role** (decided by which DE carries the ID: DE 94 sending on a presentment = acquirer; DE 93
sending on a chargeback = issuer), not an inherent property of an ICA. Mercury's own real
membership nonetheless holds **two separate Customer IDs** — one dedicated to acquiring
(`035862`), one to its issuing program segment (`025241`) — a real member's own choice to
segment activity, which the spec permits but doesn't require. `Capacity` records that choice.
An **acquiring**- or **both**-capacity institution cannot be activated without an Acquirer BIN —
without one, DE 31 cannot be built, so every presentment submitted under that ICA would reject; a
pure **issuing** institution is exempt, since issuing activity never populates DE 31.

**Getting sample data to look at.** Real Mastercard data is seeded via
`mix run apps/da_product_app/priv/repo/seeds/mastercard_member_institutions_seed.exs`. Clearly
marked, non-activatable placeholder rows for Visa/JCB/Amex/RuPay/UnionPay — proving the
multi-scheme mechanism works beyond just Mastercard — are seeded via
`mix run apps/da_product_app/priv/repo/seeds/sample_scheme_member_institutions_seed.exs`. Sample
rows land `status: "draft"` with a notes field stating plainly they are not real.

---

## 5. Processing Information from Fraud Prevention Systems and Services

Mastercard records suspicious-transaction information through SAFE (System to Avoid Fraud
Effectively). Way4 has four SAFE touchpoints — outward reporting (R847), inward confirmations
(T848/T849), the mandatory monthly negative report, and file monitoring forms — and defers to a
separate "SAFE Support" document for the byte-level format.

**Mercury has none of that separate document either.** Both real Mastercard documents this
manual was built against were checked directly (`grep -i safe` across all 1300 + 496 pages) and
neither mentions SAFE once. Building a byte-level R847/T848/T849 codec today would mean inventing
a field layout — precisely the mistake the original pre-spec IPM build made and this whole
rebuild exists to correct. So SAFE is handled honestly in phases:

**Phase 1 — case tracking (built).** `RiskCore.SafeCase` gives compliance operations a real
system of record today, independent of whether/when the file format is confirmed:

- **Outward fraud reports** (`direction: "outward"`, `case_type: "fraud_report"`) — optionally
  linked to the `risk_core.RiskRuleHit` that triggered it, or created manually with no automated
  trigger at all.
- **The mandatory monthly negative report** (`case_type: "negative_report"`) — validated to
  *require* a `reporting_period` and to *reject* a specific `transaction_id`, since a negative
  report is a periodic filing, not a per-transaction one. Way4's own manual treats a missed
  negative report as a real compliance gap, not an optional nicety, and this validation makes the
  same mistake structurally impossible here.
- **Inward confirmations** (`direction: "inward"`, `case_type: "confirmation"`) — carrying
  whatever `scheme_case_reference` Mastercard's eventual response uses (format unknown, stored
  freeform until confirmed).
- **Merchant block/unblock** — added after real operational knowledge arrived ahead of the
  formal spec: a SAFE case is expected to drive a merchant enforcement decision, not just a
  report. `merchant_action` (`none`/`block`/`unblock`) plus reason/timestamp/actor record that
  decision; `RiskCore.Context.block_merchant_via_safe_case/3`,
  `unblock_merchant_via_safe_case/3`, and `merchant_blocked?/1` are the interaction points.

  ⚠ **Scope, stated exactly:** Mercury TMS does not sit in the real-time authorization path — the
  switch (`mercury_device_middlelayer`, outside this repository) does — and
  `core_transactions.risk_hold` only ever fires *after* the end-of-day batch sync, i.e. after a
  sale has already happened at the terminal. `merchant_blocked?/1` today is a correct,
  queryable **compliance fact** — it is **not yet wired to stop anything**, neither future
  settlement holds nor real-time terminal authorization. Wiring either is a distinct, undecided
  next step.

**NOT YET BUILT:** any screen for SAFE cases (all interaction today is via `RiskCore.Context`
directly); Phase 2 (the real file codec, once a SAFE specification exists); Phase 3 (automation —
auto-drafting cases from qualifying risk hits, scheduled monthly negative-report generation so it
can never be silently missed).

---

## 6. Generating Reports

Way4's `MC.Reports` menu folder lists thirteen items. Mercury's coverage:

| Way4 report | Mercury equivalent |
|---|---|
| MC Submission / Acknowledgement / Notification / Settlement / Settlement Results Report | **NOT YET BUILT** — depend on the outbound lifecycle (§1.2.3) and settlement-position ingestion, neither built yet |
| MC Conversion Report | **NOT YET BUILT** |
| IPM Incoming/Outgoing Files Summary Report | Partial — `/admin/clearing` dashboard gives batch counts; no amount/transaction-type breakdown |
| IPM Inward/Outward Monetary Transactions Report | Partial — `/admin/clearing/batches/:id` lists every decoded record; no grouped-by-currency/file report |
| ATM Location | N/A — no ATM estate |
| MC Reject Report | **NOT YET BUILT** — no reject feedback loop exists yet (nothing has been submitted to reject) |
| MC Dispute Statistics | Partial — `DisputeCore.Analytics` covers similar ground, not scheme-shaped |

**What Mercury has that this table doesn't need to mention twice:** every message class those
reports would summarise — presentments, chargebacks, addenda — is already classified and stored
with amounts, currencies, interchange rate designators and settlement dates on each row, grouped
by logical file. Building the reports above is aggregation over data that already exists, not
work waiting on ingestion.

**Verification tooling, which Way4 has no equivalent of:**
`mix generate_ipm_sample` builds a real, spec-valid sample logical file (First Presentments, a
Financial Detail Addendum, a First Chargeback), verifies its own round-trip through
`Ipm.FileReader` before writing anything, and produces three files at
`docs/settlement_recon/samples/`:

- `sample_ipm_clearing_file.ipm` — raw wire bytes (EBCDIC, RDW-framed; an ASCII variant is
  written to `ascii_variant/` for easy inspection)
- `sample_ipm_clearing_file.decoded.txt` — every message, annotated with real DE/PDS names —
  functionally Way4's Fig. 6 (Original Docs) and Fig. 7 (Log Fields) combined into one file
- `sample_ipm_clearing_file.decoded.json` — the same, machine-readable

```
mix generate_ipm_sample
mix generate_ipm_sample --encoding=ascii --framing=rdw --out=path/to/dir
```

---

## 7. Configuring Merchant Contracts

Way4 stores a scheme-assigned merchant identifier ("MasterCard Assigned ID") as a contract
parameter, settable at merchant and device-child-contract level.

The IPM equivalent, **PDS 0176 (Mastercard Assigned ID)**, is decoded on every inbound record
(`ClearingCore.Mastercard.IpmRecord.mastercard_assigned_id`) — the **inbound** half is closed.

**NOT YET BUILT:** storing this identifier on the merchant/location record itself, so it can be
*sent* on an outbound presentment and so it joins our own locations to Mastercard's own ADDQ/
Location Compliance records. `SettlementCore.TidMaster` and `MerchantCore.LocationDetail`
currently hold only Mercury's own identifiers (`mid`, `merchant_ref_number`, `tid[]`,
`mms_store_id`) — none of them carries a scheme-assigned ID field yet.

---

## 8. Reference: Where Each Concept Lives in Code

For anyone reading this manual with the codebase open — the direct index Way4's own manual
doesn't need (it only has one product to reference), but this one does:

| Concept | Module / table |
|---|---|
| Message decode/encode | `ClearingCore.Mastercard.Ipm.MessageCodec` |
| Bit maps | `ClearingCore.Mastercard.Ipm.Bitmap` |
| Data Element catalog | `ClearingCore.Mastercard.Ipm.DeCatalog` |
| PDS tag-length-data | `ClearingCore.Mastercard.Ipm.Pds`, `PdsCatalog` |
| EBCDIC/ASCII | `ClearingCore.Mastercard.Ipm.Charset` |
| Record framing (RDW) | `ClearingCore.Mastercard.Ipm.Framing` |
| Message classification | `ClearingCore.Mastercard.Ipm.MessageCatalog` |
| DE 31 / Luhn check digit | `ClearingCore.Mastercard.Ipm.AcquirerReferenceData` |
| Physical/logical file read | `ClearingCore.Mastercard.Ipm.FileReader`, `FileReader.LogicalFile` |
| Physical/logical file write | `ClearingCore.Mastercard.Ipm.FileWriter` |
| File ID sequence allocation | `ClearingCore.Mastercard.Ipm.FileSequence` |
| Ingestion (file → DB) | `ClearingCore.Mastercard.Ipm.Ingestor` |
| Outbound transport | `ClearingCore.Mastercard.Ipm.OutboundSubmitter` |
| SFTP transport | `ClearingCore.Mastercard.Ipm.SftpClient`, `InboundPollWorker`, `Scheduler` |
| Physical file record | `clearing_batches` table, `ClearingCore.ClearingBatch` |
| Logical file record | `clearing_logical_files` table, `ClearingCore.ClearingLogicalFile` |
| Message record | `mastercard_ipm_records` table, `ClearingCore.Mastercard.IpmRecord` |
| Reconciliation exceptions | `clearing_exceptions` table, `ClearingCore.ClearingException` |
| Matching against transactions | `SettlementCore.ClearingBridge` |
| Member Institution / ICA registry | `SchemeCore.MemberInstitution`, `scheme_member_institutions` |
| Card BIN ranges | `SchemeCore.BinRange`, `scheme_bin_ranges` |
| SAFE case tracking | `RiskCore.SafeCase`, `safe_cases` table |
| Sample file generator | `mix generate_ipm_sample` (`Mix.Tasks.GenerateIpmSample`) |

**Screens:**

| Path | Purpose |
|---|---|
| `/admin/clearing` | Dashboard — batch/exception counts by source and status |
| `/admin/clearing/files` | Upload + registry (both Visa TC-33 and Mastercard IPM) |
| `/admin/clearing/batches` | Full batch list, filterable |
| `/admin/clearing/batches/:id` | One batch's every decoded record, with match status |
| `/admin/clearing/exceptions` | Exception review queue, with resolve action |
| `/admin/schemes/member-institutions` | ICA / Customer ID registry, all schemes |
| `/admin/schemes/bin-ranges` | Card BIN/IIN range registry (distinct from Acquirer BIN, §4) |

**Configuration:**

```elixir
# config/config.exs
config :clearing_core, :mastercard_sftp,
  host: nil,   # set to enable; nil = disabled everywhere until a channel exists
  port: 22,
  user: nil,
  password: nil,
  key_path: nil,
  inbound_dir: "/outbound",
  outbound_dir: "/incoming",
  connect_timeout: 30_000

config :clearing_core, :ipm_storage_path, System.tmp_dir!()
```

---

*This manual is the operational companion to
`docs/settlement_recon/way4-mc-interface-similarity-gap.md`, which explains the reasoning and
gap analysis behind every "NOT YET BUILT" marker above. Update both together when a marked gap
gets closed.*
