Khalti
KPG-2 redirect checkout, return parsing, and lookup verification.
@payments-sdk/khalti implements Khalti KPG-2 Web Checkout redirect
checkout, payment lookup, and browser return parsing.
Install and configure
npm install @payments-sdk/payments @payments-sdk/khaltiimport { khalti } from '@payments-sdk/khalti';
const provider = khalti({
secretKey: process.env.KHALTI_SECRET_KEY!,
environment: 'sandbox',
websiteUrl: 'https://merchant.example',
});Use the sandbox environment and credentials for development. The application supplies credentials and the package does not load them from the environment.
Redirect checkout
const result = await payments.checkout({
provider: 'khalti',
amount: 1500_00,
currency: 'NPR',
reference: 'order_123',
providerOptions: {
returnUrl: 'https://merchant.example/returns/khalti',
purchaseOrderName: 'Order 123',
customerInfo: {
name: 'Ada Example',
email: 'ada@example.com',
phone: '9800000000',
},
},
});
if (result.action.type === 'redirect') {
return Response.redirect(result.action.url);
}The amount is sent as integer paisa. The result includes a redirect
action and provider data containing the Khalti pidx, payment URL,
and optional expiry. Persist the pidx alongside the application
payment; the provider package is stateless.
Return parsing and lookup
Khalti browser returns are not signed. The return handler extracts
pidx and known fields but always reports pending, regardless of a
claimed callback status:
export async function khaltiReturn(request: Request) {
const returned = await payments.returns.khalti(request);
const verified = await payments.verify({
provider: 'khalti',
reference: returned.reference!,
providerOptions: {},
});
return { returned, verified };
}For Khalti, the verification reference is the returned pidx. Only
the gateway's Completed lookup status becomes succeeded. Pending,
expired, cancelled, refunded, partially refunded, and unknown values
remain distinct normalized states.
Capabilities and security
Khalti implements checkout, verify, and returns. It does not
implement webhooks, refunds, or cancellations. Keep secretKey
server-side; it is sent as an authorization header. Never fulfill an
order from the browser return alone.