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:
- JavaScript
- Python
// ✅ 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...',
})
import os
# ✅ Good - using environment variables
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# ❌ Bad - hardcoded API key
zenpays = ZenPays(api_key="zp_live_abc123...")
Network Security
IP Whitelisting
Restrict API access to known IP addresses:
- JavaScript
- Python
// Add your server IPs
await zenpays.merchants.addIPToWhitelist('203.0.113.50', 'Production Server')
await zenpays.merchants.addIPToWhitelist('203.0.113.51', 'Backup Server')
# Add your server IPs
zenpays.merchants.add_ip_to_whitelist("203.0.113.50", "Production Server")
zenpays.merchants.add_ip_to_whitelist("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.zenpayz.com
})
Authentication Security
Two-Factor Authentication
Enable 2FA for all team members with API access:
- JavaScript
- Python
// 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' })
# Setup 2FA
setup = zenpays.security.setup_two_factor()
# Store backup codes securely
print(f"Backup codes: {setup['backup_codes']}")
# Verify to complete setup
zenpays.security.verify_two_factor({"code": "123456"})
Session Management
For user-facing applications, implement proper session handling:
- JavaScript
- Python
// Use short-lived sessions
const session = await authenticate(user)
// Invalidate sessions on logout
await zenpays.security.revokeSession(sessionId)
# Use short-lived sessions
session = authenticate(user)
# Invalidate sessions on logout
zenpays.security.revoke_session(session_id)
Webhook Security
Signature Verification
Always verify webhook signatures:
- JavaScript
- Python
import { verifyWebhookSignature } from '@zenxdigitalholdings/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')
})
import os
from zenpays import verify_webhook_signature
from flask import Flask, request
@app.route('/webhooks/zenpays', methods=['POST'])
def webhook_handler():
signature = request.headers.get('x-zenpays-signature')
webhook_secret = os.environ["ZENPAYS_WEBHOOK_SECRET"]
is_valid = verify_webhook_signature(
request.get_data().decode('utf-8'),
signature,
webhook_secret
)
if not is_valid:
return 'Invalid signature', 401
# Process the webhook
handle_webhook(request.get_json())
return 'OK', 200
Replay Attack Prevention
Check webhook timestamps to prevent replay attacks:
- JavaScript
- Python
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...
}
from datetime import datetime, timedelta
def handle_webhook(payload: dict):
timestamp = datetime.fromisoformat(payload["timestamp"])
now = datetime.utcnow()
five_minutes = timedelta(minutes=5)
if now - timestamp > five_minutes:
raise ValueError("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:
- JavaScript
- Python
// ✅ 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)
# ✅ Good - using checkout link
intent = zenpays.payments.create_payment_intent({
"amount": 5000,
"currency": "USD",
})
# Redirect customer to secure checkout
checkout_url = zenpays.checkout.create_checkout_link(intent["id"])
Sensitive Data Handling
Never log sensitive information:
- JavaScript
- Python
// ✅ Good - logging safe fields only
console.log(`Payment ${payment.id} - Amount: ${payment.amount}`)
// ❌ Bad - logging sensitive data
console.log('Payment details:', JSON.stringify(payment))
# ✅ Good - logging safe fields only
print(f"Payment {payment['id']} - Amount: {payment['amount']}")
# ❌ Bad - logging sensitive data
import json
print(f"Payment details: {json.dumps(payment)}")
Error Handling
Don't Expose Internal Errors
Show generic messages to users:
- JavaScript
- Python
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.')
}
try:
zenpays.payments.create_payment_intent(data)
except Exception as error:
# Log detailed error for debugging
print(f"Payment error: {error}")
# Show generic message to user
raise Exception("Payment processing failed. Please try again.")
Security Monitoring
Monitor Security Events
Regularly review security events:
- JavaScript
- Python
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}`)
})
from datetime import datetime, timedelta
events = zenpays.security.list_security_events({
"from_": datetime.now() - timedelta(days=7), # Last 7 days
"type": ["login_failed", "api_key_used", "suspicious_activity"],
})
for event in events["data"]:
print(f"{event['type']} at {event['created_at']} from {event['ip_address']}")
Set Up Alerts
Configure webhooks for security events:
- JavaScript
- Python
await zenpays.merchants.createWebhook({
url: 'https://your-app.com/security-alerts',
events: [
'security.suspicious_activity',
'security.api_key_revoked',
'security.login_failed',
],
})
zenpays.merchants.create_webhook({
"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