# Way4 "JCB Interchange Interface" vs. Mercury — Similarity & Gap Analysis

**Source reviewed:** `docs/Switch-System/way4-jcb-interface.txt` (OpenWay Way4™ JCB Interchange
Interface System Administrator Manual, R/N 03.47.30, 25.09.2019) plus the 7 figures supplied
separately in chat (Fig. 1–7: interface functionality, WAY4 import data model, JCB File
Management menu tree, JCB Received/Sent Files forms, Original Docs form, Log Fields form).

**Also worked against:**
- `docs/Scheme-System/jcb/batch_interface_guide/Batch Interface Guide.pdf` — the real *System
  Specifications – Batch Interface Guide* (JCB International, April 2025, 328 pages). Chapters
  3 (Interchange File), 5 (Stop Data File), 7 (IIN File) and 8 (Currency Rate Information) read
  in full; Chapter 4 (Merchant Data File) and Chapter 6 (Negative Data Update) not yet read —
  see §6 below.
- `docs/Scheme-System/jcb/sample_data_and_reports/` — real raw sample files: outgoing
  Presentment (ASCII and EBCDIC, with and without RDW), Acknowledgement, Reconciliation Data,
  File Rejection, IIN files (Full and Maintenance, ASCII CRLF/LF and EBCDIC), and sample reports
  (Settlement Report Summary/Detail, Fee Invoice, Activity Report, Currency Rate).
- `docs/Scheme-System/jcb/Chargeback_codes_-_JCB.pdf` — real chargeback reason codes, not yet
  consumed by this build.

Every spec claim in this document carries its section number; every codec claim was additionally
verified against real sample bytes, not just spec prose — see each module's own moduledoc for
the exact byte offsets checked.

**Compared against:** this umbrella as of `feature/jcb-clearing`, principally `clearing_core`,
`settlement_core`, `scheme_core` and the `platform_web` Clearing screens — mirroring the same
real-spec build already done for Mastercard IPM (`way4-mc-interface-similarity-gap.md`), at the
same scope, holding the same standard: nothing here is a guess presented as a spec fact.

**Author's framing:** JCB's Batch Interface Guide, like Mastercard's own IPM spec, is silent on
whether Mercury is a principal or sponsored member — that is a business/onboarding fact this
codebase cannot derive from a PDF, and every module that would need it (`Jcb.FileWriter`,
`SettlementCore.JcbOutboundPresentment`) takes the licensee number as a caller-supplied argument
rather than assuming one. This is the one deliberately unverified assumption in this build, same
role Mastercard's own framing-mechanism question played before the GCMS Reference Manual settled
it — flagged here rather than silently baked into a hardcoded constant.

Verdicts used throughout:

- **MATCH** — we do the same job as Way4, possibly by a different mechanism.
- **GAP** — Way4 does it, we would need it, we don't have it yet.
- **PARTIAL** — real, working core logic exists; the operational wrapper (UI, mix task, or a
  named caveat) is incomplete.
- **N/A** — genuinely not ours to do.
- **UNDOCUMENTED** — named in Way4's own menu tree, zero elaboration in the manual's text.

---

## 1. Executive summary

Unlike the start of the Mastercard effort, real source material existed from day one for JCB —
both the Way4 admin manual and a current (April 2025) JCBI specification, plus real sample data
in every relevant format. That changed the build order: the codec, DE/PDE catalogs, message
framing and file reader/writer were built and byte-for-byte validated against real sample files
*before* any screen was written, rather than needing a later corrective pass the way
`mastercard_ipm_records` did.

| Area | Status |
|---|---|
| Core Interchange File codec (bitmap, RDW framing, DE/PDE catalog, message codec) | **MATCH** — validated byte-for-byte against real sample files |
| File Reader/Writer, logical-file structural validation | **MATCH** |
| Persistence (`JcbInterchangeRecord`, `ClearingLogicalFile` reuse) | **MATCH** |
| SFTP transport | **MATCH** — same primitive Mastercard's own client implements |
| File Management (Received/Sent/Pending/Rollbacked/Clearing/Documents) | **MATCH**, with one **PARTIAL** (Clearing-vs-Documents split, §3) |
| Daily Procedures — Inward/Outward Processing | **PARTIAL** — same masked-PAN caveat Mastercard's outbound presentment has |
| Daily Procedures — Stop List load, BIN Table Import, Currency Rate load | **PARTIAL** — real parsers exist; only BIN Table Import has a mix task wired |
| Reports (7 of 7 named in the manual) | **MATCH**, with 2 **PARTIAL** (fee accrual not modeled) |
| Merchant Management / JCB Merchants Export | **PARTIAL** — real Ch.4 parser+builder, real TidMaster/LocationDetail-backed export, mix task only |
| Negative Data Update (Chapter 6) | **PARTIAL** — real Ch.6 parser+builder, issuer-originated, no caller in this codebase yet |
| Exception Lists | **UNDOCUMENTED** |

---

## 2. Core Interchange File codec — MATCH, real-spec-validated from the start

`ClearingCore.Jcb.*`: `Bitmap`, `Framing` (RDW), `Charset` (CP037 EBCDIC/ASCII), `DeCatalog` (43
entries, §3.7), `PdeCatalog` (~90 entries, §3.8), `MessageCatalog` (Table 3-2-2-1), `Pde`
(number-length-value packing, §3.2.5), `Message`, `MessageCodec`, `FileReader`, `FileWriter`.

Two confirmed structural differences from Mastercard's own IPM codec, not assumed by analogy:

- **RDW framing is exclusive, not inclusive.** JCB's RDW is a plain 4-byte big-endian integer
  giving the message length *excluding* the RDW's own 4 bytes (§3.2.6, confirmed against the
  spec's own worked example: a 96-byte message → RDW `X'00000060'`). Mastercard's RDW is a
  2-byte length + 2 zero bytes, *including* the RDW's own 4 bytes.
- **A duplicate PDE is silently discarded, not rejected.** §3.2.5: *"No duplicate PDE can be used
  in an Interchange Message. If there is duplication, only one PDE is read but the other is
  discarded"* — the opposite of Mastercard's own PDS rule, which rejects the whole message on a
  duplicate tag. `Jcb.Pde.decode_carriers/1` implements the discard rule deliberately, not as an
  oversight relative to the Mastercard codec it otherwise mirrors.

Validated end to end against `Presentment_outgoing_ASCII_without_RDW.txt` and its EBCDIC
counterpart: a real File Header (DE 24=689, DE 33=888888, PDE 3901 File ID) decodes and
round-trips byte-for-byte in both encodings; a real 12-message logical file (File Header + 10
Presentments + File Trailer) independently confirms PDE 3902's own definition — *"the sum of all
Bit 4 amount in an Interchange File"* — sums to exactly the trailer's declared 34000, and PDE
3903's declared count of 12 matches the actual message count.

## 3. File Management — MATCH, with one named PARTIAL

Way4's ten `JCB → JCB.File Management` menu items (Fig. 3) map onto two LiveViews:

- `ClearingLive.JcbFiles` (`/admin/clearing/jcb/files`) — Received/Sent/Incoming Pending/Outgoing
  Pending/Rollbacked Incoming/Rollbacked Outgoing, one module with `direction`/`view` query
  params, fields mirroring Way4's own grids (Fig. 4/7) field-for-field, including the two fields
  the manual itself says are always empty for JCB (*Result*, *Parms*).
- `ClearingLive.JcbDocuments` (`/admin/clearing/jcb/documents`) — Inward/Outward Clearing and
  Inward/Outward Documents, a cross-batch message search mirroring the Original Docs grid
  (Fig. 5).

**The one real gap in this section**: the manual never defines the difference between "Clearing"
and "Documents" as menu items — both would presumably show the same Original Docs grid. This
build's own reading (implemented, not guessed at silently) is
`MessageCatalog.category/1 ∈ [:financial, :chargeback, :fee_collection, :addendum]` for
"Clearing" vs. every stored message type for "Documents" — documented as this build's own
judgment call in `JcbDocuments`'s moduledoc, not a confirmed Way4 behavior. Revisit if JCBI or
Way4 documentation ever states the real rule.

## 4. Daily Procedures — PARTIAL across the board, same shape as Mastercard's own gaps

- **Inward/Outward Processing**: real. Inward is the `JcbFiles` upload path through
  `Jcb.Ingestor`; Outward is `SettlementCore.JcbOutboundPresentment.build_and_submit/4`, selecting
  eligible `core_transactions`, encoding real Presentments (DE 31 built via
  `Jcb.AcquirerReferenceData`/`Jcb.ArdSequence`, confirmed against the real sample's own DE 31),
  and submitting via `Jcb.OutboundSubmitter`. Same **known limitation** as
  `SettlementCore.MastercardOutboundPresentment`: DE 2 uses `core_transactions.masked_pan`, so a
  built file is not submission-ready until a full-PAN/token source exists — named on every
  caller-facing surface, not hidden.
- **Load JCB Stop List / Close Inactive JCB Stop List Records**: `Jcb.StopDataFile` parses both
  the Full (§5.2.3) and Maintenance (§5.3.3) record layouts — including a **third real vocabulary
  difference** from the IIN File's Operation Sign codes: Stop Data's Maintenance Operation Sign
  is `1`/`9` (Add/Delete), not IIN's `'01'`/`'02'`/`'03'` (Add/Delete/Update), and Stop Data has no
  update operation at all (the Maintenance Trailer's own Record Count-Update field is marked
  *"reserved for future use"* by the spec itself). No real Stop Data File sample ships in the
  repo (unlike IIN), so this parser is tested against the spec's own field tables only, not a
  real byte fixture. No import mix task or Close-Inactive delete logic wired yet.
- **JCB BIN Table Import**: `Jcb.IinFile` parses both Full (§7.2.3) and Maintenance (§7.3.3)
  variants — validated byte-for-byte against all four real sample files (ASCII CRLF/LF, EBCDIC,
  Maintenance) — and `mix jcb.load_iin_table` loads them into `scheme_bin_ranges`, smoke-tested
  end to end against the real 2616-row sample. This is the one Daily Procedure item with full
  parser-to-database wiring.
- **Load Currency Rate**: `Jcb.CurrencyRateFile` parses the real CSV format (Chapter 8, both the
  "without minor unit" and "with minor unit" variants), validated against the real
  `Sample_CurrRateInfo_ASCII(CRLF).txt` (162 rows) and the spec's own two worked examples. No
  import task wired yet — this file type has no obvious destination table in this schema today
  (unlike BIN ranges), which is a real design question, not an oversight.

## 5. Reports — MATCH (5 of 7), PARTIAL (2 of 7)

All seven items under `JCB → JCB.Reports` (Chapter 4) are real, queryable pages at
`/admin/clearing/jcb/reports/:report`:

- Incoming/Outgoing Files Summary, Inward/Outward Monetary Transactions — full **MATCH**. The
  Monetary reports use the grouping the manual actually states — *"grouped by settlement
  currency and clearing file number"* — a different grouping from Mastercard's own equivalent
  (message type + currency), built to match the real wording rather than copied from the
  Mastercard screen.
- Dispute Statistics — **MATCH**, real chargeback-cycle count/amount aggregation by transaction
  type.
- Acquiring/Issuing Business Report — **PARTIAL**. Transaction-activity aggregation (by which
  side one of Mercury's own ICAs sits on) is real; the manual's own *"quarterly reports... to
  reflect accrued fees"* names fee accrual specifically, and no fee-accrual ledger exists in this
  codebase to report on. The transaction-activity half is genuine; the fee half is a named gap,
  not a silent omission.

## 6. Merchant Data File (Chapter 4) — PARTIAL, real end to end

"JCB Merchants Export" is real: `Jcb.MerchantDataFile` parses and builds the real 850-byte
fixed-record format (§4.3.1 Header / §4.3.2 Data — all 54 fields, independently verified to sum
to exactly 850 bytes — / §4.3.3 Trailer), and `SettlementCore.JcbMerchantExport` maps Mercury's
own `SettlementCore.TidMaster` (name, MCC, mobile) joined with `MerchantCore.LocationDetail`
(address/city/state/postal/country — the same table the Mastercard ADDQ compliance build already
populates) onto it, smoke-tested end to end against the dev DB with a real merchant row.

No real Merchant Data File sample ships in the repo, so this parser is tested against the spec's
own field tables only, the same discipline `Jcb.StopDataFile` used. Three real, named limitations,
not silently absorbed:

- **This module never decides which merchants are "JCB merchants."** `TidMaster.enabled_schemes`
  reflects MMS's own product taxonomy, not card-scheme codes — nothing in this codebase confirms
  its values are ever `"jcb"`. `build/3` takes an explicit MID list from the caller instead of
  guessing at a filter.
- **Addition only.** Revision/Cancellation need to know what was already sent to JCBI in a prior
  export; no such tracking exists (the same gap `MastercardOutboundPresentment` has for
  re-presentment tracking).
- **Country code format mismatch, confirmed via a real smoke test.** `LocationDetail.country_code`
  is ISO 3166-1 alpha-2; Chapter 4's Field 35 wants alpha-3 (confirmed against the IIN File's own
  Issuer Country field, which states alpha-3 explicitly). No alpha-2→alpha-3 table exists in this
  codebase, and fabricating one for ~190 countries without a verified source was rejected in favor
  of naming the gap — the smoke test shows exactly what lands (`"AE "` instead of `"ARE"`).

Only a `mix jcb.export_merchants` task exists — no UI trigger, mirroring "JCB BIN Table Import"'s
own mix-task-not-screen precedent.

## 7. Negative Data Update (Chapter 6) — PARTIAL, real, closes the last unread chapter

`Jcb.NegativeDataFile` parses/builds the real 65-byte fixed-record format (§6.3.1 Header /
§6.3.2 Data / §6.3.3 Trailer), tested against the spec's own four worked Stop Sign/Region Sign
examples (Tables 6-3-2-2 through 6-3-2-5) byte-for-byte, not invented fixtures. Two real findings:

- **Chapter 6 names its own audience explicitly as Issuers** (§6.1/§6.2: *"Requirements for
  Issuers which use IBM or its compatible system for Negative Data Update File generation"*) —
  Mercury is acquirer-side throughout this codebase, so no caller originates one of these files
  today. The parser/builder is real regardless (an acquirer receiving this file from JCBI, or a
  future issuing capability, would need it), but this is a scope note worth keeping, not a hedge
  on correctness.
- **Negative Data and Stop Data are the same underlying concept from two angles**, confirmed by
  the spec's own field design: Field 6 (Stop Sign) and Field 7 (Region Sign, 5 sub-fields) let one
  Negative Data record update the Stop List directly. This module does not attempt to keep a
  `Jcb.StopDataFile` in sync automatically — the spec never states that workflow, so inventing it
  would be a guess.

No real Negative Data Update File sample ships in the repo, so — like Stop Data File — this
parser is tested against the spec's own tables and worked examples only.

Reading Chapter 6 closes every chapter of the Batch Interface Guide this build set out to cover
(§3, 4, 5, 6, 7, 8 all read and implemented).

## 8. Remaining named gaps

- **JCB Merchant Management** and **Exception Lists** — named in Way4's own menu tree (Fig. 3)
  with zero elaboration anywhere in the manual's text, the same honest treatment Mastercard's own
  "MC. Details"/"Collect Doc Statistics" got: **UNDOCUMENTED**, not guessed at.
- **Import/trigger wiring** for Stop Data File and Currency Rate File — both have real, tested
  parsers (and Stop Data now has a real builder too) but no `mix` task or UI trigger, unlike IIN
  File (full end-to-end wiring) and Merchant Data File (mix task wiring).

## 9. What this build deliberately did not copy from Mastercard's codec

Per the explicit build decision ("build JCB's own codec now, share later"), every JCB module is a
standalone implementation, not a shared abstraction over the Mastercard one — `Jcb.Bitmap`,
`Jcb.Charset` and `Jcb.AcquirerReferenceData`/`Jcb.ArdSequence`/`Jcb.FileSequence` are structurally
close to their Mastercard counterparts (same ISO 8583:1993 base, same allocation pattern) but are
real duplicates, confirmed independently against JCB's own spec text and sample data rather than
assumed identical. Extracting a shared ISO 8583 layer is a deliberate future refactor once both
codecs have proven stable in production, not a step skipped by this build.
