Cách Xây Dựng Hệ Thống Review Sản Phẩm Với Supabase Và Stripe

02/04/2026 P T P Chung 7 phút đọc 0 bình luận

Giới thiệu

Trong thời đại mua sắm trực tuyến, đánh giá sản phẩm (review) đóng vai trò quan trọng trong việc xây dựng niềm tin và thúc đẩy quyết định mua hàng của khách hàng. Để tạo ra một hệ thống review hoàn chỉnh, chúng ta cần kết hợp giữa cơ sở dữ liệu thời gian thực, xác thực người dùng và xử lý thanh toán. Supabase cung cấp backend mạnh mẽ với PostgreSQL và Row Level Security, trong khi Stripe đảm nhận khâu thanh toán an toàn. Bài viết này sẽ hướng dẫn bạn xây dựng hệ thống review sản phẩm tích hợp cả hai công nghệ này.

Thiết kế cơ sở dữ liệu trên Supabase

Để lưu trữ reviews, chúng ta cần ít nhất hai bảng chính: một cho sản phẩm và một cho reviews. Bảng products sẽ chứa thông tin cơ bản như ID, tên, mô tả và giá. Bảng reviews sẽ lưu nội dung review, đánh giá sao, ngày tạo và liên kết với sản phẩm qua khóa ngoại. Ngoài ra, cần một bảng users để quản lý thông tin khách hàng, dù Supabase Authentication đã cung cấp sẵn user ID.

-- Bảng products
CREATE TABLE products (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  description TEXT,
  price NUMERIC(10, 2) NOT NULL
);

-- Bảng reviews CREATE TABLE reviews ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), product_id UUID REFERENCES products(id) ON DELETE CASCADE, user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5), comment TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CHECK (comment IS NOT NULL OR rating IS NOT NULL) );

Với Row Level Security, chúng ta đảm bảo chỉ chủ review mới có thể sửa hoặc xóa review của mình, và mọi người đều có thể đọc reviews công khai.

-- Chính sách cho reviews
ALTER TABLE reviews ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view reviews" ON reviews FOR SELECT USING (true);

CREATE POLICY "Users can insert their own reviews" ON reviews FOR INSERT WITH CHECK (auth.uid() = user_id);

CREATE POLICY "Users can update their own reviews" ON reviews FOR UPDATE USING (auth.uid() = user_id);

CREATE POLICY "Users can delete their own reviews" ON reviews FOR DELETE USING (auth.uid() = user_id);

Tích hợp Stripe để thu thập reviews từ khách hàng đã mua

Để reviews đến từ người mua thực sự, chúng ta cần liên kết đơn hàng với reviews. Stripe sẽ xử lý thanh toán, và webhook sẽ ghi nhận khi thanh toán thành công. Mỗi khi có sự kiện payment_intent.succeeded, chúng ta lưu thông tin đơn hàng vào Supabase.

-- Bảng orders
CREATE TABLE orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  product_id UUID REFERENCES products(id) ON DELETE CASCADE,
  stripe_payment_intent_id TEXT UNIQUE,
  status TEXT DEFAULT 'pending',
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

Trên server, khi nhận webhook từ Stripe, chúng ta lưu order và gắn kèm user ID từ Stripe:

// Node.js webhook handler
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);

async function handlePaymentSuccess(event) { const paymentIntent = event.data.object; const { client_reference_id, metadata } = paymentIntent;

await supabase.from('orders').insert([ { user_id: metadata.user_id, product_id: client_reference_id, stripe_payment_intent_id: paymentIntent.id, status: 'paid' } ]); }

Quảng cáo

300x250 In-Content Advertisement

Xây dựng API để kiểm soát quyền review

Để tránh spam, chỉ khách hàng đã mua sản phẩm mới được review. Chúng ta tạo một view trên Supabase để xác định những user đủ điều kiện:

CREATE VIEW eligible_reviews AS
SELECT DISTINCT o.user_id, o.product_id
FROM orders o
WHERE o.status = 'paid'
  AND NOT EXISTS (
    SELECT 1 FROM reviews r
    WHERE r.product_id = o.product_id
      AND r.user_id = o.user_id
  );

API trên client sẽ kiểm tra trước khi cho phép submit review:

async function canUserReview(productId) {
  const { data, error } = await supabase
    .from('eligible_reviews')
    .select('user_id')
    .eq('user_id', supabase.auth.user().id)
    .eq('product_id', productId)
    .maybeSingle();

return data !== null; }

Frontend: Hiển thị reviews và form submit

Trên giao diện, chúng ta hiển thị danh sách reviews và điểm trung bình. Form submit chỉ hiện ra nếu user đủ điều kiện.

// React component
function ProductReviews({ productId }) {
  const [reviews, setReviews] = useState([]);
  const [canReview, setCanReview] = useState(false);

useEffect(() => { fetchReviews(); checkEligibility(); }, [productId]);

const fetchReviews = async () => { const { data } = await supabase .from('reviews') .select('rating, comment, created_at, user_id (select email from auth.users)') .eq('product_id', productId) .order('created_at', { ascending: false }); setReviews(data); };

const checkEligibility = async () => { const eligible = await canUserReview(productId); setCanReview(eligible); };

return ( <div> <h3>Đánh giá sản phẩm</h3> {reviews.map(r => ( <div key={r.id}> <p>{'*'.repeat(r.rating)} ({r.rating}/5)</p> <p>{r.comment}</p> <small>{r.user_id.email}</small> </div> ))} {canReview && <ReviewForm productId={productId} />} </div> ); }

Kết luận

Hệ thống review sản phẩm với Supabase và Stripe cho phép bạn xây dựng một giải pháp hoàn chỉnh, bảo mật và mở rộng. Supabase cung cấp database thời gian thực, authentication và Row Level Security, trong khi Stripe đảm bảo thanh toán an toàn và webhook để đồng bộ dữ liệu. Việc kết hợp hai nền tảng này giúp bạn kiểm soát chặt chẽ nguồn gốc reviews, tăng độ tin cậy cho người dùng và nâng cao trải nghiệm mua sắm. Hãy bắt đầu triển khai và tùy biến theo nhu cầu kinh doanh của bạn.

Quảng cáo

728x90 Bottom Advertisement

Thay thế bằng mã Google AdSense

Chia sẻ bài viết

Facebook Twitter

Bình luận

Chia sẻ ý kiến của bạn về bài viết này

Viết bình luận

Bình luận của bạn sẽ được kiểm duyệt trước khi hiển thị

Chưa có bình luận nào

Hãy là người đầu tiên bình luận về bài viết này!