mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
Various bugfixes (#308)
* NotifyEmail field on Invoice, sending email when triggered * Styling invoices page * Exporting Invoices in JSON * Recoding based on feedback * Fixing image breaking responsive layout on mobile * Reducing amount of data sent in email notification * Turning bundling on by default
This commit is contained in:
committed by
Nicolas Dorier
parent
db40c7bc32
commit
c2bbc04c4c
@@ -58,6 +58,7 @@ namespace BTCPayServer.Controllers
|
|||||||
OrderId = invoice.OrderId,
|
OrderId = invoice.OrderId,
|
||||||
BuyerInformation = invoice.BuyerInformation,
|
BuyerInformation = invoice.BuyerInformation,
|
||||||
Fiat = _CurrencyNameTable.DisplayFormatCurrency((decimal)dto.Price, dto.Currency),
|
Fiat = _CurrencyNameTable.DisplayFormatCurrency((decimal)dto.Price, dto.Currency),
|
||||||
|
NotificationEmail = invoice.NotificationEmail,
|
||||||
NotificationUrl = invoice.NotificationURL,
|
NotificationUrl = invoice.NotificationURL,
|
||||||
RedirectUrl = invoice.RedirectURL,
|
RedirectUrl = invoice.RedirectURL,
|
||||||
ProductInformation = invoice.ProductInformation,
|
ProductInformation = invoice.ProductInformation,
|
||||||
@@ -228,7 +229,7 @@ namespace BTCPayServer.Controllers
|
|||||||
if (!isDefaultCrypto)
|
if (!isDefaultCrypto)
|
||||||
return null;
|
return null;
|
||||||
var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider)
|
var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider)
|
||||||
.Where(c=> paymentMethodId.CryptoCode == c.GetId().CryptoCode)
|
.Where(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
if (paymentMethodTemp == null)
|
if (paymentMethodTemp == null)
|
||||||
paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
|
paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
|
||||||
@@ -405,23 +406,17 @@ namespace BTCPayServer.Controllers
|
|||||||
[BitpayAPIConstraint(false)]
|
[BitpayAPIConstraint(false)]
|
||||||
public async Task<IActionResult> ListInvoices(string searchTerm = null, int skip = 0, int count = 50)
|
public async Task<IActionResult> ListInvoices(string searchTerm = null, int skip = 0, int count = 50)
|
||||||
{
|
{
|
||||||
var model = new InvoicesModel();
|
var model = new InvoicesModel
|
||||||
var filterString = new SearchString(searchTerm);
|
|
||||||
foreach (var invoice in await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
|
||||||
{
|
{
|
||||||
TextSearch = filterString.TextSearch,
|
SearchTerm = searchTerm,
|
||||||
Count = count,
|
|
||||||
Skip = skip,
|
Skip = skip,
|
||||||
UserId = GetUserId(),
|
Count = count,
|
||||||
Unusual = !filterString.Filters.ContainsKey("unusual") ? null
|
StatusMessage = StatusMessage
|
||||||
: !bool.TryParse(filterString.Filters["unusual"].First(), out var r) ? (bool?)null
|
};
|
||||||
: r,
|
|
||||||
Status = filterString.Filters.ContainsKey("status") ? filterString.Filters["status"].ToArray() : null,
|
var list = await ListInvoicesProcess(searchTerm, skip, count);
|
||||||
ExceptionStatus = filterString.Filters.ContainsKey("exceptionstatus") ? filterString.Filters["exceptionstatus"].ToArray() : null,
|
foreach (var invoice in list)
|
||||||
StoreId = filterString.Filters.ContainsKey("storeid") ? filterString.Filters["storeid"].ToArray() : null
|
|
||||||
}))
|
|
||||||
{
|
{
|
||||||
model.SearchTerm = searchTerm;
|
|
||||||
model.Invoices.Add(new InvoiceModel()
|
model.Invoices.Add(new InvoiceModel()
|
||||||
{
|
{
|
||||||
Status = invoice.Status + (invoice.ExceptionStatus == null ? string.Empty : $" ({invoice.ExceptionStatus})"),
|
Status = invoice.Status + (invoice.ExceptionStatus == null ? string.Empty : $" ({invoice.ExceptionStatus})"),
|
||||||
@@ -433,12 +428,29 @@ namespace BTCPayServer.Controllers
|
|||||||
AmountCurrency = $"{invoice.ProductInformation.Price.ToString(CultureInfo.InvariantCulture)} {invoice.ProductInformation.Currency}"
|
AmountCurrency = $"{invoice.ProductInformation.Price.ToString(CultureInfo.InvariantCulture)} {invoice.ProductInformation.Currency}"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
model.Skip = skip;
|
|
||||||
model.Count = count;
|
|
||||||
model.StatusMessage = StatusMessage;
|
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<InvoiceEntity[]> ListInvoicesProcess(string searchTerm = null, int skip = 0, int count = 50)
|
||||||
|
{
|
||||||
|
var filterString = new SearchString(searchTerm);
|
||||||
|
var list = await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||||
|
{
|
||||||
|
TextSearch = filterString.TextSearch,
|
||||||
|
Count = count,
|
||||||
|
Skip = skip,
|
||||||
|
UserId = GetUserId(),
|
||||||
|
Unusual = !filterString.Filters.ContainsKey("unusual") ? null
|
||||||
|
: !bool.TryParse(filterString.Filters["unusual"].First(), out var r) ? (bool?)null
|
||||||
|
: r,
|
||||||
|
Status = filterString.Filters.ContainsKey("status") ? filterString.Filters["status"].ToArray() : null,
|
||||||
|
ExceptionStatus = filterString.Filters.ContainsKey("exceptionstatus") ? filterString.Filters["exceptionstatus"].ToArray() : null,
|
||||||
|
StoreId = filterString.Filters.ContainsKey("storeid") ? filterString.Filters["storeid"].ToArray() : null
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("invoices/create")]
|
[Route("invoices/create")]
|
||||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||||
@@ -501,6 +513,7 @@ namespace BTCPayServer.Controllers
|
|||||||
PosData = model.PosData,
|
PosData = model.PosData,
|
||||||
OrderId = model.OrderId,
|
OrderId = model.OrderId,
|
||||||
//RedirectURL = redirect + "redirect",
|
//RedirectURL = redirect + "redirect",
|
||||||
|
NotificationEmail = model.NotificationEmail,
|
||||||
NotificationURL = model.NotificationUrl,
|
NotificationURL = model.NotificationUrl,
|
||||||
ItemDesc = model.ItemDesc,
|
ItemDesc = model.ItemDesc,
|
||||||
FullNotifications = true,
|
FullNotifications = true,
|
||||||
@@ -517,6 +530,21 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||||
|
[BitpayAPIConstraint(false)]
|
||||||
|
public async Task<IActionResult> Export(string format, string searchTerm = null)
|
||||||
|
{
|
||||||
|
var model = new ExportInvoicesModel
|
||||||
|
{
|
||||||
|
Format = format,
|
||||||
|
Invoices = await ListInvoicesProcess(searchTerm, 0, int.MaxValue)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Content(model.Process(), "application/" + format);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||||
[BitpayAPIConstraint(false)]
|
[BitpayAPIConstraint(false)]
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ namespace BTCPayServer.Controllers
|
|||||||
entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
|
entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
|
||||||
entity.ExtendedNotifications = invoice.ExtendedNotifications;
|
entity.ExtendedNotifications = invoice.ExtendedNotifications;
|
||||||
entity.NotificationURL = notificationUri?.AbsoluteUri;
|
entity.NotificationURL = notificationUri?.AbsoluteUri;
|
||||||
|
entity.NotificationEmail = invoice.NotificationEmail;
|
||||||
entity.BuyerInformation = Map<Invoice, BuyerInformation>(invoice);
|
entity.BuyerInformation = Map<Invoice, BuyerInformation>(invoice);
|
||||||
entity.PaymentTolerance = storeBlob.PaymentTolerance;
|
entity.PaymentTolerance = storeBlob.PaymentTolerance;
|
||||||
//Another way of passing buyer info to support
|
//Another way of passing buyer info to support
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Currency = model.Currency,
|
Currency = model.Currency,
|
||||||
ItemDesc = model.CheckoutDesc,
|
ItemDesc = model.CheckoutDesc,
|
||||||
OrderId = model.OrderId,
|
OrderId = model.OrderId,
|
||||||
BuyerEmail = model.NotifyEmail,
|
NotificationEmail = model.NotifyEmail,
|
||||||
NotificationURL = model.ServerIpn,
|
NotificationURL = model.ServerIpn,
|
||||||
RedirectURL = model.BrowserRedirect,
|
RedirectURL = model.BrowserRedirect,
|
||||||
FullNotifications = true
|
FullNotifications = true
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using BTCPayServer.Events;
|
|||||||
using NBXplorer;
|
using NBXplorer;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
|
using BTCPayServer.Services.Mails;
|
||||||
|
|
||||||
namespace BTCPayServer.HostedServices
|
namespace BTCPayServer.HostedServices
|
||||||
{
|
{
|
||||||
@@ -52,24 +53,44 @@ namespace BTCPayServer.HostedServices
|
|||||||
EventAggregator _EventAggregator;
|
EventAggregator _EventAggregator;
|
||||||
InvoiceRepository _InvoiceRepository;
|
InvoiceRepository _InvoiceRepository;
|
||||||
BTCPayNetworkProvider _NetworkProvider;
|
BTCPayNetworkProvider _NetworkProvider;
|
||||||
|
IEmailSender _EmailSender;
|
||||||
|
|
||||||
public InvoiceNotificationManager(
|
public InvoiceNotificationManager(
|
||||||
IBackgroundJobClient jobClient,
|
IBackgroundJobClient jobClient,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
ILogger<InvoiceNotificationManager> logger)
|
ILogger<InvoiceNotificationManager> logger,
|
||||||
|
IEmailSender emailSender)
|
||||||
{
|
{
|
||||||
Logger = logger as ILogger ?? NullLogger.Instance;
|
Logger = logger as ILogger ?? NullLogger.Instance;
|
||||||
_JobClient = jobClient;
|
_JobClient = jobClient;
|
||||||
_EventAggregator = eventAggregator;
|
_EventAggregator = eventAggregator;
|
||||||
_InvoiceRepository = invoiceRepository;
|
_InvoiceRepository = invoiceRepository;
|
||||||
_NetworkProvider = networkProvider;
|
_NetworkProvider = networkProvider;
|
||||||
|
_EmailSender = emailSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task Notify(InvoiceEntity invoice, int? eventCode = null, string name = null)
|
async Task Notify(InvoiceEntity invoice, int? eventCode = null, string name = null)
|
||||||
{
|
{
|
||||||
CancellationTokenSource cts = new CancellationTokenSource(10000);
|
CancellationTokenSource cts = new CancellationTokenSource(10000);
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(invoice.NotificationEmail))
|
||||||
|
{
|
||||||
|
// just extracting most important data for email body, merchant should query API back for full invoice based on Invoice.Id
|
||||||
|
var ipn = new
|
||||||
|
{
|
||||||
|
invoice.Id,
|
||||||
|
invoice.Status,
|
||||||
|
invoice.StoreId
|
||||||
|
};
|
||||||
|
// TODO: Consider adding info on ItemDesc and payment info (amount)
|
||||||
|
|
||||||
|
var emailBody = NBitcoin.JsonConverters.Serializer.ToString(ipn);
|
||||||
|
await _EmailSender.SendEmailAsync(
|
||||||
|
invoice.NotificationEmail, $"BtcPayServer Invoice Notification - ${invoice.StoreId}", emailBody);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(invoice.NotificationURL))
|
if (string.IsNullOrEmpty(invoice.NotificationURL))
|
||||||
@@ -264,15 +285,15 @@ namespace BTCPayServer.HostedServices
|
|||||||
sendRequest()
|
sendRequest()
|
||||||
.ContinueWith(t =>
|
.ContinueWith(t =>
|
||||||
{
|
{
|
||||||
if(t.Status == TaskStatus.RanToCompletion)
|
if (t.Status == TaskStatus.RanToCompletion)
|
||||||
{
|
{
|
||||||
completion.TrySetResult(t.Result);
|
completion.TrySetResult(t.Result);
|
||||||
}
|
}
|
||||||
if(t.Status == TaskStatus.Faulted)
|
if (t.Status == TaskStatus.Faulted)
|
||||||
{
|
{
|
||||||
completion.TrySetException(t.Exception);
|
completion.TrySetException(t.Exception);
|
||||||
}
|
}
|
||||||
if(t.Status == TaskStatus.Canceled)
|
if (t.Status == TaskStatus.Canceled)
|
||||||
{
|
{
|
||||||
completion.TrySetCanceled();
|
completion.TrySetCanceled();
|
||||||
}
|
}
|
||||||
@@ -289,7 +310,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
lock (_SendingRequestsByInvoiceId)
|
lock (_SendingRequestsByInvoiceId)
|
||||||
{
|
{
|
||||||
_SendingRequestsByInvoiceId.TryGetValue(id, out var executing2);
|
_SendingRequestsByInvoiceId.TryGetValue(id, out var executing2);
|
||||||
if(executing2 == sending)
|
if (executing2 == sending)
|
||||||
_SendingRequestsByInvoiceId.Remove(id);
|
_SendingRequestsByInvoiceId.Remove(id);
|
||||||
}
|
}
|
||||||
}, TaskScheduler.Default);
|
}, TaskScheduler.Default);
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[EmailAddress]
|
||||||
|
public string NotificationEmail
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
[Uri]
|
[Uri]
|
||||||
public string NotificationUrl
|
public string NotificationUrl
|
||||||
{
|
{
|
||||||
|
|||||||
42
BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs
Normal file
42
BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.InvoicingModels
|
||||||
|
{
|
||||||
|
public class ExportInvoicesModel
|
||||||
|
{
|
||||||
|
public InvoiceEntity[] Invoices { get; set; }
|
||||||
|
public string Format { get; set; }
|
||||||
|
|
||||||
|
public string Process()
|
||||||
|
{
|
||||||
|
if (String.Equals(Format, "json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return processJson();
|
||||||
|
else
|
||||||
|
throw new Exception("Export format not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string processJson()
|
||||||
|
{
|
||||||
|
foreach (var i in Invoices)
|
||||||
|
{
|
||||||
|
// removing error causing complex circular dependencies
|
||||||
|
i.Payments?.ForEach(a =>
|
||||||
|
{
|
||||||
|
a.Output = null;
|
||||||
|
a.Outpoint = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializerSett = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
|
||||||
|
var json = JsonConvert.SerializeObject(new { Invoices }, Formatting.Indented, serializerSett);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -142,5 +142,6 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||||||
public AddressModel[] Addresses { get; set; }
|
public AddressModel[] Addresses { get; set; }
|
||||||
public DateTimeOffset MonitoringDate { get; internal set; }
|
public DateTimeOffset MonitoringDate { get; internal set; }
|
||||||
public List<Data.InvoiceEventData> Events { get; internal set; }
|
public List<Data.InvoiceEventData> Events { get; internal set; }
|
||||||
|
public string NotificationEmail { get; internal set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"BTCPAY_NETWORK": "regtest",
|
"BTCPAY_NETWORK": "regtest",
|
||||||
"BTCPAY_BUNDLEJSCSS": "false",
|
"BTCPAY_BUNDLEJSCSS": "true",
|
||||||
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
|
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||||
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
|
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
|
||||||
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
|
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
|
||||||
|
|||||||
@@ -283,6 +283,11 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
public string NotificationEmail
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
public string NotificationURL
|
public string NotificationURL
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|||||||
@@ -57,14 +57,14 @@
|
|||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
<h2>Video tutorials</h2>
|
<h2>Video tutorials</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4 text-center">
|
<div class="col-md-2 text-center">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 text-center">
|
<div class="col-md-8 text-center">
|
||||||
<a href="https://www.youtube.com/channel/UCpG9WL6TJuoNfFVkaDMp9ug" target="_blank">
|
<a href="https://www.youtube.com/channel/UCpG9WL6TJuoNfFVkaDMp9ug" target="_blank">
|
||||||
<img src="~/img/youtube.png" height="225" width="400" />
|
<img src="~/img/youtube.png" class="img-fluid" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-center">
|
<div class="col-md-2 text-center">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,6 +44,11 @@
|
|||||||
<input asp-for="BuyerEmail" class="form-control" />
|
<input asp-for="BuyerEmail" class="form-control" />
|
||||||
<span asp-validation-for="BuyerEmail" class="text-danger"></span>
|
<span asp-validation-for="BuyerEmail" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="NotificationEmail" class="control-label"></label>
|
||||||
|
<input asp-for="NotificationEmail" class="form-control" />
|
||||||
|
<span asp-validation-for="NotificationEmail" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="NotificationUrl" class="control-label"></label>
|
<label asp-for="NotificationUrl" class="control-label"></label>
|
||||||
<input asp-for="NotificationUrl" class="form-control" />
|
<input asp-for="NotificationUrl" class="form-control" />
|
||||||
|
|||||||
@@ -87,6 +87,10 @@
|
|||||||
<th>Total fiat due</th>
|
<th>Total fiat due</th>
|
||||||
<td>@Model.Fiat</td>
|
<td>@Model.Fiat</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Notification Email</th>
|
||||||
|
<td>@Model.NotificationEmail</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Notification Url</th>
|
<th>Notification Url</th>
|
||||||
<td>@Model.NotificationUrl</td>
|
<td>@Model.NotificationUrl</td>
|
||||||
@@ -167,14 +171,14 @@
|
|||||||
<th class="text-right">Rate</th>
|
<th class="text-right">Rate</th>
|
||||||
<th class="text-right">Paid</th>
|
<th class="text-right">Paid</th>
|
||||||
<th class="text-right">Due</th>
|
<th class="text-right">Due</th>
|
||||||
@if(Model.StatusException == "paidOver")
|
@if (Model.StatusException == "paidOver")
|
||||||
{
|
{
|
||||||
<th class="text-right">Overpaid</th>
|
<th class="text-right">Overpaid</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var payment in Model.CryptoPayments)
|
@foreach (var payment in Model.CryptoPayments)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@payment.PaymentMethod</td>
|
<td>@payment.PaymentMethod</td>
|
||||||
@@ -182,7 +186,7 @@
|
|||||||
<td class="text-right">@payment.Rate</td>
|
<td class="text-right">@payment.Rate</td>
|
||||||
<td class="text-right">@payment.Paid</td>
|
<td class="text-right">@payment.Paid</td>
|
||||||
<td class="text-right">@payment.Due</td>
|
<td class="text-right">@payment.Due</td>
|
||||||
@if(Model.StatusException == "paidOver")
|
@if (Model.StatusException == "paidOver")
|
||||||
{
|
{
|
||||||
<td class="text-right">@payment.Overpaid</td>
|
<td class="text-right">@payment.Overpaid</td>
|
||||||
}
|
}
|
||||||
@@ -192,7 +196,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if(Model.OnChainPayments.Count > 0)
|
@if (Model.OnChainPayments.Count > 0)
|
||||||
{
|
{
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -207,7 +211,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var payment in Model.OnChainPayments)
|
@foreach (var payment in Model.OnChainPayments)
|
||||||
{
|
{
|
||||||
var replaced = payment.Replaced ? "class='linethrough'" : "";
|
var replaced = payment.Replaced ? "class='linethrough'" : "";
|
||||||
<tr @replaced>
|
<tr @replaced>
|
||||||
@@ -226,7 +230,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if(Model.OffChainPayments.Count > 0)
|
@if (Model.OffChainPayments.Count > 0)
|
||||||
{
|
{
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -239,7 +243,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var payment in Model.OffChainPayments)
|
@foreach (var payment in Model.OffChainPayments)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@payment.Crypto</td>
|
<td>@payment.Crypto</td>
|
||||||
@@ -262,7 +266,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var address in Model.Addresses)
|
@foreach (var address in Model.Addresses)
|
||||||
{
|
{
|
||||||
var current = address.Current ? "font-weight-bold" : "";
|
var current = address.Current ? "font-weight-bold" : "";
|
||||||
<tr>
|
<tr>
|
||||||
@@ -286,7 +290,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var evt in Model.Events)
|
@foreach (var evt in Model.Events)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@evt.Timestamp.ToBrowserDate()</td>
|
<td>@evt.Timestamp.ToBrowserDate()</td>
|
||||||
|
|||||||
@@ -32,10 +32,27 @@
|
|||||||
If you want all confirmed and complete invoices, you can duplicate a filter <code>status:confirmed status:complete</code>.
|
If you want all confirmed and complete invoices, you can duplicate a filter <code>status:confirmed status:complete</code>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row no-gutter" style="margin-bottom: 5px;">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<a asp-action="CreateInvoice" class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Create a new invoice</a>
|
||||||
|
|
||||||
|
<a class="btn btn-primary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
Export
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||||
|
<a asp-action="Export" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">JSON</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-8">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<form asp-action="SearchInvoice" method="post">
|
<form asp-action="SearchInvoice" method="post" style="float:right;">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input asp-for="SearchTerm" class="form-control" />
|
<input asp-for="SearchTerm" class="form-control" style="width:300px;" />
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button type="submit" class="btn btn-primary" title="Search invoice">
|
<button type="submit" class="btn btn-primary" title="Search invoice">
|
||||||
<span class="fa fa-search"></span> Search
|
<span class="fa fa-search"></span> Search
|
||||||
@@ -50,7 +67,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a asp-action="CreateInvoice" class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Create a new invoice</a>
|
|
||||||
<table class="table table-sm table-responsive-md">
|
<table class="table table-sm table-responsive-md">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -63,12 +79,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach(var invoice in Model.Invoices)
|
@foreach (var invoice in Model.Invoices)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@invoice.Date.ToTimeAgo()</td>
|
<td>@invoice.Date.ToTimeAgo()</td>
|
||||||
<td>
|
<td>
|
||||||
@if(invoice.RedirectUrl != string.Empty)
|
@if (invoice.RedirectUrl != string.Empty)
|
||||||
{
|
{
|
||||||
<a href="@invoice.RedirectUrl">@invoice.OrderId</a>
|
<a href="@invoice.RedirectUrl">@invoice.OrderId</a>
|
||||||
}
|
}
|
||||||
@@ -78,7 +94,7 @@
|
|||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>@invoice.InvoiceId</td>
|
<td>@invoice.InvoiceId</td>
|
||||||
@if(invoice.Status == "paid")
|
@if (invoice.Status == "paid")
|
||||||
{
|
{
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
@@ -97,7 +113,7 @@
|
|||||||
}
|
}
|
||||||
<td style="text-align:right">@invoice.AmountCurrency</td>
|
<td style="text-align:right">@invoice.AmountCurrency</td>
|
||||||
<td style="text-align:right">
|
<td style="text-align:right">
|
||||||
@if(invoice.ShowCheckout)
|
@if (invoice.ShowCheckout)
|
||||||
{
|
{
|
||||||
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a> <span>-</span>
|
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a> <span>-</span>
|
||||||
}<a asp-action="Invoice" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
}<a asp-action="Invoice" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
||||||
@@ -107,7 +123,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<span>
|
<span>
|
||||||
@if(Model.Skip != 0)
|
@if (Model.Skip != 0)
|
||||||
{
|
{
|
||||||
<a href="@Url.Action("ListInvoices", new
|
<a href="@Url.Action("ListInvoices", new
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ $(document).ready(function () {
|
|||||||
progressStart(srvModel.maxTimeSeconds); // Progress bar
|
progressStart(srvModel.maxTimeSeconds); // Progress bar
|
||||||
|
|
||||||
if (srvModel.requiresRefundEmail && !validateEmail(srvModel.customerEmail))
|
if (srvModel.requiresRefundEmail && !validateEmail(srvModel.customerEmail))
|
||||||
emailForm(); // Email form Display
|
showEmailForm();
|
||||||
else
|
else
|
||||||
hideEmailForm();
|
hideEmailForm();
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
// Email Form
|
// Email Form
|
||||||
// Setup Email mode
|
// Setup Email mode
|
||||||
function emailForm() {
|
function showEmailForm() {
|
||||||
$(".modal-dialog").addClass("enter-purchaser-email");
|
$(".modal-dialog").addClass("enter-purchaser-email");
|
||||||
|
|
||||||
$("#emailAddressForm .action-button").click(function () {
|
$("#emailAddressForm .action-button").click(function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user