Aller au contenu principal

Référence API

Référence de l'API actuelle ChinqIT Verify. Base : https://sms.chinqit.com/api/v2.

Authentification

Toutes les requêtes doivent inclure la clé API dans l'en-tête :

X-API-Key: votre-clé-api

Les clés API sont fournies par ChinqIT Verify. Contactez votre administrateur pour en obtenir une.

URL de base

  • Production : https://sms.chinqit.com/api/v2

Format du numéro (Mauritanie)

  • Canonique : +222XXXXXXXX (ex. +22238089336)
  • Avec indicatif : 222XXXXXXXX — normalisé en +222XXXXXXXX
  • Local : XXXXXXXX (doit commencer par 2, 3 ou 4) — normalisé en +222XXXXXXXX

Les formats invalides renvoient 400 Bad Request.


POST /api/v2/notify

Envoyer un SMS.

Requête :

{
"message": "Bonjour, ceci est un message de test",
"phoneNumber": "+22238089336"
}
ParamètreTypeObligatoireDescription
messagestringOuiContenu du SMS
phoneNumberstringOuiDestinataire (format Mauritanie)

Réponse :

{
"success": true,
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"queued": true
}

Les messages sont mis en file et traités de façon asynchrone. Utilisez messageId pour suivre la livraison.

Exemple :

curl -X POST https://sms.chinqit.com/api/v2/notify \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+22238089336", "message": "Bonjour"}'

POST /api/v2/code

Demander un OTP (envoyer un code de vérification par SMS ou WhatsApp).

Requête :

{
"phoneNumber": "+22238089336",
"hash": "ABC123XYZ45",
"language": "en",
"channel": "sms"
}
ParamètreTypeObligatoireDescription
phoneNumberstringOuiDestinataire (format Mauritanie)
hashstringNonHash SMS Retriever 11 caractères (SMS uniquement). Voir Guide SMS Retriever.
languagestringNonfr (défaut), en, ar
channelstringNonsms (défaut) ou whatsapp

Réponse :

{
"success": true,
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"expiresIn": 600
}
  • Code à 6 chiffres, valide 10 minutes (600 secondes), 3 tentatives de vérification max.

Exemple :

curl -X POST https://sms.chinqit.com/api/v2/code \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+22238089336", "language": "en"}'

POST /api/v2/check

Vérifier un code OTP.

Requête :

{
"phoneNumber": "+22238089336",
"otp": "123456",
"channel": "sms"
}
ParamètreTypeObligatoireDescription
phoneNumberstringOuiNuméro auquel l'OTP a été envoyé
otpstringOuiCode à 6 chiffres
channelstringNonsms (défaut) ou whatsapp

Réponse (succès) :

{
"success": true,
"message": "OTP verified successfully",
"verified": true
}

Réponse (invalide) :

{
"success": false,
"message": "Invalid OTP",
"retriesLeft": 2
}

Exemple :

curl -X POST https://sms.chinqit.com/api/v2/check \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+22238089336", "otp": "123456"}'

GET /api/v2/notify/:messageId

Obtenir le statut de livraison d'un message.

Réponse :

{
"success": true,
"data": {
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"sent": true,
"phoneNumber": "+22238089336",
"type": "message",
"createdAt": "...",
"updatedAt": "..."
}
}

Exemple :

curl -H "X-API-Key: YOUR_API_KEY" \
"https://sms.chinqit.com/api/v2/notify/550e8400-e29b-41d4-a716-446655440000"

Format des erreurs

{
"success": false,
"message": "Description de l'erreur",
"errors": []
}

Codes HTTP : 200, 400, 401, 403, 404, 429, 500, 502.

📱 Exemple pratique : Notification SMS après achat

Voici un exemple complet d'envoi d'une notification SMS transactionnelle après qu'un utilisateur ait effectué un achat.

Scénario d'utilisation

Après qu'un utilisateur finalise un achat sur votre plateforme e-commerce, envoyez-lui une confirmation SMS avec les détails de la commande.

JavaScript/Node.js

const https = require("https");
const querystring = require("querystring");

class ChinqitSMSService {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseURL = "https://sms.chinqit.com/api/v2";
}

/**
* Envoie une notification SMS après achat
* @param {string} phoneNumber - Numéro de téléphone du client (format Mauritanie)
* @param {Object} orderDetails - Détails de la commande
* @param {string} orderDetails.orderId - ID de la commande
* @param {number} orderDetails.amount - Montant total
* @param {string} orderDetails.currency - Devise (MRU, EUR, etc.)
* @param {Array} orderDetails.items - Articles commandés
* @returns {Promise<Object>} Résultat de l'envoi
*/
async sendPurchaseConfirmationSMS(phoneNumber, orderDetails) {
try {
// Validation des paramètres d'entrée
this.validatePhoneNumber(phoneNumber);
this.validateOrderDetails(orderDetails);

// Construction du message SMS
const message = this.buildPurchaseMessage(orderDetails);

// Envoi du SMS
const result = await this.sendSMS(phoneNumber, message);

// Logging pour suivi
console.log(
`✅ SMS de confirmation envoyé pour commande ${orderDetails.orderId} à ${phoneNumber}`,
);

return {
success: true,
messageId: result.messageId,
orderId: orderDetails.orderId,
phoneNumber: phoneNumber,
sentAt: new Date().toISOString(),
};
} catch (error) {
console.error(
`❌ Échec envoi SMS pour commande ${orderDetails.orderId}:`,
error.message,
);

// Relancer l'erreur avec plus de contexte
throw new Error(`Échec envoi SMS de confirmation: ${error.message}`);
}
}

/**
* Envoie un SMS via l'API Chinqit
*/
async sendSMS(phoneNumber, message) {
return new Promise((resolve, reject) => {
const postData = JSON.stringify({
phoneNumber: phoneNumber,
message: message,
});

const options = {
hostname: "sms.chinqit.com",
port: 443,
path: "/api/v2/notify",
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": this.apiKey,
"Content-Length": Buffer.byteLength(postData),
},
};

const req = https.request(options, (res) => {
let data = "";

res.on("data", (chunk) => {
data += chunk;
});

res.on("end", () => {
try {
const response = JSON.parse(data);

if (res.statusCode === 200 && response.success) {
resolve(response);
} else {
reject(
new Error(
`Erreur API: ${response.message || "Erreur inconnue"}`,
),
);
}
} catch (parseError) {
reject(new Error(`Erreur parsing réponse: ${parseError.message}`));
}
});
});

req.on("error", (error) => {
reject(new Error(`Erreur réseau: ${error.message}`));
});

req.write(postData);
req.end();
});
}

/**
* Valide le format du numéro de téléphone
*/
validatePhoneNumber(phoneNumber) {
if (!phoneNumber || typeof phoneNumber !== "string") {
throw new Error("Numéro de téléphone requis");
}

// Formats acceptés pour la Mauritanie
const phoneRegex = /^(\+222|222)?[234]\d{7}$/;
if (!phoneRegex.test(phoneNumber.replace(/\s+/g, ""))) {
throw new Error(
"Format de numéro de téléphone invalide pour la Mauritanie",
);
}
}

/**
* Valide les détails de la commande
*/
validateOrderDetails(orderDetails) {
if (!orderDetails || typeof orderDetails !== "object") {
throw new Error("Détails de commande requis");
}

if (!orderDetails.orderId || typeof orderDetails.orderId !== "string") {
throw new Error("ID de commande requis");
}

if (
!orderDetails.amount ||
typeof orderDetails.amount !== "number" ||
orderDetails.amount <= 0
) {
throw new Error("Montant de commande invalide");
}

if (!orderDetails.currency || typeof orderDetails.currency !== "string") {
throw new Error("Devise requise");
}
}

/**
* Construit le message SMS de confirmation d'achat
*/
buildPurchaseMessage(orderDetails) {
const { orderId, amount, currency, items = [] } = orderDetails;

// Message concis adapté aux limitations SMS (160 caractères max)
let message = `Chinqit: Commande ${orderId} confirmée. Montant: ${amount} ${currency}.`;

// Ajouter des détails si l'espace le permet
if (items.length > 0 && message.length < 120) {
const itemCount = items.length;
message += ` ${itemCount} article${itemCount > 1 ? "s" : ""}.`;
}

// S'assurer que le message ne dépasse pas 160 caractères
if (message.length > 160) {
message = message.substring(0, 157) + "...";
}

return message;
}
}

// Exemple d'utilisation dans un contrôleur d'achat
class OrderController {
constructor(smsService) {
this.smsService = smsService;
}

async completePurchase(req, res) {
try {
const { phoneNumber, orderDetails } = req.body;

// Logique métier d'achat (paiement, mise à jour stock, etc.)
const order = await this.processOrder(orderDetails);

// Envoi de la notification SMS en arrière-plan
// Ne pas bloquer la réponse sur l'envoi SMS
setImmediate(async () => {
try {
await this.smsService.sendPurchaseConfirmationSMS(phoneNumber, {
orderId: order.id,
amount: order.total,
currency: order.currency,
items: order.items,
});
} catch (smsError) {
// Logger l'erreur mais ne pas échouer la commande
console.error("Erreur envoi SMS confirmation:", smsError.message);
// Ici vous pourriez vouloir stocker l'erreur pour retry ultérieur
}
});

// Répondre immédiatement avec succès de la commande
res.json({
success: true,
orderId: order.id,
message:
"Commande traitée avec succès. Vous recevrez une confirmation par SMS.",
});
} catch (error) {
console.error("Erreur traitement commande:", error.message);
res.status(500).json({
success: false,
message: "Erreur lors du traitement de la commande",
});
}
}

async processOrder(orderDetails) {
// Simulation du traitement de commande
return {
id: "ORD-" + Date.now(),
total: orderDetails.amount,
currency: orderDetails.currency,
items: orderDetails.items,
status: "confirmed",
};
}
}

// Initialisation et utilisation
const smsService = new ChinqitSMSService(process.env.CHIQIT_API_KEY);
const orderController = new OrderController(smsService);

// Exemple de route Express.js
app.post("/api/orders", (req, res) =>
orderController.completePurchase(req, res),
);

Python

import requests
import json
import logging
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from datetime import datetime

@dataclass
class OrderDetails:
order_id: str
amount: float
currency: str
items: List[Dict[str, Any]]

class ChinqitSMSService:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = 'https://sms.chinqit.com/api/v2'
self.logger = logging.getLogger(__name__)

def send_purchase_confirmation_sms(
self,
phone_number: str,
order_details: OrderDetails
) -> Dict[str, Any]:
"""
Envoie une notification SMS après achat

Args:
phone_number: Numéro de téléphone du client (format Mauritanie)
order_details: Détails de la commande

Returns:
Dictionnaire avec le résultat de l'envoi

Raises:
ValueError: Si les paramètres sont invalides
requests.RequestException: Si l'envoi échoue
"""
try:
# Validation des paramètres
self._validate_phone_number(phone_number)
self._validate_order_details(order_details)

# Construction du message
message = self._build_purchase_message(order_details)

# Envoi du SMS
result = self._send_sms(phone_number, message)

# Logging
self.logger.info(
f"SMS de confirmation envoyé pour commande {order_details.order_id} à {phone_number}"
)

return {
'success': True,
'message_id': result['messageId'],
'order_id': order_details.order_id,
'phone_number': phone_number,
'sent_at': datetime.now().isoformat()
}

except Exception as error:
self.logger.error(
f"Échec envoi SMS pour commande {order_details.order_id}: {str(error)}"
)
raise

def _send_sms(self, phone_number: str, message: str) -> Dict[str, Any]:
"""Envoie un SMS via l'API Chinqit"""
url = f"{self.base_url}/notify"
headers = {
'Content-Type': 'application/json',
'X-API-Key': self.api_key
}
payload = {
'phoneNumber': phone_number,
'message': message
}

response = requests.post(url, json=payload, headers=headers, timeout=30)
response.raise_for_status()

result = response.json()

if not result.get('success'):
raise requests.RequestException(f"Erreur API: {result.get('message', 'Erreur inconnue')}")

return result

def _validate_phone_number(self, phone_number: str) -> None:
"""Valide le format du numéro de téléphone mauritanien"""
if not phone_number or not isinstance(phone_number, str):
raise ValueError('Numéro de téléphone requis')

import re
# Formats acceptés pour la Mauritanie
phone_pattern = r'^(\+222|222)?[234]\d{7}$'
if not re.match(phone_pattern, phone_number.replace(' ', '')):
raise ValueError('Format de numéro de téléphone invalide pour la Mauritanie')

def _validate_order_details(self, order_details: OrderDetails) -> None:
"""Valide les détails de la commande"""
if not order_details:
raise ValueError('Détails de commande requis')

if not order_details.order_id:
raise ValueError('ID de commande requis')

if not order_details.amount or order_details.amount <= 0:
raise ValueError('Montant de commande invalide')

if not order_details.currency:
raise ValueError('Devise requise')

def _build_purchase_message(self, order_details: OrderDetails) -> str:
"""Construit le message SMS de confirmation d'achat"""
# Message concis adapté aux limitations SMS
message = f"Chinqit: Commande {order_details.order_id} confirmée. Montant: {order_details.amount} {order_details.currency}."

# Ajouter des détails si l'espace le permet
if order_details.items and len(message) < 120:
item_count = len(order_details.items)
plural = 's' if item_count > 1 else ''
message += f" {item_count} article{plural}."

# Limiter à 160 caractères
if len(message) > 160:
message = message[:157] + '...'

return message

class OrderController:
def __init__(self, sms_service: ChinqitSMSService):
self.sms_service = sms_service
self.logger = logging.getLogger(__name__)

async def complete_purchase(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Traite une commande et envoie une confirmation SMS
"""
try:
phone_number = request_data.get('phone_number')
order_data = request_data.get('order_details', {})

# Traitement de la commande
order = await self._process_order(order_data)

# Envoi SMS en arrière-plan (ne pas bloquer la réponse)
import asyncio
asyncio.create_task(self._send_confirmation_sms_async(phone_number, order))

# Répondre immédiatement
return {
'success': True,
'order_id': order['id'],
'message': 'Commande traitée avec succès. Vous recevrez une confirmation par SMS.'
}

except Exception as error:
self.logger.error(f"Erreur traitement commande: {str(error)}")
raise

async def _process_order(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
"""Simule le traitement d'une commande"""
# Ici vous auriez la logique réelle de traitement de commande
return {
'id': f'ORD-{int(datetime.now().timestamp())}',
'total': order_data.get('amount', 0),
'currency': order_data.get('currency', 'MRU'),
'items': order_data.get('items', []),
'status': 'confirmed'
}

async def _send_confirmation_sms_async(self, phone_number: str, order: Dict[str, Any]) -> None:
"""Envoie la confirmation SMS de manière asynchrone"""
try:
order_details = OrderDetails(
order_id=order['id'],
amount=order['total'],
currency=order['currency'],
items=order['items']
)

await asyncio.get_event_loop().run_in_executor(
None,
self.sms_service.send_purchase_confirmation_sms,
phone_number,
order_details
)

except Exception as sms_error:
# Logger l'erreur mais ne pas échouer la commande
self.logger.error(f"Erreur envoi SMS confirmation: {str(sms_error)}")
# Ici vous pourriez implémenter une logique de retry ou de stockage pour retry ultérieur

# Configuration du logging
logging.basicConfig(level=logging.INFO)

# Utilisation
import os
sms_service = ChinqitSMSService(os.getenv('CHIQIT_API_KEY'))
order_controller = OrderController(sms_service)

# Exemple d'utilisation avec FastAPI ou similaire
# @app.post('/api/orders')
# async def create_order(request_data: dict):
# return await order_controller.complete_purchase(request_data)

PHP

<?php

class ChinqitSMSService {
private string $apiKey;
private string $baseUrl;
private $logger;

public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
$this->baseUrl = 'https://sms.chinqit.com/api/v2';
$this->logger = new Monolog\Logger('chinqit-sms');
}

/**
* Envoie une notification SMS après achat
*/
public function sendPurchaseConfirmationSMS(string $phoneNumber, array $orderDetails): array {
try {
// Validation
$this->validatePhoneNumber($phoneNumber);
$this->validateOrderDetails($orderDetails);

// Construction du message
$message = $this->buildPurchaseMessage($orderDetails);

// Envoi du SMS
$result = $this->sendSMS($phoneNumber, $message);

$this->logger->info("SMS de confirmation envoyé pour commande {$orderDetails['orderId']} à {$phoneNumber}");

return [
'success' => true,
'messageId' => $result['messageId'],
'orderId' => $orderDetails['orderId'],
'phoneNumber' => $phoneNumber,
'sentAt' => date('c')
];

} catch (Exception $error) {
$this->logger->error("Échec envoi SMS pour commande {$orderDetails['orderId']}: " . $error->getMessage());
throw new Exception("Échec envoi SMS de confirmation: " . $error->getMessage());
}
}

private function sendSMS(string $phoneNumber, string $message): array {
$url = $this->baseUrl . '/notify';
$payload = json_encode([
'phoneNumber' => $phoneNumber,
'message' => $message
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $this->apiKey,
'Content-Length: ' . strlen($payload)
],
CURLOPT_TIMEOUT => 30
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);

if ($curlError) {
throw new Exception("Erreur réseau: $curlError");
}

$result = json_decode($response, true);

if ($httpCode !== 200 || !$result['success']) {
throw new Exception("Erreur API: " . ($result['message'] ?? 'Erreur inconnue'));
}

return $result;
}

private function validatePhoneNumber(string $phoneNumber): void {
if (empty($phoneNumber)) {
throw new InvalidArgumentException('Numéro de téléphone requis');
}

// Formats acceptés pour la Mauritanie
if (!preg_match('/^(\+222|222)?[234]\d{7}$/', str_replace(' ', '', $phoneNumber))) {
throw new InvalidArgumentException('Format de numéro de téléphone invalide pour la Mauritanie');
}
}

private function validateOrderDetails(array $orderDetails): void {
if (empty($orderDetails['orderId'])) {
throw new InvalidArgumentException('ID de commande requis');
}

if (!isset($orderDetails['amount']) || $orderDetails['amount'] <= 0) {
throw new InvalidArgumentException('Montant de commande invalide');
}

if (empty($orderDetails['currency'])) {
throw new InvalidArgumentException('Devise requise');
}
}

private function buildPurchaseMessage(array $orderDetails): string {
$message = "Chinqit: Commande {$orderDetails['orderId']} confirmée. Montant: {$orderDetails['amount']} {$orderDetails['currency']}.";

if (!empty($orderDetails['items']) && strlen($message) < 120) {
$itemCount = count($orderDetails['items']);
$plural = $itemCount > 1 ? 's' : '';
$message .= " {$itemCount} article{$plural}.";
}

// Limiter à 160 caractères
if (strlen($message) > 160) {
$message = substr($message, 0, 157) . '...';
}

return $message;
}
}

class OrderController {
private ChinqitSMSService $smsService;
private $logger;

public function __construct(ChinqitSMSService $smsService) {
$this->smsService = $smsService;
$this->logger = new Monolog\Logger('order-controller');
}

public function completePurchase(array $requestData): array {
try {
$phoneNumber = $requestData['phone_number'] ?? '';
$orderData = $requestData['order_details'] ?? [];

// Traitement de la commande
$order = $this->processOrder($orderData);

// Envoi SMS en arrière-plan
$this->sendConfirmationSMSAsync($phoneNumber, $order);

return [
'success' => true,
'order_id' => $order['id'],
'message' => 'Commande traitée avec succès. Vous recevrez une confirmation par SMS.'
];

} catch (Exception $error) {
$this->logger->error('Erreur traitement commande: ' . $error->getMessage());
throw $error;
}
}

private function processOrder(array $orderData): array {
return [
'id' => 'ORD-' . time(),
'total' => $orderData['amount'] ?? 0,
'currency' => $orderData['currency'] ?? 'MRU',
'items' => $orderData['items'] ?? [],
'status' => 'confirmed'
];
}

private function sendConfirmationSMSAsync(string $phoneNumber, array $order): void {
// Envoi asynchrone (vous pourriez utiliser une queue comme Redis ou RabbitMQ)
$smsService = $this->smsService;
$logger = $this->logger;

// Simulation d'envoi asynchrone
register_shutdown_function(function() use ($smsService, $phoneNumber, $order, $logger) {
try {
$smsService->sendPurchaseConfirmationSMS($phoneNumber, [
'orderId' => $order['id'],
'amount' => $order['total'],
'currency' => $order['currency'],
'items' => $order['items']
]);
} catch (Exception $smsError) {
$logger->error('Erreur envoi SMS confirmation: ' . $smsError->getMessage());
}
});
}
}

// Utilisation
$smsService = new ChinqitSMSService(getenv('CHIQIT_API_KEY'));
$orderController = new OrderController($smsService);

// Exemple avec Slim Framework ou similaire
// $app->post('/api/orders', function($request, $response) use ($orderController) {
// $data = $request->getParsedBody();
// $result = $orderController->completePurchase($data);
// return $response->withJson($result);
// });

?>

Bonnes pratiques pour les SMS transactionnels

  1. Envoi asynchrone : Ne bloquez jamais la réponse utilisateur sur l'envoi SMS
  2. Validation stricte : Validez toujours les numéros de téléphone et les données
  3. Messages concis : Respectez la limite de 160 caractères
  4. Logging complet : Enregistrez tous les envois et erreurs pour le débogage
  5. Gestion d'erreurs : Gérez gracieusement les échecs d'envoi sans affecter la transaction
  6. Retry logic : Implémentez une logique de nouvelle tentative pour les erreurs temporaires
  7. Conformité : Respectez les réglementations locales sur les SMS commerciaux

Exemple de payload de requête

{
"phone_number": "+22238089336",
"order_details": {
"amount": 15000,
"currency": "MRU",
"items": [
{ "name": "Produit A", "quantity": 2 },
{ "name": "Produit B", "quantity": 1 }
]
}
}

Exemple de réponse

{
"success": true,
"order_id": "ORD-1707523456",
"message": "Commande traitée avec succès. Vous recevrez une confirmation par SMS."
}

Voir aussi