# NexGate Shipping Service — Build Plan Development
How we build docs/shipping/SHIPPING_SERVICE_DESIGN.md (the "design", § numbers below point to it).
Status: plan agreed to start, nothing built yet. Built on the Location Service (done: wards, shop locations,
user addresses, frozen places).
Ground rules for every step
- Talk first, then build — each step starts with a short talk on its details, then code.
- Code standard: new top-level package
shipping_service/(same level aslocation_service/), the project's controller / service / service/impl / payload / entity / repo shape, no boilerplate, comments only where needed. - Existing responses: add fields; reshape only when agreed (dev phase) and always recorded.
- Every change recorded in
commerce_logistics_changes.md— before / after, examples, app to-do. - Design doc kept true — any decision that changes during a step is updated in the design.
- Settings: fixed rules in properties (
shipping.*), per-shipper agreement settings in the database (§6.7.1). - Money: every money move goes through the ledger; every step that moves money ends with the ledger balance check.
- Tested live locally after each step (like the location steps), using the worked scenarios (§17) as test cases.
- Phase-0 hooks (§15b) are built in from the start: attempt records, failure reason codes, the extra statuses, timers as data, reverse legs allowed — even though their rules come later.
The order at a glance
| Step | Delivers | Real-world result after the step |
|---|---|---|
| 0 | Foundations | nothing visible; ground ready |
| 1 | Pickup, end to end (with codes and "✓ It's what I ordered") | buyers can buy and collect at the shop |
| 2 | Native shipper registration (KYC + Service Setup basics) | delivery businesses can sign up |
| 3 | Door coverage, pricing, ETA | businesses describe where / how much |
| 4 | Engine: estimation + quotation (single door leg) | buyers see real delivery costs |
| 5 | Cart / checkout / agreement, escrow per part | buyers pay real delivery |
| 6 | Job flow: offers, codes, statuses, release | first real door-to-door deliveries |
| 7 | Reassignment, switching, cancelling, bulky mismatch | unhappy-but-common cases handled |
| 8 | Reliability, reviews, auto-pause, upcoming orders | trust and planning |
| 9 | Stations (single leg) | intercity: collect at a station |
| 10 | Multi-shipper chains | shop → bus → door |
| 11 | Group purchase and installments | shipping inside group / installment buys |
| 12 | Free shipping (seller rules + campaigns) | "Free delivery" badges and filter |
| 13 | Bolt integration | boda on demand in town |
| 14 | Admin dashboard (screens) | ops tooling — last |
| later | Notifications set · unhappy paths (§15a/b) · staging deploy | after the build |
Step 0 — Foundations
Goal: everything later steps stand on, with no behaviour change.
- Package
shipping_service/with sub-packages from §2 (empty shells added as each step needs them). shipping.*properties with every value from §6.7.1 (read through one settings class).- Shared opening hours (§6.3): weekly ranges, special days, temporarily closed, cut-off, EAT, overnight;
"is open at", "next opening", "add opening hours" helpers. Attached to shops now (§6.4): shop hours endpoints,
"Open now / Opens Sat 09:00" on shop responses; shop pause modes (keep selling / stop orders,
SHOP_CLOSED). - Escrow table:
part(ITEMS / LEG),legId,orderIdfilled, provider payee (§12.1) — new columns only, current payments keep working (they are "ITEMS" parts). - Product
bulkyfield (§8.1) — default false; category pre-tick later. - File Thunder: new upload types business document (private) and business logo (separate repo, small).
- Codes service: 4 random digits, hashed, never logged, 5 tries → locked, regenerate (§11). Replaces the old
DeliveryConfirmationcode generator (and its plain-code log line).
Check: existing checkout / orders / cart-escrow tests still pass; shop hours + pause visible; a code can be generated, verified, locked after 5 wrong tries, and never appears in logs.
Step 1 — Pickup, end to end
Goal: a buyer can buy, the seller prepares, the buyer collects with a code, money is released on confirmation.
- Move Step-4
e_commerce/fulfillment(JSON) intoshipping_service/fulfillmentas a table; orders link to it (§2, §3.5). Frozen places stay inside the record. - Shipping agreement entity (§4.8):
shipping_agreement+ parties + changes; states DRAFT → PENDING → AGREED → DONE / CANCELLED. For pickup the only parties are the buyer and the seller. Escrow parts move only on agreement events from here on. - Order flow: seller accepts (24 opening hours, else auto-cancel + refund) → Ready → buyer told → pickup at the counter: buyer's code, seller enters it → collected (§3.1, §10).
- "✓ It's what I ordered" releases all escrow parts in one transaction; "⚠ Report a problem" freezes them (§12.2).
- Account setting: code or "received" button (§11).
- Pickup statuses
placed → accepted → ready → collected → completed(+ cancelled / expired); optional pickup time (shop opening hours, inside the window; reminder that morning). - 14-day collection expiry with reminders (§3.1). Cancel before the seller accepts (§3.4a). Seller cancel at once (pop-up "I have already informed the buyer"; pickup before collected, delivery before Ready).
- Recent pins recorded after payment, not at checkout (§4.4).
- Admin endpoints needed from now: manual release / refund of an escrow part (§6.7.2) —
SUPER_ADMIN, reason, audit log.
Check: scenario A (§17) live: pay → accept → ready → code → collected → confirm → seller paid; 14-day expiry; seller cancel refunds; cancel before accept refunds; problem report freezes; admin release works and is logged.
Step 2 — Native shipper registration
Goal: a delivery business can sign up and get approved automatically.
- KYC (§6.2 part 1): name, contact phone, office location (pin), business licence (file + number + expiry,
unique number), director's NIDA (file + number), LATRA (file + number + expiry) → auto-approved
KYC_APPROVED. - Service Setup basics (§6.2 part 2): profile (logo, description, WhatsApp, email, badge), service type (door / station), hours (shared piece, default Mon–Sat 08:00–18:00), limits (max value, bulky + extra, response window, heads-up). Setup checklist endpoint.
- Statuses
KYC_APPROVED → LIVE ⇄ PAUSED,SUSPENDED; documents private (owner + admin). - "Delivery business" mode in the app (owner only, §6.6) — endpoints for the owner's own business.
- Admin endpoints: suspend / lift (§6.7.2).
Check: register with all documents → auto-approved; duplicate licence number refused; documents not visible to others; checklist shows what's missing; suspend / lift logged.
Step 3 — Door coverage, pricing, ETA
Goal: a business says where it works, how much, how long — and goes LIVE.
- Location helpers (in
location_service): regions → districts → wards lists + search; coverage outline for the map. - Coverage (§7.1): ticked areas (region / district / ward), trips
WITHIN_EACH/WITHIN_AND_ACROSS, "both ends inside", fast area-code matching (the spec's second core function,areaMatch). - Pricing (§8.1): distance bands (price + time each, ×1.3, last band = max distance), flat, value add-on ladder
- max value, bulky extra, rounding up to 100.
- Goes LIVE automatically when service type + coverage + pricing exist.
- Delivery commission fee type (0%) in the fee table + per-shipper override with start date (§6.7.1).
Check: the §7.1 crossing table (Sinza → Mikocheni with WITHIN_EACH vs ACROSS…) as tests; band and add-on maths incl. rounding; business flips to LIVE when complete.
Step 4 — Engine: estimation + quotation (single door leg)
Goal: buyers see real delivery options and costs.
- Engine (§9): where → who (coverage) → filter (distance, value, bulky, hours / cut-off, paused) → cost + ETA → clean-up (dominated, outliers 3×) → labels 💰 ⚡ ⭐ (⭐ = price + time at launch) → order, top 5, fairness.
- ETA (§8.2): from "shop ready", ranges, closed hours.
- Estimation
POST /api/v1/shipping/estimations(§4.5):cost {from,to},eta {from,to}, pickup area; Redis cache shop-area → buyer-area (1 h). - Quotation (§4.1): live options for a shop's parcel and a destination (§4.2: saved / recent / pin; station recognition comes with step 9).
- Product detail: live quotation for the viewer's address.
Check: scenario L (cards) and the engine walk-throughs from the master guide (A, B, E) reproduce; cache hit on the second call; closed shipper shifts ETA instead of disappearing.
Step 5 — Cart, checkout, agreement, escrow per part
Goal: buyers pay the real delivery cost; money split into items + leg.
- Cart (§4.3, §4.4): one destination per cart, per-shop quotation held 5 days, add-to-cart
fulfillment+dropoff, defaults for a new shop, unsaved pin on the cart, re-quote on changes. - Checkout → agreement (§4.1, §4.8) with its legs (each leg escrow part points to one): editable on the checkout screen until paid (re-quote, wallet re-check), locked once paid. Buy-now uses the sheet's quotation.
- Escrow per part (§12.1): items part → seller, leg part → shipper owner; release all on confirm.
- Seller fee on items + tax only; delivery commission from the leg (§8.3).
shippingByShopnow real costs (replaces the 0 placeholder).- What the app keeps vs the server (§4.6) honoured.
Check: scenario B up to payment: escrows created per part with the right payees and fees; quotation older than 5 days refreshed; edit at checkout re-quotes; ledger balance check passes.
Step 6 — Job flow, codes, delivery
Goal: the first real door-to-door delivery from offer to release.
- Seller accepts → heads-up; Ready → offer with response window (counted while open), closed-shipper 4 h rule (§10).
- Ready pop-up: "I have packed it well" + "label / number is on the package" (recorded).
- Parcel number created when the seller accepts (QR label generated by the app, or written on the box); scanned / typed at pickup → "✓ yours / ✗ wrong parcel"; shippers see the parcel, never the order (§11, §6.6).
- Shipper: accept / reject (reason), timeout; active job steps; pickup code (seller verifies) and delivery code (shipper submits) or the buyer's button (§11); optional handover photos.
- Shipper accept = signs the agreement; all signed → AGREED, parcel moves (§4.8).
- Statuses door ladder (§3.2) incl. heading to the shop + phase-0 statuses.
- Release on "✓ It's what I ordered": items → seller, leg → shipper (one transaction).
- Shipper app endpoints for offers / jobs / earnings (§6.6), live push.
Check: scenario B end to end; scenario M (a shipper's day) step by step; timeout counted; codes locked after 5 tries.
Step 7 — Reassignment, switching, cancelling, bulky mismatch
Goal: the common unhappy-but-normal cases.
- Reassignment (§10a): rejection / timeout → that leg only; tiers 1 km → same district → buyer; same-or-cheaper and not slower = automatic (escrow re-pointed, difference refunded); dearer / none → buyer decides; loop guard 3 per order; buyer silent 24 h → pickup; chain safety net 24 h; free-shipping caps recalculated; history kept.
- Switching (§3.4): pickup → delivery (fresh quotation, pay, offer at once if Ready); delivery → pickup (before any shipper accepted, refund).
- Cancelling (§3.4a): shipping-only until pickup — free before heading to the shop, wasted-trip fee (50% / Bolt's charge) after; whole order when the chain failed.
- Agreement changes (§4.8): propose → affected parties approve → default on silence; destination change (accept new cost / keep old price / decline; no withdraw after pickup).
- Bulky mismatch (§8.4): report before pickup, bulky-only reassignment, seller pays difference + 50% wasted trip, auto-flag after 2.
- Badly packed refusal (§8.5): before pickup, photo, seller repacks + Ready again, seller pays 50% wasted trip.
- Admin endpoints: force reassign / cancel (§6.7.2).
Check: scenarios D, F, G, H, N, O, P; loop guard; silent buyer → pickup after 24 h; ledger balanced after every adjustment.
Step 8 — Reliability, reviews, auto-pause, upcoming orders
- Reliability counters (acceptance, on time, failed, broke binding) and auto-pause levels with the §14 table, grace period 10 offers, reasons shown.
- Reviews like shops, verified only, owner replies, moderation (§14); shown in quotations.
- Two-way private ratings (shipper rates buyer / seller).
- Upcoming orders on the shipper dashboard, area-only until accepted (§6.6).
- Real delivery times recorded (for after-launch ETA).
Check: the §14 trigger table as tests (2 / 3 missed, 3rd pause in 7 days → suspended…); new shipper not paused inside the grace period; upcoming card hides the exact pin until accepted.
Step 9 — Stations (reached by a door leg)
- Stations (§7.2): pins with counter name, phone, hours; ≥ 1 per ticked area; lines; reach 30 km (per business override).
- Station-pair table (price + time, both ways, pre-filled pairs) (§8.1).
- Handover points (§7.2a): leg 1 always starts with a collection at the shop (option A) — either the station business collects from shops (areas + flat fee, 0 = free; one leg) or a door shipper brings it to the station (the two-leg chain door → station, built here); buyer collects at the destination station (station staff enter the buyer's code). Step 10 extends it to 3 legs, editing and re-confirms.
- Station ladder (§3.3); quotation meeting-point recognition ("collect at Mbeya Bus Stand", §4.2).
Check: a Mbeya → Morogoro trip as boda → bus (two legs) and as a bus company collecting free (one leg, ranked cheapest); no door shipper at the shop → no station route; pin within 1 km of a station offers "collect there"; empty pair = not offered.
Step 10 — Multi-shipper chains
- Chain building (§4.7): up to 3 legs, meeting points within 1 km, always next to single-leg options.
- Quotation editing per leg: stop early, swap a leg's shipper, change a meeting point.
- All legs offered at Ready, nothing moves until all accept; binding after accept; re-confirm when an accepted leg's time moves > 4 h (§10a 7a).
- Shipper → shipper handover: "handed over" / "received" + that handover's code, optional photo (§11).
- One escrow part per leg; release all together (§12).
Check: scenarios C, D, E with three legs; one leg rejecting while others accepted; re-confirm triggered by a
4 h move.
Step 11 — Group purchase and installments
- Group (§5): each member's own fulfillment and fee with their payment; refunds on fail / withdraw; moves with a transferred payment; price frozen at payment.
- Installments (§5): immediate = fee with the down payment; layaway = choose and pay after the last instalment (reuses switch-to-delivery).
Check: scenarios J and K.
Step 12 — Free shipping
- Seller rules (areas, minimum, cap, product override) and NexGate campaigns (areas, dates, cap, audience, shops, budget reserved at payment, auto-stop) (§13).
- Stacking, the cheapest qualifying option free, faster pays the difference; campaign ledger account; seller share from the items part at release.
- Estimation
freeDelivery+ card badge + "Free delivery to my area" search filter.
Check: scenario I; campaign budget never overspent under parallel checkouts; refund returns budget.
Step 13 — Bolt integration
- Adapter (§6.5): OAuth2 token cache, estimations (boda, non-bulky, in-city), book at Ready (re-estimate, held price = max + 2%), cancel, webhooks (3 IPs + token) and polling fallback, state mapping, tracking link + driver details.
- Bolt ledger account, monthly settlement record, receipt differences; kill-switch and daily spend limit (§6.7); seller pays a shop no-show.
- Built and tested in Bolt's sandbox first.
Check: sandbox flow (estimate → create → states → completed → receipt); NO_DRIVER_FOUND → reassignment; spend limit blocks new Bolt quotations; kill-switch hides Bolt immediately.
Step 14 — Admin dashboard (last)
Screens on top of the existing admin endpoints (§6.7.3): shippers + KYC documents, Bolt status and settlement,
operations board, campaigns, reports (by area), audit log, STAFF_ADMIN / SUPER_ADMIN split.
After the build
- New dedicated shipping notifications (one set, after the shipping service).
- Unhappy paths (§15a / §15b): auto-confirm window, delivery attempts + office hold, returns, disputes.
- Staging / production: PostGIS image +
CREATE EXTENSION, ward file, settings — at deployment. - After-launch items (§16).
Dependencies
0 ─► 1 ─► 2 ─► 3 ─► 4 ─► 5 ─► 6 ─► 7 ─► 8
│ ├─► 9 ─► 10
│ ├─► 11
│ ├─► 12
│ └─► 13
└─────────────────────► 14 (last)
Steps 9, 11, 12, 13 can be reordered after step 6 (each only needs the single-leg flow). 10 needs 9.
No comments to display
No comments to display