DocsAdvanced guidesCI/CD Integration

CI/CD

This guide covers best practices for integrating lomi. with your CI/CD pipeline, ensuring reliable deployments and automated testing.

Environment setup

1. Environment variables

Example .env file for CI
# .env.ci
LOMI_API_KEY=lomi_sk_test_... # Use a dedicated test key
LOMI_WEBHOOK_SECRET=whsec_... # Test webhook secret
LOMI_API_URL=https://sandbox.api.lomi.africa/v1 # Point to sandbox
LOMI_ENV=test

2. Secrets management

Ensure your CI/CD environment securely loads required secrets. Never commit secrets directly to your repository.

Loading secrets in Node.js
// config/secrets.ts
export function loadSecrets(): void {
  const requiredSecrets = [
    'LOMI_API_KEY',
    'LOMI_WEBHOOK_SECRET'
  ];
 
  for (const secret of requiredSecrets) {
    if (!process.env[secret]) {
      console.warn(`Warning: Missing recommended secret: ${secret}`);
      // Depending on your setup, you might throw an error:
      // throw new Error(`Missing required secret: ${secret}`);
    }
  }
}
 
// Call loadSecrets() early in your application or test setup

GitHub Actions

1. Test workflow

Run your integration tests against the lomi. sandbox environment on pushes and pull requests.

.github/workflows/test.yml
name: Test
 
on:
  push:
    branches: [ main, develop ] # Trigger on main and develop branches
  pull_request:
    branches: [ main ]
 
jobs:
  test:
    runs-on: ubuntu-latest
 
    steps:
      - uses: actions/checkout@v3 # Use latest version
 
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18' # Use a current LTS version
          cache: 'npm' # Cache npm dependencies
 
      - name: Install dependencies
        run: npm ci
 
      - name: Run tests
        run: npm test
        env:
          LOMI_API_KEY: ${{ secrets.LOMI_TEST_API_KEY }} # Use GitHub secrets
          LOMI_WEBHOOK_SECRET: ${{ secrets.LOMI_TEST_WEBHOOK_SECRET }}
          LOMI_API_URL: 'https://sandbox.api.lomi.africa/v1'
          LOMI_ENV: test

2. Deploy workflow

Deploy your application using live keys only when pushing to your main branch.

.github/workflows/deploy.yml
name: Deploy
 
on:
  push:
    branches: [ main ] # Only trigger on pushes to main
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production # Optional: Define a GitHub environment for protection rules
 
    steps:
      - uses: actions/checkout@v3
 
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
 
      # Example: Deploy to AWS (replace with your deployment provider)
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-west-1 # Your region
 
      - name: Install, Build, and Deploy
        run: |
          npm ci
          npm run build
          npm run deploy # Your deployment script
        env:
          LOMI_API_KEY: ${{ secrets.LOMI_PROD_API_KEY }}
          LOMI_WEBHOOK_SECRET: ${{ secrets.LOMI_PROD_WEBHOOK_SECRET }}
          LOMI_API_URL: 'https://api.lomi.africa/v1'
          LOMI_ENV: production

Automated testing

1. Pre-deployment checks

Before deploying, run checks to ensure basic connectivity and configuration.

Example pre-deployment check script
// scripts/pre-deploy-check.ts
import { LomiSDK } from '@lomi/sdk'; // Assuming your SDK package name
 
async function runPreDeploymentChecks(): Promise<void> {
  console.log('Running pre-deployment checks...');
  const apiKey = process.env.LOMI_API_KEY;
  const apiUrl = process.env.LOMI_API_URL;
 
  if (!apiKey || !apiUrl) {
    throw new Error('LOMI_API_KEY and LOMI_API_URL must be set');
  }
 
  const lomi = new LomiSDK({
    apiKey: apiKey,
    baseUrl: apiUrl
  });
 
  try {
    // 1. Verify API connectivity (e.g., fetch providers)
    console.log('Checking API connectivity...');
    const providers = await lomi.providers.list();
    if (!providers || providers.data.length === 0) {
        throw new Error('Failed to fetch providers or no providers available.');
    }
    console.log(`Successfully fetched ${providers.data.length} providers.`);
 
    // 2. Optional: Test webhook endpoint connectivity if applicable
    // Note: The SDK might not have a direct webhook test method.
    // You might need a custom check or rely on integration tests.
    // Example placeholder:
    // console.log('Checking webhook endpoint...');
    // const webhookTestResult = await checkMyWebhookEndpoint();
    // if (!webhookTestResult.success) throw new Error('Webhook endpoint check failed');
 
    console.log('Pre-deployment checks passed!');
  } catch (error) {
    console.error('Pre-deployment check failed:', error);
    process.exit(1); // Exit with error code
  }
}
 
runPreDeploymentChecks();

2. Integration tests

Write tests that simulate user flows involving lomi. interactions.

Example integration test (using Jest)
// tests/integration/payment.test.ts
import { LomiSDK } from '@lomi/sdk';
// Assume setup/teardown logic exists elsewhere
 
describe('Payment Integration', () => {
  let lomi: LomiSDK;
 
  beforeAll(() => {
    // Ensure required env vars are set for tests
    if (!process.env.LOMI_TEST_API_KEY || !process.env.TEST_MERCHANT_ID) {
        throw new Error('Missing test environment variables');
    }
    lomi = new LomiSDK({
      apiKey: process.env.LOMI_TEST_API_KEY,
      baseUrl: 'https://sandbox.api.lomi.africa/v1'
    });
    // await setupTestEnvironment(); // Your test setup
  });
 
  it('should process a test payment end-to-end', async () => {
    // Create checkout session
    const sessionResponse = await lomi.checkoutSessions.create({
      merchant_id: process.env.TEST_MERCHANT_ID!, // Use a test merchant ID
      amount: 1000, // Min amount for testing
      currency_code: 'XOF',
      allowed_providers: ['WAVE'], // Use a testable provider
      success_url: 'https://example.com/success',
      cancel_url: 'https://example.com/cancel',
      metadata: { test_order_id: `e2e_${Date.now()}` }
    });
    const sessionId = sessionResponse.data.checkout_session_id;
    expect(sessionId).toBeDefined();
 
    // Simulate payment (requires a test helper or specific endpoint)
    // This part is highly dependent on lomi.'s testing capabilities
    // await lomi.testing.simulatePayment(sessionId, 'succeeded');
 
    // Verify success (allow time for processing)
    // await new Promise(r => setTimeout(r, 5000)); // Wait if needed
    // const updatedSession = await lomi.checkoutSessions.get(sessionId);
    // expect(updatedSession.data.status).toBe('completed');
  }, 30000); // Increase timeout for E2E test
});

Deployment strategies

Choose a strategy that minimizes risk during deployment.

1. Blue-green deployment

Maintain two identical production environments (Blue and Green). Deploy to the inactive environment, test, then switch traffic.

Conceptual blue-green deployment script
// scripts/deploy-blue-green.ts
async function blueGreenDeploy(newVersion: string): Promise<void> {
  const currentActive = await getActiveEnvironment(); // e.g., 'blue'
  const inactiveEnv = currentActive === 'blue' ? 'green' : 'blue';
 
  // 1. Deploy to inactive environment
  console.log(`Deploying version ${newVersion} to ${inactiveEnv}...`);
  await deployToEnvironment(inactiveEnv, newVersion);
 
  // 2. Run health checks on inactive environment
  console.log(`Running health checks on ${inactiveEnv}...`);
  const health = await checkHealth(inactiveEnv);
  if (!health.ok) {
    console.error(`Health check failed for ${inactiveEnv}. Rolling back deployment.`);
    // await rollbackDeployment(inactiveEnv); // Optional rollback
    throw new Error('Health check failed on inactive environment');
  }
 
  // 3. Switch traffic
  console.log(`Switching traffic to ${inactiveEnv}...`);
  await switchTraffic(inactiveEnv);
 
  // 4. Monitor the new active environment
  console.log(`Monitoring ${inactiveEnv}...`);
  // await monitorDeployment(inactiveEnv);
 
  // 5. Optional: Tear down the old environment after a period
}

2. Canary deployment

Gradually roll out the new version to a small subset of users/traffic before releasing it fully.

Conceptual canary deployment script
// scripts/deploy-canary.ts
async function canaryDeploy(newVersion: string): Promise<void> {
  // 1. Deploy new version alongside current version with limited traffic (e.g., 10%)
  console.log(`Deploying canary version ${newVersion} with 10% traffic...`);
  await deployCanaryVersion(newVersion, '10%');
 
  // 2. Monitor metrics (error rates, latency) for a defined period
  console.log('Monitoring canary metrics...');
  const metricsOk = await monitorCanaryMetrics({ duration: '1h', errorThreshold: 0.05 });
 
  if (!metricsOk) {
    console.error('Canary metrics failed. Rolling back canary...');
    await rollbackCanary(newVersion);
    throw new Error('Canary deployment failed metrics check');
  }
 
  // 3. Gradually increase traffic to the new version
  console.log('Canary metrics OK. Gradually increasing traffic...');
  await scaleCanaryTraffic(newVersion, '50%');
  await monitorCanaryMetrics({ duration: '30m' }); // Monitor again
  await scaleCanaryTraffic(newVersion, '100%'); // Full rollout
 
  // 4. Decommission the old version
  console.log('Canary deployment successful. Decommissioning old version...');
  await decommissionOldVersion();
}

Monitoring

Continuously monitor your integration health.

1. Health checks

Set up automated checks for API connectivity and critical functions.

Conceptual health check function
// monitoring/healthCheck.ts
import { LomiSDK } from '@lomi/sdk';
// import db from './database'; // Your database connection
 
export async function checkServiceHealth(): Promise<{ status: string, errors: string[] }> {
  const errors: string[] = [];
  const lomi = new LomiSDK({ apiKey: process.env.LOMI_API_KEY!, baseUrl: process.env.LOMI_API_URL! });
 
  try {
    // Check lomi. API status (e.g., list providers)
    await lomi.providers.list();
  } catch (error: any) {
    errors.push(`lomi. API check failed: ${error.message}`);
  }
 
  // try {
  //   // Check Database health
  //   await db.raw('SELECT 1');
  // } catch (error: any) {
  //   errors.push(`Database check failed: ${error.message}`);
  // }
 
  // Check essential internal services...
 
  return {
    status: errors.length === 0 ? 'healthy' : 'unhealthy',
    errors: errors
  };
}

2. Metrics collection

Track key metrics related to payments and webhooks.

Conceptual metrics collection
// monitoring/metrics.ts
export function collectMetrics(): Record<string, any> {
  // Example metrics (implementation depends on your monitoring tools)
  return {
    // Payment metrics
    payments_processed_total: getTotalPayments(),
    payments_succeeded_rate: getSuccessRate(),
    payment_api_latency_ms: getAverageApiLatency(),
 
    // Webhook metrics
    webhooks_received_total: getTotalWebhooksReceived(),
    webhook_verification_failures_total: getWebhookVerificationFailures(),
    webhook_processing_latency_ms: getAverageWebhookProcessingTime(),
 
    // System metrics
    // system_error_rate: getErrorRate(),
    // system_cpu_usage: getCPUUsage(),
  };
}

Rollback procedures

Have a plan to quickly revert to a previous stable version if a deployment introduces issues.

1. Automated rollback

Integrate rollback steps into your deployment workflow.

Conceptual automated rollback script
// scripts/rollback.ts
async function automaticRollback(failedDeploymentId: string, previousVersion: string): Promise<void> {
  console.log(`Rolling back deployment ${failedDeploymentId} to version ${previousVersion}...`);
  try {
    // 1. Stop traffic to the failed version (if applicable)
    // await stopTraffic(failedDeploymentId);
 
    // 2. Redeploy the previous stable version
    await deployToEnvironment('production', previousVersion);
 
    // 3. Verify rollback success
    const health = await checkHealth('production');
    if (!health.ok) {
      throw new Error('Rollback verification failed');
    }
    console.log(`Rollback to ${previousVersion} successful and verified.`);
 
    // 4. Notify team
    // await notifyTeam(`Rollback completed for deployment ${failedDeploymentId}. Restored version ${previousVersion}.`);
 
  } catch (error: any) {
    console.error(`Automatic rollback failed: ${error.message}`);
    // await notifyTeam(`CRITICAL: Automatic rollback failed for deployment ${failedDeploymentId}. Manual intervention required.`);
    throw error; // Re-throw to signal CI failure
  }
}

Next steps