Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e5ec822dc | |||
26aac66a76 | |||
a562e90bdb | |||
a0f3698701 | |||
02163f9482 | |||
b74fe171e2 | |||
2785bb4d9b | |||
5eac84d3a3 | |||
a0a2ab6fcd | |||
7730ead8e4 | |||
8eee0dd14c | |||
7dd88d8d8f | |||
56d1d3e645 | |||
c2308675b2 | |||
cb866a1c05 | |||
95290e8331 | |||
f5e62c775b |
@ -806,6 +806,26 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanParseCurrencyValue()
|
||||
{
|
||||
Assert.True(CurrencyValue.TryParse("1.50USD", out var result));
|
||||
Assert.Equal("1.50 USD", result.ToString());
|
||||
Assert.True(CurrencyValue.TryParse("1.50 USD", out result));
|
||||
Assert.Equal("1.50 USD", result.ToString());
|
||||
Assert.True(CurrencyValue.TryParse("1.50 usd", out result));
|
||||
Assert.Equal("1.50 USD", result.ToString());
|
||||
Assert.True(CurrencyValue.TryParse("1 usd", out result));
|
||||
Assert.Equal("1 USD", result.ToString());
|
||||
Assert.True(CurrencyValue.TryParse("1usd", out result));
|
||||
Assert.Equal("1 USD", result.ToString());
|
||||
Assert.True(CurrencyValue.TryParse("1.501 usd", out result));
|
||||
Assert.Equal("1.50 USD", result.ToString());
|
||||
Assert.False(CurrencyValue.TryParse("1.501 WTFF", out result));
|
||||
Assert.False(CurrencyValue.TryParse("1,501 usd", out result));
|
||||
Assert.False(CurrencyValue.TryParse("1.501", out result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanParseDerivationScheme()
|
||||
{
|
||||
@ -821,27 +841,29 @@ namespace BTCPayServer.Tests
|
||||
result = parser.Parse("xpub661MyMwAqRbcGeVGU5e5KBcau1HHEUGf9Wr7k4FyLa8yRPNQrrVa7Ndrgg8Afbe2UYXMSL6tJBFd2JewwWASsePPLjkcJFL1tTVEs3UQ23X");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4YSg7vGdAX6wxE8NwDrmih9SR6cK7gUtsAg37w5LfFpJgviCxC6bGGT4G3uckqH5fiV9ZLN1gm5qgQLVuymzFUR5ed7U7ksu-[legacy]", result.ToString());
|
||||
////////////////
|
||||
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o", result.ToString());
|
||||
|
||||
var tpub = "tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o";
|
||||
|
||||
result = parser.Parse(tpub);
|
||||
Assert.Equal(tpub, result.ToString());
|
||||
parser.HintScriptPubKey = BitcoinAddress.Create("tb1q4s33amqm8l7a07zdxcunqnn3gcsjcfz3xc573l", parser.Network).ScriptPubKey;
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o", result.ToString());
|
||||
result = parser.Parse(tpub);
|
||||
Assert.Equal(tpub, result.ToString());
|
||||
|
||||
parser.HintScriptPubKey = BitcoinAddress.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", parser.Network).ScriptPubKey;
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o-[p2sh]", result.ToString());
|
||||
result = parser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
parser.HintScriptPubKey = BitcoinAddress.Create("mwD8bHS65cdgUf6rZUUSoVhi3wNQFu1Nfi", parser.Network).ScriptPubKey;
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o-[legacy]", result.ToString());
|
||||
result = parser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[legacy]", result.ToString());
|
||||
|
||||
parser.HintScriptPubKey = BitcoinAddress.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", parser.Network).ScriptPubKey;
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o-[legacy]");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o-[p2sh]", result.ToString());
|
||||
result = parser.Parse($"{tpub}-[legacy]");
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
result = parser.Parse("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o");
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o-[p2sh]", result.ToString());
|
||||
result = parser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Version>1.0.1.63</Version>
|
||||
<Version>1.0.1.68</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
@ -208,10 +208,13 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
CryptoCode = network.CryptoCode,
|
||||
PaymentMethodId = paymentMethodId.ToString(),
|
||||
IsLightning = paymentMethodId.PaymentType == PaymentTypes.LightningLike,
|
||||
ServerUrl = HttpContext.Request.GetAbsoluteRoot(),
|
||||
OrderId = invoice.OrderId,
|
||||
InvoiceId = invoice.Id,
|
||||
DefaultLang = storeBlob.DefaultLang ?? "en-US",
|
||||
CustomCSSLink = storeBlob.CustomCSS?.AbsoluteUri,
|
||||
CustomLogoLink = storeBlob.CustomLogo?.AbsoluteUri,
|
||||
BtcAddress = paymentMethodDetails.GetPaymentDestination(),
|
||||
OrderAmount = (accounting.TotalDue - accounting.NetworkFee).ToString(),
|
||||
BtcDue = accounting.Due.ToString(),
|
||||
|
@ -86,15 +86,15 @@ namespace BTCPayServer.Controllers
|
||||
Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode),
|
||||
IsAvailable: Task.FromResult(false)))
|
||||
.Where(c => c.Network != null)
|
||||
.Select(c =>
|
||||
.Select(c =>
|
||||
{
|
||||
c.IsAvailable = c.Handler.IsAvailable(c.SupportedPaymentMethod, c.Network);
|
||||
return c;
|
||||
})
|
||||
.ToList();
|
||||
foreach(var supportedPaymentMethod in supportedPaymentMethods.ToList())
|
||||
foreach (var supportedPaymentMethod in supportedPaymentMethods.ToList())
|
||||
{
|
||||
if(!await supportedPaymentMethod.IsAvailable)
|
||||
if (!await supportedPaymentMethod.IsAvailable)
|
||||
{
|
||||
supportedPaymentMethods.Remove(supportedPaymentMethod);
|
||||
}
|
||||
@ -138,6 +138,7 @@ namespace BTCPayServer.Controllers
|
||||
var rate = await storeBlob.ApplyRateRules(o.Network, _RateProviders.GetRateProvider(o.Network, false)).GetRateAsync(invoice.Currency);
|
||||
PaymentMethod paymentMethod = new PaymentMethod();
|
||||
paymentMethod.ParentEntity = entity;
|
||||
paymentMethod.Network = o.Network;
|
||||
paymentMethod.SetId(o.SupportedPaymentMethod.PaymentId);
|
||||
paymentMethod.Rate = rate;
|
||||
var paymentDetails = await o.Handler.CreatePaymentMethodDetails(o.SupportedPaymentMethod, paymentMethod, o.Network);
|
||||
@ -152,14 +153,44 @@ namespace BTCPayServer.Controllers
|
||||
entity.DepositAddress = paymentMethod.DepositAddress;
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
return paymentMethod;
|
||||
return (SupportedPaymentMethod: o.SupportedPaymentMethod, PaymentMethod: paymentMethod);
|
||||
});
|
||||
entity.SetSupportedPaymentMethods(supportedPaymentMethods.Select(s => s.SupportedPaymentMethod));
|
||||
|
||||
var paymentMethods = new PaymentMethodDictionary();
|
||||
List<ISupportedPaymentMethod> supported = new List<ISupportedPaymentMethod>();
|
||||
foreach (var method in methods)
|
||||
{
|
||||
paymentMethods.Add(await method);
|
||||
var o = await method;
|
||||
|
||||
// Check if Lightning Max value is exceeded
|
||||
if(o.SupportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike &&
|
||||
storeBlob.LightningMaxValue != null)
|
||||
{
|
||||
var lightningMaxValue = storeBlob.LightningMaxValue;
|
||||
decimal rate = 0.0m;
|
||||
if (lightningMaxValue.Currency == invoice.Currency)
|
||||
rate = o.PaymentMethod.Rate;
|
||||
else
|
||||
rate = await storeBlob.ApplyRateRules(o.PaymentMethod.Network, _RateProviders.GetRateProvider(o.PaymentMethod.Network, false)).GetRateAsync(lightningMaxValue.Currency);
|
||||
|
||||
var lightningMaxValueCrypto = Money.Coins(lightningMaxValue.Value / rate);
|
||||
if (o.PaymentMethod.Calculate().Due > lightningMaxValueCrypto)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
///////////////
|
||||
supported.Add(o.SupportedPaymentMethod);
|
||||
paymentMethods.Add(o.PaymentMethod);
|
||||
}
|
||||
|
||||
if(supported.Count == 0)
|
||||
{
|
||||
throw new BitpayHttpException(400, "No derivation strategy are available now for this store");
|
||||
}
|
||||
|
||||
entity.SetSupportedPaymentMethods(supported);
|
||||
entity.SetPaymentMethods(paymentMethods);
|
||||
#pragma warning disable CS0618
|
||||
// Legacy Bitpay clients expect information for BTC information, even if the store do not support it
|
||||
var legacyBTCisSet = paymentMethods.Any(p => p.GetId().IsBTCOnChain);
|
||||
@ -177,8 +208,6 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
entity.SetPaymentMethods(paymentMethods);
|
||||
entity.PosData = invoice.PosData;
|
||||
entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider);
|
||||
_EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, "invoice_created"));
|
||||
|
@ -183,6 +183,69 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(StoreUsers), new { storeId = storeId, userId = userId });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/checkout")]
|
||||
public async Task<IActionResult> CheckoutExperience(string storeId)
|
||||
{
|
||||
var store = await _Repo.FindStore(storeId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var vm = new CheckoutExperienceViewModel();
|
||||
vm.SetCryptoCurrencies(_ExplorerProvider, store.GetDefaultCrypto());
|
||||
vm.SetLanguages(_LangService, storeBlob.DefaultLang);
|
||||
vm.LightningMaxValue = storeBlob.LightningMaxValue?.ToString() ?? "";
|
||||
vm.AllowCoinConversion = storeBlob.AllowCoinConversion;
|
||||
vm.CustomCSS = storeBlob.CustomCSS;
|
||||
vm.CustomLogo = storeBlob.CustomLogo;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/checkout")]
|
||||
public async Task<IActionResult> CheckoutExperience(string storeId, CheckoutExperienceViewModel model)
|
||||
{
|
||||
CurrencyValue currencyValue = null;
|
||||
if (!string.IsNullOrWhiteSpace(model.LightningMaxValue))
|
||||
{
|
||||
if (!CurrencyValue.TryParse(model.LightningMaxValue, out currencyValue))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.LightningMaxValue), "Invalid currency value");
|
||||
}
|
||||
}
|
||||
var store = await _Repo.FindStore(storeId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
bool needUpdate = false;
|
||||
var blob = store.GetStoreBlob();
|
||||
if (store.GetDefaultCrypto() != model.DefaultCryptoCurrency)
|
||||
{
|
||||
needUpdate = true;
|
||||
store.SetDefaultCrypto(model.DefaultCryptoCurrency);
|
||||
}
|
||||
model.SetCryptoCurrencies(_ExplorerProvider, model.DefaultCryptoCurrency);
|
||||
model.SetLanguages(_LangService, model.DefaultLang);
|
||||
blob.DefaultLang = model.DefaultLang;
|
||||
blob.AllowCoinConversion = model.AllowCoinConversion;
|
||||
blob.LightningMaxValue = currencyValue;
|
||||
blob.CustomLogo = model.CustomLogo;
|
||||
blob.CustomCSS = model.CustomCSS;
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(store);
|
||||
StatusMessage = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(CheckoutExperience), new
|
||||
{
|
||||
storeId = storeId
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}")]
|
||||
public async Task<IActionResult> UpdateStore(string storeId)
|
||||
@ -195,18 +258,14 @@ namespace BTCPayServer.Controllers
|
||||
var vm = new StoreViewModel();
|
||||
vm.Id = store.Id;
|
||||
vm.StoreName = store.StoreName;
|
||||
vm.SetCryptoCurrencies(_ExplorerProvider, store.GetDefaultCrypto());
|
||||
vm.SetLanguages(_LangService, storeBlob.DefaultLang);
|
||||
vm.StoreWebsite = store.StoreWebsite;
|
||||
vm.NetworkFee = !storeBlob.NetworkFeeDisabled;
|
||||
vm.SpeedPolicy = store.SpeedPolicy;
|
||||
AddPaymentMethods(store, vm);
|
||||
vm.StatusMessage = StatusMessage;
|
||||
vm.MonitoringExpiration = storeBlob.MonitoringExpiration;
|
||||
vm.InvoiceExpiration = storeBlob.InvoiceExpiration;
|
||||
vm.RateMultiplier = (double)storeBlob.GetRateMultiplier();
|
||||
vm.PreferredExchange = storeBlob.PreferredExchange.IsCoinAverage() ? "coinaverage" : storeBlob.PreferredExchange;
|
||||
vm.AllowCoinConversion = storeBlob.AllowCoinConversion;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
@ -248,7 +307,6 @@ namespace BTCPayServer.Controllers
|
||||
[Route("{storeId}")]
|
||||
public async Task<IActionResult> UpdateStore(string storeId, StoreViewModel model)
|
||||
{
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
@ -277,25 +335,15 @@ namespace BTCPayServer.Controllers
|
||||
store.StoreWebsite = model.StoreWebsite;
|
||||
}
|
||||
|
||||
if (store.GetDefaultCrypto() != model.DefaultCryptoCurrency)
|
||||
{
|
||||
needUpdate = true;
|
||||
store.SetDefaultCrypto(model.DefaultCryptoCurrency);
|
||||
}
|
||||
model.SetCryptoCurrencies(_ExplorerProvider, model.DefaultCryptoCurrency);
|
||||
model.SetLanguages(_LangService, model.DefaultLang);
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
blob.NetworkFeeDisabled = !model.NetworkFee;
|
||||
blob.MonitoringExpiration = model.MonitoringExpiration;
|
||||
blob.InvoiceExpiration = model.InvoiceExpiration;
|
||||
blob.DefaultLang = model.DefaultLang;
|
||||
|
||||
bool newExchange = blob.PreferredExchange != model.PreferredExchange;
|
||||
blob.PreferredExchange = model.PreferredExchange;
|
||||
|
||||
blob.SetRateMultiplier(model.RateMultiplier);
|
||||
blob.AllowCoinConversion = model.AllowCoinConversion;
|
||||
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
|
44
BTCPayServer/CurrencyValue.cs
Normal file
44
BTCPayServer/CurrencyValue.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class CurrencyValue
|
||||
{
|
||||
static Regex _Regex = new Regex("^([0-9]+(\\.[0-9]+)?)\\s*([a-zA-Z]+)$");
|
||||
static CurrencyNameTable _CurrencyTable = new CurrencyNameTable();
|
||||
public static bool TryParse(string str, out CurrencyValue value)
|
||||
{
|
||||
value = null;
|
||||
var match = _Regex.Match(str);
|
||||
if (!match.Success ||
|
||||
!decimal.TryParse(match.Groups[1].Value, out var v))
|
||||
return false;
|
||||
|
||||
var currency = match.Groups.Last().Value.ToUpperInvariant();
|
||||
var currencyData = _CurrencyTable.GetCurrencyData(currency);
|
||||
if (currencyData == null)
|
||||
return false;
|
||||
v = Math.Round(v, currencyData.Divisibility);
|
||||
value = new CurrencyValue()
|
||||
{
|
||||
Value = v,
|
||||
Currency = currency
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
public decimal Value { get; set; }
|
||||
public string Currency { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString(CultureInfo.InvariantCulture) + " " + Currency;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.JsonConverters;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@ -254,6 +255,14 @@ namespace BTCPayServer.Data
|
||||
public List<RateRule> RateRules { get; set; } = new List<RateRule>();
|
||||
public string PreferredExchange { get; set; }
|
||||
|
||||
[JsonConverter(typeof(CurrencyValueJsonConverter))]
|
||||
public CurrencyValue LightningMaxValue { get; set; }
|
||||
|
||||
[JsonConverter(typeof(UriJsonConverter))]
|
||||
public Uri CustomLogo { get; set; }
|
||||
[JsonConverter(typeof(UriJsonConverter))]
|
||||
public Uri CustomCSS { get; set; }
|
||||
|
||||
public IRateProvider ApplyRateRules(BTCPayNetwork network, IRateProvider rateProvider)
|
||||
{
|
||||
if (!PreferredExchange.IsCoinAverage())
|
||||
|
38
BTCPayServer/JsonConverters/CurrencyValueJsonConverter.cs
Normal file
38
BTCPayServer/JsonConverters/CurrencyValueJsonConverter.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using NBitcoin.JsonConverters;
|
||||
|
||||
namespace BTCPayServer.JsonConverters
|
||||
{
|
||||
public class CurrencyValueJsonConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(CurrencyValue).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
return reader.TokenType == JsonToken.Null ? null :
|
||||
CurrencyValue.TryParse((string)reader.Value, out var result) ? result :
|
||||
throw new JsonObjectException("Invalid Currency value", reader);
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
throw new JsonObjectException("Invalid Currency value", reader);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if(value != null)
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
38
BTCPayServer/JsonConverters/UriJsonConverter.cs
Normal file
38
BTCPayServer/JsonConverters/UriJsonConverter.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using NBitcoin.JsonConverters;
|
||||
|
||||
namespace BTCPayServer.JsonConverters
|
||||
{
|
||||
public class UriJsonConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(Uri).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
return reader.TokenType == JsonToken.Null ? null :
|
||||
Uri.TryCreate((string)reader.Value, UriKind.Absolute, out var result) ? result :
|
||||
throw new JsonObjectException("Invalid Currency value", reader);
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
throw new JsonObjectException("Invalid Currency value", reader);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
writer.WriteValue(((Uri)value).AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,9 +13,11 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
public string CryptoImage { get; set; }
|
||||
public string Link { get; set; }
|
||||
}
|
||||
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string CustomLogoLink { get; set; }
|
||||
public string DefaultLang { get; set; }
|
||||
public List<AvailableCrypto> AvailableCryptos { get; set; } = new List<AvailableCrypto>();
|
||||
public bool IsLightning { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public string ServerUrl { get; set; }
|
||||
public string InvoiceId { get; set; }
|
||||
|
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
{
|
||||
public class CheckoutExperienceViewModel
|
||||
{
|
||||
class Format
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
public SelectList CryptoCurrencies { get; set; }
|
||||
public SelectList Languages { get; set; }
|
||||
|
||||
[Display(Name = "Default crypto currency on checkout")]
|
||||
public string DefaultCryptoCurrency { get; set; }
|
||||
[Display(Name = "Default language on checkout")]
|
||||
public string DefaultLang { get; set; }
|
||||
[Display(Name = "Allow conversion through third party (Shapeshift, Changelly...)")]
|
||||
public bool AllowCoinConversion
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
[Display(Name = "Do not propose lightning payment if value of the invoice is above...")]
|
||||
[MaxLength(20)]
|
||||
public string LightningMaxValue { get; set; }
|
||||
|
||||
[Display(Name = "Link to a custom CSS stylesheet")]
|
||||
[Url]
|
||||
public Uri CustomCSS { get; set; }
|
||||
[Display(Name = "Link to a custom logo")]
|
||||
[Url]
|
||||
public Uri CustomLogo { get; set; }
|
||||
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 chosen = choices.FirstOrDefault(f => f.Value == defaultCrypto) ?? choices.FirstOrDefault();
|
||||
CryptoCurrencies = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||
DefaultCryptoCurrency = chosen.Name;
|
||||
}
|
||||
|
||||
public void SetLanguages(LanguageService langService, string defaultLang)
|
||||
{
|
||||
defaultLang = defaultLang ?? "en-US";
|
||||
var choices = langService.GetLanguages().Select(o => new Format() { Name = o.DisplayName, Value = o.Code }).ToArray();
|
||||
var chosen = choices.FirstOrDefault(f => f.Value == defaultLang) ?? choices.FirstOrDefault();
|
||||
Languages = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||
DefaultLang = chosen.Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,11 +17,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
public string Crypto { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
class Format
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public StoreViewModel()
|
||||
{
|
||||
|
||||
@ -95,24 +91,6 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Display(Name = "Allow conversion through third party (Shapeshift, Changelly...)")]
|
||||
public bool AllowCoinConversion
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string StatusMessage
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public SelectList CryptoCurrencies { get; set; }
|
||||
public SelectList Languages { get; set; }
|
||||
|
||||
[Display(Name = "Default crypto currency on checkout")]
|
||||
public string DefaultCryptoCurrency { get; set; }
|
||||
[Display(Name = "Default language on checkout")]
|
||||
public string DefaultLang { get; set; }
|
||||
|
||||
public class LightningNode
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
@ -123,21 +101,5 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
get; set;
|
||||
} = new List<LightningNode>();
|
||||
|
||||
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 chosen = choices.FirstOrDefault(f => f.Value == defaultCrypto) ?? choices.FirstOrDefault();
|
||||
CryptoCurrencies = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||
DefaultCryptoCurrency = chosen.Name;
|
||||
}
|
||||
|
||||
public void SetLanguages(LanguageService langService, string defaultLang)
|
||||
{
|
||||
defaultLang = defaultLang ?? "en-US";
|
||||
var choices = langService.GetLanguages().Select(o => new Format() { Name = o.DisplayName, Value = o.Code }).ToArray();
|
||||
var chosen = choices.FirstOrDefault(f => f.Value == defaultLang) ?? choices.FirstOrDefault();
|
||||
Languages = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||
DefaultLang = chosen.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
#pragma warning disable CS0618
|
||||
public List<PaymentEntity> GetPayments()
|
||||
{
|
||||
return Payments.ToList();
|
||||
return Payments?.ToList() ?? new List<PaymentEntity>();
|
||||
}
|
||||
public List<PaymentEntity> GetPayments(string cryptoCode)
|
||||
{
|
||||
@ -375,7 +375,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
BIP21 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}",
|
||||
};
|
||||
}
|
||||
if (info.GetId().PaymentType == PaymentTypes.LightningLike)
|
||||
var paymentId = info.GetId();
|
||||
if (paymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
{
|
||||
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
|
||||
{
|
||||
@ -383,7 +384,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
};
|
||||
}
|
||||
#pragma warning disable CS0618
|
||||
if (info.CryptoCode == "BTC")
|
||||
if (info.CryptoCode == "BTC" && paymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
dto.Url = cryptoInfo.Url;
|
||||
dto.BTCPrice = cryptoInfo.Price;
|
||||
|
@ -28,6 +28,7 @@ namespace BTCPayServer.Services
|
||||
new Language("es-ES", "Spanish"),
|
||||
new Language("pt-BR", "Portuguese (Brazil)"),
|
||||
new Language("nl-NL", "Dutch"),
|
||||
new Language("cs-CZ", "Česky"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
private decimal GetRate(Dictionary<string, decimal> rates, string currency)
|
||||
{
|
||||
if (currency == "BTC")
|
||||
return 1.0m;
|
||||
if (rates.TryGetValue(currency, out decimal result))
|
||||
return result;
|
||||
throw new RateUnavailableException(currency);
|
||||
|
@ -3,7 +3,14 @@
|
||||
<div class="top-header">
|
||||
<div class="header">
|
||||
<div class="header__icon">
|
||||
<img class="header__icon__img" src="~/img/logo-white.png" height="40">
|
||||
@if(Model.CustomLogoLink != null)
|
||||
{
|
||||
<img class="header__icon__img" src="@Model.CustomLogoLink" height="40">
|
||||
}
|
||||
else
|
||||
{
|
||||
<img class="header__icon__img" src="~/img/logo-white.png" height="40">
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="timer-row">
|
||||
@ -26,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-details">
|
||||
@if (Model.AvailableCryptos.Count > 1)
|
||||
@if(Model.AvailableCryptos.Count > 1)
|
||||
{
|
||||
<div class="currency-selection">
|
||||
<div class="single-item-order__left">
|
||||
@ -36,7 +43,7 @@
|
||||
</div>
|
||||
<div class="single-item-order__right">
|
||||
<div class="payment__currencies">
|
||||
@foreach (var crypto in Model.AvailableCryptos)
|
||||
@foreach(var crypto in Model.AvailableCryptos)
|
||||
{
|
||||
<a href="@crypto.Link" onclick="return changeCurrency('@crypto.PaymentMethodId');">
|
||||
<img style="height:32px; margin-left:5px;" alt="@crypto.PaymentMethodId" src="@crypto.CryptoImage" />
|
||||
@ -101,7 +108,7 @@
|
||||
<div class="payment-tabs__tab" id="copy-tab">
|
||||
<span>{{$t("Copy")}}</span>
|
||||
</div>
|
||||
@if (Model.AllowCoinConversion)
|
||||
@if(Model.AllowCoinConversion)
|
||||
{
|
||||
<div class="payment-tabs__tab" id="altcoins-tab">
|
||||
<span>{{$t("Conversion")}}</span>
|
||||
@ -193,10 +200,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.AllowCoinConversion)
|
||||
@if(Model.AllowCoinConversion)
|
||||
{
|
||||
<div id="altcoins" class="bp-view payment manual-flow">
|
||||
<div v-if="srvModel.paymentMethodId != 'BTC_LightningLike'">
|
||||
<div v-if="srvModel.isLightning">
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_Lightning")}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_BodyTop", srvModel)}}
|
||||
@ -217,13 +231,6 @@
|
||||
</a>*@
|
||||
</center>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_Lightning")}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,12 @@
|
||||
</script>
|
||||
|
||||
<bundle name="wwwroot/bundles/checkout-bundle.min.js" />
|
||||
|
||||
|
||||
@if(Model.CustomCSSLink != null)
|
||||
{
|
||||
<link href="@Model.CustomCSSLink" rel="stylesheet" />
|
||||
}
|
||||
</head>
|
||||
<body style="background: #E4E4E4">
|
||||
<noscript>
|
||||
@ -83,6 +89,9 @@
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div style="margin-top: 10px; text-align: right;" class="form-text small text-muted">
|
||||
<span>Powered by <a target="_blank" href="https://github.com/btcpayserver/btcpayserver">BTCPay Server</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,7 +111,8 @@
|
||||
'ja-JP': { translation: locales_ja },
|
||||
'fr-FR': { translation: locales_fr },
|
||||
'pt-BR': { translation: locales_pt_br },
|
||||
'nl': { translation: locales_nl }
|
||||
'nl': { translation: locales_nl },
|
||||
'cs-CZ': { translation: locales_cs }
|
||||
},
|
||||
});
|
||||
|
||||
|
54
BTCPayServer/Views/Stores/CheckoutExperience.cshtml
Normal file
54
BTCPayServer/Views/Stores/CheckoutExperience.cshtml
Normal file
@ -0,0 +1,54 @@
|
||||
@model CheckoutExperienceViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["Title"] = "Checkout experience";
|
||||
ViewData.AddActivePage(BTCPayServer.Views.Stores.StoreNavPages.Checkout);
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<form method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomLogo"></label>
|
||||
<input asp-for="CustomLogo" class="form-control" />
|
||||
<span asp-validation-for="CustomLogo" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSS"></label>
|
||||
<input asp-for="CustomCSS" class="form-control" />
|
||||
<span asp-validation-for="CustomCSS" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCryptoCurrency"></label>
|
||||
<select asp-for="DefaultCryptoCurrency" asp-items="Model.CryptoCurrencies" class="form-control"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultLang"></label>
|
||||
<select asp-for="DefaultLang" asp-items="Model.Languages" class="form-control"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="AllowCoinConversion"></label>
|
||||
<input asp-for="AllowCoinConversion" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="LightningMaxValue"></label>
|
||||
<input asp-for="LightningMaxValue" class="form-control" />
|
||||
<span asp-validation-for="LightningMaxValue" 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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
@ -11,13 +11,14 @@ namespace BTCPayServer.Views.Stores
|
||||
{
|
||||
public static string ActivePageKey => "ActivePage";
|
||||
public static string Index => "Index";
|
||||
|
||||
public static string Checkout => "Checkout experience";
|
||||
|
||||
public static string Tokens => "Tokens";
|
||||
public static string Users => "Users";
|
||||
public static string UsersNavClass(ViewContext viewContext) => PageNavClass(viewContext, Users);
|
||||
public static string TokensNavClass(ViewContext viewContext) => PageNavClass(viewContext, Tokens);
|
||||
|
||||
public static string CheckoutNavClass(ViewContext viewContext) => PageNavClass(viewContext, Checkout);
|
||||
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
|
||||
|
||||
public static string PageNavClass(ViewContext viewContext, string page)
|
||||
|
@ -17,7 +17,8 @@
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<h5>Users</h5>
|
||||
<span>Add access to your store to other users (Guest will not be able to see and modify the store settings)</span>
|
||||
<span>Add access to your store to other users (Guest will not be able to see and modify the store settings)<br />
|
||||
Note that the user must have a registered account on this BTCPay Server.</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
<form method="post">
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@ -30,14 +30,6 @@
|
||||
<input asp-for="StoreWebsite" class="form-control" />
|
||||
<span asp-validation-for="StoreWebsite" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCryptoCurrency"></label>
|
||||
<select asp-for="DefaultCryptoCurrency" asp-items="Model.CryptoCurrencies" class="form-control"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultLang"></label>
|
||||
<select asp-for="DefaultLang" asp-items="Model.Languages" class="form-control"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="NetworkFee"></label>
|
||||
<input asp-for="NetworkFee" type="checkbox" class="form-check" />
|
||||
@ -74,10 +66,6 @@
|
||||
</select>
|
||||
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="AllowCoinConversion"></label>
|
||||
<input asp-for="AllowCoinConversion" type="checkbox" class="form-check" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<h5>Derivation Scheme</h5>
|
||||
<span>The DerivationScheme represents the destination of the funds received by your invoice on chain.</span>
|
||||
@ -94,19 +82,19 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var scheme in Model.DerivationSchemes)
|
||||
{
|
||||
<tr>
|
||||
<td>@scheme.Crypto</td>
|
||||
<td style="max-width:300px;overflow:hidden;">@scheme.Value</td>
|
||||
<td style="text-align:right">
|
||||
@if(!string.IsNullOrWhiteSpace(scheme.Value))
|
||||
{
|
||||
<a asp-action="Wallet" asp-route-cryptoCode="@scheme.Crypto">Wallet</a><span> - </span>
|
||||
}
|
||||
<a asp-action="AddDerivationScheme" asp-route-cryptoCode="@scheme.Crypto">Modify</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
{
|
||||
<tr>
|
||||
<td>@scheme.Crypto</td>
|
||||
<td style="max-width:300px;overflow:hidden;">@scheme.Value</td>
|
||||
<td style="text-align:right">
|
||||
@if(!string.IsNullOrWhiteSpace(scheme.Value))
|
||||
{
|
||||
<a asp-action="Wallet" asp-route-cryptoCode="@scheme.Crypto">Wallet</a><span> - </span>
|
||||
}
|
||||
<a asp-action="AddDerivationScheme" asp-route-cryptoCode="@scheme.Crypto">Modify</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -130,13 +118,13 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var scheme in Model.LightningNodes)
|
||||
{
|
||||
<tr>
|
||||
<td>@scheme.CryptoCode</td>
|
||||
<td>@scheme.Address</td>
|
||||
<td style="text-align:right"><a asp-action="AddLightningNode" asp-route-cryptoCode="@scheme.CryptoCode">Modify</a></td>
|
||||
</tr>
|
||||
}
|
||||
{
|
||||
<tr>
|
||||
<td>@scheme.CryptoCode</td>
|
||||
<td>@scheme.Address</td>
|
||||
<td style="text-align:right"><a asp-action="AddLightningNode" asp-route-cryptoCode="@scheme.CryptoCode">Modify</a></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -2,7 +2,8 @@
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="@StoreNavPages.IndexNavClass(ViewContext)"><a asp-action="UpdateStore">Information</a></li>
|
||||
<li class="@StoreNavPages.IndexNavClass(ViewContext)"><a asp-action="UpdateStore">General settings</a></li>
|
||||
<li class="@StoreNavPages.CheckoutNavClass(ViewContext)"><a asp-action="CheckoutExperience">Checkout experience</a></li>
|
||||
<li class="@StoreNavPages.TokensNavClass(ViewContext)"><a asp-action="ListTokens">Access Tokens</a></li>
|
||||
<li class="@StoreNavPages.UsersNavClass(ViewContext)"><a asp-action="StoreUsers">Users</a></li>
|
||||
</ul>
|
||||
|
48
BTCPayServer/wwwroot/js/checkout/langs/cs.js
Normal file
48
BTCPayServer/wwwroot/js/checkout/langs/cs.js
Normal file
@ -0,0 +1,48 @@
|
||||
const locales_cs = {
|
||||
nested: {
|
||||
lang: 'Jazyk'
|
||||
},
|
||||
"Awaiting Payment...": "Očekávám platbu...",
|
||||
"Pay with": "Zaplatit pomocí",
|
||||
"Contact and Refund Email": "Kontaktní email",
|
||||
"Contact_Body": "Prosímte poskytněte vaši emailovou adresu. Kontaktujeme vás v případě, že se objeví problému s vaší platbou.",
|
||||
"Your email": "Váš email",
|
||||
"Continue": "Pokračovat",
|
||||
"Please enter a valid email address": "Prosíme vložte platnou emailovou adresu",
|
||||
"Order Amount": "Cena objednávky",
|
||||
"Network Cost": "Síťové náklady",
|
||||
"Already Paid": "Již zaplaceno",
|
||||
"Due": "Do kdy",
|
||||
// Tabs
|
||||
"Scan": "Skenovat",
|
||||
"Copy": "Kopírovat",
|
||||
"Conversion": "Konverze",
|
||||
// Scan tab
|
||||
"Open in wallet": "Otevřít v peněžence",
|
||||
// Copy tab
|
||||
"CompletePay_Body": "K dokončení platby, prosíme pošlete {{btcDue}} {{cryptoCode}} na adresu níže.",
|
||||
"Amount": "Částka",
|
||||
"Address": "Adresa",
|
||||
"Copied": "Zkopírováno",
|
||||
// Conversion tab
|
||||
"ConversionTab_BodyTop": "Můžete zaplatit {{btcDue}} {{cryptoCode}} i pomocí altcoinů které přímo nepodporuje obchodník.",
|
||||
"ConversionTab_BodyDesc": "Tato služba je poskytována třetí stranou. Prosíme mějte na paměti, že nemáme žádnou kontrolu nad tím, jak poskytovatelé budou nakládat s vašimi prostředky. Faktura bude označena jako zaplacena, pouze když jsou prostředky obdrženy v {{cryptoCode}} Blockchainu.",
|
||||
"Shapeshift_Button_Text": "Zaplatit pomocí Altcoinů",
|
||||
"ConversionTab_Lightning": "Pro platby Lightning Network nejsou dostupní žádní poskytovatelé konverzí.",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "Faktura brzy vyprší...",
|
||||
"Invoice expired": "Faktura vypršela",
|
||||
"What happened?": "Co se stalo?",
|
||||
"InvoiceExpired_Body_1": "Tato faktura již vypršela. Faktura je platná pouze {{maxTimeMinutes}} minut. \
|
||||
Můžete se vrátit do {{storeName}}, pokud chcete svojí objednávku založit znovu.",
|
||||
"InvoiceExpired_Body_2": "Pokud jste se pokoušeli poslat platbu, nebyla zatím zaznamenána v Bitcoinové síti. Zatím jsme neobdrželi vaše prostředky.",
|
||||
"InvoiceExpired_Body_3": "Pokud nebude transakce přijata Bitcoinovou sítí, vaše prostředky bude opět použitelné ve vaší peněžence. V závislosti na vaší peněžence toto může trvat 48-72 hodin.",
|
||||
"Invoice ID": "ID Faktury",
|
||||
"Order ID": "ID Objednávky",
|
||||
"Return to StoreName": "Vrátit se na {{storeName}}",
|
||||
// Invoice paid
|
||||
"This invoice has been paid": "Faktura byla zaplacena",
|
||||
// Invoice archived
|
||||
"This invoice has been archived": "Tato faktura byla archivována",
|
||||
"Archived_Body": "Prosíme kontaktujte prodejce pro informace o objednávce a případnou pomoc"
|
||||
};
|
@ -28,7 +28,7 @@ const locales_de = {
|
||||
"ConversionTab_BodyTop": "Sie können {{btcDue}} {{cryptoCode}} mit altcoins bezahlen, die nicht direkt vom Händler unterstützt werden.",
|
||||
"ConversionTab_BodyDesc": "Dieser Service wird von Drittanbietern bereitgestellt. Bitte beachten Sie, dass wir keine Kontrolle darüber haben, wie die Anbieter Ihre Gelder weiterleiten. Die Rechnung wird erst bezahlt, wenn das Geld in {{cryptoCode}} Blockchain eingegangen ist.",
|
||||
"Shapeshift_Button_Text": "Bezahlen mit Altcoins",
|
||||
"ConversionTab_Lightning": "Für BTC Lightning Network-Zahlungen sind keine Conversion-Anbieter verfügbar.",
|
||||
"ConversionTab_Lightning": "Für Lightning Network-Zahlungen sind keine Conversion-Anbieter verfügbar.",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "Die Rechnung läuft bald ab...",
|
||||
"Invoice expired": "Die Rechnung ist abgelaufen",
|
||||
|
@ -28,7 +28,7 @@ const locales_en = {
|
||||
"ConversionTab_BodyTop": "You can pay {{btcDue}} {{cryptoCode}} using altcoins other than the ones merchant directly supports.",
|
||||
"ConversionTab_BodyDesc": "This service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on {{cryptoCode}} Blockchain.",
|
||||
"Shapeshift_Button_Text": "Pay with Altcoins",
|
||||
"ConversionTab_Lightning": "No conversion providers available for BTC Lightning Network payments.",
|
||||
"ConversionTab_Lightning": "No conversion providers available for Lightning Network payments.",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "Invoice expiring soon...",
|
||||
"Invoice expired": "Invoice expired",
|
||||
|
@ -28,7 +28,7 @@ const locales_es = {
|
||||
"ConversionTab_BodyTop": "Puede pagar {{btcDue}} {{cryptoCode}} usando altcoins que no sean los que el comerciante soporta directamente.",
|
||||
"ConversionTab_BodyDesc": "Este servicio es proveído por terceros. Tenga en cuenta que no tenemos control sobre cómo los proveedores enviarán sus fondos. La factura solo se marcará como abonada una vez que se reciban los fondos en el bloque de cadenas de {{cryptoCode}} .",
|
||||
"Shapeshift_Button_Text": "Pagar con Altcoins",
|
||||
"ConversionTab_Lightning": "No hay proveedores de conversión disponibles para los pagos de BTC Lightning Network.",
|
||||
"ConversionTab_Lightning": "No hay proveedores de conversión disponibles para los pagos de Lightning Network.",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "La factura expira pronto...",
|
||||
"Invoice expired": "La factura expiro",
|
||||
|
@ -28,7 +28,7 @@
|
||||
"ConversionTab_BodyTop": "代わりに、お店が受け付けていなくても {{btcDue}} {{cryptoCode}} での支払いもできます。",
|
||||
"ConversionTab_BodyDesc": "ただし、この変換は第三者サービスによるものですので、お店が受け付けている通貨で着金するまでの間の処理に関しては何の保証もいたしません。変換後に受付中の通貨 ({{cryptoCode}}) がお店に着金してから支払い済みとなりますのでご了承ください。",
|
||||
"Shapeshift_Button_Text": "他の仮想通貨で支払う",
|
||||
"ConversionTab_Lightning": "BTCライトニングのペイメントでは現在変換サービスが存在しないためご利用いただけません。ご了承ください。",
|
||||
"ConversionTab_Lightning": "ライトニングのペイメントでは現在変換サービスが存在しないためご利用いただけません。ご了承ください。",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "お支払いの期限が迫っています...",
|
||||
"Invoice expired": "お支払いの期限が切れました",
|
||||
@ -36,7 +36,7 @@
|
||||
"InvoiceExpired_Body_1": "当件のお支払いの有効期限が過ぎてしまいました。最大 {{maxTimeMinutes}} 分以内に支払うことが義務付けられています。 \
|
||||
まだお支払いのご希望の場合 {{storeName}} に一旦戻っていただき、もう一度お支払いの手続きを最初からやり直してみてください。",
|
||||
"InvoiceExpired_Body_2": "送金手続きを完了したつもりでも、ネットワークにて取り込まれて処理されるまでは決済となりません。現時点ではまだ着金しておりません。",
|
||||
"InvoiceExpired_Body_3": "ビットコインネットワークにて取り込まれなかった送金はいずれ送金元のウォレットに戻りますが、ウォレットソフトによっては2〜3日かかる場合もございますのでご了承ください。",
|
||||
"InvoiceExpired_Body_3": "ネットワークにて取り込まれなかった送金はいずれ送金元のウォレットに戻りますが、ウォレットソフトによっては2〜3日かかる場合もございますのでご了承ください。",
|
||||
"Invoice ID": "お支払い ID",
|
||||
"Order ID": "ご注文 ID",
|
||||
"Return to StoreName": "{{storeName}} に戻る",
|
||||
|
@ -28,7 +28,7 @@ const locales_pt_br = {
|
||||
"ConversionTab_BodyTop": "Você pode pagar {{btcDue}} {{cryptoCode}} utilizando outras altcoins além das que a loja aceita diretamente.",
|
||||
"ConversionTab_BodyDesc": "Esse serviço é oferecido por terceiros. Por favor, tenha em mente que não temos nenhum controle sobre como seus fundos serão utilizados. A fatura apenas será marcada como paga quando os fundos forem recebidos na Blockchain {{cryptoCode}}.",
|
||||
"Shapeshift_Button_Text": "Pague com Altcoins",
|
||||
"ConversionTab_Lightning": "Não há provedores de conversão disponíveis para pagamentos via Lightning Network de BTC.",
|
||||
"ConversionTab_Lightning": "Não há provedores de conversão disponíveis para pagamentos via Lightning Network.",
|
||||
// Invoice expired
|
||||
"Invoice expiring soon...": "A fatura está vencendo...",
|
||||
"Invoice expired": "Fatura vencida",
|
||||
|
@ -1,3 +1,6 @@
|
||||
$ver = [regex]::Match((Get-Content BTCPayServer\BTCPayServer.csproj), '<Version>([^<]+)<').Groups[1].Value
|
||||
git tag -a "v$ver" -m "$ver"
|
||||
git push --tags
|
||||
git push --tags
|
||||
git tag -d "stable"
|
||||
git tag -a "stable" -m "stable"
|
||||
git push --tags --force
|
||||
|
Reference in New Issue
Block a user