POS till authorization
This document describes how cashier-sensitive POS actions are gated behind tenant administrator approval (owner, branch manager, or supervisor), and how that design reduces employee misuse.
Problem
Cashiers need fast checkout, but several actions carry financial or compliance risk if used without oversight:
- Refunds and returns
- Issuing customer invoices from the cart
- Line discounts and line notes
- Selling on account (credit)
- Owner-restricted payment methods
- Cart corrections (remove line, decrease quantity, clear cart, discard held sale)
Previously, only cart edits had a partial supervisor flow (owner-only dashboard approval). Refunds, invoices, and discounts relied on static role permissions that cashiers could not hold—but nothing stopped a cashier from attempting them in the UI, and server enforcement was inconsistent.
Design principles
- Server is authoritative — UI prompts are for UX; every sensitive endpoint re-checks authorization. A cashier cannot bypass approval by calling the API directly.
- Cashier-only gating — Supervisors and owners with the right permissions perform actions without a temporary grant.
- Time-limited grants — After approval, the cashier receives a short-lived cache grant (15 minutes for most actions; 25 minutes for cart-edit bucket).
- Two approval channels — Remote (dashboard) or physical (supervisor at counter with password). Fingerprint hardware can replace password verification in a future release using the same audit hook.
- Full audit trail — Requests and approvals are recorded as
PosEvententries (SUPERVISOR_REQUESTED,SUPERVISOR_APPROVED).
Protected actions
| Action key | What it covers |
|---|---|
clear_cart |
Empty the active cart |
remove_line |
Remove a cart line |
decrease_qty |
Lower quantity on a line |
discard_hold |
Delete a parked / held sale |
refund_return |
Open or post sale returns |
issue_invoice |
Create/issue invoice from POS cart |
line_discount |
Line-level discount % or notes |
sell_on_credit |
On-account payment method |
owner_payment_method |
Payment methods flagged owner-only |
Cart-edit actions share one grant bucket (cart_edit) so one approval unlocks all cart corrections for the window.
Roles and permissions
pos_till_approve— Assigned toowner,branch_manager, andsupervisor. Allows listing pending requests and approving/dismissing them.- Approvers can also authorize at the counter by entering their own login password (verified with
Hash::check).
Cashiers do not receive refund, discount, credit, or till-approve permissions.
Cashier experience (POS)
When a cashier triggers a protected action:
- The Administrator authorization dialog opens.
- At counter — Cashier selects a supervisor; supervisor enters their password. On success, a grant is stored in cache.
- Dashboard request — A
PosSupervisorRequestrow is created; administrators see it on the dashboard widget Till authorization requests.
The cashier can tap Check if approved to poll grant status after a remote approval.
Sensitive entry points wired in the UI:
- Cart remove / qty down / clear
- Footer Refund (F8)
- Invoice from cart
- Line details (discount / notes)
- On-account and owner-only payment methods
- Manager panel (F7) request buttons
Administrator experience (dashboard)
Users with pos_till_approve see pending requests with human-readable labels (e.g. “Refund / return”, “Issue invoice from cart”). Approve calls PosTillAuthorizationService::grant() for the specific action (or cart-edit bucket). Dismiss closes the request without granting access.
JSON API (for integrations or mobile supervisor apps):
GET /pos/supervisor-requests/pendingPOSTto each row’sapprove_urlordismiss_urlwith session auth + CSRF
At-counter API (cashier session):
GET /pos/till-authorization/approversGET /pos/till-authorization/grantsPOST /pos/till-authorization/verify-at-counter
Server enforcement
| Endpoint / validator | Check |
|---|---|
SaleReturnController::create/store |
refund_return grant or refund_sales permission |
PosCheckoutInvoiceController |
issue_invoice grant (cashiers) |
StoreSaleRequest |
Grants for line discount, credit, owner payment methods |
ParkedSaleController::destroy |
discard_hold grant or non-cashier |
| Cart edit unlock (existing) | cart_edit bucket |
Misuse prevention
| Risk | Mitigation |
|---|---|
| Cashier issues unauthorised refund | Return routes return 403 without grant; UI requires approval first |
| Cashier discounts friends’ purchases | Line discount blocked in StoreSaleRequest without grant; supervisor password logged on at-counter approval |
| Credit sales without approval | On-account payment blocked client- and server-side without sell_on_credit grant |
| Invoice fraud before payment | Invoice API requires grant for cashiers |
| Cart manipulation to hide theft | Cart edits need approval; one destructive edit consumes cart grant (existing behaviour) |
| Supervisor collusion | All approvals logged with approver user id, cashier id, action, and mode (dashboard vs at_counter) |
| Stale wide-open access | Grants expire automatically (15–25 minutes); not persisted in DB |
| Cross-tenant approval | Organization id matched on requests, approvers, and verify-at-counter |
Fingerprint (future)
The at-counter flow is intentionally password-based today so it works on any device. The verifyAtCounter path records mode: at_counter in PosEvent; a biometric driver can call the same grant() + audit sequence after hardware verification without changing cashier or dashboard flows.
Key files
app/Support/Pos/PosTillAuthorizationAction.php— action constants and labelsapp/Services/Pos/PosTillAuthorizationService.php— grants, verification, approver listsapp/Http/Controllers/PosSupervisorRequestController.php— dashboard requestsapp/Http/Controllers/PosTillAuthorizationController.php— at-counter verify + grant pollingresources/js/pos-till-authorization.js— POS dialog and client orchestrationresources/views/dashboard/widgets/supervisor_requests.blade.php— dashboard widgetconfig/rbac.php—pos_till_approvepermission
Testing
Run:
php artisan test --filter=SupervisorPendingApiTest
Extend coverage for supervisor approver role and at-counter verification as the suite grows.