Who is this for?

Mobile developers using React Native or Flutter who want to accept payments, create payment links, or manage wallets with Monieswitch APIs.

Overview

This guide covers:
  • Setting up secure API integration architecture
  • Creating a backend proxy for Monieswitch APIs
  • Building mobile app clients that communicate with your backend
  • Creating payment links and handling responses
  • Security best practices for mobile payments
  • Error handling and user experience considerations

Prerequisites

1

Create Monieswitch Account

A Monieswitch account. Register at Monieswitch Dashboard.
2

Get API Key

API Key from your Monieswitch dashboard.
3

Set Up Development Environment

React Native or Flutter development environment set up.
4

Backend Service

A backend service (Node.js, Python, etc.) to securely handle Monieswitch API calls.

Architecture Overview

Mobile App → Your Backend API → Monieswitch APIs
Mobile apps should never communicate directly with Monieswitch APIs. Always use a secure backend to proxy requests and protect your API keys.

1. Backend Setup (Required)

First, set up your backend to handle Monieswitch API calls:

Environment Configuration

Add these to your backend .env file:
MONIESWITCH_BASE_URL=https://nini.monieswitch.com
MONIESWITCH_API_KEY=your_api_key_here
PORT=3000

Backend Implementation

server.js
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

const BASE_URL = process.env.MONIESWITCH_BASE_URL;
const API_KEY = process.env.MONIESWITCH_API_KEY;

// Get merchant details
app.get('/api/monieswitch/merchant', async (req, res) => {
  try {
    const response = await axios.get(`${BASE_URL}/api/merchant`, {
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      }
    });
    res.json(response.data);
  } catch (error) {
    console.error('Monieswitch API Error:', error.response?.data);
    res.status(error.response?.status || 500).json({
      error: 'Failed to fetch merchant details'
    });
  }
});

// Create payment link
app.post('/api/monieswitch/payment-links', async (req, res) => {
  try {
    const { amount, currency, description, email } = req.body;

    // Validate required fields
    if (!amount || !currency || !email) {
      return res.status(400).json({
        error: 'Missing required fields: amount, currency, email'
      });
    }

    const payload = {
      amount: parseInt(amount),
      currency,
      description: description || 'Mobile payment',
      customer: { email }
    };

    const response = await axios.post(`${BASE_URL}/api/payment-links`, payload, {
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      }
    });

    res.json(response.data);
  } catch (error) {
    console.error('Payment Link Error:', error.response?.data);
    res.status(error.response?.status || 500).json({
      error: 'Failed to create payment link'
    });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Backend server running on port ${PORT}`);
});

2. Mobile App Setup

Install HTTP Request Libraries

npm install axios
# For secure storage (optional)
npm install @react-native-async-storage/async-storage

Configure API Client

Create an API service file:
services/api.js
import axios from 'axios';

// Configure your backend URL
const BACKEND_BASE_URL = 'https://your-backend.com'; // Replace with your backend URL
// For local development: 'http://localhost:3000'

const apiClient = axios.create({
  baseURL: BACKEND_BASE_URL,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Add request interceptor for logging
apiClient.interceptors.request.use(
  (config) => {
    console.log('API Request:', config.method?.toUpperCase(), config.url);
    return config;
  },
  (error) => Promise.reject(error)
);

// Add response interceptor for error handling
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error('API Error:', error.response?.data || error.message);
    return Promise.reject(error);
  }
);

export const monieswitchAPI = {
  // Get merchant details
  getMerchantDetails: async () => {
    try {
      const response = await apiClient.get('/api/monieswitch/merchant');
      return response.data;
    } catch (error) {
      throw new Error(error.response?.data?.error || 'Failed to fetch merchant details');
    }
  },

  // Create payment link
  createPaymentLink: async (paymentData) => {
    try {
      const response = await apiClient.post('/api/monieswitch/payment-links', paymentData);
      return response.data;
    } catch (error) {
      throw new Error(error.response?.data?.error || 'Failed to create payment link');
    }
  },
};

3. Using the API in Your App

Fetching Merchant Details

components/MerchantInfo.js
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, Alert } from 'react-native';
import { monieswitchAPI } from '../services/api';

const MerchantInfo = () => {
  const [merchantData, setMerchantData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchMerchantDetails();
  }, []);

  const fetchMerchantDetails = async () => {
    try {
      setLoading(true);
      const data = await monieswitchAPI.getMerchantDetails();
      setMerchantData(data);
    } catch (error) {
      Alert.alert('Error', error.message);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" />
      </View>
    );
  }

  return (
    <View>
      <Text>Merchant: {merchantData?.name}</Text>
      <Text>Email: {merchantData?.email}</Text>
    </View>
  );
};

export default MerchantInfo;
components/PaymentLinkForm.js
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, Alert, ActivityIndicator } from 'react-native';
import { monieswitchAPI } from '../services/api';

const PaymentLinkForm = () => {
  const [formData, setFormData] = useState({
    amount: '',
    currency: 'NGN',
    description: '',
    email: '',
  });
  const [loading, setLoading] = useState(false);

  const handleInputChange = (field, value) => {
    setFormData(prev => ({ ...prev, [field]: value }));
  };

  const createPaymentLink = async () => {
    if (!formData.amount || !formData.email) {
      Alert.alert('Error', 'Please fill in amount and email');
      return;
    }

    try {
      setLoading(true);
      const paymentData = {
        amount: parseInt(formData.amount),
        currency: formData.currency,
        description: formData.description || 'Mobile payment',
        email: formData.email,
      };

      const response = await monieswitchAPI.createPaymentLink(paymentData);

      Alert.alert(
        'Success',
        `Payment link created! URL: ${response.payment_url}`,
        [{ text: 'OK' }]
      );

      // Reset form
      setFormData({ amount: '', currency: 'NGN', description: '', email: '' });
    } catch (error) {
      Alert.alert('Error', error.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 18, marginBottom: 20 }}>Create Payment Link</Text>

      <TextInput
        placeholder="Amount (e.g., 5000)"
        value={formData.amount}
        onChangeText={(value) => handleInputChange('amount', value)}
        keyboardType="numeric"
        style={{ borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 5 }}
      />

      <TextInput
        placeholder="Currency (e.g., NGN)"
        value={formData.currency}
        onChangeText={(value) => handleInputChange('currency', value)}
        style={{ borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 5 }}
      />

      <TextInput
        placeholder="Description (optional)"
        value={formData.description}
        onChangeText={(value) => handleInputChange('description', value)}
        style={{ borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 5 }}
      />

      <TextInput
        placeholder="Customer Email"
        value={formData.email}
        onChangeText={(value) => handleInputChange('email', value)}
        keyboardType="email-address"
        style={{ borderWidth: 1, padding: 10, marginBottom: 20, borderRadius: 5 }}
      />

      <TouchableOpacity
        onPress={createPaymentLink}
        disabled={loading}
        style={{
          backgroundColor: loading ? '#ccc' : '#007AFF',
          padding: 15,
          borderRadius: 5,
          alignItems: 'center'
        }}
      >
        {loading ? (
          <ActivityIndicator color="white" />
        ) : (
          <Text style={{ color: 'white', fontSize: 16 }}>Create Payment Link</Text>
        )}
      </TouchableOpacity>
    </View>
  );
};

export default PaymentLinkForm;

4. Error Handling Best Practices

Common Error Scenarios

  1. Network connectivity issues
  2. Invalid API responses
  3. Authentication failures
  4. Validation errors
  5. Server timeouts

Implementation Examples

utils/errorHandler.js
export const handleAPIError = (error, showAlert = true) => {
  let errorMessage = 'An unexpected error occurred';

  if (error.response) {
    // Server responded with error status
    errorMessage = error.response.data?.error || `Server error: ${error.response.status}`;
  } else if (error.request) {
    // Request was made but no response received
    errorMessage = 'Network error. Please check your connection.';
  } else {
    // Something else happened
    errorMessage = error.message || errorMessage;
  }

  console.error('API Error:', errorMessage);

  if (showAlert) {
    Alert.alert('Error', errorMessage);
  }

  return errorMessage;
};

5. Security Best Practices

  • Never store API keys in mobile app code
  • Always use HTTPS for all communications
  • Implement proper input validation
  • Use certificate pinning for production apps
  • Implement request signing for sensitive operations

Additional Security Measures

  1. Input Validation: Always validate user input before sending to your backend
  2. Rate Limiting: Implement rate limiting on your backend API endpoints
  3. Authentication: Use proper authentication mechanisms between your app and backend
  4. Logging: Log API requests and responses for debugging (exclude sensitive data)
  5. Error Messages: Don’t expose internal system details in error messages

6. Testing Your Integration

Backend Testing

# Test merchant details endpoint
curl -X GET http://localhost:3000/api/monieswitch/merchant

# Test payment link creation
curl -X POST http://localhost:3000/api/monieswitch/payment-links \
  -H "Content-Type: application/json" \
  -d '{"amount": 5000, "currency": "NGN", "email": "[email protected]"}'

Next Steps

  • Explore more endpoints in the API Reference
  • Implement webhooks on your backend for real-time payment updates

Troubleshooting

Common Issues

  1. CORS errors: Ensure your backend properly handles CORS if testing from web
  2. Network timeouts: Implement proper timeout handling and retry logic
  3. Environment variables not loading: Double-check your .env file location and format
  4. API key authentication failures: Verify your API key is correct and has proper permissions

Debug Tips

  • Enable detailed logging in both mobile app and backend
  • Use network debugging tools (React Native Debugger, Flutter Inspector)
  • Test API endpoints independently before integrating with mobile app
  • Monitor backend logs for detailed error informati