Skip to content

Pluggable multi-backend registry (LND / Core Lightning / Alby / …) #26

Description

@Chemaclass

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.phpgetBackendForUser() hard-wires LnbitsBackendInvoice.
  • src/Config/Backend/BackendType.php — enum with a single case Lnbits.
  • src/Config/LightningConfig.phpcreateBackendConfig() builds the per-user config from the JSON type.
  • src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.phprequestInvoice(int $satsAmount, string $metadata, string $memo = ''): InvoiceTransfer.
  • src/Invoice/InvoiceConfig.phpgetBackendOptionsFor() 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

  • Keep the per-user type available at request time (stop dropping it in getBackendOptionsFor()), so the factory can dispatch on it.
  • Introduce a backend registry/factory keyed by BackendType that maps a type + its options to a BackendInvoiceInterface. A match on BackendType (mirroring LightningConfig::createBackendConfig()) is fine — the point is a single, obvious extension seam.
  • Refactor InvoiceFactory::getBackendForUser() to resolve the backend through the registry instead of new LnbitsBackendInvoice(...).
  • LnbitsBackendInvoice stays the LNbits implementation; no behavior change for existing configs.
  • Add a second concrete backend as a proof of extensibility only if it can be unit-tested without a live node (e.g. a thin CLN/LND REST adapter behind HttpApiInterface); otherwise document the extension steps and leave a scaffolding test that asserts the registry resolves each BackendType.

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

  • Adding a backend requires touching only: a new BackendInvoiceInterface impl + a BackendType case + the registry match arm. InvoiceFactory public shape unchanged.
  • Existing LNbits feature test (tests/Feature/InvoiceFacadeTest.php) still passes unchanged.
  • New unit test proves the registry returns the correct implementation per BackendType.
  • composer test-all green (psalm, phpstan level max, phpunit, rector).

Out of scope

  • Real LND/CLN production integrations against live nodes (follow-up per backend).

References

  • BackendInvoiceInterface, LnbitsBackendInvoice, EmptyBackendInvoice in src/Invoice/Domain/BackendInvoice/.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions