alouspike / cmi-payment
Laravel package for CMI (Centre Monetique Interbancaire) payment gateway integration - Moroccan banking payment solution
Requires
- php: ^8.1
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/events: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/routing: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- illuminate/view: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
This package is auto-updated.
Last update: 2026-04-27 15:43:42 UTC
README
Package Laravel pour l'intégration de la passerelle de paiement CMI (Centre Monétique Interbancaire) — la solution de paiement bancaire marocaine.
Fonctionnalités
- API fluide pour initier les paiements facilement
- Génération et vérification sécurisée du hash HMAC
- Support multi-boutiques (SaaS)
- Intégration des événements Laravel
- Journalisation des transactions en base de données
- Contrôleur de callback intégré
- Vue Blade de redirection avec auto-submit
- Commande Artisan de test
- Zéro dépendance externe
Prérequis
- PHP 8.1+
- Laravel 10, 11 ou 12
Installation
composer require alouspike/cmi-payment
Le package se découvre automatiquement. Pas besoin d'enregistrer manuellement le service provider.
Publier la configuration
php artisan vendor:publish --tag=cmi-config
Publier et exécuter les migrations
php artisan vendor:publish --tag=cmi-migrations php artisan migrate
Publier les vues (optionnel)
php artisan vendor:publish --tag=cmi-views
Configuration
Ajoutez ces variables à votre fichier .env :
CMI_CLIENT_ID=votre_id_marchand CMI_STORE_KEY=votre_cle_secrete CMI_OK_URL=https://votresite.com/payment/success CMI_FAIL_URL=https://votresite.com/payment/failed CMI_SHOP_URL=https://votresite.com CMI_CALLBACK_URL=https://votresite.com/cmi/callback CMI_SANDBOX=true CMI_HASH_ALGORITHM=SHA512 CMI_CURRENCY=504 CMI_LANG=fr CMI_STORE_TRANSACTIONS=true
Options complètes
| Paramètre | Défaut | Description |
|---|---|---|
CMI_CLIENT_ID |
'' |
ID marchand fourni par CMI |
CMI_STORE_KEY |
'' |
Clé secrète pour la génération du hash |
CMI_BASE_URI |
testpayment.cmi.co.ma/... |
URL de la passerelle |
CMI_OK_URL |
'' |
URL de redirection en cas de succès |
CMI_FAIL_URL |
'' |
URL de redirection en cas d'échec |
CMI_SHOP_URL |
'' |
URL de la boutique |
CMI_CALLBACK_URL |
'' |
URL de callback serveur |
CMI_CURRENCY |
504 |
Devise par défaut (504 = MAD) |
CMI_LANG |
fr |
Langue par défaut |
CMI_AUTO_REDIRECT |
true |
Redirection automatique après paiement |
CMI_SESSION_TIMEOUT |
1800 |
Timeout de session en secondes |
CMI_HASH_ALGORITHM |
SHA512 |
Algorithme de hash (SHA512 ou SHA256) |
CMI_STORE_TRANSACTIONS |
true |
Journaliser les transactions en BDD |
CMI_SANDBOX |
true |
Mode sandbox |
CMI_ROUTE_PREFIX |
cmi |
Préfixe des routes |
URL de production
Pour la production, modifiez :
CMI_BASE_URI=https://payment.cmi.co.ma/fim/est3Dgate CMI_SANDBOX=false
Utilisation
Paiement simple
use Cmi; // Dans votre contrôleur public function pay() { return Cmi::amount(150.00) ->orderId('CMD-001') ->email('client@email.com') ->pay(); }
Paiement avancé
use Cmi; use Alouspike\CmiPayment\Enums\Currency; use Alouspike\CmiPayment\Enums\Language; return Cmi::amount(250.50) ->orderId('CMD-002') ->customerName('Ahmed Alami') ->email('ahmed@email.com') ->phone('0661000000') ->currency(Currency::MAD) ->lang(Language::FR) ->description('Achat en ligne #002') ->billingAddress('123 Rue Hassan II', 'Casablanca', 'MA') ->sessionTimeout(600) ->autoRedirect(true) ->okUrl('https://monsite.com/custom/success') ->failUrl('https://monsite.com/custom/fail') ->extraData(['champ_personnalise' => 'valeur']) ->pay();
Multi-Boutiques (SaaS)
return Cmi::useCredentials( clientId: $shop->cmi_client_id, storeKey: $shop->cmi_store_key, )->amount(100.00) ->orderId('CMD-003') ->email('test@test.com') ->pay();
Gestion des callbacks
Automatique (Contrôleur intégré)
Le package enregistre automatiquement une route POST /cmi/callback qui gère la vérification du hash, la mise à jour des transactions et le dispatch des événements.
Utilisation du Trait
Dans votre contrôleur, utilisez le trait CmiGateway pour gérer les redirections OK/Fail :
use Alouspike\CmiPayment\Traits\CmiGateway; use Alouspike\CmiPayment\CmiPaymentResponse; class PaymentController extends Controller { use CmiGateway; protected function onCmiPaymentSuccess(CmiPaymentResponse $response) { $orderId = $response->getOrderId(); // Mettre à jour la commande, notifier l'utilisateur... return redirect()->route('orders.show', $orderId) ->with('success', 'Paiement effectué avec succès !'); } protected function onCmiPaymentFailed(CmiPaymentResponse $response) { return redirect()->route('checkout') ->with('error', 'Le paiement a échoué. Veuillez réessayer.'); } }
Puis dans vos routes :
Route::post('/payment/success', [PaymentController::class, 'cmiOk']); Route::post('/payment/failed', [PaymentController::class, 'cmiFail']);
Vérification manuelle du hash
use Cmi; if (Cmi::verifyHash($request->all())) { // Le hash est valide }
Événements
Le package dispatch les événements suivants :
| Événement | Quand |
|---|---|
CmiCallbackReceived |
Chaque callback reçu |
CmiPaymentSucceeded |
ProcReturnCode === '00' (paiement approuvé) |
CmiPaymentFailed |
Paiement échoué |
Écouter les événements
// Dans EventServiceProvider ou avec Event::listen use Alouspike\CmiPayment\Events\CmiPaymentSucceeded; use Alouspike\CmiPayment\Events\CmiPaymentFailed; Event::listen(CmiPaymentSucceeded::class, function ($event) { $response = $event->response; $orderId = $response->getOrderId(); $amount = $response->getAmount(); // Envoyer un email de confirmation, mettre à jour le statut... }); Event::listen(CmiPaymentFailed::class, function ($event) { $response = $event->response; // Journaliser l'échec, notifier l'administrateur... });
Méthodes de CmiPaymentResponse
| Méthode | Retour | Description |
|---|---|---|
isSuccessful() |
bool |
Si le paiement a réussi |
isFailed() |
bool |
Si le paiement a échoué |
getOrderId() |
?string |
ID de la commande |
getAmount() |
?string |
Montant du paiement |
getProcReturnCode() |
?string |
Code retour CMI |
getAuthCode() |
?string |
Code d'autorisation |
getTransId() |
?string |
ID de la transaction |
getCardBrand() |
?string |
Marque de la carte (Visa, MC...) |
getMaskedPan() |
?string |
Numéro de carte masqué |
getClientIp() |
?string |
Adresse IP du client |
getStatus() |
string |
Statut : approved/declined/error |
getErrMsg() |
?string |
Message d'erreur éventuel |
toArray() |
array |
Données complètes de la réponse |
Commande Artisan
php artisan cmi:test
Vérifie votre configuration CMI et affiche un exemple de code de paiement.
Cartes de test
Utilisez ces cartes en mode sandbox :
| Type de carte | Numéro | Expiration | CVV |
|---|---|---|---|
| Visa | 4000 0000 0000 0002 |
Toute date future | 3 chiffres |
| Mastercard | 5200 0000 0000 0007 |
Toute date future | 3 chiffres |
| Visa (3DS) | 4000 0000 0000 0051 |
Toute date future | 3 chiffres |
Note : Les numéros de cartes de test peuvent varier selon votre contrat CMI. Contactez le support CMI pour les identifiants de test exacts.
Dépannage
Erreurs "Hash invalide"
- Vérifiez que
CMI_STORE_KEYcorrespond exactement à ce que CMI vous a fourni - Assurez-vous que
CMI_HASH_ALGORITHMcorrespond à votre configuration CMI (SHA512 ou SHA256) - Vérifiez que votre URL de callback est accessible depuis internet (CMI envoie des requêtes serveur à serveur)
Callback non reçu
- L'URL de callback doit être accessible publiquement (pas localhost)
- Assurez-vous que votre pare-feu autorise les requêtes POST provenant des serveurs CMI
- Vérifiez que la route
cmi/callbackn'est pas bloquée par un middleware
Token CSRF invalide
Le package exclut automatiquement les routes CMI de la vérification CSRF. Si vous rencontrez toujours des problèmes, assurez-vous que les routes du package sont chargées correctement.
Transaction non sauvegardée
- Assurez-vous d'avoir exécuté
php artisan migrate - Vérifiez que
CMI_STORE_TRANSACTIONS=truedans votre.env
Sécurité
- Toutes les données de callback sont vérifiées via le hash HMAC avant traitement
- La clé secrète n'est jamais exposée dans les formulaires ou le code côté client
- Les données de transaction sont validées côté serveur
- Le middleware
VerifyCmiHashpeut être appliqué à n'importe quelle route pour une sécurité supplémentaire - La protection CSRF est maintenue sur toutes les routes non-CMI
Licence
Licence MIT. Voir LICENSE pour plus d'informations.