Next.js中集成KNET Payment Gateway
Hey there! I’ve worked with KNET integrations a few times, so let me break down how to get this set up in your Next.js app—since official JS-specific docs are pretty hard to find, this step-by-step should help you get started.
First off, make sure you have your KNET merchant credentials handy: you’ll need a Merchant ID, Terminal ID, and Secret Key (you can get these from your KNET merchant dashboard).
1. Frontend: Add a Payment Trigger Component
In Next.js, you’ll create a client-side component to handle the payment initiation. This component will call your backend API to generate KNET payment parameters, then auto-submit a hidden form to redirect users to the KNET payment page.
Here’s a sample component:
'use client'; import { useState } from 'react'; export default function KnetPaymentButton() { const [isLoading, setIsLoading] = useState(false); const handlePayment = async () => { setIsLoading(true); try { // Call your backend API to get KNET payment details const res = await fetch('/api/knet/initiate-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: '1000', // Remember: KNET uses fils (1KD = 1000 fils) orderId: `ORDER-${Date.now()}`, // Unique order ID for every payment returnUrl: `${window.location.origin}/api/knet/callback`, }), }); const data = await res.json(); if (data.success) { // Create a hidden form to submit to KNET const paymentForm = document.createElement('form'); paymentForm.method = 'POST'; paymentForm.action = data.paymentUrl; // Add all required KNET parameters as hidden inputs Object.entries(data.paymentParams).forEach(([key, value]) => { const input = document.createElement('input'); input.type = 'hidden'; input.name = key; input.value = value; paymentForm.appendChild(input); }); document.body.appendChild(paymentForm); paymentForm.submit(); } } catch (err) { console.error('Failed to start payment:', err); alert('Oops, something went wrong. Please try again later.'); } finally { setIsLoading(false); } }; return ( <button onClick={handlePayment} disabled={isLoading} className="px-6 py-3 bg-blue-600 text-white rounded" > {isLoading ? 'Processing...' : 'Pay with KNET'} </button> ); }
2. Backend: Initiate Payment API Route
Create an API route (/api/knet/initiate-payment/route.js) to generate the signed KNET payment parameters. This is critical because you never want to expose your secret key on the frontend.
import crypto from 'crypto'; export async function POST(request) { try { const { amount, orderId, returnUrl } = await request.json(); // Load KNET credentials from environment variables (never hardcode these!) const merchantId = process.env.KNET_MERCHANT_ID; const terminalId = process.env.KNET_TERMINAL_ID; const secretKey = process.env.KNET_SECRET_KEY; const knetPaymentUrl = process.env.KNET_PAYMENT_URL; // Test: https://testpay.knet.com.kw/PaymentHTTP.htm | Production: https://pay.knet.com.kw/PaymentHTTP.htm // Build required KNET parameters const paymentParams = { MerchantID: merchantId, TerminalID: terminalId, Amount: amount, OrderID: orderId, ReturnURL: returnUrl, ErrorURL: returnUrl, // Can use the same URL as return, or a separate error page Language: 'ENG', // Use 'ARB' for Arabic CurrencyCode: '414', // Currency code for KD; adjust if using other currencies }; // Generate the secure hash (follow KNET's exact parameter order!) const signatureString = `${paymentParams.MerchantID}${paymentParams.TerminalID}${paymentParams.OrderID}${paymentParams.Amount}${paymentParams.CurrencyCode}`; const secureHash = crypto.createHmac('sha256', secretKey) .update(signatureString) .digest('hex') .toUpperCase(); paymentParams.SecureHash = secureHash; return new Response( JSON.stringify({ success: true, paymentUrl: knetPaymentUrl, paymentParams, }), { status: 200 } ); } catch (err) { return new Response( JSON.stringify({ success: false, error: err.message }), { status: 500 } ); } }
3. Backend: Handle KNET Callback
KNET will send a POST request to your specified returnUrl once the payment is complete. Create another API route (/api/knet/callback/route.js) to verify the payment and update your order status.
import crypto from 'crypto'; import { redirect } from 'next/navigation'; export async function POST(request) { try { const formData = await request.formData(); const callbackData = Object.fromEntries(formData.entries()); // Extract key callback parameters const { MerchantID, TerminalID, OrderID, Amount, CurrencyCode, ResponseCode, TrackID, SecureHash, } = callbackData; const secretKey = process.env.KNET_SECRET_KEY; // Verify the secure hash to ensure the callback is legitimate const verifyString = `${MerchantID}${TerminalID}${OrderID}${ResponseCode}${TrackID}${Amount}${CurrencyCode}`; const calculatedHash = crypto.createHmac('sha256', secretKey) .update(verifyString) .digest('hex') .toUpperCase(); if (calculatedHash !== SecureHash) { console.error('KNET callback signature verification failed'); return redirect('/payment/failed'); } // Process payment result if (ResponseCode === '000') { // ✅ Payment succeeded: Update your database (mark order as paid, save TrackID, etc.) console.log(`Payment successful for order ${OrderID} | TrackID: ${TrackID}`); return redirect(`/payment/success?orderId=${OrderID}`); } else { // ❌ Payment failed: Log the error and redirect to failure page console.log(`Payment failed for order ${OrderID} | Response Code: ${ResponseCode}`); return redirect(`/payment/failed?orderId=${OrderID}`); } } catch (err) { console.error('Error handling KNET callback:', err); return redirect('/payment/failed'); } }
Key Things to Remember
- Environment Variables: Store all KNET credentials in
.env.local(e.g.,KNET_MERCHANT_ID=xxx) to keep them secure. - Test First: Use KNET’s test environment to debug before switching to production. You can get test cards from your merchant dashboard.
- Parameter Order: The secure hash generation relies on a strict parameter order—double-check KNET’s docs to make sure you’re using the right sequence (I’ve included the standard ones here, but confirm with your KNET rep).
- Amount Format: Always use fils for the amount (no decimals!). For example, 2.5 KD becomes
2500. - Error Handling: Add proper error messages for users and log all payment events for debugging.
备注:内容来源于stack exchange,提问作者Samiur Akif

