KwadMarket Docs
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).
  • /support page: FAQ, "Contact us" form (creates a SupportThread), 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.

On this page