Security Checklist
Credentials
✅ Use environment variables✅ Never commit to Git✅ Rotate keys every 90 days✅ Use separate sandbox/production keys
API Communication
✅ HTTPS only (never HTTP)✅ Validate SSL certificates✅ Server-side integration only✅ Verify webhook signatures
Data Handling
✅ Never log sensitive data✅ PCI compliance for card data✅ Encrypt data at rest✅ Minimal data retention
Monitoring
✅ Track failed auth attempts✅ Monitor unusual patterns✅ Set up security alerts✅ Regular security audits
Critical Rules
Credential Storage
Server-Side Only
Logging
✅ Correct: # .env file
EIGHTY_EIGHT_PAY_API_KEY=sk_live_abc123...
EIGHTY_EIGHT_PAY_MERCHANT_ID=MCH-COL-29ZTP4
// Load from environment
const apiKey = process.env.EIGHTY_EIGHT_PAY_API_KEY;
❌ Wrong: // Hardcoded (NEVER DO THIS!)
const apiKey = 'sk_live_abc123...'; // EXPOSED!
✅ Correct: // Backend API route
export default async function handler(req, res) {
const token = await getToken(); // Server-side
const payment = await create88PayPayment(token, req.body);
res.json(payment);
}
❌ Wrong: // Frontend (NEVER!)
const payment = await fetch('https://api.88pay.io/...', {
headers: { 'x-api-key': apiKey } // EXPOSED IN BROWSER!
});
✅ Safe to log: logger.info({
merchant_id: 'MCH-COL-29ZTP4',
reference: 'IP9BD7CJES6',
amount: 50000,
currency: 'COP',
status: 'COMPLETED'
});
❌ Never log: logger.info({
api_key: '***', // ❌ Credentials
access_token: '***', // ❌ Tokens
card_number: '***', // ❌ PCI data
cvv: '***', // ❌ Security codes
password: '***' // ❌ Passwords
});
Webhook Security
Verify Signatures
Always decrypt and verify webhook tokens:
import crypto from 'crypto';
function verifyWebhook(authHeader, payload) {
// 1. Extract token
const token = authHeader.replace('Bearer ', '');
// 2. Decrypt with your secret key
const decrypted = decryptToken(MERCHANT_SECRET, token);
// 3. Verify payload matches
if (decrypted.transaction_id !== payload.transaction_id) {
throw new Error('Invalid webhook signature');
}
if (decrypted.transaction_amount !== payload.transaction_amount) {
throw new Error('Amount mismatch');
}
// 4. Check timestamp (within 5 minutes)
const age = Date.now() - decrypted.ts;
if (age > 300000) {
throw new Error('Webhook expired');
}
return true;
}
Complete webhook security guide →
IP Whitelisting
Enable in production for extra security:
Get Your Server IPs
# Find your server's public IP
curl ifconfig.me
Add to Dashboard
Navigate to Settings → Security → IP WhitelistAdd your server IPs (IPv4 and IPv6)
Test
API will reject requests from non-whitelisted IPs
Update IP whitelist when scaling infrastructure or changing hosting providers.
Key Rotation
Rotate credentials every 90 days:
Generate New Key
Dashboard → Settings → API Credentials → Regenerate
Update Environment
Update .env in all environments (dev, staging, prod)
Deploy
Deploy changes with zero-downtime (both keys work for 24h)
Verify
Monitor for errors, then deactivate old key
Common Vulnerabilities
Exposed Credentials in Git
Risk: API keys committed to repositoryPrevention: # Add to .gitignore FIRST
echo ".env" >> .gitignore
echo ".env.*" >> .gitignore
# Remove from history if already committed
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
# Force push
git push origin --force --all
# Rotate compromised keys immediately
Risk: Credentials exposed in browser DevToolsPrevention: // ✅ Frontend calls your backend
const response = await fetch('/api/create-payment', {
method: 'POST',
body: JSON.stringify(orderData)
});
// ✅ Backend handles 88Pay
export default async function handler(req, res) {
const token = await getToken(); // Server-side only
// ... call 88Pay API
}
Risk: Accepting fake webhook notificationsPrevention: app.post('/webhook', (req, res) => {
// ✅ Always verify signature
const isValid = verifyWebhook(
req.headers.authorization,
req.body
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook...
});
Risk: PCI/PII data in logs accessible to attackersPrevention: // ✅ Sanitize before logging
function sanitize(data) {
const safe = { ...data };
// Remove sensitive fields
delete safe.api_key;
delete safe.access_token;
delete safe.card_number;
delete safe.cvv;
return safe;
}
logger.info(sanitize(requestData));
PCI Compliance
If handling card data:
88Pay handles card data, not you. Always redirect to our checkout page. Never collect card numbers directly.
Safe integration:
// ✅ Redirect to 88Pay checkout
const payment = await create88PayPayment(data);
window.location.href = payment.urlCheckout;
Unsafe integration:
// ❌ Never collect card data directly
<input name="card_number" /> // DON'T!
<input name="cvv" /> // DON'T!
Incident Response
If credentials are compromised:
Immediate Actions
- Rotate API keys immediately
- Review recent transactions
- Check for unauthorized access
- Notify [email protected]
Investigation
- Review logs for suspicious activity
- Identify how credentials were exposed
- Check for unauthorized transactions
- Document timeline
Prevention
- Fix vulnerability
- Update security processes
- Train team on security
- Implement additional monitoring
Security Monitoring
Set up alerts for:
// Failed authentication attempts
if (error.status === 401) {
securityLogger.warn('Failed auth attempt', {
ip: req.ip,
user_agent: req.headers['user-agent'],
timestamp: new Date()
});
// Alert if >5 failures in 5 minutes
if (failureCount > 5) {
await sendSecurityAlert('Multiple auth failures detected');
}
}
// Unusual transaction patterns
if (transaction.amount > UNUSUAL_THRESHOLD) {
await sendSecurityAlert('Large transaction detected', {
amount: transaction.amount,
reference: transaction.reference
});
}
// API requests from unexpected IPs
if (!ALLOWED_IPS.includes(req.ip)) {
securityLogger.error('Request from unknown IP', {
ip: req.ip,
endpoint: req.url
});
}
Go-Live Security Checklist
- ✅ Environment variables configured
- ✅
.env in .gitignore
- ✅ Production keys separate from sandbox
- ✅ HTTPS enforced everywhere
- ✅ Webhook signature verification
- ✅ IP whitelisting enabled (production)
- ✅ No credentials in logs
- ✅ Server-side integration only
- ✅ Security monitoring configured
- ✅ Incident response plan documented
Resources