Date de mise à jour : 31 Mars 2026
Le système de vérification a été renforcé avec deux nouveaux cas de réponse. Toutes les intégrations existantes doivent être mises à jour.
| Code HTTP | Status | Ancien Comportement | Nouveau Comportement |
|-----------|--------|---------------------|----------------------|
| 202 | pending_user_confirmation | ❌ N'existait pas | ✅ NOUVEAU - User doit confirmer |
| 409 | data_not_matching | ❌ N'existait pas | ✅ NOUVEAU - Données ne correspondent pas |
AVANT (Ancien flow) :
Compte vérifié + NIU correspond
→ HTTP 200 + verified: true
→ ✅ Accès autorisé immédiatement
APRÈS (Nouveau flow) :
Compte vérifié + NIU correspond
→ HTTP 202 + pending_user_confirmation
→ ⏳ L'utilisateur DOIT confirmer dans SagaPass
→ ⏳ Le partenaire attend la confirmation
Même si le compte est vérifié et que les données correspondent exactement, le système demande maintenant à l'utilisateur de confirmer activement que c'est bien lui qui a demandé cette vérification.
🔒 Sécurité renforcée : Prévenir l'usurpation d'identité
Code HTTP : 202 Accepted (En attente d'approbation)
{
"success": false,
"verified": false,
"error": "user_confirmation_required",
"status": "pending_user_confirmation",
"message": "L'utilisateur a été notifié et doit confirmer cette demande de vérification pour des raisons de sécurité.",
"action_required": "L'utilisateur doit ouvrir son application SagaPass et confirmer qu'il est bien à l'origine de cette demande.",
"challenge_id": 123,
"expires_at": "2026-04-02T15:30:00Z"
}
| Champ | Type | Description |
|-------|------|-------------|
| error | string | "user_confirmation_required" |
| status | string | "pending_user_confirmation" |
| challenge_id | integer | ID du challenge de confirmation |
| expires_at | string | Date d'expiration (48h) |
| action_required | string | Instructions pour l'utilisateur |
❌ NE PAS autoriser l'accès immédiatement
📱 Afficher un message à l'utilisateur :
"Veuillez ouvrir votre application SagaPass et confirmer cette demande."
⏳ Implémenter un système d'attente :
expires_at)🔄 Vérifier le statut ultérieurement :
/api/partner/v1/verify après confirmation// 1. Première requête de vérification
$response = callSagaPassAPI($citizenData);
if ($response['status'] === 'pending_user_confirmation') {
// 2. Afficher message à l'utilisateur
echo "Veuillez confirmer dans votre app SagaPass (Challenge #{$response['challenge_id']})";
// 3. Attendre confirmation (polling ou webhook)
$challengeId = $response['challenge_id'];
$expiresAt = $response['expires_at'];
// Option A: Polling
while (time() < strtotime($expiresAt)) {
sleep(15); // Attendre 15 secondes
$statusResponse = callSagaPassAPI($citizenData);
if ($statusResponse['status'] === 'verified') {
// ✅ Confirmation reçue
allowAccess();
break;
}
}
// Option B: Webhook (voir section Webhooks)
// Le système SagaPass appelle votre endpoint quand l'user confirme
}
Lorsqu'une variation de nom est détectée (ex: "Sebastian" vs "Sebastien"), le système :
🔒 Sécurité : Ne jamais exposer les données officielles SagaPass
Code HTTP : 409 Conflict (Action utilisateur requise)
{
"success": false,
"verified": false,
"error": "data_mismatch",
"status": "data_not_matching",
"message": "Les informations fournies ne correspondent pas exactement aux données enregistrées. L'utilisateur a été notifié.",
"action_required": "Demandez à l'utilisateur de vérifier et corriger ses informations chez vous pour qu'elles correspondent à son compte SagaPass officiel.",
"challenge_id": 456,
"expires_at": "2026-04-02T15:30:00Z"
}
| Champ | Type | Description |
|-------|------|-------------|
| error | string | "data_mismatch" |
| status | string | "data_not_matching" |
| challenge_id | integer | ID du challenge |
| expires_at | string | Date d'expiration |
| action_required | string | Instructions |
AVANT :
{
"error": "name_verification_required",
"data": {
"sagapass_name": "Dary Sebastian Petion", // ❌ EXPOSÉ
"partner_name": "Dary Sebastien Petion"
}
}
APRÈS :
{
"error": "data_mismatch",
"message": "Les informations ne correspondent pas",
// ✅ AUCUNE donnée SagaPass exposée
}
❌ NE PAS autoriser l'accès
📱 Afficher un message à l'utilisateur :
"Les informations ne correspondent pas à votre compte SagaPass.
Veuillez vérifier et corriger votre nom dans votre profil."
✏️ Permettre à l'utilisateur de modifier ses informations :
🔄 Réessayer après correction :
if ($response['error'] === 'data_mismatch') {
echo "Vos informations ne correspondent pas.";
echo "Veuillez mettre à jour votre profil.";
// Rediriger vers formulaire de profil
redirectToProfileEdit();
}
| Scénario | HTTP | Réponse | Action |
|----------|------|---------|--------|
| Compte vérifié + Données OK | 200 | verified: true | ✅ Accès immédiat |
| Compte vérifié + Variation nom | 409 | name_verification_required + données exposées | ⚠️ Challenge |
| Compte non vérifié | 200 | pending_verification | ⏳ Attendre |
| Scénario | HTTP | Réponse | Action |
|----------|------|---------|--------|
| Compte vérifié + Données exactes | 202 | pending_user_confirmation | ⏳ User doit confirmer |
| Compte vérifié + Variation nom | 409 | data_not_matching (sans données) | ⚠️ User corrige chez partner |
| Compte non vérifié | 200 | pending_verification | ⏳ Attendre |
Ancien code :
if ($response['verified'] === true) {
allowAccess(); // ✅ Accès immédiat
}
Nouveau code :
if ($response['verified'] === true) {
allowAccess(); // ✅ OK
} elseif ($response['status'] === 'pending_user_confirmation') {
// 🆕 NOUVEAU CAS
showConfirmationPendingMessage($response['challenge_id']);
waitForUserConfirmation(); // Polling ou webhook
}
Ancien code :
if ($response['error'] === 'name_verification_required') {
// Afficher les deux noms
echo "SagaPass: " . $response['data']['sagapass_name'];
echo "Votre profil: " . $response['data']['partner_name'];
}
Nouveau code :
if ($response['error'] === 'data_mismatch') {
// 🆕 PLUS DE DONNÉES EXPOSÉES
echo "Les informations ne correspondent pas.";
echo "Veuillez corriger votre profil.";
redirectToProfileEdit();
}
function waitForUserConfirmation($citizenData, $expiresAt) {
$maxAttempts = 96; // 48h / 30 sec = 96 tentatives
$attempt = 0;
while ($attempt < $maxAttempts) {
sleep(30); // Attendre 30 secondes
$response = callSagaPassAPI($citizenData);
if ($response['verified'] === true) {
return true; // ✅ Confirmé
}
if ($response['status'] === 'rejected') {
return false; // ❌ Rejeté par user
}
$attempt++;
}
return null; // ⏱️ Timeout
}
[ ] Gérer HTTP 202 (pending_user_confirmation)
[ ] Gérer HTTP 409 (data_mismatch)
[ ] Retirer références aux anciennes réponses
name_verification_requiredsagapass_name / partner_name dans erreur[ ] Tester les nouveaux scénarios
Au lieu d'attendre avec polling (vérifier toutes les 15-30 secondes), vous pouvez maintenant recevoir des notifications instantanées via webhook quand l'utilisateur confirme ou rejette une demande de vérification.
⚠️ IMPORTANT : Les webhooks sont UNIQUEMENT envoyés pour les challenges de type "confirmation" (HTTP 202). Pour les challenges de type "data_mismatch" (HTTP 409), aucun webhook n'est envoyé car vous recevez déjà la réponse immédiatement.
| Scénario | Réponse API | Webhook ? | Pourquoi ? | |----------|-------------|-----------|------------| | Compte vérifié + données exactes | HTTP 202 (confirmation) | ✅ OUI | User doit confirmer dans SagaPass | | Variation de nom détectée | HTTP 409 (data_mismatch) | ❌ NON | User corrige chez le partenaire, pas d'attente | | Compte non vérifié | HTTP 200 (pending) | ❌ NON | Pas de challenge créé |
Pour les challenges "confirmation" (HTTP 202) uniquement :
| Méthode | Délai | Charge Serveur | UX | |---------|-------|----------------|-----| | Webhook | ⚡ 0-2 secondes | ✅ Minimal | ⭐⭐⭐⭐⭐ | | Polling | 🐌 15-30 secondes | ❌ Élevé | ⭐⭐ |
Pour les challenges "data_mismatch" (HTTP 409) : Pas de webhook nécessaire car réponse immédiate.
1. Ajoutez webhook_url lors de votre requête de vérification (optionnel) :
$payload = [
'encrypted' => true,
'data' => $encryptedData,
'webhook_url' => 'https://votresite.com/api/sagapass/webhook' // 🆕 NOUVEAU (optionnel)
];
⚠️ Note importante :
webhook_url est optionnel2. SagaPass enverra automatiquement une requête POST vers votre webhook :
POST /api/sagapass/webhook HTTP/1.1
Host: votresite.com
Content-Type: application/json
X-Saga-Signature: sha256=abc123def456...
X-Saga-Event: verification.confirmed
X-Saga-Challenge-Id: 9abc1234-5678-90ef
{
"event": "verification.confirmed",
"challenge_id": "9abc1234-5678-90ef",
"status": "confirmed",
"user": {
"citizen_id": 42,
"verified": true
},
"verification": {
"reference": "ORDER-12345",
"niu": "1234567890"
},
"timestamp": "2026-03-31T20:15:00Z"
}
| Événement | Description | Payload Inclut |
|-----------|-------------|----------------|
| verification.confirmed | User a confirmé | user.citizen_id, verified: true |
| verification.rejected | User a rejeté (sécurité) | user: null, raison |
| verification.expired | Timeout 48h | user: null |
⚠️ IMPORTANT : Toujours vérifier la signature pour éviter les webhooks forgés.
function verifyWebhookSignature($request) {
$signature = $request->header('X-Saga-Signature');
$payload = $request->getContent();
$secret = config('sagapass.app_key'); // Votre clé d'app
$expectedHash = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($signature, $expectedHash);
}
// routes/api.php
Route::post('/sagapass/webhook', [SagaPassWebhookController::class, 'handle']);
// Controller
class SagaPassWebhookController extends Controller
{
public function handle(Request $request)
{
// 1. Vérifier la signature
if (!$this->verifySignature($request)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
// 2. Récupérer l'événement
$event = $request->header('X-Saga-Event');
$payload = $request->all();
// 3. Traiter
switch ($event) {
case 'verification.confirmed':
$this->handleConfirmed($payload);
break;
case 'verification.rejected':
$this->handleRejected($payload);
break;
}
// 4. Répondre rapidement (< 5 secondes)
return response()->json(['success' => true], 200);
}
private function handleConfirmed($payload)
{
$reference = $payload['verification']['reference'];
$order = Order::where('reference', $reference)->first();
$order->update([
'verified' => true,
'citizen_id' => $payload['user']['citizen_id']
]);
// Déclencher actions métier (paiement, livraison, etc.)
}
}
SagaPass fait 3 tentatives de livraison :
| Code | Action SagaPass | |------|-----------------| | 200-299 | ✅ Succès, pas de retry | | 4xx | ❌ Erreur client, pas de retry | | 5xx | 🔁 Retry automatique (3x) |
📖 Voir : PARTNER_WEBHOOK_GUIDE.md pour :
En cas de questions sur la migration :
PARTNER_API_RESPONSES.mdPARTNER_API_INTEGRATION.md