Pakistani gateways each want a different payload, signed a different way. pk-pay gives you one function call β and checks the callback is genuine when the money comes back.
npm i pk-pay
Swap the provider and nothing else changes. You get back a form to send the customer to.
const payment = await pay.createPayment({
provider: 'jazzcash', // or 'easypaisa'
amount: 250000,
currency: 'PKR',
returnUrl: 'https://shop.pk/callback',
customerPhone: '03001234567',
});
res.send(payment.redirectForm);
Money is passed in paisa β the smallest unit β so a rounding error can never cost someone a rupee. JazzCash takes paisa directly; the SDK reformats it for gateways that want something else.
// 250000 paisa = PKR 2,500.00 amount: 250000 // never this: // amount: 2500.00 β floats and money // don't mix
Each gateway wants its fields sorted, joined and hashed with your secret salt. Get the order wrong and the gateway rejects the payment. The SDK builds it; you never touch the crypto.
createClient({
jazzcash: {
merchantId,
integritySalt, // β the signing key
}
});
// pk_SecureHash is computed and attached
// HMAC-SHA256, uppercase hex
Change the salt or the amount above β the signature changes completely.
When the gateway posts back, anyone could have sent that request. verifyWebhook recomputes the hash and throws if it doesn't match β so a forged "payment succeeded" never reaches your order code.
app.post('/callback', async (req, res) => {
const event =
await pay.verifyWebhook('jazzcash', req.body);
// only runs if the signature is genuine
markOrderPaid(event.transactionId);
});
Raise the amount and watch it fail β the gateway signed the original numbers.