UNPKG

nagad-payment-gateway

Version:

Nagad Payment Gateway API Library to accept nagad payments on your node.js backend

189 lines (184 loc) 5.78 kB
// src/index.ts import * as crypto from "crypto"; import * as fs from "fs"; // src/utils/request.ts import fetch from "node-fetch"; // src/exceptions/NagadException.ts var NagadException = class extends Error { constructor(message, reason) { super(reason); this.reason = reason; this.name = "NagadException"; this.stack = this.stack ?? new Error().stack; } }; // src/utils/request.ts async function get(url, additionalHeaders) { const r = await fetch(url, { method: "GET", headers: { "content-type": "application/json", Accept: "application/json", ...additionalHeaders } }); const data = await r.json(); if (data.devMessage) { throw new NagadException(data.devMessage, data.reason); } if (data.reason) { throw new NagadException(data.message, data.reason); } return data; } async function post(url, payload = {}, additionalHeaders) { const r = await fetch(url, { headers: { "content-type": "application/json", Accept: "application/json", ...additionalHeaders }, body: JSON.stringify(payload), method: "POST" }); const data = await r.json(); if (data.devMessage) { throw new NagadException(data.devMessage, data.reason); } if (data.reason) { throw new NagadException(data.message, data.reason); } return data; } // src/index.ts import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; var NagadGateway = class { constructor(config) { const { baseURL, callbackURL, merchantID, merchantNumber, privKey, pubKey, apiVersion, isPath } = config; this.baseURL = baseURL; this.merchantID = merchantID; this.merchantNumber = merchantNumber; this.headers = { "X-KM-Api-Version": apiVersion }; this.callbackURL = callbackURL; const { privateKey, publicKey } = this.genKeys(privKey, pubKey, isPath); this.privKey = privateKey; this.pubKey = publicKey; dayjs.extend(utc); dayjs.extend(timezone); } async createPayment(createPaymentConfig) { const { amount, ip, orderId, productDetails, clientType } = createPaymentConfig; const endpoint = `${this.baseURL}/api/dfs/check-out/initialize/${this.merchantID}/${orderId}`; const timestamp = this.getTimeStamp(); const sensitive = { merchantId: this.merchantID, datetime: timestamp, orderId, challenge: this.createHash(orderId) }; const payload = { accountNumber: this.merchantNumber, dateTime: timestamp, sensitiveData: this.encrypt(sensitive), signature: this.sign(sensitive) }; const newIP = ip === "::1" || ip === "127.0.0.1" ? "103.100.200.100" : ip; const { sensitiveData } = await post(endpoint, payload, { ...this.headers, "X-KM-IP-V4": newIP, "X-KM-Client-Type": clientType }); const decrypted = this.decrypt(sensitiveData); const { paymentReferenceId, challenge } = decrypted; const confirmArgs = { paymentReferenceId, challenge, orderId, amount, productDetails, ip: newIP }; const { callBackUrl } = await this.confirmPayment(confirmArgs, clientType); return callBackUrl; } async verifyPayment(paymentRefID) { return await get( `${this.baseURL}/api/dfs/verify/payment/${paymentRefID}`, this.headers ); } async confirmPayment(data, clientType) { const { amount, challenge, ip, orderId, paymentReferenceId, productDetails } = data; const sensitiveData = { merchantId: this.merchantID, orderId, amount, currencyCode: "050", challenge }; const payload = { paymentRefId: paymentReferenceId, sensitiveData: this.encrypt(sensitiveData), signature: this.sign(sensitiveData), merchantCallbackURL: this.callbackURL, additionalMerchantInfo: { ...productDetails } }; const newIP = ip === "::1" || ip === "127.0.0.1" ? "103.100.102.100" : ip; return await post(`${this.baseURL}/api/dfs/check-out/complete/${paymentReferenceId}`, payload, { ...this.headers, "X-KM-IP-V4": newIP, "X-KM-Client-Type": clientType }); } encrypt(data) { const signerObject = crypto.publicEncrypt( { key: this.pubKey, padding: crypto.constants.RSA_PKCS1_PADDING }, Buffer.from(JSON.stringify(data)) ); return signerObject.toString("base64"); } decrypt(data) { const decrypted = crypto.privateDecrypt({ key: this.privKey, padding: crypto.constants.RSA_PKCS1_PADDING }, Buffer.from(data, "base64")).toString(); return JSON.parse(decrypted); } sign(data) { const signerObject = crypto.createSign("SHA256"); signerObject.update(JSON.stringify(data)); signerObject.end(); return signerObject.sign(this.privKey, "base64"); } getTimeStamp() { const timestamp = dayjs().tz("Asia/Dhaka").format("YYYYMMDDHHmmss"); return timestamp; } createHash(string) { return crypto.createHash("sha1").update(string).digest("hex").toUpperCase(); } genKeys(privKeyPath, pubKeyPath, isPath) { if (!isPath) { return { privateKey: this.formatKey(privKeyPath, "PRIVATE"), publicKey: this.formatKey(pubKeyPath, "PUBLIC") }; } const fsPrivKey = fs.readFileSync(privKeyPath, { encoding: "utf-8" }); const fsPubKey = fs.readFileSync(pubKeyPath, { encoding: "utf-8" }); return { publicKey: this.formatKey(fsPubKey, "PUBLIC"), privateKey: this.formatKey(fsPrivKey, "PRIVATE") }; } formatKey(key, type) { return /begin/i.test(key) ? key.trim() : `-----BEGIN ${type} KEY----- ${key.trim()} -----END ${type} KEY-----`; } }; var src_default = NagadGateway; export { NagadGateway, src_default as default };