# Tank Track — Fuel Request Module API

Frontend/mobile integration guide for requesting and transferring fuel payments between accepted connections.

---

## Base URL

| Environment | Base URL |
|-------------|----------|
| Local | `http://localhost:6260/api/v1/fuel-requests` |
| Staging / Production | `https://<your-api-host>/api/v1/fuel-requests` |

All routes are mounted at `{API_PREFIX}/fuel-requests` (default prefix: `/api/v1`).

---

## Authentication (required on every endpoint)

Every fuel request endpoint requires a valid JWT from login/signup.

### Request headers

```http
Authorization: Bearer <access_token>
Content-Type: application/json
```

| Header | Required | Value |
|--------|----------|-------|
| `Authorization` | Yes | `Bearer <jwt_token>` |
| `Content-Type` | Yes (POST/PATCH) | `application/json` |

### Unauthorized responses

**401 — Missing token**

```json
{
  "message": "UnAuthorized Request"
}
```

**401 — Invalid / expired token**

```json
{
  "message": "Invalid Token"
}
```

---

## Standard response envelope

### Success (`200`)

```json
{
  "status": 200,
  "success": true,
  "message": "Human-readable success message",
  "data": {}
}
```

### Error (business logic)

```json
{
  "statusCode": 400,
  "success": false,
  "message": "Error message string or validation array",
  "data": {}
}
```

> **Note:** Success responses use `"status"`. Error responses use `"statusCode"`. This matches the existing Tank Track API pattern.

### Validation error (`400`)

```json
{
  "statusCode": 400,
  "success": false,
  "message": [
    {
      "field": "amount",
      "message": "Amount must be greater than 0"
    }
  ],
  "data": {}
}
```

---

## Business rules

| Rule | Detail |
|------|--------|
| Connection required | You can only send a fuel request to a user you are **accepted friends** with (Connections module). |
| Roles | **Sender** = user asking for money. **Receiver** = user who can approve and pay. |
| Amount unit | Request body uses **`amount` in USD dollars** (e.g. `200` = $200.00, `5.50` = $5.50). |
| Stored amount | Backend stores `amountCents` internally (integer cents). Responses include both `amount` and `amountCents`. |
| Max amount | $10,000 per request. |
| Transfer | Only the **receiver** can call transfer or reject. |
| Wallet | Transfer debits the receiver's wallet and credits the sender's wallet. Receiver must have sufficient balance. |
| One-time processing | Each request can only be transferred or rejected once (`pending` → `accepted` or `rejected`). |

### Amount examples

| You send `amount` | Stored `amountCents` | Meaning |
|-------------------|----------------------|---------|
| `200` | `20000` | $200.00 |
| `5.50` | `550` | $5.50 |
| `0.99` | `99` | $0.99 |

> **Important:** Do **not** send cents in the `amount` field. Sending `200` means **$200**, not $2.00.

---

## Data model (`fuelrequests` collection)

| Field | Type | Description |
|-------|------|-------------|
| `_id` | ObjectId | Fuel request document ID. Use as `requestId` for transfer/reject. |
| `senderId` | ObjectId | User who requested the fuel payment (asks for money). |
| `receiverId` | ObjectId | User who receives the request (can pay or reject). |
| `amountCents` | number | Amount in USD cents (stored internally). |
| `message` | string \| null | Optional note from sender, max 255 chars. |
| `status` | string | `pending` \| `accepted` \| `rejected` |
| `createdAt` | ISO date | When the request was created. |
| `updatedAt` | ISO date | Last status change (e.g. when transferred). |

### Important IDs for the mobile app

| ID | Where it comes from | Used for |
|----|---------------------|----------|
| `requestId` | `POST /` response `_id`, or list endpoints | Transfer / reject |
| `receiverUserId` | `GET /connections/my-connections` → friend's `_id` | Create fuel request (who should pay) |
| `senderId` / `receiverId` | List responses | Display who asked / who pays |

---

## Endpoints overview

| # | Method | Path | Who can call | Description |
|---|--------|------|--------------|-------------|
| 1 | `POST` | `/` | Sender (logged-in user) | Send fuel payment request to a connection |
| 2 | `GET` | `/sent` | Sender | List fuel requests you sent |
| 3 | `GET` | `/received` | Receiver | List fuel requests you received |
| 4 | `PATCH` | `/:requestId/transfer` | Receiver only | Approve and pay (wallet debit → credit sender) |
| 5 | `PATCH` | `/:requestId/reject` | Receiver only | Reject a pending request |

---

## 1. Send fuel request

Ask an accepted connection to send you a fuel payment.

### Request

```http
POST /api/v1/fuel-requests
Authorization: Bearer <token>
Content-Type: application/json
```

**Body**

```json
{
  "receiverUserId": "684a0000ce7cbc33d8946b00",
  "amount": 200,
  "message": "Need fuel help at the station"
}
```

| Field | Type | Required | Rules |
|-------|------|----------|-------|
| `receiverUserId` | string | Yes | Valid MongoDB ObjectId of an accepted connection |
| `amount` | number | Yes | USD dollars, > 0, max 10000 |
| `message` | string | No | Max 255 characters |

### Success response

```json
{
  "status": 200,
  "success": true,
  "message": "Fuel request sent",
  "data": {
    "_id": "686a1b2c3d4e5f6789012345",
    "receiver": {
      "_id": "684a0000ce7cbc33d8946b00",
      "fullName": "jim",
      "email": "jim@yopmail.com",
      "image": "http://localhost:6260/public/uploads/profile.jpg"
    },
    "amount": 200,
    "amountCents": 20000,
    "message": "Need fuel help at the station",
    "status": "pending",
    "createdAt": "2026-07-08T14:00:00.000Z"
  }
}
```

### Error responses

| HTTP | Message |
|------|---------|
| `400` | `"You cannot send a fuel request to yourself"` |
| `400` | Validation errors (invalid `receiverUserId`, invalid `amount`, etc.) |
| `403` | `"You can only send fuel requests to your connections"` |
| `404` | `"Receiver not found"` |

---

## 2. List sent fuel requests

List fuel payment requests you have sent to others.

### Request

```http
GET /api/v1/fuel-requests/sent?page=1&limit=20&status=pending
Authorization: Bearer <token>
```

**Query parameters**

| Param | Type | Required | Default | Rules |
|-------|------|----------|---------|-------|
| `page` | number | No | `1` | Positive integer |
| `limit` | number | No | `20` | Max 100 |
| `status` | string | No | — | `pending` \| `accepted` \| `rejected` |

### Success response

```json
{
  "status": 200,
  "success": true,
  "message": "Fuel requests fetched successfully",
  "data": {
    "data": [
      {
        "_id": "686a1b2c3d4e5f6789012345",
        "receiver": {
          "_id": "684a0000ce7cbc33d8946b00",
          "fullName": "jim",
          "email": "jim@yopmail.com",
          "image": null
        },
        "amount": 200,
        "amountCents": 20000,
        "message": "Need fuel help at the station",
        "status": "pending",
        "createdAt": "2026-07-08T14:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "totalPages": 1
  }
}
```

---

## 3. List received fuel requests

List fuel payment requests others have sent to you (requests you can pay or reject).

### Request

```http
GET /api/v1/fuel-requests/received?page=1&limit=20&status=pending
Authorization: Bearer <token>
```

**Query parameters**

Same as [List sent fuel requests](#2-list-sent-fuel-requests).

### Success response

```json
{
  "status": 200,
  "success": true,
  "message": "Fuel requests fetched successfully",
  "data": {
    "data": [
      {
        "_id": "686a1b2c3d4e5f6789012345",
        "sender": {
          "_id": "683ef251ce7cbc33d8946b9c",
          "fullName": "jack",
          "email": "jack@yopmail.com",
          "image": "http://localhost:6260/public/uploads/profile.jpg"
        },
        "amount": 200,
        "amountCents": 20000,
        "message": "Need fuel help at the station",
        "status": "pending",
        "createdAt": "2026-07-08T14:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "totalPages": 1
  }
}
```

---

## 4. Transfer fuel payment (approve & pay)

Receiver approves the request and transfers money from their wallet to the sender.

### Request

```http
PATCH /api/v1/fuel-requests/686a1b2c3d4e5f6789012345/transfer
Authorization: Bearer <token>
Content-Type: application/json
```

**Path parameters**

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `requestId` | string | Yes | Fuel request `_id` from received list |

**Body:** none

### Success response

```json
{
  "status": 200,
  "success": true,
  "message": "Fuel payment transferred successfully",
  "data": {
    "walletBalance": 10,
    "walletBalanceCents": 1000,
    "request": {
      "_id": "686a1b2c3d4e5f6789012345",
      "senderId": "683ef251ce7cbc33d8946b9c",
      "receiverId": "684a0000ce7cbc33d8946b00",
      "amount": 200,
      "amountCents": 20000,
      "message": "Need fuel help at the station",
      "status": "accepted",
      "updatedAt": "2026-07-08T14:05:00.000Z"
    }
  }
}
```

| Field | Description |
|-------|-------------|
| `walletBalance` | Receiver's wallet balance in USD dollars **after** the transfer |
| `walletBalanceCents` | Receiver's wallet balance in cents after the transfer |
| `request.status` | Updated to `"accepted"` |

### What happens on transfer

1. Receiver's wallet is debited by `amountCents`.
2. Sender's wallet is credited by the same amount.
3. Wallet transaction records are created for both users.
4. Request status changes from `pending` to `accepted`.

### Error responses

| HTTP | Message |
|------|---------|
| `400` | `"Please top up your wallet first"` (insufficient balance) |
| `403` | `"You do not have permission to perform this action"` (not the receiver) |
| `404` | `"Fuel request not found"` |
| `409` | `"This fuel request has already been processed"` |

---

## 5. Reject fuel request

Receiver declines a pending fuel payment request. No wallet movement occurs.

### Request

```http
PATCH /api/v1/fuel-requests/686a1b2c3d4e5f6789012345/reject
Authorization: Bearer <token>
Content-Type: application/json
```

**Path parameters**

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `requestId` | string | Yes | Fuel request `_id` from received list |

**Body:** none

### Success response

```json
{
  "status": 200,
  "success": true,
  "message": "Fuel request rejected",
  "data": {
    "_id": "686a1b2c3d4e5f6789012345",
    "senderId": "683ef251ce7cbc33d8946b9c",
    "receiverId": "684a0000ce7cbc33d8946b00",
    "amount": 200,
    "amountCents": 20000,
    "message": "Need fuel help at the station",
    "status": "rejected",
    "updatedAt": "2026-07-08T14:06:00.000Z"
  }
}
```

### Error responses

| HTTP | Message |
|------|---------|
| `403` | `"You do not have permission to perform this action"` |
| `404` | `"Fuel request not found"` |
| `409` | `"This fuel request has already been processed"` |

---

## Image URLs

Profile images in `sender` / `receiver` objects are returned as **full absolute URLs**:

```
http://localhost:6260/public/uploads/<filename>.jpg
```

Production uses your API host from `PUBLIC_API_URL` / `BASE_URL`. If `image` is `null`, show a default avatar.

---

## Status lifecycle

```
User A (sender)                    User B (receiver)
      │                                   │
      │  POST /  (amount, receiverUserId) │
      ├──────────────────────────────────►│  status: pending
      │                                   │
      │         PATCH /:id/transfer       │
      │◄──────────────────────────────────┤  debit B wallet
      │  credit A wallet                  │  status: accepted
      │                                   │
      │         PATCH /:id/reject           │
      │◄──────────────────────────────────┤  no wallet change
      │                                   │  status: rejected
```

- Only `pending` requests can be transferred or rejected.
- `accepted` and `rejected` requests are kept in the database for history.
- There is no cancel endpoint for the sender in v1.

---

## Wallet integration

Before calling **transfer**, the receiver should have enough wallet balance.

### Check balance (Wallet module)

```http
GET /api/v1/wallet/balance
Authorization: Bearer <receiver_token>
```

Example response:

```json
{
  "status": 200,
  "success": true,
  "message": "Wallet balance",
  "data": {
    "balance": 210,
    "balanceCents": 21000,
    "currency": "usd"
  }
}
```

### Example scenario

| Step | User | Action | Balance |
|------|------|--------|---------|
| 1 | sha | Has $108.80 | sha: $108.80 |
| 2 | sha1 | Tops up $210 | sha1: $210.00 |
| 3 | sha | Requests $200 from sha1 | Request pending |
| 4 | sha1 | Transfers request | sha1: $10.00, sha: $308.80 |

Use `"amount": 200` in the create request body (dollars), not `200` as cents.

---

## Mobile UI suggestions

### Sender screen (I need fuel money)

| Status | UI |
|--------|-----|
| `pending` | Show "Waiting for {receiver.fullName}" |
| `accepted` | Show "Paid — ${amount}" |
| `rejected` | Show "Declined by {receiver.fullName}" |

**Create flow:** Pick a friend from connections → enter dollar amount → optional message → `POST /`.

### Receiver screen (someone asked me to pay)

| Status | UI |
|--------|-----|
| `pending` | Show **Transfer** and **Reject** buttons |
| `accepted` | Show "You paid ${amount}" |
| `rejected` | Show "You declined" |

Before **Transfer**, compare `wallet.balance` with `request.amount`. If balance is too low, prompt user to top up via Wallet module.

---

## Quick reference — which ID to use

| Action | ID to pass | Example source |
|--------|------------|----------------|
| Send request | `receiverUserId` in body | `GET /connections/my-connections` → `data.data[]._id` |
| Transfer | `requestId` in URL | `GET /fuel-requests/received` → `data.data[]._id` |
| Reject | `requestId` in URL | `GET /fuel-requests/received` → `data.data[]._id` |
| List sent | — | Logged-in user is sender |
| List received | — | Logged-in user is receiver |

---

## Error code summary

| Error key | HTTP | Message |
|-----------|------|---------|
| `CANNOT_SELF_REQUEST` | 400 | You cannot send a fuel request to yourself |
| `NOT_CONNECTED` | 403 | You can only send fuel requests to your connections |
| `RECEIVER_NOT_FOUND` | 404 | Receiver not found |
| `NOT_FOUND` | 404 | Fuel request not found |
| `FORBIDDEN` | 403 | You do not have permission to perform this action |
| `ALREADY_PROCESSED` | 409 | This fuel request has already been processed |
| `INSUFFICIENT_FUNDS` | 400 | Please top up your wallet first |

---

## Source files (backend)

| File | Purpose |
|------|---------|
| `src/routes/fuelRequestRoutes.ts` | Route definitions |
| `src/controllers/fuelRequestController.ts` | HTTP handlers |
| `src/services/fuelRequestService.ts` | Business logic & wallet transfer |
| `src/models/FuelRequestModel.ts` | MongoDB schema |
| `src/validators/fuelRequestValidator/index.ts` | Request validation |
| `src/constants/messages.ts` | `FUEL_REQUEST_CONSTANTS` messages |

---

## Example: full flow (cURL)

```bash
# Users: sha (sender) and sha1 (receiver) are already accepted connections.

# 1. Login as sha (sender)
SHA_TOKEN="eyJhbGciOiJIUzI1NiIs..."

# 2. sha gets sha1's user ID from connections
curl "http://localhost:6260/api/v1/connections/my-connections?page=1&limit=20" \
  -H "Authorization: Bearer $SHA_TOKEN"

# 3. sha sends fuel request for $200 to sha1
curl -X POST "http://localhost:6260/api/v1/fuel-requests" \
  -H "Authorization: Bearer $SHA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "receiverUserId": "684a0000ce7cbc33d8946b00",
    "amount": 200,
    "message": "Need fuel at station"
  }'

# 4. Login as sha1 (receiver)
SHA1_TOKEN="eyJhbGciOiJIUzI1NiIs..."

# 5. sha1 lists received requests
curl "http://localhost:6260/api/v1/fuel-requests/received?status=pending&page=1&limit=20" \
  -H "Authorization: Bearer $SHA1_TOKEN"

# 6. sha1 checks wallet balance (optional)
curl "http://localhost:6260/api/v1/wallet/balance" \
  -H "Authorization: Bearer $SHA1_TOKEN"

# 7. sha1 approves and transfers payment
REQUEST_ID="686a1b2c3d4e5f6789012345"
curl -X PATCH "http://localhost:6260/api/v1/fuel-requests/$REQUEST_ID/transfer" \
  -H "Authorization: Bearer $SHA1_TOKEN" \
  -H "Content-Type: application/json"

# 8. sha lists sent requests to confirm status = accepted
curl "http://localhost:6260/api/v1/fuel-requests/sent?page=1&limit=20" \
  -H "Authorization: Bearer $SHA_TOKEN"
```

---

## Related modules

| Module | Base path | Used for |
|--------|-----------|----------|
| Auth | `/api/v1/auth` | Login / JWT token |
| Connections | `/api/v1/connections` | Friends list, must be connected before fuel request |
| Wallet | `/api/v1/wallet` | Balance check, top-up before paying a request |

See also: [connections-module-README.md](./connections-module-README.md)
