pos login with the ph number and pin

This commit is contained in:
2026-08-12 17:10:27 +05:30
parent ff72af9a8a
commit d566ca5591
9 changed files with 1007 additions and 91 deletions

View File

@@ -29,10 +29,10 @@ told.**
```bash
BASE=https://fiesta.nearle.app/live/api/v1/pos
# 1. Sign in
# 1. Sign in — a mobile number and a 4-digit PIN
curl -s -X POST $BASE/login \
-H 'Content-Type: application/json' \
-d '{"authname":"rsselvapuram@gmail.com","password":"…","terminal_id":"T5EDD"}'
-d '{"contactno":"9876543210","pin":"4821","terminal_id":"T5EDD"}'
# 2. Use the token on everything else
curl -s $BASE/session -H "Authorization: Bearer $TOKEN"
@@ -44,9 +44,9 @@ curl -s $BASE/session -H "Authorization: Bearer $TOKEN"
These steps are in order, and the order matters.
**1. Sign in.** `POST /login` with the operator's own credentials — the same
`app_users` account they use for the web console. There is no separate POS
password.
**1. Sign in.** `POST /login` with the operator's **mobile number and 4-digit
PIN** — the pair the back office issued them. Both are held on their own
`app_users` row; there is no separate POS credential store.
**2. Read `store_id` out of the response.** Do not ask anyone to type it. It is
whatever the back office says that account's outlet is.
@@ -74,8 +74,8 @@ The only unauthenticated route. It is where a token comes from.
```json
{
"authname": "rsselvapuram@gmail.com",
"password": "…",
"contactno": "9876543210",
"pin": "4821",
"terminal_id": "T5EDD",
"device_id": "a5f3…",
"location_id": 1135,
@@ -85,15 +85,15 @@ The only unauthenticated route. It is where a token comes from.
| Field | Required | Notes |
|---|---|---|
| `authname` | yes* | Email. **Or** send `contactno` instead. |
| `contactno` | yes* | Mobile number, as an alternative to `authname`. |
| `password` | yes | |
| `contactno` | yes | Mobile number. Send it as typed — `+91 98765 43210`, `098765-43210` and `9876543210` all reach the same account. |
| `pin` | yes | Exactly 4 digits, never starting with `0`. |
| `terminal_id` | no | This till's short code, e.g. `T5EDD`. Recorded on the session. |
| `device_id` | no | The device's stable UUID. |
| `location_id` | no | **Only** meaningful for a multi-outlet account. A request, not an assertion — it is checked against what the account may reach. |
| `configid` | no | Inferred when absent. Send it only if you get the ambiguity error below. |
| `authname` + `password` | no | The previous way in. Still accepted, so a shop whose numbers have not been backfilled is not stranded — see [POS_PHONE_PIN_LOGIN_HANDOVER.md](POS_PHONE_PIN_LOGIN_HANDOVER.md). |
\* one of `authname` or `contactno`.
`pin` wins if a password is sent as well; `authname` wins over `contactno`.
### Response — `200`
@@ -128,7 +128,7 @@ The only unauthenticated route. It is where a token comes from.
"staff": [
{ "user_id": 1148, "full_name": "Ragul Kannan",
"role": "Super admin", "pin": "1111", "status": "Active" }
"role": "Super admin", "status": "Active" }
]
}
}
@@ -156,7 +156,10 @@ sign-in.
**`locations`** — every outlet this account may open a till at. Length 1 is the
normal case.
**`staff`** — see [Staff and PINs](#staff-and-pins). **Often empty.**
**`staff`** — see [Staff and PINs](#staff-and-pins). **Often empty**, and it
**no longer carries `pin`**: a PIN is now half of the sign-in, so a list of them
is a list of working credentials for the outlet. Switch operator through
`POST /login/pin` instead.
---
@@ -191,9 +194,13 @@ Requires the token. Returns `401` when there isn't one.
Who may ring a bill at this terminal's outlet. For pulling down somebody hired
mid-shift without signing the terminal out.
**Takes no parameters.** The answer carries PINs, so the outlet comes from the
caller's own token — a till must not be able to ask who works at the shop next
door. A request without a token is refused whatever the enforcement setting is.
**Takes no parameters.** The outlet comes from the caller's own token — a till
must not be able to ask who works at the shop next door. A request without a
token is refused whatever the enforcement setting is.
Names and roles only; **`pin` is not returned here either**, for the same reason
it left the login session. A supervisor who needs to see or change one uses
`GET /pos/users`, which is role-gated.
```json
{
@@ -258,14 +265,18 @@ login, it is simply not found.
one, 22 live accounts have it including a delivery rider, and it grants nothing
on either side.
### Every till account gets its own username and password
### Every till account gets its own number and PIN
Both roles. A PIN cannot open a *closed* terminal — `/pos/login/pin` requires a
session that already exists — so a PIN-only account works only while somebody
else is standing there to unlock the till first. For a Supervisor that was an
outright deadlock; for a Cashier it meant a shop that could not open until two
people had arrived, and whoever gets in at seven is as often the cashier as the
supervisor.
Both roles. A PIN now opens a *closed* terminal too, paired with the account's
mobile number — which is what removed the old deadlock: `/pos/login/pin` needs a
session that already exists, so before this a PIN-only account could not unlock
a till at all. For a Supervisor that was an outright deadlock; for a Cashier it
meant a shop that could not open until two people had arrived, and whoever gets
in at seven is as often the cashier as the supervisor.
**So a till account needs both `contactno` and `pin` set.** One without the
other cannot sign in. A username and password still work, and every account
created before this still has them.
So a Cashier signs in exactly like a Supervisor does, and the *role* decides
what they get — not which credential they used:
@@ -293,9 +304,10 @@ outlet — becomes `cashier2.1185@pos.nearle.in`; a name **you** supplied is nev
adjusted, it is refused, because silently signing somebody in as another
person's address is worse than an error.
The PIN stays optional. It switches operator at an open counter, which not every
shop does, and it is the one credential the till holds in plaintext to hand
around — so it is set deliberately, never by default.
**The PIN is no longer optional in practice.** The field still is — creation
accepts an account without one — but an account with no PIN cannot sign a
terminal in, and is told so by name: *"this account has no PIN set; ask your
supervisor to set one in the web console first."*
---
@@ -303,11 +315,12 @@ around — so it is set deliberately, never by default.
For a cashier taking over a counter a supervisor has already opened.
**Requires an existing valid token.** That is the security model, not an
oversight: four digits is ten thousand guesses, which is no barrier at all to an
anonymous caller. Tying it to a session means a supervisor has opened the
terminal with a real password first, and the guesses are confined to that one
outlet's staff.
**Requires an existing valid token.** A PIN alone is four digits — ten thousand
guesses, and no barrier to an anonymous caller. Tying it to a session confines
the guesses to one outlet's staff, at a terminal somebody has already opened.
That is why this route takes a bare PIN and `/login` does not: there, the PIN is
checked against one mobile number, and the number is what makes the pair worth
anything.
```bash
curl -s -X POST $BASE/login/pin \
@@ -486,23 +499,29 @@ GET /catalogue?store_id=1185 → 403 {"message":"this session cannot reach o
| Code | Meaning | What the till should do |
|---|---|---|
| `400` | Body unreadable, or neither `authname` nor `contactno` sent | Fix the request |
| `401` | `those sign-in details were not recognised` | Ask them to re-type. **Wrong email and wrong password give the same message** — deliberately, so the endpoint isn't a directory of who banks here |
| `400` | Body unreadable, or `a mobile number is required` / `a PIN is required` — one of the two fields is empty | Fix the request; do not send it again unchanged |
| `401` | `those sign-in details were not recognised` | Ask them to re-type. **A wrong number and a wrong PIN give the same message** — deliberately, so the endpoint isn't a directory of who banks here. A number that cannot be ten digits, and a PIN that is not four, answer the same way |
| `403` | Real account, but it can't open this till | Show the message; re-typing won't help |
The `403` messages, verbatim:
- `this account is not set up for the till; ask your store admin to add you as a Supervisor or Cashier in the web console`
- `this account is inactive; contact your administrator`
- `this account has no PIN set; ask your supervisor to set one in the web console first`
- `this account has no password set; set one in the web console first`
- `this account is not attached to a tenant and cannot open a till`
- `no active outlet is registered for this account`
- `this account cannot open a till at outlet 1185`
- `more than one account uses these sign-in details; ask your administrator for the configid and send it with the login`
That last one is real, not theoretical: `authname` is not unique in this schema.
Live data has the same address twice. We refuse rather than pick one, because
picking wrong means billing into another tenant's books.
That last one is real, not theoretical: neither `authname` nor `contactno` is
unique in this schema. Live data has the same address twice, and 34 mobile
numbers shared by 104 accounts. We refuse rather than pick one, because picking
wrong means billing into another tenant's books.
Sign-in only ever looks at **till accounts** (roleid 7 and 8), which is what
makes a number workable as a credential: it has to be unique among a tenant's
own till staff, not across all 608 users on the platform.
The **first** one is the common case now, and it is deliberately specific where a
bad password is deliberately vague. By the time it fires the caller has already