ISO 8583 · 0200 DE 55 · ICC 9F26 → host 0210 · response ARPC ← issuer
Paying.co · Foundry · The Adapter Spec · how a casting is built

The casting. What a processor
adapter actually implements.

The first thing built against the mold. An adapter takes a canonical transaction, gets it authorized by one processor, and returns a canonical result — nothing more. Same contract for every processor. In the native C engine this is a function-pointer vtable (fdy_processor_adapter_vtbl_t); the Kotlin SDK exposes the same shape as an interface. This spec defines that contract and walks a worked example, then lists the processors shipped against it.

status shipped implements fdy_processor_adapter_vtbl_t canonical in → canonical out shipped NAB · TSYS · Santander
The contract · one shape, every processor

An adapter is a pure translator bounded on both sides. Canonical on the inside (the mold from doc 03), a published host spec on the outside (ISO 8583 / 20022). It never touches the kernel, never sees raw TLV, never makes an EMV decision — it formats, sends, parses, and returns.

◆ input · from the engine

Canonical Transaction

The assembled object — amount, currency, panToken, cryptogram, aid, risk tags. Already normalized at the seam; the adapter only reads it.

ADAPTER

◆ output · back to the engine

Canonical Result

Fills authResponse — response code, auth code, ARPC, network refs. The engine feeds it to the kernel via emv_completeTransaction. Adapter is done.

outside the adapter, on the wire: canonical → ISO 8583 / 20022 → processor → response → canonical
The interface · what every casting implements

Foundry defines the adapter contract; each processor implements it. In the C engine that contract is fdy_processor_adapter_vtbl_t, a struct of function pointers. The engine depends only on this vtable — never on a concrete processor or its library. A single authorize() branches on the canonical transaction type (PURCHASE, REFUND, VOID, AUTH_ONLY, COMPLETION) rather than widening the vtable per operation. That dependency direction is the whole reuse guarantee.

fdy_processor_adapter_vtbl_t adapter-side · core depends on this, not the reverse
authorize(canonical)
→ AuthResult
the core method

Canonical in → host message → send → parse response → AuthResult out. The one call the engine makes at the online-auth step. Everything else supports this.

tlvFilterPolicy()
→ FilterPolicy
declares; Foundry applies

The adapter declares which tags it needs and in what order. Foundry's getTlvView(policy) derives the processor-specific TLV set without mutating canonical. NAB's policy ≠ Santander's.

buildHostMessage(view)
→ bytes
processor-specific

Assembles the actual host request — ISO 8583 fields or ISO 20022 elements, DE 55 / ICC data built from the filtered view. This is where the processor's dialect lives.

parseHostResponse(bytes)
→ AuthResult
processor-specific

Reads the response back to canonical — response code, auth code, ARPC, network references. Maps the processor's codes onto Foundry's normalized result enum.

keyContext()
→ KeyContext
crypto · behind the distilled interface

Supplies the processor's key/HSM specifics — zone keys, working-key rotation, MAC scheme — through the common crypto interface. The adapter provisions; it never implements primitives.

capabilities()
→ Capabilities
declarative

What this processor supports — currencies, message format, reversal/advice handling, partial-auth. Lets the engine know whether a hasReversal flag needs an action.

dependency direction The engine imports the fdy_processor_adapter_vtbl_t contract, never a concrete adapter. A registry resolves the active adapter at runtime. So NAB's ISO 8583 build, TSYS's gateway dialect, and Santander's 0200 format never share a dependency graph — each is sealed inside its own casting. Adding a processor in the C engine is mechanical: create a src/<name>/ directory, add one line to the build PROCESSORS list, and one #include to processors.h. No engine change. That is Checkpoint A — proven across the shipped roster.
Who owns what · the line the casting must not cross

Everything universal stays in Foundry; only the processor-specific translation lives in the adapter. If a casting starts reaching back into kernel or EMV logic, the seam has leaked.

Foundry core never in the adapter

built once, reused by every casting
  • Engage, capture, TLV parse, normalize to canonical
  • The getTlvView() / buildDOL() engine
  • State machine, cancel / reset / timeout
  • Kernel completion — emv_completeTransaction, ctls_displayOnlineAuthResult
  • The distilled common crypto interface
  • Observability, audit, PCI-safe logging

The casting per processor

cast against the contract, sealed inside its module
  • The host message format (ISO 8583 / 20022 dialect)
  • Its tlvFilterPolicy — which tags, what order
  • Response-code → canonical result mapping
  • Processor key ceremony & HSM specifics via keyContext()
  • Host transport / comms library (bundled, private)
  • Reversal / advice / partial-auth quirks
Shipped castings · processors live in the C engine

The production adapters built against fdy_processor_adapter_vtbl_t in the native C engine, each sealed in its own src/<name>/ directory with no shared dependency graph. The full roster — shipped and planned — lives on the Processor Castings page; the engine suite runs green under -std=c99 -Wall -Wextra -Wpedantic.

NAB · North American Bancard
Canonical → NAB ISO 8583 MTI 0100 → AuthResponse. Stub APPROVED proving the casting runs; ISO build/parse and host send land at the documented TODO. USD.
TSYS · Global Payments
TSYS dialect on the shared vtable. Working stub APPROVED, processor specifics confined to the build/parse seams so routing never sees them.
Santander · Mexico
Canonical → Santander ISO 8583 0200 → AuthResponse. Derives the DE 55 view, then returns FDY_ERR_NOT_IMPLEMENTED pending host spec + test endpoint. USD · MXN.
mock · test
The reference casting that exercises the engine and stands in for any adapter whose host isn't wired — the integration-test stand-in for Santander.

Each adapter carries two TODO seams — the ISO 8583 / schema build and parse functions — where live host wiring lands. These are pure functions isolated from routing and I/O, safe to upgrade without restructuring. Not yet wired: live HTTP transports, a credential/config store, and the adapter factory.

Worked example · a Santander-style casting

Adapter #1. What authorize(canonical) does, end to end, the first time the engine calls it at the online-auth step.

1
Read the canonical transaction
The engine hands over the assembled object. Santander adapter reads amount, currency (840/484), panToken, cryptogram, aid, tvr, atc — by name, not tag.
2
Request its TLV view
Calls Foundry's getTlvView(santanderPolicy) to get exactly the tags Santander wants for DE 55, in their required order. Canonical stays untouched.
3
Build the host message
buildHostMessage() assembles Santander's ISO 8583 0200 authorization with DE 55 ICC data, applying the MAC via keyContext(). The processor dialect lives entirely here.
4
Send & receive — over the casting's own library
Transmits to Santander's host through the bundled comms library and awaits the 0210 response. Timeout / retry policy is the adapter's, declared in capabilities().
5
Parse response → canonical result
parseHostResponse() extracts response code, auth code, and ARPC, maps Santander's codes onto Foundry's result enum, and returns an AuthResult. The casting's job ends here.
6
Engine completes — back in core
Foundry takes the result and finalizes: emv_completeTransaction + ctls_displayOnlineAuthResult feed the ARPC to the kernel. Universal again — the adapter is no longer in the loop.
the test When the second casting (Global Payments, or EverTec) is built, the only new code is a new class implementing the same six methods plus its own filter policy and dialect. If the engine needs zero changes, the seam held and the mold is proven. If it doesn't, the canonical model needs one more field — better found at casting #2 than #5.
◆ Checkpoint A · the go/no-go this doc sets up
Does adapter #2 leave the engine untouched?

The adapter spec exists to make this question answerable. One casting proves the interface compiles; two castings prove it's reusable. That measurement has now been run: multiple processors — NAB, TSYS, and the Santander (Mexico) casting — were built against the same fdy_processor_adapter_vtbl_t contract with zero core files changed. That validates the entire Foundry thesis in practice, not just on paper.