AstroBaaS

Reviews & social proof

Product Reviews with Photos, Video & UGC

Free — GPL coresize XLplanned, not built

Generated from docs/plan/core/product-reviews-with-photos-video-ugc/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Core-free text review system with verified-buyer badges and moderation, allowing customers to post ratings, titles, and body text. Paid add-on modules extend this to photo uploads, video embedding, and UGC aggregation—customers buy based on seeing what others actually experienced, and merchants get social proof.

The problem

Customers won’t buy without peer reviews and verified proof of purchase. A product page with zero reviews, or only text reviews without photos, loses sales to competitors who showcase real user experiences. Merchants need a full review ecosystem with moderation and visual proof.

What it does

  • Review CRUD: create (storefront), read (storefront + admin), update (admin), soft-delete (audit trail)
  • Review fields: rating (1-5 int), title (required, <100 chars), body (required, <5000 chars), reviewer name, email
  • Verified buyer flag: set to true IFF reviewer has a completed order for this product at review time
  • Review moderation workflow: pending → approved/rejected; show only approved on storefront
  • Spam flagging: reviewers or moderators can mark as spam; spam reviews hidden from storefront
  • Reviewer identity: optional account link if logged-in; otherwise name + email
  • Timestamps: createdAt, publishedAt, rejectedAt, updatedAt for audit
  • Review sorting on storefront: by recent, by rating (high-to-low, low-to-high), by helpful count
  • Admin moderation queue: list pending reviews, approve/reject/spam with reason
  • Bulk moderation: approve/reject/spam multiple reviews in one action
  • Notification: email reviewer when their review is published
  • Audit log: record every state change (create, publish, reject, spam) with actor and reason
  • Webhooks: on_review_published, on_review_rejected for external integrations
  • Rating display: aggregate stats visible on product (via rating-display-component)
  • Media framework: support photo and video attachments (storage handled by paid modules)

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Star rating alone (that’s the rating-display-component feature)
  • Reviews from other platforms (Google, Amazon, Trustpilot—those are paid integrations, need credentials)
  • AI sentiment analysis or spam detection enhancement (that’s a paid module add-on)
  • Merchant replies to reviews (requires approval workflow and separate response moderation)
  • Review helpfulness voting or thumbs-up/down (gamification feature, separate)
  • Q&A (different format: question-answer pairs, not reviews; separate feature)

Data model

New reviews table: { id, productId, reviewerId, rating: int, title: string, body: string, status: ‘pending’|‘approved’|‘rejected’|‘spam’, verifiedBuyer: boolean, publishedAt: DateTime|null, rejectedAt: DateTime|null, createdAt: DateTime, updatedAt: DateTime, locale: string }. Optional review_media table for photos/videos (paid modules populate). No migration; new tables.

API

  • POST /storefront/products/:id/reviews — create review (requires CSRF or auth); returns { id, status: ‘pending’ }
  • GET /storefront/products/:id/reviews — list approved reviews, paginated, filterable by rating
  • GET /storefront/products/:id/reviews?rating=5 — filter to 5-star reviews only
  • GET /storefront/reviews/:id — single review detail (if approved)
  • GET /admin/reviews — moderation queue; default status=pending, sortable by created DESC
  • PATCH /admin/reviews/:id — transition status, set rejectionReason, publishedAt timestamp
  • DELETE /admin/reviews/:id — soft delete (mark status=‘deleted’, log audit)
  • POST /admin/reviews/bulk-approve — approve array of review IDs
  • POST /admin/reviews/bulk-reject — reject array of review IDs with reason
  • GET /admin/reviews/:id/moderation-context — reviewer info, purchase history, other reviews by this reviewer

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Moderation dashboard with tabs: Pending (default), Approved, Rejected, Spam. Each review card shows: text preview, reviewer name, email, rating stars, verified badge if applicable, and buttons: Approve, Reject (with reason modal), Mark Spam. Moderator can leave notes on review. Show review count per product. Bulk actions: checkboxes + ‘Approve Selected’ / ‘Reject Selected’ buttons.

The seam — why this is core

Core owns: review CRUD, moderation workflow, verified buyer check (queries purchase history), text storage, admin UI, audit logging, and email notification. Paid module owns: photo upload (storage backend + moderation AI), video embedding (transcoding + hosting), UGC aggregation (social API credentials). Reason: core provides the honest review interface; paid modules add external integrations that need credentials or support commitments.

Core owns moderated review interface and basic text display (already ships verified_buyer); paid module owns photo/video hosting and AI-powered UGC aggregation (credential = CDN + moderation AI service)

Dependencies

  • Order/purchase history system to verify buyer
  • Email delivery system for notifications
  • Audit log system
  • Webhook/event system
  • CSRF middleware for storefront POST (or auth-only reviews)
  • Moderation queue UI framework
  • Admin permission system (e.g., ‘reviews.moderate’ role)
  • Paid modules: file storage backend (S3, local), UGC service API (Unsplash, Reddit, etc.)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • A review with verifiedBuyer=true is only set if reviewer has a completed order (paid AND fulfilled) for that product
  • verifiedBuyer is computed at creation time; does not update retroactively if order is later refunded
  • Reviews with status != ‘approved’ do NOT appear in GET /storefront/products/:id/reviews
  • Reviews with status=‘spam’ are soft-deleted: not in storefront lists but queryable by admin
  • Publishing a review sends email to reviewer.email with subject line ‘Your review is now live’
  • Rejecting a review logs the rejection reason in audit log with moderator’s user ID
  • GET /admin/reviews?status=pending returns all unapproved, sorted by createdAt DESC
  • Bulk approval of 100 reviews completes in <2 seconds and logs each as separate audit entry
  • Review rating is an integer between 1 and 5 inclusive; validation rejects 0, 6, or float values
  • Review body >0 chars and <5000 chars; title >0 chars and <100 chars

Risks

Verified buyer check fails (wrong order match, wrong product)—fraudulent reviews appear with false verified badge. Spam not caught—moderation queue overwhelmed, customers see inappropriate content. Photo/video paid module unavailable—reviews can’t include media, UX broken. Soft delete not applied to all queries—deleted reviews leak to storefront. Email sending fails silently—reviewers never know if published. CSRF not checked on storefront POST—malicious reviews injected at scale. Race condition: same reviewer submits twice before first publish, results in duplicate approved reviews.

Commercial context

Suggested pricefree (core)
Rival anchor6 freemium apps (Judge.me, Loox, AG, CWILL, Junip); paid tiers $15-50/mo for photo/video features

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.