--- title: Delivery Confirmation Flow tags: [flow, delivery, escrow-release, code] related_models: ["[[PurchaseRequest]]", "[[Payment]]"] related_apis: ["POST /api/marketplace/purchase-requests/:id/delivery-code/generate", "POST /api/marketplace/purchase-requests/:id/delivery-code/verify"] --- # Delivery Confirmation 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 - **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`. ## Preconditions - `PurchaseRequest.status` is `payment`, `processing`, or `delivery`. - `Payment.escrowState === 'funded'`. ## 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". The frontend action `updateDelivery` calls `PUT /api/marketplace/purchase-requests/:id/delivery`. The controller's `updateDeliveryInfo` sets `shippedAt` and advances 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}`. - 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). Handled by `DeliveryService.verifyDeliveryCode` (lines 180-212): - 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`. - Sends delivery-confirmed notifications to both buyer and seller directly within `DeliveryService.verifyDeliveryCode`. 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). This endpoint emits only `purchase-request-update` with `status-changed` — it does **not** send delivery-specific notifications to either party. **⚠️ 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 ```mermaid sequenceDiagram autonumber actor S as Seller actor B as Buyer participant FE as Frontend participant BE as Backend participant DB as MongoDB participant IO as Socket.IO S->>FE: Click "Mark as shipped" FE->>BE: PUT /api/marketplace/purchase-requests/{id}/delivery BE->>DB: PurchaseRequest.shippedAt=now, status="delivery" 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' {code, expiresAt} FE->>B: Display 6-digit 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" BE->>IO: emit request-{id} 'purchase-request-update' status-changed BE->>B: notifyDeliveryConfirmed (DeliveryService.verifyDeliveryCode) BE->>S: notifyDeliveryConfirmed (DeliveryService.verifyDeliveryCode) Note over BE: Auto-release timer (planned) → seller_paid → payout ``` ## API calls | Method | Endpoint | Purpose | |---|---|---| | `PUT` | `/api/marketplace/purchase-requests/:id/delivery` | Seller marks shipped (sets shippedAt, advances to `delivery`) | | `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, no delivery notifications | ### 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`. Both buyer and seller receive delivery-confirmed notifications (sent by `DeliveryService.verifyDeliveryCode`). 2. **Fast-track path** — buyer calls `PATCH .../confirm-delivery` (no code required) → also becomes `delivered`. ⚠️ Currently no authorization check on this endpoint, and no delivery-specific notifications are sent to either party. ## Database writes - **`purchaserequests.deliveryInfo`** — `deliveryCode`, `deliveryCodeGeneratedAt`, `deliveryCodeExpiresAt`, `deliveryCodeUsed`, `deliveryCodeUsedAt`. - **`purchaserequests.shippedAt`** — set when seller calls `PUT .../delivery`. - **`purchaserequests.status`** — `delivery` → `delivered` → (eventually `seller_paid` → `completed`). - **`notifications`** — generated for both parties (code path only). ## Socket events emitted - **`delivery-code-generated`** → `request-{id}` room (payload: `{ requestId, code, expiresAt, timestamp }`). **⚠️ Security note:** the full 6-digit code is included in the payload and broadcast to all subscribers in the room, including the seller. The buyer dashboard displays the code; the seller receives it via socket as well. - **`delivery-update`** → `request-{id}` (`type: 'code-generated'`). - **`purchase-request-update`** `status-changed` on `delivery → delivered`. ## Side effects - The code is displayed to the **buyer** in their dashboard. The buyer verbally shares it with the seller at hand-off. Note that the `delivery-code-generated` socket event also broadcasts the raw code to the entire request room (including the seller — see socket events section above). - 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`. 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 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 / expired code** → buyer re-triggers `POST .../delivery-code/generate` to get a fresh code, invalidating the old one. > [!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 - [[PRD - Request Network In-House Checkout]] / [[Escrow Flow]] — funding precondition. - [[Escrow Flow]] — state transitions triggered by confirmation. - [[Payout Flow]] — fires after confirmation (manual today). - [[Dispute Flow]] — escape hatch. - [[Notification Flow]] — channel for the code. ## Source files - Backend: `backend/src/services/delivery/DeliveryService.ts` (generateDeliveryCode, verifyDeliveryCode lines 180-212) - Backend: `backend/src/services/marketplace/routes.ts` (delivery endpoints) - Frontend: `frontend/src/sections/request/components/seller-steps/step-3-ship-goods.tsx` - Frontend: `frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx` - Frontend: `frontend/src/sections/request/components/buyer-steps/step-5-receive-goods.tsx`