UNPKG

neppayments

Version:

A simple and easy-to-use package for integrating Nepali payment gateways (Khalti and eSewa) into your applications

149 lines (148 loc) 6.19 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.KhaltiGateway = void 0; const axios_1 = __importDefault(require("axios")); const payment_enums_1 = require("../types/payment.enums"); function isApiError(error) { return typeof error === 'object' && error !== null && ('error_key' in error || 'detail' in error); } /** * Khalti payment gateway implementation */ class KhaltiGateway { /** * Create a new Khalti gateway instance * @param config Khalti configuration */ constructor(config) { this.config = config; if (!config.secretKey) { throw new payment_enums_1.PaymentError('Khalti secret key is required', payment_enums_1.PaymentErrorCode.INVALID_CONFIG); } // Set base URL based on environment this.baseUrl = config.environment === 'sandbox' ? 'https://dev.khalti.com/api/v2' : 'https://khalti.com/api/v2'; } /** * Validate payment options * @private */ validatePaymentOptions(options) { // Validate amount if (options.amount < 1000) { throw new payment_enums_1.PaymentError('Amount should be greater than Rs. 10 (1000 paisa)', payment_enums_1.PaymentErrorCode.INVALID_AMOUNT); } // Validate URLs if (!options.return_url || !options.website_url) { throw new payment_enums_1.PaymentError('return_url and website_url are required', payment_enums_1.PaymentErrorCode.INVALID_URL); } try { new URL(options.return_url); new URL(options.website_url); } catch (_a) { throw new payment_enums_1.PaymentError('Invalid return_url or website_url', payment_enums_1.PaymentErrorCode.INVALID_URL); } // Validate amount breakdown if provided if (options.amount_breakdown) { const total = options.amount_breakdown.reduce((sum, item) => sum + item.amount, 0); if (total !== options.amount) { throw new payment_enums_1.PaymentError('Sum of amount_breakdown amounts must equal the total amount', payment_enums_1.PaymentErrorCode.INVALID_AMOUNT); } } // Validate product details if provided if (options.product_details) { for (const product of options.product_details) { if (product.total_price !== product.unit_price * product.quantity) { throw new payment_enums_1.PaymentError('Product total_price must equal unit_price * quantity', payment_enums_1.PaymentErrorCode.INVALID_AMOUNT); } } } } /** * Create a new payment * @param options Payment options * @returns Payment response with payment URL and ID */ async createPayment(options) { try { // Validate options this.validatePaymentOptions(options); const response = await axios_1.default.post(`${this.baseUrl}/epayment/initiate/`, { amount: options.amount, purchase_order_id: options.purchase_order_id, purchase_order_name: options.purchase_order_name, return_url: options.return_url, website_url: options.website_url, customer_info: options.customer_info, amount_breakdown: options.amount_breakdown, product_details: options.product_details, merchant_username: options.merchant_username, merchant_extra: options.merchant_extra }, { headers: { Authorization: `Key ${this.config.secretKey}` } }); const data = response.data; return { pidx: data.pidx, payment_url: data.payment_url, expires_at: data.expires_at, expires_in: data.expires_in }; } catch (error) { if (isApiError(error)) { if (error.error_key === 'validation_error') { throw new payment_enums_1.PaymentError(Object.values(error).filter(v => typeof v === 'string').join(', '), payment_enums_1.PaymentErrorCode.VALIDATION_ERROR); } throw new payment_enums_1.PaymentError(error.detail || 'Failed to create payment', payment_enums_1.PaymentErrorCode.PAYMENT_FAILED); } throw error; } } /** * Verify a payment * @param options Verification options * @returns Payment verification response */ async verifyPayment(options) { try { const response = await axios_1.default.post(`${this.baseUrl}/epayment/lookup/`, { pidx: options.pidx }, { headers: { Authorization: `Key ${this.config.secretKey}` } }); const payment = response.data; const allowedStatuses = ['COMPLETED', 'FAILED', 'PENDING', 'EXPIRED', 'CANCELED']; let status = (payment.status || '').toUpperCase(); if (!allowedStatuses.includes(status)) { status = 'FAILED'; } return { status: status, transaction_id: payment.transaction_id, amount: payment.amount, payment_details: payment }; } catch (error) { if (isApiError(error)) { if (error.error_key === 'validation_error') { throw new payment_enums_1.PaymentError(Object.values(error).filter(v => typeof v === 'string').join(', '), payment_enums_1.PaymentErrorCode.VALIDATION_ERROR); } throw new payment_enums_1.PaymentError(error.detail || 'Failed to verify payment', payment_enums_1.PaymentErrorCode.VERIFICATION_FAILED); } throw error; } } } exports.KhaltiGateway = KhaltiGateway;