diff --git a/BTCPayServer.Client/BTCPayServer.Client.csproj b/BTCPayServer.Client/BTCPayServer.Client.csproj
index 033cfee25..f3dca2308 100644
--- a/BTCPayServer.Client/BTCPayServer.Client.csproj
+++ b/BTCPayServer.Client/BTCPayServer.Client.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs
index 61e20e3e9..2a6529f65 100644
--- a/BTCPayServer.Tests/SeleniumTests.cs
+++ b/BTCPayServer.Tests/SeleniumTests.cs
@@ -493,7 +493,7 @@ namespace BTCPayServer.Tests
var client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
await client.AuthorizeClient(new NBitpayClient.PairingCode(pairingCode));
await client.CreateInvoiceAsync(
- new NBitpayClient.Invoice() { Price = 0.000000012m, Currency = "USD", FullNotifications = true },
+ new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
@@ -503,7 +503,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("ApprovePairing")).Click();
await client.CreateInvoiceAsync(
- new NBitpayClient.Invoice() { Price = 0.000000012m, Currency = "USD", FullNotifications = true },
+ new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
s.Driver.Navigate().GoToUrl(s.Link("/api-tokens"));
diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs
index d725edf19..3666bccbf 100644
--- a/BTCPayServer.Tests/UnitTest1.cs
+++ b/BTCPayServer.Tests/UnitTest1.cs
@@ -3004,15 +3004,31 @@ namespace BTCPayServer.Tests
[Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")]
+ [Trait("Lightning", "Lightning")]
public async Task CanCreateStrangeInvoice()
{
using (var tester = ServerTester.Create())
{
+ tester.ActivateLightning();
await tester.StartAsync();
var user = tester.NewAccount();
- user.GrantAccess();
+ user.GrantAccess(true);
user.RegisterDerivationScheme("BTC");
+
DateTimeOffset expiration = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(21);
+
+ // This should fail, the amount is too low to be above the dust limit of bitcoin
+ var ex = Assert.Throws(() => user.BitPay.CreateInvoice(
+ new Invoice()
+ {
+ Price = 0.000000012m,
+ Currency = "USD",
+ FullNotifications = true,
+ ExpirationTime = expiration
+ }, Facade.Merchant));
+ Assert.Contains("dust threshold", ex.Message);
+ await user.RegisterLightningNodeAsync("BTC");
+
var invoice1 = user.BitPay.CreateInvoice(
new Invoice()
{
@@ -3021,6 +3037,7 @@ namespace BTCPayServer.Tests
FullNotifications = true,
ExpirationTime = expiration
}, Facade.Merchant);
+
Assert.Equal(expiration.ToUnixTimeSeconds(), invoice1.ExpirationTime.ToUnixTimeSeconds());
var invoice2 = user.BitPay.CreateInvoice(new Invoice() { Price = 0.000000019m, Currency = "USD" },
Facade.Merchant);
diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj
index dd86838f1..ee5c6d52e 100644
--- a/BTCPayServer/BTCPayServer.csproj
+++ b/BTCPayServer/BTCPayServer.csproj
@@ -1,4 +1,4 @@
-
+
diff --git a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs
index 057f19c59..a840af0ca 100644
--- a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs
+++ b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs
@@ -136,7 +136,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
claimDestination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination)
{
txout.ScriptPubKey = bitcoinLikeClaimDestination.Address.ScriptPubKey;
- return Task.FromResult(txout.GetDustThreshold(new FeeRate(1.0m)).ToDecimal(MoneyUnit.BTC));
+ return Task.FromResult(txout.GetDustThreshold().ToDecimal(MoneyUnit.BTC));
}
return Task.FromResult(0m);
diff --git a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs
index a2d99c0ec..3f0db280e 100644
--- a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs
+++ b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs
@@ -190,6 +190,15 @@ namespace BTCPayServer.Payments.Bitcoin
}
var reserved = await prepare.ReserveAddress;
+ if (paymentMethod.ParentEntity.Type != InvoiceType.TopUp)
+ {
+ var txOut = network.NBitcoinNetwork.Consensus.ConsensusFactory.CreateTxOut();
+ txOut.ScriptPubKey = reserved.Address.ScriptPubKey;
+ var dust = txOut.GetDustThreshold();
+ var amount = paymentMethod.Calculate().Due;
+ if (amount < dust)
+ throw new PaymentMethodUnavailableException("Amount below the dust threshold. For amounts of this size, it is recommended to enable an off-chain (Lightning) payment method");
+ }
onchainMethod.DepositAddress = reserved.Address.ToString();
onchainMethod.KeyPath = reserved.KeyPath;
onchainMethod.PayjoinEnabled = blob.PayJoinEnabled &&
diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs
index c90e4c311..808edeb87 100644
--- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs
+++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs
@@ -424,7 +424,7 @@ namespace BTCPayServer.Payments.PayJoin
{
var outputContribution = Money.Min(additionalFee, -due);
outputContribution = Money.Min(outputContribution,
- newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold(minRelayTxFee));
+ newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold());
newTx.Outputs[i].Value -= outputContribution;
additionalFee -= outputContribution;
due += outputContribution;
@@ -437,7 +437,7 @@ namespace BTCPayServer.Payments.PayJoin
{
var outputContribution = Money.Min(additionalFee, feeOutput.Value);
outputContribution = Money.Min(outputContribution,
- feeOutput.Value - feeOutput.GetDustThreshold(minRelayTxFee));
+ feeOutput.Value - feeOutput.GetDustThreshold());
outputContribution = Money.Min(outputContribution, allowedSenderFeeContribution);
feeOutput.Value -= outputContribution;
additionalFee -= outputContribution;