Fix several issues in cart

* Fix: Only USD currency with 2 decimals were properly handled for tips
* Fix: All PoS apps would were sharing the same basket
* Fix: Currency formatting was not using server side information
* Fix: Various bug of formatting for decimal 0 and more than 2.
This commit is contained in:
nicolas.dorier
2018-12-04 13:04:26 +09:00
parent 1f14bd6188
commit cad602ad14
5 changed files with 58 additions and 197 deletions

View File

@@ -41,6 +41,7 @@ namespace BTCPayServer.Controllers
var currency = _AppsHelper.GetCurrencyData(settings.Currency, false); var currency = _AppsHelper.GetCurrencyData(settings.Currency, false);
double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility)); double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility));
var numberFormatInfo = _AppsHelper.Currencies.GetNumberFormatInfo(currency.Code) ?? _AppsHelper.Currencies.GetNumberFormatInfo("USD");
return View(new ViewPointOfSaleViewModel() return View(new ViewPointOfSaleViewModel()
{ {
Title = settings.Title, Title = settings.Title,
@@ -49,6 +50,14 @@ namespace BTCPayServer.Controllers
ShowCustomAmount = settings.ShowCustomAmount, ShowCustomAmount = settings.ShowCustomAmount,
CurrencyCode = currency.Code, CurrencyCode = currency.Code,
CurrencySymbol = currency.Symbol, CurrencySymbol = currency.Symbol,
CurrencyInfo = new ViewPointOfSaleViewModel.CurrencyInfoData()
{
CurrencySymbol = string.IsNullOrEmpty(currency.Symbol) ? currency.Code : currency.Symbol,
Divisibility = currency.Divisibility,
DecimalSeparator = numberFormatInfo.CurrencyDecimalSeparator,
ThousandSeparator = numberFormatInfo.NumberGroupSeparator,
Prefixed = new[] { 0, 2 }.Contains(numberFormatInfo.CurrencyPositivePattern)
},
Items = _AppsHelper.Parse(settings.Template, settings.Currency), Items = _AppsHelper.Parse(settings.Template, settings.Currency),
ButtonText = settings.ButtonText, ButtonText = settings.ButtonText,
CustomButtonText = settings.CustomButtonText, CustomButtonText = settings.CustomButtonText,
@@ -124,7 +133,7 @@ namespace BTCPayServer.Controllers
{ {
ApplicationDbContextFactory _ContextFactory; ApplicationDbContextFactory _ContextFactory;
CurrencyNameTable _Currencies; CurrencyNameTable _Currencies;
public CurrencyNameTable Currencies => _Currencies;
public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies) public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies)
{ {
_ContextFactory = contextFactory; _ContextFactory = contextFactory;

View File

@@ -22,6 +22,17 @@ namespace BTCPayServer.Models.AppViewModels
public bool Custom { get; set; } public bool Custom { get; set; }
} }
public class CurrencyInfoData
{
public bool Prefixed { get; set; }
public string CurrencySymbol { get; set; }
public string ThousandSeparator { get; set; }
public string DecimalSeparator { get; set; }
public int Divisibility { get; internal set; }
}
public CurrencyInfoData CurrencyInfo { get; set; }
public bool EnableShoppingCart { get; set; } public bool EnableShoppingCart { get; set; }
public bool ShowCustomAmount { get; set; } public bool ShowCustomAmount { get; set; }
public string Step { get; set; } public string Step { get; set; }
@@ -29,7 +40,7 @@ namespace BTCPayServer.Models.AppViewModels
public Item[] Items { get; set; } public Item[] Items { get; set; }
public string CurrencyCode { get; set; } public string CurrencyCode { get; set; }
public string CurrencySymbol { get; set; } public string CurrencySymbol { get; set; }
public string AppId { get; set; }
public string ButtonText { get; set; } public string ButtonText { get; set; }
public string CustomButtonText { get; set; } public string CustomButtonText { get; set; }
public string CustomTipText { get; set; } public string CustomTipText { get; set; }

View File

@@ -5,7 +5,7 @@
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"BTCPAY_NETWORK": "regtest", "BTCPAY_NETWORK": "regtest",
"BTCPAY_BUNDLEJSCSS": "true", "BTCPAY_BUNDLEJSCSS": "false",
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/", "BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify", "BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true", "BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
@@ -24,7 +24,7 @@
"BTCPAY_NETWORK": "regtest", "BTCPAY_NETWORK": "regtest",
"BTCPAY_PORT": "14142", "BTCPAY_PORT": "14142",
"BTCPAY_HttpsUseDefaultCertificate": "true", "BTCPAY_HttpsUseDefaultCertificate": "true",
"BTCPAY_BUNDLEJSCSS": "true", "BTCPAY_BUNDLEJSCSS": "false",
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/", "BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify", "BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true", "BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",

View File

@@ -61,7 +61,15 @@ namespace BTCPayServer.Services.Rates
currencyInfo.CurrencySymbol = currency; currencyInfo.CurrencySymbol = currency;
return currencyInfo; return currencyInfo;
} }
public NumberFormatInfo GetNumberFormatInfo(string currency)
{
var curr = GetCurrencyProvider(currency);
if (curr is CultureInfo cu)
return cu.NumberFormat;
if (curr is NumberFormatInfo ni)
return ni;
return null;
}
public IFormatProvider GetCurrencyProvider(string currency) public IFormatProvider GetCurrencyProvider(string currency)
{ {
lock (_CurrencyProviders) lock (_CurrencyProviders)

View File

@@ -245,215 +245,48 @@ Cart.prototype.formatCurrency = function(amount, currency, symbol) {
thousandsSep = '', thousandsSep = '',
decimalSep = '' decimalSep = ''
prefix = '', prefix = '',
postfix = '', postfix = '';
currencyName = currency.toLowerCase();
// Currency symbols if (srvModel.currencyInfo.prefixed) {
switch (currencyName) { prefix = srvModel.currencyInfo.currencySymbol;
case 'usd':
case 'aud':
case 'cad':
case 'clp':
case 'mxn':
case 'nzd':
case 'sgd':
prefix = '$';
break;
case 'eur':
postfix = ' €';
break;
case 'chf':
prefix = 'CHF ';
break;
case 'gbp':
prefix = '£';
break;
case 'jpy':
case 'cny':
prefix = '¥';
break;
case 'hkd':
prefix = 'HK$';
break;
case 'ars':
case 'cop':
prefix = '$ ';
break;
case 'brl':
prefix = 'R$ ';
break;
case 'bdt':
postfix = '৳';
break;
case 'czk':
postfix = ' Kč';
break;
case 'dkk':
postfix = ' kr.';
break;
case 'huf':
postfix = ' Ft';
break;
case 'hrk':
postfix = ' HRK';
break;
case 'ils':
postfix = ' ₪';
break;
case 'inr':
prefix = '₹ ';
break;
case 'isk':
postfix = ' ISK';
break;
case 'kzt':
postfix = ' ₸';
break;
case 'myr':
prefix = 'RM';
break;
case 'ngn':
prefix = '₦';
break;
case 'nok':
prefix = 'kr ';
break;
case 'php':
prefix = '₱';
break;
case 'pen':
prefix = 'S/';
break;
case 'pln':
postfix = ' zł';
break;
case 'ron':
postfix = ' RON';
break;
case 'rub':
postifx = ' ₽';
break;
case 'sek':
postfix = ' kr';
break;
case 'aed':
prefix = 'د.إ. ';
break;
case 'egp':
prefix = 'ج.م.';
break;
case 'irr':
prefix = 'ریال';
break;
case 'pkr':
prefix = ' ر';
break;
case 'sar':
prefix = 'ر.س.';
break;
case 'try':
prefix = '₺';
break;
case 'uah':
postfix = ' ₴';
break;
case 'vnd':
postfix = ' ₫';
break;
case 'zar':
prefix = 'R';
break;
default:
prefix = symbol || currency;
} }
else {
// Currency separators postfix = srvModel.currencyInfo.currencySymbol;
switch (currencyName) {
case 'eur':
case 'czk':
case 'huf':
case 'kzt':
case 'nok':
case 'pln':
case 'rub':
case 'sek':
case 'uah':
case 'zar':
thousandsSep = ' ';
decimalSep = ',';
break;
case 'chf':
thousandsSep = '';
decimalSep = '.';
break;
case 'cop':
case 'clp':
case 'idr':
case 'isk':
case 'vnd':
thousandsSep = '.';
break;
case 'jpy':
thousandsSep = ',';
break;
case 'ars':
case 'brl':
case 'dkk':
case 'hrk':
case 'ron':
case 'try':
thousandsSep = '.';
decimalSep = ',';
break;
case 'irr':
case 'pkr':
thousandsSep = '٬';
break;
case 'egp':
case 'sar':
thousandsSep = '٬';
decimalSep = '٫';
break;
default:
thousandsSep = ',';
decimalSep = '.';
} }
thousandsSep = srvModel.currencyInfo.thousandSeparator;
decimalSep = srvModel.currencyInfo.decimalSeparator;
amt = amount.toFixed(srvModel.currencyInfo.divisibility);
if (decimalSep !== '') {
// Replace decimal separator
amt = amount.toFixed(2).replace(/.(\d{2})$/g, decimalSep + '\$1');
} else {
// No decimal separator
amt = amount.toString();
}
// Add currency sign and thousands separator // Add currency sign and thousands separator
amt = prefix + amt.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep) + postfix; var splittedAmount = amt.split('.');
amt = (splittedAmount[0] + '.').replace(/(\d)(?=(\d{3})+\.)/g, '$1' + thousandsSep);
amt = amt.substr(0, amt.length - 1);
if(splittedAmount.length == 2)
amt = amt + '.' + splittedAmount[1];
if (srvModel.currencyInfo.divisibility !== 0) {
amt[amt.length - srvModel.currencyInfo.divisibility - 1] = decimalSep;
}
amt = prefix + amt + postfix;
return amt; return amt;
} }
Cart.prototype.toCents = function(num) { Cart.prototype.toCents = function(num) {
return num * 100; return num * Math.pow(10, srvModel.currencyInfo.divisibility);
} }
Cart.prototype.fromCents = function(num) { Cart.prototype.fromCents = function(num) {
return num / 100; return num / Math.pow(10, srvModel.currencyInfo.divisibility);
} }
Cart.prototype.getStorageKey = function () { return ('cart' + srvModel.appId + srvModel.currencyCode); }
Cart.prototype.saveLocalStorage = function() { Cart.prototype.saveLocalStorage = function() {
localStorage.setItem('cart', JSON.stringify(this.content)); localStorage.setItem(this.getStorageKey(), JSON.stringify(this.content));
} }
Cart.prototype.loadLocalStorage = function() { Cart.prototype.loadLocalStorage = function() {
this.content = $.parseJSON(localStorage.getItem('cart')) || []; this.content = $.parseJSON(localStorage.getItem(this.getStorageKey())) || [];
// Get number of cart items // Get number of cart items
for (var key in this.content) { for (var key in this.content) {
@@ -464,9 +297,9 @@ Cart.prototype.loadLocalStorage = function() {
} }
Cart.prototype.destroy = function() { Cart.prototype.destroy = function() {
localStorage.removeItem('cart'); localStorage.removeItem(this.getStorageKey());
this.content = []; this.content = [];
this.items = 0; this.items = 0;
this.totalAmount = 0; this.totalAmount = 0;
this.tip = 0; this.tip = 0;
} }