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 @@
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