Skip to main content

Security Checklist

1

Credentials

✅ Use environment variables✅ Never commit to Git✅ Rotate keys every 90 days✅ Use separate sandbox/production keys
2

API Communication

✅ HTTPS only (never HTTP)✅ Validate SSL certificates✅ Server-side integration only✅ Verify webhook signatures
3

Data Handling

✅ Never log sensitive data✅ PCI compliance for card data✅ Encrypt data at rest✅ Minimal data retention
4

Monitoring

✅ Track failed auth attempts✅ Monitor unusual patterns✅ Set up security alerts✅ Regular security audits

Critical Rules

✅ 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;
    # .gitignore
    .env
    .env.*
❌ Wrong:
    // Hardcoded (NEVER DO THIS!)
    const apiKey = 'sk_live_abc123...'; // EXPOSED!

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:
1

Get Your Server IPs

    # Find your server's public IP
    curl ifconfig.me
2

Add to Dashboard

Navigate to Settings → Security → IP WhitelistAdd your server IPs (IPv4 and IPv6)
3

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:
1

Generate New Key

Dashboard → Settings → API Credentials → Regenerate
2

Update Environment

Update .env in all environments (dev, staging, prod)
3

Deploy

Deploy changes with zero-downtime (both keys work for 24h)
4

Verify

Monitor for errors, then deactivate old key

Common Vulnerabilities

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:
1

Immediate Actions

  1. Rotate API keys immediately
  2. Review recent transactions
  3. Check for unauthorized access
  4. Notify [email protected]
2

Investigation

  1. Review logs for suspicious activity
  2. Identify how credentials were exposed
  3. Check for unauthorized transactions
  4. Document timeline
3

Prevention

  1. Fix vulnerability
  2. Update security processes
  3. Train team on security
  4. 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