diff --git a/lightning-config.dist.php b/lightning-config.dist.php index 31cd26e..1ce8000 100644 --- a/lightning-config.dist.php +++ b/lightning-config.dist.php @@ -9,6 +9,7 @@ ->setReceiver('default-receiver') ->setDescriptionTemplate('Pay to %s') ->setSuccessMessage('Payment received!') + ->setInvoiceMemo('') ->setSendableRange(min: 100_000, max: 10_000_000_000) ->setCallbackUrl('localhost:8000/callback') ->addBackendsFile(getcwd() . DIRECTORY_SEPARATOR . 'nostr.json'); diff --git a/src/Config/LightningConfig.php b/src/Config/LightningConfig.php index 7b0422e..7b50481 100644 --- a/src/Config/LightningConfig.php +++ b/src/Config/LightningConfig.php @@ -18,6 +18,7 @@ final class LightningConfig implements JsonSerializable private ?string $callbackUrl = null; private ?string $descriptionTemplate = null; private ?string $successMessage = null; + private ?string $invoiceMemo = null; public function setDomain(string $domain): self { @@ -56,6 +57,12 @@ public function setSuccessMessage(string $message): self return $this; } + public function setInvoiceMemo(string $memo): self + { + $this->invoiceMemo = $memo; + return $this; + } + public function addBackendsFile(string $path): self { $this->backends ??= new BackendsConfig(); @@ -112,6 +119,9 @@ public function jsonSerialize(): array if ($this->successMessage !== null) { $result['success-message'] = $this->successMessage; } + if ($this->invoiceMemo !== null) { + $result['invoice-memo'] = $this->invoiceMemo; + } return $result; } diff --git a/src/Invoice/Application/InvoiceGenerator.php b/src/Invoice/Application/InvoiceGenerator.php index 0b56345..4c3dbd1 100644 --- a/src/Invoice/Application/InvoiceGenerator.php +++ b/src/Invoice/Application/InvoiceGenerator.php @@ -19,6 +19,7 @@ public function __construct( private string $lnAddress, private string $descriptionTemplate, private string $successMessage, + private string $memo, ) { } @@ -36,7 +37,7 @@ public function generateInvoice(int $milliSats): array $imageMetadata = ''; $metadata = '[["text/plain","' . $description . '"],["text/identifier","' . $this->lnAddress . '"]' . $imageMetadata . ']'; - $invoice = $this->backendInvoice->requestInvoice((int)($milliSats / 1000), $metadata); + $invoice = $this->backendInvoice->requestInvoice((int)($milliSats / 1000), $metadata, $this->memo); return $this->mapResponseAsArray($invoice); } diff --git a/src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php b/src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php index 6a4d428..48bc68a 100644 --- a/src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php +++ b/src/Invoice/Domain/BackendInvoice/BackendInvoiceInterface.php @@ -8,5 +8,5 @@ interface BackendInvoiceInterface { - public function requestInvoice(int $satsAmount, string $metadata): InvoiceTransfer; + public function requestInvoice(int $satsAmount, string $metadata, string $memo = ''): InvoiceTransfer; } diff --git a/src/Invoice/Domain/BackendInvoice/EmptyBackendInvoice.php b/src/Invoice/Domain/BackendInvoice/EmptyBackendInvoice.php index b2556cf..f275978 100644 --- a/src/Invoice/Domain/BackendInvoice/EmptyBackendInvoice.php +++ b/src/Invoice/Domain/BackendInvoice/EmptyBackendInvoice.php @@ -12,7 +12,7 @@ public function __construct(private string $name) { } - public function requestInvoice(int $satsAmount, string $metadata): InvoiceTransfer + public function requestInvoice(int $satsAmount, string $metadata, string $memo = ''): InvoiceTransfer { return new InvoiceTransfer(status: 'ERROR', error: 'Unknown Backend: ' . $this->name); } diff --git a/src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php b/src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php index fbfe97d..8c53577 100644 --- a/src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php +++ b/src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php @@ -18,13 +18,14 @@ public function __construct( ) { } - public function requestInvoice(int $satsAmount, string $metadata = ''): InvoiceTransfer + public function requestInvoice(int $satsAmount, string $metadata = '', string $memo = ''): InvoiceTransfer { $endpoint = $this->options['api_endpoint'] . '/api/v1/payments'; $content = [ 'out' => false, 'amount' => $satsAmount, + 'memo' => $memo, 'unhashed_description' => bin2hex($metadata), 'description_hash' => hash('sha256', $metadata), ]; diff --git a/src/Invoice/InvoiceConfig.php b/src/Invoice/InvoiceConfig.php index 3342419..11766ec 100644 --- a/src/Invoice/InvoiceConfig.php +++ b/src/Invoice/InvoiceConfig.php @@ -64,6 +64,11 @@ public function getSuccessMessage(): string return (string)$this->get('success-message', 'Payment received!'); } + public function getInvoiceMemo(): string + { + return (string)$this->get('invoice-memo', ''); + } + public function getDomain(): string { return (string)$this->get('domain', $_SERVER['HTTP_HOST'] ?? 'localhost'); diff --git a/src/Invoice/InvoiceFactory.php b/src/Invoice/InvoiceFactory.php index 8e84734..3418d45 100644 --- a/src/Invoice/InvoiceFactory.php +++ b/src/Invoice/InvoiceFactory.php @@ -41,6 +41,7 @@ public function createInvoiceGenerator(string $username): InvoiceGenerator $this->getConfig()->getDefaultLnAddress(), $this->getConfig()->getDescriptionTemplate(), $this->getConfig()->getSuccessMessage(), + $this->getConfig()->getInvoiceMemo(), ); } diff --git a/tests/Unit/Invoice/Domain/BackendInvoice/LnbitsBackendInvoiceTest.php b/tests/Unit/Invoice/Domain/BackendInvoice/LnbitsBackendInvoiceTest.php index b1a6b11..a9bd230 100644 --- a/tests/Unit/Invoice/Domain/BackendInvoice/LnbitsBackendInvoiceTest.php +++ b/tests/Unit/Invoice/Domain/BackendInvoice/LnbitsBackendInvoiceTest.php @@ -21,7 +21,7 @@ public function test_request_invoice_when_api_returns_null(): void 'api_key' => 'key', ]); - $actual = $invoice->requestInvoice(100); + $actual = $invoice->requestInvoice(100, '', ''); $expected = new InvoiceTransfer(error: 'Backend "LnBits" unreachable', status: 'ERROR'); self::assertEquals($expected, $actual); @@ -40,7 +40,7 @@ public function test_request_invoice_when_api_returns_payment_request(): void 'api_key' => 'key', ]); - $actual = $invoice->requestInvoice(100); + $actual = $invoice->requestInvoice(100, '', ''); $expected = new InvoiceTransfer(bolt11: 'ln1234567890'); self::assertEquals($expected, $actual); diff --git a/tests/Unit/Invoice/Domain/LnAddress/InvoiceGeneratorTest.php b/tests/Unit/Invoice/Domain/LnAddress/InvoiceGeneratorTest.php index ffdabb7..d08c772 100644 --- a/tests/Unit/Invoice/Domain/LnAddress/InvoiceGeneratorTest.php +++ b/tests/Unit/Invoice/Domain/LnAddress/InvoiceGeneratorTest.php @@ -23,6 +23,7 @@ public function test_invalid_amount(): void 'ln@address', 'Pay to %s', 'Payment received!', + '', ); $actual = $invoice->generateInvoice(100); @@ -44,6 +45,7 @@ public function test_unknown_backend(): void 'ln@address', 'Pay to %s', 'Payment received!', + '', ); $actual = $invoice->generateInvoice(2_000); @@ -73,6 +75,7 @@ public function test_successful_payment_request_with_amount(): void 'ln@address', 'Pay to %s', 'Payment received!', + '', ); $actual = $invoice->generateInvoice(2_000); @@ -89,4 +92,24 @@ public function test_successful_payment_request_with_amount(): void 'error' => null, ], $actual); } + + public function test_passes_memo_to_backend(): void + { + $backend = $this->createMock(BackendInvoiceInterface::class); + $backend->expects(self::once()) + ->method('requestInvoice') + ->with(2, $this->anything(), 'Custom memo') + ->willReturn(new InvoiceTransfer()); + + $invoice = new InvoiceGenerator( + $backend, + SendableRange::withMinMax(1_000, 3_000), + 'ln@address', + 'Pay to %s', + 'Payment received!', + 'Custom memo', + ); + + $invoice->generateInvoice(2_000); + } }