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}/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}/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