Security Best Practices
This guide covers security best practices for integrating the ZenPays SDK into your application.
API Key Security
Environment Variables
Always store API keys in environment variables:
// ✅ Good - using environment variables
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// ❌ Bad - hardcoded API key
const zenpays = new ZenPays({
apiKey: 'zp_live_abc123...',
})
Key Rotation
Regularly rotate your API keys to minimize exposure:
// Create a new key
const newKey = await zenpays.merchants.createApiKey({
name: 'Rotated Key - Q4 2024',
environment: 'production',
scopes: ['payments:write', 'refunds:write'],
})
// Update your environment with the new key
// Then revoke the old key
await zenpays.merchants.revokeApiKey('old_key_id')
Minimal Scopes
Only grant the permissions each key needs:
// For a read-only dashboard
const dashboardKey = await zenpays.merchants.createApiKey({
name: 'Dashboard Read-Only',
environment: 'production',
scopes: ['payments:read', 'analytics:read'],
})
// For payment processing
const paymentKey = await zenpays.merchants.createApiKey({
name: 'Payment Processor',
environment: 'production',
scopes: ['payments:write'],
})
Network Security
IP Whitelisting
Restrict API access to known IP addresses:
// Add your server IPs
await zenpays.merchants.addIPToWhitelist('203.0.113.50', 'Production Server')
await zenpays.merchants.addIPToWhitelist('203.0.113.51', 'Backup Server')
TLS/HTTPS
The SDK always uses HTTPS for API communication. Never disable TLS verification in production:
// The SDK enforces HTTPS by default
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
// baseUrl defaults to https://api.zenpays.com
})
Authentication Security
Two-Factor Authentication
Enable 2FA for all team members with API access:
// Setup 2FA
const setup = await zenpays.security.setup2FA()
// Store backup codes securely
console.log('Backup codes:', setup.backupCodes)
// Verify to complete setup
await zenpays.security.verify2FA({ code: '123456' })
Session Management
For user-facing applications, implement proper session handling:
// Use short-lived sessions
const session = await authenticate(user)
// Invalidate sessions on logout
await zenpays.security.revokeSession(sessionId)
Webhook Security
Signature Verification
Always verify webhook signatures:
import { verifyWebhookSignature } from 'zenpays'
app.post('/webhooks/zenpays', (req, res) => {
const signature = req.headers['x-zenpays-signature']
const webhookSecret = process.env.ZENPAYS_WEBHOOK_SECRET!
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
webhookSecret
)
if (!isValid) {
return res.status(401).send('Invalid signature')
}
// Process the webhook
handleWebhook(req.body)
res.status(200).send('OK')
})
Replay Attack Prevention
Check webhook timestamps to prevent replay attacks:
function handleWebhook(payload: WebhookPayload) {
const timestamp = new Date(payload.timestamp)
const now = new Date()
const fiveMinutes = 5 * 60 * 1000
if (now.getTime() - timestamp.getTime() > fiveMinutes) {
throw new Error('Webhook too old - possible replay attack')
}
// Process the webhook...
}
Data Security
PCI Compliance
Never handle raw card data on your servers. Use the checkout flow:
// ✅ Good - using checkout link
const intent = await zenpays.payments.createPaymentIntent({
amount: 5000,
currency: 'USD',
})
// Redirect customer to secure checkout
const checkoutUrl = await zenpays.checkout.createCheckoutLink(intent.id)
Sensitive Data Handling
Never log sensitive information:
// ✅ Good - logging safe fields only
console.log(`Payment ${payment.id} - Amount: ${payment.amount}`)
// ❌ Bad - logging sensitive data
console.log('Payment details:', JSON.stringify(payment))
Error Handling
Don't Expose Internal Errors
Show generic messages to users:
try {
await zenpays.payments.createPaymentIntent(data)
}
catch (error) {
// Log detailed error for debugging
console.error('Payment error:', error)
// Show generic message to user
throw new Error('Payment processing failed. Please try again.')
}
Security Monitoring
Monitor Security Events
Regularly review security events:
const events = await zenpays.security.listSecurityEvents({
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
type: ['login_failed', 'api_key_used', 'suspicious_activity'],
})
events.data.forEach((event) => {
console.log(`${event.type} at ${event.createdAt} from ${event.ipAddress}`)
})
Set Up Alerts
Configure webhooks for security events:
await zenpays.merchants.createWebhook({
url: 'https://your-app.com/security-alerts',
events: [
'security.suspicious_activity',
'security.api_key_revoked',
'security.login_failed',
],
})
Checklist
Before going to production:
- API keys stored in environment variables
- API keys have minimal required scopes
- IP whitelisting enabled
- Webhook signature verification implemented
- 2FA enabled for all team members
- Security event monitoring configured
- No sensitive data in logs
- Error messages don't expose internal details
Next Steps
- Authentication - API key management
- Webhooks - Webhook setup and verification
- Error Handling - Handling errors securely