neppayments
Version:
A simple and easy-to-use package for integrating Nepali payment gateways (Khalti and eSewa) into your applications
167 lines (166 loc) • 7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EsewaGateway = void 0;
const axios_1 = __importDefault(require("axios"));
const crypto_1 = __importDefault(require("crypto"));
const payment_enums_1 = require("../types/payment.enums");
function isApiError(error) {
return error && typeof error.code === 'number' && typeof error.error_message === 'string';
}
/**
* eSewa payment gateway implementation
*/
class EsewaGateway {
/**
* Create a new eSewa gateway instance
* @param config eSewa configuration
*/
constructor(config) {
this.config = config;
if (!config.productCode || !config.secretKey) {
throw new payment_enums_1.PaymentError('eSewa product code and secret key are required', payment_enums_1.PaymentErrorCode.INVALID_CONFIG);
}
// Set base URLs based on environment
if (config.environment === 'sandbox') {
this.baseUrl = 'https://rc-epay.esewa.com.np/api/epay/main/v2';
this.statusCheckUrl = 'https://rc.esewa.com.np/api/epay/transaction/status/';
}
else {
this.baseUrl = 'https://epay.esewa.com.np/api/epay/main/v2';
this.statusCheckUrl = 'https://esewa.com.np/api/epay/transaction/status/';
}
}
/**
* Generate HMAC signature for the payment
* @private
*/
generateSignature(data) {
try {
const hmac = crypto_1.default.createHmac('sha256', this.config.secretKey);
hmac.update(data);
return hmac.digest('base64');
}
catch (error) {
console.error('Error generating signature:', error);
throw new payment_enums_1.PaymentError('Failed to generate payment signature', payment_enums_1.PaymentErrorCode.SIGNATURE_GENERATION_FAILED);
}
}
/**
* Create a new payment
* @param options Payment options
* @returns Payment response with form HTML and transaction UUID
*/
async createPayment(options) {
try {
// Validate required fields
if (!options.transaction_uuid || !options.total_amount) {
throw new payment_enums_1.PaymentError('Transaction UUID and total amount are required', payment_enums_1.PaymentErrorCode.INVALID_PAYMENT_OPTIONS);
}
// Generate signature
const signedFields = options.signed_field_names.split(',');
const signatureData = signedFields
.map(field => `${field}=${String(options[field])}`)
.join(',');
const signature = this.generateSignature(signatureData);
// Create form data
const formData = new URLSearchParams();
Object.entries(options).forEach(([key, value]) => {
formData.append(key, String(value));
});
formData.append('signature', signature);
// Make request to eSewa
const response = await axios_1.default.post(`${this.baseUrl}/form`, formData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
validateStatus: (status) => status >= 200 && status < 400
});
// Check if we got a valid HTML response
if (!response.data || typeof response.data !== 'string') {
throw new payment_enums_1.PaymentError('Invalid response from eSewa: Expected HTML form', payment_enums_1.PaymentErrorCode.INVALID_RESPONSE);
}
// Return the form HTML and transaction details
return {
form_html: response.data,
transaction_uuid: options.transaction_uuid,
signature
};
}
catch (error) {
if (error instanceof payment_enums_1.PaymentError) {
throw error;
}
// Log the error response for debugging
if (error.response) {
console.error('eSewa API Error:', error.response.status, error.response.data);
}
else if (error.request) {
console.error('eSewa API No Response:', error.request);
}
else {
console.error('eSewa API Error:', error.message);
}
if (isApiError(error)) {
throw new payment_enums_1.PaymentError(error.error_message || 'Failed to create payment', payment_enums_1.PaymentErrorCode.PAYMENT_FAILED);
}
throw new payment_enums_1.PaymentError('Failed to create payment', payment_enums_1.PaymentErrorCode.PAYMENT_FAILED);
}
}
/**
* Verify a payment
* @param options Verification options
* @returns Payment verification response
*/
async verifyPayment(options) {
try {
// Validate required fields
if (!options.transaction_uuid || !options.total_amount) {
throw new payment_enums_1.PaymentError('Transaction UUID and total amount are required for verification', payment_enums_1.PaymentErrorCode.INVALID_VERIFICATION_OPTIONS);
}
const response = await axios_1.default.get(this.statusCheckUrl, {
params: {
product_code: this.config.productCode,
transaction_uuid: options.transaction_uuid,
total_amount: options.total_amount
}
});
const payment = response.data;
// Map eSewa status to normalized status
let status;
switch (payment.status) {
case 'COMPLETE':
status = 'COMPLETED';
break;
case 'PENDING':
status = 'PENDING';
break;
case 'FULL_REFUND':
case 'PARTIAL_REFUND':
case 'CANCELED':
status = 'CANCELED';
break;
default:
status = 'FAILED';
}
return {
status,
transaction_id: payment.ref_id || '',
amount: payment.total_amount,
payment_details: payment
};
}
catch (error) {
if (error instanceof payment_enums_1.PaymentError) {
throw error;
}
if (isApiError(error)) {
throw new payment_enums_1.PaymentError(error.error_message || 'Failed to verify payment', payment_enums_1.PaymentErrorCode.VERIFICATION_FAILED);
}
throw new payment_enums_1.PaymentError('Failed to verify payment', payment_enums_1.PaymentErrorCode.VERIFICATION_FAILED);
}
}
}
exports.EsewaGateway = EsewaGateway;