mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
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:
parent
f3d9e07c5e
commit
ded0c8a3bc
33 changed files with 269 additions and 152 deletions
|
@ -5,7 +5,6 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using BTCPayServer.Rating;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
@ -28,14 +27,6 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new Dictionary<string, IFormatProvider>();
|
||||
public string FormatCurrency(string price, string currency)
|
||||
{
|
||||
return FormatCurrency(decimal.Parse(price, CultureInfo.InvariantCulture), currency);
|
||||
}
|
||||
public string FormatCurrency(decimal price, string currency)
|
||||
{
|
||||
return price.ToString("C", GetCurrencyProvider(currency));
|
||||
}
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency, bool useFallback)
|
||||
{
|
||||
|
@ -56,6 +47,7 @@ namespace BTCPayServer.Services.Rates
|
|||
currencyInfo.CurrencySymbol = currency;
|
||||
return currencyInfo;
|
||||
}
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency)
|
||||
{
|
||||
var curr = GetCurrencyProvider(currency);
|
||||
|
@ -65,6 +57,7 @@ namespace BTCPayServer.Services.Rates
|
|||
return ni;
|
||||
return null;
|
||||
}
|
||||
|
||||
public IFormatProvider GetCurrencyProvider(string currency)
|
||||
{
|
||||
lock (_CurrencyProviders)
|
||||
|
@ -104,30 +97,6 @@ namespace BTCPayServer.Services.Rates
|
|||
currencyProviders.TryAdd(code, number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format a currency like "0.004 $ (USD)", round to significant divisibility
|
||||
/// </summary>
|
||||
/// <param name="value">The value</param>
|
||||
/// <param name="currency">Currency code</param>
|
||||
/// <returns></returns>
|
||||
public string DisplayFormatCurrency(decimal value, string currency)
|
||||
{
|
||||
var provider = GetNumberFormatInfo(currency, true);
|
||||
var currencyData = GetCurrencyData(currency, true);
|
||||
var divisibility = currencyData.Divisibility;
|
||||
value = value.RoundToSignificant(ref divisibility);
|
||||
if (divisibility != provider.CurrencyDecimalDigits)
|
||||
{
|
||||
provider = (NumberFormatInfo)provider.Clone();
|
||||
provider.CurrencyDecimalDigits = divisibility;
|
||||
}
|
||||
|
||||
if (currencyData.Crypto)
|
||||
return value.ToString("C", provider);
|
||||
else
|
||||
return value.ToString("C", provider) + $" ({currency})";
|
||||
}
|
||||
|
||||
readonly Dictionary<string, CurrencyData> _Currencies;
|
||||
|
||||
static CurrencyData[] LoadCurrency()
|
||||
|
|
|
@ -396,21 +396,21 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
|
||||
s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1));
|
||||
Assert.Contains("$5,500.00", s.Driver.PageSource); // Should propose reimburse in fiat
|
||||
Assert.Contains("1.10000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before
|
||||
Assert.Contains("2.20000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate
|
||||
Assert.Contains("5,500.00 USD", s.Driver.PageSource); // Should propose reimburse in fiat
|
||||
Assert.Contains("1.10000000 BTC", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before
|
||||
Assert.Contains("2.20000000 BTC", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate
|
||||
s.Driver.WaitForAndClick(By.Id(rateSelection));
|
||||
s.Driver.FindElement(By.Id("ok")).Click();
|
||||
|
||||
|
||||
s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
|
||||
Assert.Contains("pull-payments", s.Driver.Url);
|
||||
if (rateSelection == "FiatOption")
|
||||
Assert.Contains("$5,500.00", s.Driver.PageSource);
|
||||
Assert.Contains("5,500.00 USD", s.Driver.PageSource);
|
||||
if (rateSelection == "CurrentOption")
|
||||
Assert.Contains("2.20000000 ₿", s.Driver.PageSource);
|
||||
Assert.Contains("2.20000000 BTC", s.Driver.PageSource);
|
||||
if (rateSelection == "RateThenOption")
|
||||
Assert.Contains("1.10000000 ₿", s.Driver.PageSource);
|
||||
|
||||
Assert.Contains("1.10000000 BTC", s.Driver.PageSource);
|
||||
|
||||
s.GoToInvoice(invoice.Id);
|
||||
s.Driver.FindElement(By.Id("IssueRefund")).Click();
|
||||
s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
|
||||
|
|
|
@ -73,6 +73,14 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PayByLNURL"));
|
||||
|
||||
// Details should show exchange rate
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue"));
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text);
|
||||
|
||||
// Switch to LNURL
|
||||
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
|
||||
TestUtils.Eventually(() =>
|
||||
|
@ -86,7 +94,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
// Default payment method
|
||||
s.GoToHome();
|
||||
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||
invoiceId = s.CreateInvoice(21000, "SATS", defaultPaymentMethod: "BTC_LightningLike");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count);
|
||||
|
@ -112,6 +120,14 @@ namespace BTCPayServer.Tests
|
|||
s.GoToInvoiceCheckout(invoiceId);
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||
|
||||
// Details should not show exchange rate
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-RecommendedFee"));
|
||||
Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||
Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||
|
||||
// Expire
|
||||
var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
||||
|
@ -145,6 +161,10 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("Exchange Rate", details.Text);
|
||||
Assert.Contains("Amount Due", details.Text);
|
||||
Assert.Contains("Recommended Fee", details.Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||
|
||||
// Pay partial amount
|
||||
await Task.Delay(200);
|
||||
|
@ -218,6 +238,14 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("&lightning=LNBCRT", qrValue);
|
||||
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||
|
||||
// Switch to amount displayed in sats
|
||||
s.GoToHome();
|
||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||
|
@ -227,7 +255,15 @@ namespace BTCPayServer.Tests
|
|||
s.GoToInvoiceCheckout(invoiceId);
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||
|
||||
// BIP21 with LN as default payment method
|
||||
s.GoToHome();
|
||||
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||
|
@ -238,6 +274,14 @@ namespace BTCPayServer.Tests
|
|||
Assert.StartsWith("bitcoin:", payUrl);
|
||||
Assert.Contains("&lightning=lnbcrt", payUrl);
|
||||
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||
|
||||
// Ensure LNURL is enabled
|
||||
s.GoToHome();
|
||||
|
@ -263,6 +307,14 @@ namespace BTCPayServer.Tests
|
|||
Assert.StartsWith("lnurl", copyAddressLightning);
|
||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue"));
|
||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice"));
|
||||
|
||||
// Expiry message should not show amount for top-up invoice
|
||||
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
public FastTests(ITestOutputHelper helper) : base(helper)
|
||||
{
|
||||
|
||||
}
|
||||
class DockerImage
|
||||
{
|
||||
|
@ -600,15 +599,16 @@ namespace BTCPayServer.Tests
|
|||
[Fact]
|
||||
public void RoundupCurrenciesCorrectly()
|
||||
{
|
||||
DisplayFormatter displayFormatter = new (CurrencyNameTable.Instance);
|
||||
foreach (var test in new[]
|
||||
{
|
||||
(0.0005m, "$0.0005 (USD)", "USD"), (0.001m, "$0.001 (USD)", "USD"), (0.01m, "$0.01 (USD)", "USD"),
|
||||
(0.1m, "$0.10 (USD)", "USD"), (0.1m, "0,10 € (EUR)", "EUR"), (1000m, "¥1,000 (JPY)", "JPY"),
|
||||
(1000.0001m, "₹ 1,000.00 (INR)", "INR"),
|
||||
(0.0m, "$0.00 (USD)", "USD")
|
||||
(0.0005m, "0.0005 USD", "USD"), (0.001m, "0.001 USD", "USD"), (0.01m, "0.01 USD", "USD"),
|
||||
(0.1m, "0.10 USD", "USD"), (0.1m, "0,10 EUR", "EUR"), (1000m, "1,000 JPY", "JPY"),
|
||||
(1000.0001m, "1,000.00 INR", "INR"),
|
||||
(0.0m, "0.00 USD", "USD")
|
||||
})
|
||||
{
|
||||
var actual = CurrencyNameTable.Instance.DisplayFormatCurrency(test.Item1, test.Item3);
|
||||
var actual = displayFormatter.Currency(test.Item1, test.Item3);
|
||||
actual = actual.Replace("¥", "¥"); // Hack so JPY test pass on linux as well
|
||||
Assert.Equal(test.Item2, actual);
|
||||
}
|
||||
|
|
|
@ -578,8 +578,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.DoesNotContain("invoice-processing", s.Driver.PageSource);
|
||||
});
|
||||
|
||||
Assert.Contains(s.Server.PayTester.GetService<CurrencyNameTable>().DisplayFormatCurrency(100, "USD"),
|
||||
s.Driver.PageSource);
|
||||
Assert.Contains("100.00 USD", s.Driver.PageSource);
|
||||
Assert.Contains(i, s.Driver.PageSource);
|
||||
|
||||
s.GoToInvoices(s.StoreId);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 =>
|
||||
{
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
57
BTCPayServer/Services/DisplayFormatter.cs
Normal file
57
BTCPayServer/Services/DisplayFormatter.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue