# Product reviews in Go: moderated ratings for GoCommerce

> Ratings and reviews on a GoCommerce product page: held for moderation, marked verified from the store’s own orders, and moved in or out as a CSV file.

- Canonical: https://kitcommerce.store/integrations/reviews/
- Last updated: 2026-10-30

---

Marketing

## Product reviews in Go

A Go package for what shoppers say about a product: a rating from one to five and some words. A review waits for an operator unless the store approves on sight, and it is marked verified when its email has bought the product. Nothing is required; two switches sit on the Plugins screen.

- **Marketing** module
- **3** settings
- **15** tests
- **8** API operations
- **ext/reviews**

### What it does

In the package’s own words, a review is “a rating out of five and some words, moderated before it shows.” The storefront posts one to POST /x/reviews and reads a product’s approved reviews from GET /x/reviews?product_id=, with the count, the average to one decimal and the number at each star beside them. The reviewer’s email is kept for the store and never sent to the storefront.

A review is marked verified when its email has an order for the product in the confirmed, partial, shipped or delivered status: the store’s own definition of a sale. The Plugins screen can make that a condition of posting at all. On the Reviews screen an operator approves or rejects a review, writes a public reply under it, or deletes it.

Reviews also travel as a CSV file. The package names two jobs: a store moving in from another platform “arrives with a few thousand reviews in a CSV and no other way to get them here”, and a store already running moderates in bulk by exporting, sorting by rating, and pasting a column of statuses back.

### Configuration

Nothing is required. Config holds two switches, which become the Plugins screen’s defaults; the screen holds the same two and wins.

*Product reviews module settings — 3 settings, 0 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `AutoApprove` Show reviews without moderation | — | No | Shows a review the moment it is posted. Off by default, so every new review waits as pending. |
| `RequirePurchase` Only buyers may review | — | No | Refuses a review from an email with no order for the product, and asks for the email the order was placed with. Public, so the storefront can say so on the form. |
| `prompt` Form heading | — | No | The review form’s heading, “Write a review” by default. Public. Panel only; there is no Config field for it. |

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own `main()` you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

### Setting it up

1. **Install the module** — Pass reviews.New to gocommerce.New, as below, or start the reference binary with -reviews. It adds one table, and the plugin starts switched on.
2. **Choose the policy** — Under Product reviews on the Plugins screen: approve on sight or moderate, and whether only buyers may review.
3. **Draw the form and the list** — The storefront posts product_id, name, email, rating, title and body to POST /x/reviews, and reads GET /x/reviews?product_id= for the approved reviews and their summary.
4. **Moderate** — The Reviews screen lists reviews by status. PATCH /api/admin/x/reviews/{id} sets the status or the reply; DELETE removes the review.

main.go

```
import (
	"github.com/itswadesh/gocommerce/core"
	"github.com/itswadesh/gocommerce/ext/reviews"
)

app, err := gocommerce.New(cfg, reviews.New(reviews.Config{}))
```

The package doc has no example of its own; this is how the reference binary’s -reviews flag and examples/store/main.go install it. cfg is your gocommerce.Config. Import path `github.com/itswadesh/gocommerce/ext/reviews`.

### How it works

- **Held, then shown**

  A new review is pending unless auto-approve is on. Only approved reviews reach the storefront and count toward the summary; a rejected one stays in the admin list.

- **Verified by the orders**

  The badge is set when the review is posted, from an order carrying the product under the reviewer’s email, compared lower-cased, in the confirmed, partial, shipped or delivered status.

- **No email on the storefront**

  The public list carries the name, rating, title, body, badge, reply and date. The email stays on the admin side, masked on a demo store or for an agent key, where search stops matching it.

- **A honeypot for bots**

  A hidden website field that a person never sees. A request that fills it is answered as if the review were accepted, and nothing is kept.

- **A file both ways**

  The export streams every review, newest first, with its product’s slug and title. On import a row with an id updates that review, and a blank cell leaves its column alone; a row without one is a new review, found by product_slug.

- **Row by row, with a rehearsal**

  Each imported row is its own transaction, so a row naming a product that is not here is a row error and the rest still land. ?dry_run=1 checks every row and rolls each back. A file is read up to 32 MB.

### What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

- **A honeypot, nothing more** — The module has no captcha and no rate limit of its own, and does not stop one email posting several reviews of one product.
- **Text and stars only** — No photos and no helpful votes. A shopper cannot edit or withdraw a review after posting it; an operator can only change its status or reply, or delete it.
- **Nobody is told** — The module sends no notice when a review is waiting. Pending reviews are found on the Reviews screen.
- **An import is believed** — A file’s status is applied as written, so an approved row goes live without moderation; a row with no status is pending. A file with a verified column is taken at its word, because a store moving in carries verifications its orders here cannot show. Without the column, the store’s orders decide.
- **Sample reviews show until erased** — The sample shop adds one approved review to each sample product, written by a sample customer whose sample order bought it. They are flagged as samples and erased with the rest of the sample shop, which the package says to do before opening.
- **The file has no button** — Export and import are API routes, GET /api/admin/x/reviews/export and POST /api/admin/x/reviews/import. The Reviews screen does not offer them.

FAQ

### Questions about the Product reviews module

**Does the store ever show a review nobody wrote?**

Only the sample shop’s. Its reviews are flagged as samples, and erasing the sample shop removes them and no shopper’s. In the package’s words, “a review nobody wrote is not one to show a real shopper.” The module adds no other reviews of its own.

**What makes a review verified?**

An order for the product placed with the reviewer’s email, in the confirmed, partial, shipped or delivered status. It is decided when the review is posted. An imported review takes the file’s verified column if it has one, and the orders’ answer if it does not.

**Can I bring reviews from another platform?**

Yes, as a CSV with the columns id, product_slug, product_title, name, email, rating, title, body, status, verified, reply and created_at. The product is named by its slug here; product_title is ignored on the way in. Run it with ?dry_run=1 first to see each row’s errors without writing anything.

**Who can moderate?**

Seeing reviews needs reviews.read, which managers and staff hold by default. Approving, rejecting, replying and deleting need reviews.moderate, which managers hold by default. The export needs data.export and the import data.import.

**What does switching the plugin off do?**

Both storefront routes answer 404. The Reviews screen and the admin routes keep working, so an operator can still read and moderate.

### Related modules

Each is a directory under `ext/`, imported the same way. Where this one fits in the store: [What modules add to a direct-to-consumer store](https://kitcommerce.store/solutions/d2c/).

- [Product video showcases in Go Put a store’s own YouTube and Facebook videos beside the products they show: no video hosting, a live label the store sets, and counts down to orders.](https://kitcommerce.store/integrations/showcases/)
- [XML sitemaps for a Go store Publish a storefront sitemap from the live GoCommerce catalogue: products, collections and CMS pages, the storefront URL and paths, and the 50,000-URL limit.](https://kitcommerce.store/integrations/sitemaps/)
- [Ecommerce wishlist in Go Let GoCommerce shoppers save products to a list kept by a token, guests included, and see which wished-for products are out of stock. Settings and limits.](https://kitcommerce.store/integrations/wishlist/)
- [Abandoned cart emails in Go Remind shoppers about baskets they left in a GoCommerce store: a sequence of emails per kind of basket, a counted link back, and the order each one brings back.](https://kitcommerce.store/integrations/cart-recovery/)

### Source

Everything on this page is read from [`ext/reviews`](https://github.com/itswadesh/gocommerce/tree/main/ext/reviews) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.

- [ext/reviews on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/reviews)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.

[Backend + admin quick start](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/reviews) · [All integrations](https://kitcommerce.store/integrations/)
