btcpayserver/BTCPayServer/Views/Invoice/Invoice.cshtml
Andrew Camilleri 04726b3ee4
Payouts: Detect External OnChain Payouts (#2462)
* Refactor and decouple Payout logic

So that we can support lightning + external payout payments

Fixes & refactoring

almost there

final

Remove uneeded payment method checks

Refactor payouts to handle custom payment method specific actions

External onchain payments to approved payouts will now require "confirmation" from the merchant that it was sent by them.

add pill tabs for payout status

* Improve some UX around feature

* add test and some fixes

* Only listen to address tracked source and determine based on wallet get tx call from nbx

* Simplify isInternal for Payout detection

* fix test

* Fix Noreferrer test

* Make EnsureNewLightningInvoiceOnPartialPayment more resilient

* Make notifications section test more resilient in CanUsePullPaymentsViaUI
2021-07-16 09:57:37 +02:00

327 lines
14 KiB
Text

@model InvoiceDetailsModel
@{
ViewData["Title"] = $"Invoice {Model.Id}";
}
@section PageHeadContent {
<meta name="robots" content="noindex,nofollow">
<style>
#posData td > table:last-child { margin-bottom: 0 !important; }
#posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; }
</style>
}
<section class="invoice-details">
<div class="container">
<partial name="_StatusMessage" />
<div class="row mb-4">
<h2 class="col-xs-12 col-lg-9 mb-4 mb-lg-0">@ViewData["Title"]</h2>
<div class="col-xs-12 col-lg-3 mb-2 mb-lg-0 text-lg-end">
<div class="d-inline-flex">
@if (Model.ShowCheckout)
{
<a asp-action="Checkout" class="invoice-checkout-link btn btn-primary text-nowrap ms-2" asp-route-invoiceId="@Model.Id">
<i class="fa fa-qrcode"></i>
Checkout
</a>
}
@if (Model.CanRefund)
{
<a id="refundlink" class="btn btn-success text-nowrap ms-2" asp-action="Refund" asp-route-invoiceId="@Context.GetRouteValue("invoiceId")"><span class="fa fa-undo"></span> Issue refund</a>
}
else
{
<button href="#" class="btn btn-secondary text-nowrap ms-2" data-bs-toggle="tooltip" title="You can only issue refunds on invoices with confirmed payments" disabled><span class="fa fa-undo"></span> Issue refund</button>
}
<form class="p-0 ms-2" asp-action="ToggleArchive" asp-route-invoiceId="@Model.Id" method="post">
<button type="submit" class="btn @(Model.Archived ? "btn-warning" : "btn btn-danger")" id="btn-archive-toggle">
@if (Model.Archived)
{
<span class="text-nowrap" data-bs-toggle="tooltip" title="Unarchive this invoice"><i class="ms-1 fa fa-close"></i> Archived</span>
}
else
{
<span class="text-nowrap" data-bs-toggle="tooltip" title="Archive this invoice so that it does not appear in the invoice list by default"><i class="ms-1 fa fa-archive"></i> Archive</span>
}
</button>
</form>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<h3 class="mb-3">Invoice Information</h3>
<table class="table table-sm table-responsive-md removetopborder">
<tr>
<th>Store</th>
<td><a href="@Model.StoreLink" rel="noreferrer noopener">@Model.StoreName</a></td>
</tr>
<tr>
<th>Invoice Id</th>
<td>@Model.Id</td>
</tr>
<tr>
<th>Order Id</th>
<td>
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderUrl))
{
@Model.TypedMetadata.OrderId
}
else
{
<a href="@Model.TypedMetadata.OrderUrl" rel="noreferrer noopener" target="_blank">
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderId))
{
<span>View Order</span>
}
else
{
@Model.TypedMetadata.OrderId
}
</a>
}
</td>
</tr>
<tr>
<th>Payment Request Id</th>
<td><a href="@Model.PaymentRequestLink" rel="noreferrer noopener">@Model.TypedMetadata.PaymentRequestId</a></td>
</tr>
<tr>
<th>State</th>
<td>@Model.State</td>
</tr>
<tr>
<th>Created date</th>
<td>@Model.CreatedDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Expiration date</th>
<td>@Model.ExpirationDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Monitoring date</th>
<td>@Model.MonitoringDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Transaction speed</th>
<td>@Model.TransactionSpeed</td>
</tr>
<tr>
<th>Total fiat due</th>
<td>@Model.Fiat</td>
</tr>
@if (!string.IsNullOrEmpty(Model.RefundEmail))
{
<tr>
<th>Refund email</th>
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.NotificationUrl))
{
<tr>
<th>Notification Url</th>
<td>@Model.NotificationUrl</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
{
<tr>
<th>Redirect Url</th>
<td><a href="@Model.RedirectUrl" rel="noreferrer noopener">@Model.RedirectUrl</a></td>
</tr>
}
</table>
</div>
<div class="col-md-6 mb-4">
<h3 class="mb-3">Buyer Information</h3>
<table class="table table-sm table-responsive-md removetopborder">
<tr>
<th>Name</th>
<td>@Model.TypedMetadata.BuyerName</td>
</tr>
<tr>
<th>Email</th>
<td><a href="mailto:@Model.TypedMetadata.BuyerEmail">@Model.TypedMetadata.BuyerEmail</a></td>
</tr>
<tr>
<th>Phone</th>
<td>@Model.TypedMetadata.BuyerPhone</td>
</tr>
<tr>
<th>Address 1</th>
<td>@Model.TypedMetadata.BuyerAddress1</td>
</tr>
<tr>
<th>Address 2</th>
<td>@Model.TypedMetadata.BuyerAddress2</td>
</tr>
<tr>
<th>City</th>
<td>@Model.TypedMetadata.BuyerCity</td>
</tr>
<tr>
<th>State</th>
<td>@Model.TypedMetadata.BuyerState</td>
</tr>
<tr>
<th>Country</th>
<td>@Model.TypedMetadata.BuyerCountry</td>
</tr>
<tr>
<th>Zip</th>
<td>@Model.TypedMetadata.BuyerZip</td>
</tr>
</table>
@if (Model.PosData.Count == 0)
{
<h3 class="mb-3">Product Information</h3>
<table class="table table-sm table-responsive-md removetopborder">
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
{
<tr>
<th>Item code</th>
<td>@Model.TypedMetadata.ItemCode</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
{
<tr>
<th>Item Description</th>
<td>@Model.TypedMetadata.ItemDesc</td>
</tr>
}
<tr>
<th>Price</th>
<td>@Model.Fiat</td>
</tr>
<tr>
<th>Tax included</th>
<td>@Model.TaxIncluded</td>
</tr>
</table>
}
</div>
</div>
@if (Model.PosData.Count != 0)
{
<div class="row">
<div class="col-md-6 mb-4">
<h3 class="mb-3">Product information</h3>
<table class="table table-sm table-responsive-md removetopborder">
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
{
<tr>
<th>Item code</th>
<td>@Model.TypedMetadata.ItemCode</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
{
<tr>
<th>Item Description</th>
<td>@Model.TypedMetadata.ItemDesc</td>
</tr>
}
<tr>
<th>Price</th>
<td>@Model.Fiat</td>
</tr>
<tr>
<th>Tax included</th>
<td>@Model.TaxIncluded</td>
</tr>
</table>
</div>
<div class="col-md-6 mb-4" id="posData">
<h3 class="mb-3">Point of Sale Data</h3>
<partial name="PosData" model="(Model.PosData, 1)" />
</div>
</div>
}
<partial name="ListInvoicesPaymentsPartial" model="(Model, true)" />
@if (Model.Deliveries.Count != 0)
{
<h3 class="mb-3">Webhook deliveries</h3>
<ul class="list-group mb-5">
@foreach (var delivery in Model.Deliveries)
{
<li class="list-group-item ">
<form
asp-action="RedeliverWebhook"
asp-route-storeId="@Model.StoreId"
asp-route-invoiceId="@Model.Id"
asp-route-deliveryId="@delivery.Id"
method="post">
<div class="d-flex align-items-center">
<span class="d-flex align-items-center flex-fill me-3">
@if (delivery.Success)
{
<span class="d-flex align-items-center fa fa-check text-success" title="Success"></span>
}
else
{
<span class="d-flex align-items-center fa fa-times text-danger" title="@delivery.ErrorMessage"></span>
}
<span class="ms-3">
<a
asp-action="WebhookDelivery"
asp-route-invoiceId="@Model.Id"
asp-route-deliveryId="@delivery.Id"
class="btn btn-link delivery-content" target="_blank">
@delivery.Id
</a>
<span class="text-light mx-2">|</span>
<span class="small text-muted">@delivery.Type</span>
</span>
</span>
<span class="d-flex align-items-center">
<strong class="d-flex align-items-center text-muted small">
@delivery.Time.ToBrowserDate()
</strong>
<button id="#redeliver-@delivery.Id"
type="submit"
class="btn btn-info py-1 ms-3 redeliver">
Redeliver
</button>
</span>
</div>
</form>
</li>
}
</ul>
}
<div class="row">
<div class="col-md-12">
<h3 class="mb-0">Events</h3>
<table class="table table-sm table-responsive-md">
<thead class="thead-inverse">
<tr>
<th>Date</th>
<th>Message</th>
</tr>
</thead>
<tbody>
@foreach (var evt in Model.Events)
{
<tr class="text-@evt.GetCssClass()">
<td>@evt.Timestamp.ToBrowserDate()</td>
<td>@evt.Message</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</section>