Integration Module 2.0 for Medusa is outRead the announcement
R

Reviews wishlist

Verified product reviews with ratings, photos, moderation, review-request e-mails and wishlists (customer + guest) for Medusa v2 — by Haus Kayu

npm install medusa-reviews-wishlist
Category
Built by
Version
0.1.2
Last updated
3 days ago
Monthly downloads
Loading downloads

Haus Kayu

medusa-reviews-wishlist

Product reviews with photos, verified purchase, moderation, review-request e-mails with magic links, JSON-LD, and wishlists (customer + guest) in one Medusa v2 plugin. Only people who bought and received a product can review it, once per order item. There is no way to create or import fake reviews.
Ulasan produk dengan foto, pembelian terverifikasi, moderasi, e-mail permintaan ulasan dengan magic link, JSON-LD, dan wishlist (pelanggan + tamu) dalam satu plugin Medusa v2. Tidak ada fitur membuat ulasan palsu.

npm CI Medusa license: MIT

Compatibility

PluginMedusaNodeTested in CI on
0.1.x2.19.0 or newer 2.x ()20.19+ or 22.12+Medusa 2.19.0 and 2.22.2

Needs a Notification Module provider for the channel (Resend, SendGrid, your own…) to send review requests; everything else works without one. Photos go through the File Module (local, S3, R2, …).

Quick start

npx medusa db:migrate

Open Reviews in the Admin sidebar. Reviews arrive as pending (unless ), a bell notification tells you, and the storefront reads and . Full steps: Installation · Options · Storefront (Next.js) · Review-request e-mail · API.

English · Bahasa Indonesia

ModerationProduct page widget
Reviews page in the AdminRating summary and wishlist count on the product page

English

Features

Reviews

  • Rating 1–5, title, text, up to N photos, locale. Verified purchase only: an item can be reviewed once it is shipped, delivered or completed (your choice), by the order's customer or through the order's magic link, once per order item. Reviews are tied to the line item in the database, so a second one is impossible.
  • Photos stay private until approved. They are stored as private files while pending, published (copied to a public file) on approval and deleted on rejection. A pending review never has a public photo URL. File type is sniffed from the bytes, size and count are capped.
  • Moderation in the Admin: pending / approved / rejected, reason, store reply shown publicly, filters (status, rating, photos, product, order, search), bulk approve / reject / delete, optional auto-approve, a bell (feed) notification for every new review, product and order page widgets.
  • Per-product aggregate (average, count, 1–5 distribution) stored in its own table and updated by the workflows, so product listings read one row per product.
  • Events for cache purging and integrations: , , , , , , , , , , .
  • JSON-LD helper (): and fragments from approved reviews only; nothing is padded or invented.
  • Anti-abuse: rate limit per IP and route (Cache Module, i.e. Redis in production, in-memory fallback), honeypot field, minimum and maximum length, HTML stripped, plain text only.
  • Privacy: the public name is shortened server-side ("Anto R.", initials or full, your choice); e-mail, customer id, order id and IP never leave the Store API; IPs are stored as salted hashes; deleting a customer anonymises (or deletes) their reviews and removes their requests, uploads and wishlist.
  • No fake reviews. There is no endpoint, script or Admin button that creates a review without a purchase. A future import from another platform will carry a label.

Review-request e-mail

  • One e-mail per order, scheduled when the order reaches the trigger (, or ) plus a delay, both overridable per region or sales channel. Sent by a job every 15 minutes through the Notification Module (, template name configurable) with all the data a template needs.
  • Magic link, single e-mail, works without an account: opening it never consumes it (refresh, second device, come back tomorrow). It stays valid until every item of the order has a review, including items shipped later, or until it expires (30 days by default). Only a hash of the token is stored.
  • One-click unsubscribe (CAN-SPAM): a signed link that works without login or publishable key; the opt-out is stored as an e-mail hash and later requests for that address are marked .
  • Admin: list of requests with status, "send now" / "send a fresh link", schedule one for an order placed before the plugin was installed.

Wishlist

  • For customers and guests (a token the storefront keeps; merged into the account on login), optional variant per item, optional share link (read-only, revocable), optional per-product count ("saved by 12 people").

API & docs

  • Store API, Admin API, typed SDK helper (), Next.js examples below.

Requirements

  • Medusa 2.19.0 or newer 2.x, PostgreSQL, Redis recommended (events, cache for the rate limit).
  • For review-request e-mails: a Notification Module provider on the channel and a template with the name from (default ).
  • For photos: the File Module (the default local provider works; S3/R2 for production).

Installation

  1. Add the plugin to (see Quick start) with at least (the storefront page that handles the magic link) and (public URL of the backend, used in the unsubscribe link).
  2. creates the tables (, , , , , , , , , ).
  3. Restart. The jobs ( every 15 min, daily) run on the worker.

Plugin options

All optional. Defaults in brackets.

OptionMeaning
[]Publish new reviews without moderation.
[]Public reviewer name: ("Anto R."), ("A. R.") or .
[], [], []Text limits (characters, after sanitising).
[], [], [jpeg, png, webp, avif]Photo limits. disables photos.
[]Field that must be empty in the create request.
[]Largest the Store API accepts.
[]Schedule and send review-request e-mails.
[], or . Pick if your orders rarely get marked delivered.
[]Days after the trigger.
[], first match wins.
[]How long a magic link stays valid after sending.
[]Notification template name.
Storefront URL with , e.g. . Required to send.
Public backend URL for the unsubscribe link.
Sender passed to the provider, if it supports one.
[]Requests sent per job run.
[]Bell notification in the Admin for every new review.
[], []Per IP and route. disables.
[], [], [], []Wishlist features.
[] keeps the reviews under "Customer"; removes them.
, Salt for the IP hash and key for unsubscribe links. Default: the project's .
[]Show a small "by Haus Kayu" line under the plugin's Admin pages. Off by default.

Storefront (Next.js)

The examples use with the helpers from . Ratings and review lists are public and cacheable; eligibility, creating and the wishlist need the customer session (or the guest token).

Stars on a product card (server component; one request for the whole grid)

Rating summary and review list on the product page (server component, ISR friendly)

The stored aggregate changes only when a review is approved, rejected or deleted, so revalidate the product page on the , , or events (a subscriber that calls your endpoint).

Wishlist button (client component; guests keep the token in )

After login, call once and drop the token: the guest items move to the account and the guest token stops working.

Magic-link page (): resolve the token, show the open items, upload photos, submit. The same link may be opened as often as needed and used once per item.

Logged-in customers review from the account's order page: lists the items with / , then ; shows their reviews with status ("awaiting moderation").

Review-request e-mail

The job calls on the Notification Module with:

Your provider (Resend, SendGrid, a custom one) renders the template. Include as the button and in the footer (the unsubscribe page needs no login and confirms in English and Indonesian; to the same URL returns JSON for a custom page). The e-mail goes out at most once per order; the Admin can send a fresh link by hand.

Store API

All routes need the publishable API key header. = logged-in customer session or bearer token.

RouteAuthPurpose
–Approved reviews. No e-mail, order or customer data.
(repeatable, ≤100)–Stored aggregates.
customerWhich items of the order can be reviewed.
–What a magic link may review (, , ). Does not consume it.
(multipart )customer or Photos for a review about to be created; ids valid 24 h.
customer or tokenCreate reviews (pending unless auto-approve).
customerOwn reviews with status.
customer or The wishlist ( when none).
customer or tokenAdd; a new guest wishlist returns once.
customer or tokenRemove.
customerMove a guest wishlist into the account.
/ customerCreate / revoke the share token.
–A shared wishlist, read-only.
–Per-product counts ().
/ –Unsubscribe page / JSON. No publishable key.

Rate limits answer with code . A with a message names the item or field; a on a token means invalid, used or expired.

Admin API

RoutePurpose
List with product titles and order numbers.
/ / Detail / / delete.
/ Store reply / remove it.
A pending (private) photo for moderation; published ones redirect.
Bulk.
Counts per status; with a product: its aggregate and wishlist count.
Recompute aggregates.
Non-secret options the Admin UI adapts to.
Requests (tokens never returned).
Send now (a sent one gets a fresh link).
Schedule for an order (e.g. placed before the install).

Events

EventPayload
, , ,
,
,

Privacy and security

  • The Store API never returns e-mail, customer id, order id or IP. Names are shortened on the server.
  • Magic-link and wishlist tokens are random (256 bit) and stored as SHA-256 hashes; unsubscribe links are HMAC-signed request ids; IPs are stored as salted hashes for abuse handling only.
  • Pending photos are private files; rejected photos are deleted; abandoned uploads are deleted after 24 h.
  • Deleting a customer anonymises or deletes their reviews and removes their requests, uploads and wishlist.
  • and cannot be set from the Store API; the honeypot and rate limit apply to every write.
  • See SECURITY.md for reporting.

FAQ

Can I import reviews from Shopify / Judge.me / another store? Not in 0.1. When it comes it will be an Admin import that marks every review with its and unless the import proves the purchase. There will never be a "generate reviews" feature.

Our orders are rarely marked delivered. Use (and a longer ), or a rule per region: .

Why did the link say "nothing to review right now"? Part of the order has not reached the trigger state yet. The same link works again once it ships.

Do I need Redis? Recommended. Without a Cache Module backed by Redis the rate limit is per process.


Bahasa Indonesia

Fitur

  • Ulasan terverifikasi: hanya pembeli yang item pesanannya sudah dikirim / diterima / selesai (pilih di opsi), satu ulasan per item pesanan (unik di database). Rating 1–5, judul, teks, foto (maks 3, bisa diubah), bahasa.
  • Foto privat sampai disetujui: disimpan sebagai file privat selama menunggu, dipublikasikan saat disetujui, dihapus saat ditolak. Ulasan yang belum dimoderasi tidak pernah punya URL foto publik.
  • Moderasi di Admin: menunggu / disetujui / ditolak, balasan toko (publik), filter, aksi massal, setujui otomatis (opsional), notifikasi lonceng tiap ulasan baru, widget di halaman produk dan pesanan.
  • Agregat per produk (rata-rata, jumlah, distribusi 1–5) disimpan dan diperbarui lewat workflow.
  • E-mail permintaan ulasan: maksimal 1 per pesanan, pemicu / / + jeda hari, bisa beda per region atau sales channel. Magic link sekali pakai per item: boleh dibuka berulang, ganti perangkat, dan tetap berlaku sampai semua item diulas atau kedaluwarsa (30 hari). Link berhenti langganan satu klik (opt-out tersimpan per e-mail, patuh CAN-SPAM).
  • JSON-LD + hanya dari ulasan yang disetujui.
  • Anti-penyalahgunaan: rate limit (Redis lewat Cache Module, fallback memori), honeypot, batas panjang, tag HTML dibuang. Nama publik disingkat ("Anto R."); e-mail, id pelanggan, id pesanan, dan IP tidak pernah keluar lewat Store API.
  • Wishlist pelanggan + tamu (token, digabung saat login), link berbagi hanya-baca, hitungan per produk.
  • Tidak ada fitur membuat atau mengimpor ulasan palsu.

Pasang

npx medusa db:migrate

Lalu buka Reviews di sidebar Admin. Untuk e-mail permintaan ulasan, pasang provider Notification Module untuk channel dan buat template bernama (field yang dikirim ada di Review-request e-mail). Tabel opsi lengkap: Plugin options. Contoh storefront Next.js (bintang di kartu produk, ringkasan + daftar ulasan, tombol wishlist, halaman magic link): Storefront (Next.js).

Alur ulasan

  1. Pesanan mencapai status pemicu → permintaan ulasan dijadwalkan (1 per pesanan).
  2. Job tiap 15 menit mengirim e-mail lewat Notification Module dengan magic link + link berhenti langganan.
  3. Pelanggan membuka link (tanpa login), mengunggah foto, mengirim ulasan per item.
  4. Ulasan masuk sebagai menunggu; Admin menyetujui / menolak / membalas. Saat disetujui foto dipublikasikan, agregat diperbarui, event dipancarkan (pakai untuk purge cache halaman produk).

Development

The end-to-end suite places real orders through the Store API, ships and delivers them through the Admin API, and walks through uploads, moderation, the review-request e-mail (recorded by a test notification provider), the magic link, unsubscribe, customer deletion and the wishlist, on Medusa 2.19.0 and 2.22.2 in CI.

Built for hauskayu.com

Built and used in production by Haus Kayu, handmade teak homeware from Jepara. Reviews on the product pages, the "saved" hearts and the review e-mails there come from this plugin.

Other plugins by Haus Kayu

Support

License

MIT © Haus Kayu