Verified purchase badge distinguishes buyer reviews from non-buyer reviews
Why it matters
The FTC Endorsement Guides (revised 2023) explicitly require that "Verified Purchase" or equivalent indicators be truthful — displaying the badge for reviews from non-buyers is a deceptive endorsement. Beyond legal risk, unverified reviews mixed into the same display as genuine buyer reviews dilute trust: a product with 100% "Verified Purchase" badges where the badge is just hardcoded to true will have the label completely reverse-fired when a single fraudulent review is exposed publicly. The verified_purchase check must reach into order history at submission time to have any meaning; a database field alone without that query is cosmetic.
Severity rationale
Medium because a hardcoded or query-free verified-purchase badge violates FTC endorsement guidelines and misleads shoppers without allowing unauthorized data access.
Remediation
Check order history at review submission time in api/reviews/submit and expose the badge in components/ReviewItem.tsx.
ALTER TABLE reviews ADD COLUMN verified_purchase BOOLEAN NOT NULL DEFAULT FALSE;
// api/reviews/submit
const purchase = await db.orders.findFirst({
where: { user_id: userId, product_id: productId, status: 'completed' }
})
await db.reviews.create({
data: {
verified_purchase: purchase !== null,
// ...
}
})
// components/ReviewItem.tsx
{review.verified_purchase && (
<span className="badge-verified" aria-label="Verified Purchase">
✓ Verified Purchase
</span>
)}
Never set verified_purchase: true on insert without the order lookup — the badge only has legal standing if it reflects an actual completed transaction.
Detection
-
ID:
verified-purchase -
Severity:
medium -
What to look for: Count the number of verification layers: (1)
verified_purchaseboolean field in the reviews database schema, (2) logic in the review submission handler that queries order history to set the flag, (3) visual badge/indicator in the review display component that distinguishes verified from unverified reviews. Report: X of 3 verification layers present. -
Pass criteria: At least 2 of 3 verification layers are implemented: the database has a
verified_purchasefield, the submission handler checks order history to set it, and the display component shows a visible "Verified Purchase" badge or distinguishing indicator for reviews from actual buyers. -
Fail criteria: Fewer than 2 of 3 verification layers exist, or all reviews display "Verified Purchase" regardless of actual purchase history (hardcoded badge).
-
Skip (N/A) when: The project has no order/purchase system or no auth system (cannot correlate users to purchases).
-
Detail on fail:
"1 of 3 verification layers found (database field exists). Submission handler does not check order history. Display has no visual badge."or"All reviews render 'Verified Purchase' badge — field is hardcoded to true on insert without checking orders." -
Remediation: Add purchase verification in
api/reviews/submitand display the badge incomponents/ReviewItem.tsx:ALTER TABLE reviews ADD COLUMN verified_purchase BOOLEAN DEFAULT FALSE;// On review submission, check if user purchased the product const purchase = await db.orders.findFirst({ where: { user_id: userId, product_id: productId, status: 'completed' } }) const review = await db.reviews.create({ data: { verified_purchase: !!purchase, // ... } }) // Display component {review.verified_purchase && <span className="badge">✓ Verified Purchase</span>}
External references
- external · ftc-endorsement-guides-verified-purchase — FTC Endorsement Guides — 16 CFR Part 255: Disclosure of Material Connections (Verified Purchase)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-reviews·automated