core field — every transaction
processor-read — the adapter needs it for auth
crypto — behind the distilled interface
required to assemble canonical
1 · Transaction Identity
who/what/when — our own envelope
core
| Field | Type | Source | Notes |
| transactionIdreq | UUID | Foundry-issued | Our own correlation id, minted at engage. Threads logs, adapter calls, and result. |
| terminalIdreq | string | 9F1C / config | Terminal identifier from provisioning. Also the link back to which config-group / limits applied. |
| timestampreq | ISO-8601 | device + host | Transaction date/time. Carries 9A date + 9F21 time, normalized to UTC with local offset retained. |
| entryModereq | enum | cardType / POS | contactless · contact · swipe. v1 is contactless-first; field exists so contact reuses the same model. |
| transactionTypereq | enum | 9C | purchase · refund · void · auth-only. Drives both kernel start and adapter message type. |
2 · Money
amount + currency as first-class data, never per-processor branches
processor-read
| Field | Type | Source | Notes |
| amountAuthorizedreq | minor-units int | 9F02 | Integer minor units (cents/centavos) — never float. Avoids rounding drift across the ISO boundary. |
| amountOther | minor-units int | 9F03 | Cashback / secondary amount. Optional; present for cashback flows. |
| currencyreq | ISO 4217 num | 5F2A | 840 USD · 484 MXN. A transaction attribute, not a processor one — the adapter reads it; no currency-specific code paths. |
| currencyExponent | int | 5F36 | Minor-unit exponent, carried per transaction rather than assumed. The shared money.h formatter is exponent-aware across 0-decimal (JPY), 2-decimal (USD/MXN/EUR), and 3-decimal (BHD/KWD) currencies — fixing an earlier engine bug that hardcoded 2 regardless of currency. |
3 · Card & Application
what was tapped — masked/tokenized, never clear PAN
processor-read
| Field | Type | Source | Notes |
| panTokenreq | ref / token | encryptedTags | Reference to the encrypted PAN — never clear PAN in canonical. The clear value lives only inside the P2PE/decrypt boundary. |
| panMasked | string | maskedTags | Display-safe masked pan (first6/last4 policy). For receipts & UI only. |
| cardholderName | masked string | 5F20 | Often absent on contactless; masked when present. |
| expiry | YYMM | 5F24 | Application expiry. Adapter may need it for the auth message. |
| aidreq | hex | 4F / 84 | Application Identifier — which app ran (Visa/MC/etc.). Drives brand routing in the adapter. |
| appLabel | string | 50 | Human label of the application. Display + diagnostics. |
| panSequence | int | 5F34 | PAN sequence number — disambiguates multiple cards on one account. |
4 · EMV Outcome & Cryptogram
the kernel's decision + the crypto the host must verify
crypto
| Field | Type | Source | Notes |
| cryptogramreq | hex | 9F26 | The ARQC the kernel generated. The adapter forwards it to the host for online verification. |
| cryptogramTypereq | enum | 9F27 | ARQC · TC · AAC — online-requested vs approved-offline vs declined. Decides whether the adapter is even called. |
| atc | hex | 9F36 | Application Transaction Counter — replay/clone signal. Host-side fraud relevant. |
| aip | hex | 82 | Application Interchange Profile — what the card supports (SDA/DDA/CDA, CVM). |
| issuerAppData | hex | 9F10 | Issuer Application Data — opaque to us, meaningful to the issuer; passed through verbatim. |
| unpredictableNumber | hex | 9F37 | Terminal nonce bound into the cryptogram. Part of the auth payload. |
crypto boundary
This group is carried, not computed. Foundry never generates or validates a cryptogram — the kernel makes it, the issuer verifies it. Canonical simply transports 9F26 + its supporting tags to the adapter, and the adapter relays them to the host. That's the distilled common interface in practice: move the crypto, don't touch the crypto.
5 · Verification & Risk
how the cardholder was verified + kernel risk flags
core
| Field | Type | Source | Notes |
| cvmPerformed | enum | 9F34 | no-CVM · CDCVM · signature · online-PIN. Unattended (Kiosk V) vs attended (VP3350) differ here — but it's a read field, set by policy in config. |
| tvr | hex | 95 | Terminal Verification Results — the kernel's per-check bitfield. Host risk input. |
| tsi | hex | 9B | Transaction Status Information — which functions were performed. |
| tacDefault / tacOnline / tacDenial | hex | config | Terminal Action Codes that shaped the decision. Useful for audit & dispute trail. |
6 · Locale & Context
language + deployment context — data-driven, two locales now
core
| Field | Type | Source | Notes |
| languagereq | ISO 639 | 5F2D | en · es. Distinct from currency-840 so the two never conflate. Drives receipts/prompts; more locales are just more data. |
| countryCode | ISO 3166 num | 9F1A | Terminal country. 840 US · 484 MX. Separate axis from currency & language. |
| merchantContext | object | config | MID, MCC, deployment profile (transit kiosk vs retail). Pulled from the provisioning manifest, not the card. |
7 · Lifecycle & Result
state machine + the round-trip back from the host
core
| Field | Type | Source | Notes |
| statereq | enum | Foundry SM | engaged · captured · normalized · online-pending · completed · declined · cancelled · timed-out. The state machine's view. |
| hasReversal / hasAdvice | bool | emv_hasReversal / emv_hasAdvise | Kernel flags the SDK surfaces directly — signals the adapter may owe a reversal or advice message. |
| authResponse | object | adapter ← host | Filled by the adapter on return: response code, auth code, ARPC, network refs. The one group written after the processor boundary. |
| rawTags | Map<tag,bytes> | IDTEMVData | Escape hatch — the full original tag-bag retained, so an adapter needing a tag we didn't surface isn't blocked. Read-only. |
escape hatch
rawTags is deliberate: the named fields cover the 95% case, but rather than risk the model being closed too early, the full tag-bag rides along read-only. If a future processor needs an exotic tag, the adapter reads it from rawTags — no core change, no re-cut of the mold. It keeps the canonical model extensible without versioning churn.
8 · TLV Filtering & Views
derived per-processor — filter, never mutate
processor-read
| Construct | Type | Source | Notes |
| tlvFilterPolicy | allow/deny + order | adapter-declared | Each processor casting declares which tags it wants, in what order. Santander's policy ≠ EverTec's. Bundled in the adapter, never in core. |
| getTlvView(policy) | method → TLV set | Foundry seam | Pure function: canonical in → filtered/ordered TLV out. Returns the processor-specific view without mutating the canonical object. The method the adapter calls. |
| buildDOL(dolPolicy) | method → byte string | Foundry seam | Same mechanism for Data Object Lists — assembles tags in a processor's required order/format. DOL construction is just a filter policy with layout. |
filter, don't mutate
Some processors require a specific TLV subset — only certain tags, in a certain order. That's a per-adapter concern, so it sits on the adapter side of the boundary. Canonical stays the complete neutral superset; the adapter supplies a tlvFilterPolicy and Foundry exposes getTlvView() to derive that processor's tailored set on demand. Two adapters can ask for two different views of the same transaction, and audit/logging still sees the full canonical. Same engine handles buildDOL() for required tag-list ordering.