@moneygraph/sdk
Version:
AI-native SDK for global payouts powered by StratosPay
165 lines (164 loc) • 6.14 kB
JavaScript
;
/**
* MoneyGraph SDK - API Client
*
* Dual-mode HTTP client that automatically detects sandbox vs live mode
* based on API key prefix (sk_test_* = sandbox, sk_live_* = live).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiClient = exports.MoneyGraphError = void 0;
class MoneyGraphError extends Error {
constructor(message, code, statusCode, details) {
super(message);
this.name = 'MoneyGraphError';
this.code = code;
this.statusCode = statusCode;
this.details = details;
}
}
exports.MoneyGraphError = MoneyGraphError;
class ApiClient {
constructor(options) {
this.apiKey = options.apiKey;
this.baseUrl = options.baseUrl || 'https://stratospay.com/api/v1';
this.timeout = options.timeout || 30000;
// Detect mode from API key prefix
this.mode = this.detectMode(options.apiKey);
}
detectMode(apiKey) {
if (apiKey.startsWith('sk_test_') || apiKey.startsWith('pk_test_')) {
return 'sandbox';
}
if (apiKey.startsWith('sk_live_') || apiKey.startsWith('pk_live_')) {
return 'live';
}
// Default to sandbox for safety
return 'sandbox';
}
get isSandbox() {
return this.mode === 'sandbox';
}
get isLive() {
return this.mode === 'live';
}
mapStatusCodeToError(status, message) {
switch (status) {
case 400:
if (message.toLowerCase().includes('invalid api key')) {
return 'INVALID_API_KEY';
}
return 'VALIDATION_ERROR';
case 401:
return 'UNAUTHORIZED';
case 403:
if (message.toLowerCase().includes('kyc already approved')) {
return 'KYC_ALREADY_APPROVED';
}
return 'FORBIDDEN';
case 404:
if (message.toLowerCase().includes('user not found')) {
return 'CUSTOMER_NOT_FOUND';
}
if (message.toLowerCase().includes('director')) {
return 'DIRECTOR_NOT_FOUND';
}
return 'NOT_FOUND';
case 429:
return 'RATE_LIMITED';
default:
return 'INTERNAL_ERROR';
}
}
async request(method, endpoint, options = {}) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try {
let url = `${this.baseUrl}${endpoint}`;
// Add query params
if (options.params) {
const queryParams = new URLSearchParams();
for (const [key, value] of Object.entries(options.params)) {
queryParams.append(key, String(value));
}
url += `?${queryParams.toString()}`;
}
const headers = {
'Accept': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
...options.headers,
};
if (options.body) {
headers['Content-Type'] = 'application/json';
}
const response = await fetch(url, {
method,
headers,
body: options.body ? JSON.stringify(options.body) : undefined,
signal: controller.signal,
});
const data = await response.json();
if (!response.ok || data.status === 'failed') {
const errorCode = this.mapStatusCodeToError(response.status, data.message);
throw new MoneyGraphError(data.message || `Request failed with status ${response.status}`, errorCode, response.status, data.data);
}
return data.data;
}
catch (error) {
if (error instanceof MoneyGraphError) {
throw error;
}
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw new MoneyGraphError('Request timed out', 'NETWORK_ERROR', undefined, { timeout: this.timeout });
}
throw new MoneyGraphError(error.message, 'NETWORK_ERROR', undefined, { originalError: error.name });
}
throw new MoneyGraphError('Unknown error occurred', 'INTERNAL_ERROR');
}
finally {
clearTimeout(timeoutId);
}
}
async get(endpoint, params) {
return this.request('GET', endpoint, { params });
}
async post(endpoint, body) {
return this.request('POST', endpoint, { body });
}
async put(endpoint, body) {
return this.request('PUT', endpoint, { body });
}
async delete(endpoint) {
return this.request('DELETE', endpoint);
}
/**
* Upload a file (for KYC documents)
*/
async uploadFile(endpoint, file, filename) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout * 2);
try {
const formData = new FormData();
const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
formData.append('file', blob, filename);
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Accept': 'application/json',
},
body: formData,
signal: controller.signal,
});
const data = await response.json();
if (!response.ok || data.status === 'failed') {
const errorCode = this.mapStatusCodeToError(response.status, data.message);
throw new MoneyGraphError(data.message, errorCode, response.status);
}
}
finally {
clearTimeout(timeoutId);
}
}
}
exports.ApiClient = ApiClient;