452 lines
14 KiB
Markdown
452 lines
14 KiB
Markdown
# Nearle Rider — API Reference
|
||
|
||
Generated from a source sweep of `lib/`. Every endpoint the app talks to, with
|
||
request payloads for all write operations.
|
||
|
||
## Base hosts
|
||
|
||
| Host | Used for | Notes |
|
||
|---|---|---|
|
||
| `jupiter.nearle.app` | all **reads** | env segment from `ApiConstants.mainRoute` (`live`) / `mainDev` (`dev`) |
|
||
| `queue.workolik.com` | all **writes** (status updates + logs) | hardcoded `live`, DNS pinned to `66.116.225.226`, TLS cert validation bypassed (`lib/providers/deliverylog/deliverylog_provider.dart:20`) |
|
||
| `msg.lionsms.com` | OTP SMS | |
|
||
| `maps.googleapis.com` | route distance | |
|
||
| `sgp1.digitaloceanspaces.com` → `images.nearle.app` | proof / ticket images (Minio SDK, not REST) | |
|
||
| `66.116.225.226:1883` | MQTT telemetry | |
|
||
|
||
**No auth header on any call.** Every request sends only
|
||
`Content-Type: application/json` / `Accept: application/json`. Identity is
|
||
carried as a `userid` field in the query string or body.
|
||
|
||
Endpoint constants live in `lib/views/helpers/constants/apiconstants.dart`.
|
||
|
||
---
|
||
|
||
## 1. Auth
|
||
|
||
### POST `https://jupiter.nearle.app/live/api/v2/users/rider/login`
|
||
`lib/providers/auth/auth_provider.dart:19`
|
||
|
||
```json
|
||
{
|
||
"contactno": "9876543210",
|
||
"devicetype": "android",
|
||
"configid": 1,
|
||
"deviceid": "<device id>",
|
||
"userfcmtoken": "<fcm token>",
|
||
"pin": 1234
|
||
}
|
||
```
|
||
`pin` is only included when non-null.
|
||
|
||
Response `details` is persisted to SharedPreferences: `userid`, `shiftid`,
|
||
`logid`, `riderid`, `partnerid`, `configid`, `logseconds`, `locationid`,
|
||
`tenantid`, `applocationid`, `userfcmtoken`, `firstname`, `lastname`,
|
||
`username`, `onduty`, `fuelcharge`, `firstmilecharge`, `starttime`, `endtime`,
|
||
`deliveryradius` (default 100 m).
|
||
|
||
### PUT `https://jupiter.nearle.app/live/api/v2/users/update`
|
||
Set / change MPIN — `lib/providers/auth/auth_provider.dart:186`
|
||
|
||
```json
|
||
{ "userid": 1242, "pin": 1234 }
|
||
```
|
||
|
||
### GET `https://jupiter.nearle.app/live/api/v1/platform/getsmsprovider?templatetypeid=1`
|
||
Fetches `templateid` + SMS `content` template. Cached 1 hour.
|
||
`lib/controllers/auth.dart:367`
|
||
|
||
### GET `https://msg.lionsms.com/api/smsapi`
|
||
`lib/controllers/auth.dart:407`
|
||
|
||
| Param | Value |
|
||
|---|---|
|
||
| `key` | `e57f5c9679af26077be1a7eadabb1b2a` |
|
||
| `route` | `7` |
|
||
| `sender` | `NEARLE` |
|
||
| `number` | `+91<phone>` |
|
||
| `templateid` | from `getsmsprovider` |
|
||
| `sms` | URL-encoded body with OTP substituted for `{#var#}` |
|
||
|
||
The OTP is **generated on-device** and stored in `SharedPreferences('lastOtp')`.
|
||
|
||
---
|
||
|
||
## 2. Deliveries — reads (all GET)
|
||
|
||
| Endpoint | Query | Caller |
|
||
|---|---|---|
|
||
| `…/{env}/api/v2/deliveries/getdeliveryqueues` | `userid, fromdate, todate, t` (+ optional `orderstatus`) | `lib/providers/delivery/delivery_provider.dart:34` |
|
||
| `…/{env}/api/v1/deliveries/getdeliveries` | `userid, fromdate, todate, t` | `lib/background/backgroundservice.dart:68` |
|
||
| `…/{env}/api/v2/deliveries/getdeliveries` | `userid, fromdate, todate, t` | hardcoded in `homepage.dart:1074`, `cartpage.dart:79`, `deliveries.dart:563` |
|
||
| `…/{env}/api/v3/deliveries/getdeliveries` | `userid, fromdate, todate, t` | picked orders — `delivery_provider.dart:92` |
|
||
|
||
- `fromdate` / `todate` are always **today** in `YYYY-MM-DD`.
|
||
- `t` is a cache-buster (epoch ms).
|
||
- Response shape: `{ "code": 200, "status": true, "message": "Success", "details": [ … ] }`.
|
||
Readers fall back to `data` then the raw body.
|
||
|
||
---
|
||
|
||
## 3. Delivery status updates
|
||
|
||
### PUT `https://queue.workolik.com/live/api/v1/deliveries/updatedelivery`
|
||
|
||
One endpoint, eight payload shapes. All from `lib/controllers/deliveries_controller.dart`.
|
||
|
||
Most variants share this GPS block:
|
||
`riderslat`, `riderslon`, `raw_latitude`, `raw_longitude`, `velocity_lat`,
|
||
`velocity_lng`, `speed`, `heading`.
|
||
|
||
#### accepted — `:695`
|
||
```json
|
||
{
|
||
"deliveryid": 0,
|
||
"orderheaderid": 0,
|
||
"orderstatus": "accepted",
|
||
"acceptedtime": "YYYY-MM-DD HH:MM:SS",
|
||
"riderslat": "", "riderslon": "",
|
||
"raw_latitude": "", "raw_longitude": "",
|
||
"velocity_lat": "", "velocity_lng": "",
|
||
"speed": "", "heading": "",
|
||
"deliverylat": "", "deliverylong": "",
|
||
"actualkms": "",
|
||
"deliveryamt": 0.0,
|
||
"notes": ""
|
||
}
|
||
```
|
||
|
||
#### active (start navigation) — `:1317`
|
||
Same as accepted, plus:
|
||
```json
|
||
{
|
||
"orderstatus": "active",
|
||
"activetime": "YYYY-MM-DD HH:MM:SS",
|
||
"starttime": "YYYY-MM-DD HH:MM:SS",
|
||
"activelat": "", "activelon": ""
|
||
}
|
||
```
|
||
Aborts if rider coordinates resolve to `0`.
|
||
|
||
#### arrived — `:1429`
|
||
Same as accepted, with `"orderstatus": "arrived"` and
|
||
`"arrivaltime": "YYYY-MM-DD HH:MM:SS"`. Geofence-checked against the pickup
|
||
location before sending.
|
||
|
||
#### picked — `:280`
|
||
No `raw_*` / velocity block on this one.
|
||
```json
|
||
{
|
||
"deliveryid": 0,
|
||
"orderheaderid": 0,
|
||
"pickuplocationid": 0,
|
||
"orderstatus": "picked",
|
||
"pickuptime": "YYYY-MM-DD HH:MM:SS",
|
||
"riderslat": "", "riderslon": "",
|
||
"deliverylat": "", "deliverylong": "",
|
||
"actualkms": "0.00",
|
||
"deliveryamt": 0.0,
|
||
"deliverytype": "",
|
||
"address": "", "city": "", "state": "", "suburb": "", "postcode": "",
|
||
"notes": "",
|
||
"pickupimage": "",
|
||
"proofimage": ""
|
||
}
|
||
```
|
||
`actualkms` is computed rider→pickup when not supplied. Geofence-checked.
|
||
|
||
#### delivered — `:1229`
|
||
```json
|
||
{
|
||
"deliveryid": 0,
|
||
"orderheaderid": 0,
|
||
"deliverylocationid": 0,
|
||
"orderstatus": "delivered",
|
||
"deliveredtime": "YYYY-MM-DD HH:MM:SS",
|
||
"deliverytime": "YYYY-MM-DD HH:MM:SS",
|
||
"smsdelivery": 0,
|
||
"riderslat": "", "riderslon": "",
|
||
"raw_latitude": "", "raw_longitude": "",
|
||
"velocity_lat": "", "velocity_lng": "",
|
||
"speed": "", "heading": "",
|
||
"pickuplat": "", "pickuplong": "",
|
||
"deliverylat": "", "deliverylong": "",
|
||
"riderkms": "",
|
||
"ridercharges": 0,
|
||
"deliveryamt": 0,
|
||
"collectionamt": 0,
|
||
"collectedamt": 0,
|
||
"collectionstatus": "",
|
||
"ridertime": 0,
|
||
"notes": "",
|
||
"wasskipped": false,
|
||
"bonuspts": 0,
|
||
"dropimage": ""
|
||
}
|
||
```
|
||
Returns `false` without sending if rider **or** drop coordinates are `0` or
|
||
out of range (`|lat| > 90`, `|lng| > 180`).
|
||
|
||
#### skipped — `:1758`
|
||
```json
|
||
{
|
||
"deliveryid": 0,
|
||
"orderheaderid": 0,
|
||
"orderstatus": "skipped",
|
||
"skippedtime": "YYYY-MM-DD HH:MM:SS",
|
||
"riderslat": "", "riderslon": "",
|
||
"deliverylat": "", "deliverylong": "",
|
||
"actualkms": "",
|
||
"deliveryamt": 0.0,
|
||
"notes": "",
|
||
"riderkms": "0.00"
|
||
}
|
||
```
|
||
`riderkms` is added only when the computed distance is > 0 (Google Directions,
|
||
falling back to straight-line).
|
||
|
||
#### rejected — `:1477`
|
||
Status block only, no timestamp: `"orderstatus": "rejected"` plus the shared
|
||
GPS block, `deliverylat`/`deliverylong`, `actualkms`, `deliveryamt`, `notes`.
|
||
|
||
#### cancelled — `:1813`
|
||
```json
|
||
{
|
||
"deliveryid": 0,
|
||
"orderheaderid": 0,
|
||
"orderstatus": "cancelled",
|
||
"canceltime": "YYYY-MM-DD HH:MM:SS",
|
||
"riderslat": "", "riderslon": "",
|
||
"raw_latitude": "", "raw_longitude": "",
|
||
"velocity_lat": "", "velocity_lng": "",
|
||
"speed": "", "heading": "",
|
||
"deliverylat": "", "deliverylong": "",
|
||
"riderkms": "0.00",
|
||
"ridercharges": 0,
|
||
"deliveryamt": 0.0,
|
||
"notes": ""
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Delivery log (background heartbeat)
|
||
|
||
### POST `https://queue.workolik.com/live/api/v2/deliveries/createdeliverylog`
|
||
|
||
**The body is wrapped in an array** — `json.encode([data])`
|
||
(`lib/providers/deliverylog/deliverylog_provider.dart:73`).
|
||
|
||
```json
|
||
[
|
||
{
|
||
"logid": 0,
|
||
"tenantid": 0,
|
||
"partnerid": 0,
|
||
"locationid": 0,
|
||
"orderheaderid": 0,
|
||
"deliveryid": 0,
|
||
"userid": 1242,
|
||
"orderid": "ORD123",
|
||
"orderstatus": "active",
|
||
"logdate": "YYYY-MM-DD HH:MM:SS",
|
||
"latitude": "", "longitude": "",
|
||
"raw_latitude": "", "raw_longitude": "",
|
||
"velocity_lat": "", "velocity_lng": "",
|
||
"speed": "", "heading": "",
|
||
"riderkms": 0.0,
|
||
"logstatus": 0
|
||
}
|
||
]
|
||
```
|
||
|
||
Base fields come from `_createPayload` (`backgroundservice.dart:236`); GPS,
|
||
`logdate`, `riderkms` and `logstatus` are merged in `_postDeliveryLog` (`:180`).
|
||
Posted roughly every 30 s per active delivery. 10 s timeout; failures are
|
||
queued in SharedPreferences and retried.
|
||
|
||
---
|
||
|
||
## 5. Rider log
|
||
|
||
### GET `…/{env}/api/v1/partners/getriderlog?userid=<id>`
|
||
Response `details` is cached as `riderlog_template` and reused as the base for
|
||
every subsequent create-log payload.
|
||
|
||
### GET `…/{env}/api/v1/partners/getridercount?userid=<id>`
|
||
|
||
### POST `https://queue.workolik.com/live/api/v2/partners/createriderlog`
|
||
Periodic on-duty ping — `lib/controllers/riderlog.dart:289`,
|
||
`lib/background/foreground_service.dart:289`
|
||
|
||
```json
|
||
{
|
||
"logid": 0,
|
||
"userid": 1242,
|
||
"partnerid": 0,
|
||
"shiftid": 0,
|
||
"logdate": "YYYY-MM-DD HH:MM:SS",
|
||
"login": "HH:MM:SS",
|
||
"latitude": "", "longitude": "",
|
||
"raw_latitude": "", "raw_longitude": "",
|
||
"velocity_lat": "", "velocity_lng": "",
|
||
"speed": "", "heading": "",
|
||
"onduty": 1,
|
||
"status": "active",
|
||
"contactno": "",
|
||
"username": "",
|
||
"firstname": "",
|
||
"lastname": "",
|
||
"tenantid": 0,
|
||
"locationid": 0,
|
||
"applocationid": 0,
|
||
"userfcmtoken": "",
|
||
"orderid": ""
|
||
}
|
||
```
|
||
|
||
- Built by spreading the cached `riderlog_template`, then overriding time, GPS
|
||
and identity fields.
|
||
- `status` is `"active"` when deliveries are live, otherwise `"idle"`.
|
||
- `firstname` / `lastname` keys are **removed** when empty; `username` is always
|
||
sent even if blank.
|
||
- Interval = `logseconds` from login, forced to **30 s** while deliveries are
|
||
active. 3 retries with 1 s backoff, then the offline queue (capped at 50).
|
||
|
||
### PUT `…/{env}/api/v1/partners/updateriderlog`
|
||
Duty toggle — `lib/controllers/riderlog.dart:520`, model at
|
||
`lib/Models/riders/riders_models.dart:275`
|
||
|
||
```json
|
||
{
|
||
"userid": 1242,
|
||
"logstatus": null,
|
||
"Logout": null,
|
||
"workhours": null,
|
||
"shorthours": null,
|
||
"latitude": "",
|
||
"longitude": "",
|
||
"onduty": 1
|
||
}
|
||
```
|
||
Note the capitalized `Logout` key (and `Login` in `RiderLogin.toJson`).
|
||
|
||
---
|
||
|
||
## 6. Breaks
|
||
|
||
### POST `https://queue.workolik.com/live/api/v2/partners/createbreaklog`
|
||
`lib/controllers/riderlog.dart:925`
|
||
|
||
```json
|
||
{
|
||
"breakid": 427,
|
||
"logid": 0,
|
||
"breakdate": "YYYY-MM-DD HH:MM:SS",
|
||
"userid": 1242,
|
||
"partnerid": 0,
|
||
"shiftid": 0,
|
||
"breakstart": "HH:MM:SS",
|
||
"breakend": "",
|
||
"breakhours": 0.0,
|
||
"latitude": "",
|
||
"longitude": ""
|
||
}
|
||
```
|
||
`breakid` is a **client-generated random 3-digit** value (100–999), replaced by
|
||
the server id when one comes back.
|
||
|
||
Also emitted by the auto-shift-end flow at `backgroundservice.dart:765`.
|
||
|
||
### PUT `https://queue.workolik.com/live/api/v2/partners/updatebreaklog`
|
||
`lib/controllers/riderlog.dart:196`
|
||
|
||
Same shape, with `breakend` filled and `breakhours` as fractional hours
|
||
(`duration.inSeconds / 3600`).
|
||
|
||
---
|
||
|
||
## 7. Stats & rewards (GET)
|
||
|
||
| Endpoint | Source |
|
||
|---|---|
|
||
| `…/live/api/v2/partners/getdeliverystats?userid=<id>` | `lib/providers/summary/summary.dart:10` |
|
||
| `…/live/api/v1/partners/getriderweeklykms?userid=<id>` | `lib/views/Dashboard/summary/summary.dart:318` |
|
||
| `…/live/api/v1/utils/getuserbonussummary/?userid=<id>` | `lib/controllers/rewards_controller.dart:16` |
|
||
|
||
---
|
||
|
||
## 8. Support tickets
|
||
|
||
### GET `…/live/api/v1/partners/getridersupport/?userid=<id>`
|
||
|
||
### POST `…/live/api/v1/partners/createridersupport/`
|
||
`lib/controllers/support_ticket.dart:186`
|
||
|
||
```json
|
||
{
|
||
"userid": 1242,
|
||
"category": "",
|
||
"priority": "",
|
||
"subject": "",
|
||
"issue": "",
|
||
"image": "https://images.nearle.app/support/ticket-1234-1242.jpg"
|
||
}
|
||
```
|
||
|
||
Images are uploaded directly to DigitalOcean Spaces with the Minio SDK
|
||
(bucket `nearle`, folder `support`, region `sgp1`) — not through the API. The
|
||
returned CDN URL is what goes in `image`.
|
||
|
||
---
|
||
|
||
## 9. External services
|
||
|
||
- **GET** `https://maps.googleapis.com/maps/api/directions/json?origin=<lat,lng>&destination=<lat,lng>&key=<key>&units=metric&mode=driving`
|
||
— road distance for km calculation, 5 s timeout (`deliveries_controller.dart:1115`)
|
||
- `launchUrl` only (no HTTP from the app):
|
||
- `https://www.google.com/maps/dir/?api=1&destination=<lat>,<lng>` (`nav.dart:444`)
|
||
- `https://nearle.in/terms`, `https://nearle.in/privacy`, `https://nearle.in/faq`
|
||
- `https://play.google.com/store/apps/details?id=com.nearle.partner`
|
||
- `https://apps.apple.com/us/app/nearle/id1596895375ls=1`
|
||
|
||
### MQTT
|
||
`66.116.225.226:1883`, user `admin` (`lib/views/helpers/constants/mqtt_constants.dart`)
|
||
|
||
| Topic | Payload |
|
||
|---|---|
|
||
| `nearle/riders/{riderId}/status` | `Online` / `Offline` / `Idle` / `Active` |
|
||
| `nearle/riders/{riderId}/profile` | `userid, username, firstname, lastname, contactno, userfcmtoken, app_version, device_info` |
|
||
| `nearle/riders/{riderId}/location` | `userid, orderid, lat, lng, raw_lat, raw_lng, speed, heading, velocity_lat, velocity_lng, timestamp` |
|
||
| `nearle/riders/{riderId}/telemetry` | battery, charging, speed, connection, accuracy |
|
||
| `nearle/riders/{riderId}/logs` | `rider_periodic_log`, `delivery_picked`, `critical_battery`, alerts |
|
||
|
||
---
|
||
|
||
## Known issues found during the sweep
|
||
|
||
1. **`lib/controllers/riderkm.dart:11` builds a broken URL.** `baseUrl` is
|
||
already `…/v1/partners/getriderweeklykms`, and the code appends
|
||
`/getriderweeklykms?userid=` → `…/getriderweeklykms/getriderweeklykms?userid=`.
|
||
The summary *view* calls it correctly, so this controller path is likely dead
|
||
or silently failing.
|
||
2. **`lib/controllers/support_ticket.dart:66` hardcodes `userId = 1242`** in
|
||
`fetchTickets()` — every rider sees user 1242's ticket list.
|
||
3. **Secrets shipped in the APK**: DO Spaces access key + secret
|
||
(`support_ticket.dart`, `deliveries_controller.dart`), LionSMS API key,
|
||
MQTT `admin` / `Package@321#`, Google Maps key.
|
||
4. **OTP is generated and verified on-device**, stored in
|
||
`SharedPreferences('lastOtp')` — trivially bypassable.
|
||
5. **TLS verification disabled** for `queue.workolik.com` plus a hardcoded IP
|
||
pin (`66.116.225.226`). If that VPS IP changes, every write endpoint breaks
|
||
at once.
|
||
6. **`backgroundservice.dart:845` POSTs to `updateriderlog`** while every other
|
||
caller PUTs it. If the server is method-strict, auto-shift-end never marks
|
||
the rider offline.
|
||
7. `updateDeliveryDev`, `createRiderLogDev`, `createBreakRiderLogDev`,
|
||
`updateBreakRiderLogDev` and `createDeliveryLogDev` all point at **live** —
|
||
there is no working dev environment for writes.
|
||
8. `lib/providers/support/support_ticket.dart` is a dead duplicate of the
|
||
controller and references an `…/partners/uploadimage/` endpoint nothing else
|
||
uses.
|