mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Receipt: Add payment proof (#4782)
* Receipt: Add payment proof Closes #4685. * shice * Add truncate-center component * Improve view * Hide button and link when printed * Describe component * Remove transaction ID from UI * Remove modification to interface --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
parent
c53d5272d6
commit
de9ac9fd43
7 changed files with 166 additions and 31 deletions
17
BTCPayServer/Components/TruncateCenter/Default.cshtml
Normal file
17
BTCPayServer/Components/TruncateCenter/Default.cshtml
Normal file
|
@ -0,0 +1,17 @@
|
|||
@model BTCPayServer.Components.TruncateCenter.TruncateCenterViewModel
|
||||
<span class="truncate-center @Model.Classes">
|
||||
<span class="truncate-center-truncated" @(Model.Truncated != Model.Text ? $"data-bs-toggle=tooltip title={Model.Text}" : "")>@Model.Truncated</span>
|
||||
<span class="truncate-center-text">@Model.Text</span>
|
||||
@if (Model.Copy)
|
||||
{
|
||||
<button type="button" class="btn btn-link p-0" data-clipboard="@Model.Text">
|
||||
<vc:icon symbol="copy" />
|
||||
</button>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Link))
|
||||
{
|
||||
<a href="@Model.Link" rel="noreferrer noopener" target="_blank">
|
||||
<vc:icon symbol="info" />
|
||||
</a>
|
||||
}
|
||||
</span>
|
29
BTCPayServer/Components/TruncateCenter/TruncateCenter.cs
Normal file
29
BTCPayServer/Components/TruncateCenter/TruncateCenter.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Components.TruncateCenter;
|
||||
|
||||
/// <summary>
|
||||
/// Truncates long strings in the center with ellipsis: Turns e.g. a BOLT11 into "lnbcrt7…q2ns60y"
|
||||
/// </summary>
|
||||
/// <param name="text">The full text, e.g. a Bitcoin address or BOLT11</param>
|
||||
/// <param name="link">Optional link, e.g. a block explorer URL</param>
|
||||
/// <param name="classes">Optional additional CSS classes</param>
|
||||
/// <param name="padding">The number of characters to show on each side</param>
|
||||
/// <param name="copy">Display a copy button</param>
|
||||
/// <returns>HTML with truncated string</returns>
|
||||
public class TruncateCenter : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke(string text, string link = null, string classes = null, int padding = 7, bool copy = true)
|
||||
{
|
||||
var vm = new TruncateCenterViewModel
|
||||
{
|
||||
Classes = classes,
|
||||
Padding = padding,
|
||||
Copy = copy,
|
||||
Text = text,
|
||||
Link = link,
|
||||
Truncated = text.Length > 2 * padding ? $"{text[..padding]}…{text[^padding..]}" : text
|
||||
};
|
||||
return View(vm);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace BTCPayServer.Components.TruncateCenter
|
||||
{
|
||||
public class TruncateCenterViewModel
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public string Truncated { get; set; }
|
||||
public string Classes { get; set; }
|
||||
public string Link { get; set; }
|
||||
public int Padding { get; set; }
|
||||
public bool Copy { get; set; }
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ using BTCPayServer.Models;
|
|||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
|
@ -235,7 +236,9 @@ namespace BTCPayServer.Controllers
|
|||
PaymentMethod = paymentMethodId.ToPrettyString(),
|
||||
Link = link,
|
||||
Id = txId,
|
||||
Destination = paymentData.GetDestination()
|
||||
Destination = paymentData.GetDestination(),
|
||||
PaymentProof = GetPaymentProof(paymentData),
|
||||
PaymentType = paymentData.GetPaymentType()
|
||||
};
|
||||
})
|
||||
.Where(payment => payment != null)
|
||||
|
@ -247,6 +250,17 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
private string? GetPaymentProof(CryptoPaymentData paymentData)
|
||||
{
|
||||
return paymentData switch
|
||||
{
|
||||
BitcoinLikePaymentData b => b.Outpoint.ToString(),
|
||||
LightningPaymentData l => l.Preimage,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private string? GetTransactionLink(PaymentMethodId paymentMethodId, string txId)
|
||||
{
|
||||
var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.Linq;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Validation;
|
||||
|
@ -208,6 +209,8 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||
public string Link { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Destination { get; set; }
|
||||
public string PaymentProof { get; set; }
|
||||
public PaymentType PaymentType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
@using BTCPayServer.Services
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Abstractions.TagHelpers
|
||||
@using BTCPayServer.Payments
|
||||
@inject BTCPayServerEnvironment Env
|
||||
@inject DisplayFormatter DisplayFormatter
|
||||
@{
|
||||
|
@ -27,6 +28,8 @@
|
|||
}
|
||||
<style>
|
||||
#InvoiceSummary { gap: var(--btcpay-space-l); }
|
||||
#PaymentDetails table tbody tr:first-child td { padding-top: 1rem; }
|
||||
#PaymentDetails table tbody:not(:last-child) tr:last-child > th,td { padding-bottom: 1rem; }
|
||||
#posData td > table:last-child { margin-bottom: 0 !important; }
|
||||
#posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; }
|
||||
</style>
|
||||
|
@ -114,38 +117,47 @@
|
|||
<div id="PaymentDetails" class="bg-tile p-3 p-sm-4 rounded">
|
||||
<h2 class="h4 mb-3 d-print-none">Payment Details</h2>
|
||||
<div class="table-responsive my-0">
|
||||
<table class="table my-0">
|
||||
<tr>
|
||||
<th class="fw-normal text-secondary">Destination</th>
|
||||
<th class="fw-normal text-secondary">Received</th>
|
||||
<th class="fw-normal text-secondary text-end">Paid</th>
|
||||
<th class="fw-normal text-secondary text-end">Rate</th>
|
||||
<th class="fw-normal text-secondary text-end">Payment</th>
|
||||
</tr>
|
||||
<table class="table table-borderless my-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="fw-normal text-secondary">Date</th>
|
||||
<th class="fw-normal text-secondary">Payment</th>
|
||||
<th class="fw-normal text-secondary text-end">Paid</th>
|
||||
<th class="fw-normal text-secondary text-end">Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var payment in Model.Payments)
|
||||
{
|
||||
<tr class="table-borderless table-light">
|
||||
<td class="text-break">
|
||||
<code>@payment.Destination</code>
|
||||
</td>
|
||||
<td>@payment.ReceivedDate.ToString("g")</td>
|
||||
<td class="text-end">@payment.PaidFormatted</td>
|
||||
<td class="text-end">@payment.RateFormatted</td>
|
||||
<td class="text-end text-nowrap">@payment.Amount @payment.PaymentMethod</td>
|
||||
</tr>
|
||||
<tr class="table-borderless table-light">
|
||||
<td class="fw-normal" colspan="5">
|
||||
<span class="text-secondary">Transaction Id:</span>
|
||||
@if (!string.IsNullOrEmpty(payment.Link))
|
||||
{
|
||||
<a href="@payment.Link" class="text-print-default text-break" rel="noreferrer noopener" target="_blank">@payment.Id</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-break">@payment.Id</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-nowrap">@payment.ReceivedDate.ToBrowserDate()</td>
|
||||
<td class="text-nowrap">@payment.Amount @payment.PaymentMethod</td>
|
||||
<td class="text-end text-nowrap">@payment.PaidFormatted</td>
|
||||
<td class="text-end text-nowrap">@payment.RateFormatted</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(payment.Destination))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-normal text-nowrap text-secondary">
|
||||
Destination
|
||||
</th>
|
||||
<td class="fw-normal" colspan="3">
|
||||
<vc:truncate-center text="@payment.Destination" classes="truncate-center-id" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(payment.PaymentProof))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-normal text-nowrap text-secondary">
|
||||
Payment Proof
|
||||
</th>
|
||||
<td class="fw-normal" colspan="3">
|
||||
<vc:truncate-center text="@payment.PaymentProof" link="@payment.Link" classes="truncate-center-id" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -912,6 +912,54 @@ input.ts-wrapper.form-control:not(.ts-hidden-accessible,.ts-inline) {
|
|||
padding-left: var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
/* Truncated text */
|
||||
.truncate-center {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--btcpay-space-s);
|
||||
}
|
||||
|
||||
.truncate-center-id {
|
||||
font-family: var(--btcpay-font-monospace);
|
||||
font-size: .875em;
|
||||
}
|
||||
|
||||
.truncate-center-text {
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.truncate-center a,
|
||||
.truncate-center button,
|
||||
.truncate-center-truncated {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.truncate-center button.btn .icon {
|
||||
--btn-icon-size: 1em;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
.truncate-center-id {
|
||||
background: var(--btcpay-neutral-100);
|
||||
border: 1px solid var(--btcpay-neutral-200);
|
||||
border-radius: var(--btcpay-border-radius-l);
|
||||
padding: var(--btcpay-space-xs) var(--btcpay-space-s);
|
||||
}
|
||||
|
||||
.truncate-center-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.truncate-center a,
|
||||
.truncate-center button,
|
||||
.truncate-center-truncated {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy */
|
||||
[data-clipboard],
|
||||
[data-clipboard] input[readonly] {
|
||||
|
|
Loading…
Add table
Reference in a new issue