Admin Payment History API
Author(s)
- Amarnath Garai
Last Updated Date
2026-06-03
Feature Overview
Admin users need a paginated payment history view that is grouped by invoice. Each invoice record includes invoice totals, invoice line items, all payment attempts for that invoice, customer basic information, and service request basic information.
This API is intended for admin payment history screens, reconciliation support, and operational review of failed, expired, processing, and successful payment attempts.
Endpoint
| Endpoint | Method | Auth Scope | Response |
|---|---|---|---|
/payments/history | GET | admin_user | ServerPaginatedData<PaymentHistoryResponse> |
Authorization
Requires JWT bearer authentication and both admin scopes:
Authorization: Bearer <access_token>
Query Parameters
The endpoint uses AdminPaymentHistoryFilter as query parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
invoiceId | Guid? | No | - | Filter by a specific invoice ID. |
serviceRequestId | Guid? | No | - | Filter by a specific service request ID. |
customerId | Guid? | No | - | Filter by a specific customer ID. |
invoicePaymentStatus | PaymentStatus? | No | - | Filter by invoice-level payment status. |
paymentStatus | TransactionStatus? | No | - | Filter invoices that have at least one payment attempt with this status. |
searchKeyword | string? | No | - | Searches invoice number, request number, customer code, customer name/email, payment reference number, trace ID, and transaction ID. |
fromDate | DateTime? | No | - | Filter invoices created on or after this date. |
toDate | DateTime? | No | - | Filter invoices created on or before this date. |
rowsPerPage | int | No | 10 | Number of invoices per page. Values below 1 are reset to 10. |
pageNumber | int | No | 1 | Page number to return. Values below 1 are reset to 1. |
Enum Values
PaymentStatus is the invoice-level payment status:
Pending
Paid
PartiallyPaid
Refunded
PartiallyRefunded
Failed
TransactionStatus is the individual payment attempt status:
Initiated
Processing
Success
Failed
Expired
Cancelled
Refunded
PartiallyRefunded
Other response enums:
PaymentMethod: NewCard, SavedCard
RequestStatus: Pending, Assigned, EnRoute, InProgress, Stopped, Completed, Canceled, Accepted, Ringing
ServiceScheduleType: OnDemand, Scheduled
LocationPreference: CustomerLocation, ServiceCenter
Gender: Male, Female, Others
DTOs
AdminPaymentHistoryFilter
public record AdminPaymentHistoryFilter
{
public Guid? InvoiceId { get; init; }
public Guid? ServiceRequestId { get; init; }
public Guid? CustomerId { get; init; }
public PaymentStatus? InvoicePaymentStatus { get; init; }
public TransactionStatus? PaymentStatus { get; init; }
public string? SearchKeyword { get; init; }
public DateTime? FromDate { get; init; }
public DateTime? ToDate { get; init; }
public int RowsPerPage { get; init; } = 10;
public int PageNumber { get; init; } = 1;
}
ServerPaginatedData
public class ServerPaginatedData<T>
{
public List<T> Data { get; set; } = [];
public int TotalNumber { get; set; }
public bool HasPreviousPage { get; set; }
public bool HasNextPage { get; set; }
public int TotalPages { get; set; }
public int PageNumber { get; set; }
public int RowsPerPage { get; set; }
}
PaymentHistoryResponse
public record PaymentHistoryResponse
{
public Guid InvoiceId { get; init; }
public string InvoiceNumber { get; init; } = string.Empty;
public Guid ServiceRequestId { get; init; }
public decimal SubTotal { get; init; }
public decimal TaxAmount { get; init; }
public decimal DiscountAmount { get; init; }
public decimal TotalAmount { get; init; }
public decimal PaidAmount { get; init; }
public decimal DueAmount { get; init; }
public PaymentStatus InvoicePaymentStatus { get; init; }
public string? TransactionNo { get; init; }
public DateTime CreatedAt { get; init; }
public DateTime? UpdatedAt { get; init; }
public required CustomerBasicInfo Customer { get; init; }
public required PaymentServiceRequestBasicInfo ServiceRequest { get; init; }
public List<InvoiceItemResponse> InvoiceItems { get; init; } = new();
public List<PaymentAttemptResponse> Payments { get; init; } = new();
}
CustomerBasicInfo
public record CustomerBasicInfo
{
public Guid UserId { get; init; }
public Guid CustomerId { get; init; }
public string CustomerCode { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string PhoneNumber { get; init; } = string.Empty;
public string? ProfileImageUrl { get; set; }
public Gender? Gender { get; init; }
public bool? IsDeleted { get; init; } = false;
}
PaymentServiceRequestBasicInfo
public record PaymentServiceRequestBasicInfo
{
public Guid RequestId { get; init; }
public string RequestNumber { get; init; } = string.Empty;
public RequestStatus RequestStatus { get; init; }
public ServiceScheduleType ServiceScheduleType { get; init; }
public LocationPreference LocationPreference { get; init; }
public DateTime RequestedAt { get; init; }
public DateTime? ScheduledAt { get; init; }
public DateTime? CompletedAt { get; init; }
public string? Location { get; init; }
public decimal? EstimatedPrice { get; init; }
}
InvoiceItemResponse
public record InvoiceItemResponse
{
public Guid InvoiceItemId { get; init; }
public Guid? ServiceId { get; init; }
public string? ServiceCode { get; init; }
public string ServiceName { get; init; } = string.Empty;
public decimal UnitPrice { get; init; }
public int Quantity { get; init; }
public decimal SubTotal { get; init; }
public decimal TaxAmount { get; init; }
public decimal DiscountAmount { get; init; }
public decimal TotalAmount { get; init; }
}
PaymentAttemptResponse
public record PaymentAttemptResponse
{
public Guid PaymentId { get; init; }
public PaymentMethod PaymentMethod { get; init; }
public TransactionStatus PaymentStatus { get; init; }
public decimal TotalAmount { get; init; }
public string ReferenceNumber { get; init; } = string.Empty;
public string? TraceId { get; init; }
public string? TransactionId { get; init; }
public DateTime? ExpireAt { get; init; }
public DateTime? InitiatedAt { get; init; }
public DateTime? CompletedAt { get; init; }
public DateTime? FailedAt { get; init; }
public string? Message { get; init; }
public string? AuthCode { get; init; }
public string? CardBrand { get; init; }
public string? Last4 { get; init; }
public bool SaveTokenRequested { get; init; }
public string? GatewayRequest { get; init; }
public string? GatewayResponse { get; init; }
public string? WebhookPayload { get; init; }
public DateTime CreatedAt { get; init; }
public DateTime UpdatedAt { get; init; }
}
Error Response
public record CommonResponse
{
public int Status { get; init; }
public string? Message { get; init; }
}
Example Requests
Fetch Failed Payment Attempts
GET /payments/history?paymentStatus=Failed&pageNumber=1&rowsPerPage=10
Authorization: Bearer <access_token>
Fetch Paid Invoices for a Customer
GET /payments/history?customerId=7bc33308-4a95-49ab-b209-9f102f1774d4&invoicePaymentStatus=Paid&pageNumber=1&rowsPerPage=10
Authorization: Bearer <access_token>
Search by Invoice, Request, Customer, or Gateway Reference
GET /payments/history?searchKeyword=INV-20260603-00012&pageNumber=1&rowsPerPage=10
Authorization: Bearer <access_token>
Example Success Response
{
"data": [
{
"invoiceId": "f79f2366-5d79-4c06-9ae5-4f1d2c3e35b5",
"invoiceNumber": "INV-20260603-00012",
"serviceRequestId": "4a3b1e93-1b58-4e8f-a0fd-55a130e5a77a",
"subTotal": 175.00,
"taxAmount": 14.00,
"discountAmount": 0.00,
"totalAmount": 189.00,
"paidAmount": 189.00,
"dueAmount": 0.00,
"invoicePaymentStatus": "Paid",
"transactionNo": "TXN-RKA-20260603-00001",
"createdAt": "2026-06-03T09:15:12Z",
"updatedAt": "2026-06-03T09:20:31Z",
"customer": {
"userId": "3d951196-5f56-4a6f-bd10-73237a48699d",
"customerId": "7bc33308-4a95-49ab-b209-9f102f1774d4",
"customerCode": "CUST-00045",
"email": "customer@example.com",
"firstName": "Alex",
"lastName": "Morgan",
"phoneNumber": "+14155552671",
"profileImageUrl": null,
"gender": "Male",
"isDeleted": false
},
"serviceRequest": {
"requestId": "4a3b1e93-1b58-4e8f-a0fd-55a130e5a77a",
"requestNumber": "RKA-26-A000836",
"requestStatus": "Completed",
"serviceScheduleType": "OnDemand",
"locationPreference": "CustomerLocation",
"requestedAt": "2026-06-03T08:30:00Z",
"scheduledAt": null,
"completedAt": "2026-06-03T09:05:00Z",
"location": "124 Cherry Drive, St. Louis, MO",
"estimatedPrice": 175.00
},
"invoiceItems": [
{
"invoiceItemId": "7db0ae2f-5a35-41a6-8e3d-d3555307e42c",
"serviceId": "21f5681c-0206-482a-85c0-bbcfa0e1c179",
"serviceCode": "OIL-CHANGE",
"serviceName": "Oil Change",
"unitPrice": 75.00,
"quantity": 1,
"subTotal": 75.00,
"taxAmount": 6.00,
"discountAmount": 0.00,
"totalAmount": 81.00
},
{
"invoiceItemId": "e13aacfa-c729-42d5-8a32-0e8615e33388",
"serviceId": "f0790ff3-2bc8-4843-a990-35f556879d73",
"serviceCode": "BRAKE-INSP",
"serviceName": "Brake Inspection",
"unitPrice": 100.00,
"quantity": 1,
"subTotal": 100.00,
"taxAmount": 8.00,
"discountAmount": 0.00,
"totalAmount": 108.00
}
],
"payments": [
{
"paymentId": "95d16a16-c2c2-4aaf-9d01-b2b5aaac810e",
"paymentMethod": "NewCard",
"paymentStatus": "Success",
"totalAmount": 189.00,
"referenceNumber": "RKA-26-A000836",
"traceId": "trace-92f42",
"transactionId": "DP-99887766",
"expireAt": "2026-06-03T09:25:00Z",
"initiatedAt": "2026-06-03T09:16:00Z",
"completedAt": "2026-06-03T09:20:31Z",
"failedAt": null,
"message": null,
"authCode": "A12345",
"cardBrand": "Visa",
"last4": "4242",
"saveTokenRequested": false,
"gatewayRequest": "{\"amount\":189.00,\"currency\":\"USD\"}",
"gatewayResponse": "{\"status\":\"approved\",\"authCode\":\"A12345\"}",
"webhookPayload": "{\"event\":\"payment.success\",\"paymentId\":\"95d16a16-c2c2-4aaf-9d01-b2b5aaac810e\"}",
"createdAt": "2026-06-03T09:16:00Z",
"updatedAt": "2026-06-03T09:20:31Z"
},
{
"paymentId": "94cc7347-013f-4eb7-a4bc-a597cc60e504",
"paymentMethod": "NewCard",
"paymentStatus": "Failed",
"totalAmount": 189.00,
"referenceNumber": "RKA-26-A000836",
"traceId": "trace-80ac1",
"transactionId": null,
"expireAt": "2026-06-03T09:10:00Z",
"initiatedAt": "2026-06-03T09:05:00Z",
"completedAt": null,
"failedAt": "2026-06-03T09:08:42Z",
"message": "Card declined",
"authCode": null,
"cardBrand": "Visa",
"last4": "1111",
"saveTokenRequested": false,
"gatewayRequest": "{\"amount\":189.00,\"currency\":\"USD\"}",
"gatewayResponse": "{\"status\":\"declined\",\"reason\":\"Card declined\"}",
"webhookPayload": null,
"createdAt": "2026-06-03T09:05:00Z",
"updatedAt": "2026-06-03T09:08:42Z"
}
]
}
],
"totalNumber": 1,
"hasPreviousPage": false,
"hasNextPage": false,
"totalPages": 1,
"pageNumber": 1,
"rowsPerPage": 10
}
Error Responses
400 Bad Request
Returned for invalid filter values, such as fromDate greater than toDate.
{
"status": 2,
"message": "FromDate cannot be greater than ToDate"
}
403 Forbidden
Returned when the caller does not have the required admin scopes.
{
"status": 401,
"message": "Forbidden"
}
500 Internal Server Error
Returned when the service or database layer fails unexpectedly.
{
"status": 500,
"message": "Failed to retrieve payment history"
}
Data Loading Notes
- Pagination is invoice-based, not payment-attempt-based.
- Each returned invoice includes all payment attempts for that invoice.
- Filtering by
paymentStatusreturns invoices that have at least one payment attempt with that status. - The response intentionally excludes checkout URL and async processing ID.
- Payment attempts are ordered newest first.