Add OnChainMinValue

This commit is contained in:
nicolas.dorier
2018-04-03 17:39:28 +09:00
parent 10fcc84379
commit 325b359ff6
6 changed files with 98 additions and 15 deletions

View File

@@ -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()
{ {

View File

@@ -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)
var lightningMaxValueCrypto = Money.Coins(lightningMaxValue.Value / lightningMaxValueRate);
if (paymentMethod.Calculate().Due > lightningMaxValueCrypto)
{ {
throw new PaymentMethodUnavailableException("Lightning max value exceeded"); compare = (a, b) => a < b;
limitValue = storeBlob.OnChainMinValue;
errorMessage = "The amount of the invoice is too low to be paid on chain";
}
if (compare != null)
{
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(errorMessage);
} }
} }
/////////////// ///////////////

View File

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

View File

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

View File

@@ -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();

View File

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