UNPKG

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
"use strict"; 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;