docs: align flow docs with code reality + create 35 implementation issue files
Flow docs updated (11 files): - Delivery Confirmation: reversed actor roles (buyer generates, seller verifies), fixed endpoint paths (/delivery-code/generate, /delivery-code/verify) - Passkey (WebAuthn): removed stub/simulated-key claims; real @simplewebauthn/server attestation is implemented; refresh tokens are persisted - Dispute: corrected resolve schema (action enum), removed non-existent statuses, documented security gaps (no role guards on status/resolve/assign), route shadowing, all socket events are TODO stubs - Seller Offer: corrected all endpoint paths, removed 'active' status, documented withdraw dead code, missing seller history page, select-offer notification gap - Notification: corrected mark-all-read method+path, fixed GET /:id broken lookup, added unread-count-update socket event - Authentication: corrected rate limiter (counts all attempts), axios 403 not handled, deleteAccount wrong endpoint bug, changePassword no UI - Password Reset: corrected 6-digit code (not 8), documented no-complexity gap on reset-with-code vs token reset - Payment Flow DePay: /create→/save, removed phantom sub-routes, SIM_ bypass risk, PaymentProvider type gap, getProviderIntentEndpoint routing bug - Payment Flow SHKeeper: removed phantom polling endpoint, fixed release/refund paths - Purchase Request: added pending_payment/active statuses, fixed sellers/attachments endpoints, corrected socket events, PUT→PATCH bug - Escrow: documented dispute resolve does not touch escrow, route shadowing, confirm-delivery auth gap Issues created (35 files in Issues/): - 9 security issues (critical) including: dispute privilege escalation ×4, unauthenticated payment/scanner endpoints ×2, SIM_ production bypass, confirm-delivery ownership gap - 26 additional major/critical bugs covering broken endpoints, missing features, data integrity gaps, and frontend-backend mismatches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,17 +2,19 @@
|
||||
title: Delivery Confirmation Flow
|
||||
tags: [flow, delivery, escrow-release, code]
|
||||
related_models: ["[[PurchaseRequest]]", "[[Payment]]"]
|
||||
related_apis: ["POST /api/marketplace/purchase-requests/:id/delivery-code", "POST /api/marketplace/purchase-requests/:id/verify-delivery"]
|
||||
related_apis: ["POST /api/marketplace/purchase-requests/:id/delivery-code/generate", "POST /api/marketplace/purchase-requests/:id/delivery-code/verify"]
|
||||
---
|
||||
|
||||
# Delivery Confirmation Flow
|
||||
|
||||
After the escrow is funded ([[PRD - Request Network In-House Checkout]] / [[Escrow Flow]]) and the seller has prepared the item, the seller **marks shipped**, the buyer **enters a delivery code** to confirm receipt, and the escrow becomes eligible for release ([[Payout Flow]]).
|
||||
> **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md))
|
||||
|
||||
After the escrow is funded ([[PRD - Request Network In-House Checkout]] / [[Escrow Flow]]) and the seller has prepared the item, the seller **marks shipped**, the buyer **generates and reads out the delivery code**, the seller **verifies the code** to confirm receipt, and the escrow becomes eligible for release ([[Payout Flow]]).
|
||||
|
||||
## Actors
|
||||
|
||||
- **Seller** — marks the order shipped and presents the delivery code to the buyer at hand-off.
|
||||
- **Buyer** — confirms by entering the code in the dashboard.
|
||||
- **Buyer** — after the order reaches `delivery` status, explicitly generates the delivery code and reads it out to the seller at hand-off.
|
||||
- **Seller** — types the code into their dashboard to confirm delivery.
|
||||
- **Backend** — `DeliveryService` (`backend/src/services/delivery/DeliveryService.ts`), exposed through the marketplace routes (`backend/src/services/marketplace/routes.ts`).
|
||||
- **MongoDB** — `purchaserequests.deliveryInfo` subdocument fields.
|
||||
- **Socket.IO** — `delivery-code-generated`, `delivery-update`.
|
||||
@@ -24,21 +26,22 @@ After the escrow is funded ([[PRD - Request Network In-House Checkout]] / [[Escr
|
||||
|
||||
## Step-by-step narrative
|
||||
|
||||
1. **Seller marks shipped** — from the seller step `frontend/src/sections/request/components/seller-steps/step-3-ship-goods.tsx`, clicks "Mark as shipped". This patches the request status to `delivery`.
|
||||
2. **Delivery code generation** — when the order transitions to `delivery`, `DeliveryService.generateDeliveryCode(requestId)` is invoked. It:
|
||||
1. **Seller marks shipped** — from the seller step `frontend/src/sections/request/components/seller-steps/step-3-ship-goods.tsx`, clicks "Mark as shipped". This patches the request status to `delivery`. No code is generated at this point.
|
||||
2. **Buyer generates the delivery code** — once status is `delivery`, the buyer explicitly triggers `POST /api/marketplace/purchase-requests/:id/delivery-code/generate` (buyerId is enforced server-side). `DeliveryService.generateDeliveryCode(requestId)`:
|
||||
- Generates a 6-digit code (`Math.floor(100000 + Math.random()*900000)`).
|
||||
- Sets `deliveryInfo.deliveryCode`, `deliveryCodeGeneratedAt = now`, `deliveryCodeExpiresAt = now + 7d`, `deliveryCodeUsed = false`.
|
||||
- Emits `delivery-code-generated` and `delivery-update` to `request-{requestId}`.
|
||||
- Sends a notification to the buyer with the code (in-app, and via email if configured).
|
||||
3. **Buyer entry** — buyer meets the courier / picks up the item, enters the code in `frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx` (also surfaced on the buyer side via `step-5-receive-goods.tsx`).
|
||||
4. **Verification** — `POST /api/marketplace/purchase-requests/:id/verify-delivery` with `{ code }`:
|
||||
- The code is displayed to the buyer in `frontend/src/sections/request/components/buyer-steps/step-5-receive-goods.tsx`.
|
||||
3. **Buyer reads code to seller** — at hand-off the buyer reads the 6-digit code out loud (or shows it) to the seller.
|
||||
4. **Seller enters code** — seller types the code into `frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx`.
|
||||
5. **Verification** — `POST /api/marketplace/purchase-requests/:id/delivery-code/verify` with `{ code }` (selectedOffer.sellerId is enforced server-side):
|
||||
- Matches `code` against `deliveryInfo.deliveryCode`.
|
||||
- Checks `deliveryCodeExpiresAt > now` and `deliveryCodeUsed === false`.
|
||||
- On success: `deliveryInfo.deliveryCodeUsed = true; deliveryCodeUsedAt = now`. Status flips `delivery → delivered`.
|
||||
- Emits `purchase-request-update` `status-changed`.
|
||||
- Triggers buyer/seller notifications via `notifyDeliveryConfirmed` (see `PurchaseRequestService.ts:631-641`).
|
||||
5. **Optional auto-release timer** — once `status === 'delivered'`, a scheduled job can flip the request to `confirming` and then to `seller_paid` after a grace period (e.g. 48h). The auto-release worker is not yet implemented; today an admin completes the chain via [[Payout Flow]].
|
||||
6. **Manual fast-track** — the buyer can also tap "Confirm I received it" to skip the code (used when the code path fails — e.g. lost in transit) which patches `status` to `delivered`. This relies on admin trust.
|
||||
6. **Alternative path — buyer fast-track** — the buyer can also call `PATCH .../confirm-delivery` to set status to `delivered` without any code (used when the code path fails, e.g. code expired or lost). **⚠️ Authorization gap:** this endpoint currently has no authorization check; any authenticated user can call it.
|
||||
7. **Optional auto-release timer** — once `status === 'delivered'`, a scheduled job can flip the request to `confirming` and then to `seller_paid` after a grace period (e.g. 48h). The auto-release worker is not yet implemented; today an admin completes the chain via [[Payout Flow]].
|
||||
|
||||
## Sequence diagram
|
||||
|
||||
@@ -55,14 +58,17 @@ sequenceDiagram
|
||||
S->>FE: Click "Mark as shipped"
|
||||
FE->>BE: PATCH /api/marketplace/purchase-requests/{id} {status:"delivery"}
|
||||
BE->>DB: PurchaseRequest.status="delivery"
|
||||
BE->>BE: DeliveryService.generateDeliveryCode
|
||||
Note over BE,DB: No code generated here
|
||||
|
||||
B->>FE: View delivery code in step-5-receive-goods
|
||||
FE->>BE: POST /api/marketplace/purchase-requests/{id}/delivery-code/generate
|
||||
BE->>DB: deliveryInfo.deliveryCode=XXXXXX\nexpires=+7d
|
||||
BE->>IO: emit request-{id} 'delivery-code-generated'
|
||||
BE->>B: notification w/ code (in-app/email)
|
||||
FE->>B: Display 6-digit code
|
||||
|
||||
S->>B: At hand-off, share the 6-digit code (verbally)
|
||||
B->>FE: Enter code in dashboard
|
||||
FE->>BE: POST /api/marketplace/purchase-requests/{id}/verify-delivery {code}
|
||||
B->>S: At hand-off, read the 6-digit code aloud
|
||||
S->>FE: Enter code in delivery-code-verification
|
||||
FE->>BE: POST /api/marketplace/purchase-requests/{id}/delivery-code/verify {code}
|
||||
BE->>DB: match code, expires>now, !used
|
||||
BE->>DB: set deliveryCodeUsed = true
|
||||
BE->>DB: set status = "delivered"
|
||||
@@ -77,9 +83,26 @@ sequenceDiagram
|
||||
| Method | Endpoint | Purpose |
|
||||
|---|---|---|
|
||||
| `PATCH` | `/api/marketplace/purchase-requests/:id` `{status:"delivery"}` | Seller marks shipped |
|
||||
| `POST` | `/api/marketplace/purchase-requests/:id/delivery-code` | Manual code regeneration (admin) |
|
||||
| `POST` | `/api/marketplace/purchase-requests/:id/verify-delivery` | Buyer confirms with code |
|
||||
| `PATCH` | `/api/marketplace/purchase-requests/:id/confirm-delivery` | Buyer fast-track confirm (no code) |
|
||||
| `GET` | `/api/marketplace/purchase-requests/:id/delivery-code` | Retrieve current code (buyer + seller) |
|
||||
| `POST` | `/api/marketplace/purchase-requests/:id/delivery-code/generate` | Buyer generates delivery code (buyer only) |
|
||||
| `POST` | `/api/marketplace/purchase-requests/:id/delivery-code/verify` | Seller verifies code (seller only) |
|
||||
| `GET` | `/api/marketplace/purchase-requests/:id/delivery-code/status` | Check code status (buyer + seller) |
|
||||
| `PATCH` | `/api/marketplace/purchase-requests/:id/confirm-delivery` | Buyer fast-track confirm (no code) — ⚠️ no auth check |
|
||||
|
||||
### Phantom frontend actions (routes do NOT exist on backend)
|
||||
|
||||
These Redux/API actions exist in the frontend but call endpoints that return 404:
|
||||
|
||||
| Frontend action | Called path | Behaviour |
|
||||
|---|---|---|
|
||||
| `regenerateDeliveryCode` | `/delivery-code/regenerate` | 404s; frontend falls back to `/delivery-code/generate` |
|
||||
| `getDeliveryAttempts` | `/delivery-code/attempts` | 404s — feature not implemented |
|
||||
| `getDeliveryStats` | `/delivery/stats` | 404s — feature not implemented |
|
||||
|
||||
## Two paths to `delivered` status
|
||||
|
||||
1. **Code path** — seller calls `POST .../delivery-code/verify` with the correct, unexpired code → status becomes `delivered`.
|
||||
2. **Fast-track path** — buyer calls `PATCH .../confirm-delivery` (no code required) → also becomes `delivered`. ⚠️ Currently no authorization check on this endpoint.
|
||||
|
||||
## Database writes
|
||||
|
||||
@@ -92,24 +115,23 @@ sequenceDiagram
|
||||
- **`delivery-code-generated`** → `request-{id}` (with code, expiresAt).
|
||||
- **`delivery-update`** → `request-{id}` (`type: 'code-generated'`).
|
||||
- **`purchase-request-update`** `status-changed` on `delivery → delivered`.
|
||||
- **`new-notification`** → `user-{buyerId}` with the code.
|
||||
|
||||
## Side effects
|
||||
|
||||
- Code is **emitted via socket and in-app notification**. If a malicious actor has access to the buyer's notifications, they could intercept and confirm delivery prematurely. Treat the code as confidential at the UI layer.
|
||||
- The code is shown only to the **buyer** in their dashboard. The buyer verbally shares it with the seller — there is no backend push of the code to the seller.
|
||||
- Triggers the path that eventually frees up the escrow (manual today via [[Payout Flow]], auto in the future).
|
||||
|
||||
## Error / edge cases
|
||||
|
||||
- **Wrong code** → `400 Invalid delivery code`.
|
||||
- **Expired code** (>7 days) → `400 Code expired`. Admin can regenerate via the manual endpoint.
|
||||
- **Expired code** (>7 days) → `400 Code expired`. Buyer can generate a new code via `POST .../delivery-code/generate` (the `regenerateDeliveryCode` frontend action also falls through to this endpoint).
|
||||
- **Already used code** → `400 Code already used`.
|
||||
- **Buyer never confirms** → status remains `delivery`. Auto-release timer (not yet built) should trigger `delivered` after N days. Until then, admin intervention.
|
||||
- **Buyer never generates / confirms** → status remains `delivery`. Auto-release timer (not yet built) should trigger `delivered` after N days. Until then, admin intervention.
|
||||
- **Seller delivers but never marks shipped** → buyer can dispute via [[Dispute Flow]]; the dispute resolution will release the escrow regardless.
|
||||
- **Lost code** → `POST /:id/delivery-code` regenerates a new 6-digit value, invalidates the old one, and re-notifies. Restrict to admin/seller to avoid abuse.
|
||||
- **Lost / expired code** → buyer re-triggers `POST .../delivery-code/generate` to get a fresh code, invalidating the old one.
|
||||
|
||||
> [!tip] Use the code as proof-of-handover
|
||||
> The seller should ask the courier or the buyer at the door for the code before leaving the item. If the buyer disputes "never received", an unused code is strong circumstantial evidence; a used code = buyer confirmed.
|
||||
> [!tip] The buyer holds the code, not the seller
|
||||
> The seller should ask the buyer for the code at hand-off. If the buyer disputes "never received", an unused code is strong circumstantial evidence that delivery has not been confirmed; a used code = seller confirmed receipt.
|
||||
|
||||
## Linked flows
|
||||
|
||||
|
||||
Reference in New Issue
Block a user