Files
nick-doc/04 - Flows/Delivery Confirmation Flow.md

7.5 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
POST /api/marketplace/purchase-requests/:id/verify-delivery

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).

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.
  • 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". This patches the request status to delivery.
  2. Delivery code generation — when the order transitions to delivery, DeliveryService.generateDeliveryCode(requestId) is invoked. It:
    • 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. VerificationPOST /api/marketplace/purchase-requests/:id/verify-delivery with { code }:
    • 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.

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"
    BE->>BE: DeliveryService.generateDeliveryCode
    BE->>DB: deliveryInfo.deliveryCode=XXXXXX\nexpires=+7d
    BE->>IO: emit request-{id} 'delivery-code-generated'
    BE->>B: notification w/ code (in-app/email)

    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}
    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

API calls

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)

Database writes

  • purchaserequests.deliveryInfodeliveryCode, deliveryCodeGeneratedAt, deliveryCodeExpiresAt, deliveryCodeUsed, deliveryCodeUsedAt.
  • purchaserequests.statusdeliverydelivered → (eventually seller_paidcompleted).
  • notifications — generated for both parties.

Socket events emitted

  • delivery-code-generatedrequest-{id} (with code, expiresAt).
  • delivery-updaterequest-{id} (type: 'code-generated').
  • purchase-request-update status-changed on delivery → delivered.
  • new-notificationuser-{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.
  • 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. Admin can regenerate via the manual endpoint.
  • Already used code400 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.
  • Seller delivers but never marks shipped → buyer can dispute via Dispute Flow; the dispute resolution will release the escrow regardless.
  • Lost codePOST /:id/delivery-code regenerates a new 6-digit value, invalidates the old one, and re-notifies. Restrict to admin/seller to avoid abuse.

[!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.

Linked flows

Source files

  • Backend: backend/src/services/delivery/DeliveryService.ts
  • Backend: backend/src/services/marketplace/routes.ts (delivery endpoints)
  • Backend: backend/src/services/marketplace/PurchaseRequestService.ts:631-641 (confirmation notifications)
  • 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