Best Practices
Guidelines for building robust Pixlpay integrations.
Security
Protect Your API Token
javascript
// Good: Use environment variables
const token = process.env.PIXLPAY_TOKEN;
// Bad: Hardcoded tokens
const token = 'abc123...'; // Never do this!Verify Webhooks
Always verify webhook signatures before processing:
javascript
const computed = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(signature))) {
throw new Error('Invalid signature');
}Use Minimal Scopes
Request only the permissions you need:
// Good: Specific scopes
orders:read, products:read
// Avoid: Full access when not needed
full_accessRotate Tokens
Regularly rotate API tokens, especially if:
- Team members leave
- A token may have been exposed
- As part of regular security practices
Error Handling
Handle All Status Codes
javascript
async function apiCall(endpoint) {
const response = await client.get(endpoint);
switch (response.status) {
case 200:
case 201:
return response.data;
case 401:
await refreshToken();
return apiCall(endpoint);
case 429:
await sleep(response.data.retry_after * 1000);
return apiCall(endpoint);
case 500:
case 502:
case 503:
await sleep(5000);
return apiCall(endpoint);
default:
throw new ApiError(response);
}
}Log Errors
Maintain error logs for debugging:
javascript
function logError(context, error) {
console.error(JSON.stringify({
timestamp: new Date().toISOString(),
context,
status: error.status,
message: error.message,
endpoint: error.endpoint
}));
}Performance
Use Pagination Efficiently
Fetch appropriate page sizes:
javascript
// Good: Reasonable page size
const response = await client.get('/orders?per_page=50');
// Bad: Too many small requests
for (let i = 1; i <= 100; i++) {
await client.get(`/orders?page=${i}&per_page=1`);
}Cache When Appropriate
Cache data that doesn't change frequently:
javascript
// Products change infrequently - cache for 5 minutes
const products = await getCached('products', 5 * 60, () =>
client.get('/products')
);
// Orders change often - shorter cache or no cache
const orders = await client.get('/orders');Use Webhooks Over Polling
javascript
// Bad: Polling every minute
setInterval(() => checkForNewOrders(), 60000);
// Good: React to webhooks instantly
app.post('/webhooks', (req, res) => {
handleNewOrder(req.body.data);
res.sendStatus(200);
});Reliability
Implement Retries
For transient failures, retry with backoff:
javascript
async function fetchWithRetry(endpoint, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await client.get(endpoint);
if (response.status < 500) return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
// Exponential backoff
await sleep(Math.pow(2, attempt) * 1000);
}
}Handle Idempotency
Process webhooks idempotently - the same event may be delivered multiple times:
javascript
const processedEvents = new Set();
function handleWebhook(event) {
// Skip if already processed
if (processedEvents.has(event.id)) {
return;
}
processEvent(event);
processedEvents.add(event.id);
}Timeout Requests
Set reasonable timeouts:
javascript
const client = axios.create({
timeout: 30000, // 30 seconds
baseURL: 'https://yourstore.pixlpay.net/api/external/v1'
});Webhooks
Respond Quickly
Respond with 200 before processing:
javascript
app.post('/webhooks', async (req, res) => {
// Respond immediately
res.sendStatus(200);
// Process asynchronously
setImmediate(() => {
processWebhook(req.body);
});
});Use a Queue
For complex processing, use a job queue:
javascript
app.post('/webhooks', (req, res) => {
// Add to queue
queue.add('process-webhook', req.body);
res.sendStatus(200);
});
// Process in background
queue.process('process-webhook', async (job) => {
await processWebhook(job.data);
});Log Everything
Log webhook deliveries for debugging:
javascript
function handleWebhook(event) {
console.log('Webhook received:', {
id: event.id,
type: event.event,
timestamp: event.timestamp
});
try {
processEvent(event);
console.log('Webhook processed:', event.id);
} catch (error) {
console.error('Webhook failed:', event.id, error);
throw error;
}
}Testing
Use Test Mode
When available, test with sandbox/test mode before production.
Mock API Responses
Test your integration without hitting the real API:
javascript
// In tests
jest.mock('./pixlpay-client', () => ({
get: jest.fn().mockResolvedValue({
status: 200,
data: { data: mockProducts }
})
}));Test Error Scenarios
Ensure your code handles errors gracefully:
javascript
test('handles 429 rate limit', async () => {
mockClient.get.mockResolvedValueOnce({
status: 429,
data: { retry_after: 60 }
});
// Verify backoff is implemented
await expect(fetchWithRetry('/products')).resolves.toBeDefined();
});