Skip to main content

Overview

This guide walks you through transitioning from sandbox to production, ensuring your QR payment integration is secure, reliable, and ready for real transactions.
Production involves real money transactions. Complete all testing in sandbox before going live.

Prerequisites

Before moving to production, ensure you have:
1

Complete Integration

  • Successfully created QR codes in sandbox
  • Tested payment flow end-to-end
  • Implemented webhook handling
  • Added proper error handling
  • Tested all edge cases and error scenarios
2

Security Review

  • Credentials stored securely (environment variables, secrets manager)
  • HTTPS enabled on all endpoints
  • Webhook endpoint secured
  • Input validation implemented
  • Logging and monitoring configured
3

Business Requirements

  • Business verification completed with Modulus Labs
  • Legal agreements signed
  • Bank account for settlements configured
  • Customer support process established

Sandbox vs Production

AspectSandboxProduction
Base URLhttps://qrph.sbx.moduluslabs.iohttps://qrph.moduluslabs.io
Secret KeyStarts with sk_test_Starts with sk_live_
Encryption KeyTest encryption keyProduction encryption key
Activation CodeTest activation codeProduction activation code
TransactionsSimulatedReal money
WebhooksManual simulationAutomatic from banks
Rate Limits100 requests/minute1000 requests/minute
QR Validity15 minutes15 minutes
SupportDeveloper support24/7 production support

Getting Production Credentials

1

Contact Modulus Labs

Email [email protected] with:
  • Company name and registration details
  • Business verification documents
  • Expected transaction volume
  • Technical contact information
2

Complete Verification

Modulus Labs will:
  • Verify your business identity
  • Review your integration
  • Conduct security assessment
  • Process legal agreements
3

Receive Credentials

Upon approval, you’ll receive:
  • Production Secret Key (sk_live_...)
  • Production Encryption Key
  • Production Activation Code (XXXX-XXXX-XXXX-XXXX)
  • Production Base URL
  • Webhook IP Whitelist
Production credential approval typically takes 3-5 business days after document submission.

Environment Configuration

1. Separate Environment Variables

Never mix sandbox and production credentials. Use separate environment files:
# Production Modulus Labs Credentials
MODULUS_BASE_URL=https://qrph.moduluslabs.io
MODULUS_SECRET_KEY=sk_live_YOUR_PRODUCTION_SECRET_KEY
MODULUS_ENCRYPTION_KEY=YOUR_PRODUCTION_ENCRYPTION_KEY
MODULUS_ACTIVATION_CODE=XXXX-XXXX-XXXX-XXXX

# Environment
NODE_ENV=production

2. Load Based on Environment

config.js
require('dotenv').config({
  path: process.env.NODE_ENV === 'production'
    ? '.env.production'
    : '.env.sandbox'
});

module.exports = {
  modulus: {
    baseUrl: process.env.MODULUS_BASE_URL,
    secretKey: process.env.MODULUS_SECRET_KEY,
    encryptionKey: process.env.MODULUS_ENCRYPTION_KEY,
    activationCode: process.env.MODULUS_ACTIVATION_CODE
  },
  environment: process.env.NODE_ENV || 'development'
};
For production, use a secrets manager instead of .env files:
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager({
  region: 'ap-southeast-1'
});

async function getSecrets() {
  const data = await secretsManager.getSecretValue({
    SecretId: 'prod/modulus-labs'
  }).promise();

  const secrets = JSON.parse(data.SecretString);

  return {
    secretKey: secrets.MODULUS_SECRET_KEY,
    encryptionKey: secrets.MODULUS_ENCRYPTION_KEY,
    activationCode: secrets.MODULUS_ACTIVATION_CODE
  };
}

Production Checklist

Before deploying to production, verify each item:

Security

  • Production credentials stored in secrets manager (not in code)
  • .env files added to .gitignore
  • No credentials in version control history
  • Secret rotation policy established
  • Access to production credentials restricted to authorized personnel only
  • All API calls use HTTPS (never HTTP)
  • Valid SSL/TLS certificate installed
  • TLS 1.2 or higher enforced
  • Certificate expiration monitoring configured
  • Webhook endpoint uses HTTPS
  • Webhook payload decryption implemented
  • Firewall configured to accept only Modulus Labs IPs
  • Request origin validation implemented
  • Rate limiting configured
  • Amount validation (1.00 - 99,999.99)
  • Currency code validation (PHP only)
  • Merchant reference number validation (1-36 chars)
  • Activation code format validation
  • SQL injection prevention
  • XSS prevention for user-facing displays

Reliability

  • All API calls wrapped in try-catch
  • Proper error logging implemented
  • User-friendly error messages
  • Retry logic for transient failures
  • Circuit breaker pattern for API failures
  • Graceful degradation when API unavailable
  • API response time monitoring
  • Error rate tracking
  • Webhook delivery monitoring
  • Transaction volume monitoring
  • Alerting configured for critical failures
  • Health check endpoints implemented
  • All API requests logged (excluding sensitive data)
  • All webhook receipts logged
  • Transaction IDs tracked
  • Log retention policy configured
  • Log aggregation service configured

Performance

  • Load testing completed
  • Auto-scaling configured
  • Database connection pooling implemented
  • Caching strategy for QR codes if needed
  • CDN configured for static assets
  • Client-side rate limiting implemented
  • Exponential backoff for retries
  • Request queuing for high traffic
  • 429 (Too Many Requests) handling

Compliance

  • PII handling compliant with regulations
  • Data retention policy established
  • Audit trail for all transactions
  • Customer data encrypted at rest
  • Privacy policy updated
  • Settlement account configured
  • Reconciliation process established
  • Refund process documented
  • Customer support trained
  • Dispute resolution process defined

Deployment Process

1. Pre-deployment Testing

# Run full test suite
npm test

# Run integration tests against sandbox
npm run test:integration

# Run security audit
npm audit

# Check for outdated dependencies
npm outdated

2. Deploy to Staging

Deploy to a staging environment that mirrors production:
# Build application
npm run build

# Deploy to staging
npm run deploy:staging

# Run smoke tests
npm run test:smoke:staging

# Verify staging with production credentials (in controlled test)

3. Deploy to Production

1

Schedule Deployment

Choose low-traffic period for deployment (e.g., 2 AM local time)
2

Notify Stakeholders

Alert customer support, operations team, and key stakeholders
3

Create Backup

# Backup database
npm run db:backup

# Tag current production version
git tag -a v1.0.0-prod -m "Pre-deployment backup"
4

Deploy

# Deploy application
npm run deploy:production

# Run post-deployment checks
npm run test:smoke:production
5

Verify

  • Test health check endpoint
  • Create a small test transaction (₱1.00)
  • Verify webhook delivery
  • Check monitoring dashboards
  • Review logs for errors
6

Monitor

Actively monitor for 1-2 hours after deployment:
  • Error rates
  • Response times
  • Transaction success rates
  • Webhook delivery rates

4. Rollback Plan

Prepare a rollback plan in case issues arise:
# Quick rollback to previous version
npm run deploy:rollback

# Or restore from backup
git checkout v1.0.0-prod
npm run deploy:production

Production URL Configuration

Update all API calls to use production URL:
// Before (Sandbox)
const BASE_URL = 'https://qrph.sbx.moduluslabs.io';

// After (Production)
const BASE_URL = 'https://qrph.moduluslabs.io';

// Better - Use environment variable
const BASE_URL = process.env.MODULUS_BASE_URL;

Verify No Sandbox URLs Remain

# Search for hardcoded sandbox URLs
grep -r "qrphsandbox" .

# Should return no results in production code

Production Best Practices

Idempotency

Use unique merchantReferenceNumber for each transaction to prevent duplicates

Retry Logic

Implement exponential backoff with max retries (3-5 attempts)

Timeouts

Set appropriate timeouts (10-30 seconds for API calls)

Circuit Breaker

Fail fast when API is down, prevent cascading failures

Monitoring

Track success rates, latency, and error rates in real-time

Logging

Log all transactions with sufficient detail for debugging

Alerts

Set up alerts for error spikes, latency increases, webhook failures

Documentation

Maintain runbooks for common production issues

Monitoring Production

Key Metrics to Track

const metrics = {
  // API Health
  apiResponseTime: 'Average response time for QR creation',
  apiErrorRate: 'Percentage of API calls that fail',
  apiAvailability: 'Uptime percentage',

  // Business Metrics
  qrCodesCreated: 'Total QR codes generated',
  transactionsCompleted: 'Successful payments',
  transactionValue: 'Total payment volume',
  conversionRate: 'QR scans to completed payments',

  // Webhook Metrics
  webhooksReceived: 'Total webhooks received',
  webhookProcessingTime: 'Time to process webhook',
  webhookFailureRate: 'Percentage of webhook processing failures',

  // Performance
  databaseQueryTime: 'Average DB query duration',
  serverCPU: 'CPU utilization',
  serverMemory: 'Memory utilization',
  requestsPerMinute: 'API request rate'
};

Example Monitoring Setup

const prometheus = require('prom-client');

// Create metrics
const qrCreationCounter = new prometheus.Counter({
  name: 'qr_codes_created_total',
  help: 'Total number of QR codes created'
});

const qrCreationDuration = new prometheus.Histogram({
  name: 'qr_creation_duration_seconds',
  help: 'QR code creation duration in seconds',
  buckets: [0.1, 0.5, 1, 2, 5]
});

const apiErrorCounter = new prometheus.Counter({
  name: 'api_errors_total',
  help: 'Total number of API errors',
  labelNames: ['error_code']
});

// Use in code
async function createQR(amount, ref) {
  const timer = qrCreationDuration.startTimer();

  try {
    const result = await modulusApi.createQR(amount, ref);
    qrCreationCounter.inc();
    return result;
  } catch (error) {
    apiErrorCounter.inc({ error_code: error.code });
    throw error;
  } finally {
    timer();
  }
}

Handling Production Issues

Common Production Issues

Symptoms:
  • HTTP 429 responses
  • Increased latency
  • Failed QR creations
Solutions:
  1. Implement request queuing
  2. Add exponential backoff
  3. Cache QR codes when appropriate
  4. Contact Modulus Labs to increase rate limit
const queue = new PQueue({ concurrency: 10, interval: 60000 });

async function createQRWithRateLimit(amount, ref) {
  return queue.add(() => modulusApi.createQR(amount, ref));
}
Symptoms:
  • Payments not updating in system
  • Manual reconciliation needed
  • Customer complaints
Solutions:
  1. Check webhook endpoint health
  2. Review firewall rules
  3. Verify SSL certificate
  4. Check webhook processing logs
  5. Contact Modulus Labs to resend webhooks
// Emergency webhook replay script
async function replayWebhooks(transactionIds) {
  // Contact [email protected] with transaction IDs
  // They can manually resend webhooks
}
Symptoms:
  • Sudden spike in API errors
  • Multiple error codes
  • Customer complaints
Solutions:
  1. Check Modulus Labs status page
  2. Review recent code changes
  3. Check production credentials haven’t expired
  4. Verify network connectivity
  5. Review error logs for patterns
# Check error patterns
grep "ERROR" app.log | cut -d' ' -f5 | sort | uniq -c | sort -nr

# Check API connectivity
curl https://qrph.moduluslabs.io/ping \
  -u $MODULUS_SECRET_KEY:
Symptoms:
  • Slow QR code generation
  • Timeouts
  • Poor user experience
Solutions:
  1. Check database performance
  2. Review application logs
  3. Scale horizontally (add more servers)
  4. Optimize database queries
  5. Implement caching
// Add caching for frequently accessed data
const redis = require('redis');
const client = redis.createClient();

async function getQRWithCache(transactionId) {
  const cached = await client.get(`qr:${transactionId}`);
  if (cached) return JSON.parse(cached);

  const qr = await database.getQR(transactionId);
  await client.setex(`qr:${transactionId}`, 300, JSON.stringify(qr));
  return qr;
}

Emergency Contacts

Production Support

Email: [email protected] Phone: Available in partner portal SLA: < 1 hour response for critical issues

Status Page

URL: status.moduluslabs.io Check for ongoing incidents or maintenance

Technical Support

Email: [email protected] For integration and API questions

Account Manager

Contact your dedicated account manager For business and billing questions

Production Maintenance

Regular Tasks

1

Daily

  • Monitor error rates and alerts
  • Review transaction volumes
  • Check webhook delivery rates
  • Scan security logs
2

Weekly

  • Review performance metrics
  • Analyze transaction patterns
  • Update documentation
  • Review and address technical debt
3

Monthly

  • Rotate API keys (if policy requires)
  • Update dependencies and security patches
  • Review and optimize database
  • Conduct security audit
  • Business reconciliation
4

Quarterly

  • Disaster recovery drill
  • Load testing
  • Review SLAs and performance
  • Update runbooks
  • Security penetration testing

Key Rotation

Rotate production credentials periodically:
# 1. Generate new credentials via partner portal

# 2. Update secrets manager with new credentials
aws secretsmanager update-secret \
  --secret-id prod/modulus-labs \
  --secret-string '{
    "MODULUS_SECRET_KEY": "sk_live_NEW_KEY",
    "MODULUS_ENCRYPTION_KEY": "NEW_ENCRYPTION_KEY"
  }'

# 3. Deploy with new credentials
npm run deploy:production

# 4. Verify new credentials work
npm run test:smoke:production

# 5. Revoke old credentials
# Contact [email protected] to revoke old API key

Scaling for Growth

As your transaction volume grows:

Horizontal Scaling

// Use load balancer to distribute requests
// across multiple application instances

// Example: AWS Auto Scaling
const autoscaling = {
  minInstances: 2,
  maxInstances: 10,
  targetCPU: 70,
  targetMemory: 80
};

// Scale based on metrics
if (requestsPerMinute > 500) {
  scaleUp();
}

Database Optimization

// Connection pooling
const pool = new Pool({
  max: 20,
  min: 5,
  idleTimeoutMillis: 30000
});

// Read replicas for queries
const writeDB = connectToMaster();
const readDB = connectToReplica();

// Use read replica for non-critical reads
async function getTransactionHistory(merchantRef) {
  return readDB.query(
    'SELECT * FROM transactions WHERE merchant_ref = $1',
    [merchantRef]
  );
}

Caching Strategy

// Cache QR codes for a short period
const cache = new Map();

async function createQRWithCache(amount, ref) {
  const cacheKey = `${amount}:${ref}`;

  if (cache.has(cacheKey)) {
    const cached = cache.get(cacheKey);
    if (Date.now() - cached.timestamp < 300000) { // 5 minutes
      return cached.data;
    }
  }

  const qr = await modulusApi.createQR(amount, ref);
  cache.set(cacheKey, {
    data: qr,
    timestamp: Date.now()
  });

  return qr;
}

Next Steps

You’re ready for production! Follow this guide carefully and don’t hesitate to contact Modulus Labs support if you need assistance.