Files
nick-doc/04 - Flows/Delivery Confirmation Flow.md
Siavash Sameni 7a616744f4 docs: complete code-reality alignment for remaining docs + reconcile issue set
Remaining docs updated to match code (the docs that the first pass had not covered):
- Flows: Chat, Referral, Rating, Registration, Google OAuth, Negotiation, Payout,
  Trezor Safekeeping — corrected endpoints, socket events, status enums, auth gaps
- API Reference: User API, Trezor API — admin route prefix/verb/status corrections,
  added undocumented endpoints (ton-proof challenge, profile email verify,
  GET /trezor/account, POST /trezor/verify-operation)
- Data Models: Chat, Notification, Payment, PointTransaction, User — corrected
  enums (PaymentProvider, escrowState, PointTransaction.type, User.status),
  90-day notification TTL, soft-delete semantics, wallet fields

Trezor "zero frontend" finding (audit C31/C32) corrected as STALE:
- Verified current code HAS a full frontend Trezor implementation (admin/trezor
  page, TrezorSettingsView, trezorConnector via @trezor/connect-web,
  TrezorSignDialog, actions/trezor.ts building the {message,signature} object)
- Fixed Trezor Safekeeping Flow doc (removed false "no frontend" warnings)
- Reclassified ISSUE-012 as invalid/superseded with explanation

Issue set reconciled to a single canonical numbering (ISSUE-001..054):
- Adopted the comprehensive 51-issue set (long-slug, fully indexed)
- Removed 35 superseded short-slug duplicates from the first pass
- Removed a duplicate ISSUE-046 file
- Added 3 issues the 51-set lacked: ISSUE-052 (completed-not-counted-in-stats),
  ISSUE-053 (axios 401-only interceptor), ISSUE-054 (rate limiter counts all attempts)
- Regenerated Issues Index: 53 open (14 critical, 39 major) + 1 invalid

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 15:15:02 +04:00

10 KiB

title, tags, related_models, related_apis
title tags related_models related_apis
Delivery Confirmation Flow
flow
delivery
escrow-release
code
PurchaseRequest
Payment
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)

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.
  • BackendDeliveryService (backend/src/services/delivery/DeliveryService.ts), exposed through the marketplace routes (backend/src/services/marketplace/routes.ts).
  • MongoDBpurchaserequests.deliveryInfo subdocument fields.
  • Socket.IOdelivery-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. VerificationPOST /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

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.deliveryInfodeliveryCode, deliveryCodeGeneratedAt, deliveryCodeExpiresAt, deliveryCodeUsed, deliveryCodeUsedAt.
  • purchaserequests.shippedAt — set when seller calls PUT .../delivery.
  • purchaserequests.statusdeliverydelivered → (eventually seller_paidcompleted).
  • notifications — generated for both parties (code path only).

Socket events emitted

  • delivery-code-generatedrequest-{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-updaterequest-{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 code400 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 code400 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

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