littleshop/LittleShop/Areas/Admin/Views/Payments/Index.cshtml
SysAdmin a2247d7c02
Some checks failed
Build and Deploy LittleShop / Build TeleBot Docker Image (push) Failing after 11s
Build and Deploy LittleShop / Build LittleShop Docker Image (push) Failing after 15s
Build and Deploy LittleShop / Deploy to Production VPS (Manual Only) (push) Has been skipped
Build and Deploy LittleShop / Deploy to Pre-Production (CT109) (push) Has been skipped
feat: Add customer management, payments, and push notifications with security enhancements
Major Feature Additions:
- Customer management: Full CRUD with data export and privacy compliance
- Payment management: Centralized payment tracking and administration
- Push notification subscriptions: Manage and track web push subscriptions

Security Enhancements:
- IP whitelist middleware for administrative endpoints
- Data retention service with configurable policies
- Enhanced push notification security documentation
- Security fixes progress tracking (2025-11-14)

UI/UX Improvements:
- Enhanced navigation with improved mobile responsiveness
- Updated admin dashboard with order status counts
- Improved product CRUD forms
- New customer and payment management interfaces

Backend Improvements:
- Extended customer service with data export capabilities
- Enhanced order service with status count queries
- Improved crypto payment service with better error handling
- Updated validators and configuration

Documentation:
- DEPLOYMENT_NGINX_GUIDE.md: Nginx deployment instructions
- IP_STORAGE_ANALYSIS.md: IP storage security analysis
- PUSH_NOTIFICATION_SECURITY.md: Push notification security guide
- UI_UX_IMPROVEMENT_PLAN.md: Planned UI/UX enhancements
- UI_UX_IMPROVEMENTS_COMPLETED.md: Completed improvements

Cleanup:
- Removed temporary database WAL files
- Removed stale commit message file

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 19:33:02 +00:00

261 lines
12 KiB
Plaintext

@model IEnumerable<LittleShop.DTOs.CryptoPaymentDto>
@{
ViewData["Title"] = "Payment Transactions";
var orders = ViewData["Orders"] as Dictionary<Guid, LittleShop.DTOs.OrderDto> ?? new Dictionary<Guid, LittleShop.DTOs.OrderDto>();
var currentStatus = ViewData["CurrentStatus"] as string ?? "";
}
<div class="row mb-3">
<div class="col">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="@Url.Action("Index", "Dashboard")">Dashboard</a></li>
<li class="breadcrumb-item active">Payment Transactions</li>
</ol>
</nav>
</div>
</div>
<div class="row mb-3">
<div class="col">
<h1><i class="fas fa-wallet"></i> Payment Transactions</h1>
<p class="text-muted mb-0">View all cryptocurrency payment transactions and their statuses</p>
</div>
</div>
<!-- Status Filter Tabs -->
<div class="row mb-4">
<div class="col">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link @(string.IsNullOrEmpty(currentStatus) ? "active" : "")"
href="@Url.Action("Index")">
All Payments
<span class="badge bg-secondary ms-1">@Model.Count()</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link @(currentStatus == "Pending" ? "active" : "")"
href="@Url.Action("Index", new { status = "Pending" })">
<i class="fas fa-clock"></i> Pending
<span class="badge bg-warning ms-1">@Model.Count(p => p.Status == LittleShop.Enums.PaymentStatus.Pending)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link @(currentStatus == "Paid" ? "active" : "")"
href="@Url.Action("Index", new { status = "Paid" })">
<i class="fas fa-check-circle"></i> Paid
<span class="badge bg-success ms-1">@Model.Count(p => p.Status == LittleShop.Enums.PaymentStatus.Paid)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link @(currentStatus == "Expired" ? "active" : "")"
href="@Url.Action("Index", new { status = "Expired" })">
<i class="fas fa-times-circle"></i> Expired
<span class="badge bg-danger ms-1">@Model.Count(p => p.Status == LittleShop.Enums.PaymentStatus.Expired)</span>
</a>
</li>
</ul>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card border-primary">
<div class="card-body">
<h6 class="text-muted mb-2">Total Transactions</h6>
<h3 class="mb-0">@Model.Count()</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-success">
<div class="card-body">
<h6 class="text-muted mb-2">Successful Payments</h6>
<h3 class="mb-0 text-success">@Model.Count(p => p.Status == LittleShop.Enums.PaymentStatus.Paid || p.Status == LittleShop.Enums.PaymentStatus.Completed)</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-warning">
<div class="card-body">
<h6 class="text-muted mb-2">Pending</h6>
<h3 class="mb-0 text-warning">@Model.Count(p => p.Status == LittleShop.Enums.PaymentStatus.Pending)</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-info">
<div class="card-body">
<h6 class="text-muted mb-2">Total Value</h6>
<h3 class="mb-0">£@Model.Where(p => p.Status == LittleShop.Enums.PaymentStatus.Paid || p.Status == LittleShop.Enums.PaymentStatus.Completed).Sum(p => p.PaidAmount).ToString("N2")</h3>
</div>
</div>
</div>
</div>
<!-- Payments Table -->
@if (Model.Any())
{
<div class="card">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-list"></i> Transaction List</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Payment ID</th>
<th>Order</th>
<th>Currency</th>
<th class="text-end">Required</th>
<th class="text-end">Paid</th>
<th class="text-center">Status</th>
<th>Created</th>
<th>Expires/Paid</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var payment in Model)
{
var statusClass = payment.Status.ToString() == "Paid" || payment.Status.ToString() == "Completed" ? "success" :
payment.Status.ToString() == "Expired" || payment.Status.ToString() == "Cancelled" ? "danger" :
payment.Status.ToString() == "Pending" ? "warning" :
"info";
var isExpired = DateTime.UtcNow > payment.ExpiresAt && payment.Status == LittleShop.Enums.PaymentStatus.Pending;
<tr class="@(isExpired ? "table-warning" : "")">
<td>
<code>@payment.Id.ToString().Substring(0, 8)</code>
@if (!string.IsNullOrEmpty(payment.SilverPayOrderId))
{
<br><small class="text-muted">SilverPay: @payment.SilverPayOrderId</small>
}
</td>
<td>
@if (orders.ContainsKey(payment.OrderId))
{
var order = orders[payment.OrderId];
<a href="@Url.Action("Details", "Orders", new { id = payment.OrderId })">
Order #@payment.OrderId.ToString().Substring(0, 8)
</a>
<br><small class="text-muted">£@order.TotalAmount.ToString("N2")</small>
}
else
{
<span class="text-muted">Order #@payment.OrderId.ToString().Substring(0, 8)</span>
}
</td>
<td>
<span class="badge bg-dark">
@payment.Currency
</span>
</td>
<td class="text-end">
<strong>@payment.RequiredAmount.ToString("0.########")</strong>
<br><small class="text-muted">@payment.Currency</small>
</td>
<td class="text-end">
@if (payment.PaidAmount > 0)
{
<strong class="text-success">@payment.PaidAmount.ToString("0.########")</strong>
<br><small class="text-muted">@payment.Currency</small>
}
else
{
<span class="text-muted">-</span>
}
</td>
<td class="text-center">
<span class="badge bg-@statusClass">
@if (payment.Status.ToString() == "Paid")
{
<i class="fas fa-check-circle"></i>
}
else if (payment.Status.ToString() == "Pending")
{
<i class="fas fa-clock"></i>
}
else if (payment.Status.ToString() == "Expired")
{
<i class="fas fa-times-circle"></i>
}
@payment.Status
</span>
@if (isExpired)
{
<br><small class="text-danger"><i class="fas fa-exclamation-triangle"></i> Expired</small>
}
</td>
<td>
@payment.CreatedAt.ToString("MMM dd, yyyy")
<br><small class="text-muted">@payment.CreatedAt.ToString("HH:mm")</small>
</td>
<td>
@if (payment.PaidAt.HasValue)
{
<span class="text-success">
<i class="fas fa-check"></i> @payment.PaidAt.Value.ToString("MMM dd, HH:mm")
</span>
}
else
{
<span class="@(isExpired ? "text-danger" : "text-muted")">
<i class="fas fa-clock"></i> @payment.ExpiresAt.ToString("MMM dd, HH:mm")
</span>
}
</td>
<td class="text-center">
@if (!string.IsNullOrEmpty(payment.TransactionHash))
{
<button type="button" class="btn btn-sm btn-outline-primary"
data-bs-toggle="tooltip"
title="@payment.TransactionHash">
<i class="fas fa-link"></i> TX
</button>
}
<a href="@Url.Action("Details", "Orders", new { id = payment.OrderId })"
class="btn btn-sm btn-primary"
title="View Order">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
else
{
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
@if (!string.IsNullOrEmpty(currentStatus))
{
<strong>No @currentStatus.ToLower() payments found</strong>
<p class="mb-0">Try <a href="@Url.Action("Index")" class="alert-link">viewing all payments</a>.</p>
}
else
{
<strong>No payment transactions yet</strong>
<p class="mb-0">Payment transactions will appear here when customers make cryptocurrency payments.</p>
}
</div>
}
@section Scripts {
<script>
// Initialize Bootstrap tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
});
</script>
}