← Tous les guides

🚀 Guide d'IntĂ©gration Partner API - SAGA ID

Vue d'ensemble

Ce guide vous explique comment envoyer des requĂȘtes Ă  l'API Partner de SAGA ID pour vĂ©rifier l'identitĂ© d'un citoyen, incluant le chiffrement des donnĂ©es pour une sĂ©curitĂ© maximale.


📋 PrĂ©requis

Avant de commencer, vous devez avoir :

  1. ✅ Client ID : Identifiant unique de votre application (UUID)
  2. ✅ Client Secret : ClĂ© secrĂšte pour l'authentification
  3. ✅ App Key : ClĂ© unique pour le chiffrement des donnĂ©es (64 caractĂšres)

Obtention : Ces identifiants sont fournis par l'équipe SAGA ID lors de l'approbation de votre application.


🔐 Authentification

L'API utilise HTTP Basic Authentication.

Format du Header

Authorization: Basic {base64(client_id:client_secret)}

Exemples par langage

PHP

$clientId = 'votre-client-id-uuid';
$clientSecret = 'votre-client-secret';

$authHeader = 'Basic ' . base64_encode($clientId . ':' . $clientSecret);

// Utilisation avec cURL
$headers = [
    'Authorization: ' . $authHeader,
    'Content-Type: application/json',
    'Accept: application/json',
];

JavaScript/Node.js

const clientId = 'votre-client-id-uuid';
const clientSecret = 'votre-client-secret';

const authHeader = 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

// Utilisation avec fetch
const headers = {
    'Authorization': authHeader,
    'Content-Type': 'application/json',
    'Accept': 'application/json',
};

Python

import base64

client_id = 'votre-client-id-uuid'
client_secret = 'votre-client-secret'

auth_string = f"{client_id}:{client_secret}"
auth_header = 'Basic ' + base64.b64encode(auth_string.encode()).decode()

# Utilisation avec requests
headers = {
    'Authorization': auth_header,
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

🔒 Chiffrement des DonnĂ©es (AES-256-CBC)

Étape 1 : DĂ©rivation de la ClĂ© de Chiffrement

La clé de chiffrement est unique à votre application et dérivée de vos credentials :

derivedKey = sha256(client_secret + app_key)[0:32 bytes]

Important : Prenez les 32 premiers octets du hash SHA-256, pas les 32 premiers caractÚres hexadécimaux.

Étape 2 : GĂ©nĂ©ration du Vecteur d'Initialisation (IV)

Un IV alĂ©atoire de 16 octets doit ĂȘtre gĂ©nĂ©rĂ© pour chaque requĂȘte.

Étape 3 : Chiffrement

Étape 4 : Format Final

IV_base64:ciphertext_base64

Exemple : aGVsbG8xMjM0NTY3ODk=:dGVzdGRhdGExMjM0NTY3ODk=


đŸ’» Exemples de Code Complet

PHP (Recommandé)

<?php

/**
 * Fonction de chiffrement AES-256-CBC
 */
function encryptPayload($data, $clientSecret, $appKey) {
    // 1. Dériver la clé (32 premiers octets du hash)
    $derivedKey = substr(hash('sha256', $clientSecret . $appKey, true), 0, 32);
    
    // 2. Générer un IV aléatoire (16 octets)
    $iv = openssl_random_pseudo_bytes(16);
    
    // 3. Convertir les données en JSON
    $jsonData = json_encode($data);
    
    // 4. Chiffrer avec AES-256-CBC
    $ciphertext = openssl_encrypt(
        $jsonData,
        'AES-256-CBC',
        $derivedKey,
        OPENSSL_RAW_DATA,
        $iv
    );
    
    if ($ciphertext === false) {
        throw new Exception('Échec du chiffrement');
    }
    
    // 5. Format : IV:ciphertext (en base64)
    return base64_encode($iv) . ':' . base64_encode($ciphertext);
}

/**
 * Fonction d'envoi de requĂȘte
 */
function verifyIdentity($citizenData) {
    $clientId = 'votre-client-id-uuid';
    $clientSecret = 'votre-client-secret';
    $appKey = 'votre-app-key-64-chars';
    $apiUrl = 'https://api.sagapass.ht/api/partner/v1/verify';
    
    // Données du citoyen
    $payload = [
        'first_name'    => $citizenData['first_name'],
        'last_name'     => $citizenData['last_name'],
        'date_of_birth' => $citizenData['date_of_birth'], // Format: YYYY-MM-DD
        'niu'           => $citizenData['niu'] ?? null,    // 10 chiffres
        'email'         => $citizenData['email'] ?? null,
        'phone'         => $citizenData['phone'] ?? null,  // Max 20 chars
        'reference'     => $citizenData['reference'] ?? null, // Votre référence interne
    ];
    
    // Chiffrer le payload
    $encryptedData = encryptPayload($payload, $clientSecret, $appKey);
    
    // PrĂ©parer la requĂȘte
    $requestData = [
        'encrypted' => true,
        'data'      => $encryptedData,
    ];
    
    // Headers d'authentification
    $headers = [
        'Authorization: Basic ' . base64_encode($clientId . ':' . $clientSecret),
        'Content-Type: application/json',
        'Accept: application/json',
    ];
    
    // Envoyer la requĂȘte
    $ch = curl_init($apiUrl);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestData));
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // Important en production
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return [
        'http_code' => $httpCode,
        'response'  => json_decode($response, true),
    ];
}

// Exemple d'utilisation
$result = verifyIdentity([
    'first_name'    => 'Jean',
    'last_name'     => 'Pierre',
    'date_of_birth' => '1990-05-15',
    'niu'           => '9005151234',
    'email'         => 'jean.pierre@example.ht',
    'phone'         => '+50937123456',
    'reference'     => 'ORDER_12345',
]);

if ($result['http_code'] === 200 && $result['response']['verified'] === true) {
    echo "✅ IdentitĂ© vĂ©rifiĂ©e !";
} else {
    echo "❌ VĂ©rification Ă©chouĂ©e : " . $result['response']['message'];
}

JavaScript/Node.js

const crypto = require('crypto');
const axios = require('axios');

/**
 * Fonction de chiffrement AES-256-CBC
 */
function encryptPayload(data, clientSecret, appKey) {
    // 1. Dériver la clé (32 premiers octets du hash)
    const hash = crypto.createHash('sha256').update(clientSecret + appKey).digest();
    const derivedKey = hash.slice(0, 32);
    
    // 2. Générer un IV aléatoire (16 octets)
    const iv = crypto.randomBytes(16);
    
    // 3. Convertir les données en JSON
    const jsonData = JSON.stringify(data);
    
    // 4. Chiffrer avec AES-256-CBC
    const cipher = crypto.createCipheriv('aes-256-cbc', derivedKey, iv);
    let encrypted = cipher.update(jsonData, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    
    // 5. Format : IV:ciphertext (en base64)
    const ivBase64 = iv.toString('base64');
    return `${ivBase64}:${encrypted}`;
}

/**
 * Fonction d'envoi de requĂȘte
 */
async function verifyIdentity(citizenData) {
    const clientId = 'votre-client-id-uuid';
    const clientSecret = 'votre-client-secret';
    const appKey = 'votre-app-key-64-chars';
    const apiUrl = 'https://api.sagapass.ht/api/partner/v1/verify';
    
    // Données du citoyen
    const payload = {
        first_name: citizenData.first_name,
        last_name: citizenData.last_name,
        date_of_birth: citizenData.date_of_birth, // Format: YYYY-MM-DD
        niu: citizenData.niu || null,             // 10 chiffres
        email: citizenData.email || null,
        phone: citizenData.phone || null,         // Max 20 chars
        reference: citizenData.reference || null, // Votre référence interne
    };
    
    // Chiffrer le payload
    const encryptedData = encryptPayload(payload, clientSecret, appKey);
    
    // PrĂ©parer la requĂȘte
    const requestData = {
        encrypted: true,
        data: encryptedData,
    };
    
    // Headers d'authentification
    const authHeader = 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
    
    try {
        const response = await axios.post(apiUrl, requestData, {
            headers: {
                'Authorization': authHeader,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
        });
        
        return {
            http_code: response.status,
            response: response.data,
        };
    } catch (error) {
        return {
            http_code: error.response?.status || 500,
            response: error.response?.data || { error: error.message },
        };
    }
}

// Exemple d'utilisation
(async () => {
    const result = await verifyIdentity({
        first_name: 'Jean',
        last_name: 'Pierre',
        date_of_birth: '1990-05-15',
        niu: '9005151234',
        email: 'jean.pierre@example.ht',
        phone: '+50937123456',
        reference: 'ORDER_12345',
    });
    
    if (result.http_code === 200 && result.response.verified === true) {
        console.log('✅ IdentitĂ© vĂ©rifiĂ©e !');
    } else {
        console.log('❌ VĂ©rification Ă©chouĂ©e :', result.response.message);
    }
})();

Python

import base64
import hashlib
import json
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
import requests

def encrypt_payload(data, client_secret, app_key):
    """
    Fonction de chiffrement AES-256-CBC
    """
    # 1. Dériver la clé (32 premiers octets du hash)
    hash_input = (client_secret + app_key).encode('utf-8')
    derived_key = hashlib.sha256(hash_input).digest()[:32]
    
    # 2. Générer un IV aléatoire (16 octets)
    iv = get_random_bytes(16)
    
    # 3. Convertir les données en JSON
    json_data = json.dumps(data).encode('utf-8')
    
    # 4. Chiffrer avec AES-256-CBC
    cipher = AES.new(derived_key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(pad(json_data, AES.block_size))
    
    # 5. Format : IV:ciphertext (en base64)
    iv_base64 = base64.b64encode(iv).decode('utf-8')
    ciphertext_base64 = base64.b64encode(ciphertext).decode('utf-8')
    
    return f"{iv_base64}:{ciphertext_base64}"

def verify_identity(citizen_data):
    """
    Fonction d'envoi de requĂȘte
    """
    client_id = 'votre-client-id-uuid'
    client_secret = 'votre-client-secret'
    app_key = 'votre-app-key-64-chars'
    api_url = 'https://api.sagapass.ht/api/partner/v1/verify'
    
    # Données du citoyen
    payload = {
        'first_name': citizen_data['first_name'],
        'last_name': citizen_data['last_name'],
        'date_of_birth': citizen_data['date_of_birth'],  # Format: YYYY-MM-DD
        'niu': citizen_data.get('niu'),                  # 10 chiffres
        'email': citizen_data.get('email'),
        'phone': citizen_data.get('phone'),              # Max 20 chars
        'reference': citizen_data.get('reference'),      # Votre référence interne
    }
    
    # Chiffrer le payload
    encrypted_data = encrypt_payload(payload, client_secret, app_key)
    
    # PrĂ©parer la requĂȘte
    request_data = {
        'encrypted': True,
        'data': encrypted_data,
    }
    
    # Headers d'authentification
    auth_string = f"{client_id}:{client_secret}"
    auth_header = 'Basic ' + base64.b64encode(auth_string.encode()).decode()
    
    headers = {
        'Authorization': auth_header,
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    }
    
    # Envoyer la requĂȘte
    try:
        response = requests.post(api_url, json=request_data, headers=headers)
        return {
            'http_code': response.status_code,
            'response': response.json(),
        }
    except Exception as e:
        return {
            'http_code': 500,
            'response': {'error': str(e)},
        }

# Exemple d'utilisation
if __name__ == '__main__':
    result = verify_identity({
        'first_name': 'Jean',
        'last_name': 'Pierre',
        'date_of_birth': '1990-05-15',
        'niu': '9005151234',
        'email': 'jean.pierre@example.ht',
        'phone': '+50937123456',
        'reference': 'ORDER_12345',
    })
    
    if result['http_code'] == 200 and result['response'].get('verified') == True:
        print('✅ IdentitĂ© vĂ©rifiĂ©e !')
    else:
        print(f"❌ VĂ©rification Ă©chouĂ©e : {result['response'].get('message')}")

đŸ“€ Structure de la RequĂȘte

Endpoint

POST https://api.sagapass.ht/api/partner/v1/verify

Headers

Authorization: Basic {base64(client_id:client_secret)}
Content-Type: application/json
Accept: application/json

Body (avec chiffrement)

{
  "encrypted": true,
  "data": "aGVsbG8xMjM0NTY3ODk=:dGVzdGRhdGExMjM0NTY3ODk="
}

Body (sans chiffrement - DEV UNIQUEMENT)

{
  "encrypted": false,
  "first_name": "Jean",
  "last_name": "Pierre",
  "date_of_birth": "1990-05-15",
  "niu": "9005151234",
  "email": "jean.pierre@example.ht",
  "phone": "+50937123456",
  "reference": "ORDER_12345"
}

đŸ“„ RĂ©ponses Possibles

SuccÚs - Compte Vérifié (HTTP 200)

{
  "success": true,
  "verified": true,
  "status": "verified",
  "citizen_id": 123,
  "message": "Identité vérifiée avec succÚs.",
  "data": {
    "full_name": "Jean Pierre",
    "verification_date": "2026-03-15T10:30:00Z"
  }
}

SuccÚs - Compte Non Vérifié (HTTP 200)

{
  "success": true,
  "verified": false,
  "status": "pending_verification",
  "citizen_id": 456,
  "message": "Ce citoyen a un compte Sagapass, mais la vérification d'identité n'est pas encore complÚte.",
  "action": "Invitez le citoyen à ouvrir l'application Sagapass pour finaliser sa vérification.",
  "app_links": {
    "android": "https://play.google.com/store/apps/details?id=ht.sagapass",
    "ios": "https://apps.apple.com/app/sagapass"
  }
}

Compte Rejeté (HTTP 403)

{
  "success": false,
  "verified": false,
  "error": "verification_rejected",
  "status": "rejected",
  "citizen_id": 789,
  "message": "Ce compte a été rejeté lors de la vérification d'identité.",
  "action": "L'utilisateur doit se connecter sur Sagapass avec son email pour corriger ses informations ou soumettre à nouveau ses documents de vérification.",
  "app_links": {
    "android": "https://play.google.com/store/apps/details?id=ht.sagapass",
    "ios": "https://apps.apple.com/app/sagapass"
  }
}

Erreur - Authentification Invalide (HTTP 401)

{
  "success": false,
  "error": "invalid_client",
  "message": "Authentification invalide. Vérifiez votre client_id et client_secret."
}

Erreur - NIU en Double (HTTP 409)

{
  "success": false,
  "error": "duplicate_niu",
  "message": "Ce NIU est déjà utilisé par un autre citoyen.",
  "conflict": {
    "field": "niu",
    "value": "9005151234"
  }
}

📖 Voir toutes les rĂ©ponses : PARTNER_API_RESPONSES.md


✅ Checklist d'IntĂ©gration

Phase 1 : Préparation

Phase 2 : Développement

Phase 3 : Tests

Phase 4 : Production


🔒 Bonnes Pratiques de SĂ©curitĂ©

1. Stockage des Credentials

❌ NE JAMAIS :

✅ À FAIRE :

2. Chiffrement

✅ Obligatoire en production :

3. Communication

✅ Toujours utiliser HTTPS :

4. Validation

✅ Valider les donnĂ©es avant envoi :


🐛 DĂ©pannage

Erreur : "Impossible de décrypter les données"

Causes possibles :

Solution :

// Vérifier la dérivation de clé
$derivedKey = substr(hash('sha256', $clientSecret . $appKey, true), 0, 32);
echo bin2hex($derivedKey); // Afficher pour debug

Erreur : "invalid_client" (HTTP 401)

Causes possibles :

Solution :

// Vérifier le header
$header = base64_encode($clientId . ':' . $clientSecret);
echo $header; // Doit ĂȘtre une chaĂźne base64 valide

Erreur : Timeout

Solution :

// Augmenter le timeout
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30 secondes
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10 secondes pour connexion

📞 Support

Documentation

Contact

📧 Email : support@sagapass.ht
🌐 Portal : https://developer.sagapass.ht
📚 Docs : https://docs.sagapass.ht


📝 Exemple Complet d'IntĂ©gration

Voici un exemple complet d'intégration dans une application e-commerce :

<?php

class SagapassVerification {
    private $clientId;
    private $clientSecret;
    private $appKey;
    private $apiUrl = 'https://api.sagapass.ht/api/partner/v1/verify';
    
    public function __construct($clientId, $clientSecret, $appKey) {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->appKey = $appKey;
    }
    
    private function encryptPayload($data) {
        $derivedKey = substr(hash('sha256', $this->clientSecret . $this->appKey, true), 0, 32);
        $iv = openssl_random_pseudo_bytes(16);
        $jsonData = json_encode($data);
        $ciphertext = openssl_encrypt($jsonData, 'AES-256-CBC', $derivedKey, OPENSSL_RAW_DATA, $iv);
        
        if ($ciphertext === false) {
            throw new Exception('Échec du chiffrement');
        }
        
        return base64_encode($iv) . ':' . base64_encode($ciphertext);
    }
    
    public function verifyCustomer($customerData) {
        $payload = [
            'first_name'    => $customerData['first_name'],
            'last_name'     => $customerData['last_name'],
            'date_of_birth' => $customerData['date_of_birth'],
            'niu'           => $customerData['niu'] ?? null,
            'email'         => $customerData['email'] ?? null,
            'phone'         => $customerData['phone'] ?? null,
            'reference'     => 'ORDER_' . $customerData['order_id'],
        ];
        
        $encryptedData = $this->encryptPayload($payload);
        
        $requestData = [
            'encrypted' => true,
            'data'      => $encryptedData,
        ];
        
        $headers = [
            'Authorization: Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret),
            'Content-Type: application/json',
            'Accept: application/json',
        ];
        
        $ch = curl_init($this->apiUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            throw new Exception("Erreur cURL : $error");
        }
        
        return [
            'http_code' => $httpCode,
            'response'  => json_decode($response, true),
        ];
    }
    
    public function processVerificationResult($result) {
        $httpCode = $result['http_code'];
        $data = $result['response'];
        
        switch ($httpCode) {
            case 200:
                if ($data['verified'] === true) {
                    return [
                        'status' => 'approved',
                        'message' => 'Identité vérifiée. Transaction autorisée.',
                    ];
                } else {
                    return [
                        'status' => 'pending',
                        'message' => 'Identité non vérifiée. Invitez le client à compléter sa vérification Sagapass.',
                        'action' => $data['action'] ?? null,
                        'app_links' => $data['app_links'] ?? null,
                    ];
                }
            
            case 403:
                return [
                    'status' => 'rejected',
                    'message' => 'Compte rejeté. Le client doit corriger ses informations sur Sagapass.',
                    'action' => $data['action'] ?? null,
                ];
            
            case 409:
                return [
                    'status' => 'conflict',
                    'message' => 'Données en conflit (NIU ou téléphone déjà utilisé).',
                    'error' => $data['error'] ?? null,
                ];
            
            case 401:
            case 422:
            default:
                return [
                    'status' => 'error',
                    'message' => $data['message'] ?? 'Erreur lors de la vérification',
                ];
        }
    }
}

// Utilisation
$sagapass = new SagapassVerification(
    getenv('SAGAPASS_CLIENT_ID'),
    getenv('SAGAPASS_CLIENT_SECRET'),
    getenv('SAGAPASS_APP_KEY')
);

try {
    $result = $sagapass->verifyCustomer([
        'first_name' => 'Jean',
        'last_name' => 'Pierre',
        'date_of_birth' => '1990-05-15',
        'niu' => '9005151234',
        'email' => 'jean.pierre@example.ht',
        'phone' => '+50937123456',
        'order_id' => '12345',
    ]);
    
    $decision = $sagapass->processVerificationResult($result);
    
    if ($decision['status'] === 'approved') {
        // Autoriser la transaction
        echo "✅ Transaction autorisĂ©e !";
    } else {
        // Refuser ou mettre en attente
        echo "⏳ " . $decision['message'];
    }
    
} catch (Exception $e) {
    echo "❌ Erreur : " . $e->getMessage();
}

DerniĂšre mise Ă  jour : 30 mars 2026
Version API : v1
Auteur : Équipe SAGA ID