mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Improve invoice page with currencies information
This commit is contained in:
@@ -25,46 +25,22 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
public partial class InvoiceController
|
||||
{
|
||||
[HttpPost]
|
||||
[Route("invoices/{invoiceId}")]
|
||||
public IActionResult Invoice(string invoiceId, string command, string cryptoCode = null)
|
||||
{
|
||||
if (command == "refresh")
|
||||
{
|
||||
_EventAggregator.Publish(new Events.InvoiceCreatedEvent(invoiceId));
|
||||
}
|
||||
StatusMessage = "Invoice is state is being refreshed, please refresh the page soon...";
|
||||
return RedirectToAction(nameof(Invoice), new
|
||||
{
|
||||
invoiceId = invoiceId
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("invoices/{invoiceId}")]
|
||||
public async Task<IActionResult> Invoice(string invoiceId, string cryptoCode = null)
|
||||
public async Task<IActionResult> Invoice(string invoiceId)
|
||||
{
|
||||
if (cryptoCode == null)
|
||||
cryptoCode = "BTC";
|
||||
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
UserId = GetUserId(),
|
||||
InvoiceId = invoiceId
|
||||
InvoiceId = invoiceId,
|
||||
IncludeAddresses = true
|
||||
})).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return NotFound();
|
||||
|
||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||
if (network == null || !invoice.Support(network))
|
||||
return NotFound();
|
||||
|
||||
var cryptoData = invoice.GetCryptoData(network);
|
||||
|
||||
var dto = invoice.EntityToDTO(_NetworkProvider);
|
||||
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(cryptoCode, StringComparison.OrdinalIgnoreCase));
|
||||
var store = await _StoreRepository.FindStore(invoice.StoreId);
|
||||
|
||||
var accounting = cryptoData.Calculate();
|
||||
InvoiceDetailsModel model = new InvoiceDetailsModel()
|
||||
{
|
||||
StoreName = store.StoreName,
|
||||
@@ -74,34 +50,46 @@ namespace BTCPayServer.Controllers
|
||||
RefundEmail = invoice.RefundMail,
|
||||
CreatedDate = invoice.InvoiceTime,
|
||||
ExpirationDate = invoice.ExpirationTime,
|
||||
MonitoringDate = invoice.MonitoringExpiration,
|
||||
OrderId = invoice.OrderId,
|
||||
BuyerInformation = invoice.BuyerInformation,
|
||||
Rate = cryptoData.Rate,
|
||||
Fiat = dto.Price + " " + dto.Currency,
|
||||
BTC = accounting.TotalDue.ToString() + $" {network.CryptoCode}",
|
||||
BTCDue = accounting.Due.ToString() + $" {network.CryptoCode}",
|
||||
BTCPaid = accounting.Paid.ToString() + $" {network.CryptoCode}",
|
||||
NetworkFee = accounting.NetworkFee.ToString() + $" {network.CryptoCode}",
|
||||
Fiat = FormatCurrency((decimal)dto.Price, dto.Currency),
|
||||
NotificationUrl = invoice.NotificationURL,
|
||||
ProductInformation = invoice.ProductInformation,
|
||||
BitcoinAddress = BitcoinAddress.Create(cryptoInfo.Address, network.NBitcoinNetwork),
|
||||
PaymentUrl = cryptoInfo.PaymentUrls.BIP72
|
||||
};
|
||||
|
||||
foreach (var data in invoice.GetCryptoData())
|
||||
{
|
||||
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(data.Key, StringComparison.OrdinalIgnoreCase));
|
||||
var accounting = data.Value.Calculate();
|
||||
var paymentNetwork = _NetworkProvider.GetNetwork(data.Key);
|
||||
var cryptoPayment = new InvoiceDetailsModel.CryptoPayment();
|
||||
cryptoPayment.CryptoCode = paymentNetwork.CryptoCode;
|
||||
cryptoPayment.Due = accounting.Due.ToString() + $" {paymentNetwork.CryptoCode}";
|
||||
cryptoPayment.Paid = accounting.CryptoPaid.ToString() + $" {paymentNetwork.CryptoCode}";
|
||||
cryptoPayment.Address = data.Value.DepositAddress.ToString();
|
||||
cryptoPayment.Rate = FormatCurrency(data.Value);
|
||||
cryptoPayment.PaymentUrl = cryptoInfo.PaymentUrls.BIP21;
|
||||
model.CryptoPayments.Add(cryptoPayment);
|
||||
}
|
||||
|
||||
var payments = invoice
|
||||
.Payments
|
||||
.Select(async payment =>
|
||||
{
|
||||
var m = new InvoiceDetailsModel.Payment();
|
||||
m.DepositAddress = payment.GetScriptPubKey().GetDestinationAddress(network.NBitcoinNetwork);
|
||||
var paymentNetwork = _NetworkProvider.GetNetwork(payment.GetCryptoCode());
|
||||
m.CryptoCode = payment.GetCryptoCode();
|
||||
m.DepositAddress = payment.GetScriptPubKey().GetDestinationAddress(paymentNetwork.NBitcoinNetwork);
|
||||
m.Confirmations = (await _ExplorerClients.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(payment.Outpoint.Hash))?.Confirmations ?? 0;
|
||||
m.TransactionId = payment.Outpoint.Hash.ToString();
|
||||
m.ReceivedTime = payment.ReceivedTime;
|
||||
m.TransactionLink = string.Format(network.BlockExplorerLink, m.TransactionId);
|
||||
m.TransactionLink = string.Format(paymentNetwork.BlockExplorerLink, m.TransactionId);
|
||||
return m;
|
||||
})
|
||||
.ToArray();
|
||||
await Task.WhenAll(payments);
|
||||
model.Addresses = invoice.HistoricalAddresses;
|
||||
model.Payments = payments.Select(p => p.GetAwaiter().GetResult()).ToList();
|
||||
model.StatusMessage = StatusMessage;
|
||||
return View(model);
|
||||
@@ -156,7 +144,7 @@ namespace BTCPayServer.Controllers
|
||||
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
|
||||
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
|
||||
ItemDesc = invoice.ProductInformation.ItemDesc,
|
||||
Rate = cryptoData.Rate.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})",
|
||||
Rate = FormatCurrency(cryptoData),
|
||||
MerchantRefLink = invoice.RedirectURL ?? "/",
|
||||
StoreName = store.StoreName,
|
||||
TxFees = cryptoData.TxFee.ToString(),
|
||||
@@ -171,6 +159,16 @@ namespace BTCPayServer.Controllers
|
||||
return model;
|
||||
}
|
||||
|
||||
private string FormatCurrency(CryptoData cryptoData)
|
||||
{
|
||||
string currency = cryptoData.ParentEntity.ProductInformation.Currency;
|
||||
return FormatCurrency(cryptoData.Rate, currency);
|
||||
}
|
||||
public string FormatCurrency(decimal price, string currency)
|
||||
{
|
||||
return price.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})";
|
||||
}
|
||||
|
||||
private string PrettyPrint(TimeSpan expiration)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -224,7 +222,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
|
||||
ArraySegment<Byte> DummyBuffer = new ArraySegment<Byte>(new Byte[1]);
|
||||
private async Task NotifySocket(WebSocket webSocket, string invoiceId, string expectedId)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using NBitcoin;
|
||||
|
||||
@@ -9,8 +10,18 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
{
|
||||
public class InvoiceDetailsModel
|
||||
{
|
||||
public class CryptoPayment
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public string Due { get; set; }
|
||||
public string Paid { get; set; }
|
||||
public string Address { get; internal set; }
|
||||
public string Rate { get; internal set; }
|
||||
public string PaymentUrl { get; internal set; }
|
||||
}
|
||||
public class Payment
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public int Confirmations
|
||||
{
|
||||
get; set;
|
||||
@@ -48,10 +59,12 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<Payment> Payments
|
||||
public List<CryptoPayment> CryptoPayments
|
||||
{
|
||||
get; set;
|
||||
} = new List<Payment>();
|
||||
} = new List<CryptoPayment>();
|
||||
|
||||
public List<Payment> Payments { get; set; } = new List<Payment>();
|
||||
|
||||
public string Status
|
||||
{
|
||||
@@ -107,40 +120,12 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string BTC
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string BTCDue
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string BTCPaid
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public String NetworkFee
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public ProductInformation ProductInformation
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public BitcoinAddress BitcoinAddress
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public string PaymentUrl
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public HistoricalAddressInvoiceData[] Addresses { get; set; }
|
||||
public DateTimeOffset MonitoringDate { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
.Invoices
|
||||
.Include(o => o.Payments)
|
||||
.Include(o => o.RefundAddresses);
|
||||
|
||||
if(queryObject.IncludeAddresses)
|
||||
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||
if (!string.IsNullOrEmpty(queryObject.InvoiceId))
|
||||
{
|
||||
query = query.Where(i => i.Id == queryObject.InvoiceId);
|
||||
@@ -549,5 +550,6 @@ namespace BTCPayServer.Services.Invoices
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool IncludeAddresses { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
<th>Expiration date</th>
|
||||
<td>@Model.ExpirationDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Monitoring date</th>
|
||||
<td>@Model.MonitoringDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>@Model.Status</td>
|
||||
@@ -69,34 +73,10 @@
|
||||
<th>Total fiat due</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Network Fee</th>
|
||||
<td>@Model.NetworkFee</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total crypto due</th>
|
||||
<td>@Model.BTC</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Crypto due</th>
|
||||
<td>@Model.BTCDue</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Crypto paid</th>
|
||||
<td>@Model.BTCPaid</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Notification Url</th>
|
||||
<td>@Model.NotificationUrl</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Payment address</th>
|
||||
<td>@Model.BitcoinAddress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Payment Url</th>
|
||||
<td class="overflowbox"><a href="@Model.PaymentUrl">@Model.PaymentUrl</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -161,17 +141,39 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Payments</h3>
|
||||
<div class="form-group">
|
||||
<form asp-action="Invoice" method="post">
|
||||
<button type="submit" name="command" class="btn btn-success" value="refresh" title="Refresh State">
|
||||
Refresh state
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<h3>Paid summary</h3>
|
||||
<table class="table">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Crypto</th>
|
||||
<th>Rate</th>
|
||||
<th>Paid</th>
|
||||
<th>Due</th>
|
||||
<th>Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var payment in Model.CryptoPayments)
|
||||
{
|
||||
<tr>
|
||||
<td>@payment.CryptoCode</td>
|
||||
<td>@payment.Rate</td>
|
||||
<td>@payment.Paid</td>
|
||||
<td>@payment.Due</td>
|
||||
<td>@payment.Address</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Payments</h3>
|
||||
<table class="table">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Crypto</th>
|
||||
<th>Date</th>
|
||||
<th>Deposit address</th>
|
||||
<th>Transaction Id</th>
|
||||
@@ -180,14 +182,39 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var payment in Model.Payments)
|
||||
{
|
||||
<tr>
|
||||
<td>@payment.ReceivedTime</td>
|
||||
<td>@payment.DepositAddress</td>
|
||||
<td><a href="@payment.TransactionLink" target="_blank">@payment.TransactionId</a></td>
|
||||
<td>@payment.Confirmations</td>
|
||||
</tr>
|
||||
}
|
||||
{
|
||||
<tr>
|
||||
<td>@payment.CryptoCode</td>
|
||||
<td>@payment.ReceivedTime</td>
|
||||
<td>@payment.DepositAddress</td>
|
||||
<td><a href="@payment.TransactionLink" target="_blank">@payment.TransactionId</a></td>
|
||||
<td>@payment.Confirmations</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Addresses</h3>
|
||||
<table class="table">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Crypto</th>
|
||||
<th>Address</th>
|
||||
<th>Current</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(var address in Model.Addresses)
|
||||
{
|
||||
<tr>
|
||||
<td>@address.GetCryptoCode()</td>
|
||||
<td>@address.Address</td>
|
||||
<td>@(!address.UnAssigned.HasValue)</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user