UNPKG

@zed-io/wam-payment-sdk

Version:

Official WAM Payment SDK for creating and signing payment links

230 lines (222 loc) 8.18 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var crypto = require('crypto'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto); class WamPaymentError extends Error { constructor(message, code, validationErrors) { super(message); this.code = code; this.validationErrors = validationErrors; this.name = 'WamPaymentError'; } } exports.WamPaymentErrorCode = void 0; (function (WamPaymentErrorCode) { WamPaymentErrorCode["INVALID_CONFIG"] = "INVALID_CONFIG"; WamPaymentErrorCode["INVALID_AMOUNT"] = "INVALID_AMOUNT"; WamPaymentErrorCode["INVALID_CURRENCY"] = "INVALID_CURRENCY"; WamPaymentErrorCode["INVALID_REFERENCE"] = "INVALID_REFERENCE"; WamPaymentErrorCode["INVALID_RETURN_URL"] = "INVALID_RETURN_URL"; WamPaymentErrorCode["SIGNING_ERROR"] = "SIGNING_ERROR"; WamPaymentErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR"; })(exports.WamPaymentErrorCode || (exports.WamPaymentErrorCode = {})); class WamPaymentSDK { constructor(config) { this.validateConfig(config); this.config = config; this.baseUrl = this.getBaseUrl(); } /** * Generates a signed payment link for WAM payment processing * @param params Payment parameters including amount, currency, reference, and optional return URL * @returns Payment link response with URL, signature, and metadata */ generatePaymentLink(params) { // Validate input parameters this.validatePaymentParams(params); // Generate timestamp const timestamp = Date.now(); // Create the payload to sign (must match the format expected by WAM) const payload = `${params.amount}|${params.currency}|${params.reference}|${timestamp}`; // Generate HMAC-SHA256 signature const signature = this.generateSignature(payload); // Build the payment URL const paymentUrl = this.buildPaymentUrl(params, timestamp, signature); return { url: paymentUrl, signature, timestamp, payload }; } /** * Validates payment parameters * @param params Payment parameters to validate * @throws WamPaymentError if validation fails */ validatePaymentParams(params) { const errors = []; // Validate amount if (typeof params.amount !== 'number' || params.amount <= 0) { errors.push({ field: 'amount', message: 'Amount must be a positive number' }); } // Validate currency if (!params.currency || (params.currency !== 'TTD' && params.currency !== 'USD')) { errors.push({ field: 'currency', message: 'Currency must be either "TTD" or "USD"' }); } // Validate reference if (!params.reference || typeof params.reference !== 'string' || params.reference.trim().length === 0) { errors.push({ field: 'reference', message: 'Reference must be a non-empty string' }); } // Validate return URL if provided if (params.returnUrl && typeof params.returnUrl !== 'string') { errors.push({ field: 'returnUrl', message: 'Return URL must be a string' }); } if (params.returnUrl && !this.isValidUrl(params.returnUrl)) { errors.push({ field: 'returnUrl', message: 'Return URL must be a valid URL' }); } if (errors.length > 0) { throw new WamPaymentError('Validation failed', exports.WamPaymentErrorCode.VALIDATION_ERROR, errors); } } /** * Validates the SDK configuration * @param config Configuration to validate * @throws WamPaymentError if configuration is invalid */ validateConfig(config) { const errors = []; if (!config.businessId || typeof config.businessId !== 'string') { errors.push({ field: 'businessId', message: 'Business ID is required and must be a string' }); } if (!config.privateKey || typeof config.privateKey !== 'string') { errors.push({ field: 'privateKey', message: 'Private key is required and must be a string' }); } if (errors.length > 0) { throw new WamPaymentError('Invalid configuration', exports.WamPaymentErrorCode.INVALID_CONFIG, errors); } } /** * Generates HMAC-SHA256 signature * @param payload The payload to sign * @returns Hex-encoded signature */ generateSignature(payload) { try { return crypto__namespace .createHmac('sha256', this.config.privateKey) .update(payload) .digest('hex'); } catch (error) { throw new WamPaymentError('Failed to generate signature', exports.WamPaymentErrorCode.SIGNING_ERROR); } } /** * Gets the base URL from environment variables or defaults to production * @returns Base URL for WAM payment gateway */ getBaseUrl() { // Check for environment variable first if (process.env.WAM_PAY_URL) { return process.env.WAM_PAY_URL; } // Check for common environment indicators const nodeEnv = process.env.NODE_ENV?.toLowerCase(); const wamEnv = process.env.WAM_ENVIRONMENT?.toLowerCase(); if (nodeEnv === 'development' || wamEnv === 'development') { return 'https://wam-payments-v2-git-development-zed-io.vercel.app'; } if (nodeEnv === 'staging' || wamEnv === 'staging') { return 'https://pay-staging.wam.money'; } // Default to production return 'https://pay.wam.money'; } /** * Builds the complete payment URL with all parameters * @param params Payment parameters * @param timestamp Generated timestamp * @param signature Generated signature * @returns Complete payment URL */ buildPaymentUrl(params, timestamp, signature) { const paymentUrl = new URL(`${this.baseUrl}/pay/external/generic/${this.config.businessId}`); // Add required parameters paymentUrl.searchParams.set('amount', params.amount.toString()); paymentUrl.searchParams.set('currency', params.currency); paymentUrl.searchParams.set('reference', params.reference); paymentUrl.searchParams.set('timestamp', timestamp.toString()); paymentUrl.searchParams.set('signature', signature); // Add optional return URL if (params.returnUrl) { paymentUrl.searchParams.set('returnUrl', params.returnUrl); } return paymentUrl.toString(); } /** * Validates if a string is a valid URL * @param url String to validate * @returns true if valid URL, false otherwise */ isValidUrl(url) { try { new URL(url); return true; } catch { return false; } } /** * Gets the current configuration (without sensitive data) * @returns Configuration object without private key */ getConfig() { return { businessId: this.config.businessId, baseUrl: this.baseUrl }; } } exports.WamPaymentError = WamPaymentError; exports.WamPaymentSDK = WamPaymentSDK; exports.default = WamPaymentSDK; //# sourceMappingURL=index.js.map