diff --git a/BTCPayServer.Tests/PaymentHandlerTest.cs b/BTCPayServer.Tests/PaymentHandlerTest.cs new file mode 100644 index 000000000..f456c62be --- /dev/null +++ b/BTCPayServer.Tests/PaymentHandlerTest.cs @@ -0,0 +1,239 @@ +using BTCPayServer.Tests.Logging; +using NBitcoin; +using Xunit; +using Xunit.Abstractions; +using BTCPayServer.Data; +using BTCPayServer.Services.Rates; +using System.Collections.Generic; +using System.Threading.Tasks; +using BTCPayServer.Payments; +using BTCPayServer.Payments.Bitcoin; +using BTCPayServer.Payments.Lightning; +using BTCPayServer.Rating; + +namespace BTCPayServer.Tests +{ + public class PaymentHandlerTest + { + private BitcoinLikePaymentHandler handlerBTC; + private LightningLikePaymentHandler handlerLN; + private Dictionary> currencyPairRateResult; + + public PaymentHandlerTest(ITestOutputHelper helper) + { + +#pragma warning disable CS0618 + + Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; + Logs.LogProvider = new XUnitLogProvider(helper); + + var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString(); + var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest); + + currencyPairRateResult = new Dictionary>(); + + var rateResultUSDBTC = new RateResult(); + rateResultUSDBTC.BidAsk= new BidAsk(1m); + + var rateResultBTCUSD = new RateResult(); + rateResultBTCUSD.BidAsk= new BidAsk(1m); + + currencyPairRateResult.Add(new CurrencyPair("USD", "BTC"), Task.FromResult(rateResultUSDBTC)); + currencyPairRateResult.Add(new CurrencyPair("BTC", "USD"), Task.FromResult(rateResultBTCUSD)); + + handlerBTC = new BitcoinLikePaymentHandler(null, networkProvider, null, null); + handlerLN = new LightningLikePaymentHandler(null, null, networkProvider, null); + +#pragma warning restore CS0618 + } + + [Fact] + public void CanPayWithLightningWhenInvoiceTotalUnderLightningMaxValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = null, + LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"} + }; + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike); + + //When + var totalInvoiceAmount = new Money(98m, MoneyUnit.BTC); + + + //Then + var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.Equal(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + + [Fact] + public void CannotPayWithLightningWhenInvoiceTotalAboveLightningMaxValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = null, + LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"} + }; + var totalInvoiceAmount = new Money(102m, MoneyUnit.BTC); + + //When + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike); + + //Then + var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.NotEqual(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + [Fact] + public void CanPayWithLightningWhenInvoiceTotalEqualLightningMaxValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = null, + LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"} + }; + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike); + + //When + var totalInvoiceAmount = new Money(100m, MoneyUnit.BTC); + + //Then + var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.Equal(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + [Fact] + public void CanPayWithBitcoinWhenInvoiceTotalAboveOnChainMinValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}, + LightningMaxValue = null + }; + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); + + //When + var totalInvoiceAmount = new Money(105m, MoneyUnit.BTC); + + + //Then + var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.Equal(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + + [Fact] + public void CannotPayWithBitcoinWhenInvoiceTotalUnderOnChainMinValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}, + LightningMaxValue = null + }; + var totalInvoiceAmount = new Money(98m, MoneyUnit.BTC); + + //When + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); + + //Then + var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.NotEqual(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + [Fact] + public void CanPayWithBitcoinWhenInvoiceTotalEqualOnChainMinValue() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}, + LightningMaxValue = null + }; + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); + + //When + var totalInvoiceAmount = new Money(100m, MoneyUnit.BTC); + + //Then + var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.Equal(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + + [Fact] + public void CannotPayWithBitcoinWhenInvoiceTotalUnderOnChainMinValueWhenLightningMaxValueIsGreater() + { + +#pragma warning disable CS0618 + + //Given + var store = new StoreBlob + { + OnChainMinValue = new CurrencyValue() {Value = 50.00m, Currency = "USD"}, + LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"} + }; + var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); + + //When + var totalInvoiceAmount = new Money(45m, MoneyUnit.BTC); + + //Then + var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult, + totalInvoiceAmount, paymentMethodId); + + Assert.NotEqual(errorMessage.Result, string.Empty); + +#pragma warning restore CS0618 + } + + + } +} diff --git a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs index d553c0ba0..c2cbb71d1 100644 --- a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs +++ b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using BTCPayServer.Data; @@ -11,9 +10,6 @@ using BTCPayServer.Services; using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Rates; using NBitcoin; -using NBitpayClient; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace BTCPayServer.Payments.Bitcoin { @@ -73,37 +69,35 @@ namespace BTCPayServer.Payments.Bitcoin public override async Task IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob, Dictionary> rate, Money amount, PaymentMethodId paymentMethodId) { + Func compare = null; CurrencyValue limitValue = null; string errorMessage = null; - if (paymentMethodId.PaymentType == PaymentTypes.LightningLike && - storeBlob.LightningMaxValue != null) + + if (storeBlob.OnChainMinValue != null) { - compare = (a, b) => a > b; - limitValue = storeBlob.LightningMaxValue; - errorMessage = "The amount of the invoice is too high to be paid with lightning"; - } - else if (paymentMethodId.PaymentType == PaymentTypes.BTCLike && - storeBlob.OnChainMinValue != null) - { - compare = (a, b) => a < b; + compare = (value, limit) => value < limit; limitValue = storeBlob.OnChainMinValue; errorMessage = "The amount of the invoice is too low to be paid on chain"; } + if (compare != null) { - var limitValueRate = await rate[new CurrencyPair(paymentMethodId.CryptoCode, storeBlob.OnChainMinValue.Currency)]; - if (limitValueRate.BidAsk != null) + var currentRateToCrypto = await rate[new CurrencyPair(paymentMethodId.CryptoCode, limitValue.Currency)]; + + if (currentRateToCrypto.BidAsk != null) { - var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate.BidAsk.Bid); + var limitValueCrypto = Money.Coins(limitValue.Value / currentRateToCrypto.BidAsk.Bid); if (compare(amount, limitValueCrypto)) { return errorMessage; } } } + return string.Empty; + } public override IEnumerable GetSupportedPaymentMethods() diff --git a/BTCPayServer/Payments/IPaymentMethodHandler.cs b/BTCPayServer/Payments/IPaymentMethodHandler.cs index 5cf3403cc..d0a7d099e 100644 --- a/BTCPayServer/Payments/IPaymentMethodHandler.cs +++ b/BTCPayServer/Payments/IPaymentMethodHandler.cs @@ -7,8 +7,6 @@ using BTCPayServer.Rating; using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Rates; using NBitcoin; -using NBitpayClient; -using Newtonsoft.Json.Linq; using InvoiceResponse = BTCPayServer.Models.InvoiceResponse; namespace BTCPayServer.Payments diff --git a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs index 4d65c8936..0018fdea3 100644 --- a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs +++ b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs @@ -13,9 +13,6 @@ using BTCPayServer.Services.Invoices; using BTCPayServer.Services; using BTCPayServer.Services.Rates; using NBitcoin; -using NBitpayClient; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace BTCPayServer.Payments.Lightning { @@ -149,23 +146,34 @@ namespace BTCPayServer.Payments.Lightning public override async Task IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob, Dictionary> rate, Money amount, PaymentMethodId paymentMethodId) { - if (storeBlob.OnChainMinValue == null) + + Func compare = null; + CurrencyValue limitValue = null; + string errorMessage = null; + + if (storeBlob.LightningMaxValue != null) { - return null; + compare = (value, limit) => value > limit; + limitValue = storeBlob.LightningMaxValue; + errorMessage = "The amount of the invoice is too high to be paid with lightning"; } - var limitValueRate = await rate[new CurrencyPair(paymentMethodId.CryptoCode, storeBlob.OnChainMinValue.Currency)]; - if (limitValueRate.BidAsk != null) + if (compare != null) { - var limitValueCrypto = Money.Coins(storeBlob.OnChainMinValue.Value / limitValueRate.BidAsk.Bid); - - if (amount < limitValueCrypto) + var currentRateToCrypto = await rate[new CurrencyPair(paymentMethodId.CryptoCode, limitValue.Currency)]; + + if (currentRateToCrypto.BidAsk != null) { - return null; + var limitValueCrypto = Money.Coins(limitValue.Value / currentRateToCrypto.BidAsk.Bid); + if (compare(amount, limitValueCrypto)) + { + return errorMessage; + } } } - return "The amount of the invoice is too high to be paid with lightning"; + + return string.Empty; } public override void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse)