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).
PurchaseRequest.status is payment, processing, or delivery.
Payment.escrowState === 'funded'.
Step-by-step narrative
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.
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)).
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.
Buyer reads code to seller — at hand-off the buyer reads the 6-digit code out loud (or shows it) to the seller.
Seller enters code — seller types the code into frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx.
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-updatestatus-changed.
Triggers buyer/seller notifications via notifyDeliveryConfirmed (see PurchaseRequestService.ts:631-641).
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.
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: PATCH /api/marketplace/purchase-requests/{id} {status:"delivery"}
BE->>DB: PurchaseRequest.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'
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
BE->>S: notifyDeliveryConfirmed
Note over BE: Auto-release timer (planned) → seller_paid → payout
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
Code path — seller calls POST .../delivery-code/verify with the correct, unexpired code → status becomes delivered.
Fast-track path — buyer calls PATCH .../confirm-delivery (no code required) → also becomes delivered. ⚠️ Currently no authorization check on this endpoint.
purchase-request-updatestatus-changed on delivery → delivered.
Side effects
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. 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.