MoMo Proxy API Documentation

This document describes the HTTP API exposed by the momopay.swazigist.co.za proxy. It explains how third-party apps can initiate mobile-money (MoMo) payments via MTN Collections, poll status, and receive payment result callbacks forwarded by the proxy.

Base URL

Authentication (for calling apps)

Endpoints

  1. POST /request-to-pay
{
    "amount": "100.00",
    "currency": "SZL",              // optional, default SZL
    "externalId": "ORDER-12345",   // app's own order/reference (required)
    "payer": {
      "partyIdType": "MSISDN",
      "partyId": "26876123456"     // E.164 without + (required)
    },
    "payerMessage": "...",         // optional
    "payeeNote": "...",            // optional
    "callbackUrl": "https://..."   // optional per-request override
  }

Example curl:

curl -X POST https://momopay.swazigist.co.za/request-to-pay \
  -H "Content-Type: application/json" \
  -H "X-Proxy-Api-Key: <your-app-key>" \
  -d '{
    "amount":"150.00",
    "externalId":"ORDER-98765",
    "payer":{"partyIdType":"MSISDN","partyId":"26876123456"}
  }'
  1. GET /status/{referenceId}

Example curl:

curl -H "X-Proxy-Api-Key: <your-app-key>" \
  https://momopay.swazigist.co.za/status/<referenceId>
  1. POST /callback (called by MTN)

Forwarding signature verification (for app developers):

$rawBody = file_get_contents('php://input');
$expected = hash_hmac('sha256', $rawBody, 'YOUR_APP_API_KEY');
$received = $_SERVER['HTTP_X_MOMO_PROXY_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) { http_response_code(403); exit; }
$payload = json_decode($rawBody, true);

Schema

Reference-only schema (read-only): the proxy already maintains these tables. Use this section to understand fields returned by API endpoints and callback payloads.

Note: This schema is provided for reference only. The proxy manages these tables; integrating apps should treat them as read-only externally.

MTN Integration details

Error handling & status mapping

Security recommendations

Integration checklist for an app (e.g. APP)

Example flow (end-to-end)

  1. App calls POST /request-to-pay with externalId, amount, payer and X-Proxy-Api-Key.
  2. Proxy stores transaction and calls MTN, passing X-Reference-Id: <referenceId> and X-Callback-Url: https://proxy/callback.
  3. MTN returns 202; proxy returns { referenceId, status: PENDING } to the app.
  4. MTN calls proxy /callback when the transaction settles.
  5. Proxy updates DB and forwards the callback to the app's callback URL, signing with HMAC using the app's api_key.
  6. App verifies signature and updates its own records (mark payment complete/failed).

Troubleshooting & operational notes


Examples, SDKs & Test Scripts (embedded)

All example clients, verification helpers, the OpenAPI spec, and test scripts are included below so you only need this single documentation file. Copy any snippet directly into your app or CI runner.


OpenAPI (openapi.yaml)

openapi: 3.0.1
info:
  title: MoMo Proxy API
  version: 1.0.0
  description: MTN Collections proxy used by apps to initiate requests-to-pay, poll status, and receive forwarded callbacks.
servers:
  - url: https://momopay.swazigist.co.za
security:
  - ProxyApiKey: []
components:
  securitySchemes:
    ProxyApiKey:
      type: apiKey
      in: header
      name: X-Proxy-Api-Key
  schemas:
    Payer:
      type: object
      properties:
        partyIdType:
          type: string
          example: MSISDN
        partyId:
          type: string
          example: 26876123456
      required: [partyIdType, partyId]
    RequestToPay:
      type: object
      properties:
        amount:
          type: string
          example: "150.00"
        currency:
          type: string
          example: SZL
        externalId:
          type: string
          example: ORDER-12345
        payer:
          $ref: '#/components/schemas/Payer'
        payerMessage:
          type: string
        payeeNote:
          type: string
        callbackUrl:
          type: string
      required: [amount, externalId, payer]
    RequestToPayResponse:
      type: object
      properties:
        referenceId:
          type: string
          format: uuid
        status:
          type: string
          example: PENDING
    StatusResponse:
      type: object
      properties:
        referenceId:
          type: string
        externalId:
          type: string
        status:
          type: string
        amount:
          type: number
        currency:
          type: string
        payer:
          type: string
        financialTxId:
          type: string
        failureReason:
          type: string
        callbackForwarded:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
paths:
  /request-to-pay:
    post:
      summary: Initiate a request-to-pay
      security:
        - ProxyApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestToPay'
      responses:
        '202':
          description: Accepted and processed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RequestToPayResponse'
        '401':
          description: Unauthorized
        '422':
          description: Unprocessable entity (validation error)
  /status/{referenceId}:
    get:
      summary: Poll transaction status
      security:
        - ProxyApiKey: []
      parameters:
        - name: referenceId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Current status of the transaction
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
        '403':
          description: Forbidden (transaction not owned by caller)
        '404':
          description: Not found
  /callback:
    post:
      summary: MTN posts transaction callback here (forwarded to app)
      description: |
        MTN will call this endpoint. Proxy will persist and forward to the originating app.
      requestBody:
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: OK (proxy always returns 200 to MTN)

PHP client example

Save as php_client.php or run directly on any PHP-enabled machine.

<?php
// Example PHP client: initiate a Request-to-Pay

$proxyBase = getenv('PROXY_BASE') ?: 'https://momopay.swazigist.co.za';
$apiKey    = getenv('PROXY_API_KEY') ?: 'APP_CHANGE_THIS';

$payload = [
    'amount'     => '150.00',
    'currency'   => 'SZL',
    'externalId' => 'ORDER-'.rand(1000,9999),
    'payer' => [
        'partyIdType' => 'MSISDN',
        'partyId'     => '26876123456',
    ],
    'payerMessage' => 'Payment for demo',
];

$ch = curl_init($proxyBase . '/request-to-pay');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-Proxy-Api-Key: ' . $apiKey,
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);

$res = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($res === false) {
    echo 'cURL error: ' . curl_error($ch) . PHP_EOL;
} else {
    echo "HTTP $code\n";
    echo $res . PHP_EOL;
}
curl_close($ch);

Node.js client example

Run with Node.js (install axios first: npm install axios).

// Example Node.js client using axios
// Run: node node_client.js

const axios = require('axios');
const PROXY_BASE = process.env.PROXY_BASE || 'https://momopay.swazigist.co.za';
const API_KEY = process.env.PROXY_API_KEY || 'APP_CHANGE_THIS';

(async () => {
  try {
    const payload = {
      amount: '150.00',
      currency: 'SZL',
      externalId: 'ORDER-' + Math.floor(Math.random()*9000 + 1000),
      payer: { partyIdType: 'MSISDN', partyId: '26876123456' },
      payerMessage: 'Node demo',
    };

    const res = await axios.post(PROXY_BASE + '/request-to-pay', payload, {
      headers: {
        'Content-Type': 'application/json',
        'X-Proxy-Api-Key': API_KEY,
      },
      timeout: 30000,
    });

    console.log('Status:', res.status);
    console.log(res.data);
  } catch (err) {
    if (err.response) {
      console.error('HTTP', err.response.status, err.response.data);
    } else {
      console.error(err.message);
    }
  }
})();

Callback verification (PHP)

Place this code in your app's callback endpoint to verify the X-MoMo-Proxy-Signature header.

<?php
// Example verification of forwarded callback in your app
// Place this in your app to verify X-MoMo-Proxy-Signature

$raw = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_MOMO_PROXY_SIGNATURE'] ?? '';
$apiKey = getenv('PROXY_API_KEY') ?: 'APP_CHANGE_THIS';
$expected = hash_hmac('sha256', $raw, $apiKey);

if (!hash_equals($expected, $received)) {
    http_response_code(403);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

$payload = json_decode($raw, true);
// Process payment status: $payload['referenceId'], $payload['status']
http_response_code(200);
echo json_encode(['ok' => true]);

cURL Examples

Initiate Request-to-Pay:

curl -X POST https://momopay.swazigist.co.za/request-to-pay \
  -H "Content-Type: application/json" \
  -H "X-Proxy-Api-Key: <YOUR_APP_KEY>" \
  -d '{
    "amount":"150.00",
    "externalId":"ORDER-1234",
    "payer":{"partyIdType":"MSISDN","partyId":"26876123456"}
  }'

Poll status:

curl -H "X-Proxy-Api-Key: <YOUR_APP_KEY>" \
  https://momopay.swazigist.co.za/status/<referenceId>

Simulate MTN callback to the proxy (for local testing):

curl -X POST https://momopay.swazigist.co.za/callback \
  -H "Content-Type: application/json" \
  -d '{
    "externalId":"ORDER-1234",
    "status":"SUCCESSFUL",
    "financialTransactionId":"FTX-123456"
  }'

Test scripts (bash)

Use these small scripts to run quick smoke tests. They assume curl and jq are installed. Set environment variables before running:

export PROXY_BASE=https://momopay.swazigist.co.za
export API_KEY=APP_CHANGE_THIS
  1. request_to_pay_test.sh - initiate a request-to-pay and print JSON response
#!/usr/bin/env bash
set -euo pipefail

PROXY_BASE=${PROXY_BASE:-https://momopay.swazigist.co.za}
API_KEY=${API_KEY:-APP_CHANGE_THIS}

resp=$(curl -s -w "\n%{http_code}" -X POST "$PROXY_BASE/request-to-pay" \
  -H "Content-Type: application/json" \
  -H "X-Proxy-Api-Key: $API_KEY" \
  -d '{"amount":"10.00","externalId":"TEST-'$RANDOM'","payer":{"partyIdType":"MSISDN","partyId":"26876123456"}}')

body=$(echo "$resp" | sed '$d')
code=$(echo "$resp" | tail -n1)

echo "HTTP $code"
echo "$body" | jq
  1. status_test.sh - poll status for a given referenceId
#!/usr/bin/env bash
set -euo pipefail

if [ "$#" -lt 1 ]; then
  echo "Usage: $0 <referenceId>"
  exit 1
fi

PROXY_BASE=${PROXY_BASE:-https://momopay.swazigist.co.za}
API_KEY=${API_KEY:-APP_CHANGE_THIS}
REF=$1

curl -s -H "X-Proxy-Api-Key: $API_KEY" "$PROXY_BASE/status/$REF" | jq
  1. send_callback_sim.sh - simulate an MTN callback to the proxy
#!/usr/bin/env bash
set -euo pipefail

if [ "$#" -lt 1 ]; then
  echo "Usage: $0 <externalId-or-referenceId>"
  exit 1
fi

PROXY_BASE=${PROXY_BASE:-https://momopay.swazigist.co.za}
ID=$1

payload=$(printf '{"externalId":"%s","status":"SUCCESSFUL","financialTransactionId":"FTX-%s"}' "$ID" "$RANDOM")

echo "Sending callback to $PROXY_BASE/callback with payload: $payload"
curl -i -X POST "$PROXY_BASE/callback" \
  -H "Content-Type: application/json" \
  -d "$payload"