API Integration Best Practices
This guide provides best practices for integrating with the Cryptofuse API. Following these guidelines will help you create a secure, reliable, and efficient integration.
Security Best Practices
API Key Management
API keys grant access to your Cryptofuse account and should be treated with the same level of security as passwords:
-
Store securely: Never hardcode API keys in your application or include them in your version control system.
// ❌ Bad - hardcoded API key
const apiKey = "cryp_1a2b3c4d5e6f7g8h9i0j";
// ✅ Good - load from environment variables
const apiKey = process.env.CRYPTOFUSE_API_KEY; -
Use environment variables: Store API keys as environment variables or in a secure key management system.
-
Rotate regularly: Create a schedule for rotating API keys, especially for production environments.
-
Use the principle of least privilege: Create API keys with only the permissions they need.
-
Have separate keys for different environments: Use different API keys for development, staging, and production.
Secure Communication
-
Always use HTTPS: All API requests must use HTTPS to encrypt data in transit.
-
Validate SSL certificates: Ensure your HTTP client validates SSL certificates and rejects invalid ones.
-
Implement certificate pinning: For mobile applications, consider implementing certificate pinning for additional security.
-
Filter sensitive data in logs: Ensure API keys, tokens, and other sensitive data are not written to logs.
User Authentication with OAuth 2.0
When implementing user authentication with OAuth 2.0:
-
Store tokens securely: Use secure HTTP-only cookies for web applications when storing access tokens.
-
Implement token refresh: Request new OAuth 2.0 access tokens before they expire (typically after 1 hour) to maintain a seamless user experience.
-
Implement proper logout: Always call the logout endpoint when a user signs out to invalidate the token on the server side.
-
Handle expired tokens: Detect expired tokens and request a new access token or redirect to the login page when necessary.
Error Handling
Implement robust error handling to provide a good user experience and simplify debugging:
-
Check HTTP status codes: Always check the HTTP status code to determine if a request was successful.
-
Parse error responses: Error responses include detailed information about what went wrong.
try {
const response = await fetch('https://api.cryptofuse.io/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify(paymentData)
});
if (!response.ok) {
const error = await response.json();
console.error(`Error creating payment: ${error.error.message}`);
// Handle specific error cases
if (error.error.code === 'invalid_address') {
// Show address validation error to user
}
return;
}
const data = await response.json();
// Process successful response
} catch (err) {
console.error('Network or parsing error:', err);
} -
Implement retry logic: For transient errors (e.g., rate limiting, temporary service issues), implement exponential backoff with jitter.
async function fetchWithRetry(url, options, maxRetries = 3) {
let retries = 0;
while (retries < maxRetries) {
try {
const response = await fetch(url, options);
if (response.status !== 429) return response; // Only retry rate limiting errors
// If rate limited, wait and retry
const retryAfter = response.headers.get('Retry-After') || 1;
const delay = parseInt(retryAfter, 10) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
retries++;
} catch (err) {
if (retries === maxRetries - 1) throw err;
retries++;
// Add exponential backoff with jitter
const delay = Math.min(1000 * 2 ** retries + Math.random() * 1000, 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
} -
Log detailed error information: Include request IDs, timestamps, and request details in your error logs.
-
Present user-friendly error messages: Translate API error codes into user-friendly messages.
Rate Limiting
The Cryptofuse API implements rate limiting to ensure fair usage for all users:
-
Monitor rate limit headers: Check the
X-RateLimit-*headers to monitor your usage.function checkRateLimits(response) {
const remaining = response.headers.get('X-RateLimit-Remaining');
const limit = response.headers.get('X-RateLimit-Limit');
const reset = response.headers.get('X-RateLimit-Reset');
if (remaining && parseInt(remaining, 10) < 10) {
console.warn(`Rate limit warning: ${remaining}/${limit} requests remaining. Resets at ${new Date(reset * 1000).toISOString()}`);
}
} -
Implement a queueing system: For high-volume applications, implement a request queue to stay within rate limits.
-
Distribute requests evenly: Avoid sending bursts of requests; distribute them evenly over time.
-
Handle 429 responses: When you receive a 429 response, respect the
Retry-Afterheader.
Webhook Integration
When implementing webhooks for payment and withdrawal notifications:
-
Verify webhook signatures: Always verify the signature of incoming webhook requests.
function verifyWebhookSignature(payload, signature, secret) {
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(digest, 'hex'),
Buffer.from(signature, 'hex')
);
} -
Respond quickly: Your webhook endpoint should respond with a 200 status code as quickly as possible.
-
Process webhooks asynchronously: Handle time-consuming processing outside the request-response cycle.
-
Implement idempotency: Process webhooks idempotently to handle potential duplicate deliveries.
-
Log all webhook requests: Maintain detailed logs of all webhook requests for debugging.
Performance Optimization
Optimize your integration for performance and reliability:
-
Use connection pooling: Reuse HTTP connections to reduce latency.
-
Implement caching: Cache responses where appropriate to reduce API calls.
const cache = new Map();
async function fetchWithCache(url, options, ttlSeconds = 60) {
const cacheKey = `${url}-${JSON.stringify(options.body || {})}`;
const cached = cache.get(cacheKey);
if (cached && cached.expiry > Date.now()) {
return cached.data;
}
const response = await fetch(url, options);
const data = await response.json();
cache.set(cacheKey, {
data,
expiry: Date.now() + (ttlSeconds * 1000)
});
return data;
} -
Paginate efficiently: For endpoints that return large datasets, use pagination parameters efficiently.
-
Batch operations: Where possible, use batch operations instead of making multiple single-item requests.
-
Use compression: Enable GZIP compression in your HTTP client to reduce payload size.
Working with Cryptocurrencies
The Cryptofuse system supports multiple cryptocurrencies and tokens, but availability may vary by tenant. Follow these best practices when working with cryptocurrencies:
Check Available Cryptocurrencies
-
Always check available cryptocurrencies first: Use the
/cryptocurrency/availableendpoint to determine which cryptocurrencies and tokens are available for your tenant before creating payments or withdrawals.async function getAvailableCryptocurrencies() {
try {
const response = await fetch('https://api.cryptofuse.io/cryptocurrency/available', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const availableCryptos = await response.json();
return availableCryptos;
} catch (error) {
console.error('Error fetching available cryptocurrencies:', error);
throw error;
}
} -
Validate cryptocurrency availability: Before creating a payment or withdrawal, check if the cryptocurrency is available for your tenant.
function isCryptocurrencyAvailable(cryptoCode, availableCryptos) {
return availableCryptos.some(crypto => crypto.code === cryptoCode.toUpperCase());
}
// Example usage
const createPayment = async (paymentData) => {
const availableCryptos = await getAvailableCryptocurrencies();
if (!isCryptocurrencyAvailable(paymentData.blockchain, availableCryptos)) {
const availableCodes = availableCryptos.map(crypto => crypto.code).join(', ');
throw new Error(`Cryptocurrency ${paymentData.blockchain} is not available. Available options: ${availableCodes}`);
}
// Proceed with payment creation
// ...
}; -
Validate transaction parameters: Use the
/cryptocurrency/validateendpoint to validate cryptocurrency, token, and amount parameters before creating a transaction.async function validateTransaction(cryptoCode, amountUsd, tokenCode = null) {
try {
const url = new URL('https://api.cryptofuse.io/cryptocurrency/validate');
url.searchParams.append('crypto_code', cryptoCode);
url.searchParams.append('amount_usd', amountUsd);
if (tokenCode) {
url.searchParams.append('token_code', tokenCode);
}
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const validationResult = await response.json();
return validationResult.valid;
} catch (error) {
console.error('Error validating transaction parameters:', error);
throw error;
}
} -
Handle dynamic changes: Remember that available cryptocurrencies can change based on tenant configuration. Always check availability at runtime rather than hardcoding cryptocurrency options.
-
Be aware of cryptocurrency-specific requirements: Different blockchains have different address formats, transaction fees, and processing times. Design your application to handle these differences.
Exchange Rate Considerations
-
Check for mock data: When using the exchange rate endpoint, check the
is_mock_datafield in the response to determine if the rates are from a live provider or simulated.async function getExchangeRate(fromCurrency, toCurrency, amount) {
try {
const url = new URL('https://api.cryptofuse.io/rate');
url.searchParams.append('fromCurrency', fromCurrency);
url.searchParams.append('toCurrency', toCurrency);
url.searchParams.append('amount', amount);
url.searchParams.append('full', 'true');
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const rateData = await response.json();
if (rateData.is_mock_data) {
console.warn('Exchange rate data is mocked and may not reflect actual market rates');
}
return rateData;
} catch (error) {
console.error('Error fetching exchange rate:', error);
throw error;
}
} -
Consider rate volatility: Cryptocurrency exchange rates can be volatile. Consider implementing a time buffer for payment processing to account for rate fluctuations.
-
Use rate caching carefully: When caching exchange rates, use short TTLs to ensure rates remain relatively accurate.
-
Display mock data warnings: If using mock data in a user interface, clearly indicate to users that rates are simulated and not real-time market rates.
Testing
Implement a comprehensive testing strategy for your integration:
-
Use the sandbox environment: Test all functionality in the sandbox environment before moving to production.
-
Create automated tests: Implement automated tests for critical API interactions.
-
Test error scenarios: Test how your application handles various error responses.
-
Test rate limiting: Ensure your application handles rate limiting correctly.
-
Perform load testing: Validate that your integration can handle expected traffic volumes.
Monitoring and Logging
Implement robust monitoring and logging for your integration:
-
Monitor API response times: Track and alert on abnormal API response times.
-
Log all API requests: Maintain detailed logs of all API requests for debugging.
-
Set up alerts: Configure alerts for unusual error rates or response patterns.
-
Track business metrics: Monitor key business metrics related to payments and withdrawals.
-
Implement distributed tracing: For complex integrations, implement distributed tracing to track requests across services.
Going to Production
When moving your integration to production:
-
Gradual rollout: Implement a gradual rollout strategy to detect issues early.
-
Maintain feature parity: Ensure your production environment matches your testing environment.
-
Document your integration: Create detailed documentation for your integration.
-
Create a runbook: Develop a runbook for common issues and how to resolve them.
-
Have a rollback plan: Prepare a plan for rolling back changes if issues occur.
Support and Troubleshooting
When you need help with your integration:
-
Check the documentation: Review the API documentation for guidance.
-
Include relevant information: When contacting support, include request IDs, timestamps, and detailed error information.
-
Provide reproduction steps: Describe the exact steps to reproduce the issue.
-
Share code samples: Provide code samples that demonstrate the issue.
-
Contact support: If you can't resolve the issue, contact our support team at support@cryptofuse.io.
By following these best practices, you'll create a secure, reliable, and efficient integration with the Cryptofuse API. If you have any questions or need further assistance, our support team is ready to help.