Paying.co · Foundry · Plugins & Loading · Extending without recompiling

Plugins. Adapters and drivers, loaded at runtime.

Doc 04 defined the adapter contract. This doc covers how an implementation of that contract actually reaches the engine. There are three routes, and the engine cannot tell them apart: a statically linked C adapter, a native .so discovered at runtime, or a Kotlin APK plugin on Android. The same story holds for the card-reader HAL. The core never changes.

three routes static · .so · APK discovery dlopen gate FDY_ENABLE_PLUGINS engine unchanged

The premise · the engine imports a contract, not an adapter

Everything below is a delivery mechanism for the same vtable. A plugin is not a second-class adapter; it is the identical fdy_processor_adapter_t, handed to the registry from a different place.

Because the engine resolves adapters through a registry rather than linking them directly, an adapter can arrive at build time or at run time and the transaction path is unchanged. This is what makes it possible for a third party to ship a processor adapter without a fork, a patch, or a rebuild of the core.

Route 1 · statically linked C

The default. The adapter is compiled into the engine binary.

Static casting

Create a sealed directory under foundry-processors/src/<name>/, implement the vtable, and register it in the build. The adapter is linked into the .so at build time and is available with no discovery step. This is how the shipped castings are delivered.

Trade-off: requires a build of the engine. Best when Paying.co owns the adapter.

Route 2 · native .so plugin

Compiled separately, discovered at runtime via dlopen.

Processor adapter plugin fdy_plugin_create_adapter

A shared library exporting the factory symbol fdy_plugin_create_adapter, which returns a fully-formed fdy_processor_adapter_t. Point the engine at a directory and it loads every plugin it finds, registering each into the adapter registry.

Gated behind FDY_ENABLE_PLUGINS. When the flag is off, the loader compiles to a no-op stub, so a build that forbids dynamic loading carries none of the machinery.

HAL driver plugin fdy_hal_create

The card-reader HAL is loaded the same way. A driver is a shared library exporting fdy_hal_create, returning an fdy_reader_hal_t. Load one by path, or scan a directory for all of them. This is how the ID TECH HAL is delivered.

Consequence: a new reader does not require a new engine. The HAL is a boundary, and drivers plug into it.

Route 3 · Kotlin APK plugin (Android)

An adapter or HAL shipped as a separate installable APK, discovered by metadata.

APK-hosted plugin

An APK declares itself through manifest metadata and is loaded with a PathClassLoader. Two shapes are supported: a native-backed plugin declaring co.paying.foundry.HAL_PLUGIN with the bare .so name, or a Kotlin-backed plugin declaring co.paying.foundry.HAL_MANAGER with a fully-qualified class name.

This is the route that lets a processor or a device vendor distribute their own integration through the Play Store or an MDM push, independent of the host application's release cycle.

The Kotlin surface

The Android SDK exposes the loaders directly; each is a thin bridge over the native loader described above.

Loaders

FoundryPluginLoader — discovers and registers native processor-adapter plugins from a directory.

FoundryHalPluginLoader — loads a HAL driver by path, or every driver in a directory.

FoundryHalApkLoader — resolves HAL plugins hosted inside installed APKs.

Each exposes isSupported(), so an application can degrade gracefully on a build where plugin loading is compiled out.

Choosing a route

Use a static casting when Paying.co owns the adapter and it ships with the engine. Use a native plugin when an adapter or reader driver must be delivered or updated on its own cadence, or when a third party owns the code and must not see the engine source. Use an APK plugin on Android when the integration is distributed and updated independently of the host application. In all three cases the contract is identical and the engine is untouched.