Fonctionnement
Lorsque vous initiez une transaction (collect ou deposit), vous fournissez unewebhookUrl. Dès que la transaction atteint un état final (SUCCESS ou FAILED), Remita envoie une requête HTTP POST vers votre URL.
[Remita] ──POST──▶ [Votre serveur] ──▶ Traitement du résultat
Configuration
Fournissez le champwebhookUrl dans le corps de votre requête de transaction :
{
"transferMethod": "OMCM",
"customerName": "Jean Dupont",
"externalId": "550e8400-e29b-41d4-a716-446655440000",
"phoneNumber": "237690000000",
"amount": 5000,
"webhookUrl": "https://votre-serveur.com/remita/callback"
}
Format du payload reçu
Remita envoie unPOST avec Content-Type: application/json :
{
"externalId": "550e8400-e29b-41d4-a716-446655440000",
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "SUCCESS",
"amount": 5000,
"phoneNumber": "237690000000",
"transferMethod": "OMCM",
"message": "Transaction réussie",
"timestamp": "2026-03-19T10:02:30Z"
}
| Champ | Type | Description |
|---|---|---|
externalId | UUID | Votre identifiant externe |
transactionId | String | Identifiant interne Remita |
status | String | SUCCESS ou FAILED |
amount | Number | Montant de la transaction |
phoneNumber | String | Numéro de téléphone concerné |
transferMethod | String | Code opérateur (ex : OMCM, MOMOCM) |
message | String | Message descriptif |
timestamp | String | Horodatage ISO 8601 |
Réponse attendue
Votre endpoint doit répondre avec un code HTTP
200 dans un délai de 5 secondes. Toute autre réponse ou timeout entraîne une nouvelle tentative. Déléguez tout traitement lourd à une file de tâches et répondez immédiatement.Politique de retry
| Tentative | Délai |
|---|---|
| 1ère | Immédiate |
| 2ème | Après 30 s |
| 3ème | Après 2 min |
| 4ème | Après 10 min |
POST /api/v1/transaction/chekTransactionStatus?transactionId=<id> pour forcer la synchronisation avec l’opérateur.
Implémentation de l’endpoint
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/remita")
public class RemitaWebhookController {
@PostMapping("/callback")
public ResponseEntity<String> handleCallback(@RequestBody Map<String, Object> payload) {
String externalId = (String) payload.get("externalId");
String status = (String) payload.get("status");
Number amount = (Number) payload.get("amount");
if ("SUCCESS".equals(status)) {
// Créditer le compte client, envoyer une notification...
System.out.println("Transaction " + externalId + " réussie : " + amount + " XAF");
} else {
// Logger l'erreur, notifier l'équipe...
System.err.println("Transaction " + externalId + " échouée");
}
// Toujours répondre 200 rapidement
return ResponseEntity.ok("OK");
}
}
from fastapi import FastAPI, Request
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.post("/remita/callback")
async def remita_callback(request: Request):
payload = await request.json()
external_id = payload.get("externalId")
status = payload.get("status")
amount = payload.get("amount", 0)
if status == "SUCCESS":
print(f"Transaction {external_id} réussie : {amount} XAF")
# Créditer le compte, envoyer notification...
elif status == "FAILED":
print(f"Transaction {external_id} échouée")
# Logger, notifier...
# Toujours répondre 200 dans les 5 secondes
return PlainTextResponse("OK", status_code=200)
from flask import Flask, request
app = Flask(__name__)
@app.post("/remita/callback")
def remita_callback():
payload = request.get_json()
external_id = payload.get("externalId")
status = payload.get("status")
if status == "SUCCESS":
print(f"Transaction {external_id} réussie")
else:
print(f"Transaction {external_id} échouée")
return "OK", 200
<?php
$payload = json_decode(file_get_contents('php://input'), true);
if (!$payload) {
http_response_code(400);
exit('Invalid payload');
}
$externalId = $payload['externalId'] ?? null;
$status = $payload['status'] ?? null;
$amount = $payload['amount'] ?? 0;
if ($status === 'SUCCESS') {
error_log("Transaction $externalId réussie : $amount XAF");
// Créditer le compte client...
} elseif ($status === 'FAILED') {
error_log("Transaction $externalId échouée");
// Logger, notifier...
}
// Toujours répondre 200 rapidement
http_response_code(200);
echo 'OK';
const express = require('express');
const app = express();
app.use(express.json());
app.post('/remita/callback', (req, res) => {
const { transactionId, externalId, status, amount } = req.body;
if (status === 'SUCCESS') {
console.log(`Paiement reçu — externalId: ${externalId}, montant: ${amount}`);
// Créditer le compte, envoyer un email...
} else if (status === 'FAILED') {
console.error(`Paiement échoué — externalId: ${externalId}`);
}
// Toujours répondre 200 dans les 5 secondes
res.status(200).send('OK');
});
Sécurité — Vérifier avant de traiter
Vérifiez toujours le statut réel via l’API avant d’agir sur un webhook, pour éviter les faux callbacks.
curl -X POST "https://api.remita.cm/api/v1/transaction/transaction-status?id=3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "apiKey: YOUR_API_KEY" \
-H "apiId: YOUR_API_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
import requests
def verify_and_process(payload: dict, access_token: str, api_key: str, api_id: str):
transaction_id = payload["transactionId"]
verified = requests.post(
"https://api.remita.cm/api/v1/transaction/transaction-status",
headers={
"apiKey": api_key,
"apiId": api_id,
"Authorization": f"Bearer {access_token}",
},
params={"id": transaction_id}
).json()
if verified.get("status") == "SUCCESS":
print(f"Transaction vérifiée et réussie : {transaction_id}")
else:
print(f"Statut non confirmé : {verified.get('status')}")