Product Roadmap
Support & Disputes
Dispute resolution flow and admin support tooling
When something goes wrong between buyer and seller (product not as described, damaged in shipping, non-delivery), we need: a way for users to escalate to platform support, admin tools to investigate and resolve, and the ability to issue refunds.
Dispute flow
Buyer opens a dispute (within grace period or after delivery) — reason: "Not as described" / "Damaged" / "Not received" / "Other", with optional photo evidence.
Transaction status →
DISPUTED; funds remain in escrow; seller + admin notified.Admin reviews: chat history (read-only), deal photos vs dispute evidence, contact via support thread.
Admin resolves: full refund (seller at fault) · partial refund (compromise) · release to seller (false dispute) · ban user if scam confirmed.
Data model
model Dispute {
id Int @id @default(autoincrement())
transactionId Int
transaction Transaction @relation(fields: [transactionId], references: [id])
openedBy String // userId
opener User @relation("disputeOpener", fields: [openedBy], references: [id])
reason DisputeReason
description String
evidence Json? // array of image URLs
status DisputeStatus @default(OPEN)
resolution String? // admin's resolution note
resolvedBy String? // admin userId
resolvedAt DateTime?
refundAmount Decimal? // if partial refund
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum DisputeReason {
NOT_AS_DESCRIBED
DAMAGED
NOT_RECEIVED
COUNTERFEIT
OTHER
}
enum DisputeStatus {
OPEN
UNDER_REVIEW // admin is investigating
RESOLVED_BUYER // resolved in buyer's favor
RESOLVED_SELLER // resolved in seller's favor
RESOLVED_PARTIAL // compromise
CLOSED
}
model SupportThread {
id Int @id @default(autoincrement())
disputeId Int? // optional: linked to dispute
dispute Dispute? @relation(fields: [disputeId], references: [id])
userId String // user who opened support
user User @relation(fields: [userId], references: [id])
subject String
status SupportStatus @default(OPEN)
messages SupportMessage[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model SupportMessage {
id Int @id @default(autoincrement())
threadId Int
thread SupportThread @relation(fields: [threadId], references: [id])
senderId String // userId (user or admin)
sender User @relation(fields: [senderId], references: [id])
content String
attachments Json? // image URLs
createdAt DateTime @default(now())
}
enum SupportStatus {
OPEN
AWAITING_USER
AWAITING_ADMIN
RESOLVED
CLOSED
}Admin UI
- Dispute dashboard (
/admin/disputes): open disputes with urgency sorting (oldest first); filter by status/reason; quick view of deal, amount, both users. - Dispute detail page: side-by-side deal photos vs evidence; read-only chat history; support thread (admin ↔ user); action buttons ("Refund buyer", "Release to seller", "Partial refund", "Ban user"); internal resolution notes.
- General support (
/admin/support): threads not linked to disputes — a help-desk inbox.
User-facing support
- "Help" / "Report issue" buttons: transaction page (dispute flow), deal page (report listing), chat page (report user).
/supportpage: FAQ, "Contact us" form (creates aSupportThread), active tickets list.
Tasks
Models; dispute creation flow + evidence upload; admin dashboard + detail pages; support thread messaging; refund action (Stripe) + partial refund; ban user action; dispute notifications; "Report" button on deal pages; user support page; dispute timeline audit log.
Decision: build custom for V1
Keeps user data in-house, no extra cost, and volume will be low initially. Re-evaluate a third-party helpdesk if volume exceeds ~50 tickets/week.