Chuyển tới nội dung chính

Các ví dụ

🚀 Dự án mẫu Full-stack

Next.js 15 Full-stack Example

Tính năng nổi bật:

  • Server-side VNPay integration - Xử lý thanh toán hoàn toàn trên server
  • Next.js 15 App Router với Server Actions và API Routes
  • IPN Handler - Xử lý webhook từ VNPay
  • Types-only imports cho frontend components
  • Responsive UI với Tailwind CSS
  • Demo sandbox environment với test data

Express MVC Example

📋 Ví dụ sử dụng cơ bản

Các phương thức thư viện

Xem các ví dụ về cách sử dụng tất cả các phương thức thư viện tại đây

Triển khai với Express

Xem ví dụ triển khai hoàn chỉnh với Express tại đây

🏗️ Kiến trúc Full-stack đúng cách

⚠️ Nguyên tắc quan trọng

CẢNH BÁO

Thư viện VNPay chỉ dành cho Node.js backend! Không thể sử dụng trực tiếp trong React/Vue/Angular components vì:

  • 🚫 Sử dụng Node.js modules: fs, crypto, path
  • 🚫 Chứa logic server-side để bảo mật secureSecret
  • 🚫 Sẽ gây lỗi build khi import vào client components

✅ Kiến trúc khuyến nghị

┌─────────────────┐    API calls    ┌─────────────────┐
│ Frontend │ ──────────────► │ Backend │
│ │ │ │
│ • React/Vue │ │ • Node.js │
│ • Types only │ │ • VNPay library │
│ • UI components │ ◄────────────── │ • Payment logic │
└─────────────────┘ JSON response └─────────────────┘

💼 Ví dụ Backend (Node.js/Express)

// backend/routes/payment.ts
import { VNPay } from 'vnpay'; // ✅ Import đầy đủ trên backend

const vnpay = new VNPay({
tmnCode: process.env.VNP_TMNCODE!,
secureSecret: process.env.VNP_SECRET!,
testMode: true
});

// Tạo URL thanh toán
app.post('/api/payments/create', async (req, res) => {
try {
const { amount, orderInfo } = req.body;

const paymentUrl = vnpay.buildPaymentUrl({
vnp_Amount: amount,
vnp_IpAddr: req.ip,
vnp_ReturnUrl: `${process.env.APP_URL}/payment/callback`,
vnp_TxnRef: `ORDER_${Date.now()}`,
vnp_OrderInfo: orderInfo,
});

res.json({ success: true, paymentUrl });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});

// Xác thực kết quả thanh toán
app.get('/api/payments/verify', (req, res) => {
const verification = vnpay.verifyReturnUrl(req.query);
res.json(verification);
});

🎨 Ví dụ Frontend (React/Next.js)

// frontend/components/PaymentButton.tsx
import { useState } from 'react';
import type { VerifyReturnUrl } from 'vnpay/types-only'; // ✅ Chỉ import types

interface PaymentButtonProps {
amount: number;
orderInfo: string;
onPaymentResult?: (result: VerifyReturnUrl) => void;
}

export const PaymentButton: React.FC<PaymentButtonProps> = ({
amount,
orderInfo,
onPaymentResult
}) => {
const [loading, setLoading] = useState(false);

const handlePayment = async () => {
setLoading(true);

try {
// ✅ Gọi API backend thay vì import trực tiếp
const response = await fetch('/api/payments/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount, orderInfo }),
});

const data = await response.json();

if (data.success) {
// Chuyển hướng đến VNPay
window.location.href = data.paymentUrl;
} else {
alert('Lỗi tạo thanh toán');
}
} catch (error) {
console.error('Payment error:', error);
alert('Lỗi kết nối');
} finally {
setLoading(false);
}
};

return (
<button
onClick={handlePayment}
disabled={loading}
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700"
>
{loading ? 'Đang xử lý...' : `Thanh toán ${amount.toLocaleString()} VND`}
</button>
);
};

🔐 IPN Handler Example

// backend/routes/ipn.ts
app.post('/api/payment/ipn', (req, res) => {
try {
const verification = vnpay.verifyIpnCall(req.body);

if (verification.isSuccess) {
// ✅ Thanh toán thành công - cập nhật database
console.log('Payment successful:', verification.vnp_TxnRef);

// Cập nhật order status trong database
// updateOrderStatus(verification.vnp_TxnRef, 'PAID');

res.status(200).json({ RspCode: '00', Message: 'success' });
} else {
// ❌ Thanh toán thất bại
console.log('Payment failed:', verification.message);
res.status(200).json({ RspCode: '01', Message: 'fail' });
}
} catch (error) {
console.error('IPN processing error:', error);
res.status(500).json({ RspCode: '99', Message: 'error' });
}
});

🎯 Lưu ý quan trọng cho Frontend

❌ KHÔNG làm thế này

// 🚫 SẼ GÂY LỖI BUILD!
import { VNPay } from 'vnpay';
// Error: Module not found: Can't resolve 'fs'

const MyComponent = () => {
const vnpay = new VNPay(config); // ❌ Không thể làm trong browser!
return <div>Payment</div>;
};

✅ SỬ DỤNG đúng cách

// ✅ An toàn - chỉ import types
import type {
VNPayConfig,
BuildPaymentUrl,
Bank,
VerifyReturnUrl
} from 'vnpay/types-only';

// Hoặc sử dụng type import với main package
import type { VNPayConfig } from 'vnpay';

interface PaymentComponentProps {
config: VNPayConfig;
onPaymentResult: (result: VerifyReturnUrl) => void;
}

📚 Tài nguyên thêm