Complete Guide to SATIM CIB and Edahabia Payment Integration in Algeria
Step-by-step technical guide covering SATIM API setup, CIB credit card and Edahabia debit card payment flows, error handling, and production deployment for Algerian e-commerce.
The Algerian Payment Landscape
If you are building an e-commerce platform or any application that accepts online payments in Algeria, you will inevitably encounter SATIM. The Societe d'Automatisation des Transactions Interbancaires et de Monetique (SATIM) is the national electronic payment switch that processes virtually all card-based transactions in Algeria. It sits between merchants, banks, and card networks, acting as the central routing and settlement layer.
Two card types dominate the Algerian market:
- CIB (Carte Interbancaire): The standard interbank credit/debit card issued by Algerian banks. CIB cards carry the CIB network logo and are accepted at POS terminals and online merchants across the country.
- Edahabia: Algerie Poste's debit card, linked to CCP (Compte Courant Postal) accounts. With over 20 million CCP accounts in Algeria, Edahabia represents the largest potential payment base in the country.
Both card types are processed through SATIM's payment gateway (sometimes referred to as SATIM e-payment or SATIM IPAY). From a technical standpoint, the integration flow is nearly identical for both. The gateway handles card type detection automatically based on the card number entered by the customer.
Prerequisites
Before writing any code, you need the following:
- A merchant account with a bank that supports SATIM e-payment (most major Algerian banks do: BNA, CPA, BEA, BADR, Gulf Bank Algeria, among others).
- Terminal ID (format:
E010XXXXXX), assigned to identify your merchant terminal. Provided through the SATIM portal. - Username and password for the SATIM gateway API, provided through the SATIM portal after your merchant account is approved.
- Test credentials for the sandbox environment at
test2.satim.dz, also provided through the SATIM portal.
The onboarding process is handled through the SATIM portal at cibweb.dz. Follow the instructions there to submit your documents and receive your credentials. Timelines vary, so start this process early. If you need help navigating the certification, we offer SATIM certification consulting.
How the Payment Flow Works
SATIM uses a redirect-based payment flow, conceptually similar to 3D Secure or Stripe Checkout. Your server never handles raw card data. Here is the full sequence:
- Order Registration: Your backend calls SATIM's
register.doendpoint with the order amount, a unique order number, and callback URLs. SATIM responds with anorderIdand aformUrl. - Customer Redirect: You redirect the customer's browser to the
formUrl. SATIM hosts the payment page where the customer enters their CIB or Edahabia card details. - Payment Processing: SATIM processes the transaction with the issuing bank. The customer may see an OTP/password verification screen (similar to 3DS).
- Callback Redirect: After payment (success or failure), SATIM redirects the customer back to your
returnUrlorfailUrlwith theorderIdas a query parameter. - Server-Side Confirmation: Your backend calls the
acknowledgeTransaction.doendpoint to verify the transaction status. This is the critical step; never trust the client-side redirect alone.
Customer Your Server SATIM Gateway Bank
| | | |
| Place Order | | |
|----------------->| | |
| | register.do | |
| |------------------->| |
| | orderId + formUrl | |
| |<-------------------| |
| Redirect to | | |
| formUrl | | |
|<-----------------| | |
| Enter card info | | |
|---------------------------------------->| |
| | | Process payment |
| | |----------------->|
| | | Auth response |
| | |<-----------------|
| Redirect to | | |
| returnUrl | | |
|<-----------------------------------------| |
| Page loads | | |
|----------------->| | |
| | acknowledgeTransaction.do |
| |------------------->| |
| | Payment status | |
| |<-------------------| |
| Order confirmed | | |
|<-----------------| | |
Setting Up with satim-ts
The @bakissation/satim package is a production-grade TypeScript SDK that wraps the SATIM API with strict types, input validation, automatic amount conversion, and comprehensive error handling.
Installation
1npm install @bakissation/satim
The SDK supports both ESM and CommonJS, requires Node.js 18 or later, and has zero external dependencies (it uses the native fetch API).
Configuration
The simplest approach is environment variables. Create a .env file:
1SATIM_USERNAME=your_merchant_username 2SATIM_PASSWORD=your_merchant_password 3SATIM_TERMINAL_ID=E010XXXXXX 4SATIM_API_URL=https://test2.satim.dz/payment/rest
Then initialize the client:
1import { createSatimClient, fromEnv } from '@bakissation/satim'; 2 3const satim = createSatimClient(fromEnv());
For more explicit control, pass the configuration directly:
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, // switch to API_BASE_URLS.PRODUCTION for live 8 language: 'fr', 9 currency: '012', // DZD (ISO 4217) 10});
Implementing the Payment Flow
Step 1: Register the Order
When your customer is ready to pay, create an order on your 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 chars, must be unique per transaction 9 amount: amountDzd, // In DZD (minimum 50 DZD), auto-converted to minor units 10 returnUrl: 'https://yoursite.com/payment/callback', 11 failUrl: 'https://yoursite.com/payment/failed', 12 description: 'Purchase from YourStore', 13 udf1: orderNumber, // Required: your internal reference 14 idempotencyKey: randomUUID(), // Prevents duplicate orders on retry 15 }); 16 17 if (response.isSuccessful()) { 18 // Save response.orderId to your database, linked to the order 19 return { 20 orderId: response.orderId, 21 redirectUrl: response.formUrl, 22 }; 23 } 24 25 throw new Error('Failed to register order with SATIM'); 26}
Key points about the register call:
- Amount handling: Pass amounts in DZD as whole numbers or decimals (e.g.,
5000for 5,000 DZD, or806.5for 806.50 DZD). The SDK automatically converts to minor units (centimes) as required by the API. - Order number: Maximum 10 characters. Must be unique per transaction. If you reuse an order number, SATIM will return error code 1.
- udf1: Required by SATIM. Use it for your internal reference (invoice number, order ID, etc.).
- Idempotency: Always pass an
idempotencyKeyin production to prevent duplicate charges if your request is retried due to network issues.
Step 2: Redirect the Customer
On your frontend, redirect the customer to the formUrl returned by the register call:
1// In your API response handler (frontend) 2window.location.href = redirectUrl;
Or if you are building an API-first backend, return the URL to your frontend/mobile client and let it handle the redirect. The SATIM payment page supports French, English, and Arabic, configurable via the language parameter.
Step 3: Handle the Callback
After the customer completes (or abandons) the payment, SATIM redirects them to your returnUrl or failUrl with an orderId query parameter. On your callback endpoint, verify the payment server-side:
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 // Payment successful, 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); // e.g., 6280****7215 11 12 // Update your order status in the database 13 // Deliver the product/service 14 return { success: true, orderNumber: status.orderNumber }; 15 } 16 17 // Payment not successful 18 console.log('Payment status:', interpretOrderStatus(status.orderStatus)); 19 console.log('Details:', status.actionCodeDescription); 20 return { success: false, reason: status.actionCodeDescription }; 21}
The confirm method (aliased as getOrderStatus) calls SATIM's acknowledgeTransaction.do endpoint. This is the only reliable way to know if a payment succeeded. Never rely solely on the redirect URL the customer landed on, as it can be spoofed or manipulated.
Step 4: Refunds (When Needed)
To refund a completed transaction:
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}
Refunds must reference the original orderId returned during registration. Partial refunds are supported by passing a smaller amount than the original transaction.
CIB vs Edahabia: What Developers Need to Know
From an API integration perspective, CIB and Edahabia payments are handled identically by the SATIM gateway. You do not need to specify the card type when registering an order. The gateway determines the card type from the card number the customer enters on the payment page.
There are a few practical differences to be aware of:
- Card number format: CIB cards typically start with
6280(16 digits). Edahabia cards follow Algerie Poste's numbering scheme. Both are processed through the sameregister.doandacknowledgeTransaction.doendpoints. - Transaction limits: Edahabia cards may have lower daily transaction limits compared to CIB cards, depending on the cardholder's configuration with Algerie Poste. Your application should gracefully handle "limit exceeded" declines.
- Settlement timelines: Settlement to your merchant account may differ slightly between CIB and Edahabia transactions, depending on your acquiring bank's arrangement. This is a banking operations concern, not a code concern.
- OTP verification: Both card types may require an OTP or password entry on the SATIM payment page. The
passwordfield on the test cards is used for this purpose in the sandbox.
If you specifically need to handle bill payments or a particular transaction type, you can use the fundingTypeIndicator parameter (e.g., 'CP' or '698'), but for standard e-commerce purchases, this is not required.
Error Handling
The SDK provides typed error classes for every failure scenario. Robust error handling is especially important for payment integrations where silent failures can lead to lost revenue or double charges.
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 // Bad input: invalid amount, missing fields, etc. 23 console.error(`Validation error [${error.code}]:`, error.message); 24 } else if (error instanceof SatimApiError) { 25 // SATIM rejected the request 26 console.error(`SATIM error [${error.satimErrorCode}]:`, error.message); 27 } else if (error instanceof TimeoutError) { 28 // Request timed out, safe to retry with same idempotency key 29 console.error(`Timeout after ${error.timeoutMs}ms`); 30 } else if (error instanceof HttpError) { 31 // Network or HTTP-level failure 32 console.error(`HTTP error [${error.httpStatus}]:`, error.message); 33 } else if (error instanceof ConfigError) { 34 // Missing or invalid configuration 35 console.error('Config error, missing keys:', error.missingKeys); 36 } 37 throw error; 38 } 39}
SATIM Error Code Reference
These are the error codes returned by the SATIM API across its endpoints:
| Code | register.do | acknowledgeTransaction.do | refund.do |
|---|---|---|---|
| 0 | Success | Success | Success |
| 1 | Order already processed | - | - |
| 2 | - | Payment credentials error | - |
| 3 | Unknown currency | - | - |
| 4 | Required param missing | - | - |
| 5 | Invalid parameter | Access denied | Access denied / Invalid amount |
| 6 | - | Unregistered order | Unregistered order |
| 7 | System error | System error | System error |
| 14 | Invalid paymentway | - | - |
Order Status Codes
After calling confirm() or getOrderStatus(), check the orderStatus field:
| Code | Meaning | Action |
|---|---|---|
| 0 | Registered but not paid | Customer abandoned or has not completed payment yet |
| -1 | Unknown decline | Contact SATIM support if persistent |
| 1 | Approved (pre-authorization held) | Awaiting deposit |
| 2 | Deposited (payment complete) | Fulfill the order |
| 3 | Authorization reversed | Payment was reversed before settlement |
| 4 | Refunded | Transaction was refunded |
| 6 | Declined | Card was declined by the issuing bank |
Testing with the SATIM Sandbox
SATIM provides a sandbox environment at https://test2.satim.dz/payment/rest for integration testing. Use the following test cards:
| Card Number | CVV2 | Expiry | Password | Scenario |
|---|---|---|---|---|
| 6280581110007215 | 373 | 01/2027 | 123456 | Successful payment |
| 6280581110006712 | 897 | 01/2027 | 123456 | Temporarily blocked card |
| 6280580610061110 | 260 | 01/2027 | 123456 | Insufficient balance |
| 6280580610061219 | 049 | 01/2027 | 123456 | Limit exceeded |
| 6280581110006514 | 205 | 01/2027 | 123456 | Incorrect CVV2 |
| 6280580610061011 | 992 | 01/2027 | 123456 | Valid credit card |
When testing:
- Set your
SATIM_API_URLtohttps://test2.satim.dz/payment/rest. - Use the test credentials provided by your bank (not your production credentials).
- Test every scenario: successful payment, declined card, insufficient balance, timeout handling, and duplicate order prevention.
- Verify that your
confirm()call correctly interprets each order status. - Test the full user journey, including what happens when the customer closes the browser on the SATIM payment page without completing the transaction.
Production Deployment Checklist
Before going live, work through this checklist:
Configuration
- Switch
SATIM_API_URLtohttps://satim.dz/payment/rest - Use production credentials (username, password, terminal ID) from your bank
- Store all credentials in environment variables, never in source code
- Ensure
.envfiles are in.gitignore - Set
NODE_ENV=productionto disable verbose dev logging
Security
- Use POST method for all SATIM API calls (the SDK defaults to this)
- Always verify payment status server-side via
confirm(), never trust client-side redirects - Serve your callback URLs over HTTPS only
- Implement idempotency keys on every
register()call to prevent duplicate charges - Never log or store full card numbers (the SDK never exposes them)
Reliability
- Handle all error types (
SatimApiError,TimeoutError,HttpError,ValidationError) - Implement retry logic for
TimeoutErrorscenarios (using the same idempotency key) - Set up monitoring/alerting for failed payments
- Log transaction results (using the SDK's built-in logger or your own via the
customLoggeroption) - Store the SATIM
orderIdin your database for every transaction, enabling future status checks and refunds
Testing
- Run end-to-end tests against the sandbox before switching to production
- Test with all sandbox card scenarios (success, decline, insufficient funds, blocked)
- Verify refund flow works correctly
- Test what happens when SATIM is unreachable (timeout handling)
Compliance
- Verify with your bank that your integration is approved for production use
- Ensure your terms of service and privacy policy cover electronic payments
- Keep records of all transactions as required by Algerian commerce regulations
Advanced: Integrating with Fastify
If you are using Fastify as your web framework, the @bakissation/fastify-satim plugin provides a streamlined integration that registers the SATIM client as a Fastify decorator:
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});
This avoids manual client instantiation and ensures the SATIM client lifecycle is managed by Fastify.
Need Help with Your Integration?
Integrating payments is high-stakes work where bugs translate directly into lost revenue or compliance issues. If you need expert help with your SATIM CIB/Edahabia integration, from initial setup and bank coordination to production deployment and ongoing support, I offer dedicated consulting for Algerian payment systems.