Paying.co · Foundry · Plugins & Loading · Extending without recompiling
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.
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.
The default. The adapter is compiled into the engine binary.
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.
.so pluginCompiled separately, discovered at runtime via dlopen.
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.
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.
An adapter or HAL shipped as a separate installable APK, discovered by metadata.
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 Android SDK exposes the loaders directly; each is a thin bridge over the native loader described above.
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.