Examples
π Full-stack Sample Projectsβ
Next.js 15 Full-stack Exampleβ
- GitHub: lehuygiang28/vnpay-nextjs-fullstack-example
- Live Demo: vnpay-nextjs-fullstack-example.vercel.app
- Technologies: Next.js 15, TypeScript, Tailwind CSS, Server Actions
Key Features:
- β Server-side VNPay integration - All payment processing on server
- β Next.js 15 App Router with Server Actions and API Routes
- β IPN Handler - Webhook processing from VNPay
- β Types-only imports for frontend components
- β Responsive UI with Tailwind CSS
- β Demo sandbox environment with test data
Express MVC Exampleβ
- GitHub: lehuygiang28/vnpay-mvc-example
- Live Demo: vnpay-mvc-example.vercel.app
- Technologies: Express, TypeScript, Bootstrap 5, Handlebars
π Basic Usage Examplesβ
Library Methodsβ
See examples of all library methods here
Express Implementationβ
See a complete Express implementation example here
ποΈ Proper Full-stack Architectureβ
β οΈ Important Principlesβ
WARNING
The VNPay library is designed for Node.js backends only! Cannot be used directly in React/Vue/Angular components because:
- π« Uses Node.js modules:
fs
,crypto
,path
- π« Contains server-side logic to secure
secureSecret
- π« Will cause build errors when imported in client components
β Recommended Architectureβ
βββββββββββββββββββ API calls βββββββββββββββββββ
β Frontend β βββββββββββββββΊ β Backend β
β β β β
β β’ React/Vue β β β’ Node.js β
β β’ Types only β β β’ VNPay library β
β β’ UI components β βββββββββββββββ β β’ Payment logic β
βββββββββββββββββββ JSON response βββββββββββββββββββ
πΌ Backend Example (Node.js/Express)β
// backend/routes/payment.ts
import { VNPay } from 'vnpay'; // β
Full import on backend
const vnpay = new VNPay({
tmnCode: process.env.VNP_TMNCODE!,
secureSecret: process.env.VNP_SECRET!,
testMode: true
});
// Create payment URL
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 });
}
});
// Verify payment result
app.get('/api/payments/verify', (req, res) => {
const verification = vnpay.verifyReturnUrl(req.query);
res.json(verification);
});
π¨ Frontend Example (React/Next.js)β
// frontend/components/PaymentButton.tsx
import { useState } from 'react';
import type { VerifyReturnUrl } from 'vnpay/types-only'; // β
Types only
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 {
// β
Call backend API instead of direct import
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) {
// Redirect to VNPay
window.location.href = data.paymentUrl;
} else {
alert('Payment creation failed');
}
} catch (error) {
console.error('Payment error:', error);
alert('Connection error');
} 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 ? 'Processing...' : `Pay ${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) {
// β
Payment successful - update database
console.log('Payment successful:', verification.vnp_TxnRef);
// Update order status in database
// updateOrderStatus(verification.vnp_TxnRef, 'PAID');
res.status(200).json({ RspCode: '00', Message: 'success' });
} else {
// β Payment failed
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' });
}
});
π― Important Notes for Frontendβ
β DON'T do thisβ
// π« WILL CAUSE BUILD ERRORS!
import { VNPay } from 'vnpay';
// Error: Module not found: Can't resolve 'fs'
const MyComponent = () => {
const vnpay = new VNPay(config); // β Cannot do this in browser!
return <div>Payment</div>;
};
β CORRECT usageβ
// β
Safe - import types only
import type {
VNPayConfig,
BuildPaymentUrl,
Bank,
VerifyReturnUrl
} from 'vnpay/types-only';
// Or use type import with main package
import type { VNPayConfig } from 'vnpay';
interface PaymentComponentProps {
config: VNPayConfig;
onPaymentResult: (result: VerifyReturnUrl) => void;
}
π Additional Resourcesβ
- π VNPay Documentation: sandbox.vnpayment.vn/apis
- π§ Next.js Server Actions: nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
- π¨ Tailwind CSS: tailwindcss.com/docs
- π¦ vnpay npm package: npmjs.com/package/vnpay