Firman Aprilian Sugiharto
Work

Disbursement API

A disbursement starts as PENDING and can be approved or rejected once. Anything already processed returns 409.

Category
Transactional backend API
Stack
  • Go
  • REST API
  • MySQL
  • JWT
  • GORM
  • Docker
Source
shiinobu/disbursement-api
localhost:8080captured in Postman
  1. POST/api/auth/login200 OKReturns a JWT and the user with role: admin.
  2. POST/api/disbursements201 Createdstatus: "PENDING", admin_fee: 2500 for an amount of 1500000.
  3. PATCH/api/disbursements/1/status200 OKBody { "status": "APPROVED" }. Response has status: "APPROVED" and processed_by.
  4. PATCH/api/disbursements/1/status409 ConflictBody { "status": "REJECTED" } on the same record. Error: "hanya disbursement pending yang dapat diproses" (only pending disbursements can be processed).
Real requests against the local API. The last one is rejected because the disbursement was already processed.

Overview

A backend API for managing fund disbursement requests, with JWT authentication, role-based authorization, status rules, pagination, search and CSV export.

At a glance

  • JWT authentication
  • Role-based access control
  • PENDING → APPROVED / REJECTED workflow
  • Status-based deletion rules
  • Protected administrative operations
  • Pagination metadata
  • Search query support
  • CSV reporting
  • Layered repository/service/handler structure
  • MySQL persistence with GORM

Problem and approach

Problem

Money movement needs more than CRUD. Each operation has to follow business rules, user permissions and controlled state transitions.

Approach

The API is built around the disbursement lifecycle: JWT authentication, role-based authorization, status-based rules, pagination, search and reporting endpoints.

Architecture

Requests pass through the API layer to business services, which reach the MySQL database through repositories. Authentication and authorization middleware protect operations by role and by record status.

  1. API client

    Sends authenticated requests to the disbursement REST API.

  2. HTTP handler

    Receives requests, validates input and maps them to application operations.

    HTTP + JWT

  3. Service layer

    Applies business rules, authorization requirements and status transitions.

  4. Repository

    Handles persistence against the database.

  5. MySQL

    Stores users, disbursement records and transaction data.

    GORM

How it works

A disbursement is not an open-ended CRUD record. Each state change is checked against its current status and the role of the user making the request.

  • ApprovePENDINGAPPROVED
  • RejectPENDINGREJECTED

Implementation

JWT authentication
Protected operations verify the identity of the requesting user before any business logic runs.
Role-based authorization
Sensitive operations are limited by user role, so only authorized users can perform administrative actions.
Status management
A disbursement moves through PENDING, APPROVED and REJECTED with controlled transitions instead of open-ended CRUD.
Business rules
Approval, rejection and deletion are checked against the current record status and the user's permissions before anything is written.
Search and pagination
List endpoints support search and pagination and return metadata about the current page and total records.
CSV reporting
Disbursement data can be exported as CSV for reporting.

Screens

Postman captures (6)
Login returns a JWT and the authenticated user.
Creating a disbursement starts it as PENDING.
Paginated listing with response metadata.
Approval moves PENDING to APPROVED.
Rejection records a reason.
Processing an already processed disbursement returns 409.

Notes

Challenges

  • Implementing controlled disbursement state transitions
  • Enforcing role-based permissions on sensitive operations
  • Preventing invalid operations based on the current record status
  • Designing consistent paginated API responses
  • Supporting search and CSV reporting alongside transactional operations

Status

Every state change is checked against the current status and the user's role. Responses share one shape (data, message, success), and errors carry field-level messages.