Skip to main content

Client Payments API

Retrieves payments that belong to the authenticated client (or, for staff roles, to any client records that share the signed‑in user’s email).

Base URL: /api/client/payments
Auth: HTTP‑only JWT cookie (token) issued by POST /api/auth/login
Required roles: admin, finance, or client (authorization middleware)
Content type: application/json


GET /api/client/payments/

Summary

Return all payment records whose payments.clientId matches client(s) whose email equals the authenticated user’s email.

  • If the caller’s role is client, this yields that client’s own payments.
  • If the caller’s role is admin or finance, results are still filtered by the caller’s email, which typically means no client data is returned unless a client record exists with the same email address. (This is current server behavior.)

Ordering & pagination

  • No explicit ordering is applied by the API; treat the returned list as unsorted.
  • Pagination is not implemented.

Authorization

Requires a valid session cookie:
Cookie: token=<jwt>

Query parameters

None.

Request body

None.

Response — 200 OK

An array of Payment objects:

[
{
"id": 123,
"clientId": 45,
"amount": 25000,
"paymentDate": "2025-02-10T00:00:00.000Z",
"paymentType": "income",
"description": "Initial booking",
"serviceType": "installment_payment",
"company": 3,
"companyName": "Acme Properties"
}
]

Payment object

FieldTypeNotes
idintegerPayment identifier.
clientIdintegerID of the client who made/owns the payment.
amountnumberMonetary amount.
paymentDatestringISO 8601 date‑time.
paymentTypestringCommon values: "income" or "expense".
descriptionstring|nullFree‑text note.
serviceTypestring|nullE.g. "installment_payment"; other service types may appear.
companyintegerCompany ID that owns the record.
companyNamestringHuman‑readable company name (via join).

Errors

StatusWhenExample payload
401Missing/invalid session or req.user absent{"message":"Unauthorized"}
500Any unhandled server/database error{"message":"Error retrieving payments"}

cURL example

curl -X GET https://<host>/api/client/payments \
-H "Cookie: token=<jwt>"

JavaScript (fetch) example

const res = await fetch("/api/client/payments", {
credentials: "include"
});
if (!res.ok) throw new Error("Failed to load payments");
const data = await res.json();
console.log(data); // Array of payments

Security & data access

  • Endpoint is protected by role-based middleware: authorize(["admin","finance","client"]).
  • Result set is always constrained by the authenticated user’s email:
    • Backend first finds client rows with clients.email == req.user.email.
    • It then returns payments whose payments.clientId is in that set.
  • There is no company scoping or additional filtering on this endpoint.

Implementation notes (server)

  • SQL shape (via Drizzle):
    • SELECT payments.* , companies.name AS companyName
    • FROM payments LEFT JOIN companies ON payments.company = companies.id
    • WHERE payments.clientId IN (SELECT id FROM clients WHERE email = req.user.email)
  • File: server/routes/client/payments.ts (handler: getClientPayment).

Planned extensions (non‑breaking)

  • Add explicit ordering, e.g., ORDER BY payments.paymentDate DESC.
  • Add optional pagination (page, pageSize) and date filters (from, to).
  • Allow admins/finance to query by clientId or email (with proper authorization).