Skip to main content

Overview

Security is paramount when building financial applications. This guide covers essential security practices for your Monei integration. What you’ll learn:
  • API key management
  • Authentication best practices
  • Data protection
  • Rate limiting
  • Common security threats
  • Incident response

API Key Security

Your API key is the gateway to your Monei account. Protect it at all costs.

Never Expose Keys

Never commit API keys to version control or expose them client-side

Use Environment Variables

Store keys in environment variables, not in code

Rotate Regularly

Rotate API keys periodically (every 90 days recommended)

Separate Environments

Use different keys for development, staging, and production

Storing API Keys

CORRECT - Environment Variables

// .env file (DO NOT COMMIT)
MONEI_API_KEY=your_api_key_here
MONEI_ENVIRONMENT=production

// Load from environment
require('dotenv').config();

const monei = new MoneiSDK({
  apiKey: process.env.MONEI_API_KEY,
});

WRONG - Hardcoded in Code

// ❌ NEVER hardcode API keys
const monei = new MoneiSDK({
  apiKey: 'sk_live_abc123xyz456...',  // NEVER!
});

Environment Separation

Use different API keys for different environments:
EnvironmentKey TypePurpose
Developmentsk_test_...Local development and testing
Stagingsk_test_...Pre-production testing
Productionsk_live_...Live production environment
// config.js
const config = {
  development: {
    apiKey: process.env.MONEI_DEV_API_KEY,
    baseUrl: 'https://api.dev.monei.cc'
  },
  production: {
    apiKey: process.env.MONEI_PROD_API_KEY,
    baseUrl: 'https://api.monei.cc'
  }
};

const env = process.env.NODE_ENV || 'development';
const monei = new MoneiSDK(config[env]);

Request Authentication

All API requests must include your API key in the x-api-key header:
// Using SDK (handles automatically)
const monei = new MoneiSDK({
  apiKey: process.env.MONEI_API_KEY,
});

// Manual request
const response = await fetch('https://api.monei.cc/api/v1/wallet/me', {
  headers: {
    'x-api-key': process.env.MONEI_API_KEY,
    'Content-Type': 'application/json'
  }
});

Data Protection

Sensitive Data Handling

What to encrypt:
  • User personal information (PII)
  • Transaction details
  • Bank account numbers
  • Phone numbers
Best practices:
  • Use AES-256 encryption at rest
  • Use TLS 1.3 for data in transit
  • Encrypt database backups
  • Never log sensitive data
Database security:
  • Enable encryption at rest
  • Use strong passwords
  • Limit database access
  • Regular security audits
Example:
// ❌ NEVER store plaintext sensitive data
const user = {
  name: 'John Doe',
  accountNumber: '0123456789'  // ❌ Don't do this
};

// ✅ Hash or encrypt sensitive fields
const user = {
  name: 'John Doe',
  accountNumberHash: encrypt('0123456789', encryptionKey)  // ✅ Better
};
Collect only what you need:
  • Don’t store card CVVs
  • Don’t store full card numbers
  • Minimize PII collection
  • Delete data when no longer needed
Data retention:
  • Transaction logs: 7 years (compliance)
  • Temporary data: Delete after use
  • Inactive accounts: Archive after 1 year
Principle of least privilege:
  • Limit API key permissions
  • Use role-based access control (RBAC)
  • Audit access logs regularly
  • Implement multi-factor authentication (MFA)

Rate Limiting

Monei implements rate limiting to prevent abuse:
Endpoint TypeRate LimitWindow
Read Operations1000 requests1 minute
Write Operations100 requests1 minute
Authentication10 requests1 minute
Webhooks500 requests1 minute

Handle Rate Limits

async function makeRequestWithRetry(apiCall, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await apiCall();
    } catch (error) {
      // Check for rate limit error
      if (error.statusCode === 429) {
        const retryAfter = error.headers['retry-after'] || 60;
        console.log(`Rate limited. Retrying after ${retryAfter}s...`);
        
        // Wait before retry
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }
      
      throw error;
    }
  }
  
  throw new Error('Max retries exceeded');
}

// Usage
const wallet = await makeRequestWithRetry(() => monei.wallet.me());

Common Security Threats

Threat: API key leaked in public repository or client-side codePrevention:
  • Never commit .env files
  • Add .env to .gitignore
  • Use environment variables
  • Never expose keys in frontend
  • Scan repositories for leaked keys
If compromised:
  1. Immediately rotate API key
  2. Revoke old key
  3. Audit all transactions
  4. Check for unauthorized access
  5. Update all environments

Input Validation

Always validate and sanitize user inputs:
// Validation helpers
function validatePhoneNumber(phone) {
  // Nigerian phone: 11 digits starting with 0
  const cleaned = phone.replace(/\s+/g, '');
  if (!/^0[789][01]\d{8}$/.test(cleaned)) {
    throw new Error('Invalid phone number format');
  }
  return cleaned;
}

function validateAmount(amount, min = 0, max = Infinity) {
  const num = parseFloat(amount);
  
  if (isNaN(num)) {
    throw new Error('Amount must be a number');
  }
  
  if (num < min) {
    throw new Error(`Amount must be at least ${min}`);
  }
  
  if (num > max) {
    throw new Error(`Amount cannot exceed ${max}`);
  }
  
  return num;
}

function sanitizeNarration(narration) {
  // Remove potentially dangerous characters
  return narration
    .replace(/[<>]/g, '')  // Remove HTML tags
    .replace(/['"]/g, '')  // Remove quotes
    .trim()
    .substring(0, 200);    // Limit length
}

// Usage
try {
  const phone = validatePhoneNumber(userInput.phone);
  const amount = validateAmount(userInput.amount, 50, 50000);
  const narration = sanitizeNarration(userInput.narration);
  
  // Safe to use
  const payment = await monei.bills.pay({
    billerId: 'mtn-ng',
    customerId: phone,
    amount: amount,
    narration: narration
  });
} catch (error) {
  console.error('Validation error:', error.message);
}

Logging Best Practices

DO log:
  • API requests (without sensitive data)
  • Response status codes
  • Error messages
  • Transaction references
  • User actions
  • System events
Example:
logger.info('Payment initiated', {
  reference: payment.reference,
  amount: payment.amount,
  currency: payment.currency,
  timestamp: new Date().toISOString()
});
NEVER log:
  • API keys
  • Passwords
  • Card numbers
  • CVVs
  • PINs
  • Full bank account numbers
  • Personal identification numbers
Example:
// ❌ NEVER log sensitive data
logger.info('Payment', {
  cardNumber: '4242424242424242',  // NEVER!
  cvv: '123'                       // NEVER!
});

// ✅ Log safely
logger.info('Payment', {
  cardLast4: '4242',  // ✅ OK
  reference: 'TXN-123'
});
Best practices:
  • Encrypt logs at rest
  • Restrict log access
  • Rotate logs regularly
  • Centralized logging
  • Log retention policies
Retention:
  • Application logs: 30 days
  • Transaction logs: 7 years
  • Error logs: 90 days
  • Audit logs: 1 year

Incident Response

What to do if security is compromised:
1

Identify the Breach

  • Detect unauthorized access
  • Check logs for anomalies
  • Identify affected systems
2

Contain the Damage

  • Rotate API keys immediately
  • Revoke compromised credentials
  • Block suspicious IPs
  • Isolate affected systems
3

Assess the Impact

  • Review transaction logs
  • Check for unauthorized transactions
  • Identify affected users
  • Document all findings
4

Notify Stakeholders

  • Contact Monei support
  • Notify affected users
  • Report to authorities (if required)
  • Update security team
5

Remediate

  • Fix security vulnerabilities
  • Update security policies
  • Implement additional controls
  • Train team on prevention
6

Monitor

  • Enhanced monitoring
  • Regular security audits
  • Penetration testing
  • Continuous improvement

Security Checklist

API Keys

✅ Stored in environment variables
✅ Never committed to git
✅ Rotated every 90 days
✅ Separate keys per environment

Data Protection

✅ Encryption at rest
✅ TLS 1.3 in transit
✅ No sensitive data in logs
✅ Regular backups

Access Control

✅ Role-based access
✅ Least privilege principle
✅ MFA enabled
✅ Regular access audits

Monitoring

✅ Real-time alerts
✅ Transaction monitoring
✅ Error tracking
✅ Audit logs

Next Steps

Webhooks

Secure webhook implementation

Best Practices

Additional security best practices

Testing

Test in sandbox environment

Error Handling

Handle errors securely