mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Add page for viewing the Invoice details
This commit is contained in:
@@ -108,7 +108,7 @@ namespace BTCPayServer.Tests
|
|||||||
_Host.Start();
|
_Host.Start();
|
||||||
Runtime = (BTCPayServerRuntime)_Host.Services.GetService(typeof(BTCPayServerRuntime));
|
Runtime = (BTCPayServerRuntime)_Host.Services.GetService(typeof(BTCPayServerRuntime));
|
||||||
var watcher = (InvoiceWatcher)_Host.Services.GetService(typeof(InvoiceWatcher));
|
var watcher = (InvoiceWatcher)_Host.Services.GetService(typeof(InvoiceWatcher));
|
||||||
watcher.PollInterval = TimeSpan.FromMilliseconds(50);
|
watcher.PollInterval = TimeSpan.FromMilliseconds(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BTCPayServerRuntime Runtime
|
public BTCPayServerRuntime Runtime
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<Version>1.0.0.8</Version>
|
<Version>1.0.0.9</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Build\dockerfiles\**" />
|
<Compile Remove="Build\dockerfiles\**" />
|
||||||
|
|||||||
@@ -17,8 +17,79 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace BTCPayServer.Controllers
|
namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
public partial class InvoiceController
|
public partial class InvoiceController
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("invoices/{invoiceId}")]
|
||||||
|
public async Task<IActionResult> Invoice(string invoiceId, string command)
|
||||||
|
{
|
||||||
|
if(command == "refresh")
|
||||||
|
{
|
||||||
|
await _Watcher.WatchAsync(invoiceId, true);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||||
|
{
|
||||||
|
UserId = GetUserId(),
|
||||||
|
InvoiceId = invoiceId
|
||||||
|
})).FirstOrDefault();
|
||||||
|
if(invoice == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var dto = invoice.EntityToDTO();
|
||||||
|
var store = await _StoreRepository.FindStore(invoice.StoreId);
|
||||||
|
InvoiceDetailsModel model = new InvoiceDetailsModel()
|
||||||
|
{
|
||||||
|
StoreName = store.StoreName,
|
||||||
|
StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }),
|
||||||
|
Id = invoice.Id,
|
||||||
|
Status = invoice.Status,
|
||||||
|
RefundEmail = invoice.RefundMail,
|
||||||
|
CreatedDate = invoice.InvoiceTime,
|
||||||
|
ExpirationDate = invoice.ExpirationTime,
|
||||||
|
OrderId = invoice.OrderId,
|
||||||
|
BuyerInformation = invoice.BuyerInformation,
|
||||||
|
Rate = invoice.Rate,
|
||||||
|
Fiat = dto.Price + " " + dto.Currency,
|
||||||
|
BTC = invoice.GetTotalCryptoDue().ToString() + " BTC",
|
||||||
|
BTCDue = invoice.GetCryptoDue().ToString() + " BTC",
|
||||||
|
BTCPaid = invoice.GetTotalPaid().ToString() + " BTC",
|
||||||
|
NetworkFee = invoice.GetNetworkFee().ToString() + " BTC",
|
||||||
|
NotificationUrl = invoice.NotificationURL,
|
||||||
|
ProductInformation = invoice.ProductInformation,
|
||||||
|
BitcoinAddress = invoice.DepositAddress,
|
||||||
|
PaymentUrl = dto.PaymentUrls.BIP72
|
||||||
|
};
|
||||||
|
|
||||||
|
var payments = invoice
|
||||||
|
.Payments
|
||||||
|
.Select(async payment =>
|
||||||
|
{
|
||||||
|
var m = new InvoiceDetailsModel.Payment();
|
||||||
|
m.DepositAddress = payment.Output.ScriptPubKey.GetDestinationAddress(_Network);
|
||||||
|
m.Confirmations = (await _Explorer.GetTransactionAsync(payment.Outpoint.Hash))?.Confirmations ?? 0;
|
||||||
|
m.TransactionId = payment.Outpoint.Hash.ToString();
|
||||||
|
m.ReceivedTime = payment.ReceivedTime;
|
||||||
|
m.TransactionLink = _Network == Network.Main ? $"https://www.smartbit.com.au/tx/{m.TransactionId}" : $"https://testnet.smartbit.com.au/{m.TransactionId}";
|
||||||
|
return m;
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
await Task.WhenAll(payments);
|
||||||
|
model.Payments = payments.Select(p => p.GetAwaiter().GetResult()).ToList();
|
||||||
|
model.StatusMessage = StatusMessage;
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("i/{invoiceId}")]
|
[Route("i/{invoiceId}")]
|
||||||
@@ -168,14 +239,14 @@ namespace BTCPayServer.Controllers
|
|||||||
FullNotifications = true,
|
FullNotifications = true,
|
||||||
BuyerEmail = model.BuyerEmail,
|
BuyerEmail = model.BuyerEmail,
|
||||||
}, store);
|
}, store);
|
||||||
|
|
||||||
StatusMessage = $"Invoice {result.Data.Id} just created!";
|
StatusMessage = $"Invoice {result.Data.Id} just created!";
|
||||||
return RedirectToAction(nameof(ListInvoices));
|
return RedirectToAction(nameof(ListInvoices));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SelectList> GetStores(string userId, string storeId = null)
|
private async Task<SelectList> GetStores(string userId, string storeId = null)
|
||||||
{
|
{
|
||||||
return new SelectList(await _StoreRepository.GetStoresByUserId(userId), nameof(StoreData.Id), nameof(StoreData.StoreName), storeId);
|
return new SelectList(await _StoreRepository.GetStoresByUserId(userId), nameof(StoreData.Id), nameof(StoreData.StoreName), storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ using BTCPayServer.Validations;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using NBXplorer.DerivationStrategy;
|
using NBXplorer.DerivationStrategy;
|
||||||
|
using NBXplorer;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers
|
namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Network _Network;
|
Network _Network;
|
||||||
UserManager<ApplicationUser> _UserManager;
|
UserManager<ApplicationUser> _UserManager;
|
||||||
IFeeProvider _FeeProvider;
|
IFeeProvider _FeeProvider;
|
||||||
|
ExplorerClient _Explorer;
|
||||||
|
|
||||||
public InvoiceController(
|
public InvoiceController(
|
||||||
Network network,
|
Network network,
|
||||||
@@ -61,8 +63,10 @@ namespace BTCPayServer.Controllers
|
|||||||
IRateProvider rateProvider,
|
IRateProvider rateProvider,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
InvoiceWatcher watcher,
|
InvoiceWatcher watcher,
|
||||||
|
ExplorerClient explorerClient,
|
||||||
IFeeProvider feeProvider)
|
IFeeProvider feeProvider)
|
||||||
{
|
{
|
||||||
|
_Explorer = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient));
|
||||||
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
||||||
_Network = network ?? throw new ArgumentNullException(nameof(network));
|
_Network = network ?? throw new ArgumentNullException(nameof(network));
|
||||||
_TokenRepository = tokenRepository ?? throw new ArgumentNullException(nameof(tokenRepository));
|
_TokenRepository = tokenRepository ?? throw new ArgumentNullException(nameof(tokenRepository));
|
||||||
|
|||||||
146
BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs
Normal file
146
BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Servcices.Invoices;
|
||||||
|
using NBitcoin;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.InvoicingModels
|
||||||
|
{
|
||||||
|
public class InvoiceDetailsModel
|
||||||
|
{
|
||||||
|
public class Payment
|
||||||
|
{
|
||||||
|
public int Confirmations
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public BitcoinAddress DepositAddress
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public string Amount
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public string TransactionId
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public DateTimeOffset ReceivedTime
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
internal set;
|
||||||
|
}
|
||||||
|
public string TransactionLink
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusMessage
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public String Id
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Payment> Payments
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
} = new List<Payment>();
|
||||||
|
|
||||||
|
public string Status
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset CreatedDate
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset ExpirationDate
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OrderId
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public string RefundEmail
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public BuyerInformation BuyerInformation
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public object StoreName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
internal set;
|
||||||
|
}
|
||||||
|
public string StoreLink
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public double Rate
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
internal set;
|
||||||
|
}
|
||||||
|
public string NotificationUrl
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
internal set;
|
||||||
|
}
|
||||||
|
public string Fiat
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -302,6 +302,12 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
var str = JsonConvert.SerializeObject(from);
|
var str = JsonConvert.SerializeObject(from);
|
||||||
JsonConvert.PopulateObject(str, dest);
|
JsonConvert.PopulateObject(str, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money GetNetworkFee()
|
||||||
|
{
|
||||||
|
var item = Calculate();
|
||||||
|
return TxFee * item.TxCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PaymentEntity
|
public class PaymentEntity
|
||||||
|
|||||||
@@ -65,12 +65,17 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemovePendingInvoice(string invoiceId)
|
public async Task<bool> RemovePendingInvoice(string invoiceId)
|
||||||
{
|
{
|
||||||
using(var ctx = _ContextFactory.CreateContext())
|
using(var ctx = _ContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
ctx.PendingInvoices.Remove(new PendingInvoiceData() { Id = invoiceId });
|
ctx.PendingInvoices.Remove(new PendingInvoiceData() { Id = invoiceId });
|
||||||
await ctx.SaveChangesAsync();
|
try
|
||||||
|
{
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(DbUpdateException) { return false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,12 +212,16 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
{
|
{
|
||||||
using(var context = _ContextFactory.CreateContext())
|
using(var context = _ContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
|
|
||||||
IQueryable<InvoiceData> query = context
|
IQueryable<InvoiceData> query = context
|
||||||
.Invoices
|
.Invoices
|
||||||
.Include(o => o.Payments)
|
.Include(o => o.Payments)
|
||||||
.Include(o => o.RefundAddresses);
|
.Include(o => o.RefundAddresses);
|
||||||
|
|
||||||
|
if(!string.IsNullOrEmpty(queryObject.InvoiceId))
|
||||||
|
{
|
||||||
|
query = query.Where(i => i.Id == queryObject.InvoiceId);
|
||||||
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(queryObject.StoreId))
|
if(!string.IsNullOrEmpty(queryObject.StoreId))
|
||||||
{
|
{
|
||||||
query = query.Where(i => i.StoreDataId == queryObject.StoreId);
|
query = query.Where(i => i.StoreDataId == queryObject.StoreId);
|
||||||
@@ -380,5 +389,10 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
public string InvoiceId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
InvoiceNotificationManager _NotificationManager;
|
InvoiceNotificationManager _NotificationManager;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWallet _Wallet;
|
||||||
|
|
||||||
public InvoiceWatcher(ExplorerClient explorerClient,
|
public InvoiceWatcher(ExplorerClient explorerClient,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
BTCPayWallet wallet,
|
BTCPayWallet wallet,
|
||||||
InvoiceNotificationManager notificationManager)
|
InvoiceNotificationManager notificationManager)
|
||||||
{
|
{
|
||||||
|
LongPollingMode = explorerClient.Network == Network.RegTest;
|
||||||
|
PollInterval = explorerClient.Network == Network.RegTest ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromMinutes(1.0);
|
||||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
||||||
_ExplorerClient = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient));
|
_ExplorerClient = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient));
|
||||||
_DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network);
|
_DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network);
|
||||||
@@ -36,6 +38,11 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
_NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager));
|
_NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool LongPollingMode
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task NotifyReceived(Script scriptPubKey)
|
public async Task NotifyReceived(Script scriptPubKey)
|
||||||
{
|
{
|
||||||
var invoice = await _Wallet.GetInvoiceId(scriptPubKey);
|
var invoice = await _Wallet.GetInvoiceId(scriptPubKey);
|
||||||
@@ -52,7 +59,6 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
|
|
||||||
private async Task UpdateInvoice(string invoiceId)
|
private async Task UpdateInvoice(string invoiceId)
|
||||||
{
|
{
|
||||||
Logs.PayServer.LogInformation("Updating invoice " + invoiceId);
|
|
||||||
UTXOChanges changes = null;
|
UTXOChanges changes = null;
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
@@ -75,8 +81,8 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
|
|
||||||
if(invoice.Status == "complete" || invoice.Status == "invalid")
|
if(invoice.Status == "complete" || invoice.Status == "invalid")
|
||||||
{
|
{
|
||||||
await _InvoiceRepository.RemovePendingInvoice(invoice.Id).ConfigureAwait(false);
|
if(await _InvoiceRepository.RemovePendingInvoice(invoice.Id).ConfigureAwait(false))
|
||||||
Logs.PayServer.LogInformation("Stopped watching invoice " + invoiceId);
|
Logs.PayServer.LogInformation("Stopped watching invoice " + invoiceId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,34 +103,27 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
|
|
||||||
|
|
||||||
private async Task<(bool NeedSave, UTXOChanges Changes)> UpdateInvoice(UTXOChanges changes, InvoiceEntity invoice)
|
private async Task<(bool NeedSave, UTXOChanges Changes)> UpdateInvoice(UTXOChanges changes, InvoiceEntity invoice)
|
||||||
{
|
{
|
||||||
if(invoice.Status == "invalid")
|
|
||||||
{
|
|
||||||
return (false, changes);
|
|
||||||
}
|
|
||||||
bool needSave = false;
|
bool needSave = false;
|
||||||
bool shouldWait = true;
|
|
||||||
|
|
||||||
if(invoice.ExpirationTime < DateTimeOffset.UtcNow && (invoice.Status == "new" || invoice.Status == "paidPartial"))
|
if(invoice.Status != "invalid" && invoice.ExpirationTime < DateTimeOffset.UtcNow && (invoice.Status == "new" || invoice.Status == "paidPartial"))
|
||||||
{
|
{
|
||||||
needSave = true;
|
needSave = true;
|
||||||
invoice.Status = "invalid";
|
invoice.Status = "invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(invoice.Status == "new" || invoice.Status == "paidPartial")
|
if(invoice.Status == "invalid" || invoice.Status == "new" || invoice.Status == "paidPartial")
|
||||||
{
|
{
|
||||||
var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);
|
var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);
|
||||||
changes = await _ExplorerClient.SyncAsync(strategy, changes, true, _Cts.Token).ConfigureAwait(false);
|
changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
var utxos = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
|
var utxos = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
|
||||||
var invoiceIds = utxos.Select(u => _Wallet.GetInvoiceId(u.Output.ScriptPubKey)).ToArray();
|
var invoiceIds = utxos.Select(u => _Wallet.GetInvoiceId(u.Output.ScriptPubKey)).ToArray();
|
||||||
utxos =
|
utxos =
|
||||||
utxos
|
utxos
|
||||||
.Where((u,i) => invoiceIds[i].GetAwaiter().GetResult() == invoice.Id)
|
.Where((u, i) => invoiceIds[i].GetAwaiter().GetResult() == invoice.Id)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
shouldWait = false; //should not wait, Sync is blocking call
|
|
||||||
|
|
||||||
List<Coin> receivedCoins = new List<Coin>();
|
List<Coin> receivedCoins = new List<Coin>();
|
||||||
foreach(var received in utxos)
|
foreach(var received in utxos)
|
||||||
if(received.Output.ScriptPubKey == invoice.DepositAddress.ScriptPubKey)
|
if(received.Output.ScriptPubKey == invoice.DepositAddress.ScriptPubKey)
|
||||||
@@ -218,24 +217,31 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldWait = shouldWait && !needSave;
|
|
||||||
|
|
||||||
if(shouldWait)
|
|
||||||
{
|
|
||||||
await Task.Delay(PollInterval, _Cts.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (needSave, changes);
|
return (needSave, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TimeSpan _PollInterval;
|
||||||
public TimeSpan PollInterval
|
public TimeSpan PollInterval
|
||||||
{
|
{
|
||||||
get; set;
|
get
|
||||||
} = TimeSpan.FromSeconds(10);
|
{
|
||||||
|
return _PollInterval;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_PollInterval = value;
|
||||||
|
if(_UpdatePendingInvoices != null)
|
||||||
|
{
|
||||||
|
_UpdatePendingInvoices.Change(0, (int)value.TotalMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task WatchAsync(string invoiceId)
|
public async Task WatchAsync(string invoiceId, bool singleShot = false)
|
||||||
{
|
{
|
||||||
await _InvoiceRepository.AddPendingInvoice(invoiceId).ConfigureAwait(false);
|
if(!singleShot)
|
||||||
|
await _InvoiceRepository.AddPendingInvoice(invoiceId).ConfigureAwait(false);
|
||||||
_WatchRequests.Add(invoiceId);
|
_WatchRequests.Add(invoiceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +270,7 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
{
|
{
|
||||||
_WatchRequests.Add(pending);
|
_WatchRequests.Add(pending);
|
||||||
}
|
}
|
||||||
}, null, 0, (int)TimeSpan.FromMinutes(1.0).TotalMilliseconds);
|
}, null, 0, (int)PollInterval.TotalMilliseconds);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +285,7 @@ namespace BTCPayServer.Servcices.Invoices
|
|||||||
var localItem = item;
|
var localItem = item;
|
||||||
|
|
||||||
// If the invoice is already updating, ignore
|
// If the invoice is already updating, ignore
|
||||||
Lazy<Task> updateInvoice =new Lazy<Task>(() => UpdateInvoice(localItem), false);
|
Lazy<Task> updateInvoice = new Lazy<Task>(() => UpdateInvoice(localItem), false);
|
||||||
if(updating.TryAdd(item, updateInvoice))
|
if(updating.TryAdd(item, updateInvoice))
|
||||||
{
|
{
|
||||||
updateInvoice.Value.ContinueWith(i => updating.TryRemove(item, out updateInvoice));
|
updateInvoice.Value.ContinueWith(i => updating.TryRemove(item, out updateInvoice));
|
||||||
|
|||||||
187
BTCPayServer/Views/Invoice/Invoice.cshtml
Normal file
187
BTCPayServer/Views/Invoice/Invoice.cshtml
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
@model InvoiceDetailsModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Invoice " + Model.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading">@ViewData["Title"]</h2>
|
||||||
|
<hr class="primary">
|
||||||
|
<p>Invoice details</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h3>Information</h3>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Store</th>
|
||||||
|
<td><a href="@Model.StoreLink">@Model.StoreName</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
|
<td>@Model.Id</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Created date</th>
|
||||||
|
<td>@Model.CreatedDate</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Expiration date</th>
|
||||||
|
<td>@Model.CreatedDate</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Status</th>
|
||||||
|
<td>@Model.Status</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Refund email</th>
|
||||||
|
<td>@Model.RefundEmail</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Order Id</th>
|
||||||
|
<td>@Model.OrderId</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Rate</th>
|
||||||
|
<td>@Model.Rate</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<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><a href="@Model.PaymentUrl">@Model.PaymentUrl</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h3>Buyer information</h3>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Name
|
||||||
|
<th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerName</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerEmail</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Phone</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerPhone</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address 1</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerAddress1</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address 2</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerAddress2</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>City</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerCity</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>State</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerState</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Country</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerCountry</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Zip</th>
|
||||||
|
<td>@Model.BuyerInformation.BuyerZip</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Product information</h3>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Item code</th>
|
||||||
|
<td>@Model.ProductInformation.ItemCode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Item Description</th>
|
||||||
|
<td>@Model.ProductInformation.ItemDesc</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Price</th>
|
||||||
|
<td>@Model.ProductInformation.Price @Model.ProductInformation.Currency</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
<table class="table">
|
||||||
|
<thead class="thead-inverse">
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Deposit address</th>
|
||||||
|
<th>Transaction Id</th>
|
||||||
|
<th>Confirmations</th>
|
||||||
|
</tr>
|
||||||
|
</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>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
<td>@invoice.InvoiceId</td>
|
<td>@invoice.InvoiceId</td>
|
||||||
<td>@invoice.Status</td>
|
<td>@invoice.Status</td>
|
||||||
<td>@invoice.AmountCurrency</td>
|
<td>@invoice.AmountCurrency</td>
|
||||||
<td><a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a></td>
|
<td><a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a> - <a asp-action="Invoice" asp-route-invoiceId="@invoice.InvoiceId">Details</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user