mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Add OnChainMinValue
This commit is contained in:
@@ -868,6 +868,49 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanSetPaymentMethodLimits()
|
||||||
|
{
|
||||||
|
using (var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
tester.Start();
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
user.GrantAccess();
|
||||||
|
user.RegisterDerivationScheme("BTC");
|
||||||
|
user.RegisterLightningNode("BTC", LightningConnectionType.Charge);
|
||||||
|
var vm = Assert.IsType<CheckoutExperienceViewModel>(Assert.IsType<ViewResult>(user.GetController<StoresController>().CheckoutExperience(user.StoreId).Result).Model);
|
||||||
|
vm.LightningMaxValue = "2 USD";
|
||||||
|
vm.OnChainMinValue = "5 USD";
|
||||||
|
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(user.StoreId, vm).Result);
|
||||||
|
|
||||||
|
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
|
{
|
||||||
|
Price = 1.5,
|
||||||
|
Currency = "USD",
|
||||||
|
PosData = "posData",
|
||||||
|
OrderId = "orderId",
|
||||||
|
ItemDesc = "Some description",
|
||||||
|
FullNotifications = true
|
||||||
|
}, Facade.Merchant);
|
||||||
|
|
||||||
|
Assert.Single(invoice.CryptoInfo);
|
||||||
|
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
||||||
|
|
||||||
|
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
|
{
|
||||||
|
Price = 5.5,
|
||||||
|
Currency = "USD",
|
||||||
|
PosData = "posData",
|
||||||
|
OrderId = "orderId",
|
||||||
|
ItemDesc = "Some description",
|
||||||
|
FullNotifications = true
|
||||||
|
}, Facade.Merchant);
|
||||||
|
|
||||||
|
Assert.Single(invoice.CryptoInfo);
|
||||||
|
Assert.Equal(PaymentTypes.BTCLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanUsePoSApp()
|
public void CanUsePoSApp()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
StringBuilder errors = new StringBuilder();
|
StringBuilder errors = new StringBuilder();
|
||||||
errors.AppendLine("No payment method available for this store");
|
errors.AppendLine("No payment method available for this store");
|
||||||
foreach(var error in paymentMethodErrors)
|
foreach (var error in paymentMethodErrors)
|
||||||
{
|
{
|
||||||
errors.AppendLine(error);
|
errors.AppendLine(error);
|
||||||
}
|
}
|
||||||
@@ -196,21 +196,36 @@ namespace BTCPayServer.Controllers
|
|||||||
paymentDetails.SetNoTxFee();
|
paymentDetails.SetNoTxFee();
|
||||||
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
||||||
|
|
||||||
// Check if Lightning Max value is exceeded
|
Func<Money, Money, bool> compare = null;
|
||||||
|
CurrencyValue limitValue = null;
|
||||||
|
string errorMessage = null;
|
||||||
if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike &&
|
if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike &&
|
||||||
storeBlob.LightningMaxValue != null)
|
storeBlob.LightningMaxValue != null)
|
||||||
{
|
{
|
||||||
var lightningMaxValue = storeBlob.LightningMaxValue;
|
compare = (a, b) => a > b;
|
||||||
var lightningMaxValueRate = 0.0m;
|
limitValue = storeBlob.LightningMaxValue;
|
||||||
if (lightningMaxValue.Currency == entity.ProductInformation.Currency)
|
errorMessage = "The amount of the invoice is too high to be paid with lightning";
|
||||||
lightningMaxValueRate = paymentMethod.Rate;
|
}
|
||||||
else
|
else if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.BTCLike &&
|
||||||
lightningMaxValueRate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(lightningMaxValue.Currency);
|
storeBlob.OnChainMinValue != null)
|
||||||
|
{
|
||||||
|
compare = (a, b) => a < b;
|
||||||
|
limitValue = storeBlob.OnChainMinValue;
|
||||||
|
errorMessage = "The amount of the invoice is too low to be paid on chain";
|
||||||
|
}
|
||||||
|
|
||||||
var lightningMaxValueCrypto = Money.Coins(lightningMaxValue.Value / lightningMaxValueRate);
|
if (compare != null)
|
||||||
if (paymentMethod.Calculate().Due > lightningMaxValueCrypto)
|
{
|
||||||
|
var limitValueRate = 0.0m;
|
||||||
|
if (limitValue.Currency == entity.ProductInformation.Currency)
|
||||||
|
limitValueRate = paymentMethod.Rate;
|
||||||
|
else
|
||||||
|
limitValueRate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(limitValue.Currency);
|
||||||
|
|
||||||
|
var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate);
|
||||||
|
if (compare(paymentMethod.Calculate().Due, limitValueCrypto))
|
||||||
{
|
{
|
||||||
throw new PaymentMethodUnavailableException("Lightning max value exceeded");
|
throw new PaymentMethodUnavailableException(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///////////////
|
///////////////
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ namespace BTCPayServer.Controllers
|
|||||||
vm.SetCryptoCurrencies(_ExplorerProvider, store.GetDefaultCrypto());
|
vm.SetCryptoCurrencies(_ExplorerProvider, store.GetDefaultCrypto());
|
||||||
vm.SetLanguages(_LangService, storeBlob.DefaultLang);
|
vm.SetLanguages(_LangService, storeBlob.DefaultLang);
|
||||||
vm.LightningMaxValue = storeBlob.LightningMaxValue?.ToString() ?? "";
|
vm.LightningMaxValue = storeBlob.LightningMaxValue?.ToString() ?? "";
|
||||||
|
vm.OnChainMinValue = storeBlob.OnChainMinValue?.ToString() ?? "";
|
||||||
vm.AllowCoinConversion = storeBlob.AllowCoinConversion;
|
vm.AllowCoinConversion = storeBlob.AllowCoinConversion;
|
||||||
vm.CustomCSS = storeBlob.CustomCSS;
|
vm.CustomCSS = storeBlob.CustomCSS;
|
||||||
vm.CustomLogo = storeBlob.CustomLogo;
|
vm.CustomLogo = storeBlob.CustomLogo;
|
||||||
@@ -205,14 +206,24 @@ namespace BTCPayServer.Controllers
|
|||||||
[Route("{storeId}/checkout")]
|
[Route("{storeId}/checkout")]
|
||||||
public async Task<IActionResult> CheckoutExperience(string storeId, CheckoutExperienceViewModel model)
|
public async Task<IActionResult> CheckoutExperience(string storeId, CheckoutExperienceViewModel model)
|
||||||
{
|
{
|
||||||
CurrencyValue currencyValue = null;
|
CurrencyValue lightningMaxValue = null;
|
||||||
if (!string.IsNullOrWhiteSpace(model.LightningMaxValue))
|
if (!string.IsNullOrWhiteSpace(model.LightningMaxValue))
|
||||||
{
|
{
|
||||||
if (!CurrencyValue.TryParse(model.LightningMaxValue, out currencyValue))
|
if (!CurrencyValue.TryParse(model.LightningMaxValue, out lightningMaxValue))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(model.LightningMaxValue), "Invalid currency value");
|
ModelState.AddModelError(nameof(model.LightningMaxValue), "Invalid lightning max value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrencyValue onchainMinValue = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(model.OnChainMinValue))
|
||||||
|
{
|
||||||
|
if (!CurrencyValue.TryParse(model.OnChainMinValue, out onchainMinValue))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(model.OnChainMinValue), "Invalid on chain min value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var store = await _Repo.FindStore(storeId, GetUserId());
|
var store = await _Repo.FindStore(storeId, GetUserId());
|
||||||
if (store == null)
|
if (store == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@@ -227,7 +238,8 @@ namespace BTCPayServer.Controllers
|
|||||||
model.SetLanguages(_LangService, model.DefaultLang);
|
model.SetLanguages(_LangService, model.DefaultLang);
|
||||||
blob.DefaultLang = model.DefaultLang;
|
blob.DefaultLang = model.DefaultLang;
|
||||||
blob.AllowCoinConversion = model.AllowCoinConversion;
|
blob.AllowCoinConversion = model.AllowCoinConversion;
|
||||||
blob.LightningMaxValue = currencyValue;
|
blob.LightningMaxValue = lightningMaxValue;
|
||||||
|
blob.OnChainMinValue = onchainMinValue;
|
||||||
blob.CustomLogo = model.CustomLogo;
|
blob.CustomLogo = model.CustomLogo;
|
||||||
blob.CustomCSS = model.CustomCSS;
|
blob.CustomCSS = model.CustomCSS;
|
||||||
if (store.SetStoreBlob(blob))
|
if (store.SetStoreBlob(blob))
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ namespace BTCPayServer.Data
|
|||||||
|
|
||||||
[JsonConverter(typeof(CurrencyValueJsonConverter))]
|
[JsonConverter(typeof(CurrencyValueJsonConverter))]
|
||||||
public CurrencyValue LightningMaxValue { get; set; }
|
public CurrencyValue LightningMaxValue { get; set; }
|
||||||
|
[JsonConverter(typeof(CurrencyValueJsonConverter))]
|
||||||
|
public CurrencyValue OnChainMinValue { get; set; }
|
||||||
|
|
||||||
[JsonConverter(typeof(UriJsonConverter))]
|
[JsonConverter(typeof(UriJsonConverter))]
|
||||||
public Uri CustomLogo { get; set; }
|
public Uri CustomLogo { get; set; }
|
||||||
|
|||||||
@@ -31,12 +31,17 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
[MaxLength(20)]
|
[MaxLength(20)]
|
||||||
public string LightningMaxValue { get; set; }
|
public string LightningMaxValue { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Do not propose on chain payment if the value of the invoice is below...")]
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string OnChainMinValue { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Link to a custom CSS stylesheet")]
|
[Display(Name = "Link to a custom CSS stylesheet")]
|
||||||
[Url]
|
[Url]
|
||||||
public Uri CustomCSS { get; set; }
|
public Uri CustomCSS { get; set; }
|
||||||
[Display(Name = "Link to a custom logo")]
|
[Display(Name = "Link to a custom logo")]
|
||||||
[Url]
|
[Url]
|
||||||
public Uri CustomLogo { get; set; }
|
public Uri CustomLogo { get; set; }
|
||||||
|
|
||||||
public void SetCryptoCurrencies(ExplorerClientProvider explorerProvider, string defaultCrypto)
|
public void SetCryptoCurrencies(ExplorerClientProvider explorerProvider, string defaultCrypto)
|
||||||
{
|
{
|
||||||
var choices = explorerProvider.GetAll().Select(o => new Format() { Name = o.Item1.CryptoCode, Value = o.Item1.CryptoCode }).ToArray();
|
var choices = explorerProvider.GetAll().Select(o => new Format() { Name = o.Item1.CryptoCode, Value = o.Item1.CryptoCode }).ToArray();
|
||||||
|
|||||||
@@ -44,6 +44,12 @@
|
|||||||
<span asp-validation-for="LightningMaxValue" class="text-danger"></span>
|
<span asp-validation-for="LightningMaxValue" class="text-danger"></span>
|
||||||
<p class="form-text text-muted">Example: 5.50 USD</p>
|
<p class="form-text text-muted">Example: 5.50 USD</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="OnChainMinValue"></label>
|
||||||
|
<input asp-for="OnChainMinValue" class="form-control" />
|
||||||
|
<span asp-validation-for="OnChainMinValue" class="text-danger"></span>
|
||||||
|
<p class="form-text text-muted">Example: 5.50 USD</p>
|
||||||
|
</div>
|
||||||
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
|
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user