Skip to main content

Overview

The PaymentController handles payment recording and tracking for medical services. It supports multiple payment methods and can link payments to specific consultations. Controller Location: app/Http/Controllers/PaymentController.php
Base Route: /payments
Middleware: auth, verified, role:admin|receptionist

Endpoints

List Payments

method
string
HTTP Method: GET
route
string
Route: /payments
name
string
Route Name: payments.index
Retrieve a paginated list of all payments with patient and consultation information.

Query Parameters

Search by patient full name (partial match)

Response

Returns an Inertia response rendering payments/index with:
payments
object
Paginated payment collection (10 per page)
filters
object
Applied search filters

Example Request

GET /payments?search=John

Implementation

public function index(Request $request)
{
    $query = Payment::with(['patient', 'consultation'])->latest();

    if ($request->has('search')) {
        $search = $request->get('search');
        $query->whereHas('patient', function ($q) use ($search) {
            $q->where('full_name', 'like', "%{$search}%");
        });
    }

    return Inertia::render('payments/index', [
        'payments' => $query->paginate(10)->withQueryString(),
        'filters' => $request->only(['search']),
    ]);
}

Create Payment

method
string
HTTP Method: POST
route
string
Route: /payments
name
string
Route Name: payments.store
Record a new payment transaction.

Request Body

patient_id
integer
required
ID of the patient making the payment
consultation_id
integer
ID of the associated consultation (optional)
amount
decimal
required
Payment amount (minimum: 0)
payment_method
string
required
Payment method: cash, card, or transfer
status
string
required
Payment status: paid, pending, or cancelled
notes
string
Optional payment notes or description

Response

Redirects back with success message: “Pago registrado correctamente.”

Example Request

POST /payments
Content-Type: application/json

{
  "patient_id": 15,
  "consultation_id": 42,
  "amount": 150.00,
  "payment_method": "card",
  "status": "paid",
  "notes": "Payment for consultation - March 2026"
}

Validation Rules

[
    'patient_id' => 'required|exists:patients,id',
    'consultation_id' => 'nullable|exists:consultations,id',
    'amount' => 'required|numeric|min:0',
    'payment_method' => 'required|string|in:cash,card,transfer',
    'status' => 'required|string|in:paid,pending,cancelled',
    'notes' => 'nullable|string',
]

Implementation

public function store(Request $request, CreatePaymentAction $action)
{
    $validated = $request->validate([
        'patient_id' => 'required|exists:patients,id',
        'consultation_id' => 'nullable|exists:consultations,id',
        'amount' => 'required|numeric|min:0',
        'payment_method' => 'required|string|in:cash,card,transfer',
        'status' => 'required|string|in:paid,pending,cancelled',
        'notes' => 'nullable|string',
    ]);

    $action->execute($validated);

    return redirect()->back()->with('success', 'Pago registrado correctamente.');
}

Authorization

Only users with admin or receptionist roles can access payment endpoints.
Route::middleware(['role:admin|receptionist'])->group(function () {
    Route::get('payments', [PaymentController::class, 'index']);
    Route::post('payments', [PaymentController::class, 'store']);
});

Payment Methods

The system supports three payment methods:

Cash

Cash payment at the clinic

Card

Credit or debit card payment

Transfer

Bank transfer or wire payment

Payment Statuses

Action Classes

The controller uses an action class for payment creation: CreatePaymentAction (app/Actions/Payments/CreatePaymentAction.php) This separates business logic from the controller for better testability and reusability.

Use Cases

Recording a Consultation Payment

// Frontend: After consultation is complete
const paymentData = {
  patient_id: consultation.patient_id,
  consultation_id: consultation.id,
  amount: 150.00,
  payment_method: 'card',
  status: 'paid',
  notes: `Payment for consultation ${consultation.id}`
};

await axios.post('/payments', paymentData);

Recording a General Payment

// Payment not linked to consultation (e.g., registration fee)
const paymentData = {
  patient_id: patient.id,
  consultation_id: null,
  amount: 50.00,
  payment_method: 'cash',
  status: 'paid',
  notes: 'Patient registration fee'
};

await axios.post('/payments', paymentData);

Searching Payments

// Search by patient name
const searchQuery = 'Maria';
const response = await axios.get(`/payments?search=${searchQuery}`);

Build docs developers (and LLMs) love