import { FinixContext, FinixApiResponse, FinixApiError } from '../types.js';
export class FinixClient {
private username: string;
private password: string;
private apiUrl: string;
private docsSearchUrl: string;
constructor(context: FinixContext = {}) {
this.username = context.username || process.env.FINIX_USERNAME || '';
this.password = context.password || process.env.FINIX_PASSWORD || '';
// Determine API URL based on environment
const environment = context.environment || process.env.FINIX_ENVIRONMENT || 'Sandbox';
this.apiUrl = this.getApiUrl(environment);
this.docsSearchUrl = 'https://docs.finix.com/_search';
}
private getApiUrl(environment: string): string {
switch (environment.toLowerCase()) {
case 'live':
case 'production':
return 'https://finix.live-payments-api.com';
case 'sandbox':
case 'development':
default:
return 'https://finix.sandbox-payments-api.com';
}
}
private getAuthHeader(): string {
if (!this.username || !this.password) {
throw new Error('Finix username and password are required for API operations');
}
return `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`;
}
async makeRequest<T = any>(
endpoint: string,
options: RequestInit = {}
): Promise<FinixApiResponse<T>> {
try {
const url = endpoint.startsWith('http') ? endpoint : `${this.apiUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01',
...(this.username && this.password && { 'Authorization': this.getAuthHeader() }),
...options.headers,
},
});
const data = await response.json();
if (!response.ok) {
return {
error: {
message: data.message || `Request failed with status ${response.status}`,
code: data.code || response.status.toString(),
details: data,
},
};
}
return { data };
} catch (error) {
return {
error: {
message: error instanceof Error ? error.message : 'Unknown error occurred',
details: error,
},
};
}
}
async get<T = any>(endpoint: string): Promise<FinixApiResponse<T>> {
return this.makeRequest<T>(endpoint, { method: 'GET' });
}
async post<T = any>(endpoint: string, body: any): Promise<FinixApiResponse<T>> {
return this.makeRequest<T>(endpoint, {
method: 'POST',
body: JSON.stringify(body),
});
}
async put<T = any>(endpoint: string, body: any): Promise<FinixApiResponse<T>> {
return this.makeRequest<T>(endpoint, {
method: 'PUT',
body: JSON.stringify(body),
});
}
async delete<T = any>(endpoint: string): Promise<FinixApiResponse<T>> {
return this.makeRequest<T>(endpoint, { method: 'DELETE' });
}
async searchDocs(query: string, filters: any[] = []): Promise<FinixApiResponse<any>> {
try {
const requestBody = {
query,
filter: filters,
locale: 'default_locale',
};
const response = await fetch(this.docsSearchUrl, {
method: 'POST',
headers: {
'Accept': '*/*',
'Content-Type': 'application/json',
'User-Agent': 'Finix-MCP-Server/2.0',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
return {
error: {
message: `Documentation search failed with status ${response.status}`,
code: response.status.toString(),
},
};
}
const data = await response.json();
return { data };
} catch (error) {
return {
error: {
message: error instanceof Error ? error.message : 'Documentation search failed',
details: error,
},
};
}
}
hasCredentials(): boolean {
return !!(this.username && this.password);
}
}