@model BTCPayServer.Models.InvoicingModels.InvoiceReceiptViewModel @functions { public bool IsManualEntryCart(Dictionary<string, object> additionalData) { _ = additionalData.TryGetValue("cart", out var data) || additionalData.TryGetValue("Cart", out data); if (data is Dictionary<string, object> cart) { return cart.Count == 1 && cart.ContainsKey("Manual entry 1"); } return false; } } @using BTCPayServer.Client.Models @using BTCPayServer.Components.QRCode @using BTCPayServer.Services @inject DisplayFormatter DisplayFormatter @{ Layout = null; ViewData["Title"] = $"Receipt from {Model.StoreName}"; var isProcessing = Model.Status == InvoiceStatus.Processing; var isFreeInvoice = (Model.Status == InvoiceStatus.New && Model.Amount == 0); var isSettled = Model.Status == InvoiceStatus.Settled; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="icon" href="~/favicon.ico" type="image/x-icon"> <meta name="robots" content="noindex"> <title>@ViewData["Title"]</title> @* CSS *@ <link href="~/main/bootstrap/bootstrap.css" asp-append-version="true" rel="stylesheet" /> <link href="~/main/fonts/OpenSans.css" asp-append-version="true" rel="stylesheet" /> <link href="~/main/layout.css" asp-append-version="true" rel="stylesheet" /> <link href="~/main/site.css" asp-append-version="true" rel="stylesheet" /> <link href="~/main/themes/default.css" asp-append-version="true" rel="stylesheet" /> <meta name="robots" content="noindex,nofollow"> @if (isProcessing) { <script type="text/javascript"> setTimeout(() => { window.location.reload(); }, 10000); </script> } else if (isFreeInvoice) { <script type="text/javascript"> setTimeout(() => { window.location.reload(); }, 2000); </script> } <style> h1 { margin: 0; } .qr-code { width: 128px; } /* change height as you like */ @@media print { body { width: 58mm; margin: 0; padding: 0; } .p-1 { padding: 1mm !important; } .m-1 { margin: 1mm !important; } } /* this line is needed for fixing Chrome's bug */ @@page { margin-left: 0px; margin-right: 0px; margin-top: 0px; margin-bottom: 0px; } </style> </head> <body class="m-0 p-0 bg-white"> <center> <partial name="_StoreHeader" model="(Model.StoreName, Model.StoreBranding)" /> <div id="InvoiceSummary" style="max-width:600px"> @if (isProcessing) { <div class="lead text-center fw-semibold" id="invoice-processing"> The invoice has detected a payment but is still waiting to be settled. </div> } else if (!isSettled) { <div class="lead text-center fw-semibold" id="invoice-unsettled"> The invoice is not settled. </div> } else { var hasCart = Model.CartData?.Any() is true; <div id="PaymentDetails"> <div class="my-2 text-center small"> @if (!string.IsNullOrEmpty(Model.OrderId)) { <div>Order ID: @Model.OrderId</div> } @Model.Timestamp.ToBrowserDate() </div> <table class="table table-borderless table-sm small my-0"> @if (Model.AdditionalData?.Any() is true) { @foreach (var (key, value) in Model.AdditionalData) { <tr> <td class="text-secondary">@key</td> <td class="text-end">@value</td> </tr> } <tr> <td colspan="2"><hr class="w-100 my-0"/></td> </tr> } @if (hasCart && !IsManualEntryCart(Model.AdditionalData)) { _ = Model.CartData.TryGetValue("cart", out var cart) || Model.CartData.TryGetValue("Cart", out cart); var hasTotal = Model.CartData.TryGetValue("total", out var total) || Model.CartData.TryGetValue("Total", out total); var hasSubtotal = Model.CartData.TryGetValue("subtotal", out var subtotal) || Model.CartData.TryGetValue("subTotal", out subtotal) || Model.CartData.TryGetValue("Subtotal", out subtotal); var hasDiscount = Model.CartData.TryGetValue("discount", out var discount) || Model.CartData.TryGetValue("Discount", out discount); var hasTip = Model.CartData.TryGetValue("tip", out var tip) || Model.CartData.TryGetValue("Tip", out tip); if (cart is Dictionary<string, object> { Keys.Count: > 0 } cartDict) { @foreach (var (key, value) in cartDict) { <tr> <td class="text-secondary">@key</td> <td class="text-end">@value</td> </tr> } } else if (cart is ICollection<object> { Count: > 0 } cartCollection) { @foreach (var value in cartCollection) { <tr> <td class="text-end">@value</td> </tr> } } if (hasSubtotal && (hasDiscount || hasTip)) { <tr> <td colspan="2"><hr class="w-100 my-0"/></td> </tr> <tr> <td class="text-secondary">Subtotal</td> <td class="text-end">@subtotal</td> </tr> } if (hasDiscount) { <tr> <td class="text-secondary">Discount</td> <td class="text-end">@discount</td> </tr> } if (hasTip) { <tr> <td class="text-secondary">Tip</td> <td class="text-end">@tip</td> </tr> } if (hasTotal) { <tr> <td colspan="2"><hr class="w-100 my-0"/></td> </tr> <tr> <th class="text-secondary">Total</th> <td class="text-end fw-semibold">@total</td> </tr> } } else { <tr> <td class="text-nowrap text-secondary">Total</td> <td class="text-end fw-semibold">@DisplayFormatter.Currency(Model.Amount, Model.Currency, DisplayFormatter.CurrencyFormat.Symbol)</td> </tr> } @if (Model.Payments?.Any() is true) { <tr> <td colspan="2"><hr class="w-100 my-0"/></td> </tr> @for (var i = 0; i < Model.Payments.Count; i++) { var payment = Model.Payments[i]; @if (Model.Payments.Count > 1) { <tr> <td colspan="2" class="text-nowrap text-secondary">Payment @(i + 1)</td> </tr> <tr> <td class="text-nowrap">Received</td> <td>@payment.ReceivedDate.ToBrowserDate()</td> </tr> } <tr> <td class="text-nowrap text-secondary">@(Model.Payments.Count == 1 ? "Paid" : "")</td> <td class="text-end">@payment.AmountFormatted</td> </tr> <tr> <td colspan="2" class="text-end">@payment.PaidFormatted</td> </tr> <tr> <td class="text-nowrap text-secondary">Rate</td> <td class="text-end">@payment.RateFormatted</td> </tr> @if (!string.IsNullOrEmpty(payment.Destination)) { <tr> <td class="text-nowrap text-secondary">Destination</td> <td class="text-break"> @if (payment.Destination.Length > 69) { <span> <span>@payment.Destination[..19]</span> <span>...</span> <span>@payment.Destination.Substring(payment.Destination.Length - 20, 20)</span> </span> } else { @payment.Destination } </td> </tr> } @if (!string.IsNullOrEmpty(payment.PaymentProof)) { <tr> <td class="text-nowrap text-secondary">Pay Proof</td> <td class="text-break">@payment.PaymentProof</td> </tr> } } <tr> <td colspan="2"><hr class="w-100 my-0"/></td> </tr> } </table> </div> if (Model.ReceiptOptions.ShowQR is true) { <vc:qr-code data="@Context.Request.GetCurrentUrl()" size="128" /> } } </div> <div class="store-footer p-3"> <a class="store-powered-by" style="color:#000;">Powered by <partial name="_StoreFooterLogo" /></a> </div> <hr class="w-100 my-0 bg-none"/> </center> </body> <script src="~/main/utils.js" asp-append-version="true"></script> <script> formatDateTimes(); window.print(); </script> </html>