Exemples d'intégration
Des programmes complets, prêts à exécuter dans la sandbox. Chacun est testé contre l'API à chaque mise à jour de cette documentation.
Fichiers à télécharger
| Langage | Client (signature comprise) | Parcours complet |
|---|---|---|
| Node.js 18+ | coveraf.js | parcours-complet.js |
| Python 3.8+ | coveraf.py | parcours-complet.py |
| PHP 7.4+ | coveraf.php | parcours-complet.php |
| bash (curl, openssl) | coveraf.sh | - |
Aucune dépendance : seulement la bibliothèque standard de chaque langage (et l'extension curl pour PHP). Les fichiers Node.js sont des modules ES : placez-les dans un projet dont le package.json contient "type": "module", ou renommez-les en .mjs (import compris : ./coveraf.mjs).
Parcours complet
Du devis aux documents : devis, Conditions Générales, souscription, paiement (mode A ou B selon votre compte), contrat, puis téléchargement des PDF.
// Parcours complet de souscription, du devis aux documents (Node.js 18+).
// Utilise le client coveraf.js (même dossier). Modules ES : "type": "module"
// dans package.json, ou fichiers renommés en .mjs (import compris). En sandbox :
// COVERAF_CLE_API=cvr_test_... COVERAF_SECRET=cvr_sig_... node parcours-complet.js
import crypto from 'node:crypto'
import { writeFile } from 'node:fs/promises'
import { appelCoveraf } from './coveraf.js'
const BASE_URL = process.env.COVERAF_BASE_URL || 'https://api.coveraf.com'
const attendre = (ms) => new Promise((ok) => setTimeout(ok, ms))
async function telecharger(chemin, fichier) {
const reponse = await fetch(BASE_URL + chemin, { headers: { Authorization: `Bearer ${process.env.COVERAF_CLE_API}` } })
if (!reponse.ok) throw new Error(`Document ${chemin} : ${(await reponse.json()).erreur.code}`)
await writeFile(fichier, Buffer.from(await reponse.arrayBuffer()))
console.log(' enregistré :', fichier)
}
// 1. Devis : le prix est calculé par l'API.
const devis = await appelCoveraf('POST', '/v1/devis', {
offre: 'securvie_500k',
donnees: { assure: { date_naissance: '1985-06-15' } },
reference_partenaire: 'CMD-2026-0042',
})
console.log(`1. Devis ${devis.id} : ${devis.prime.totale} FCFA, valable jusqu'au ${devis.expire_le}`)
// 2. Conditions Générales, à présenter au client AVANT le paiement.
console.log('2. Conditions Générales')
await telecharger(`/v1/produits/${devis.offre}/conditions-generales`, 'conditions-generales.pdf')
// 3. Souscription : la demande du client. Aucun contrat n'existe encore.
const souscription = await appelCoveraf('POST', '/v1/souscriptions', {
devis_id: devis.id,
souscripteur: { nom: 'KONE', prenom: 'Awa', telephone: '+2250701020304', email: 'awa.kone@example.com' },
informations: { profession: 'Commerçante', beneficiaires: [{ nom: 'KONE Moussa', lien: 'Conjoint', part: 100 }] },
}, { idempotencyKey: crypto.randomUUID() })
console.log(`3. Souscription ${souscription.id} : ${souscription.montant_a_payer.montant} FCFA à payer (${souscription.mode_paiement})`)
// 4. Paiement, selon le mode du compte.
let numero
if (souscription.mode_paiement === 'encaissement_partenaire') {
// Mode A : vous avez encaissé le client, vous le déclarez.
const payee = await appelCoveraf('POST', `/v1/souscriptions/${souscription.id}/confirmer-paiement`, {
reference_transaction: `OM-${Date.now()}`,
operateur: 'orange',
montant: souscription.montant_a_payer.montant,
}, { idempotencyKey: crypto.randomUUID() })
numero = payee.contrat.numero
} else {
// Mode B : COVERAF encaisse en Mobile Money. En sandbox, 0700000001 réussit.
const enCours = await appelCoveraf('POST', `/v1/souscriptions/${souscription.id}/paiement`, {
operateur: 'orange',
telephone_payeur: '0700000001',
}, { idempotencyKey: crypto.randomUUID() })
console.log(` paiement ${enCours.paiement.id} en attente, le client valide sur son téléphone`)
// Sans notifications : consultation toutes les 5 secondes, 3 minutes au plus.
for (let i = 0; i < 36 && !numero; i++) {
await attendre(5000)
const etat = await appelCoveraf('GET', `/v1/souscriptions/${souscription.id}`)
if (etat.paiement?.statut === 'echoue') throw new Error(`Paiement échoué : ${etat.paiement.motif_echec}`)
numero = etat.contrat?.numero
}
if (!numero) throw new Error('Paiement toujours en attente : réessayez plus tard.')
}
console.log(`4. Paiement confirmé, contrat ${numero} émis`)
// 5. Contrat et documents.
const contrat = await appelCoveraf('GET', `/v1/contrats/${numero}`)
console.log(`5. Contrat ${contrat.statut}, du ${contrat.date_effet} au ${contrat.date_echeance}`)
for (const document of contrat.documents.filter((d) => d.disponible)) {
await telecharger(document.adresse, `${document.type}.pdf`)
}export COVERAF_CLE_API="cvr_test_..."
export COVERAF_SECRET="cvr_sig_..."
node parcours-complet.jsRésultat attendu dans la sandbox :
1. Devis dev_... : 12980 FCFA, valable jusqu'au 2026-11-19T...
2. Conditions Générales
enregistré : conditions-generales.pdf
3. Souscription sou_... : 12980 FCFA à payer (encaissement_partenaire)
4. Paiement confirmé, contrat TEST-CVR-API-... émis
5. Contrat actif, du 2026-11-04 au 2027-11-03
enregistré : conditions_generales.pdf
enregistré : conditions_particulieres.pdfRecevoir les notifications
Serveur minimal qui vérifie la signature de chaque notification avant de la traiter :
import crypto from 'node:crypto'
import express from 'express'
const SECRET_NOTIFICATIONS = process.env.COVERAF_SECRET_NOTIFICATIONS // cvr_whsec_...
export function signatureValide(corpsBrut, timestamp, signature) {
if (!timestamp || !signature) return false
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false // 5 minutes
const attendue = 'v1=' + crypto.createHmac('sha256', SECRET_NOTIFICATIONS).update(`${timestamp}.${corpsBrut}`).digest('hex')
return attendue.length === signature.length && crypto.timingSafeEqual(Buffer.from(attendue), Buffer.from(signature))
}
const app = express()
// Corps BRUT : la signature porte sur les octets reçus, pas sur un JSON relu.
app.post('/coveraf/notifications', express.text({ type: 'application/json' }), (req, res) => {
if (!signatureValide(req.body, req.get('X-Coveraf-Timestamp'), req.get('X-Coveraf-Signature'))) {
return res.status(401).end()
}
const evenement = JSON.parse(req.body)
// Dédoublonnez avec evenement.id : un événement peut arriver plusieurs fois.
res.status(200).end() // répondez vite, traitez ensuite
traiter(evenement)
})Explications détaillées : Notifications (webhooks).
Importez openapi.json : tous les appels sont prêts, avec leurs exemples. La signature des écritures reste à calculer (un script de pré-requête, ou nos clients ci-dessus).