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
|
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]
|
[HttpGet]
|
||||||
[Route("invoices/{invoiceId}")]
|
[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()
|
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||||
{
|
{
|
||||||
UserId = GetUserId(),
|
UserId = GetUserId(),
|
||||||
InvoiceId = invoiceId
|
InvoiceId = invoiceId,
|
||||||
|
IncludeAddresses = true
|
||||||
})).FirstOrDefault();
|
})).FirstOrDefault();
|
||||||
if (invoice == null)
|
if (invoice == null)
|
||||||
return NotFound();
|
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 dto = invoice.EntityToDTO(_NetworkProvider);
|
||||||
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(cryptoCode, StringComparison.OrdinalIgnoreCase));
|
|
||||||
var store = await _StoreRepository.FindStore(invoice.StoreId);
|
var store = await _StoreRepository.FindStore(invoice.StoreId);
|
||||||
|
|
||||||
var accounting = cryptoData.Calculate();
|
|
||||||
InvoiceDetailsModel model = new InvoiceDetailsModel()
|
InvoiceDetailsModel model = new InvoiceDetailsModel()
|
||||||
{
|
{
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
@@ -74,34 +50,46 @@ namespace BTCPayServer.Controllers
|
|||||||
RefundEmail = invoice.RefundMail,
|
RefundEmail = invoice.RefundMail,
|
||||||
CreatedDate = invoice.InvoiceTime,
|
CreatedDate = invoice.InvoiceTime,
|
||||||
ExpirationDate = invoice.ExpirationTime,
|
ExpirationDate = invoice.ExpirationTime,
|
||||||
|
MonitoringDate = invoice.MonitoringExpiration,
|
||||||
OrderId = invoice.OrderId,
|
OrderId = invoice.OrderId,
|
||||||
BuyerInformation = invoice.BuyerInformation,
|
BuyerInformation = invoice.BuyerInformation,
|
||||||
Rate = cryptoData.Rate,
|
Fiat = FormatCurrency((decimal)dto.Price, dto.Currency),
|
||||||
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}",
|
|
||||||
NotificationUrl = invoice.NotificationURL,
|
NotificationUrl = invoice.NotificationURL,
|
||||||
ProductInformation = invoice.ProductInformation,
|
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
|
var payments = invoice
|
||||||
.Payments
|
.Payments
|
||||||
.Select(async payment =>
|
.Select(async payment =>
|
||||||
{
|
{
|
||||||
var m = new InvoiceDetailsModel.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.Confirmations = (await _ExplorerClients.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(payment.Outpoint.Hash))?.Confirmations ?? 0;
|
||||||
m.TransactionId = payment.Outpoint.Hash.ToString();
|
m.TransactionId = payment.Outpoint.Hash.ToString();
|
||||||
m.ReceivedTime = payment.ReceivedTime;
|
m.ReceivedTime = payment.ReceivedTime;
|
||||||
m.TransactionLink = string.Format(network.BlockExplorerLink, m.TransactionId);
|
m.TransactionLink = string.Format(paymentNetwork.BlockExplorerLink, m.TransactionId);
|
||||||
return m;
|
return m;
|
||||||
})
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
await Task.WhenAll(payments);
|
await Task.WhenAll(payments);
|
||||||
|
model.Addresses = invoice.HistoricalAddresses;
|
||||||
model.Payments = payments.Select(p => p.GetAwaiter().GetResult()).ToList();
|
model.Payments = payments.Select(p => p.GetAwaiter().GetResult()).ToList();
|
||||||
model.StatusMessage = StatusMessage;
|
model.StatusMessage = StatusMessage;
|
||||||
return View(model);
|
return View(model);
|
||||||
@@ -156,7 +144,7 @@ namespace BTCPayServer.Controllers
|
|||||||
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
|
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
|
||||||
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
|
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
|
||||||
ItemDesc = invoice.ProductInformation.ItemDesc,
|
ItemDesc = invoice.ProductInformation.ItemDesc,
|
||||||
Rate = cryptoData.Rate.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})",
|
Rate = FormatCurrency(cryptoData),
|
||||||
MerchantRefLink = invoice.RedirectURL ?? "/",
|
MerchantRefLink = invoice.RedirectURL ?? "/",
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
TxFees = cryptoData.TxFee.ToString(),
|
TxFees = cryptoData.TxFee.ToString(),
|
||||||
@@ -171,6 +159,16 @@ namespace BTCPayServer.Controllers
|
|||||||
return model;
|
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)
|
private string PrettyPrint(TimeSpan expiration)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
|
||||||
@@ -9,8 +10,18 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||||||
{
|
{
|
||||||
public class InvoiceDetailsModel
|
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 class Payment
|
||||||
{
|
{
|
||||||
|
public string CryptoCode { get; set; }
|
||||||
public int Confirmations
|
public int Confirmations
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
@@ -48,10 +59,12 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Payment> Payments
|
public List<CryptoPayment> CryptoPayments
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
} = new List<Payment>();
|
} = new List<CryptoPayment>();
|
||||||
|
|
||||||
|
public List<Payment> Payments { get; set; } = new List<Payment>();
|
||||||
|
|
||||||
public string Status
|
public string Status
|
||||||
{
|
{
|
||||||
@@ -107,40 +120,12 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||||||
get;
|
get;
|
||||||
set;
|
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
|
public ProductInformation ProductInformation
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
internal set;
|
internal set;
|
||||||
}
|
}
|
||||||
public BitcoinAddress BitcoinAddress
|
public HistoricalAddressInvoiceData[] Addresses { get; set; }
|
||||||
{
|
public DateTimeOffset MonitoringDate { get; internal set; }
|
||||||
get;
|
|
||||||
internal set;
|
|
||||||
}
|
|
||||||
public string PaymentUrl
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,7 +344,8 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
.Invoices
|
.Invoices
|
||||||
.Include(o => o.Payments)
|
.Include(o => o.Payments)
|
||||||
.Include(o => o.RefundAddresses);
|
.Include(o => o.RefundAddresses);
|
||||||
|
if(queryObject.IncludeAddresses)
|
||||||
|
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||||
if (!string.IsNullOrEmpty(queryObject.InvoiceId))
|
if (!string.IsNullOrEmpty(queryObject.InvoiceId))
|
||||||
{
|
{
|
||||||
query = query.Where(i => i.Id == queryObject.InvoiceId);
|
query = query.Where(i => i.Id == queryObject.InvoiceId);
|
||||||
@@ -549,5 +550,6 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
public bool IncludeAddresses { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,10 @@
|
|||||||
<th>Expiration date</th>
|
<th>Expiration date</th>
|
||||||
<td>@Model.ExpirationDate</td>
|
<td>@Model.ExpirationDate</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Monitoring date</th>
|
||||||
|
<td>@Model.MonitoringDate</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<td>@Model.Status</td>
|
<td>@Model.Status</td>
|
||||||
@@ -69,34 +73,10 @@
|
|||||||
<th>Total fiat due</th>
|
<th>Total fiat due</th>
|
||||||
<td>@Model.Fiat</td>
|
<td>@Model.Fiat</td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<th>Notification Url</th>
|
<th>Notification Url</th>
|
||||||
<td>@Model.NotificationUrl</td>
|
<td>@Model.NotificationUrl</td>
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -161,17 +141,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h3>Payments</h3>
|
<h3>Paid summary</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>
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead class="thead-inverse">
|
<thead class="thead-inverse">
|
||||||
<tr>
|
<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>Date</th>
|
||||||
<th>Deposit address</th>
|
<th>Deposit address</th>
|
||||||
<th>Transaction Id</th>
|
<th>Transaction Id</th>
|
||||||
@@ -182,6 +184,7 @@
|
|||||||
@foreach(var payment in Model.Payments)
|
@foreach(var payment in Model.Payments)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>@payment.CryptoCode</td>
|
||||||
<td>@payment.ReceivedTime</td>
|
<td>@payment.ReceivedTime</td>
|
||||||
<td>@payment.DepositAddress</td>
|
<td>@payment.DepositAddress</td>
|
||||||
<td><a href="@payment.TransactionLink" target="_blank">@payment.TransactionId</a></td>
|
<td><a href="@payment.TransactionLink" target="_blank">@payment.TransactionId</a></td>
|
||||||
@@ -192,5 +195,29 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user