Qu'est-ce qu'OPTGateway ?
OPTGateway est une passerelle de paiement. Elle se place entre votre site et les opérateurs de paiement : M-Pesa, Orange Money, Airtel Money, Afrimoney, illicocash et les cartes Visa et Mastercard.
Sans passerelle, il faudrait signer un contrat et écrire une intégration différente pour chaque opérateur. Avec OPTGateway, vous écrivez une seule intégration, et vos clients choisissent leur moyen de paiement.
Les mots à connaître
Vous les croiserez partout dans ce guide. Pas besoin de tout retenir maintenant : revenez ici au besoin.
- Clé API
- Un mot de passe réservé à votre serveur, qui prouve à OPTGateway que la demande vient bien de vous. Elle commence par
sk_test_(essais) ousk_live_(argent réel). Ne la mettez jamais dans une page web ou une application mobile. - Mode test
- Un bac à sable : tout fonctionne comme en vrai, mais aucun argent ne circule. Idéal pour apprendre.
- Mode live
- Le mode réel : les clients sont vraiment débités. Il s'active après la vérification de votre établissement par notre équipe.
- Référence
- Votre numéro à vous pour ce paiement, par exemple un numéro de facture. Il doit être unique : deux paiements ne peuvent pas avoir la même référence.
- Session de paiement
- Une demande de paiement prête à être payée. Elle possède une adresse (
url) vers laquelle vous envoyez votre client. - Webhook
- Un message qu'OPTGateway envoie automatiquement à votre serveur pour dire « ce paiement est réussi ». C'est la preuve fiable qu'il faut attendre avant de livrer.
- OTP
- Un code à usage unique envoyé par SMS au client, par exemple par illicocash, pour confirmer qu'il est bien le titulaire du compte.
Comment se passe un paiement
- Votre site crée une demande : le montant, la devise et votre référence.
- Le client paie sur la page OPTGateway, avec son téléphone ou sa carte.
- OPTGateway vérifie auprès de l'opérateur que l'argent est bien reçu, avec le bon montant.
- Votre serveur est prévenu par un webhook, et le client revient sur votre site.
Moyens de paiement acceptés
Vos clients choisissent sur la page de paiement. Selon le moyen choisi, ils valident de trois façons différentes : OPTGateway gère chacune pour vous.
| Moyen | Valeur de method | Comment le client valide | Montants |
|---|---|---|---|
illicocash | Il reçoit un code par SMS et le saisit sur la page de paiement. | à partir de 1 000 CDF | |
mobile_money | Une demande s'affiche sur son téléphone ; il la valide avec son code secret. | aucune limite propre à OPTGateway | |
card | Il est redirigé vers la page sécurisée de sa banque, puis revient. | aucune limite propre à OPTGateway |
Chaque paiement renvoyé par l'API, et chaque webhook, indique son moyen dans method. Pour le mobile money, le champ operator précise l'opérateur : mpesa, orange, airtel ou afrimoney (déduit du numéro). Vous distinguez ainsi sans ambiguïté un paiement illicocash d'un paiement M-Pesa.
Sur la page de paiement, le client choisit parmi les moyens actifs sur votre compte. Pour n'en proposer que certains, passez methods à la création de la session, par exemple "methods": ["illicocash"]. En API directe, method est obligatoire. GET /v1/payment-methods liste les moyens actifs sur votre compte.
Devises : francs congolais (CDF) et dollars (USD). Les moyens actifs dépendent des comptes d'encaissement configurés pour vous par notre équipe ; votre tableau de bord les affiche. Un montant hors limites est refusé avant tout envoi au client, avec le code amount_below_minimum.
Créer votre compte et votre clé de test
- Créez votre compte sur l'espace marchand. Vous êtes tout de suite en mode test.
- Dans le menu, ouvrez Clés API, choisissez « Test », puis cliquez sur Créer.
- Copiez la clé tout de suite : pour votre sécurité, elle ne sera plus jamais affichée. En cas de perte, créez-en une nouvelle et révoquez l'ancienne.
- Rangez-la sur votre serveur dans une variable d'environnement nommée
OPTGATEWAY_KEY, par exemple dans le fichier.envde votre projet.
Astuce : dans l'espace marchand, la page Documentation contient des boutons « Essayer » qui envoient de vraies requêtes de test. Pratique pour voir les réponses sans écrire une ligne.
Méthode 1 : le lien de paiement, sans code
Vous n'avez pas de site, ou vous voulez encaisser un montant ponctuel ? Le lien de paiement suffit.
- Dans l'espace marchand, ouvrez Liens de paiement.
- Indiquez le montant, la devise et le motif, par exemple « Frais d'inscription 2026-2027 ».
- Cliquez sur Créer le lien, puis sur Envoyer par WhatsApp ou Copier le lien.
- Le client ouvre le lien et paie. Le paiement apparaît dans Paiements, avec son statut.
Un lien reste valable 24 heures.
Méthode 2 : la page de paiement (recommandée)
Votre site envoie le client vers la page de paiement OPTGateway, puis le récupère une fois le paiement terminé. C'est la méthode la plus simple à coder : la page gère le numéro de téléphone, le code SMS, la carte bancaire et les erreurs.
Étape 1 : installer le SDK
Le SDK est une petite bibliothèque qui vous évite d'écrire les appels HTTP à la main. Téléchargez celui de votre langage, puis installez-le dans votre projet.
- PHP avec Composer : ajoutez le bloc ci-dessous à votre
composer.json(dans la sectionrepositoriessi elle existe déjà), puis lancezcomposer require enywork/optgateway:1.1.0. - PHP sans Composer : décompressez l'archive dans votre projet et chargez
autoload.php. - Node.js : une seule commande, qui télécharge et installe le paquet.
{
"repositories": [
{
"type": "package",
"package": {
"name": "enywork/optgateway",
"version": "1.1.0",
"dist": { "url": "https://pay.optsolution.pro/sdk/optgateway-php-1.1.0.zip", "type": "zip" },
"require": { "php": ">=7.4", "ext-curl": "*", "ext-json": "*" },
"autoload": { "psr-4": { "Enywork\\OPTGateway\\": "src/" } }
}
}
]
}// 1. Décompressez optgateway-php-1.1.0.zip dans votre projet
// 2. Chargez le SDK en haut de vos scripts :
require __DIR__ . '/optgateway-php/autoload.php';npm install https://pay.optsolution.pro/sdk/enywork-optgateway-1.1.0.tgzAvec Node.js, le code reste identique : import OPTGateway from '@enywork/optgateway'. Les paquets seront aussi publiés sur Packagist et npm ; les commandes composer require et npm install @enywork/optgateway fonctionneront alors directement, avec le même code.
Étape 2 : créer la demande de paiement
Ce code s'exécute sur votre serveur, au moment où le client clique sur « Payer » sur votre site.
<?php
require 'vendor/autoload.php'; // sans Composer : require 'optgateway-php/autoload.php';
use Enywork\OPTGateway\Client;
// La clé est lue dans une variable d'environnement, jamais écrite dans le code
$optg = new Client(getenv('OPTGATEWAY_KEY'), ['base_url' => 'https://pay.optsolution.pro']);
$session = $optg->checkout->createSession([
'amount' => '150000', // montant en francs congolais
'currency' => 'CDF', // ou 'USD'
'reference' => 'FACT-2026-000123', // votre numéro de facture, unique
'description' => 'Frais académiques - 1er semestre',
'return_url' => 'https://votre-site.cd/paiement/retour',
]);
// Étape suivante : envoyer le client vers la page de paiement
header('Location: ' . $session['url'], true, 303);
exit;import OPTGateway from '@enywork/optgateway';
// La clé est lue dans une variable d'environnement, jamais écrite dans le code
const optg = new OPTGateway(process.env.OPTGATEWAY_KEY, { baseUrl: 'https://pay.optsolution.pro' });
app.post('/payer', async (req, res) => {
const session = await optg.checkout.sessions.create({
amount: '150000', // montant en francs congolais
currency: 'CDF', // ou 'USD'
reference: 'FACT-2026-000123', // votre numéro de facture, unique
description: 'Frais académiques - 1er semestre',
return_url: 'https://votre-site.cd/paiement/retour',
});
// Étape suivante : envoyer le client vers la page de paiement
res.redirect(303, session.url);
});curl https://pay.optsolution.pro/v1/checkout/sessions \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{
"amount": "150000",
"currency": "CDF",
"reference": "FACT-2026-000123",
"description": "Frais académiques - 1er semestre",
"return_url": "https://votre-site.cd/paiement/retour"
}'OPTGateway répond avec la session créée. Le champ important est url :
{
"id": "cs_test_Gq3…",
"url": "https://pay.optsolution.pro/pay/cs_test_Gq3…",
"status": "open",
"amount": "150000.00",
"currency": "CDF",
"reference": "FACT-2026-000123"
}Et si le client clique deux fois ? Aucun risque : avec la même reference, OPTGateway renvoie la même session. Le client ne peut pas payer deux fois la même facture.
Étape 3 : envoyer le client vers la page
C'est la dernière ligne de l'exemple ci-dessus : une redirection vers session.url. Le client voit la page de paiement, choisit son moyen de paiement et paie.
Étape 4 : recevoir la confirmation (webhook)
Quand le paiement est confirmé, OPTGateway appelle une adresse de votre serveur. Pour la configurer :
- Dans l'espace marchand, ouvrez Webhooks et saisissez l'adresse, par exemple
https://votre-site.cd/webhooks/optgateway. - Cliquez sur Générer un nouveau secret et rangez-le dans la variable
OPTGATEWAY_WEBHOOK_SECRET. - Ajoutez ce code sur votre serveur, puis cliquez sur Envoyer un test pour vérifier qu'il répond.
<?php
// Fichier appelé par OPTGateway : https://votre-site.cd/webhooks/optgateway
require 'vendor/autoload.php';
use Enywork\OPTGateway\Webhook;
use Enywork\OPTGateway\Exception\SignatureVerificationException;
try {
$event = Webhook::constructEvent(
file_get_contents('php://input'), // le corps brut de la requête
$_SERVER['HTTP_OPTGATEWAY_SIGNATURE'] ?? '',
getenv('OPTGATEWAY_WEBHOOK_SECRET') // le secret whsec_... de votre tableau de bord
);
} catch (SignatureVerificationException $e) {
http_response_code(400); // pas envoyé par OPTGateway : on ignore
exit;
}
if ($event['type'] === 'checkout.session.completed') {
// Le paiement est confirmé : marquez la facture comme payée
marquerFacturePayee($event['data']['reference']);
}
http_response_code(200); // répondre vite : "bien reçu"import express from 'express';
import OPTGateway from '@enywork/optgateway';
// express.raw : on a besoin du corps brut pour vérifier la signature
app.post('/webhooks/optgateway', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = OPTGateway.webhooks.constructEvent(
req.body,
req.get('OPTGateway-Signature'),
process.env.OPTGATEWAY_WEBHOOK_SECRET // le secret whsec_... de votre tableau de bord
);
} catch (err) {
return res.sendStatus(400); // pas envoyé par OPTGateway : on ignore
}
if (event.type === 'checkout.session.completed') {
// Le paiement est confirmé : marquez la facture comme payée
marquerFacturePayee(event.data.reference);
}
res.sendStatus(200); // répondre vite : "bien reçu"
});Important : marquez une facture comme payée uniquement dans ce code-là. Le webhook est la seule preuve fiable. OPTGateway le renvoie automatiquement pendant environ 22 heures si votre serveur ne répond pas.
Étape 5 : la page de retour
Après le paiement, le client revient sur votre return_url. L'adresse contient status=succeeded, mais un client malin pourrait la taper lui-même. Pour afficher le bon message, demandez l'état réel à OPTGateway :
<?php
// https://votre-site.cd/paiement/retour?session_id=...&status=succeeded
// On ne se fie pas à l'adresse : on demande l'état réel à OPTGateway
$session = $optg->checkout->retrieveSession($_GET['session_id'] ?? '');
if ($session['status'] === 'completed') {
echo 'Merci, votre paiement est bien reçu.';
} else {
echo 'Le paiement n\'est pas encore confirmé.';
}// https://votre-site.cd/paiement/retour?session_id=...&status=succeeded
// On ne se fie pas à l'adresse : on demande l'état réel à OPTGateway
app.get('/paiement/retour', async (req, res) => {
const session = await optg.checkout.sessions.retrieve(String(req.query.session_id || ''));
res.send(session.status === 'completed'
? 'Merci, votre paiement est bien reçu.'
: 'Le paiement n\'est pas encore confirmé.');
});Étape 6 : tester
Faites un paiement complet avec votre clé de test et les numéros de test. Vérifiez trois choses : la page de paiement s'ouvre, le webhook arrive sur votre serveur, et la facture passe à « payée ».
Méthode 3 : l'API directe (avancé)
Pour construire votre propre écran de paiement, par exemple dans une application mobile. Votre écran propose les moyens de paiement, le client en choisit un, puis vous créez le paiement avec ce moyen dans method (obligatoire). Vous suivez ensuite le champ next_action, qui indique ce que le client doit faire.
// 1. Créer le paiement, avec le moyen choisi par le client (obligatoire)
let p = await optg.payments.create({
method: 'illicocash', // ou 'mobile_money', ou 'card'
amount: '5000', currency: 'CDF', reference: 'CMD-42',
customer: { phone: '+243850000005' },
});
// 2. Suivre l'action demandée
if (p.next_action?.type === 'otp') {
// illicocash : le client a reçu un code par SMS, vous le lui demandez
p = await optg.payments.confirm(p.id, codeSaisiParLeClient);
} else if (p.next_action?.type === 'redirect') {
// carte bancaire : envoyer le client vers p.next_action.url
} else if (p.next_action?.type === 'wait_for_customer') {
// M-Pesa, Orange, Airtel, Afrimoney : le client valide sur son téléphone
// attendez le webhook payment.succeeded
}// 1. Créer le paiement, avec le moyen choisi par le client (obligatoire)
$p = $optg->payments->create([
'method' => 'illicocash', // ou 'mobile_money', ou 'card'
'amount' => '5000', 'currency' => 'CDF', 'reference' => 'CMD-42',
'customer' => ['phone' => '+243850000005'],
]);
// 2. Suivre l'action demandée
$action = $p['next_action']['type'] ?? null;
if ($action === 'otp') {
// illicocash : le client a reçu un code par SMS, vous le lui demandez
$p = $optg->payments->confirm($p['id'], $_POST['code']);
} elseif ($action === 'redirect') {
header('Location: ' . $p['next_action']['url']); // carte bancaire
}
// wait_for_customer : le client valide sur son téléphone, attendez le webhooknext_action.type | Ce qui se passe | Ce que vous faites |
|---|---|---|
otp | illicocash envoie un code par SMS au client | Demandez le code au client, puis appelez confirm |
wait_for_customer | Le client reçoit une demande sur son téléphone (M-Pesa, Orange, Airtel, Afrimoney) | Affichez « Validez sur votre téléphone » et attendez le webhook |
redirect | Paiement par carte bancaire | Envoyez le client vers next_action.url |
Tester sans argent réel
Avec une clé sk_test_, les paiements sont simulés, avec le parcours réel de chaque moyen :
| Moyen | Scénario | Résultat |
|---|---|---|
illicocash | N'importe quel numéro | Code SMS demandé : 123456 réussit, tout autre code est refusé |
mobile_money | Par exemple +243 97 000 00 01 | Validation sur le téléphone, puis paiement réussi après environ 5 secondes |
mobile_money | Numéro qui finit par 2, par exemple +243 97 000 00 02 | Validation sur le téléphone, puis échec pour solde insuffisant |
card | Aucun | Page bancaire simulée : le paiement réussit quand le client revient sur la page de paiement |
Si votre compte n'a qu'illicocash en live, seul illicocash est proposé en test aussi : votre intégration se comporte comme en production.
Sécurité : les bons réflexes
Cinq règles suffisent à éviter l'essentiel des problèmes. Relisez-les avant la mise en ligne.
- La clé API reste sur votre serveur. Jamais dans une page web, une application mobile ou un dépôt Git. Rangez-la dans une variable d'environnement. Si elle a fuité, révoquez-la dans Clés API et créez-en une autre.
- Livrez uniquement sur webhook. La page de retour peut être ouverte par n'importe qui ; seul le webhook signé prouve le paiement.
- Vérifiez la signature de chaque webhook, avec le SDK ou à la main, et refusez les messages de plus de 5 minutes.
- Contrôlez le montant et la référence reçus dans le webhook avant de marquer la facture comme payée.
- Gardez une référence unique par facture. Si vous renvoyez la même demande, OPTGateway renvoie le même paiement au lieu d'en créer un second : pas de double débit en cas de clic répété ou de coupure réseau.
Passer en production
- Dans le tableau de bord, onglet Vérification, renseignez votre établissement et déposez vos documents : RCCM (ou arrêté / agrément pour une école ou une université) et pièce d'identité du représentant légal. Notre équipe examine le dossier, en général sous 48 heures ouvrées, configure votre compte d'encaissement (wallet illicocash ou compte FlexPay) et active le mode live. Vous êtes prévenu par e-mail à chaque étape.
- Créez une clé Live dans Clés API, et remplacez la clé de test sur votre serveur de production.
- Vérifiez que l'adresse de webhook est en
https://et qu'elle répond. - Faites un vrai paiement d'un petit montant et vérifiez-le dans Paiements.
Adresses de l'API
Toutes les adresses commencent par https://pay.optsolution.pro. Chaque requête porte l'en-tête Authorization: Bearer VOTRE_CLE.
| Adresse | Rôle |
|---|---|
POST /v1/checkout/sessions | Créer une demande de paiement (page hébergée) |
GET /v1/checkout/sessions/:id | Consulter une demande de paiement |
POST /v1/payments | Créer un paiement (API directe) |
GET /v1/payments/:id | Consulter un paiement |
GET /v1/payments | Lister les paiements (filtres : status, reference, limit) |
POST /v1/payments/:id/confirm | Envoyer le code SMS du client (illicocash) |
GET /v1/balance | Consulter votre solde à reverser |
Champs d'une demande de paiement
| Champ | Obligatoire | Description |
|---|---|---|
amount | Oui | Montant en chaîne de caractères : "150000" ou "12.50" |
currency | Oui | CDF ou USD |
reference | Oui | Votre identifiant unique (numéro de facture) |
return_url | Oui | Page de votre site où revient le client |
cancel_url | Non | Page de votre site si le client annule |
description | Non | Texte affiché au client, par exemple « Frais académiques » |
customer.phone | Non | Numéro du client, au format +243…. S'il est absent, le client le saisit. |
metadata | Non | Informations libres, renvoyées telles quelles (matricule, identifiant interne…) |
expires_in_minutes | Non | Durée de validité, de 5 à 1440 minutes (60 par défaut) |
Statuts d'un paiement
| Statut | Signification | Que faire |
|---|---|---|
pending | En attente de validation par le client | Attendre |
requires_action | Le client doit agir (code SMS, carte) | Suivre next_action |
succeeded | Payé et vérifié | Livrer |
failed, expired, cancelled | Non payé | Proposer de réessayer |
processing | L'opérateur n'a pas encore confirmé ; notre équipe vérifie | Ne pas faire payer une deuxième fois : attendre le webhook |
Événements (webhooks)
| Événement | Quand |
|---|---|
checkout.session.completed | Une demande de paiement est payée : c'est celui à écouter en priorité |
payment.succeeded | Un paiement est réussi |
payment.failed, payment.expired, payment.cancelled | Un paiement n'a pas abouti |
payment.review_required | Un paiement est en vérification par notre équipe |
checkout.session.expired | Une demande n'a pas été payée à temps |
payout.succeeded, payout.failed | Un reversement de fonds vers votre compte |
Chaque webhook porte l'en-tête OPTGateway-Signature. Les fonctions constructEvent des SDK la vérifient pour vous. Votre serveur doit répondre par un code 2xx en moins de 10 secondes ; sinon OPTGateway recommence plus tard, avec 8 tentatives au total. Un même événement peut donc arriver deux fois : ignorez celui que vous avez déjà traité (même id).
Vérifier une signature sans SDK
Si vous n'utilisez pas nos SDK, la vérification tient en quelques lignes. L'en-tête a la forme t=HORODATAGE,v1=SIGNATURE. La signature est un HMAC-SHA256, en hexadécimal, calculé avec votre secret de webhook sur le texte HORODATAGE.CORPS, où le corps est le JSON brut reçu, tel quel.
<?php
// Sans SDK : vérifier soi-même la signature d'un webhook
$body = file_get_contents('php://input'); // corps brut, avant json_decode
$header = $_SERVER['HTTP_OPTGATEWAY_SIGNATURE'] ?? ''; // ex. t=1790000000,v1=5f2c...
$secret = getenv('OPTGATEWAY_WEBHOOK_SECRET');
parse_str(str_replace(',', '&', $header), $parts); // ['t' => ..., 'v1' => ...]
$attendu = hash_hmac('sha256', $parts['t'] . '.' . $body, $secret);
$valide = isset($parts['v1'])
&& hash_equals($attendu, $parts['v1']) // comparaison à temps constant
&& abs(time() - (int) $parts['t']) <= 300; // message de moins de 5 minutes
if (!$valide) { http_response_code(400); exit; }
$event = json_decode($body, true);import crypto from 'node:crypto';
// Sans SDK : vérifier soi-même la signature d'un webhook.
// Important : utilisez le corps BRUT, par exemple express.raw({ type: 'application/json' })
function verifier(rawBody, header, secret) {
const parts = Object.fromEntries(String(header).split(',').map((p) => p.split('=')));
const attendu = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const recu = Buffer.from(parts.v1 || '', 'hex');
const ok = recu.length === 32 && crypto.timingSafeEqual(recu, Buffer.from(attendu, 'hex'));
const recent = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
return ok && recent;
}Piège fréquent : calculer la signature sur un JSON déjà décodé puis réencodé. L'ordre des champs ou les espaces peuvent changer, et la signature ne correspond plus. Utilisez toujours le corps brut.
Erreurs
En cas d'erreur, la réponse contient un code et un message en français, par exemple :
{ "error": { "code": "invalid_request", "message": "Requete invalide", "details": ["currency : CDF ou USD"] } }| Code HTTP | Code | Cause fréquente |
|---|---|---|
| 400 | invalid_request | Un champ manque ou est mal écrit, par exemple method absent : lisez details |
| 400 | method_not_available | Ce moyen de paiement n'est pas actif sur votre compte dans ce mode. details.available_methods liste ceux qui le sont |
| 400 | amount_below_minimum | Montant trop petit pour le moyen de paiement. Le minimum est indiqué dans details.min_amount (1 000 CDF pour illicocash) |
| 400 | amount_above_maximum | Montant trop élevé pour le moyen de paiement |
| 401 | unauthorized | Clé absente, mal copiée ou révoquée |
| 403 | forbidden | Clé live utilisée avant l'activation du mode live |
| 404 | not_found | Identifiant inconnu, ou créé dans l'autre mode (test ou live) |
| 409 | duplicate_reference | Référence déjà utilisée avec un autre montant |
| 429 | rate_limited | Trop de requêtes : patientez quelques secondes |
Questions fréquentes
Faut-il un site web ?
Non. Les liens de paiement fonctionnent sans site : vous les envoyez par WhatsApp ou par SMS.
Le client ne reçoit pas le code SMS, que faire ?
Sur la page de paiement, il clique sur « Je n'ai pas reçu de code » pour relancer une demande. Vérifiez aussi que le numéro est bien celui de son compte illicocash.
Un paiement est « En vérification » : c'est grave ?
Non. L'opérateur n'a pas répondu à temps. Notre équipe vérifie auprès de lui, puis vous recevez le webhook final. Surtout, ne faites pas payer le client une deuxième fois.
Puis-je encaisser en dollars ?
Oui. Utilisez "currency": "USD". Les montants acceptent deux décimales : "12.50".
Quand est-ce que je reçois l'argent ?
Cela dépend de vos comptes d'encaissement. Si vous avez vos propres comptes (wallet illicocash, compte FlexPay), l'argent arrive directement chez vous, sans délai de notre côté. Sinon, vos paiements sont encaissés sur les comptes de notre équipe, qui vous reverse les fonds, commission déduite ; le calendrier est convenu à l'activation de votre compte. Votre espace marchand indique, pour chaque moyen, quel compte est utilisé.
J'ai une autre question
Contactez notre équipe depuis votre espace marchand ou avec les coordonnées de la page d'accueil.