Trong thời đại thương mại điện tử bùng nổ, việc cung cấp đa dạng phương thức thanh toán là yếu tố sống còn để tăng tỷ lệ chuyển đổi. VNPay hiện đang là một trong những cổng thanh toán phổ biến và uy tín nhất tại Việt Nam.
Hôm nay, Techblog.vn sẽ hướng dẫn bạn cách tích hợp cổng thanh toán VNPay với Laravel một cách chuẩn xác, bảo mật và nhanh chóng nhất. Bài viết này dành cho các lập trình viên đang xây dựng website bán hàng hoặc dịch vụ bằng framework Laravel.
1. Chuẩn bị trước khi tích hợp VNPay vào Laravel
Để quá trình tích hợp diễn ra suôn sẻ, bạn cần chuẩn bị các yếu tố sau:
Một Project Laravel đã được cài đặt và đang hoạt động (phiên bản Laravel 8, 9, 10 hoặc 11 đều áp dụng được).
Tài khoản môi trường thử nghiệm (Sandbox) của VNPay. Bạn cần đăng ký trên trang chủ VNPay Sandbox để lấy 3 thông tin quan trọng:
vnp_TmnCode: Mã website của bạn tại VNPay.
vnp_HashSecret: Chuỗi bí mật dùng để tạo checksum (đảm bảo tính toàn vẹn dữ liệu).
vnp_Url: URL thanh toán của VNPay.
2. Các bước tích hợp VNPay với Laravel
Bước 1: Cấu hình môi trường (.env)
Mở file .env ở thư mục gốc của project Laravel và thêm các thông tin bạn vừa lấy được từ VNPay:
VNPAY_TMN_CODE=Mã_TmnCode_Của_Bạn
VNPAY_HASH_SECRET=Chuỗi_HashSecret_Của_Bạn
VNPAY_URL=https://sandbox.vnpayment.vn/paymentv2/vpcpay.html
VNPAY_RETURN_URL="${APP_URL}/vnpay/return"
Tiếp theo, để dễ dàng gọi các biến này trong code, hãy khai báo chúng trong file config/services.php:
'vnpay' => [
'tmn_code' => env('VNPAY_TMN_CODE'),
'hash_secret' => env('VNPAY_HASH_SECRET'),
'url' => env('VNPAY_URL'),
'return_url' => env('VNPAY_RETURN_URL'),
],
Bước 2: Tạo Controller xử lý thanh toán VNPay
Bạn chạy lệnh sau trong terminal để tạo Controller: php artisan make:controller VNPayController
Trong VNPayController, chúng ta sẽ cần 2 hàm chính: một hàm để tạo URL thanh toán và chuyển hướng người dùng, một hàm để xử lý kết quả trả về (Return URL).
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class VNPayController extends Controller
{
// Hàm tạo URL và chuyển hướng đến VNPay
public function createPayment(Request $request)
{
$vnp_Url = config('services.vnpay.url');
$vnp_Returnurl = config('services.vnpay.return_url');
$vnp_TmnCode = config('services.vnpay.tmn_code');
$vnp_HashSecret = config('services.vnpay.hash_secret');
$vnp_TxnRef = time(); // Mã đơn hàng (có thể thay bằng mã ID đơn hàng thật)
$vnp_OrderInfo = "Thanh toán đơn hàng #" . $vnp_TxnRef;
$vnp_OrderType = 'billpayment';
$vnp_Amount = $request->amount * 100; // Số tiền thanh toán (VNĐ) nhân 100
$vnp_Locale = 'vn';
$vnp_IpAddr = $request->ip();
$inputData = array(
"vnp_Version" => "2.1.0",
"vnp_TmnCode" => $vnp_TmnCode,
"vnp_Amount" => $vnp_Amount,
"vnp_Command" => "pay",
"vnp_CreateDate" => date('YmdHis'),
"vnp_CurrCode" => "VND",
"vnp_IpAddr" => $vnp_IpAddr,
"vnp_Locale" => $vnp_Locale,
"vnp_OrderInfo" => $vnp_OrderInfo,
"vnp_OrderType" => $vnp_OrderType,
"vnp_ReturnUrl" => $vnp_Returnurl,
"vnp_TxnRef" => $vnp_TxnRef
);
ksort($inputData);
$query = "";
$i = 0;
$hashdata = "";
foreach ($inputData as $key => $value) {
if ($i == 1) {
$hashdata .= '&' . urlencode($key) . "=" . urlencode($value);
} else {
$hashdata .= urlencode($key) . "=" . urlencode($value);
$i = 1;
}
$query .= urlencode($key) . "=" . urlencode($value) . '&';
}
$vnp_Url = $vnp_Url . "?" . $query;
if (isset($vnp_HashSecret)) {
$vnpSecureHash = hash_hmac('sha512', $hashdata, $vnp_HashSecret);
$vnp_Url .= 'vnp_SecureHash=' . $vnpSecureHash;
}
return redirect()->away($vnp_Url);
}
// Hàm xử lý kết quả trả về từ VNPay
public function returnPayment(Request $request)
{
$vnp_HashSecret = config('services.vnpay.hash_secret');
$inputData = array();
foreach ($request->all() as $key => $value) {
if (substr($key, 0, 4) == "vnp_") {
$inputData[$key] = $value;
}
}
$vnp_SecureHash = $inputData['vnp_SecureHash'];
unset($inputData['vnp_SecureHash']);
ksort($inputData);
$i = 0;
$hashData = "";
foreach ($inputData as $key => $value) {
if ($i == 1) {
$hashData = $hashData . '&' . urlencode($key) . "=" . urlencode($value);
} else {
$hashData = $hashData . urlencode($key) . "=" . urlencode($value);
$i = 1;
}
}
$secureHash = hash_hmac('sha512', $hashData, $vnp_HashSecret);
if ($secureHash == $vnp_SecureHash) {
if ($request->vnp_ResponseCode == '00') {
// Giao dịch thành công
// Cập nhật trạng thái đơn hàng vào database tại đây
return view('payment.success', ['message' => 'Thanh toán thành công!']);
} else {
// Giao dịch lỗi
return view('payment.error', ['message' => 'Giao dịch bị hủy hoặc lỗi!']);
}
} else {
// Chữ ký không hợp lệ
return view('payment.error', ['message' => 'Chữ ký không hợp lệ!']);
}
}
}
Bước 3: Thiết lập Route
Mở file routes/web.php và định tuyến cho 2 hàm chúng ta vừa tạo:
use App\Http\Controllers\VNPayController;
// Route xử lý form gửi đi
Route::post('/vnpay/payment', [VNPayController::class, 'createPayment'])->name('vnpay.payment');
// Route nhận kết quả trả về
Route::get('/vnpay/return', [VNPayController::class, 'returnPayment'])->name('vnpay.return');
Bước 4: Tạo View nút thanh toán (Blade Template)
Tạo một form HTML cơ bản để người dùng nhập số tiền và bấm nút thanh toán:
<form action="{{ route('vnpay.payment') }}" method="POST">
@csrf
<div class="form-group">
<label for="amount">Số tiền cần thanh toán (VNĐ)</label>
<input type="number" name="amount" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary mt-3">Thanh toán qua VNPay</button>
</form>
3. Lưu ý quan trọng khi đưa lên môi trường thật (Production)
Sử dụng IPN (Instant Payment Notification): Hàm
returnPaymentở trên chỉ để hiển thị cho người dùng. Để đảm bảo tính chính xác tuyệt đối (tránh trường hợp người dùng tắt trình duyệt khi đang load), bạn phải cấu hình thêm URL IPN. VNPay sẽ gọi ngầm vào URL IPN này để cập nhật trạng thái đơn hàng vào Database của bạn.Thay đổi thông tin Cấu hình: Khi golive, bạn phải thay đổi
vnp_TmnCode,vnp_HashSecretvàVNPAY_URLthành thông tin môi trường thật do VNPay cấp.
Kết Luận
Việc tích hợp cổng thanh toán VNPay với Laravel không quá phức tạp nếu bạn làm đúng theo tài liệu chuẩn. Hy vọng bài viết này của Techblog.vn đã giúp bạn cấu hình thành công và tối ưu hóa hệ thống thanh toán cho dự án của mình.





