npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lino_sap/campay-plugin

v1.0.0

Published

Plugin d'intégration CamPay pour paiements Mobile Money au Cameroun (MTN et Orange)

Downloads

124

Readme

@linoSAP/campay-plugin

Plugin d'intégration pour l'API de paiement CamPay (Mobile Money Cameroun : MTN et Orange).

npm version License: MIT Node.js Version

Table des matières

Installation

npm install @linoSAP/campay-plugin

Prérequis

  • Node.js >= 18.0.0
  • Un compte CamPay avec des clés API (sandbox ou production)
  • Pour le mode production : compte validé par CamPay (demande "Go Live")

Configuration

Via constructeur (recommandé)

import { CamPayClient } from '@linoSAP/campay-plugin';

const campay = new CamPayClient({
    username: 'votre_app_username',      // Nom d'utilisateur de l'application
    password: 'votre_app_password',      // Mot de passe de l'application
    environment: 'DEV'                   // 'DEV' ou 'PROD'
});

Via variables d'environnement

CAMPAY_USERNAME=votre_app_username
CAMPAY_PASSWORD=votre_app_password
CAMPAY_ENVIRONMENT=DEV
const campay = new CamPayClient(); // Lit automatiquement les variables

Note : Les paramètres explicites priment sur les variables d'environnement.

Utilisation

Initier un paiement (collect)

Déclenche une notification sur le téléphone du client.

const result = await campay.initCollect({
    amount: '1500',                    // Montant en XAF (string)
    currency: 'XAF',                   // Uniquement XAF
    from: '2376XXXXXXXX',              // Numéro client (format 2376XXXXXXXX)
    description: 'Achat sur ma boutique',
    external_reference: 'CMD-001'      // Optionnel : votre référence interne
});

console.log('Référence CamPay:', result.reference);
console.log('Code USSD:', result.ussd_code);
console.log('Opérateur:', result.operator); // 'mtn' ou 'orange'

Vérifier le statut d'une transaction (polling)

const status = await campay.getStatus('la_reference_campay');

if (status.status === 'SUCCESSFUL') {
    console.log('✅ Paiement confirmé');
} else if (status.status === 'FAILED') {
    console.log('❌ Paiement échoué');
} else {
    console.log('⏳ En attente de confirmation');
}

Créer un lien de paiement

Génère une URL à envoyer par SMS, email ou à afficher sur votre site.

const link = await campay.createPaymentLink({
    amount: '5000',
    currency: 'XAF',
    description: 'Facture Décembre 2025',
    external_reference: 'FACT-001',
    redirect_url: 'https://monsite.com/merci',      // Où rediriger après succès
    failure_redirect_url: 'https://monsite.com/echec' // Optionnel : après échec
});

console.log('Lien de paiement:', link.link);
console.log('Référence CamPay:', link.reference);

Récupération du statut après paiement

Votre application a besoin de savoir quand un paiement est confirmé. Voici les trois méthodes possibles.

Méthode 1 : Webhook (RECOMMANDÉ pour la production)

CamPay appelle automatiquement votre serveur dès qu'un paiement change de statut.

Configuration dans votre dashboard CamPay :

  • Renseignez l'URL de votre webhook (ex: https://votre-api.com/campay/webhook)
  • Choisissez les événements à écouter (collect, disburse, etc.)

Dans votre application (Express.js exemple) :

import express from 'express';
const app = express();

app.post('/campay/webhook', express.json(), async (req, res) => {
    const { reference, status, external_reference, amount, operator, signature } = req.body;
    
    // Mettre à jour votre base de données
    await db.orders.update({
        where: { reference: external_reference },
        data: { 
            paymentStatus: status,
            campayRef: reference,
            amount: amount,
            operator: operator
        }
    });
    
    // Répondre rapidement à CamPay (200 OK)
    res.json({ received: true });
});

Vérification de la signature JWT (sécurité) :

import jwt from 'jsonwebtoken';

function verifyWebhookSignature(signature: string, webhookSecret: string): boolean {
    try {
        const decoded = jwt.verify(signature, webhookSecret);
        return true;
    } catch {
        return false;
    }
}

La webhookSecret est votre "Clé webhook de l'application" disponible sur le dashboard.

Méthode 2 : Polling (simple pour le développement)

Votre application interroge périodiquement l'API.

// Après avoir initié un paiement
const collect = await campay.initCollect({ ... });

let status = 'PENDING';
let attempts = 0;
const maxAttempts = 10;

while (status === 'PENDING' && attempts < maxAttempts) {
    await new Promise(r => setTimeout(r, 3000)); // Attendre 3 secondes
    const result = await campay.getStatus(collect.reference);
    status = result.status;
    attempts++;
}

if (status === 'SUCCESSFUL') {
    // Valider la commande
} else if (status === 'FAILED') {
    // Annuler la commande
}

Méthode 3 : Redirection après lien de paiement

Quand vous utilisez createPaymentLink, CamPay redirige l'utilisateur vers votre redirect_url avec tous les paramètres de la transaction.

Exemple d'URL après redirection :

https://monsite.com/merci?reference=2f69b2e4-e6ad-421c-a5fc-a455de9dbd5b&status=SUCCESSFUL&amount=25.00&currency=XAF&operator=MTN&phone_number=237673082287&external_reference=LINK-1776439725074&signature=eyJhbGciOiJIUzI1NiIs...

Dans votre page de confirmation (Next.js / React / Express) :

// Avec Express
app.get('/merci', (req, res) => {
    const { reference, status, amount, external_reference, signature } = req.query;
    
    if (status === 'SUCCESSFUL') {
        // Mettre à jour votre commande
        db.orders.update({
            where: { reference: external_reference },
            data: { paymentStatus: 'SUCCESSFUL', campayRef: reference }
        });
    }
    
    res.render('confirmation', { status, amount });
});

Gestion des erreurs

Le plugin exporte plusieurs classes d'erreur :

import { 
    CamPayClient, 
    ValidationError, 
    AuthError, 
    ApiError, 
    RateLimitError,
    NetworkError,
    ConfigError 
} from '@linoSAP/campay-plugin';

try {
    await campay.initCollect(params);
} catch (error) {
    if (error instanceof ValidationError) {
        console.error('Donnée invalide:', error.message, 'Champ:', error.field);
    } else if (error instanceof AuthError) {
        console.error('Échec authentification:', error.message);
    } else if (error instanceof RateLimitError) {
        console.error('Trop de requêtes, réessayer dans:', error.retryAfterSeconds, 'secondes');
    } else if (error instanceof ApiError) {
        console.error('Erreur CamPay:', error.message, 'Code:', error.statusCode);
    } else if (error instanceof NetworkError) {
        console.error('Erreur réseau:', error.message);
    } else if (error instanceof ConfigError) {
        console.error('Erreur configuration:', error.message);
    }
}

Types d'erreurs disponibles

| Classe | Description | Quand ça arrive | |--------|-------------|-----------------| | ValidationError | Paramètre invalide | Numéro mal formaté, montant nul | | AuthError | Échec d'authentification | Clés API erronées, token expiré | | ApiError | Erreur métier CamPay | Solde insuffisant, opérateur indisponible | | NetworkError | Problème réseau | Timeout, connexion impossible | | ConfigError | Configuration invalide | Clés manquantes, environnement inconnu | | RateLimitError | Trop de requêtes | Limite de débit dépassée |

Environnements

| Environnement | URL API | Montant max | Utilisation | |---------------|---------|-------------|-------------| | DEV | https://demo.campay.net | 25 XAF | Tests et développement | | PROD | https://app.campay.net | Illimité | Production réelle |

Pour passer en production :

  1. Testez votre intégration en environnement DEV
  2. Cliquez sur "Go Live" dans votre dashboard CamPay
  3. Remplacez environment: 'DEV' par environment: 'PROD'
  4. Utilisez vos clés API de production

Numéros de test (Sandbox)

En environnement DEV, utilisez ces numéros pour simuler des comportements :

| Numéro | Opérateur | Comportement simulé | |--------|-----------|---------------------| | 237677777777 | MTN | Paiement réussi | | 237677777770 | MTN | Paiement échoué | | 237699999999 | Orange | Paiement réussi | | 237699999990 | Orange | Paiement échoué |

⚠️ Ces numéros ne fonctionnent qu'en sandbox. En production, utilisez de vrais numéros clients.

API Reference

CamPayClient

new CamPayClient(config?: Partial<CamPayConfig>)

CamPayConfig :

| Champ | Type | Requis | Défaut | |-------|------|--------|--------| | username | string | Oui | - | | password | string | Oui | - | | environment | 'DEV' \| 'PROD' | Non | 'DEV' |

initCollect(params)

initCollect(params: CollectParams): Promise<InitCollectResponse>

CollectParams :

| Champ | Type | Requis | Description | |-------|------|--------|-------------| | amount | string \| number | Oui | Montant en XAF (ex: "1500" ou 1500) | | currency | 'XAF' | Oui | Devise (uniquement XAF) | | from | string | Oui | Numéro client (2376XXXXXXXX) | | description | string | Oui | Description de la transaction | | external_reference | string | Non | Votre référence interne |

InitCollectResponse :

| Champ | Type | Description | |-------|------|-------------| | reference | string | Référence unique CamPay | | ussd_code | string | Code USSD pour le client | | operator | string | 'mtn' ou 'orange' |

getStatus(reference)

getStatus(reference: string): Promise<TransactionStatusResponse>

TransactionStatusResponse :

| Champ | Type | Description | |-------|------|-------------| | reference | string | Référence CamPay | | external_reference | string? | Votre référence interne | | status | 'PENDING' \| 'SUCCESSFUL' \| 'FAILED' | Statut | | amount | number | Montant en XAF | | currency | 'XAF' | Devise | | operator | 'MTN' \| 'ORANGE' | Opérateur | | operator_reference | string? | Référence chez l'opérateur |

createPaymentLink(params)

createPaymentLink(params: PaymentLinkParams): Promise<PaymentLinkResponse>

PaymentLinkParams :

| Champ | Type | Requis | Description | |-------|------|--------|-------------| | amount | string \| number | Oui | Montant en XAF | | currency | 'XAF' | Oui | Devise | | description | string | Non | Description | | external_reference | string | Non | Votre référence | | redirect_url | string | Non | URL après paiement réussi | | failure_redirect_url | string | Non | URL après échec |

PaymentLinkResponse :

| Champ | Type | Description | |-------|------|-------------| | link | string | URL du lien de paiement | | reference | string | Référence CamPay |

Tests

Lancer les tests unitaires :

npm run test

Lancer les tests en mode watch (développement) :

npm run test:watch

Tester l'intégration réelle avec la sandbox :

npx tsx test-integration.ts

Lancer la page de demo de test graphique :

npm run demo : ("démarre le serveur local et ouvre directement la page dans le navigateur")

npm run demo:serve : ("démarre seulement le serveur")


npm run demo:open : ("ouvre la page si le serveur tourne déjà")

Liens utiles

Licence

MIT

Auteur

Développé par linokouete — Yaoundé, Cameroun 🇨🇲 Contactez-moi : https://www.linkedin.com/in/lino-kouete-6538502b3/ ou [email protected]


Résumé des méthodes pour récupérer le statut

| Méthode | Recommandation | Quand l'utiliser | |---------|----------------|------------------| | Webhook | ✅ Production | Applications critiques, fort trafic | | Polling | ⚠️ Développement | Petits projets, tests | | Redirect URL | ✅ Complément | Paiements par lien, expérience utilisateur |

Meilleure pratique : Webhook + polling comme fallback. Le webhook notifie votre serveur en temps réel, le polling permet de récupérer le statut si le webhook échoue.