mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +01:00
Can tweak the rate at store level
This commit is contained in:
parent
2b31af80cb
commit
5f6913b3a2
8 changed files with 163 additions and 8 deletions
|
@ -61,10 +61,9 @@ namespace BTCPayServer.Tests
|
|||
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
||||
StoreId = store.CreatedStoreId;
|
||||
DerivationScheme = new DerivationStrategyFactory(SupportedNetwork.NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]");
|
||||
await store.UpdateStore(StoreId, new StoreViewModel()
|
||||
{
|
||||
SpeedPolicy = SpeedPolicy.MediumSpeed
|
||||
});
|
||||
var vm = (StoreViewModel)((ViewResult)await store.UpdateStore(StoreId)).Model;
|
||||
vm.SpeedPolicy = SpeedPolicy.MediumSpeed;
|
||||
await store.UpdateStore(StoreId, vm);
|
||||
|
||||
await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel()
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ using BTCPayServer.Services.Rates;
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
using BTCPayServer.Eclair;
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -392,6 +393,50 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CanTweakRate()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
|
||||
|
||||
// First we try payment with a merchant having only BTC
|
||||
var invoice1 = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some description",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
|
||||
|
||||
var storeController = tester.PayTester.GetController<StoresController>(user.UserId);
|
||||
var vm = (StoreViewModel)((ViewResult)storeController.UpdateStore(user.StoreId).Result).Model;
|
||||
Assert.Equal(1.0, vm.RateMultiplier);
|
||||
vm.RateMultiplier = 0.5;
|
||||
storeController.UpdateStore(user.StoreId, vm).Wait();
|
||||
|
||||
|
||||
var invoice2 = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some description",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
|
||||
Assert.True(invoice2.BtcPrice.Almost(invoice1.BtcPrice * 2, 0.00001m));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanHaveLTCOnlyStore()
|
||||
{
|
||||
|
|
|
@ -130,7 +130,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
network = _.Network,
|
||||
getFeeRate = _.FeeRateProvider.GetFeeRateAsync(),
|
||||
getRate = _.RateProvider.GetRateAsync(invoice.Currency),
|
||||
getRate = storeBlob.ApplyRateRules(_.Network, _.RateProvider).GetRateAsync(invoice.Currency),
|
||||
getAddress = _.Wallet.ReserveAddressAsync(_.DerivationStrategy)
|
||||
};
|
||||
});
|
||||
|
@ -164,7 +164,7 @@ namespace BTCPayServer.Controllers
|
|||
#pragma warning disable CS0618
|
||||
var btc = _NetworkProvider.BTC;
|
||||
var feeProvider = _FeeProviderFactory.CreateFeeProvider(btc);
|
||||
var rateProvider = _RateProviders.GetRateProvider(btc);
|
||||
var rateProvider = storeBlob.ApplyRateRules(btc, _RateProviders.GetRateProvider(btc));
|
||||
if (feeProvider != null && rateProvider != null)
|
||||
{
|
||||
var gettingFee = feeProvider.GetFeeRateAsync();
|
||||
|
|
|
@ -164,6 +164,7 @@ namespace BTCPayServer.Controllers
|
|||
vm.StatusMessage = StatusMessage;
|
||||
vm.MonitoringExpiration = storeBlob.MonitoringExpiration;
|
||||
vm.InvoiceExpiration = storeBlob.InvoiceExpiration;
|
||||
vm.RateMultiplier = (double)storeBlob.GetRateMultiplier();
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
|
@ -307,6 +308,8 @@ namespace BTCPayServer.Controllers
|
|||
var blob = store.GetStoreBlob();
|
||||
blob.NetworkFeeDisabled = !model.NetworkFee;
|
||||
blob.MonitoringExpiration = model.MonitoringExpiration;
|
||||
blob.InvoiceExpiration = model.InvoiceExpiration;
|
||||
blob.SetRateMultiplier(model.RateMultiplier);
|
||||
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
@ -99,10 +100,10 @@ namespace BTCPayServer.Data
|
|||
|
||||
if (!existing && string.IsNullOrEmpty(derivationScheme))
|
||||
{
|
||||
if(network.IsBTC)
|
||||
if (network.IsBTC)
|
||||
DerivationStrategy = null;
|
||||
}
|
||||
else if(!existing)
|
||||
else if (!existing)
|
||||
strategies.Add(new JProperty(network.CryptoCode, new JValue(derivationScheme)));
|
||||
// This is deprecated so we don't have to set anymore
|
||||
//if (network.IsBTC)
|
||||
|
@ -173,6 +174,22 @@ namespace BTCPayServer.Data
|
|||
}
|
||||
}
|
||||
|
||||
public class RateRule
|
||||
{
|
||||
public RateRule()
|
||||
{
|
||||
RuleName = "Multiplier";
|
||||
}
|
||||
public string RuleName { get; set; }
|
||||
|
||||
public double Multiplier { get; set; }
|
||||
|
||||
public decimal Apply(BTCPayNetwork network, decimal rate)
|
||||
{
|
||||
return rate * (decimal)Multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
public class StoreBlob
|
||||
{
|
||||
public StoreBlob()
|
||||
|
@ -200,5 +217,30 @@ namespace BTCPayServer.Data
|
|||
set;
|
||||
}
|
||||
|
||||
public void SetRateMultiplier(double rate)
|
||||
{
|
||||
RateRules = new List<RateRule>();
|
||||
RateRules.Add(new RateRule() { Multiplier = rate });
|
||||
}
|
||||
public decimal GetRateMultiplier()
|
||||
{
|
||||
decimal rate = 1.0m;
|
||||
if (RateRules == null || RateRules.Count == 0)
|
||||
return rate;
|
||||
foreach (var rule in RateRules)
|
||||
{
|
||||
rate = rule.Apply(null, rate);
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
public List<RateRule> RateRules { get; set; } = new List<RateRule>();
|
||||
|
||||
public IRateProvider ApplyRateRules(BTCPayNetwork network, IRateProvider rateProvider)
|
||||
{
|
||||
if (RateRules == null || RateRules.Count == 0)
|
||||
return rateProvider;
|
||||
return new TweakRateProvider(network, rateProvider, RateRules.ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,14 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||
|
||||
public List<StoreViewModel.DerivationScheme> DerivationSchemes { get; set; } = new List<StoreViewModel.DerivationScheme>();
|
||||
|
||||
[Display(Name = "Multiply the original rate by ...")]
|
||||
[Range(0.01, 10.0)]
|
||||
public double RateMultiplier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Display(Name = "Invoice expires if the full amount has not been paid after ... minutes")]
|
||||
[Range(1, 60 * 24 * 31)]
|
||||
public int InvoiceExpiration
|
||||
|
|
53
BTCPayServer/Services/Rates/TweakRateProvider.cs
Normal file
53
BTCPayServer/Services/Rates/TweakRateProvider.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class TweakRateProvider : IRateProvider
|
||||
{
|
||||
private BTCPayNetwork network;
|
||||
private IRateProvider rateProvider;
|
||||
private List<RateRule> rateRules;
|
||||
|
||||
public TweakRateProvider(BTCPayNetwork network, IRateProvider rateProvider, List<RateRule> rateRules)
|
||||
{
|
||||
if (network == null)
|
||||
throw new ArgumentNullException(nameof(network));
|
||||
if (rateProvider == null)
|
||||
throw new ArgumentNullException(nameof(rateProvider));
|
||||
if (rateRules == null)
|
||||
throw new ArgumentNullException(nameof(rateRules));
|
||||
this.network = network;
|
||||
this.rateProvider = rateProvider;
|
||||
this.rateRules = rateRules;
|
||||
}
|
||||
|
||||
public async Task<decimal> GetRateAsync(string currency)
|
||||
{
|
||||
var rate = await rateProvider.GetRateAsync(currency);
|
||||
foreach(var rule in rateRules)
|
||||
{
|
||||
rate = rule.Apply(network, rate);
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Rate>> GetRatesAsync()
|
||||
{
|
||||
List<Rate> rates = new List<Rate>();
|
||||
foreach (var rate in await rateProvider.GetRatesAsync())
|
||||
{
|
||||
var localRate = rate.Value;
|
||||
foreach (var rule in rateRules)
|
||||
{
|
||||
localRate = rule.Apply(network, localRate);
|
||||
}
|
||||
rates.Add(new Rate(rate.Currency, localRate));
|
||||
}
|
||||
return rates;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,11 @@
|
|||
<label asp-for="NetworkFee"></label>
|
||||
<input asp-for="NetworkFee" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RateMultiplier"></label>
|
||||
<input asp-for="RateMultiplier" class="form-control" />
|
||||
<span asp-validation-for="RateMultiplier" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="InvoiceExpiration"></label>
|
||||
<input asp-for="InvoiceExpiration" class="form-control" />
|
||||
|
|
Loading…
Add table
Reference in a new issue