Skip to content

Payment History

Complete guide to accessing payment history with dual authentication support for both staff and customers in the Reserva platform.


Overview

The payment history system provides comprehensive transaction tracking with support for:

  • Dual Authentication - Access for both staff/admin and customers with RBAC
  • Platform Fee Breakdown - Display base amount + platform fee for transparency
  • Flexible Filtering - Filter by payment type, status, date range, and customer
  • Pagination - Efficient data retrieval with customizable page size
  • Tenant Isolation - Automatic data isolation per tenant
  • Transaction Details - Complete payment information with reference IDs

Key Features:

  • Staff Access: View all payments within their tenant with optional customer filtering
  • Customer Access: View only their own payment history automatically
  • Fee Transparency: Shows base_amount and platform_fee when available
  • RBAC Enforcement: Role-based access control with SUPER_ADMIN privileges
  • Comprehensive Filtering: Multiple filter options for precise data retrieval

Get Payment History

Retrieve payment history with comprehensive filtering options and dual authentication support.

Endpoint

GET /api/v1/customer-payments/history

Authentication: Required (JWT token - Staff or Customer)

Access Control

User Type Access Level Filtering
Customer Own payments only Automatic customer_id filter applied
Staff All tenant payments Can filter by specific customer_id
SUPER_ADMIN All payments across tenants Full access to all data

Query Parameters

  • payment_type (optional) - Filter by payment type: APPOINTMENT, WALLET_TOPUP
  • status (optional) - Filter by payment status: COMPLETED, PENDING, FAILED, CANCELLED
  • date_from (optional) - Filter payments from this date (ISO 8601 format)
  • date_to (optional) - Filter payments until this date (ISO 8601 format)
  • customer_id (optional) - Filter by customer ID (staff only, ignored for customers)
  • skip (optional) - Number of records to skip (default: 0, min: 0)
  • limit (optional) - Maximum records to return (default: 20, min: 1, max: 100)

Example Requests

Customer Request (View Own Payments)

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?limit=20&skip=0" \
  -H "Authorization: Bearer CUSTOMER_JWT_TOKEN"

Staff Request (View All Tenant Payments)

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?limit=20&skip=0" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Staff Request (Filter by Specific Customer)

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?customer_id=507f1f77bcf86cd799439011&limit=20" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Filter by Payment Type and Status

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?payment_type=APPOINTMENT&status=COMPLETED&limit=50" \
  -H "Authorization: Bearer JWT_TOKEN"

Filter by Date Range

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?date_from=2025-01-01T00:00:00Z&date_to=2025-01-31T23:59:59Z" \
  -H "Authorization: Bearer JWT_TOKEN"

Response

Success Response (200 OK)

{
  "data": [
    {
      "_id": "507f1f77bcf86cd799439020",
      "appointment_id": "507f1f77bcf86cd799439015",
      "payment_type": "APPOINTMENT",
      "amount": 108000.00,
      "base_amount": 100000.00,
      "platform_fee": 8000.00,
      "payment_method": "QRIS",
      "status": "COMPLETED",
      "created_at": "2025-01-15T10:30:00Z",
      "completed_at": "2025-01-15T10:35:00Z",
      "description": "Appointment payment for John Doe",
      "reference_id": "APT-507f1f77bcf86cd799439015-20250115103000"
    },
    {
      "_id": "507f1f77bcf86cd799439021",
      "appointment_id": null,
      "payment_type": "WALLET_TOPUP",
      "amount": 54000.00,
      "base_amount": 50000.00,
      "platform_fee": 4000.00,
      "payment_method": "VIRTUAL_ACCOUNT",
      "status": "COMPLETED",
      "created_at": "2025-01-14T14:20:00Z",
      "completed_at": "2025-01-14T14:25:00Z",
      "description": "Wallet top-up for customer@example.com",
      "reference_id": "WALLET-507f1f77bcf86cd799439012-20250114142000"
    },
    {
      "_id": "507f1f77bcf86cd799439022",
      "appointment_id": "507f1f77bcf86cd799439016",
      "payment_type": "APPOINTMENT",
      "amount": 216000.00,
      "base_amount": 200000.00,
      "platform_fee": 16000.00,
      "payment_method": "CREDIT_CARD",
      "status": "PENDING",
      "created_at": "2025-01-13T09:15:00Z",
      "completed_at": null,
      "description": "Appointment payment for Jane Smith",
      "reference_id": "APT-507f1f77bcf86cd799439016-20250113091500"
    }
  ],
  "total": 45,
  "skip": 0,
  "limit": 20,
  "has_more": true
}

Response Fields

Field Type Description
_id string Unique payment record ID
appointment_id string|null Associated appointment ID (null for wallet top-ups)
payment_type enum Payment type: APPOINTMENT, WALLET_TOPUP
amount decimal Total amount paid (base + platform fee)
base_amount decimal|null Base amount before platform fee
platform_fee decimal|null Platform fee charged
payment_method enum|null Payment method used (see Payment Methods)
status enum Payment status (see Payment Statuses)
created_at datetime Payment creation timestamp
completed_at datetime|null Payment completion timestamp
description string|null Payment description
reference_id string|null Unique reference ID for tracking

Payment Types

  • APPOINTMENT - Payment for booking appointment services
  • WALLET_TOPUP - Customer wallet balance top-up

Payment Statuses

  • COMPLETED - Payment successfully processed and confirmed
  • PENDING - Payment initiated, awaiting completion
  • PROCESSING - Payment being processed by gateway
  • FAILED - Payment attempt failed
  • CANCELLED - Payment cancelled by user or system
  • REFUNDED - Payment refunded to customer

Payment Methods

  • BANK_TRANSFER - Bank transfer via Indonesian banks
  • VIRTUAL_ACCOUNT - Virtual account payment
  • E_WALLET - E-wallet (OVO, GoPay, Dana, LinkAja)
  • QRIS - QR code payment
  • CREDIT_CARD - Credit/debit card payment

Platform Fee Calculation

The platform fee is automatically calculated based on the tenant's subscription plan:

Subscription Plan Platform Fee Rate Example (Base: IDR 100,000)
FREE 8% Fee: IDR 8,000, Total: IDR 108,000
PRO 5% Fee: IDR 5,000, Total: IDR 105,000
ENTERPRISE 3% Fee: IDR 3,000, Total: IDR 103,000

Note: The fee is displayed when the payment was created using the PaymentProcessor service, which automatically includes fee calculation.


Business Rules

Access Control Rules

  1. Customer Authentication:

  2. Can only view their own payment history

  3. Automatic customer_id filter applied
  4. customer_id query parameter ignored
  5. Restricted to their tenant's data only

  6. Staff Authentication:

  7. Can view all payments within their tenant

  8. Can filter by specific customer_id
  9. Cannot access other tenants' data (unless SUPER_ADMIN)
  10. Must belong to the tenant being queried

  11. SUPER_ADMIN Access:

  12. Can view payments across all tenants

  13. No tenant restriction applied
  14. Full access to all historical data

Data Isolation

  1. Tenant Isolation:

  2. Payments automatically filtered by tenant_id

  3. Staff cannot access other tenants' payments
  4. Customers cannot access other tenants' payments

  5. Soft Delete Exclusion:

  6. Soft-deleted payments (is_deleted: true) excluded from results

  7. Only active payment records returned

  8. Pagination Limits:

  9. Maximum 100 records per page

  10. Default page size: 20 records
  11. Efficient data retrieval for large datasets

Filtering Rules

  1. Date Range:

  2. date_from and date_to support timezone-aware timestamps

  3. Inclusive filtering (includes boundary dates)
  4. Based on payment created_at timestamp

  5. Status Filtering:

  6. Only valid PaymentStatus enum values accepted

  7. Multiple statuses require separate API calls

  8. Customer Filtering (Staff Only):

  9. Valid ObjectId format required

  10. Customer must belong to the same tenant
  11. Invalid customer_id returns 400 Bad Request

Error Responses

401 Unauthorized

Scenario: Invalid or expired JWT token

{
  "detail": "Could not validate credentials"
}

403 Forbidden

Scenario: Staff attempting to access another tenant's payments

{
  "detail": "Access denied to this tenant's payments"
}

400 Bad Request

Scenario: Invalid customer_id format

{
  "detail": "Invalid customer ID format"
}

422 Unprocessable Entity

Scenario: Invalid query parameters

{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "ensure this value is less than or equal to 100",
      "type": "value_error.number.not_le"
    }
  ]
}

Use Cases

Use Case 1: Customer Views Their Payment History

Scenario: Customer wants to see all their past payments

Request:

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history" \
  -H "Authorization: Bearer CUSTOMER_JWT_TOKEN"

Result:

  • Returns only payments belonging to the authenticated customer
  • Automatically filtered by customer_id
  • Includes all payment types (appointments, wallet top-ups)

Use Case 2: Staff Views All Tenant Payments

Scenario: Staff member wants to see all payments for their salon

Request:

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?limit=50" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Result:

  • Returns all payments within the staff's tenant
  • Sorted by most recent first
  • Includes fee breakdown for transparency

Use Case 3: Staff Views Specific Customer's Payments

Scenario: Staff member wants to review a specific customer's payment history

Request:

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?customer_id=507f1f77bcf86cd799439011" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Result:

  • Returns only payments for the specified customer
  • Must be within the same tenant
  • Useful for customer support and reconciliation

Use Case 4: Filter Completed Appointment Payments

Scenario: View only successful appointment payments

Request:

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?payment_type=APPOINTMENT&status=COMPLETED" \
  -H "Authorization: Bearer JWT_TOKEN"

Result:

  • Returns only completed appointment payments
  • Excludes pending, failed, or cancelled payments
  • Useful for revenue reporting

Use Case 5: Monthly Payment Report

Scenario: Generate payment report for January 2025

Request:

curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?date_from=2025-01-01T00:00:00Z&date_to=2025-01-31T23:59:59Z&limit=100" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Result:

  • Returns all payments within the specified date range
  • Includes fee breakdown for accounting
  • Useful for monthly reconciliation and reporting

Integration Examples

Frontend Integration (React)

import axios from 'axios';

interface PaymentHistoryItem {
  _id: string;
  appointment_id?: string;
  payment_type: 'APPOINTMENT' | 'WALLET_TOPUP';
  amount: number;
  base_amount?: number;
  platform_fee?: number;
  payment_method?: string;
  status: string;
  created_at: string;
  completed_at?: string;
  description?: string;
  reference_id?: string;
}

interface PaymentHistoryResponse {
  data: PaymentHistoryItem[];
  total: number;
  skip: number;
  limit: number;
  has_more: boolean;
}

// Customer viewing their own payments
async function getMyPaymentHistory(
  page: number = 0,
  pageSize: number = 20
): Promise<PaymentHistoryResponse> {
  const response = await axios.get(
    'https://api.myreserva.id/api/v1/customer-payments/history',
    {
      params: {
        skip: page * pageSize,
        limit: pageSize
      },
      headers: {
        Authorization: `Bearer ${customerToken}`
      }
    }
  );
  return response.data;
}

// Staff viewing tenant payments with filters
async function getTenantPaymentHistory(
  filters: {
    customerId?: string;
    paymentType?: string;
    status?: string;
    dateFrom?: string;
    dateTo?: string;
    page?: number;
    pageSize?: number;
  }
): Promise<PaymentHistoryResponse> {
  const response = await axios.get(
    'https://api.myreserva.id/api/v1/customer-payments/history',
    {
      params: {
        customer_id: filters.customerId,
        payment_type: filters.paymentType,
        status: filters.status,
        date_from: filters.dateFrom,
        date_to: filters.dateTo,
        skip: (filters.page || 0) * (filters.pageSize || 20),
        limit: filters.pageSize || 20
      },
      headers: {
        Authorization: `Bearer ${staffToken}`
      }
    }
  );
  return response.data;
}

// Display payment with fee breakdown
function PaymentItem({ payment }: { payment: PaymentHistoryItem }) {
  return (
    <div className="payment-item">
      <div className="payment-header">
        <span className="payment-type">{payment.payment_type}</span>
        <span className={`status ${payment.status.toLowerCase()}`}>
          {payment.status}
        </span>
      </div>

      <div className="payment-details">
        <p>Reference: {payment.reference_id}</p>
        <p>Date: {new Date(payment.created_at).toLocaleDateString()}</p>
        {payment.appointment_id && (
          <p>Appointment: {payment.appointment_id}</p>
        )}
      </div>

      <div className="payment-amount">
        {payment.base_amount && payment.platform_fee ? (
          <>
            <div>Base Amount: IDR {payment.base_amount.toLocaleString()}</div>
            <div>Platform Fee: IDR {payment.platform_fee.toLocaleString()}</div>
            <div className="total">
              Total: IDR {payment.amount.toLocaleString()}
            </div>
          </>
        ) : (
          <div className="total">
            Amount: IDR {payment.amount.toLocaleString()}
          </div>
        )}
      </div>
    </div>
  );
}

Best Practices

For Customers

DO:

  • Check payment history regularly for completed bookings
  • Verify payment amounts match appointment totals
  • Keep reference IDs for customer support
  • Review platform fees for cost transparency

DON'T:

  • Share payment history with unauthorized parties
  • Assume pending payments are completed
  • Ignore failed payment notifications

For Staff

DO:

  • Use customer_id filter for customer support inquiries
  • Apply date range filters for monthly reconciliation
  • Export payment data regularly for accounting
  • Monitor pending payments for follow-up
  • Review platform fee breakdown for revenue calculation

DON'T:

  • Access payments from other tenants (unless SUPER_ADMIN)
  • Share customer payment details without authorization
  • Modify payment records directly (use refund process)
  • Ignore failed payment patterns

For Developers

DO:

  • Implement proper pagination for large datasets
  • Cache payment history for better performance
  • Handle all error responses gracefully
  • Display platform fee breakdown transparently
  • Use appropriate authentication type (staff vs customer)
  • Implement retry logic for network failures

DON'T:

  • Load entire payment history without pagination
  • Skip error handling for 403 Forbidden responses
  • Hard-code customer_id filters
  • Ignore tenant isolation rules
  • Mix staff and customer authentication flows

Performance Considerations

Pagination

  • Default Page Size: 20 records
  • Maximum Page Size: 100 records per request
  • Recommended: Use smaller page sizes (20-50) for better performance
  • Large Datasets: Implement infinite scroll or "Load More" pattern

Indexing

The following database indexes optimize query performance:

// MongoDB Indexes (for reference)
db.payments.createIndex({ "tenant_id": 1, "customer_id": 1, "created_at": -1 })
db.payments.createIndex({ "tenant_id": 1, "payment_type": 1, "status": 1 })
db.payments.createIndex({ "tenant_id": 1, "created_at": -1 })
db.payments.createIndex({ "is_deleted": 1 })

Caching Strategy

// Example: Client-side caching with 5-minute TTL
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
const paymentCache = new Map();

async function getCachedPaymentHistory(
  filters: any
): Promise<PaymentHistoryResponse> {
  const cacheKey = JSON.stringify(filters);
  const cached = paymentCache.get(cacheKey);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const data = await getTenantPaymentHistory(filters);
  paymentCache.set(cacheKey, { data, timestamp: Date.now() });

  return data;
}

Troubleshooting

Issue: Staff Cannot See Customer Payments

Symptoms: Staff receives empty array despite customer having payments

Checks:

  1. Verify staff belongs to the same tenant as customer
  2. Check if customer_id is valid ObjectId format
  3. Confirm staff has appropriate role (not restricted by RBAC)
  4. Verify payments are not soft-deleted

Solution:

# Verify tenant association
curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

# If empty, check customer_id filter
curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?customer_id=VALID_CUSTOMER_ID" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

Issue: Platform Fee Not Displayed

Symptoms: base_amount and platform_fee are null in response

Possible Causes:

  1. Payment was created before PaymentProcessor integration
  2. Legacy payment record without fee breakdown
  3. Manual payment entry without fee calculation

Solution:

  • Platform fee breakdown only available for payments created via PaymentProcessor
  • Legacy payments show total amount only
  • Contact support for historical fee data if needed

Issue: Date Range Returns No Results

Symptoms: Date filter returns empty array despite having payments

Checks:

  1. Verify date format is ISO 8601 (e.g., 2025-01-01T00:00:00Z)
  2. Check timezone offset (use UTC for consistency)
  3. Confirm date range includes payment timestamps

Solution:

# Correct format with UTC timezone
curl -X GET "https://api.myreserva.id/api/v1/customer-payments/history?date_from=2025-01-01T00:00:00Z&date_to=2025-01-31T23:59:59Z" \
  -H "Authorization: Bearer JWT_TOKEN"

API Reference Summary

Endpoint Method Authentication Access Level
/customer-payments/history GET Required (Staff or Customer) RBAC-based

Query Parameters Summary

Parameter Type Required Default Description
payment_type enum No - Filter by payment type
status enum No - Filter by payment status
date_from datetime No - Start date filter
date_to datetime No - End date filter
customer_id string No - Customer filter (staff only)
skip integer No 0 Pagination offset
limit integer No 20 Page size (max: 100)


Next Steps:

  1. Implement payment history display in your application
  2. Add filters for payment type and status
  3. Implement pagination for better UX
  4. Display platform fee breakdown for transparency
  5. Set up proper error handling for all scenarios

For webhook integration and payment confirmation flow, refer to the Webhook Integration documentation.