Guide complet d'intégration des paiements SATIM CIB et Edahabia en Algérie
Guide technique étape par étape couvrant la configuration de l'API SATIM, les flux de paiement par carte CIB et Edahabia, la gestion des erreurs et le déploiement en production pour le e-commerce algérien.
Le paysage des paiements en Algérie
Si vous construisez une plateforme e-commerce ou toute application acceptant les paiements en ligne en Algérie, vous rencontrerez inévitablement SATIM. La Société d'Automatisation des Transactions Interbancaires et de Monétique (SATIM) est le switch national de paiement électronique qui traite virtuellement toutes les transactions par carte en Algérie. Elle se situe entre les marchands, les banques et les réseaux de cartes, agissant comme la couche centrale de routage et de règlement.
Deux types de cartes dominent le marché algérien :
- CIB (Carte Interbancaire) : la carte de crédit/débit interbancaire standard émise par les banques algériennes. Les cartes CIB portent le logo du réseau CIB et sont acceptées aux terminaux de point de vente et chez les marchands en ligne dans tout le pays.
- Edahabia : la carte de débit d'Algérie Poste, liée aux comptes CCP (Compte Courant Postal). Avec plus de 20 millions de comptes CCP en Algérie, Edahabia représente la plus grande base potentielle de paiement du pays.
Les deux types de cartes sont traités via la passerelle de paiement SATIM (parfois appelée SATIM e-payment ou SATIM IPAY). D'un point de vue technique, le flux d'intégration est quasiment identique pour les deux. La passerelle détecte automatiquement le type de carte en fonction du numéro entré par le client.
Prérequis
Avant d'écrire la moindre ligne de code, vous avez besoin des éléments suivants :
- Un compte marchand auprès d'une banque supportant le e-paiement SATIM (la plupart des grandes banques algériennes le font : BNA, CPA, BEA, BADR, Gulf Bank Algeria, entre autres).
- Un Terminal ID (format :
E010XXXXXX), fourni via le portail SATIM pour identifier votre terminal marchand. - Un nom d'utilisateur et un mot de passe pour l'API de la passerelle SATIM, fournis via le portail SATIM après approbation de votre compte marchand.
- Des identifiants de test pour l'environnement sandbox à
test2.satim.dz, également fournis via le portail SATIM.
Le processus d'onboarding est géré via le portail SATIM à cibweb.dz. Suivez les instructions pour soumettre vos documents et recevoir vos identifiants. Les délais varient, commencez donc ce processus tôt. Si vous avez besoin d'aide pour naviguer la certification, nous proposons un accompagnement certification SATIM.
Comment fonctionne le flux de paiement
SATIM utilise un flux de paiement basé sur la redirection, conceptuellement similaire à 3D Secure ou Stripe Checkout. Votre serveur ne manipule jamais les données brutes de carte. Voici la séquence complète :
- Enregistrement de la commande : votre backend appelle l'endpoint
register.dode SATIM avec le montant de la commande, un numéro de commande unique et les URLs de callback. SATIM répond avec unorderIdet uneformUrl. - Redirection du client : vous redirigez le navigateur du client vers la
formUrl. SATIM héberge la page de paiement où le client entre les détails de sa carte CIB ou Edahabia. - Traitement du paiement : SATIM traite la transaction avec la banque émettrice. Le client peut voir un écran de vérification OTP/mot de passe (similaire à 3DS).
- Redirection callback : après le paiement (succès ou échec), SATIM redirige le client vers votre
returnUrloufailUrlavec l'orderIdcomme paramètre de requête. - Confirmation côté serveur : votre backend appelle l'endpoint
acknowledgeTransaction.dopour vérifier le statut de la transaction. C'est l'étape critique ; ne faites jamais confiance à la redirection côté client seule.
Client Votre serveur Passerelle SATIM Banque
| | | |
| Passer commande | | |
|----------------->| | |
| | register.do | |
| |------------------->| |
| | orderId + formUrl | |
| |<-------------------| |
| Redirection | | |
| vers formUrl | | |
|<-----------------| | |
| Saisie carte | | |
|---------------------------------------->| |
| | | Traiter paiement|
| | |----------------->|
| | | Réponse auth |
| | |<-----------------|
| Redirection | | |
| vers returnUrl | | |
|<-----------------------------------------| |
| Page chargée | | |
|----------------->| | |
| | acknowledgeTransaction.do |
| |------------------->| |
| | Statut paiement | |
| |<-------------------| |
| Commande | | |
| confirmée | | |
|<-----------------| | |
Configuration avec satim-ts
Le package @bakissation/satim est un SDK TypeScript de qualité production qui encapsule l'API SATIM avec des types stricts, la validation des entrées, la conversion automatique des montants et une gestion d'erreurs complète.
Installation
1npm install @bakissation/satim
Le SDK supporte ESM et CommonJS, nécessite Node.js 18 ou supérieur, et n'a aucune dépendance externe (il utilise l'API native fetch).
Configuration
L'approche la plus simple passe par les variables d'environnement. Créez un fichier .env :
1SATIM_USERNAME=your_merchant_username 2SATIM_PASSWORD=your_merchant_password 3SATIM_TERMINAL_ID=E010XXXXXX 4SATIM_API_URL=https://test2.satim.dz/payment/rest
Puis initialisez le client :
1import { createSatimClient, fromEnv } from '@bakissation/satim'; 2 3const satim = createSatimClient(fromEnv());
Pour un contrôle plus explicite, passez la configuration directement :
1import { createSatimClient, API_BASE_URLS } from '@bakissation/satim'; 2 3const satim = createSatimClient({ 4 userName: process.env.SATIM_USERNAME!, 5 password: process.env.SATIM_PASSWORD!, 6 terminalId: process.env.SATIM_TERMINAL_ID!, 7 apiBaseUrl: API_BASE_URLS.TEST, // basculer vers API_BASE_URLS.PRODUCTION pour le live 8 language: 'fr', 9 currency: '012', // DZD (ISO 4217) 10});
Implémenter le flux de paiement
Étape 1 : enregistrer la commande
Quand votre client est prêt à payer, créez une commande sur votre backend :
1import { createSatimClient, fromEnv } from '@bakissation/satim'; 2import { randomUUID } from 'crypto'; 3 4const satim = createSatimClient(fromEnv()); 5 6async function createPayment(orderNumber: string, amountDzd: number) { 7 const response = await satim.register({ 8 orderNumber, // Max 10 caractères, doit être unique par transaction 9 amount: amountDzd, // En DZD (minimum 50 DZD), converti automatiquement en unités mineures 10 returnUrl: 'https://yoursite.com/payment/callback', 11 failUrl: 'https://yoursite.com/payment/failed', 12 description: 'Purchase from YourStore', 13 udf1: orderNumber, // Requis : votre référence interne 14 idempotencyKey: randomUUID(), // Empêche les commandes en double lors des retry 15 }); 16 17 if (response.isSuccessful()) { 18 // Sauvegardez response.orderId dans votre base de données, lié à la commande 19 return { 20 orderId: response.orderId, 21 redirectUrl: response.formUrl, 22 }; 23 } 24 25 throw new Error('Failed to register order with SATIM'); 26}
Points clés concernant l'appel register :
- Gestion des montants : passez les montants en DZD sous forme de nombres entiers ou décimaux (ex.
5000pour 5 000 DZD, ou806.5pour 806,50 DZD). Le SDK convertit automatiquement en unités mineures (centimes) comme requis par l'API. - Numéro de commande : maximum 10 caractères. Doit être unique par transaction. Si vous réutilisez un numéro de commande, SATIM retournera le code d'erreur 1.
- udf1 : requis par SATIM. Utilisez-le pour votre référence interne (numéro de facture, ID de commande, etc.).
- Idempotence : passez toujours un
idempotencyKeyen production pour éviter les doubles prélèvements si votre requête est relancée suite à des problèmes réseau.
Étape 2 : rediriger le client
Côté frontend, redirigez le client vers la formUrl retournée par l'appel register :
1// Dans votre gestionnaire de réponse API (frontend) 2window.location.href = redirectUrl;
Ou si vous construisez un backend API-first, retournez l'URL à votre frontend/client mobile et laissez-le gérer la redirection. La page de paiement SATIM supporte le français, l'anglais et l'arabe, configurable via le paramètre language.
Étape 3 : gérer le callback
Après que le client a complété (ou abandonné) le paiement, SATIM le redirige vers votre returnUrl ou failUrl avec un paramètre de requête orderId. Sur votre endpoint de callback, vérifiez le paiement côté serveur :
1import { OrderStatus, interpretOrderStatus } from '@bakissation/satim'; 2 3async function handlePaymentCallback(orderId: string) { 4 const status = await satim.confirm(orderId); 5 6 if (status.isPaid()) { 7 // Paiement réussi, orderStatus === 2 (DEPOSITED) 8 console.log('Payment confirmed for order:', status.orderNumber); 9 console.log('Amount:', status.amount); 10 console.log('Card (masked):', status.pan); // ex. 6280****7215 11 12 // Mettez à jour le statut de votre commande en base de données 13 // Livrez le produit/service 14 return { success: true, orderNumber: status.orderNumber }; 15 } 16 17 // Paiement non réussi 18 console.log('Payment status:', interpretOrderStatus(status.orderStatus)); 19 console.log('Details:', status.actionCodeDescription); 20 return { success: false, reason: status.actionCodeDescription }; 21}
La méthode confirm (alias getOrderStatus) appelle l'endpoint acknowledgeTransaction.do de SATIM. C'est le seul moyen fiable de savoir si un paiement a réussi. Ne vous fiez jamais uniquement à l'URL de redirection sur laquelle le client atterrit, car elle peut être falsifiée ou manipulée.
Étape 4 : remboursements (si nécessaire)
Pour rembourser une transaction complétée :
1async function refundPayment(orderId: string, amountDzd: number) { 2 const result = await satim.refund(orderId, amountDzd); 3 4 if (result.isSuccessful()) { 5 console.log('Refund processed successfully'); 6 return true; 7 } 8 9 console.error('Refund failed:', result.errorMessage); 10 return false; 11}
Les remboursements doivent référencer l'orderId original retourné lors de l'enregistrement. Les remboursements partiels sont supportés en passant un montant inférieur à celui de la transaction originale.
CIB vs Edahabia : ce que les développeurs doivent savoir
Du point de vue de l'intégration API, les paiements CIB et Edahabia sont gérés de manière identique par la passerelle SATIM. Vous n'avez pas besoin de spécifier le type de carte lors de l'enregistrement d'une commande. La passerelle détermine le type de carte à partir du numéro entré par le client sur la page de paiement.
Il y a quelques différences pratiques à connaître :
- Format du numéro de carte : les cartes CIB commencent typiquement par
6280(16 chiffres). Les cartes Edahabia suivent le schéma de numérotation d'Algérie Poste. Les deux sont traitées via les mêmes endpointsregister.doetacknowledgeTransaction.do. - Limites de transaction : les cartes Edahabia peuvent avoir des limites de transaction quotidiennes inférieures à celles des cartes CIB, selon la configuration du titulaire auprès d'Algérie Poste. Votre application devrait gérer gracieusement les refus "limite dépassée".
- Délais de règlement : le règlement sur votre compte marchand peut différer légèrement entre les transactions CIB et Edahabia, selon l'arrangement de votre banque acquéreuse. C'est une question d'opérations bancaires, pas de code.
- Vérification OTP : les deux types de cartes peuvent nécessiter la saisie d'un OTP ou mot de passe sur la page de paiement SATIM. Le champ
passwordsur les cartes de test est utilisé à cet effet dans le sandbox.
Si vous avez spécifiquement besoin de gérer des paiements de factures ou un type de transaction particulier, vous pouvez utiliser le paramètre fundingTypeIndicator (ex. 'CP' ou '698'), mais pour les achats e-commerce standard, ce n'est pas nécessaire.
Gestion des erreurs
Le SDK fournit des classes d'erreur typées pour chaque scénario d'échec. Une gestion robuste des erreurs est particulièrement importante pour les intégrations de paiement où les échecs silencieux peuvent entraîner des pertes de revenus ou des doubles prélèvements.
1import { 2 SatimApiError, 3 ValidationError, 4 HttpError, 5 TimeoutError, 6 ConfigError, 7 mapSatimErrorCode, 8} from '@bakissation/satim'; 9 10async function safePayment(orderNumber: string, amount: number) { 11 try { 12 const response = await satim.register({ 13 orderNumber, 14 amount, 15 returnUrl: 'https://yoursite.com/payment/callback', 16 udf1: orderNumber, 17 idempotencyKey: crypto.randomUUID(), 18 }); 19 return response; 20 } catch (error) { 21 if (error instanceof ValidationError) { 22 // Entrée invalide : montant incorrect, champs manquants, etc. 23 console.error(`Validation error [${error.code}]:`, error.message); 24 } else if (error instanceof SatimApiError) { 25 // SATIM a rejeté la requête 26 console.error(`SATIM error [${error.satimErrorCode}]:`, error.message); 27 } else if (error instanceof TimeoutError) { 28 // Timeout de la requête, sans risque de réessayer avec la même clé d'idempotence 29 console.error(`Timeout after ${error.timeoutMs}ms`); 30 } else if (error instanceof HttpError) { 31 // Échec réseau ou HTTP 32 console.error(`HTTP error [${error.httpStatus}]:`, error.message); 33 } else if (error instanceof ConfigError) { 34 // Configuration manquante ou invalide 35 console.error('Config error, missing keys:', error.missingKeys); 36 } 37 throw error; 38 } 39}
Référence des codes d'erreur SATIM
Voici les codes d'erreur retournés par l'API SATIM à travers ses endpoints :
| Code | register.do | acknowledgeTransaction.do | refund.do |
|---|---|---|---|
| 0 | Succès | Succès | Succès |
| 1 | Commande déjà traitée | - | - |
| 2 | - | Erreur d'identifiants de paiement | - |
| 3 | Devise inconnue | - | - |
| 4 | Paramètre requis manquant | - | - |
| 5 | Paramètre invalide | Accès refusé | Accès refusé / Montant invalide |
| 6 | - | Commande non enregistrée | Commande non enregistrée |
| 7 | Erreur système | Erreur système | Erreur système |
| 14 | Mode de paiement invalide | - | - |
Codes de statut de commande
Après avoir appelé confirm() ou getOrderStatus(), vérifiez le champ orderStatus :
| Code | Signification | Action |
|---|---|---|
| 0 | Enregistrée mais non payée | Le client a abandonné ou n'a pas encore complété le paiement |
| -1 | Refus inconnu | Contactez le support SATIM si le problème persiste |
| 1 | Approuvée (pré-autorisation retenue) | En attente de dépôt |
| 2 | Déposée (paiement complété) | Exécutez la commande |
| 3 | Autorisation annulée | Le paiement a été annulé avant le règlement |
| 4 | Remboursée | La transaction a été remboursée |
| 6 | Refusée | La carte a été refusée par la banque émettrice |
Tests avec le sandbox SATIM
SATIM fournit un environnement sandbox à https://test2.satim.dz/payment/rest pour les tests d'intégration. Utilisez les cartes de test suivantes :
| Numéro de carte | CVV2 | Expiration | Mot de passe | Scénario |
|---|---|---|---|---|
| 6280581110007215 | 373 | 01/2027 | 123456 | Paiement réussi |
| 6280581110006712 | 897 | 01/2027 | 123456 | Carte temporairement bloquée |
| 6280580610061110 | 260 | 01/2027 | 123456 | Solde insuffisant |
| 6280580610061219 | 049 | 01/2027 | 123456 | Limite dépassée |
| 6280581110006514 | 205 | 01/2027 | 123456 | CVV2 incorrect |
| 6280580610061011 | 992 | 01/2027 | 123456 | Carte de crédit valide |
Lors des tests :
- Configurez votre
SATIM_API_URLàhttps://test2.satim.dz/payment/rest. - Utilisez les identifiants de test fournis par votre banque (pas vos identifiants de production).
- Testez chaque scénario : paiement réussi, carte refusée, solde insuffisant, gestion du timeout et prévention des commandes en double.
- Vérifiez que votre appel
confirm()interprète correctement chaque statut de commande. - Testez le parcours utilisateur complet, y compris ce qui se passe quand le client ferme le navigateur sur la page de paiement SATIM sans compléter la transaction.
Checklist de déploiement en production
Avant de passer en production, parcourez cette checklist :
Configuration
- Basculer
SATIM_API_URLvershttps://satim.dz/payment/rest - Utiliser les identifiants de production (nom d'utilisateur, mot de passe, terminal ID) de votre banque
- Stocker tous les identifiants dans des variables d'environnement, jamais dans le code source
- Vérifier que les fichiers
.envsont dans.gitignore - Configurer
NODE_ENV=productionpour désactiver le logging verbeux de développement
Sécurité
- Utiliser la méthode POST pour tous les appels API SATIM (le SDK le fait par défaut)
- Toujours vérifier le statut du paiement côté serveur via
confirm(), ne jamais faire confiance aux redirections côté client - Servir vos URLs de callback uniquement en HTTPS
- Implémenter des clés d'idempotence sur chaque appel
register()pour prévenir les doubles prélèvements - Ne jamais logger ou stocker les numéros de carte complets (le SDK ne les expose jamais)
Fiabilité
- Gérer tous les types d'erreur (
SatimApiError,TimeoutError,HttpError,ValidationError) - Implémenter une logique de retry pour les scénarios
TimeoutError(en utilisant la même clé d'idempotence) - Mettre en place du monitoring/alerting pour les paiements échoués
- Logger les résultats de transaction (en utilisant le logger intégré du SDK ou le vôtre via l'option
customLogger) - Stocker le
orderIdSATIM dans votre base de données pour chaque transaction, permettant les vérifications de statut et remboursements futurs
Tests
- Exécuter des tests end-to-end contre le sandbox avant de basculer en production
- Tester avec tous les scénarios de cartes sandbox (succès, refus, fonds insuffisants, carte bloquée)
- Vérifier que le flux de remboursement fonctionne correctement
- Tester ce qui se passe quand SATIM est injoignable (gestion du timeout)
Conformité
- Vérifier auprès de votre banque que votre intégration est approuvée pour l'usage en production
- Vérifier que vos conditions générales d'utilisation et votre politique de confidentialité couvrent les paiements électroniques
- Conserver les enregistrements de toutes les transactions comme requis par la réglementation commerciale algérienne
Avancé : intégration avec Fastify
Si vous utilisez Fastify comme framework web, le plugin @bakissation/fastify-satim fournit une intégration simplifiée qui enregistre le client SATIM comme décorateur Fastify :
1npm install @bakissation/fastify-satim
1import Fastify from 'fastify'; 2import satimPlugin from '@bakissation/fastify-satim'; 3 4const app = Fastify(); 5 6app.register(satimPlugin, { 7 userName: process.env.SATIM_USERNAME!, 8 password: process.env.SATIM_PASSWORD!, 9 terminalId: process.env.SATIM_TERMINAL_ID!, 10 apiBaseUrl: 'https://satim.dz/payment/rest', 11}); 12 13app.post('/pay', async (request, reply) => { 14 const { orderNumber, amount } = request.body as any; 15 16 const result = await app.satim.register({ 17 orderNumber, 18 amount, 19 returnUrl: 'https://yoursite.com/payment/callback', 20 udf1: orderNumber, 21 }); 22 23 if (result.isSuccessful()) { 24 return reply.redirect(result.formUrl!); 25 } 26 27 return reply.status(500).send({ error: 'Payment registration failed' }); 28});
Cela évite l'instanciation manuelle du client et garantit que le cycle de vie du client SATIM est géré par Fastify.
Besoin d'aide pour votre intégration ?
L'intégration de paiements est un travail à enjeux élevés où les bugs se traduisent directement en perte de revenus ou en problèmes de conformité. Si vous avez besoin d'aide experte pour votre intégration SATIM CIB/Edahabia, de la configuration initiale et la coordination bancaire au déploiement en production et au support continu, je propose du consulting dédié pour les systèmes de paiement algériens.