SOLIMI - For Cashless World

API publique de collecte de paiement

Documentation d'intégration SOLIMI pour marchands, fintechs et banques

Ce guide explique comment connecter votre application à l'API publique SOLIMI pour initier des paiements directs, créer des sessions checkout hébergées, consulter leur statut, récupérer les moyens disponibles et recevoir les notifications webhook.

REST JSON HMAC-SHA256 Idempotence Webhooks sécurisés
Authentification Chaque requête est signée avec votre clé secrète API.
Devise Les transactions de collecte sont exprimées en XOF.
Canaux Mobile money, carte bancaire et carte virtuelle SOLIMI.

Environnements et prérequis

Avant l'intégration, l'équipe SOLIMI vous fournit un compte partenaire, une clé API, un secret API et configure les adresses IP autorisées. Le secret API est affiché une seule fois: conservez-le dans un coffre de secrets.

Environnement Base URL Usage
Sandbox https://sandbox-api.solimi.co Intégration partenaire avant mise en production.
Production https://api.solimi.co Transactions réelles après validation SOLIMI.
Les domaines sandbox et production ci-dessus sont les URLs de référence à confirmer avec SOLIMI lors de l'onboarding. Ne partagez jamais votre secret API dans un frontend, une application mobile ou un dépôt Git.
Simulation sandbox. Pendant la phase d'intégration, les paiements initiés en sandbox ne déclenchent pas toujours un débit réel chez les opérateurs Mobile Money ou sur les cartes virtuelles SOLIMI. SOLIMI met donc à disposition un simulateur de retour provider pour reproduire les cas SUCCESS, DECLINED, FAILED et TIMEOUT. Le simulateur s'utilise après la création d'un paiement, avec la référence SOLIMI retournée par l'API. Il met à jour le statut du paiement et déclenche le webhook partenaire comme le ferait un callback provider réel. Cette fonctionnalité est réservée à la sandbox et n'est pas disponible en production.

Authentification HMAC

Toutes les routes publiques sécurisées utilisent une signature HMAC SHA-256. SOLIMI vérifie votre clé API, l'adresse IP source, l'horodatage et la signature calculée à partir du corps exact de la requête.

Headers obligatoires

Header Description
x-api-key Votre clé API publique, par exemple sk_test_... ou sk_live_....
x-timestamp Timestamp Unix en secondes. La tolérance est de 300 secondes.
x-signature HMAC SHA-256 hexadécimal du message raw_body + timestamp.
Idempotency-Key Obligatoire pour POST /v1/payments/request. Doit être unique par tentative logique.

Algorithme

message = raw_request_body + x_timestamp
x_signature = HMAC_SHA256_HEX(api_secret, message)

Pour une requête GET, le corps est vide. Le message signé est donc uniquement le timestamp.

Protection contre les tentatives abusives

Si une clé API valide est utilisée avec de mauvaises signatures ou de mauvais timestamps à répétition, SOLIMI bloque temporairement l'authentification de cette clé afin de limiter les tentatives de force brute sur le secret API.

  • Le blocage est temporaire et retourne 429 api_key_temporarily_locked.
  • La durée standard du blocage est de 15 minutes.
  • Les appels depuis une IP non autorisée restent refusés en 403 authorization_failed.

Choisir son mode d'intégration

SOLIMI propose deux façons d'encaisser un paiement. Le choix dépend du niveau de contrôle que vous voulez garder sur l'expérience utilisateur.

1. API directe

Votre plateforme affiche ses propres formulaires de paiement, collecte le numéro de téléphone ou la carte virtuelle SOLIMI, puis appelle directement l'API SOLIMI.

  • Endpoint principal: POST /v1/payments/request.
  • Suivi: GET /v1/payments/status/{reference}.
  • Idéal si vous maîtrisez déjà votre propre interface de paiement.

2. Page de paiement hébergée

Votre backend crée une session checkout. SOLIMI retourne un lien de paiement; le client est redirigé vers une page SOLIMI où il choisit Mixx, Moov Money, carte bancaire ou carte virtuelle SOLIMI.

  • Création: POST /v1/checkout/sessions.
  • Suivi: GET /v1/checkout/sessions/{reference}.
  • Idéal pour intégrer vite sans développer l'interface de choix du moyen de paiement.

1. API directe

Ce mode est l'intégration où votre propre application pilote l'interface de paiement. Votre backend appelle directement POST /v1/payments/request, puis suit le statut avec GET /v1/payments/status/{reference} et les webhooks.

Une demande de paiement crée une intention de collecte. Selon le type de paiement, le client approuve le paiement sur mobile money, via une carte virtuelle SOLIMI ou sur une page bancaire sécurisée.

Mobile money

payment_type = mobile_money

Requiert provider: mixx, coris_money ou moov_money.

Carte virtuelle SOLIMI

payment_type = solimi_card

Requiert card_id sur 8 chiffres.

Carte bancaire

payment_type = bank_card

Retourne un provider_payment_url pour rediriger le client vers Visa/Mastercard.

Cycle de vie

Statut Signification
INITIATEDLa demande de paiement est créée.
PENDING_APPROVALLe paiement attend l'approbation du client.
APPROVEDLe client a approuvé le paiement.
PROCESSINGLe traitement est en cours.
SUCCESSLe paiement est réussi.
FAILEDLe paiement a échoué.
DECLINEDLe client ou le système a refusé le paiement.
EXPIREDLa demande a expiré.
CANCELLEDLa demande a été annulée.

2. Page de paiement hébergée SOLIMI

Ce mode est l'intégration où SOLIMI héberge l'interface de choix du moyen de paiement. Le partenaire ne développe pas l'écran de paiement: il crée une session, reçoit un lien, puis redirige le client.

Le checkout hébergé permet au partenaire d'appeler une seule API pour créer une session de paiement. SOLIMI retourne ensuite un payment_url à ouvrir côté client. Sur cette page, le client final choisit son moyen de paiement: Mixx, Moov Money, carte virtuelle SOLIMI ou carte bancaire.

Quand utiliser le mode 2

  • Vous souhaitez déléguer l'interface de choix du moyen de paiement à SOLIMI.
  • Vous ne voulez pas intégrer séparément chaque provider dans votre application.
  • Vous voulez un parcours web simple avec redirection vers votre site après paiement.

Redirections

Les URLs de succès, d'échec et d'annulation peuvent être configurées sur la fiche partenaire par SOLIMI. Le payload peut aussi les surcharger ponctuellement, mais ce n'est pas nécessaire dans le flux standard.

Endpoints à intégrer côté partenaire

POST /v1/checkout/sessions

Crée la session de paiement et retourne le lien à ouvrir côté client.

Payload standard

{
  "merchant_reference": "CMD-20260729-001",
  "amount": 12500,
  "description": "Vente de chaussure",
  "customer_phone": "22891390850",
  "customer_first_name": "Magloire",
  "customer_last_name": "ZOI"
}

Réponse 201

{
  "checkout_reference": "CHK-20260729-8F2A91",
  "merchant_reference": "CMD-20260729-001",
  "payment_url": "https://pay.solimi.co/checkout/CHK-20260729-8F2A91",
  "amount": 12500,
  "currency": "XOF",
  "description": "Vente de chaussure",
  "status": "created",
  "expires_at": "2026-07-29T12:30:00Z"
}

Erreurs possibles: 401 authentication_failed, 403 authorization_failed, 409 duplicate_merchant_reference, 422 validation_error.

GET /v1/checkout/sessions/{reference}

Consulte l'état d'une session checkout en secours du webhook.

Payload

Aucun body requis. La requête reste signée en HMAC avec un body vide.

Réponse 200

{
  "checkout_reference": "CHK-20260729-8F2A91",
  "merchant_reference": "CMD-20260729-001",
  "payment_reference": "SOLPAY20260729881234",
  "amount": 12500,
  "currency": "XOF",
  "description": "Vente de chaussure",
  "status": "payment_initiated",
  "selected_payment_type": "mobile_money",
  "selected_provider": "mixx",
  "provider_payment_url": null,
  "expires_at": "2026-07-29T12:30:00Z",
  "completed_at": null,
  "cancelled_at": null
}

Erreurs possibles: 401 authentication_failed, 403 authorization_failed, 404 not_found.

Flux recommandé

  1. Votre backend crée une session avec POST /v1/checkout/sessions.
  2. Vous redirigez le client vers le payment_url retourné.
  3. Le client choisit un moyen de paiement sur la page SOLIMI Checkout.
  4. SOLIMI exécute le paiement, met à jour le statut et appelle votre webhook.
  5. Votre backend peut consulter GET /v1/checkout/sessions/{reference} en secours.
Le checkout hébergé cohabite avec POST /v1/payments/request. Gardez le paiement direct si vous maîtrisez déjà votre propre interface de paiement; utilisez le checkout si vous voulez un parcours SOLIMI prêt à l'emploi.

Référence des payloads et validations

Cette section précise le rôle de chaque champ, son type, son caractère obligatoire, sa taille et le format attendu. Les regex ci-dessous sont les validations recommandées côté intégrateur, afin de détecter les erreurs avant l'appel API.

SOLIMI valide strictement les payloads. Si un champ optionnel n'est pas applicable au type de paiement, ne l'envoyez pas. Exemple: n'envoyez pas provider pour un paiement solimi_card.

Headers de sécurité

Champ Rôle Type Obligatoire Taille Regex / Format Exemple
x-api-key Identifie la clé API du partenaire. string Oui Variable ^sk_(test|live)_[A-Za-z0-9]+$ sk_test_abcd...
x-timestamp Protège contre le rejeu de requêtes. string numeric Oui 10 chiffres en secondes ^\d{10}$, Unix timestamp, tolérance 300s 1781899200
x-signature Prouve que la requête vient du serveur partenaire. string Oui 64 caractères ^[a-f0-9]{64}$ 7d9f...a31c
Idempotency-Key Évite la création de doublons lors des retries. string Oui pour création paiement Recommandé: 1 à 100 ^[A-Za-z0-9._:-]{1,100}$ ORDER-2026-0001

PaymentRequestCreate - Création d'un paiement

Champ Rôle métier Type Obligatoire Taille Regex / Valeurs Notes
merchant_reference Référence unique de la transaction dans le système du marchand. string Oui 1 à 100 caractères ^[A-Za-z0-9._:-]{1,100}$ recommandé Utilisez la même valeur comme base de Idempotency-Key.
payment_type Canal de paiement à utiliser. enum string Oui Variable mobile_money, bank_card, solimi_card Détermine les champs conditionnels requis.
amount Montant à collecter. integer Oui Nombre entier positif ^[1-9]\d*$ Montant en XOF, sans décimales.
currency Devise de la transaction. literal string Non 3 caractères ^XOF$ Facultatif. Si absent, le backend applique XOF.
description Libellé visible pour réconciliation ou support. string Oui Minimum 1 caractère ^.{1,}$ Recommandé: inclure le numéro de commande.
customer_phone Numéro du client payeur. string Oui 8 à 20 caractères ^\+?[0-9]{8,20}$ recommandé Envoyez le numéro au format international quand possible.
customer_first_name Prénom du client final. string Non Maximum 100 caractères ^.{0,100}$ Utilisé pour l'historique, la réconciliation et le support. Non affiché sur le checkout.
customer_last_name Nom du client final. string Non Maximum 100 caractères ^.{0,100}$ Utilisé pour l'historique, la réconciliation et le support. Non affiché sur le checkout.
provider Opérateur mobile money à utiliser. enum string Oui si mobile_money Variable mixx, coris_money, moov_money, bank_card Obligatoire pour mobile_money. Pour bank_card, le backend applique automatiquement bank_card.
coris_country_code Code pays Coris Money fixé par SOLIMI. string Non 1 à 10 caractères Toujours 228 Le backend utilise automatiquement 228 pour construire l'appel Coris Money.
coris_otp Code OTP saisi par le client Coris Money. string Oui si provider=coris_money 1 à 20 caractères Exemple: 123456 Transmis au provider Coris Money pour valider le débit.
card_id Carte ID de la carte virtuelle SOLIMI client. string Oui si solimi_card Exactement 8 chiffres ^\d{8}$ Visible dans l'application SOLIMI sur la carte virtuelle. Différent du numéro complet de carte à 16 chiffres.
Important pour la carte virtuelle SOLIMI. Le champ card_id correspond au Carte ID affiché sur la carte virtuelle dans l'application SOLIMI, par exemple 24924478. Il ne faut pas envoyer le numéro complet de la carte bancaire virtuelle à 16 chiffres.
Parcours Coris Money en 2 étapes. Avant d'appeler POST /v1/payments/request avec provider=coris_money, appelez d'abord POST /v1/payments/coris-money/otp avec phone. SOLIMI vérifie l'existence du compte Coris Money et déclenche l'envoi de l'OTP. Le client saisit ensuite cet OTP dans coris_otp lors de la création du paiement.

Règles conditionnelles par type de paiement

payment_type Champ requis Champs à ne pas envoyer Exemple minimal
mobile_money provider card_id {"payment_type":"mobile_money","provider":"mixx"}
mobile_money + coris_money provider, coris_otp card_id {"payment_type":"mobile_money","provider":"coris_money","coris_otp":"123456"}
bank_card Aucun champ conditionnel card_id {"payment_type":"bank_card"}
solimi_card card_id provider {"payment_type":"solimi_card","card_id":"12345678"}

PaymentRequestResponse - Réponse de création

Champ Rôle Type Format / Valeurs
referenceRéférence SOLIMI du paiement.stringPAY-..., à conserver pour le suivi.
merchant_referenceRéférence marchand transmise dans la requête.string1 à 100 caractères.
payment_typeType de paiement retenu.enummobile_money, bank_card, solimi_card.
providerOpérateur ou provider technique.enum ou nullmixx, coris_money, moov_money, bank_card, ou null.
amountMontant demandé.integerEntier positif.
currencyDevise.stringXOF.
descriptionLibellé de la demande.stringTexte non vide.
customer_phoneNuméro client.string8 à 20 caractères.
provider_payment_urlLien de paiement externe à ouvrir pour la carte bancaire.URL ou nullRenseigné principalement quand payment_type=bank_card.
statusÉtat initial ou courant.enumVoir la table des statuts.
expires_atDate d'expiration de la demande.datetimeISO 8601, UTC recommandé.
created_atDate de création.datetimeISO 8601.

PaymentStatusResponse - Suivi de paiement

Champ Rôle Type Format / Valeurs
referenceRéférence SOLIMI à interroger.stringPAY-....
merchant_referenceRéférence du marchand.string1 à 100 caractères.
payment_typeCanal utilisé.enumTypes de paiement supportés.
providerProvider mobile money.enum ou nullNull hors mobile money.
amountMontant.integerEntier positif.
currencyDevise.stringXOF.
statusÉtat courant.enumINITIATED, PENDING_APPROVAL, APPROVED, PROCESSING, SUCCESS, FAILED, DECLINED, EXPIRED, CANCELLED.
provider_payment_urlLien de paiement bancaire si disponible.URL ou nullÀ utiliser pour rediriger le client vers la page sécurisée Visa/Mastercard.
failure_reasonCause d'échec si disponible.string ou nullRenseigne principalement les statuts non réussis.
expires_atExpiration.datetimeISO 8601.
approved_atDate d'approbation client.datetime ou nullISO 8601.
completed_atDate de succès final.datetime ou nullISO 8601.
failed_atDate d'échec.datetime ou nullISO 8601.
declined_atDate de refus.datetime ou nullISO 8601.

Payloads webhook

Champ Rôle Type Obligatoire Format / Regex
urlEndpoint HTTPS du partenaire.URLOui à la création^https://.+ recommandé en production.
secretSecret partagé avec SOLIMI pour authentifier les webhooks reçus.stringNonMinimum 16 caractères. Recommandé: 32+ caractères aléatoires.
is_activeActive ou désactive la réception.booleanNon, uniquement updatetrue ou false.

Endpoints publics par mode d'intégration

Les endpoints ci-dessous sont regroupés selon leur usage: mode 1 API directe pour piloter votre propre interface de paiement, et mode 2 page hébergée pour rediriger le client vers SOLIMI Checkout. Les endpoints partenaire sont accessibles sous le préfixe /v1.

GET/v1/health

Public

Vérifie la disponibilité de l'API.

Headers

HeaderObligatoireValeur
content-typeNonapplication/json

Payload

Aucun body requis.

Réponses possibles

HTTPCasCorps
200API disponible{"status":"ok","service":"solimi-payment-api"}
5xxService indisponibleErreur serveur ou timeout infrastructure.

Exemples d'appel

cURL
curl -X GET "{{base_url}}/v1/health"
PHP
<?php
echo file_get_contents("{{base_url}}/v1/health");
Node.js
const response = await fetch("{{base_url}}/v1/health");
console.log(await response.json());
Python
import requests

response = requests.get("{{base_url}}/v1/health")
print(response.json())

Exemple de réponse 200

{
  "status": "ok",
  "service": "solimi-payment-api"
}

POST/v1/payments/request

HMAC + Idempotency-Key

Mode 1 - API directe. Crée une demande de paiement depuis votre propre interface.

Headers obligatoires

HeaderRôleFormat / valeur
content-typeFormat du body.application/json
x-api-keyIdentifie le partenaire.Cle API fournie par SOLIMI.
x-timestampProtection anti-rejeu.Timestamp Unix en secondes, tolérance 300s.
x-signatureAuthentifie la requête.HMAC SHA-256 hex de raw_body + timestamp.
Idempotency-KeyEvite les doublons lors des retries.Unique par tentative logique, ex: ORDER-2026-0001.

Payload et validations

ChampTypeObligatoireValeurs / regexRôle
merchant_referencestringOui1-100, ^[A-Za-z0-9._:-]{1,100}$Référence unique côté marchand.
payment_typeenumOuimobile_money, bank_card, solimi_cardCanal de collecte.
providerenum/nullOui si mobile moneymixx, moov_money, coris_money, bank_cardOpérateur mobile money. Pour bank_card, le backend le renseigne automatiquement.
coris_country_codestring/nullNonToujours 228Code pays Coris fixé automatiquement par le backend.
coris_otpstring/nullOui si Coris MoneyMax 20 caractèresOTP Coris Money saisi par le client.
amountintegerOui^[1-9]\d*$Montant XOF sans décimales.
currencystringNonXOFFacultatif. Devise par defaut appliquee par le backend.
descriptionstringOuiNon videLibelle/réconciliation.
customer_phonestringOui^\+?[0-9]{8,20}$Numéro du client payeur.
customer_first_namestring/nullNonMax 100 caractèresPrénom conservé pour l'historique et le support.
customer_last_namestring/nullNonMax 100 caractèresNom conservé pour l'historique et le support.
card_idstring/nullOui si carte virtuelle SOLIMI^\d{8}$Carte ID de 8 chiffres visible sur la carte virtuelle dans l'application SOLIMI. Ne pas envoyer le numéro complet à 16 chiffres.
Carte virtuelle SOLIMI. Pour payment_type = solimi_card, le partenaire doit envoyer le card_id de 8 chiffres affiché dans l'application SOLIMI, et non le numéro complet de la carte.

Exemple de payload

{
  "merchant_reference": "ORDER-2026-0001",
  "payment_type": "mobile_money",
  "provider": "mixx",
  "amount": 25000,
  "description": "Commande ORDER-2026-0001",
  "customer_phone": "22890112233",
  "customer_first_name": "Magloire",
  "customer_last_name": "ZOI"
}

Exemple carte bancaire

{
  "merchant_reference": "ORDER-2026-0002",
  "payment_type": "bank_card",
  "amount": 25000,
  "description": "Commande ORDER-2026-0002",
  "customer_phone": "22890112233",
  "customer_first_name": "Magloire",
  "customer_last_name": "ZOI"
}

Réponses possibles

HTTPCode applicatifCas
201-Demande de paiement créée.
400domain_errorRègle métier invalide.
401authentication_failedHeaders HMAC manquants ou invalides.
403authorization_failedIP non autorisée, partenaire inactif ou credentials invalides.
409idempotency_conflict / duplicate_merchant_referenceClé idempotente réutilisée avec un payload différent ou référence marchand déjà utilisée.
422validation_errorChamp manquant ou format invalide.
500payment_core_sync_failedErreur de synchronisation interne avec le noyau SOLIMI.

Exemples d'appel

cURL
BODY='{"merchant_reference":"ORDER-2026-0001","payment_type":"mobile_money","provider":"mixx","amount":25000,"description":"Commande ORDER-2026-0001","customer_phone":"22890112233"}'
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf "%s%s" "$BODY" "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SOLIMI_API_SECRET" -hex | sed 's/^.* //')

curl -X POST "{{base_url}}/v1/payments/request" \
  -H "content-type: application/json" \
  -H "x-api-key: $SOLIMI_API_KEY" \
  -H "x-timestamp: $TIMESTAMP" \
  -H "x-signature: $SIGNATURE" \
  -H "Idempotency-Key: ORDER-2026-0001" \
  -d "$BODY"
PHP
<?php
$apiKey = getenv("SOLIMI_API_KEY");
$apiSecret = getenv("SOLIMI_API_SECRET");
$timestamp = (string) time();
$body = json_encode([
  "merchant_reference" => "ORDER-2026-0001",
  "payment_type" => "mobile_money",
  "provider" => "mixx",
  "amount" => 25000,
  "description" => "Commande ORDER-2026-0001",
  "customer_phone" => "22890112233"
], JSON_UNESCAPED_SLASHES);
$signature = hash_hmac("sha256", $body . $timestamp, $apiSecret);

$ch = curl_init("{{base_url}}/v1/payments/request");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_HTTPHEADER => [
    "content-type: application/json",
    "x-api-key: $apiKey",
    "x-timestamp: $timestamp",
    "x-signature: $signature",
    "Idempotency-Key: ORDER-2026-0001"
  ]
]);
echo curl_exec($ch);
Node.js
import crypto from "node:crypto";

const body = JSON.stringify({
  merchant_reference: "ORDER-2026-0001",
  payment_type: "mobile_money",
  provider: "mixx",
  amount: 25000,
  description: "Commande ORDER-2026-0001",
  customer_phone: "22890112233"
});
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHmac("sha256", process.env.SOLIMI_API_SECRET).update(body + timestamp).digest("hex");

const response = await fetch("{{base_url}}/v1/payments/request", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.SOLIMI_API_KEY,
    "x-timestamp": timestamp,
    "x-signature": signature,
    "Idempotency-Key": "ORDER-2026-0001"
  },
  body
});
console.log(await response.json());
Python
import hashlib
import hmac
import json
import os
import time
import requests

payload = {
    "merchant_reference": "ORDER-2026-0001",
    "payment_type": "mobile_money",
    "provider": "mixx",
    "amount": 25000,
    "description": "Commande ORDER-2026-0001",
    "customer_phone": "22890112233",
}
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
signature = hmac.new(os.environ["SOLIMI_API_SECRET"].encode(), (body + timestamp).encode(), hashlib.sha256).hexdigest()

response = requests.post(
    "{{base_url}}/v1/payments/request",
    data=body.encode(),
    headers={
        "content-type": "application/json",
        "x-api-key": os.environ["SOLIMI_API_KEY"],
        "x-timestamp": timestamp,
        "x-signature": signature,
        "Idempotency-Key": "ORDER-2026-0001",
    },
)
print(response.json())

Exemple de réponse 201

{
  "reference": "PAY-8C7F36A1B2",
  "merchant_reference": "ORDER-2026-0001",
  "payment_type": "mobile_money",
  "provider": "mixx",
  "amount": 25000,
  "currency": "XOF",
  "description": "Commande ORDER-2026-0001",
  "customer_phone": "22890112233",
  "status": "PENDING_APPROVAL",
  "provider_payment_url": null,
  "expires_at": "2026-06-19T21:00:00Z",
  "created_at": "2026-06-19T20:45:00Z"
}

GET/v1/payments/status/{reference}

HMAC

Mode 1 - API directe. Retourne l'état courant d'un paiement initié via l'API directe.

Headers obligatoires

HeaderRôleFormat / valeur
x-api-keyIdentifie le partenaire.Cle API fournie par SOLIMI.
x-timestampProtection anti-rejeu.Timestamp Unix en secondes.
x-signatureAuthentifie la requête GET.HMAC SHA-256 hex du timestamp.

Parametres

ParametreTypeObligatoireFormatRôle
referencepath stringOuiPAY-... ou SOLPAY...Référence SOLIMI retournée à la création.

Réponses possibles

HTTPCode applicatifCas
200-Statut retourne.
401authentication_failedSignature invalide.
403authorization_failedPartenaire non autorise.
404not_foundRéférence inconnue pour ce partenaire.

Exemples d'appel

cURL
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf "%s" "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SOLIMI_API_SECRET" -hex | sed 's/^.* //')
curl -X GET "{{base_url}}/v1/payments/status/PAY-8C7F36A1B2" \
  -H "x-api-key: $SOLIMI_API_KEY" \
  -H "x-timestamp: $TIMESTAMP" \
  -H "x-signature: $SIGNATURE"
PHP
<?php
$timestamp = (string) time();
$signature = hash_hmac("sha256", $timestamp, getenv("SOLIMI_API_SECRET"));
$opts = ["http" => ["header" => "x-api-key: ".getenv("SOLIMI_API_KEY")."\r\nx-timestamp: $timestamp\r\nx-signature: $signature\r\n"]];
echo file_get_contents("{{base_url}}/v1/payments/status/PAY-8C7F36A1B2", false, stream_context_create($opts));
Node.js
import crypto from "node:crypto";

const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHmac("sha256", process.env.SOLIMI_API_SECRET).update(timestamp).digest("hex");
const response = await fetch("{{base_url}}/v1/payments/status/PAY-8C7F36A1B2", {
  headers: {"x-api-key": process.env.SOLIMI_API_KEY, "x-timestamp": timestamp, "x-signature": signature}
});
console.log(await response.json());
Python
import hashlib, hmac, os, time, requests

timestamp = str(int(time.time()))
signature = hmac.new(os.environ["SOLIMI_API_SECRET"].encode(), timestamp.encode(), hashlib.sha256).hexdigest()
response = requests.get(
    "{{base_url}}/v1/payments/status/PAY-8C7F36A1B2",
    headers={"x-api-key": os.environ["SOLIMI_API_KEY"], "x-timestamp": timestamp, "x-signature": signature},
)
print(response.json())

Exemple de réponse 200

{
  "reference": "PAY-8C7F36A1B2",
  "merchant_reference": "ORDER-2026-0001",
  "payment_type": "mobile_money",
  "provider": "mixx",
  "amount": 25000,
  "currency": "XOF",
  "status": "SUCCESS",
  "provider_payment_url": null,
  "failure_reason": null,
  "expires_at": "2026-06-19T21:00:00Z",
  "approved_at": "2026-06-19T20:46:11Z",
  "completed_at": "2026-06-19T20:46:18Z",
  "failed_at": null,
  "declined_at": null
}

GET/v1/payment-methods

HMAC

Liste les moyens de paiement actifs.

Headers obligatoires

HeaderRôleFormat / valeur
x-api-keyIdentifie le partenaire.Cle API fournie par SOLIMI.
x-timestampProtection anti-rejeu.Timestamp Unix en secondes.
x-signatureAuthentifie la requête GET.HMAC SHA-256 hex du timestamp.

Payload

Aucun body requis.

Champs de réponse

ChampTypeValeurs possiblesRôle
codestringmixx, coris_money, moov_money, bank_card, solimi_cardCode technique à envoyer dans provider si applicable.
namestringLibellé humainNom affichable.
providerstring/nullProvider mobile money ou nullOperateur associe.
is_activebooleantrue/falseDisponibilite du moyen.

Réponses possibles

HTTPCode applicatifCas
200-Liste retournée.
401authentication_failedSignature invalide.
403authorization_failedPartenaire non autorise.

Exemples d'appel

cURL
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf "%s" "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SOLIMI_API_SECRET" -hex | sed 's/^.* //')
curl -X GET "{{base_url}}/v1/payment-methods" \
  -H "x-api-key: $SOLIMI_API_KEY" \
  -H "x-timestamp: $TIMESTAMP" \
  -H "x-signature: $SIGNATURE"
PHP
<?php
$timestamp = (string) time();
$signature = hash_hmac("sha256", $timestamp, getenv("SOLIMI_API_SECRET"));
$opts = ["http" => ["header" => "x-api-key: ".getenv("SOLIMI_API_KEY")."\r\nx-timestamp: $timestamp\r\nx-signature: $signature\r\n"]];
echo file_get_contents("{{base_url}}/v1/payment-methods", false, stream_context_create($opts));
Node.js
import crypto from "node:crypto";

const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHmac("sha256", process.env.SOLIMI_API_SECRET).update(timestamp).digest("hex");
const response = await fetch("{{base_url}}/v1/payment-methods", {
  headers: {"x-api-key": process.env.SOLIMI_API_KEY, "x-timestamp": timestamp, "x-signature": signature}
});
console.log(await response.json());
Python
import hashlib, hmac, os, time, requests

timestamp = str(int(time.time()))
signature = hmac.new(os.environ["SOLIMI_API_SECRET"].encode(), timestamp.encode(), hashlib.sha256).hexdigest()
response = requests.get(
    "{{base_url}}/v1/payment-methods",
    headers={"x-api-key": os.environ["SOLIMI_API_KEY"], "x-timestamp": timestamp, "x-signature": signature},
)
print(response.json())

Exemple de réponse 200

[
  {
    "code": "mixx",
    "name": "Mixx by Yas",
    "provider": "mixx",
    "is_active": true
  },
  {
    "code": "mobile_money",
    "name": "Coris Money",
    "provider": "coris_money",
    "is_active": true
  },
  {
    "code": "moov_money",
    "name": "Moov Money",
    "provider": "moov_money",
    "is_active": true
  },
  {
    "code": "bank_card",
    "name": "Carte bancaire",
    "provider": "bank_card",
    "is_active": true
  },
  {
    "code": "solimi_card",
    "name": "Carte virtuelle SOLIMI",
    "provider": null,
    "is_active": true
  }
]

POST/v1/checkout/sessions

HMAC + Idempotency-Key

Mode 2 - Page de paiement hébergée. Crée une session checkout et retourne le lien de paiement à afficher au client.

Headers obligatoires

HeaderRôleFormat / valeur
content-typeFormat du body.application/json
x-api-keyIdentifie le partenaire.Clé API fournie par SOLIMI.
x-timestampProtection anti-rejeu.Timestamp Unix en secondes, tolérance 300s.
x-signatureAuthentifie la requête.HMAC SHA-256 hex de raw_body + timestamp.
Idempotency-KeyÉvite les doublons lors des retries.Unique par tentative logique.

Payload et validations

ChampTypeObligatoireValeurs / regexRôle
merchant_referencestringOui1-100, ^[A-Za-z0-9._:-]{1,100}$Référence unique côté marchand.
amountintegerOui^[1-9]\d*$Montant à collecter en XOF.
descriptionstringOuiNon videLibellé affiché au client.
customer_emailstring/nullNonEmail valide, max 255Facultatif, utilisé uniquement pour l'historique/support. Aucun email automatique n'est envoyé au payeur.
customer_phonestring/nullNon^\+?[0-9]{8,20}$Pré-remplissage du numéro client.
customer_first_namestring/nullNonMax 100 caractèresPrénom conservé pour l'historique. Non affiché sur la page checkout.
customer_last_namestring/nullNonMax 100 caractèresNom conservé pour l'historique. Non affiché sur la page checkout.
currencystringNonXOFFacultatif. Par défaut le backend applique XOF.
success_url, failure_url, cancel_urlURL/nullNonURL HTTPS recommandéeSurcharge ponctuelle des URLs configurées sur le partenaire.
metadataobject/nullNonObjet JSON libreDonnées de rapprochement interne du partenaire.

Exemple de payload standard

{
  "merchant_reference": "CMD-20260729-001",
  "amount": 12500,
  "description": "Vente de chaussure",
  "customer_phone": "22891390850",
  "customer_first_name": "Magloire",
  "customer_last_name": "ZOI"
}

Réponses possibles

HTTPCode applicatifCas
201-Session créée.
401authentication_failedSignature invalide.
403authorization_failedIP ou partenaire non autorisé.
409duplicate_merchant_referenceRéférence marchand déjà utilisée.
422validation_errorChamp manquant ou invalide.

Exemple de réponse 201

{
  "checkout_reference": "CHK-20260729-8F2A91",
  "merchant_reference": "CMD-20260729-001",
  "payment_url": "https://pay.solimi.co/checkout/CHK-20260729-8F2A91",
  "amount": 12500,
  "currency": "XOF",
  "description": "Vente de chaussure",
  "status": "OPEN",
  "expires_at": "2026-07-29T12:30:00Z"
}

GET/v1/checkout/sessions/{reference}

HMAC

Mode 2 - Page de paiement hébergée. Consulte le statut d'une session checkout créée par le partenaire.

Headers obligatoires

HeaderRôleFormat / valeur
x-api-keyIdentifie le partenaire.Clé API fournie par SOLIMI.
x-timestampProtection anti-rejeu.Timestamp Unix en secondes, tolérance 300s.
x-signatureAuthentifie la requête.HMAC SHA-256 hex de timestamp, car le body est vide.

Payload

Aucun body requis.

Réponses possibles

HTTPCode applicatifCas
200-Statut de la session retourné.
401authentication_failedSignature invalide ou headers absents.
403authorization_failedIP ou partenaire non autorisé.
404not_foundSession introuvable pour ce partenaire.

Exemple de réponse 200

{
  "checkout_reference": "CHK-20260729-8F2A91",
  "merchant_reference": "CMD-20260729-001",
  "payment_reference": "SOLPAY20260729881234",
  "amount": 12500,
  "currency": "XOF",
  "description": "Vente de chaussure",
  "status": "payment_initiated",
  "selected_payment_type": "mobile_money",
  "selected_provider": "mixx",
  "provider_payment_url": null,
  "expires_at": "2026-07-29T12:30:00Z",
  "completed_at": null,
  "cancelled_at": null
}

POST/checkout/v1/sessions/{reference}/pay

Checkout public

Route interne de la page hébergée. Déclenche le paiement choisi par le client sur la page checkout SOLIMI.

Headers

content-type: application/json. Cette route est appelée par la page checkout, elle ne demande pas de signature HMAC partenaire.

Payload et validations

ChampTypeObligatoireValeurs / regexRôle
payment_typeenumOuimobile_money, bank_card, solimi_cardMoyen choisi par le client.
providerenum/nullOui si mobile moneymixx, moov_money, bank_cardProvider technique.
customer_phonestring/nullOui sauf carte bancaire^\+?[0-9]{8,20}$Numéro client ou support d'identification.
card_idstring/nullOui si carte virtuelle SOLIMI^\d{8}$Carte ID de 8 chiffres visible sur la carte virtuelle SOLIMI. Différent du numéro complet à 16 chiffres.
Aide client. Le client retrouve le card_id dans l'application SOLIMI, sur sa carte virtuelle, au niveau du libellé Carte ID. Cette valeur sert uniquement à identifier la carte virtuelle SOLIMI à débiter après validation.

Exemples de payload

{
  "payment_type": "bank_card",
  "provider": "bank_card"
}
{
  "payment_type": "mobile_money",
  "provider": "mixx",
  "customer_phone": "22891390850"
}

Exemple de réponse 200

{
  "checkout_reference": "CHK-20260729-8F2A91",
  "merchant_reference": "CMD-20260729-001",
  "payment_reference": "SOLPAY20260729881234",
  "amount": 12500,
  "currency": "XOF",
  "description": "Vente de chaussure",
  "status": "PROCESSING",
  "selected_payment_type": "bank_card",
  "selected_provider": "bank_card",
  "provider_payment_url": "https://secure-gateway.example/pay/abc123",
  "expires_at": "2026-07-29T12:30:00Z",
  "completed_at": null,
  "cancelled_at": null
}
L'URL webhook du partenaire est configurée par l'équipe SOLIMI dans la plateforme interne. Il n'existe plus d'endpoint public permettant au partenaire de créer ou modifier lui-même son webhook.

Webhooks

SOLIMI appelle votre endpoint webhook lorsqu'un paiement change d'état. Votre serveur doit répondre avec un statut HTTP 2xx. En cas d'échec, SOLIMI relance selon les délais: 30s, 60s, 300s, 900s.

Header SOLIMI Description
x-solimi-signatureSecret webhook partage avec SOLIMI. Comparez cette valeur au secret configuré.
x-solimi-timestampTimestamp Unix en secondes, utile pour refuser les notifications trop anciennes.
x-solimi-eventNom de l'événement, par exemple payment.success.

Événements

payment.initiated, payment.pending_approval, payment.success, payment.failed, payment.declined, payment.expired.

Vérification webhook FastAPI

import time

from fastapi import APIRouter, Header, HTTPException, Request

router = APIRouter()

SOLIMI_WEBHOOK_SECRET = "secret_fourni_par_solimi"
SIGNATURE_TOLERANCE_SECONDS = 300


@router.post("/webhooks/solimi/payments")
async def solimi_payment_webhook(
    request: Request,
    x_solimi_signature: str = Header(...),
    x_solimi_timestamp: str = Header(...),
    x_solimi_event: str = Header(...),
):
    if x_solimi_signature != SOLIMI_WEBHOOK_SECRET:
        raise HTTPException(status_code=401, detail="Invalid webhook signature")

    try:
        timestamp = int(x_solimi_timestamp)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid timestamp")

    if abs(int(time.time()) - timestamp) > SIGNATURE_TOLERANCE_SECONDS:
        raise HTTPException(status_code=400, detail="Expired webhook timestamp")

    payload = await request.json()
    payment = payload.get("payment", {})

    # Mettre à jour la commande/facture du partenaire ici.
    # Exemple: payment.success => commande payée.

    return {
        "received": True,
        "event": x_solimi_event,
        "merchant_reference": payment.get("merchant_reference"),
        "solimi_reference": payment.get("reference"),
        "status": payment.get("status"),
    }
La vérification est volontairement simple: le header x-solimi-signature contient directement le secret webhook. Gardez ce secret côté serveur uniquement.

Erreurs

Les erreurs sont retournées au format JSON suivant.

{
  "code": "authentication_failed",
  "message": "Missing authentication headers",
  "details": null
}
HTTP Code Action recommandée
401authentication_failedVérifier les headers HMAC, le timestamp et la signature.
403authorization_failedVérifier l'IP autorisée, l'état de la clé API et du partenaire.
404not_foundVérifier la référence de paiement ou la route appelée.
409idempotency_conflictRéutiliser une meme clé uniquement pour la meme requête logique.
429api_key_temporarily_lockedAttendre la fin du blocage temporaire, puis vérifier le secret API et le calcul HMAC avant de relancer les appels.
422validation_errorCorriger les champs invalides dans le payload.

Collection Postman

La collection Postman SOLIMI regroupe les requêtes prêtes à tester pour les endpoints publics: création de paiement, création de session checkout, consultation de statut et moyens de paiement disponibles. Elle aide les équipes techniques à démarrer rapidement sans reconstruire les headers et payloads depuis zéro.

Comment l'utiliser

  1. Téléchargez le fichier JSON de collection.
  2. Dans Postman, cliquez sur Import, puis sélectionnez le fichier téléchargé.
  3. Renseignez les variables base_url, api_key, api_secret et les valeurs de test.
  4. Lancez les requêtes de la section 03 - Payments pour valider l'intégration.
  5. Utilisez la section 05 - Sandbox Simulator pour simuler les retours succès, refus, échec ou expiration.

Fichier disponible

Format JSON compatible avec Postman. Le fichier contient les scripts nécessaires pour calculer les headers HMAC, ajouter automatiquement l'idempotency key sur les paiements et tester le simulateur sandbox pendant la recette.

Télécharger la collection Postman

POST/v1/payments/coris-money/otp

HMAC

Mode 1 - API directe. Vérifie le numéro Coris Money et déclenche l'envoi du code OTP avant l'initiation du paiement.

Payload

ChampTypeObligatoireFormatRôle
phonestringOui^\+?[0-9]{8,20}$Numéro du client Coris Money.

Réponses possibles

HTTPCodeCasExemple
200200OTP envoyé.{"code":"200","message":"OTP envoyé"}
404404Aucun compte Coris Money associé au numéro.{"code":"404","message":"Aucun compte associé à ce numéro."}
502500Erreur provider Coris Money.{"code":"500","message":"Une erreur s'est produite. Veuillez réessayer plus tard."}

Exemple

{
  "phone": "22891390850"
}
La collection ne contient pas vos secrets. Les valeurs sensibles doivent être renseignées dans vos variables d'environnement Postman et ne doivent pas être partagées publiquement.

Checklist de mise en production

  • Stocker la clé API et le secret API dans un coffre de secrets côté serveur.
  • Signer toutes les requêtes avec le corps exact envoyé a SOLIMI.
  • Utiliser une cle d'idempotence unique pour chaque paiement marchand.
  • Vérifier les webhooks en comparant x-solimi-signature au secret webhook configuré.
  • Prevoir le polling de statut en secours si un webhook est indisponible.
  • Fournir à SOLIMI les adresses IP publiques de vos serveurs.
  • Tester les cas succès, échec, refus, expiration et retry webhook en sandbox.