Context
Today the library only supports LNbits. InvoiceFactory::getBackendForUser() hard-codes new LnbitsBackendInvoice(...), and InvoiceConfig::getBackendOptionsFor() drops the per-user type field, so the factory has no way to pick a different backend.
src/Invoice/InvoiceFactory.php — getBackendForUser() hard-wires LnbitsBackendInvoice.
src/Config/Backend/BackendType.php — enum with a single case Lnbits.
src/Config/LightningConfig.php — createBackendConfig() builds the per-user config from the JSON type.
src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php — requestInvoice(int $satsAmount, string $metadata, string $memo = ''): InvoiceTransfer.
src/Invoice/InvoiceConfig.php — getBackendOptionsFor() returns {api_endpoint, api_key} only (drops type).
Goal
Make invoice backends pluggable: adding a new backend (LND, Core Lightning/CLN, Alby Hub, Phoenixd, Strike, …) should be one class implementing BackendInvoiceInterface plus one BackendType case — no edits to InvoiceFactory.
Scope / Tasks
Implementation notes
- Backends make HTTP calls through the injected
HttpApiInterface (InvoiceDependencyProvider::HTTP_API) — reuse it; don't new an HTTP client inside a backend.
- Mirror the existing
BackendType::fromString() error style for unknown types.
- The unknown-type path is already guarded at config-parse time in
LightningConfig::createBackendConfig(); keep the factory-side resolution total over the enum (exhaustive match).
Acceptance criteria
Out of scope
- Real LND/CLN production integrations against live nodes (follow-up per backend).
References
BackendInvoiceInterface, LnbitsBackendInvoice, EmptyBackendInvoice in src/Invoice/Domain/BackendInvoice/.
Context
Today the library only supports LNbits.
InvoiceFactory::getBackendForUser()hard-codesnew LnbitsBackendInvoice(...), andInvoiceConfig::getBackendOptionsFor()drops the per-usertypefield, so the factory has no way to pick a different backend.src/Invoice/InvoiceFactory.php—getBackendForUser()hard-wiresLnbitsBackendInvoice.src/Config/Backend/BackendType.php— enum with a single caseLnbits.src/Config/LightningConfig.php—createBackendConfig()builds the per-user config from the JSONtype.src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php—requestInvoice(int $satsAmount, string $metadata, string $memo = ''): InvoiceTransfer.src/Invoice/InvoiceConfig.php—getBackendOptionsFor()returns{api_endpoint, api_key}only (dropstype).Goal
Make invoice backends pluggable: adding a new backend (LND, Core Lightning/CLN, Alby Hub, Phoenixd, Strike, …) should be one class implementing
BackendInvoiceInterfaceplus oneBackendTypecase — no edits toInvoiceFactory.Scope / Tasks
typeavailable at request time (stop dropping it ingetBackendOptionsFor()), so the factory can dispatch on it.BackendTypethat maps a type + its options to aBackendInvoiceInterface. AmatchonBackendType(mirroringLightningConfig::createBackendConfig()) is fine — the point is a single, obvious extension seam.InvoiceFactory::getBackendForUser()to resolve the backend through the registry instead ofnew LnbitsBackendInvoice(...).LnbitsBackendInvoicestays the LNbits implementation; no behavior change for existing configs.HttpApiInterface); otherwise document the extension steps and leave a scaffolding test that asserts the registry resolves eachBackendType.Implementation notes
HttpApiInterface(InvoiceDependencyProvider::HTTP_API) — reuse it; don'tnewan HTTP client inside a backend.BackendType::fromString()error style for unknown types.LightningConfig::createBackendConfig(); keep the factory-side resolution total over the enum (exhaustivematch).Acceptance criteria
BackendInvoiceInterfaceimpl + aBackendTypecase + the registrymatcharm.InvoiceFactorypublic shape unchanged.tests/Feature/InvoiceFacadeTest.php) still passes unchanged.BackendType.composer test-allgreen (psalm, phpstan level max, phpunit, rector).Out of scope
References
BackendInvoiceInterface,LnbitsBackendInvoice,EmptyBackendInvoiceinsrc/Invoice/Domain/BackendInvoice/.