UNPKG

@stratosphere-network/wallet

Version:

Wallet module for StratoSphere SDK

156 lines 5.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MpesaHttpClient = void 0; class MpesaHttpClient { config; baseUrl; constructor(config) { this.config = config; this.baseUrl = this.getMpesaBaseUrl(); } getMpesaBaseUrl() { // Always use the production Mpesa service URL as specified return "https://mpesa-offramping-onramping-production.up.railway.app"; } async request(requestConfig) { const { method, url, data, params, headers = {} } = requestConfig; // Build URL with query parameters const fullUrl = new URL(url, this.baseUrl); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null) { fullUrl.searchParams.append(key, String(value)); } }); } // Build headers const requestHeaders = { "Content-Type": "application/json", ...headers, }; // Add API key if available if (this.config.apiKey) { requestHeaders["X-API-Key"] = this.config.apiKey; } // Build fetch options const fetchOptions = { method, headers: requestHeaders, }; // Add body for POST/PUT requests if (data && (method === "POST" || method === "PUT")) { fetchOptions.body = JSON.stringify(data); } // Add timeout const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); }, this.config.timeout || 30000); fetchOptions.signal = controller.signal; try { const response = await this.executeWithRetry(fullUrl.toString(), fetchOptions); clearTimeout(timeoutId); return await this.handleResponse(response); } catch (error) { clearTimeout(timeoutId); throw this.handleError(error); } } async executeWithRetry(url, options) { const maxRetries = this.config.retries || 3; let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const response = await fetch(url, options); // Don't retry on client errors (4xx), only on server errors (5xx) and network errors if (response.ok || (response.status >= 400 && response.status < 500)) { return response; } if (attempt === maxRetries) { return response; } // Wait before retrying (exponential backoff) await this.delay(Math.pow(2, attempt) * 1000); } catch (error) { lastError = error; if (attempt === maxRetries) { throw lastError; } // Wait before retrying await this.delay(Math.pow(2, attempt) * 1000); } } throw lastError; } async handleResponse(response) { const contentType = response.headers.get("content-type"); if (!response.ok) { let errorMessage = `HTTP ${response.status}: ${response.statusText}`; let errorDetails = {}; try { if (contentType?.includes("application/json")) { errorDetails = await response.json(); errorMessage = errorDetails.message || errorDetails.error || errorMessage; } else { errorMessage = (await response.text()) || errorMessage; } } catch { // If we can't parse the error response, use the default message } const apiError = { message: errorMessage, error: errorDetails.error, status: response.status, }; throw apiError; } // Handle different response types if (response.status === 204) { return {}; } if (contentType?.includes("application/json")) { return await response.json(); } // For non-JSON responses, return as text return (await response.text()); } handleError(error) { if (error.name === "AbortError") { return { message: "Request timeout", error: "The request took too long to complete", status: 408, }; } if (error.message && error.status) { // Already an ApiError return error; } if (error instanceof TypeError && error.message.includes("fetch")) { return { message: "Network error", error: "Unable to connect to the server. Please check your internet connection.", status: 0, }; } return { message: error.message || "An unexpected error occurred", error: error.toString(), status: 500, }; } delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } // Method to update API key setApiKey(apiKey) { this.config.apiKey = apiKey; } } exports.MpesaHttpClient = MpesaHttpClient; //# sourceMappingURL=mpesa-http-client.js.map