Update price display (#4736)

* Update price display

As proposed by @dstrukt in #4364.

* Update format

* Unify price display across the app

* Add DisplayFormatter

* Replace DisplayFormatCurrency method

* Use symbol currency format for invoice

* Unify currency formats on backend pages

* Revert recent changes

* Do not show exchange rate and fiat order amount for crypto denominations

* Fix test and add test cases
This commit is contained in:
d11n
2023-03-13 02:12:58 +01:00
committed by GitHub
parent f3d9e07c5e
commit ded0c8a3bc
33 changed files with 269 additions and 152 deletions

View File

@@ -1,6 +1,8 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client.Models
@using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@inject DisplayFormatter DisplayFormatter
@model BTCPayServer.Components.StoreRecentInvoices.StoreRecentInvoicesViewModel
<div class="widget store-recent-invoices" id="StoreRecentInvoices-@Model.Store.Id">
@@ -63,7 +65,7 @@
</span>
}
</td>
<td class="text-end">@invoice.AmountCurrency</td>
<td class="text-end">@DisplayFormatter.Currency(invoice.Amount, invoice.Currency)</td>
</tr>
}
</tbody>

View File

@@ -7,7 +7,8 @@ public class StoreRecentInvoiceViewModel
{
public string InvoiceId { get; set; }
public string OrderId { get; set; }
public string AmountCurrency { get; set; }
public decimal Amount { get; set; }
public string Currency { get; set; }
public InvoiceState Status { get; set; }
public DateTimeOffset Date { get; set; }
public bool HasRefund { get; set; }

View File

@@ -61,7 +61,8 @@ public class StoreRecentInvoices : ViewComponent
HasRefund = invoice.Refunds.Any(),
InvoiceId = invoice.Id,
OrderId = invoice.Metadata.OrderId ?? string.Empty,
AmountCurrency = _currencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
Amount = invoice.Price,
Currency = invoice.Currency
}).ToList();
return View(vm);

View File

@@ -1,4 +1,6 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Services
@inject DisplayFormatter DisplayFormatter
@model BTCPayServer.Components.StoreRecentTransactions.StoreRecentTransactionsViewModel
<div class="widget store-recent-transactions" id="StoreRecentTransactions-@Model.Store.Id">
@@ -49,11 +51,11 @@
</td>
@if (tx.Positive)
{
<td class="text-end text-success">@tx.Balance</td>
<td class="text-end text-success">@DisplayFormatter.Currency(tx.Balance, tx.Currency)</td>
}
else
{
<td class="text-end text-danger">@tx.Balance</td>
<td class="text-end text-danger">@DisplayFormatter.Currency(tx.Balance, tx.Currency)</td>
}
</tr>
}

View File

@@ -5,6 +5,7 @@ namespace BTCPayServer.Components.StoreRecentTransactions;
public class StoreRecentTransactionViewModel
{
public string Id { get; set; }
public string Currency { get; set; }
public string Balance { get; set; }
public bool Positive { get; set; }
public bool IsConfirmed { get; set; }

View File

@@ -58,6 +58,7 @@ public class StoreRecentTransactions : ViewComponent
Id = tx.TransactionId.ToString(),
Positive = tx.BalanceChange.GetValue(network) >= 0,
Balance = tx.BalanceChange.ShowMoney(network),
Currency = vm.CryptoCode,
IsConfirmed = tx.Confirmations != 0,
Link = string.Format(CultureInfo.InvariantCulture, network.BlockExplorerLink, tx.TransactionId.ToString()),
Timestamp = tx.SeenAt

View File

@@ -13,6 +13,7 @@ using BTCPayServer.Data;
using BTCPayServer.Filters;
using BTCPayServer.Models.CustodianAccountViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Custodian.Client;
using BTCPayServer.Services.Rates;
using Microsoft.AspNetCore.Authorization;
@@ -32,13 +33,13 @@ namespace BTCPayServer.Controllers
{
private readonly IEnumerable<ICustodian> _custodianRegistry;
private readonly CustodianAccountRepository _custodianAccountRepository;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly BTCPayServerClient _btcPayServerClient;
private readonly BTCPayNetworkProvider _networkProvider;
private readonly LinkGenerator _linkGenerator;
public UICustodianAccountsController(
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
UserManager<ApplicationUser> userManager,
CustodianAccountRepository custodianAccountRepository,
IEnumerable<ICustodian> custodianRegistry,
@@ -47,7 +48,7 @@ namespace BTCPayServer.Controllers
LinkGenerator linkGenerator
)
{
_currencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
_displayFormatter = displayFormatter;
_custodianAccountRepository = custodianAccountRepository;
_custodianRegistry = custodianRegistry;
_btcPayServerClient = btcPayServerClient;
@@ -144,7 +145,7 @@ namespace BTCPayServer.Controllers
if (asset.Equals(defaultCurrency))
{
assetBalance.FormattedFiatValue =
_currencyNameTable.DisplayFormatCurrency(pair.Value.Qty, defaultCurrency);
_displayFormatter.Currency(pair.Value.Qty, defaultCurrency);
assetBalance.FiatValue = pair.Value.Qty;
}
else
@@ -156,11 +157,11 @@ namespace BTCPayServer.Controllers
assetBalance.Bid = quote.Bid;
assetBalance.Ask = quote.Ask;
assetBalance.FormattedBid =
_currencyNameTable.DisplayFormatCurrency(quote.Bid, quote.FromAsset);
_displayFormatter.Currency(quote.Bid, quote.FromAsset);
assetBalance.FormattedAsk =
_currencyNameTable.DisplayFormatCurrency(quote.Ask, quote.FromAsset);
_displayFormatter.Currency(quote.Ask, quote.FromAsset);
assetBalance.FormattedFiatValue =
_currencyNameTable.DisplayFormatCurrency(pair.Value.Qty * quote.Bid,
_displayFormatter.Currency(pair.Value.Qty * quote.Bid,
defaultCurrency);
assetBalance.FiatValue = pair.Value.Qty * quote.Bid;
}

View File

@@ -20,6 +20,7 @@ using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Invoices.Export;
@@ -137,10 +138,10 @@ namespace BTCPayServer.Controllers
CreatedDate = invoice.InvoiceTime,
ExpirationDate = invoice.ExpirationTime,
MonitoringDate = invoice.MonitoringExpiration,
Fiat = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
Fiat = _displayFormatter.Currency(invoice.Price, invoice.Currency),
TaxIncluded = invoice.Metadata.TaxIncluded is null
? null
: _CurrencyNameTable.DisplayFormatCurrency(invoice.Metadata.TaxIncluded ?? 0.0m, invoice.Currency),
: _displayFormatter.Currency(invoice.Metadata.TaxIncluded ?? 0.0m, invoice.Currency),
NotificationUrl = invoice.NotificationURL?.AbsoluteUri,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri,
TypedMetadata = invoice.Metadata,
@@ -229,8 +230,8 @@ namespace BTCPayServer.Controllers
Amount = amount,
Paid = paid,
ReceivedDate = paymentEntity.ReceivedTime.DateTime,
PaidFormatted = _CurrencyNameTable.FormatCurrency(paid, i.Currency),
RateFormatted = _CurrencyNameTable.FormatCurrency(rate, i.Currency),
PaidFormatted = _displayFormatter.Currency(paid, i.Currency, DisplayFormatter.CurrencyFormat.Symbol),
RateFormatted = _displayFormatter.Currency(rate, i.Currency, DisplayFormatter.CurrencyFormat.Symbol),
PaymentMethod = paymentMethodId.ToPrettyString(),
Link = link,
Id = txId,
@@ -354,8 +355,7 @@ namespace BTCPayServer.Controllers
var cryptoPaid = paymentMethod.Calculate().Paid.ToDecimal(MoneyUnit.BTC);
var paidCurrency = Math.Round(cryptoPaid * paymentMethod.Rate, cdCurrency.Divisibility);
model.CryptoAmountThen = cryptoPaid.RoundToSignificant(paymentMethodDivisibility);
model.RateThenText =
_CurrencyNameTable.DisplayFormatCurrency(model.CryptoAmountThen, paymentMethodId.CryptoCode);
model.RateThenText = _displayFormatter.Currency(model.CryptoAmountThen, paymentMethodId.CryptoCode);
rules = store.GetStoreBlob().GetRateRules(_NetworkProvider);
rateResult = await _RateProvider.FetchRate(
new CurrencyPair(paymentMethodId.CryptoCode, invoice.Currency), rules,
@@ -369,13 +369,12 @@ namespace BTCPayServer.Controllers
}
model.CryptoAmountNow = Math.Round(paidCurrency / rateResult.BidAsk.Bid, paymentMethodDivisibility);
model.CurrentRateText =
_CurrencyNameTable.DisplayFormatCurrency(model.CryptoAmountNow, paymentMethodId.CryptoCode);
model.CurrentRateText = _displayFormatter.Currency(model.CryptoAmountNow, paymentMethodId.CryptoCode);
model.FiatAmount = paidCurrency;
}
model.CustomAmount = model.FiatAmount;
model.CustomCurrency = invoice.Currency;
model.FiatText = _CurrencyNameTable.DisplayFormatCurrency(model.FiatAmount, invoice.Currency);
model.FiatText = _displayFormatter.Currency(model.FiatAmount, invoice.Currency);
return View("_RefundModal", model);
case RefundSteps.SelectRate:
@@ -477,7 +476,6 @@ namespace BTCPayServer.Controllers
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
{
var overpaid = false;
var model = new InvoiceDetailsModel
{
@@ -500,15 +498,11 @@ namespace BTCPayServer.Controllers
{
PaymentMethodId = paymentMethodId,
PaymentMethod = paymentMethodId.ToPrettyString(),
Due = _CurrencyNameTable.DisplayFormatCurrency(accounting.Due.ToDecimal(MoneyUnit.BTC),
paymentMethodId.CryptoCode),
Paid = _CurrencyNameTable.DisplayFormatCurrency(
accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC),
paymentMethodId.CryptoCode),
Overpaid = _CurrencyNameTable.DisplayFormatCurrency(
overpaidAmount, paymentMethodId.CryptoCode),
Due = _displayFormatter.Currency(accounting.Due.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode),
Paid = _displayFormatter.Currency(accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode),
Overpaid = _displayFormatter.Currency(overpaidAmount, paymentMethodId.CryptoCode),
Address = data.GetPaymentMethodDetails().GetPaymentDestination(),
Rate = ExchangeRate(data),
Rate = ExchangeRate(data.GetId().CryptoCode, data),
PaymentMethodRaw = data
};
}).ToList()
@@ -794,10 +788,10 @@ namespace BTCPayServer.Controllers
CryptoImage = Request.GetRelativePathOrAbsolute(paymentMethodHandler.GetCryptoImage(paymentMethodId)),
BtcAddress = paymentMethodDetails.GetPaymentDestination(),
BtcDue = accounting.Due.ShowMoney(divisibility),
BtcPaid = accounting.Paid.ShowMoney(divisibility),
InvoiceCurrency = invoice.Currency,
OrderAmount = (accounting.TotalDue - accounting.NetworkFee).ShowMoney(divisibility),
IsUnsetTopUp = invoice.IsUnsetTopUp(),
OrderAmountFiat = OrderAmountFromInvoice(network.CryptoCode, invoice),
CustomerEmail = invoice.RefundMail,
RequiresRefundEmail = invoice.RequiresRefundEmail ?? storeBlob.RequiresRefundEmail,
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
@@ -805,7 +799,7 @@ namespace BTCPayServer.Controllers
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
MaxTimeMinutes = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
ItemDesc = invoice.Metadata.ItemDesc,
Rate = ExchangeRate(paymentMethod),
Rate = ExchangeRate(network.CryptoCode, paymentMethod, DisplayFormatter.CurrencyFormat.Symbol),
MerchantRefLink = invoice.RedirectURL?.AbsoluteUri ?? receiptUrl ?? "/",
ReceiptLink = receiptUrl,
RedirectAutomatically = invoice.RedirectAutomatically,
@@ -818,7 +812,6 @@ namespace BTCPayServer.Controllers
NetworkFeeMode.Never => 0,
_ => throw new NotImplementedException()
},
BtcPaid = accounting.Paid.ShowMoney(divisibility),
#pragma warning disable CS0618 // Type or member is obsolete
Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
@@ -865,23 +858,33 @@ namespace BTCPayServer.Controllers
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
model.PaymentMethodId = paymentMethodId.ToString();
model.PaymentType = paymentMethodId.PaymentType.ToString();
model.OrderAmountFiat = OrderAmountFromInvoice(model.CryptoCode, invoice, DisplayFormatter.CurrencyFormat.Symbol);
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
model.TimeLeft = expiration.PrettyPrint();
return model;
}
private string? OrderAmountFromInvoice(string cryptoCode, InvoiceEntity invoiceEntity)
private string? OrderAmountFromInvoice(string cryptoCode, InvoiceEntity invoiceEntity, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code)
{
var currency = invoiceEntity.Currency;
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
if (cryptoCode == invoiceEntity.Currency)
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
return null;
return _CurrencyNameTable.DisplayFormatCurrency(invoiceEntity.Price, invoiceEntity.Currency);
return _displayFormatter.Currency(invoiceEntity.Price, currency, format);
}
private string ExchangeRate(PaymentMethod paymentMethod)
private string? ExchangeRate(string cryptoCode, PaymentMethod paymentMethod, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code)
{
string currency = paymentMethod.ParentEntity.Currency;
return _CurrencyNameTable.DisplayFormatCurrency(paymentMethod.Rate, currency);
var currency = paymentMethod.ParentEntity.Currency;
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
return null;
return _displayFormatter.Currency(paymentMethod.Rate, currency, format);
}
[HttpGet("i/{invoiceId}/status")]
@@ -1004,7 +1007,8 @@ namespace BTCPayServer.Controllers
InvoiceId = invoice.Id,
OrderId = invoice.Metadata.OrderId ?? string.Empty,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri ?? string.Empty,
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
Amount = invoice.Price,
Currency = invoice.Currency,
CanMarkInvalid = state.CanMarkInvalid(),
CanMarkSettled = state.CanMarkComplete(),
Details = InvoicePopulatePayments(invoice),

View File

@@ -45,6 +45,7 @@ namespace BTCPayServer.Controllers
readonly StoreRepository _StoreRepository;
readonly UserManager<ApplicationUser> _UserManager;
private readonly CurrencyNameTable _CurrencyNameTable;
private readonly DisplayFormatter _displayFormatter;
readonly EventAggregator _EventAggregator;
readonly BTCPayNetworkProvider _NetworkProvider;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
@@ -61,6 +62,7 @@ namespace BTCPayServer.Controllers
public UIInvoiceController(
InvoiceRepository invoiceRepository,
WalletRepository walletRepository,
DisplayFormatter displayFormatter,
CurrencyNameTable currencyNameTable,
UserManager<ApplicationUser> userManager,
RateFetcher rateProvider,
@@ -78,6 +80,7 @@ namespace BTCPayServer.Controllers
InvoiceActivator invoiceActivator,
LinkGenerator linkGenerator)
{
_displayFormatter = displayFormatter;
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));

View File

@@ -14,6 +14,7 @@ using BTCPayServer.Forms.Models;
using BTCPayServer.Models;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.PaymentRequest;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.Rates;
@@ -37,6 +38,7 @@ namespace BTCPayServer.Controllers
private readonly PaymentRequestService _PaymentRequestService;
private readonly EventAggregator _EventAggregator;
private readonly CurrencyNameTable _Currencies;
private readonly DisplayFormatter _displayFormatter;
private readonly InvoiceRepository _InvoiceRepository;
private readonly StoreRepository _storeRepository;
@@ -50,6 +52,7 @@ namespace BTCPayServer.Controllers
PaymentRequestService paymentRequestService,
EventAggregator eventAggregator,
CurrencyNameTable currencies,
DisplayFormatter displayFormatter,
StoreRepository storeRepository,
InvoiceRepository invoiceRepository,
FormComponentProviders formProviders,
@@ -61,6 +64,7 @@ namespace BTCPayServer.Controllers
_PaymentRequestService = paymentRequestService;
_EventAggregator = eventAggregator;
_Currencies = currencies;
_displayFormatter = displayFormatter;
_storeRepository = storeRepository;
_InvoiceRepository = invoiceRepository;
FormProviders = formProviders;
@@ -89,7 +93,7 @@ namespace BTCPayServer.Controllers
var blob = data.GetBlob();
return new ViewPaymentRequestViewModel(data)
{
AmountFormatted = _Currencies.DisplayFormatCurrency(blob.Amount, blob.Currency)
AmountFormatted = _displayFormatter.Currency(blob.Amount, blob.Currency)
};
}).ToList();

View File

@@ -27,6 +27,7 @@ namespace BTCPayServer.Controllers
{
private readonly ApplicationDbContextFactory _dbContextFactory;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly PullPaymentHostedService _pullPaymentHostedService;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
@@ -34,6 +35,7 @@ namespace BTCPayServer.Controllers
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
PullPaymentHostedService pullPaymentHostedService,
BTCPayNetworkJsonSerializerSettings serializerSettings,
IEnumerable<IPayoutHandler> payoutHandlers,
@@ -41,6 +43,7 @@ namespace BTCPayServer.Controllers
{
_dbContextFactory = dbContextFactory;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
_pullPaymentHostedService = pullPaymentHostedService;
_serializerSettings = serializerSettings;
_payoutHandlers = payoutHandlers;
@@ -79,12 +82,9 @@ namespace BTCPayServer.Controllers
{
BrandColor = storeBlob.BrandColor,
CssFileId = storeBlob.CssFileId,
AmountFormatted = _currencyNameTable.FormatCurrency(blob.Limit, blob.Currency),
AmountCollected = totalPaid,
AmountCollectedFormatted = _currencyNameTable.FormatCurrency(totalPaid, blob.Currency),
AmountDue = amountDue,
ClaimedAmount = amountDue,
AmountDueFormatted = _currencyNameTable.FormatCurrency(amountDue, blob.Currency),
CurrencyData = cd,
StartDate = pp.StartDate,
LastRefreshed = DateTime.UtcNow,
@@ -93,7 +93,6 @@ namespace BTCPayServer.Controllers
{
Id = entity.Entity.Id,
Amount = entity.Blob.Amount,
AmountFormatted = _currencyNameTable.FormatCurrency(entity.Blob.Amount, blob.Currency),
Currency = blob.Currency,
Status = entity.Entity.State,
Destination = entity.Blob.Destination,
@@ -200,8 +199,8 @@ namespace BTCPayServer.Controllers
var amount = ppBlob.Currency == "SATS" ? new Money(vm.ClaimedAmount, MoneyUnit.Satoshi).ToUnit(MoneyUnit.BTC) : vm.ClaimedAmount;
if (destination.destination.Amount != null && amount != destination.destination.Amount)
{
var implied = _currencyNameTable.DisplayFormatCurrency(destination.destination.Amount.Value, paymentMethodId.CryptoCode);
var provided = _currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency);
var implied = _displayFormatter.Currency(destination.destination.Amount.Value, paymentMethodId.CryptoCode, DisplayFormatter.CurrencyFormat.Symbol);
var provided = _displayFormatter.Currency(vm.ClaimedAmount, ppBlob.Currency, DisplayFormatter.CurrencyFormat.Symbol);
ModelState.AddModelError(nameof(vm.ClaimedAmount),
$"Amount implied in destination ({implied}) does not match the payout amount provided ({provided}).");
}
@@ -235,7 +234,7 @@ namespace BTCPayServer.Controllers
TempData.SetStatusMessageModel(new StatusMessageModel
{
Message = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting {(result.PayoutData.State == PayoutState.AwaitingApproval ? "approval" : "payment")}.",
Message = $"Your claim request of {_displayFormatter.Currency(vm.ClaimedAmount, ppBlob.Currency, DisplayFormatter.CurrencyFormat.Symbol)} to {vm.Destination} has been submitted and is awaiting {(result.PayoutData.State == PayoutState.AwaitingApproval ? "approval" : "payment")}.",
Severity = StatusMessageModel.StatusSeverity.Success
});

View File

@@ -34,6 +34,7 @@ namespace BTCPayServer.Controllers
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly PullPaymentHostedService _pullPaymentService;
private readonly ApplicationDbContextFactory _dbContextFactory;
private readonly BTCPayNetworkJsonSerializerSettings _jsonSerializerSettings;
@@ -49,6 +50,7 @@ namespace BTCPayServer.Controllers
public UIStorePullPaymentsController(BTCPayNetworkProvider btcPayNetworkProvider,
IEnumerable<IPayoutHandler> payoutHandlers,
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
PullPaymentHostedService pullPaymentHostedService,
ApplicationDbContextFactory dbContextFactory,
BTCPayNetworkJsonSerializerSettings jsonSerializerSettings)
@@ -56,6 +58,7 @@ namespace BTCPayServer.Controllers
_btcPayNetworkProvider = btcPayNetworkProvider;
_payoutHandlers = payoutHandlers;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
_pullPaymentService = pullPaymentHostedService;
_dbContextFactory = dbContextFactory;
_jsonSerializerSettings = jsonSerializerSettings;
@@ -532,7 +535,7 @@ namespace BTCPayServer.Controllers
PullPaymentName = ppBlob?.Name ?? item.PullPayment?.Id,
Date = item.Payout.Date,
PayoutId = item.Payout.Id,
Amount = _currencyNameTable.DisplayFormatCurrency(payoutBlob.Amount, ppBlob?.Currency ?? PaymentMethodId.Parse(item.Payout.PaymentMethodId).CryptoCode),
Amount = _displayFormatter.Currency(payoutBlob.Amount, ppBlob?.Currency ?? PaymentMethodId.Parse(item.Payout.PaymentMethodId).CryptoCode),
Destination = payoutBlob.Destination
};
var handler = _payoutHandlers

View File

@@ -251,6 +251,7 @@ namespace BTCPayServer.HostedServices
IEnumerable<IPayoutHandler> payoutHandlers,
ILogger<PullPaymentHostedService> logger,
Logs logs,
DisplayFormatter displayFormatter,
CurrencyNameTable currencyNameTable) : base(logs)
{
_dbContextFactory = dbContextFactory;
@@ -262,6 +263,7 @@ namespace BTCPayServer.HostedServices
_payoutHandlers = payoutHandlers;
_logger = logger;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
}
Channel<object> _Channel;
@@ -274,6 +276,7 @@ namespace BTCPayServer.HostedServices
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly ILogger<PullPaymentHostedService> _logger;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly CompositeDisposable _subscriptions = new CompositeDisposable();
internal override Task[] InitializeTasks()
@@ -754,7 +757,7 @@ namespace BTCPayServer.HostedServices
Completed = totalCompleted,
CompletedFormatted = totalCompleted.ToString("C", nfi),
Limit = ppBlob.Limit.RoundToSignificant(currencyData.Divisibility),
LimitFormatted = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
LimitFormatted = _displayFormatter.Currency(ppBlob.Limit, ppBlob.Currency),
ResetIn = period?.End is { } nr ? ZeroIfNegative(nr - now).TimeString() : null,
EndIn = pp.EndDate is { } end ? ZeroIfNegative(end - now).TimeString() : null,
};

View File

@@ -280,6 +280,7 @@ namespace BTCPayServer.Hosting
services.AddTransient<PluginService>();
services.AddSingleton<IPluginHookService, PluginHookService>();
services.TryAddTransient<Safe>();
services.TryAddTransient<DisplayFormatter>();
services.TryAddSingleton<Ganss.XSS.HtmlSanitizer>(o =>
{

View File

@@ -28,7 +28,8 @@ namespace BTCPayServer.Models.InvoicingModels
public bool CanMarkStatus => CanMarkSettled || CanMarkInvalid;
public bool ShowCheckout { get; set; }
public string ExceptionStatus { get; set; }
public string AmountCurrency { get; set; }
public decimal Amount { get; set; }
public string Currency { get; set; }
public InvoiceDetailsModel Details { get; set; }
public bool HasRefund { get; set; }

View File

@@ -82,7 +82,6 @@ namespace BTCPayServer.Models
public decimal ClaimedAmount { get; set; }
public decimal MinimumClaim { get; set; }
public string Destination { get; set; }
public string AmountDueFormatted { get; set; }
public decimal Amount { get; set; }
public string Id { get; set; }
public string Currency { get; set; }
@@ -97,8 +96,6 @@ namespace BTCPayServer.Models
public DateTimeOffset StartDate { get; set; }
public DateTime LastRefreshed { get; set; }
public CurrencyData CurrencyData { get; set; }
public string AmountCollectedFormatted { get; set; }
public string AmountFormatted { get; set; }
public bool Archived { get; set; }
public bool AutoApprove { get; set; }

View File

@@ -5,6 +5,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.PaymentRequests;
@@ -20,17 +21,20 @@ namespace BTCPayServer.PaymentRequest
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
private readonly AppService _AppService;
private readonly CurrencyNameTable _currencies;
private readonly DisplayFormatter _displayFormatter;
public PaymentRequestService(
PaymentRequestRepository paymentRequestRepository,
BTCPayNetworkProvider btcPayNetworkProvider,
AppService appService,
DisplayFormatter displayFormatter,
CurrencyNameTable currencies)
{
_PaymentRequestRepository = paymentRequestRepository;
_BtcPayNetworkProvider = btcPayNetworkProvider;
_AppService = appService;
_currencies = currencies;
_displayFormatter = displayFormatter;
}
public async Task UpdatePaymentRequestStateIfNeeded(string id)
@@ -90,11 +94,11 @@ namespace BTCPayServer.PaymentRequest
return new ViewPaymentRequestViewModel(pr)
{
Archived = pr.Archived,
AmountFormatted = _currencies.FormatCurrency(blob.Amount, blob.Currency),
AmountFormatted = _displayFormatter.Currency(blob.Amount, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
AmountCollected = paymentStats.TotalCurrency,
AmountCollectedFormatted = _currencies.FormatCurrency(paymentStats.TotalCurrency, blob.Currency),
AmountCollectedFormatted = _displayFormatter.Currency(paymentStats.TotalCurrency, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
AmountDue = amountDue,
AmountDueFormatted = _currencies.FormatCurrency(amountDue, blob.Currency),
AmountDueFormatted = _displayFormatter.Currency(amountDue, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
CurrencyData = _currencies.GetCurrencyData(blob.Currency, true),
LastUpdated = DateTime.UtcNow,
FormId = blob.FormId,
@@ -128,8 +132,8 @@ namespace BTCPayServer.PaymentRequest
Amount = amount,
Paid = paid,
ReceivedDate = paymentEntity.ReceivedTime.DateTime,
PaidFormatted = _currencies.FormatCurrency(paid, blob.Currency),
RateFormatted = _currencies.FormatCurrency(rate, blob.Currency),
PaidFormatted = _displayFormatter.Currency(paid, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
RateFormatted = _displayFormatter.Currency(rate, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
PaymentMethod = paymentMethodId.ToPrettyString(),
Link = link,
Id = txId,
@@ -147,7 +151,7 @@ namespace BTCPayServer.PaymentRequest
{
Id = entity.Id,
Amount = entity.Price,
AmountFormatted = _currencies.FormatCurrency(entity.Price, blob.Currency),
AmountFormatted = _displayFormatter.Currency(entity.Price, blob.Currency, DisplayFormatter.CurrencyFormat.Symbol),
Currency = entity.Currency,
ExpiryDate = entity.ExpirationTime.DateTime,
State = state,

View File

@@ -10,7 +10,6 @@ using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBXplorer.Models;
@@ -24,14 +23,14 @@ namespace BTCPayServer.Payments.Bitcoin
private readonly BTCPayNetworkProvider _networkProvider;
private readonly IFeeProviderFactory _FeeRateProviderFactory;
private readonly NBXplorerDashboard _dashboard;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly Services.Wallets.BTCPayWalletProvider _WalletProvider;
private readonly Dictionary<string, string> _bech32Prefix;
public BitcoinLikePaymentHandler(ExplorerClientProvider provider,
BTCPayNetworkProvider networkProvider,
IFeeProviderFactory feeRateProviderFactory,
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
NBXplorerDashboard dashboard,
Services.Wallets.BTCPayWalletProvider walletProvider)
{
@@ -40,7 +39,7 @@ namespace BTCPayServer.Payments.Bitcoin
_FeeRateProviderFactory = feeRateProviderFactory;
_dashboard = dashboard;
_WalletProvider = walletProvider;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
_bech32Prefix = networkProvider.GetAll().OfType<BTCPayNetwork>()
.Where(network => network.NBitcoinNetwork?.Consensus?.SupportSegwit is true).ToDictionary(network => network.CryptoCode,
@@ -144,7 +143,7 @@ namespace BTCPayServer.Payments.Bitcoin
if (model.Activated && amountInSats)
{
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);
}
}

View File

@@ -6,6 +6,7 @@ using BTCPayServer.Data;
using BTCPayServer.Logging;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using NBitcoin;
@@ -100,7 +101,7 @@ namespace BTCPayServer.Payments
return null;
}
public virtual void PreparePaymentModelForAmountInSats(PaymentModel model, IPaymentMethod paymentMethod, CurrencyNameTable currencyNameTable)
public virtual void PreparePaymentModelForAmountInSats(PaymentModel model, IPaymentMethod paymentMethod, DisplayFormatter displayFormatter)
{
var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name)
{
@@ -111,7 +112,9 @@ namespace BTCPayServer.Payments
model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.NetworkFee = new Money(model.NetworkFee, MoneyUnit.BTC).ToUnit(MoneyUnit.Satoshi);
model.Rate = currencyNameTable.DisplayFormatCurrency(paymentMethod.Rate / 100_000_000, model.InvoiceCurrency);
model.Rate = model.InvoiceCurrency is "BTC" or "SATS"
? null
: displayFormatter.Currency(paymentMethod.Rate / 100_000_000, model.InvoiceCurrency, DisplayFormatter.CurrencyFormat.Symbol);
}
public Task<IPaymentMethodDetails> CreatePaymentMethodDetails(InvoiceLogs logs,

View File

@@ -9,8 +9,8 @@ using BTCPayServer.Lightning;
using BTCPayServer.Logging;
using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using Microsoft.Extensions.Options;
namespace BTCPayServer.Payments.Lightning
@@ -18,17 +18,17 @@ namespace BTCPayServer.Payments.Lightning
public class LNURLPayPaymentHandler : PaymentMethodHandlerBase<LNURLPaySupportedPaymentMethod, BTCPayNetwork>
{
private readonly BTCPayNetworkProvider _networkProvider;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
private readonly LightningLikePaymentHandler _lightningLikePaymentHandler;
public LNURLPayPaymentHandler(
BTCPayNetworkProvider networkProvider,
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
IOptions<LightningNetworkOptions> options,
LightningLikePaymentHandler lightningLikePaymentHandler)
{
_networkProvider = networkProvider;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
_lightningLikePaymentHandler = lightningLikePaymentHandler;
Options = options;
}
@@ -115,7 +115,7 @@ namespace BTCPayServer.Payments.Lightning
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);
}
}

View File

@@ -14,7 +14,6 @@ using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using Microsoft.Extensions.Options;
using NBitcoin;
@@ -27,21 +26,21 @@ namespace BTCPayServer.Payments.Lightning
private readonly LightningClientFactoryService _lightningClientFactory;
private readonly BTCPayNetworkProvider _networkProvider;
private readonly SocketFactory _socketFactory;
private readonly CurrencyNameTable _currencyNameTable;
private readonly DisplayFormatter _displayFormatter;
public LightningLikePaymentHandler(
NBXplorerDashboard dashboard,
LightningClientFactoryService lightningClientFactory,
BTCPayNetworkProvider networkProvider,
SocketFactory socketFactory,
CurrencyNameTable currencyNameTable,
DisplayFormatter displayFormatter,
IOptions<LightningNetworkOptions> options)
{
_Dashboard = dashboard;
_lightningClientFactory = lightningClientFactory;
_networkProvider = networkProvider;
_socketFactory = socketFactory;
_currencyNameTable = currencyNameTable;
_displayFormatter = displayFormatter;
Options = options;
}
@@ -221,7 +220,7 @@ namespace BTCPayServer.Payments.Lightning
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);
}
}
public override string GetCryptoImage(PaymentMethodId paymentMethodId)

View File

@@ -35,12 +35,14 @@ namespace BTCPayServer.Services.Apps
readonly ApplicationDbContextFactory _ContextFactory;
private readonly InvoiceRepository _InvoiceRepository;
readonly CurrencyNameTable _Currencies;
private readonly DisplayFormatter _displayFormatter;
private readonly StoreRepository _storeRepository;
private readonly HtmlSanitizer _HtmlSanitizer;
public CurrencyNameTable Currencies => _Currencies;
public AppService(ApplicationDbContextFactory contextFactory,
InvoiceRepository invoiceRepository,
CurrencyNameTable currencies,
DisplayFormatter displayFormatter,
StoreRepository storeRepository,
HtmlSanitizer htmlSanitizer)
{
@@ -49,6 +51,7 @@ namespace BTCPayServer.Services.Apps
_Currencies = currencies;
_storeRepository = storeRepository;
_HtmlSanitizer = htmlSanitizer;
_displayFormatter = displayFormatter;
}
public async Task<object> GetAppInfo(string appId)
@@ -599,7 +602,7 @@ namespace BTCPayServer.Services.Apps
if (pValue != null)
{
price.Value = decimal.Parse(pValue.Value.Value, CultureInfo.InvariantCulture);
price.Formatted = Currencies.FormatCurrency(pValue.Value.Value, currency);
price.Formatted = _displayFormatter.Currency(price.Value.Value, currency, DisplayFormatter.CurrencyFormat.Symbol);
}
break;
case "fixed":
@@ -607,7 +610,7 @@ namespace BTCPayServer.Services.Apps
case null:
price.Type = ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed;
price.Value = decimal.Parse(pValue.Value.Value, CultureInfo.InvariantCulture);
price.Formatted = Currencies.FormatCurrency(pValue.Value.Value, currency);
price.Formatted = _displayFormatter.Currency(price.Value.Value, currency, DisplayFormatter.CurrencyFormat.Symbol);
break;
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Globalization;
using BTCPayServer.Rating;
using BTCPayServer.Services.Rates;
namespace BTCPayServer.Services;
public class DisplayFormatter
{
private readonly CurrencyNameTable _currencyNameTable;
public DisplayFormatter(CurrencyNameTable currencyNameTable)
{
_currencyNameTable = currencyNameTable;
}
public enum CurrencyFormat
{
Code,
Symbol,
CodeAndSymbol
}
/// <summary>
/// Format a currency, rounded to significant divisibility
/// </summary>
/// <param name="value">The value</param>
/// <param name="currency">Currency code</param>
/// <param name="format">The format, defaults to amount + code, e.g. 1.234,56 USD</param>
/// <returns>Formatted amount and currency string</returns>
public string Currency(decimal value, string currency, CurrencyFormat format = CurrencyFormat.Code)
{
var provider = _currencyNameTable.GetNumberFormatInfo(currency, true);
var currencyData = _currencyNameTable.GetCurrencyData(currency, true);
var divisibility = currencyData.Divisibility;
value = value.RoundToSignificant(ref divisibility);
if (divisibility != provider.CurrencyDecimalDigits)
{
provider = (NumberFormatInfo)provider.Clone();
provider.CurrencyDecimalDigits = divisibility;
}
var formatted = value.ToString("C", provider);
return format switch
{
CurrencyFormat.Code => $"{formatted.Replace(provider.CurrencySymbol, "").Trim()} {currency}",
CurrencyFormat.Symbol => formatted,
CurrencyFormat.CodeAndSymbol => $"{formatted} ({currency})",
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
};
}
public string Currency(string value, string currency, CurrencyFormat format = CurrencyFormat.Code)
{
return Currency(decimal.Parse(value, CultureInfo.InvariantCulture), currency, format);
}
}

View File

@@ -1,6 +1,8 @@
@using System.Globalization
@using BTCPayServer.Payments
@using BTCPayServer.Payments.Bitcoin
@using BTCPayServer.Services
@inject DisplayFormatter DisplayFormatter
@model IEnumerable<BTCPayServer.Services.Invoices.PaymentEntity>
@{
@@ -78,7 +80,7 @@
<td>@(payment.CryptoPaymentData.KeyPath?.ToString()?? "Unknown")</td>
<td style="max-width:300px;" data-bs-toggle="tooltip" class="text-truncate" title="@payment.DepositAddress">@payment.DepositAddress</td>
<td class="payment-value">
@payment.CryptoPaymentData.GetValue()
@DisplayFormatter.Currency(payment.CryptoPaymentData.GetValue(), payment.Crypto)
@if (!string.IsNullOrEmpty(payment.AdditionalInformation))
{
<div>(@payment.AdditionalInformation)</div>

View File

@@ -183,37 +183,37 @@
</noscript>
<script type="text/x-template" id="payment-details">
<dl>
<div v-if="orderAmount > 0">
<div v-if="orderAmount > 0" id="PaymentDetails-TotalPrice">
<dt v-t="'total_price'"></dt>
<dd :data-clipboard="srvModel.orderAmount" :data-clipboard-confirm="$t('copy_confirm')">{{srvModel.orderAmount}} {{ srvModel.cryptoCode }}</dd>
</div>
<div v-if="orderAmount > 0 && srvModel.orderAmountFiat">
<div v-if="orderAmount > 0 && srvModel.orderAmountFiat" id="PaymentDetails-TotalFiat">
<dt v-t="'total_fiat'"></dt>
<dd :data-clipboard="srvModel.orderAmountFiat" :data-clipboard-confirm="$t('copy_confirm')">{{srvModel.orderAmountFiat}}</dd>
</div>
<div v-if="srvModel.rate && srvModel.cryptoCode">
<div v-if="srvModel.rate && srvModel.cryptoCode" id="PaymentDetails-ExchangeRate">
<dt v-t="'exchange_rate'"></dt>
<dd :data-clipboard="srvModel.rate" :data-clipboard-confirm="$t('copy_confirm')">
<template v-if="srvModel.cryptoCode === 'sats'">1 sat = {{ srvModel.rate }}</template>
<template v-else>1 {{ srvModel.cryptoCode }} = {{ srvModel.rate }}</template>
</dd>
</div>
<div v-if="srvModel.networkFee">
<div v-if="srvModel.networkFee" id="PaymentDetails-NetworkCost">
<dt v-t="'network_cost'"></dt>
<dd :data-clipboard="srvModel.networkFee" :data-clipboard-confirm="$t('copy_confirm')">
<div v-if="srvModel.txCountForFee > 0" v-t="{ path: 'tx_count', args: { count: srvModel.txCount } }"></div>
<div v-text="`${srvModel.networkFee} ${srvModel.cryptoCode}`"></div>
</dd>
</div>
<div v-if="btcPaid > 0">
<div v-if="btcPaid > 0" id="PaymentDetails-AmountPaid">
<dt v-t="'amount_paid'"></dt>
<dd :data-clipboard="srvModel.btcPaid" :data-clipboard-confirm="$t('copy_confirm')" v-text="`${srvModel.btcPaid} ${srvModel.cryptoCode}`"></dd>
</div>
<div v-if="btcDue > 0">
<div v-if="btcDue > 0" id="PaymentDetails-AmountDue">
<dt v-t="'amount_due'"></dt>
<dd :data-clipboard="srvModel.btcDue" :data-clipboard-confirm="$t('copy_confirm')" v-text="`${srvModel.btcDue} ${srvModel.cryptoCode}`"></dd>
</div>
<div v-if="showRecommendedFee">
<div v-if="showRecommendedFee" id="PaymentDetails-RecommendedFee">
<dt v-t="'recommended_fee'"></dt>
<dd :data-clipboard="srvModel.feeRate" :data-clipboard-confirm="$t('copy_confirm')" v-t="{ path: 'fee_rate', args: { feeRate: srvModel.feeRate } }"></dd>
</div>

View File

@@ -1,10 +1,12 @@
@model BTCPayServer.Models.InvoicingModels.InvoiceReceiptViewModel
@using BTCPayServer.Client
@using BTCPayServer.Client.Models
@using BTCPayServer.Services.Rates
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayServer.Services.ThemeSettings Theme
@inject CurrencyNameTable CurrencyNameTable
@using BTCPayServer.Components.QRCode
@using BTCPayServer.Services
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.TagHelpers
@inject BTCPayServerEnvironment Env
@inject DisplayFormatter DisplayFormatter
@{
Layout = null;
ViewData["Title"] = $"Receipt from {Model.StoreName}";
@@ -66,7 +68,7 @@
</button>
<dd class="text-muted mb-0 fw-semibold">Amount Paid</dd>
</div>
<dt class="fs-2 mb-0 text-nowrap fw-semibold">@CurrencyNameTable.DisplayFormatCurrency(Model.Amount, Model.Currency)</dt>
<dt class="fs-2 mb-0 text-nowrap fw-semibold">@DisplayFormatter.Currency(Model.Amount, Model.Currency, DisplayFormatter.CurrencyFormat.Symbol)</dt>
</div>
<div class="d-flex flex-column">
<dd class="text-muted mb-0 fw-semibold">Date</dd>

View File

@@ -1,5 +1,7 @@
@using BTCPayServer.Client
@using BTCPayServer.Client.Models
@using BTCPayServer.Services
@inject DisplayFormatter DisplayFormatter
@model InvoicesModel
@{
ViewData.SetActivePage(InvoiceNavPages.Index, "Invoices");
@@ -371,7 +373,7 @@
<span class="badge bg-warning">Refund</span>
}
</td>
<td class="text-end text-nowrap">@invoice.AmountCurrency</td>
<td class="text-end text-nowrap">@DisplayFormatter.Currency(invoice.Amount, invoice.Currency)</td>
<td class="text-end text-nowrap">
@if (invoice.ShowCheckout)
{

View File

@@ -1,11 +1,13 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@using BTCPayServer.Client
@using BTCPayServer.Components.ThemeSwitch
@using BTCPayServer.Payments
@model BTCPayServer.Models.ViewPullPaymentModel
@using BTCPayServer.Services
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.TagHelpers
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
@inject BTCPayServerEnvironment Env
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@inject DisplayFormatter DisplayFormatter
@model BTCPayServer.Models.ViewPullPaymentModel
@{
ViewData["Title"] = Model.Title;
Csp.UnsafeEval();
@@ -141,18 +143,18 @@
<h2 class="h4 mb-3">Payment Details</h2>
<dl class="mb-0 mt-md-4">
<div class="d-flex d-print-inline-block flex-column mb-4 me-5">
<dt class="h4 fw-semibold text-nowrap text-primary text-print-default order-2 order-sm-1 mb-1">@Model.AmountDueFormatted</dt>
<dt class="h4 fw-semibold text-nowrap text-primary text-print-default order-2 order-sm-1 mb-1">@DisplayFormatter.Currency(Model.AmountDue, Model.Currency)</dt>
<dd class="order-1 order-sm-2 mb-1">Available claim</dd>
</div>
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected / Model.Amount) * 100)%"></div>
</div>
<div class="d-flex d-print-inline-block flex-column mb-4 me-5 d-sm-inline-flex mb-sm-0">
<dt class="h4 fw-semibold text-nowrap order-2 order-sm-1 mb-1">@Model.AmountCollectedFormatted</dt>
<dt class="h4 fw-semibold text-nowrap order-2 order-sm-1 mb-1">@DisplayFormatter.Currency(Model.AmountCollected, Model.Currency)</dt>
<dd class="order-1 order-sm-2 mb-1">Already claimed</dd>
</div>
<div class="d-flex d-print-inline-block flex-column mb-0 d-sm-inline-flex float-sm-end">
<dt class="h4 text-sm-end fw-semibold text-nowrap order-2 order-sm-1 mb-1">@Model.AmountFormatted</dt>
<dt class="h4 text-sm-end fw-semibold text-nowrap order-2 order-sm-1 mb-1">@DisplayFormatter.Currency(Model.Amount, Model.Currency)</dt>
<dd class="text-sm-end order-1 order-sm-2 mb-1">Claim limit</dd>
</div>
</dl>