diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc1370..689a6d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.3.2 — 2026-09-26 + +### Fixed +- **A mock invoice's prefix now carries the amount it charges.** Every invoice began `lnbc10n` + (1 sat) whatever `amountSat` was requested, so a consumer reading the price off the invoice saw + 1 sat. imani-gateway-core checks that a client mint's invoice charges what the sale costs + (imani-gateway-core#92), and refused every staging client mint because of it. The prefix is now + `lnbc{amountSat × 10}n`, the nano-BTC form of the requested amount, and an amount of zero or + less is written as an amountless invoice. The invoice is still not signed or payable. + ## 0.3.1 — 2026-09-23 ### Fixed diff --git a/phoenixd-base/pom.xml b/phoenixd-base/pom.xml index 94c5270..a22d1a8 100644 --- a/phoenixd-base/pom.xml +++ b/phoenixd-base/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 phoenixd-base diff --git a/phoenixd-mock/pom.xml b/phoenixd-mock/pom.xml index dc59caa..7f82f45 100644 --- a/phoenixd-mock/pom.xml +++ b/phoenixd-mock/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 phoenixd-mock diff --git a/phoenixd-mock/src/main/java/xyz/tcheeric/phoenixd/mock/MockLnServer.java b/phoenixd-mock/src/main/java/xyz/tcheeric/phoenixd/mock/MockLnServer.java index a111537..ecee62f 100644 --- a/phoenixd-mock/src/main/java/xyz/tcheeric/phoenixd/mock/MockLnServer.java +++ b/phoenixd-mock/src/main/java/xyz/tcheeric/phoenixd/mock/MockLnServer.java @@ -176,7 +176,7 @@ private void handleCreateInvoice(HttpExchange exchange) throws IOException { // Generate a unique mock bolt11 invoice with valid Bech32 encoding String invoiceId = Long.toHexString(System.nanoTime()); String paymentHash = "hash" + invoiceId; - String bolt11 = generateValidBolt11Invoice(); + String bolt11 = generateValidBolt11Invoice(amountSat); // Track the invoice for auto-settlement InvoiceInfo invoiceInfo = new InvoiceInfo(paymentHash, bolt11, amountSat, externalId); @@ -241,10 +241,14 @@ private void handleGetInvoice(HttpExchange exchange) throws IOException { * * This generates a mock invoice that passes basic Bech32 validation but is not * cryptographically valid for actual Lightning Network payments. + * + *

The human-readable part carries the requested amount, as a real invoice does. It used + * to be a fixed {@code lnbc10n} (1 sat) whatever was asked for, so anything that reads the + * price off the invoice, such as imani-gateway-core's check that a client mint charged what + * the sale costs, refused every invoice this mock issued. */ - private String generateValidBolt11Invoice() { - // Human-readable part: ln + bc (bitcoin mainnet) + 10n (10 nanosats = ~0 sats for testing) - String hrp = "lnbc10n"; + String generateValidBolt11Invoice(long amountSat) { + String hrp = "lnbc" + bolt11Amount(amountSat); // Generate random payment hash (32 bytes = 52 chars in bech32, roughly) // For a minimal valid invoice, we need at least timestamp + payment hash @@ -262,6 +266,14 @@ private String generateValidBolt11Invoice() { return hrp + "1" + data + checksum; } + /** + * The BOLT11 amount for a whole number of sats. With the {@code n} (nano-BTC) multiplier one + * sat is 10 units. Zero or less is an amountless invoice, which BOLT11 writes as no amount. + */ + static String bolt11Amount(long amountSat) { + return amountSat > 0 ? (amountSat * 10) + "n" : ""; + } + /** * Calculates Bech32 checksum for the given HRP and data. * Simplified implementation for mock purposes. diff --git a/phoenixd-mock/src/test/java/xyz/tcheeric/phoenixd/mock/MockLnServerTest.java b/phoenixd-mock/src/test/java/xyz/tcheeric/phoenixd/mock/MockLnServerTest.java index 5b6d02f..bbb3c26 100644 --- a/phoenixd-mock/src/test/java/xyz/tcheeric/phoenixd/mock/MockLnServerTest.java +++ b/phoenixd-mock/src/test/java/xyz/tcheeric/phoenixd/mock/MockLnServerTest.java @@ -97,6 +97,31 @@ void shouldCreateInvoiceWithDefaultAmount() throws Exception { assertThat(bolt11).startsWith("lnbc"); } + // The invoice charges what was asked for: 500 sat is 5000 nano-BTC in the BOLT11 prefix, so + // a consumer reading the price off the invoice sees 500, not the 1 sat the prefix used to say. + @Test + void createdInvoiceCarriesTheRequestedAmountInItsPrefix() throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(BASE_URL + "/createinvoice")) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString("amountSat=500")) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + String bolt11 = objectMapper.readTree(response.body()).get("serialized").asText(); + assertThat(bolt11).startsWith("lnbc5000n1"); + } + + // One sat is ten nano-BTC, and a non-positive amount is written as no amount at all. + @Test + void bolt11AmountEncodesWholeSatsWithTheNanoMultiplier() { + assertThat(MockLnServer.bolt11Amount(1)).isEqualTo("10n"); + assertThat(MockLnServer.bolt11Amount(100)).isEqualTo("1000n"); + assertThat(MockLnServer.bolt11Amount(0)).isEmpty(); + assertThat(MockLnServer.bolt11Amount(-3)).isEmpty(); + } + @Test void shouldGetInvoiceStatus() throws Exception { // Arrange: Create an invoice first diff --git a/phoenixd-model/pom.xml b/phoenixd-model/pom.xml index 51106ad..9a35a51 100644 --- a/phoenixd-model/pom.xml +++ b/phoenixd-model/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 phoenixd-model diff --git a/phoenixd-rest/pom.xml b/phoenixd-rest/pom.xml index d50f69d..923c7ef 100644 --- a/phoenixd-rest/pom.xml +++ b/phoenixd-rest/pom.xml @@ -4,12 +4,12 @@ xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 phoenixd-rest jar - 0.3.1 + 0.3.2 phoenixd-rest https://maven.apache.org diff --git a/phoenixd-test/pom.xml b/phoenixd-test/pom.xml index 15f8941..e9a856a 100644 --- a/phoenixd-test/pom.xml +++ b/phoenixd-test/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 phoenixd-test diff --git a/pom.xml b/pom.xml index 1c5d635..a999807 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 xyz.tcheeric phoenixd-java - 0.3.1 + 0.3.2 pom