Réconciliation
Comment garantir que votre base et SOAMTAR restent toujours synchrones.
Trois lignes de défense
SOAMTAR utilise trois mécanismes complémentaires pour la cohérence :
- Webhooks (push, primaire) : événement émis dès qu'un statut change
- Endpoint verify (pull, à la demande) : vous interrogez
POST/v1/payments/{id}/verify - Cron de réconciliation interne (pull, automatique) : toutes les 5 min, SOAMTAR revérifie auprès d'Orange Money les transactions bloquées et émet les webhooks manqués
Vous, marchand, devez gérer la 4ème ligne : votre propre cron quotidien.
Cron quotidien recommandé
// Tous les jours à 03:00 (heure de Freetown, GMT)
class ReconcileSoamtarTransactions extends Command
{
public function handle()
{
// Pour chaque transaction de votre DB encore en pending depuis > 30 min
$stuck = Order::where('payment_status', 'pending')
->where('created_at', '<', now()->subMinutes(30))
->whereNotNull('soamtar_id')
->get();
foreach ($stuck as $order) {
$response = Http::withToken(env('SOAMTAR_SECRET_KEY'))
->post("https://app.so-amtar.com/v1/payments/{$order->soamtar_id}/verify");
$status = $response->json('data.status');
// Synchroniser avec votre DB
$order->update(['payment_status' => $status]);
// Si succès, déclencher vos workflows métier (livraison, etc.)
if ($status === 'success' && $order->wasChanged('payment_status')) {
FulfillOrder::dispatch($order);
}
}
}
}Audit hebdomadaire
Une fois par semaine, comparez les volumes :
-- Votre DB
SELECT DATE(paid_at) AS day, COUNT(*) AS count, SUM(amount) AS total_sle
FROM orders
WHERE payment_status = 'success' AND paid_at >= NOW() - INTERVAL '7 days'
GROUP BY DATE(paid_at);# SOAMTAR
curl 'https://app.so-amtar.com/v1/payments?status=success&created_from=2026-04-07&per_page=100' \
-H "Authorization: Bearer sk_live_xxx"Tout écart > 0.01 % nécessite investigation. Contactez support@so-amtar.com avec :
- Date de début / fin de la fenêtre
- Volume attendu vs. observé (en SLE)
- Liste des
merchant_refdivergents
Cas de désynchronisation typiques
| Cas | Cause | Solution |
|---|---|---|
| Webhook reçu mais pas traité côté votre serveur | Bug applicatif | Re-traiter via le dashboard "Resend" |
Transaction en pending chez vous, success chez SOAMTAR | Webhook perdu | POST/verify puis sync |
Transaction success chez vous, failed chez SOAMTAR | Race condition rare | Audit manuel + correction |
| Refund chez SOAMTAR, pas chez vous | Webhook refund manqué | GET/v1/refunds?transaction_id=... |
Webhook manqué : recovery automatique
Si vous savez que vous avez perdu des webhooks (incident interne, déploiement raté), forcez le replay :
# Pour toutes les transactions du jour
for ID in $(jq -r '.data[].id' < my-orders-today.json); do
curl -X POST "https://app.so-amtar.com/v1/payments/$ID/verify" \
-H "Authorization: Bearer sk_live_xxx"
doneverify n'a aucun effet si la transaction est déjà au bon statut côté SOAMTAR (idempotent). En revanche, il ré-émet le webhook vers votre endpoint si une transition vient d'être détectée.
Réconciliation comptable mensuelle
Comparez :
| Source | Champ |
|---|---|
| Vos commandes | SUM(amount) WHERE payment_status = 'success' |
| Dashboard SOAMTAR | Volume du mois (livemode = true) |
| Settlements reçus | SUM(net_amount) reversé sur votre compte bancaire |
| Refunds émis | SUM(amount) WHERE refund.status = 'success' |
L'égalité attendue : Volume - Refunds - Fees - Disputes_perdus = Settlements. Tous les montants sont en leones (SLE).