Home/Developer Docs
Payment Gateway API

Dropme Payment Gateway

Dropme's Payment Gateway API lets you accept mobile money payments (MTN MoMo, Orange Money) from your own applications — without managing PawaPay credentials or a callback infrastructure. Dropme acts as the single certified intermediary and fans out payment results to your webhook endpoint.

The API is REST-based, returns JSON, and uses Bearer token authentication. Every endpoint is versioned under /api/v1/.

REST + JSON

Standard HTTP methods, predictable response shapes

Bearer Auth

Scoped API keys — never share credentials with clients

Webhook delivery

Terminal results POSTed to your callback URL

Base URL

All API calls are made to the following base URL:

https://dropme.cm

Sandbox and production environments are distinguished by the API key prefix (dm_test_ vs dm_live_), not by a different base URL.

Authentication

Include your API key as a Bearer token in the Authorization header of every request.

curl https://dropme.cm/api/v1/payments/mobile-money/initiate \
  -H "Authorization: Bearer dm_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"phone":"237670000000","amount":5000,"provider":"MTN_MOMO_CMR","currency":"XAF","country":"CMR","callbackUrl":"https://yourapp.com/webhooks/payment"}'

API key types

PrefixEnvironmentNote
dm_live_...ProductionReal money movement — guard carefully
dm_test_...SandboxNo real transactions — safe for development
Security: Never expose your API key in frontend code or version control. Store it in environment variables (e.g. DROPME_API_KEY) and access it only server-side.

Quick Start

Accept your first mobile money payment in under 5 minutes.

1

Get your API key

In the Dropme admin, go to Integrations → Payment Gateway and create a client. Your API key is shown once — copy it to a secure vault.

2

Initiate a payment

POST to /api/v1/payments/mobile-money/initiate with the payer's phone, amount, and your callback URL.

3

Handle the callback

Dropme POSTs the terminal result (COMPLETED, FAILED, etc.) to your callbackUrl. Respond with HTTP 200 immediately, then process asynchronously.

POST /initiate
const axios = require('axios');

const DROPME_KEY = 'dm_live_your_api_key_here';
const BASE_URL   = 'https://dropme.cm';

async function initiatePayment({ phone, amount, provider, reference, callbackUrl }) {
  const { data } = await axios.post(
    `${BASE_URL}/api/v1/payments/mobile-money/initiate`,
    {
      phone,
      amount,
      currency: 'XAF',
      country: 'CMR',
      provider,            // e.g. 'MTN_MOMO_CMR' or 'ORANGE_CMR'
      externalReference: reference,
      callbackUrl,
    },
    { headers: { Authorization: `Bearer ${DROPME_KEY}` } }
  );
  return data; // { depositId, status: 'PENDING', externalReference }
}

// Usage
initiatePayment({
  phone: '237670000000',
  amount: 5000,
  provider: 'MTN_MOMO_CMR',
  reference: 'order_abc123',
  callbackUrl: 'https://yourapp.com/webhooks/payment',
}).then(console.log);

Initiate a Payment

POST/api/v1/payments/mobile-money/initiate

Triggers a mobile money deposit request via PawaPay. Returns immediately with a depositId. The final result is delivered asynchronously to your callbackUrl.

Request body

ParameterTypeRequiredDescription
phonestringrequiredE.164 formatted phone number including country code. e.g. '237670000000'
amountnumberrequiredPayment amount as a positive integer (no decimals for XAF).
providerstringrequiredMobile money provider code. See Providers table below.
currencystringoptionalISO 4217 currency code. Defaults to 'XAF'.
countrystringoptionalISO 3166-1 alpha-3 country code. Defaults to 'CMR'.
callbackUrlstringoptionalHTTPS URL where the terminal result will be POSTed. Falls back to your client's default callback URL.
externalReferencestringoptionalYour own order or transaction ID. Echoed back in the webhook payload.

Supported providers

provider valueNetworkCountry
MTN_MOMO_CMRMTN MoMoCameroon (CMR)
ORANGE_CMROrange MoneyCameroon (CMR)
MTN_MOMO_CIVMTN MoMoCôte d'Ivoire (CIV)
ORANGE_CIVOrange MoneyCôte d'Ivoire (CIV)
MTN_MOMO_GHAMTN MoMoGhana (GHA)

Success response 202 Accepted

{
  "depositId": "3e4fc1a0-8b2d-4f5a-9c1e-7a6b8d0e2f3c",
  "status": "PENDING",
  "externalReference": "order_abc123",
  "message": "Payment initiated. You will receive a callback when complete."
}

Code examples

const axios = require('axios');

const DROPME_KEY = 'dm_live_your_api_key_here';
const BASE_URL   = 'https://dropme.cm';

async function initiatePayment({ phone, amount, provider, reference, callbackUrl }) {
  const { data } = await axios.post(
    `${BASE_URL}/api/v1/payments/mobile-money/initiate`,
    {
      phone,
      amount,
      currency: 'XAF',
      country: 'CMR',
      provider,            // e.g. 'MTN_MOMO_CMR' or 'ORANGE_CMR'
      externalReference: reference,
      callbackUrl,
    },
    { headers: { Authorization: `Bearer ${DROPME_KEY}` } }
  );
  return data; // { depositId, status: 'PENDING', externalReference }
}

// Usage
initiatePayment({
  phone: '237670000000',
  amount: 5000,
  provider: 'MTN_MOMO_CMR',
  reference: 'order_abc123',
  callbackUrl: 'https://yourapp.com/webhooks/payment',
}).then(console.log);

Check Payment Status

GET/api/v1/payments/mobile-money/{depositId}

Poll the current status of a payment. Use this as a fallback if your webhook endpoint is temporarily unavailable. Prefer webhook delivery for real-time updates.

Path parameter

ParameterTypeRequiredDescription
depositIdstring (UUID)requiredThe depositId returned from the initiate endpoint.

Success response 200 OK

{
  "payment": {
    "id": "...",
    "deposit_id": "3e4fc1a0-8b2d-4f5a-9c1e-7a6b8d0e2f3c",
    "external_reference": "order_abc123",
    "phone": "237670000000",
    "provider": "MTN_MOMO_CMR",
    "amount": "5000",
    "currency": "XAF",
    "status": "COMPLETED",
    "callback_status": "delivered",
    "created_at": "2025-06-15T07:00:00.000Z",
    "updated_at": "2025-06-15T07:02:14.000Z"
  }
}

Code examples

async function checkPayment(depositId) {
  const { data } = await axios.get(
    `${BASE_URL}/api/v1/payments/mobile-money/${depositId}`,
    { headers: { Authorization: `Bearer ${DROPME_KEY}` } }
  );
  return data.payment; // { depositId, status, amount, currency, phone, ... }
}

const payment = await checkPayment('3e4fc1a0-...');
if (payment.status === 'COMPLETED') {
  // Fulfil order
}

Webhooks

When a payment reaches a terminal state, Dropme sends a POST request to your registered callback URL. Callbacks are only sent for terminal statuses — never for intermediate states like PENDING.

Payload schema

{
  "depositId": "3e4fc1a0-8b2d-4f5a-9c1e-7a6b8d0e2f3c",
  "status": "COMPLETED",          // or FAILED, CANCELED, TIMEOUT, REJECTED
  "amount": "5000",
  "currency": "XAF",
  "providerTransactionId": "PP-...",  // PawaPay reference, may be null on failure
  "payer": { "type": "MSISDN", "address": { "value": "237670000000" } },
  "timestamp": "2025-06-15T07:02:14.000Z"
}

Terminal statuses

StatusMeaningAction
COMPLETEDPayment succeeded, funds transferredFulfil order / credit account
FAILEDPayment failed at the operator levelNotify user, release holds
CANCELEDPayment was explicitly cancelledNotify user
TIMEOUTPayer did not approve within the time limitPrompt user to retry
REJECTEDOperator rejected the requestLog and notify support

Best practices

  • Respond with HTTP 200 immediately before processing. Dropme marks delivery as failed after 10 seconds.
  • Make your webhook handler idempotent — the same depositId may be delivered more than once.
  • Process the actual business logic asynchronously (background job / queue).
  • Verify the depositId exists in your own database before acting on it.
  • Dropme retries failed deliveries with exponential backoff.

Handler examples

// Express.js webhook handler
const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhooks/payment', (req, res) => {
  // Always respond 200 immediately — retries use exponential backoff
  res.status(200).json({ received: true });

  const { depositId, status, amount, currency, externalReference } = req.body;

  if (status === 'COMPLETED') {
    // Fulfil the order or credit the user
    fulfillOrder(externalReference, { depositId, amount, currency });

  } else if (['FAILED', 'CANCELED', 'TIMEOUT', 'REJECTED'].includes(status)) {
    // Notify the user and release any holds
    handleFailedPayment(externalReference, status);
  }
  // 'PENDING' callbacks are NOT sent — only terminal statuses are delivered
});

app.listen(3000);

Error Reference

All error responses follow a consistent JSON shape:

{
  "error": "Human-readable error message"
}

HTTP status codes

CodeMeaningCommon cause
200 / 202SuccessRequest processed successfully
400Bad RequestMissing required fields or invalid values
401UnauthorizedMissing, malformed, or invalid API key
403ForbiddenAPI key suspended or scope not permitted
404Not FounddepositId does not belong to this client
422UnprocessablePawaPay rejected the deposit (e.g. invalid provider)
429Rate LimitedExceeded requests/minute for this API key
500Internal ErrorUpstream PawaPay error or platform issue. Retry with backoff.

Rate Limits & Testing

Each API key has a configurable rate limit set by the Dropme admin (default: 60 requests/minute). Exceeding the limit returns 429 Too Many Requests.

Testing in sandbox

Use a dm_test_ key for all development. Sandbox keys hit the PawaPay sandbox environment — no real money moves. Your webhook endpoint will still be called with the real callback payload, so you can test your full integration end-to-end.

Sandbox base URLhttps://dropme.cm (same — key prefix controls env)
Test phone237670000000 (any valid E.164 number works)
Test amountAny positive integer
Test providerMTN_MOMO_CMR / ORANGE_CMR

SDK / library support

No official SDK is required — the API is a plain REST interface. Any HTTP client works. The code examples on this page are ready to copy directly into your project.