📚 Documentation API Movapay

Intégrez facilement les paiements dans votre application avec notre API simple et puissante

🚀 Démarrage Rapide

  1. Créez votre compte développeur sur Movapay
  2. Récupérez vos identifiants (Client ID et Client Secret)
  3. Configurez votre URL de callback pour les notifications
  4. Intégrez nos endpoints dans votre application

🔐 Authentification

L'API Movapay utilise OAuth2 Client Credentials Flow. Vous devez inclure votre token d'accès dans l'en-tête Authorization :

Authorization: Bearer YOUR_ACCESS_TOKEN

🔔 Configuration des Webhooks

Configurez votre URL de callback dans votre tableau de bord développeur pour recevoir les notifications de paiement en temps réel.

URL de callback: https://votre-site.com/webhook/payment
Méthode: POST
Content-Type: application/json

💳 Initier un Paiement

POST /api/payments/initiate/

Crée une nouvelle transaction de paiement et génère une URL de paiement sécurisée.

import requests
import json

# Configuration
API_BASE_URL = "https://api.movapay.com"
CLIENT_ID = "your_client_id"
CLIENT_SECRET = "your_client_secret"

# Obtenir le token d'accès
def get_access_token():
    token_url = f"{API_BASE_URL}/oauth/token/"
    data = {
        "grant_type": "client_credentials",
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET
    }
    response = requests.post(token_url, data=data)
    return response.json()["access_token"]

# Initier un paiement
def initiate_payment(amount, currency="XOF", description="", customer_email="", customer_phone=""):
    token = get_access_token()
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "amount": amount,
        "currency": currency,
        "description": description,
        "customer_email": customer_email,
        "customer_phone": customer_phone,
        "return_url": "https://votre-site.com/payment/success",
        "cancel_url": "https://votre-site.com/payment/cancel"
    }
    
    response = requests.post(
        f"{API_BASE_URL}/api/payments/initiate/",
        headers=headers,
        json=payload
    )
    
    return response.json()

# Exemple d'utilisation
if __name__ == "__main__":
    result = initiate_payment(
        amount=5000,
        description="Paiement pour commande #12345",
        customer_email="client@example.com",
        customer_phone="+2250701234567"
    )
    print(json.dumps(result, indent=2))
const axios = require('axios');

// Configuration
const API_BASE_URL = 'https://api.movapay.com';
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';

// Obtenir le token d'accès
async function getAccessToken() {
    try {
        const response = await axios.post(`${API_BASE_URL}/oauth/token/`, {
            grant_type: 'client_credentials',
            client_id: CLIENT_ID,
            client_secret: CLIENT_SECRET
        });
        return response.data.access_token;
    } catch (error) {
        console.error('Erreur lors de l\'obtention du token:', error);
        throw error;
    }
}

// Initier un paiement
async function initiatePayment(amount, currency = 'XOF', description = '', customerEmail = '', customerPhone = '') {
    try {
        const token = await getAccessToken();
        
        const payload = {
            amount: amount,
            currency: currency,
            description: description,
            customer_email: customerEmail,
            customer_phone: customerPhone,
            return_url: 'https://votre-site.com/payment/success',
            cancel_url: 'https://votre-site.com/payment/cancel'
        };
        
        const response = await axios.post(`${API_BASE_URL}/api/payments/initiate/`, payload, {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            }
        });
        
        return response.data;
    } catch (error) {
        console.error('Erreur lors de l\'initiation du paiement:', error);
        throw error;
    }
}

// Exemple d'utilisation
initiatePayment(5000, 'XOF', 'Paiement pour commande #12345', 'client@example.com', '+2250701234567')
    .then(result => console.log(result))
    .catch(error => console.error(error));
// Configuration
const API_BASE_URL = 'https://api.movapay.com';
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';

// Obtenir le token d'accès
async function getAccessToken() {
    try {
        const response = await fetch(`${API_BASE_URL}/oauth/token/`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                grant_type: 'client_credentials',
                client_id: CLIENT_ID,
                client_secret: CLIENT_SECRET
            })
        });
        
        const data = await response.json();
        return data.access_token;
    } catch (error) {
        console.error('Erreur lors de l\'obtention du token:', error);
        throw error;
    }
}

// Initier un paiement
async function initiatePayment(amount, currency = 'XOF', description = '', customerEmail = '', customerPhone = '') {
    try {
        const token = await getAccessToken();
        
        const payload = {
            amount: amount,
            currency: currency,
            description: description,
            customer_email: customerEmail,
            customer_phone: customerPhone,
            return_url: 'https://votre-site.com/payment/success',
            cancel_url: 'https://votre-site.com/payment/cancel'
        };
        
        const response = await fetch(`${API_BASE_URL}/api/payments/initiate/`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        });
        
        return await response.json();
    } catch (error) {
        console.error('Erreur lors de l\'initiation du paiement:', error);
        throw error;
    }
}

// Exemple d'utilisation
initiatePayment(5000, 'XOF', 'Paiement pour commande #12345', 'client@example.com', '+2250701234567')
    .then(result => console.log(result))
    .catch(error => console.error(error));
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MovapayAPI {
    private static final String API_BASE_URL = "https://api.movapay.com";
    private static final String CLIENT_ID = "your_client_id";
    private static final String CLIENT_SECRET = "your_client_secret";
    private static final HttpClient client = HttpClient.newHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();
    
    // Obtenir le token d'accès
    public static String getAccessToken() throws Exception {
        String tokenUrl = API_BASE_URL + "/oauth/token/";
        
        String formData = String.format("grant_type=client_credentials&client_id=%s&client_secret=%s",
                CLIENT_ID, CLIENT_SECRET);
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(tokenUrl))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(formData))
                .build();
        
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        Map result = mapper.readValue(response.body(), Map.class);
        return (String) result.get("access_token");
    }
    
    // Initier un paiement
    public static Map initiatePayment(double amount, String currency, String description,
                                                    String customerEmail, String customerPhone) throws Exception {
        String token = getAccessToken();
        
        Map payload = Map.of(
            "amount", amount,
            "currency", currency,
            "description", description,
            "customer_email", customerEmail,
            "customer_phone", customerPhone,
            "return_url", "https://votre-site.com/payment/success",
            "cancel_url", "https://votre-site.com/payment/cancel"
        );
        
        String jsonPayload = mapper.writeValueAsString(payload);
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(API_BASE_URL + "/api/payments/initiate/"))
                .header("Authorization", "Bearer " + token)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                .build();
        
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return mapper.readValue(response.body(), Map.class);
    }
    
    public static void main(String[] args) {
        try {
            Map result = initiatePayment(5000.0, "XOF", 
                "Paiement pour commande #12345", "client@example.com", "+2250701234567");
            System.out.println(mapper.writeValueAsString(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
<?php

class MovapayAPI {
    private $apiBaseUrl = 'https://api.movapay.com';
    private $clientId = 'your_client_id';
    private $clientSecret = 'your_client_secret';
    
    // Obtenir le token d'accès
    public function getAccessToken() {
        $tokenUrl = $this->apiBaseUrl . '/oauth/token/';
        
        $postData = http_build_query([
            'grant_type' => 'client_credentials',
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret
        ]);
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $tokenUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/x-www-form-urlencoded'
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        $data = json_decode($response, true);
        return $data['access_token'];
    }
    
    // Initier un paiement
    public function initiatePayment($amount, $currency = 'XOF', $description = '', 
                                  $customerEmail = '', $customerPhone = '') {
        $token = $this->getAccessToken();
        
        $payload = [
            'amount' => $amount,
            'currency' => $currency,
            'description' => $description,
            'customer_email' => $customerEmail,
            'customer_phone' => $customerPhone,
            'return_url' => 'https://votre-site.com/payment/success',
            'cancel_url' => 'https://votre-site.com/payment/cancel'
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->apiBaseUrl . '/api/payments/initiate/');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json'
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        return json_decode($response, true);
    }
}

// Exemple d'utilisation
$api = new MovapayAPI();
$result = $api->initiatePayment(5000, 'XOF', 'Paiement pour commande #12345', 
                               'client@example.com', '+2250701234567');
print_r($result);

?>
# 1. Obtenir le token d'accès
curl -X POST https://api.movapay.com/oauth/token/ \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=your_client_id&client_secret=your_client_secret"

# 2. Initier un paiement (remplacez YOUR_ACCESS_TOKEN par le token obtenu)
curl -X POST https://api.movapay.com/api/payments/initiate/ \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 5000,
    "currency": "XOF",
    "description": "Paiement pour commande #12345",
    "customer_email": "client@example.com",
    "customer_phone": "+2250701234567",
    "return_url": "https://votre-site.com/payment/success",
    "cancel_url": "https://votre-site.com/payment/cancel"
  }'

📋 Exemple de Réponse

{
  "success": true,
  "transaction_ref": "TXN_20241201_001",
  "payment_url": "https://pay.movapay.com/payment/TXN_20241201_001",
  "amount": 5000,
  "currency": "XOF",
  "status": "pending",
  "expires_at": "2024-12-01T23:59:59Z"
}

🔍 Vérifier le Statut d'un Paiement

GET /api/payments/status/{transaction_ref}/

Récupère le statut actuel d'une transaction de paiement.

import requests
import json

# Configuration
API_BASE_URL = "https://api.movapay.com"
CLIENT_ID = "your_client_id"
CLIENT_SECRET = "your_client_secret"

# Obtenir le token d'accès
def get_access_token():
    token_url = f"{API_BASE_URL}/oauth/token/"
    data = {
        "grant_type": "client_credentials",
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET
    }
    response = requests.post(token_url, data=data)
    return response.json()["access_token"]

# Vérifier le statut d'un paiement
def check_payment_status(transaction_ref):
    token = get_access_token()
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    response = requests.get(
        f"{API_BASE_URL}/api/payments/status/{transaction_ref}/",
        headers=headers
    )
    
    return response.json()

# Exemple d'utilisation
if __name__ == "__main__":
    result = check_payment_status("TXN_20241201_001")
    print(json.dumps(result, indent=2))
const axios = require('axios');

// Configuration
const API_BASE_URL = 'https://api.movapay.com';
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';

// Obtenir le token d'accès
async function getAccessToken() {
    try {
        const response = await axios.post(`${API_BASE_URL}/oauth/token/`, {
            grant_type: 'client_credentials',
            client_id: CLIENT_ID,
            client_secret: CLIENT_SECRET
        });
        return response.data.access_token;
    } catch (error) {
        console.error('Erreur lors de l\'obtention du token:', error);
        throw error;
    }
}

// Vérifier le statut d'un paiement
async function checkPaymentStatus(transactionRef) {
    try {
        const token = await getAccessToken();
        
        const response = await axios.get(`${API_BASE_URL}/api/payments/status/${transactionRef}/`, {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            }
        });
        
        return response.data;
    } catch (error) {
        console.error('Erreur lors de la vérification du statut:', error);
        throw error;
    }
}

// Exemple d'utilisation
checkPaymentStatus('TXN_20241201_001')
    .then(result => console.log(result))
    .catch(error => console.error(error));
// Configuration
const API_BASE_URL = 'https://api.movapay.com';
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';

// Obtenir le token d'accès
async function getAccessToken() {
    try {
        const response = await fetch(`${API_BASE_URL}/oauth/token/`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                grant_type: 'client_credentials',
                client_id: CLIENT_ID,
                client_secret: CLIENT_SECRET
            })
        });
        
        const data = await response.json();
        return data.access_token;
    } catch (error) {
        console.error('Erreur lors de l\'obtention du token:', error);
        throw error;
    }
}

// Vérifier le statut d'un paiement
async function checkPaymentStatus(transactionRef) {
    try {
        const token = await getAccessToken();
        
        const response = await fetch(`${API_BASE_URL}/api/payments/status/${transactionRef}/`, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            }
        });
        
        return await response.json();
    } catch (error) {
        console.error('Erreur lors de la vérification du statut:', error);
        throw error;
    }
}

// Exemple d'utilisation
checkPaymentStatus('TXN_20241201_001')
    .then(result => console.log(result))
    .catch(error => console.error(error));
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MovapayAPI {
    private static final String API_BASE_URL = "https://api.movapay.com";
    private static final String CLIENT_ID = "your_client_id";
    private static final String CLIENT_SECRET = "your_client_secret";
    private static final HttpClient client = HttpClient.newHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();
    
    // Obtenir le token d'accès
    public static String getAccessToken() throws Exception {
        String tokenUrl = API_BASE_URL + "/oauth/token/";
        
        String formData = String.format("grant_type=client_credentials&client_id=%s&client_secret=%s",
                CLIENT_ID, CLIENT_SECRET);
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(tokenUrl))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(formData))
                .build();
        
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        Map result = mapper.readValue(response.body(), Map.class);
        return (String) result.get("access_token");
    }
    
    // Vérifier le statut d'un paiement
    public static Map checkPaymentStatus(String transactionRef) throws Exception {
        String token = getAccessToken();
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(API_BASE_URL + "/api/payments/status/" + transactionRef + "/"))
                .header("Authorization", "Bearer " + token)
                .header("Content-Type", "application/json")
                .GET()
                .build();
        
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return mapper.readValue(response.body(), Map.class);
    }
    
    public static void main(String[] args) {
        try {
            Map result = checkPaymentStatus("TXN_20241201_001");
            System.out.println(mapper.writeValueAsString(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
<?php

class MovapayAPI {
    private $apiBaseUrl = 'https://api.movapay.com';
    private $clientId = 'your_client_id';
    private $clientSecret = 'your_client_secret';
    
    // Obtenir le token d'accès
    public function getAccessToken() {
        $tokenUrl = $this->apiBaseUrl . '/oauth/token/';
        
        $postData = http_build_query([
            'grant_type' => 'client_credentials',
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret
        ]);
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $tokenUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/x-www-form-urlencoded'
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        $data = json_decode($response, true);
        return $data['access_token'];
    }
    
    // Vérifier le statut d'un paiement
    public function checkPaymentStatus($transactionRef) {
        $token = $this->getAccessToken();
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->apiBaseUrl . '/api/payments/status/' . $transactionRef . '/');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json'
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        return json_decode($response, true);
    }
}

// Exemple d'utilisation
$api = new MovapayAPI();
$result = $api->checkPaymentStatus('TXN_20241201_001');
print_r($result);

?>
# 1. Obtenir le token d'accès
curl -X POST https://api.movapay.com/oauth/token/ \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=your_client_id&client_secret=your_client_secret"

# 2. Vérifier le statut (remplacez YOUR_ACCESS_TOKEN par le token obtenu)
curl -X GET https://api.movapay.com/api/payments/status/TXN_20241201_001/ \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json"

📋 Exemple de Réponse

{
  "success": true,
  "transaction_ref": "TXN_20241201_001",
  "status": "success",
  "amount": 5000,
  "currency": "XOF",
  "payment_method": "wave_ci",
  "customer_email": "client@example.com",
  "customer_phone": "+2250701234567",
  "created_at": "2024-12-01T10:30:00Z",
  "paid_at": "2024-12-01T10:35:00Z",
  "receipt_identifier": "REC_20241201_001"
}

🔔 Webhook/Callback

POST {votre_url_webhook}

Endpoint pour recevoir les notifications de paiement en temps réel.

from flask import Flask, request, jsonify
import json
import hmac
import hashlib

app = Flask(__name__)

# Clé secrète pour vérifier la signature (à configurer)
WEBHOOK_SECRET = "your_webhook_secret"

def verify_signature(payload, signature):
    """Vérifie la signature du webhook"""
    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected_signature)

@app.route('/webhook/payment', methods=['POST'])
def payment_webhook():
    try:
        # Récupérer les données
        payload = request.get_data(as_text=True)
        signature = request.headers.get('X-Movapay-Signature', '')
        
        # Vérifier la signature
        if not verify_signature(payload, signature):
            return jsonify({'error': 'Signature invalide'}), 401
        
        # Parser les données
        data = json.loads(payload)
        
        # Traiter la notification
        transaction_ref = data.get('transaction_ref')
        status = data.get('status')
        amount = data.get('amount')
        
        print(f"Notification reçue: {transaction_ref} - {status} - {amount}")
        
        # Logique métier selon le statut
        if status == 'success':
            # Paiement réussi
            print(f"Paiement réussi pour {transaction_ref}")
            # Mettre à jour votre base de données
            # Envoyer un email de confirmation
            # etc.
        elif status == 'failed':
            # Paiement échoué
            print(f"Paiement échoué pour {transaction_ref}")
            # Gérer l'échec
            # Notifier le client
            # etc.
        
        # Répondre avec succès
        return jsonify({'status': 'received'}), 200
        
    except Exception as e:
        print(f"Erreur lors du traitement du webhook: {e}")
        return jsonify({'error': 'Erreur interne'}), 500

if __name__ == '__main__':
    app.run(debug=True, port=5000)
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
import json
import hmac
import hashlib

# Clé secrète pour vérifier la signature
WEBHOOK_SECRET = "your_webhook_secret"

def verify_signature(payload, signature):
    """Vérifie la signature du webhook"""
    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected_signature)

@csrf_exempt
@require_http_methods(["POST"])
def payment_webhook(request):
    try:
        # Récupérer les données
        payload = request.body.decode('utf-8')
        signature = request.headers.get('X-Movapay-Signature', '')
        
        # Vérifier la signature
        if not verify_signature(payload, signature):
            return JsonResponse({'error': 'Signature invalide'}, status=401)
        
        # Parser les données
        data = json.loads(payload)
        
        # Traiter la notification
        transaction_ref = data.get('transaction_ref')
        status = data.get('status')
        amount = data.get('amount')
        
        print(f"Notification reçue: {transaction_ref} - {status} - {amount}")
        
        # Logique métier selon le statut
        if status == 'success':
            # Paiement réussi
            print(f"Paiement réussi pour {transaction_ref}")
            # Mettre à jour votre base de données
            # Envoyer un email de confirmation
            # etc.
        elif status == 'failed':
            # Paiement échoué
            print(f"Paiement échoué pour {transaction_ref}")
            # Gérer l'échec
            # Notifier le client
            # etc.
        
        # Répondre avec succès
        return JsonResponse({'status': 'received'}, status=200)
        
    except Exception as e:
        print(f"Erreur lors du traitement du webhook: {e}")
        return JsonResponse({'error': 'Erreur interne'}, status=500)
const express = require('express');
const crypto = require('crypto');
const app = express();

// Middleware pour parser le JSON
app.use(express.json({ verify: (req, res, buf) => {
    req.rawBody = buf;
}}));

// Clé secrète pour vérifier la signature
const WEBHOOK_SECRET = 'your_webhook_secret';

function verifySignature(payload, signature) {
    const expectedSignature = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(payload)
        .digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedSignature)
    );
}

app.post('/webhook/payment', (req, res) => {
    try {
        const payload = req.rawBody.toString();
        const signature = req.headers['x-movapay-signature'] || '';
        
        // Vérifier la signature
        if (!verifySignature(payload, signature)) {
            return res.status(401).json({ error: 'Signature invalide' });
        }
        
        // Les données sont déjà parsées dans req.body
        const data = req.body;
        
        // Traiter la notification
        const transactionRef = data.transaction_ref;
        const status = data.status;
        const amount = data.amount;
        
        console.log(`Notification reçue: ${transactionRef} - ${status} - ${amount}`);
        
        // Logique métier selon le statut
        if (status === 'success') {
            // Paiement réussi
            console.log(`Paiement réussi pour ${transactionRef}`);
            // Mettre à jour votre base de données
            // Envoyer un email de confirmation
            // etc.
        } else if (status === 'failed') {
            // Paiement échoué
            console.log(`Paiement échoué pour ${transactionRef}`);
            // Gérer l'échec
            // Notifier le client
            // etc.
        }
        
        // Répondre avec succès
        res.status(200).json({ status: 'received' });
        
    } catch (error) {
        console.error('Erreur lors du traitement du webhook:', error);
        res.status(500).json({ error: 'Erreur interne' });
    }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Serveur webhook démarré sur le port ${PORT}`);
});
<?php

// Clé secrète pour vérifier la signature
$WEBHOOK_SECRET = 'your_webhook_secret';

// Récupérer les données
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_MOVAPAY_SIGNATURE'] ?? '';

// Vérifier la signature
function verifySignature($payload, $signature, $secret) {
    $expectedSignature = hash_hmac('sha256', $payload, $secret);
    return hash_equals($expectedSignature, $signature);
}

if (!verifySignature($payload, $signature, $WEBHOOK_SECRET)) {
    http_response_code(401);
    echo json_encode(['error' => 'Signature invalide']);
    exit;
}

// Parser les données
$data = json_decode($payload, true);

// Traiter la notification
$transactionRef = $data['transaction_ref'] ?? '';
$status = $data['status'] ?? '';
$amount = $data['amount'] ?? '';

error_log("Notification reçue: $transactionRef - $status - $amount");

// Logique métier selon le statut
if ($status === 'success') {
    // Paiement réussi
    error_log("Paiement réussi pour $transactionRef");
    // Mettre à jour votre base de données
    // Envoyer un email de confirmation
    // etc.
} elseif ($status === 'failed') {
    // Paiement échoué
    error_log("Paiement échoué pour $transactionRef");
    // Gérer l'échec
    // Notifier le client
    // etc.
}

// Répondre avec succès
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['status' => 'received']);

?>
<?php
/*
Plugin Name: Movapay Webhook Handler
Description: Gère les webhooks de paiement Movapay
Version: 1.0
*/

// Clé secrète pour vérifier la signature
define('MOVAPAY_WEBHOOK_SECRET', 'your_webhook_secret');

// Ajouter l'action pour le webhook
add_action('init', 'handle_movapay_webhook');

function handle_movapay_webhook() {
    // Vérifier si c'est notre endpoint webhook
    if ($_SERVER['REQUEST_URI'] === '/wp-json/movapay/webhook') {
        // Vérifier la méthode HTTP
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            http_response_code(405);
            exit;
        }
        
        // Récupérer les données
        $payload = file_get_contents('php://input');
        $signature = $_SERVER['HTTP_X_MOVAPAY_SIGNATURE'] ?? '';
        
        // Vérifier la signature
        if (!verify_movapay_signature($payload, $signature)) {
            http_response_code(401);
            echo json_encode(['error' => 'Signature invalide']);
            exit;
        }
        
        // Parser les données
        $data = json_decode($payload, true);
        
        // Traiter la notification
        $transactionRef = $data['transaction_ref'] ?? '';
        $status = $data['status'] ?? '';
        $amount = $data['amount'] ?? '';
        
        error_log("Movapay webhook: $transactionRef - $status - $amount");
        
        // Logique métier selon le statut
        if ($status === 'success') {
            // Paiement réussi
            handle_successful_payment($data);
        } elseif ($status === 'failed') {
            // Paiement échoué
            handle_failed_payment($data);
        }
        
        // Répondre avec succès
        http_response_code(200);
        header('Content-Type: application/json');
        echo json_encode(['status' => 'received']);
        exit;
    }
}

function verify_movapay_signature($payload, $signature) {
    $expectedSignature = hash_hmac('sha256', $payload, MOVAPAY_WEBHOOK_SECRET);
    return hash_equals($expectedSignature, $signature);
}

function handle_successful_payment($data) {
    // Exemple: Mettre à jour une commande WooCommerce
    $transactionRef = $data['transaction_ref'];
    
    // Chercher la commande par référence
    $orders = wc_get_orders([
        'meta_key' => '_movapay_transaction_ref',
        'meta_value' => $transactionRef,
        'limit' => 1
    ]);
    
    if (!empty($orders)) {
        $order = $orders[0];
        $order->update_status('completed', 'Paiement Movapay confirmé');
        $order->add_order_note("Paiement Movapay réussi: $transactionRef");
    }
    
    // Envoyer un email de confirmation
    $customerEmail = $data['customer_email'] ?? '';
    if ($customerEmail) {
        wp_mail($customerEmail, 'Paiement confirmé', 'Votre paiement a été confirmé avec succès.');
    }
}

function handle_failed_payment($data) {
    $transactionRef = $data['transaction_ref'];
    error_log("Paiement échoué: $transactionRef");
    
    // Notifier l'administrateur
    wp_mail(get_option('admin_email'), 'Paiement échoué', "Paiement échoué pour: $transactionRef");
}

// Ajouter l'endpoint REST API
add_action('rest_api_init', function () {
    register_rest_route('movapay/v1', '/webhook', [
        'methods' => 'POST',
        'callback' => 'handle_movapay_webhook',
        'permission_callback' => '__return_true'
    ]);
});
?>

📋 Exemple de Données Webhook

{
  "transaction_ref": "TXN_20241201_001",
  "status": "success",
  "amount": "5000",
  "amount_with_fees": "5000",
  "currency": "XOF",
  "provider_reference": "WAVE_123456789",
  "receipt_identifier": "REC_20241201_001",
  "payment_method": "wave_ci",
  "customer_phone": "+2250701234567",
  "customer_email": "client@example.com",
  "created_at": "2024-12-01T10:30:00Z",
  "integration_id": "INT_001"
}

📚 Informations Supplémentaires

Codes d'erreur, bonnes pratiques et exemples d'intégration.

🚨 Codes d'Erreur

{
  "error": "400 Bad Request",
  "message": "Données invalides",
  "details": {
    "amount": ["Ce champ est requis"],
    "currency": ["Devise non supportée"]
  }
}

{
  "error": "401 Unauthorized",
  "message": "Token d'accès invalide ou expiré"
}

{
  "error": "403 Forbidden",
  "message": "Accès refusé - Vérifiez vos permissions"
}

{
  "error": "404 Not Found",
  "message": "Transaction non trouvée"
}

{
  "error": "429 Too Many Requests",
  "message": "Limite de requêtes dépassée"
}

{
  "error": "500 Internal Server Error",
  "message": "Erreur interne du serveur"
}

✅ Bonnes Pratiques

  • Sécurité : Stockez toujours vos identifiants de manière sécurisée (variables d'environnement)
  • Gestion d'erreurs : Implémentez une gestion robuste des erreurs dans votre application
  • Webhooks : Configurez votre URL de callback et testez-la en mode développement
  • Vérification : Vérifiez toujours la signature des webhooks pour éviter les attaques
  • Logs : Gardez des logs détaillés de toutes les transactions
  • Test : Testez d'abord en mode sandbox avant de passer en production
  • Montants : Utilisez des types décimaux pour les calculs financiers
  • Idempotence : Gérez les doublons de webhooks

🔗 Exemples d'Intégration

E-commerce (WooCommerce)

// Dans functions.php de votre thème WordPress
add_action('woocommerce_payment_complete', 'handle_movapay_payment');

function handle_movapay_payment($order_id) {
    $order = wc_get_order($order_id);
    $payment_method = $order->get_payment_method();
    
    if ($payment_method === 'movapay') {
        // Traitement spécifique Movapay
        $transaction_ref = get_post_meta($order_id, '_movapay_transaction_ref', true);
        
        // Vérifier le statut du paiement
        $status = check_movapay_payment_status($transaction_ref);
        
        if ($status === 'success') {
            $order->update_status('completed', 'Paiement Movapay confirmé');
        }
    }
}

Application Mobile (React Native)

import axios from 'axios';

const MovapayAPI = {
    baseURL: 'https://api.movapay.com',
    
    async initiatePayment(amount, description) {
        try {
            const token = await this.getAccessToken();
            const response = await axios.post(
                `${this.baseURL}/api/payments/initiate/`,
                {
                    amount: amount,
                    description: description,
                    return_url: 'myapp://payment/success',
                    cancel_url: 'myapp://payment/cancel'
                },
                {
                    headers: {
                        'Authorization': `Bearer ${token}`,
                        'Content-Type': 'application/json'
                    }
                }
            );
            
            // Ouvrir l'URL de paiement dans le navigateur
            Linking.openURL(response.data.payment_url);
            
            return response.data;
        } catch (error) {
            console.error('Erreur paiement:', error);
            throw error;
        }
    }
};

Application Web (Vue.js)

<template>
  <div>
    <button @click="initiatePayment" :disabled="loading">
      {{ loading ? 'Chargement...' : 'Payer' }}
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      loading: false
    }
  },
  methods: {
    async initiatePayment() {
      this.loading = true;
      try {
        const response = await this.$http.post('/api/payments/initiate/', {
          amount: 5000,
          description: 'Commande #12345',
          return_url: window.location.origin + '/payment/success',
          cancel_url: window.location.origin + '/payment/cancel'
        });
        
        // Rediriger vers la page de paiement
        window.location.href = response.data.payment_url;
      } catch (error) {
        console.error('Erreur:', error);
        this.$toast.error('Erreur lors de l\'initiation du paiement');
      } finally {
        this.loading = false;
      }
    }
  }
}
</script>

🆘 Support

Besoin d'aide pour intégrer l'API Movapay ?

  • Documentation : Consultez notre documentation complète
  • Support technique : Contactez-nous à support@movapay.com
  • Communauté : Rejoignez notre communauté de développeurs
  • Status API : Vérifiez le statut de nos services