PHPackages                             vnuswilliams/laravel-kpay - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. vnuswilliams/laravel-kpay

ActiveLibrary[Payment Processing](/categories/payments)

vnuswilliams/laravel-kpay
=========================

Laravel integration for KPay - Mobile Money payment aggregator for Africa

v1.0.0(yesterday)00MITPHP ^8.2

Since Jul 22Compare

[ Source](https://github.com/vnuswilliams/laravel-kpay)[ Packagist](https://packagist.org/packages/vnuswilliams/laravel-kpay)[ Docs](https://github.com/vnuswilliams/laravel-kpay)[ RSS](/packages/vnuswilliams-laravel-kpay/feed)WikiDiscussions Synced today

READMEChangelogDependencies (8)Versions (2)Used By (0)

Laravel KPay
============

[](#laravel-kpay)

Intégration Laravel pour [KPay](https://admin.kpay.site) — Agrégateur de paiements Mobile Money pour l'Afrique.

[English Version](README.en.md)

[![Latest Stable Version](https://camo.githubusercontent.com/cfe7665013bac8c37cc7553f24fe25dee243b5e74d19b91a9fc13a6baa4cb5c7/68747470733a2f2f706f7365722e707567782e6f72672f766e757377696c6c69616d732f6c61726176656c2d6b7061792f762f737461626c65)](https://packagist.org/packages/vnuswilliams/laravel-kpay)[![License](https://camo.githubusercontent.com/c31353ec32f1d95647cc70a46c3bf6ed83425d76f2269e12b2eef42efc68ced9/68747470733a2f2f706f7365722e707567782e6f72672f766e757377696c6c69616d732f6c61726176656c2d6b7061792f6c6963656e7365)](https://packagist.org/packages/vnuswilliams/laravel-kpay)

Fonctionnalités
---------------

[](#fonctionnalités)

- **Paiements (USSD &amp; Gateway)** — Encaissez via MTN MoMo, Orange Money, M-Pesa et 20+ opérateurs dans 13 pays africains
- **Retraits (USSD &amp; Gateway)** — Envoyez de l'argent sur des comptes Mobile Money, y compris transfrontaliers avec conversion automatique
- **Transferts entre portefeuilles** — Déplacez des fonds entre les portefeuilles pays avec des taux de change en temps réel
- **Webhooks** — Traitez les mises à jour de statut de manière asynchrone avec support queue et vérification de signature
- **Modèles Eloquent** — Stockez et interrogez paiements et retraits localement
- **Événements** — Réagissez à `PaymentCompleted`, `PayoutFailed` et autres changements de statut
- **API Fluide** — Syntaxe chainable et expressive pour toutes les opérations
- **Commandes Artisan** — Vérifiez les soldes, la disponibilité des opérateurs et testez les webhooks

Prérequis
---------

[](#prérequis)

- PHP 8.2+
- Laravel 12.x ou 13.x

Installation
------------

[](#installation)

```
composer require vnuswilliams/laravel-kpay
php artisan vendor:publish --tag="kpay"
php artisan migrate
```

Configuration `.env`
--------------------

[](#configuration-env)

Ajoutez ces variables à votre fichier `.env`. Chaque variable a un rôle précis :

```
# ─── Clés API (obtenues sur admin.kpay.site) ────────────────────────────
# La clé API identifie votre application. Le préfixe détermine l'environnement :
#   kpay_test_xxx  → Sandbox (aucune vraie argent ne bouge)
#   kpay_live_xxx   → Production (transactions réelles)
KPAY_API_KEY=kpay_test_xxxxxxxxxxxxxxxx

# La clé secrète sert à authentifier vos requêtes vers l'API KPay.
# Ne JAMAIS la commiter dans Git.
KPAY_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxx

# ─── Secrets de vérification ──────────────────────────────────────────────
# Secret pour vérifier la signature HMAC-SHA256 des webhooks reçus de KPay.
# À configurer dans le dashboard KPay (Section Webhooks).
KPAY_WEBHOOK_SECRET=your_webhook_secret_here

# Secret pour vérifier les signatures des URLs de retour Gateway.
# À configurer dans le dashboard KPay (Section Gateway).
KPAY_GATEWAY_SECRET=your_gateway_secret_here

# ─── Configuration technique ──────────────────────────────────────────────
# URL de base de l'API KPay (ne pas modifier sauf instance personnalisée)
KPAY_BASE_URL=https://admin.kpay.site

# Timeout des requêtes API en secondes
KPAY_TIMEOUT=30

# ─── Queue (pour le traitement asynchrone des webhooks) ───────────────────
# Active le traitement webhook via la queue Laravel (recommandé en production)
KPAY_QUEUE_ENABLED=true

# Connection queue à utiliser (redis, database, sqs, etc.)
KPAY_QUEUE_CONNECTION=database

# Nom de la queue dédiée aux webhooks KPay
KPAY_QUEUE_NAME=kpay

# ─── URLs Gateway par défaut ──────────────────────────────────────────────
# URLs utilisées automatiquement pour les paiements/retraits Gateway
# quand ->returnUrl() ou ->cancelUrl() n'est pas appelé explicitement.
# Vous pouvez quand même les surcharger par appel avec ->returnUrl() / ->cancelUrl().
KPAY_RETURN_URL=https://mysite.com/payment/return
KPAY_CANCEL_URL=https://mysite.com/payment/cancel
```

> **Important** : Les clés `KPAY_API_KEY` et `KPAY_SECRET_KEY` s'obtiennent sur [admin.kpay.site](https://admin.kpay.site) après avoir créé un compte et une application.

Démarrage rapide
----------------

[](#démarrage-rapide)

### 1. Encaisser un paiement USSD

[](#1-encaisser-un-paiement-ussd)

Le mode USSD envoie une notification push directement sur le téléphone du client. Le client reçoit un popup USSD et confirme avec son PIN.

```
use VnusWilliams\KPay\Facades\KPay;

$payment = KPay::payment()
    ->amount(5000)                              // Montant brut (minimum 500 XAF au Cameroun)
    ->provider('MTN_MOMO_CMR')                  // Opérateur Mobile Money
    ->phoneNumber('237670000001')                // Numéro du client (format international)
    ->externalId('ORDER-12345')                  // Votre ID unique (idempotence)
    ->description('Achat t-shirt KPay')         // Description lisible
    ->customerName('Jean Dupont')                // Nom du client
    ->customerEmail('jean@example.com')          // Email du client
    ->metadata([                                 // Métadonnées libres (optionnel)
        'product_id' => 42,
        'size' => 'L',
    ])
    ->create();                                  // → PaymentData

// Résultat :
// $payment->id             → "pay_abc123def456" (ID KPay)
// $payment->reference      → "KPAY-XYZ-789"     (référence KPay)
// $payment->status         → PaymentStatus::PENDING
// $payment->amount         → 5000.0
// $payment->currency       → "XAF"
// $payment->netAmount      → 4750.0  (après commission)
// $payment->feeAmount      → 250.0   (commission KPay)
// $payment->isTest         → true    (si clés sandbox)

// Le client reçoit un popup USSD sur son téléphone.
// Une fois le paiement confirmé/échoué/annulé, KPay envoie un webhook.
```

**Comment ça marche** :

1. Vous appelez `create()` → le package envoie une requête POST à l'API KPay
2. KPay retourne un `PaymentData` avec le statut `PENDING`
3. Le client reçoit un popup USSD sur son téléphone avec le montant et la description
4. Le client confirme en entrant son PIN Mobile Money
5. KPay met à jour le statut et envoie un webhook à votre endpoint `/kpay/webhook`

### 2. Encaisser via Gateway (page hébergée)

[](#2-encaisser-via-gateway-page-hébergée)

Le mode Gateway héberge une page de paiement sur KPay. Le client choisit **lui-même** son opérateur et son numéro sur la page hébergée — vous ne passez **ni `provider` ni `phoneNumber`**.

Les URLs de retour (`KPAY_RETURN_URL` / `KPAY_CANCEL_URL`) sont lues automatiquement depuis votre `.env`.

```
use VnusWilliams\KPay\Facades\KPay;

// Minimal — le client choisit opérateur et numéro sur la page KPay
$payment = KPay::payment()
    ->amount(5000)
    ->externalId('ORDER-12346')
    ->createGateway();

// Résultat :
// $payment->gatewayUrl  → "https://gateway.kpay.site/pay/abc123..."
// $payment->mode        → PaymentMode::GATEWAY

// Redirigez le client vers la page de paiement :
return redirect($payment->gatewayUrl);
```

> **Attention** : En mode Gateway, `provider`, `phoneNumber` et `customerName` sont **interdits**. Le package lève une `ValidationException` si vous les passez. Le client les saisit lui-même sur la page hébergée KPay.

Si vous avez besoin de surcharger les URLs pour une commande spécifique (ex: ajouter un paramètre `order_id`) :

```
$payment = KPay::payment()
    ->amount(5000)
    ->externalId('ORDER-12346')
    ->returnUrl(route('payment.return', ['order' => 12346]))  // surcharge pour cette commande
    ->cancelUrl(route('payment.cancel', ['order' => 12346]))
    ->createGateway();
```

**Comment ça marche** :

1. Vous appelez `createGateway()` → KPay retourne une URL de paiement
2. Les URLs de retour sont lues depuis le `.env` (ou surchargées via les builders)
3. Vous redirigez le client vers `$payment->gatewayUrl`
4. Le client choisit **lui-même** son opérateur et entre son numéro sur la page KPay
5. Le client confirme avec son PIN Mobile Money
6. Après paiement, KPay redirige le client vers votre `returnUrl`
7. Le webhook `/kpay/webhook` est appelé pour confirmer le statut

### 3. Envoyer un retrait (payout)

[](#3-envoyer-un-retrait-payout)

```
$payout = KPay::payout()
    ->amount(10000)                             // Montant à retirer
    ->provider('MTN_MOMO_CMR')                  // Opérateur du bénéficiaire
    ->phoneNumber('237670000002')                // Numéro du bénéficiaire
    ->externalId('WD-98765')                     // Votre ID unique
    ->description('Retrait commission vendeur')
    ->create();                                  // → PayoutData

// Vérifiez le solde AVANT de faire un retrait :
$balances = KPay::balance();
$xafWallet = $balances->first(fn ($b) => $b->currency === 'XAF');
if ($xafWallet->availableBalance < 10000) {
    throw new \Exception('Solde insuffisant pour ce retrait');
}
```

### 4. Retrait transfrontalier

[](#4-retrait-transfrontalier)

```
$payout = KPay::payout()
    ->amount(50000)                              // Montant en XAF (devise source)
    ->provider('ORANGE_SEN')                     // Opérateur au Sénégal
    ->phoneNumber('221770000003')                // Numéro sénégalais
    ->sourceCountry('CMR')                       // Retirer du portefeuille Cameroun
    ->externalId('PAYOUT-CROSS-001')
    ->create();

// Résultat :
// $payout->payoutCurrency  → "XOF"   (devise du Sénégal)
// $payout->exchangeRate    → 0.654   (taux XAF → XOF)
// $payout->payoutAmount    → 32700   (montant crédité au Sénégal)
```

### 5. Transfert entre portefeuilles

[](#5-transfert-entre-portefeuilles)

```
// D'abord, récupérez votre applicationId :
$appInfo = KPay::applicationInfo();
$applicationId = $appInfo->id;

$transfer = KPay::wallet()
    ->applicationId($applicationId)             // UUID de votre application
    ->fromCountry('CMR')                        // Portefeuille source (Cameroun)
    ->toCountry('SEN')                          // Portefeuille destination (Sénégal)
    ->amount(100000)                            // Montant en XAF
    ->externalId('TRF-2026-001')
    ->description('Approvisionnement portefeuille Sénégal')
    ->transfer();                               // → WalletTransferData
```

Configuration des Webhooks
--------------------------

[](#configuration-des-webhooks)

### Étape 1 : Endpoint

[](#étape-1--endpoint)

L'endpoint `POST /kpay/webhook` est enregistré automatiquement par le package. Aucune configuration de route n'est nécessaire.

### Étape 2 : Configurer le secret dans `.env`

[](#étape-2--configurer-le-secret-dans-env)

```
KPAY_WEBHOOK_SECRET=votre_secret_webhook_ici
```

Ce secret doit correspondre à celui configuré dans votre dashboard KPay. Il sert à vérifier la signature HMAC-SHA256 de chaque webhook reçu.

### Étape 3 : Écouter les événements

[](#étape-3--écouter-les-événements)

Dans `AppServiceProvider::boot()` ou `EventServiceProvider` :

```
use VnusWilliams\KPay\Events\PaymentCompleted;
use VnusWilliams\KPay\Events\PaymentFailed;
use VnusWilliams\KPay\Events\PaymentCancelled;
use VnusWilliams\KPay\Events\PayoutCompleted;
use VnusWilliams\KPay\Events\PayoutFailed;

// ✅ Paiement réussi → Marquer la commande comme payée
Event::listen(PaymentCompleted::class, function (PaymentCompleted $event) {
    $webhook = $event->event;  // WebhookEvent DTO

    // Mettre à jour votre commande
    Order::where('external_id', $webhook->externalId)
        ->update(['status' => 'paid']);

    // Envoyer un email de confirmation
    Mail::to($customer->email)->send(new PaymentConfirmation($webhook));
});

// ❌ Paiement échoué → Notifier le client
Event::listen(PaymentFailed::class, function (PaymentFailed $event) {
    $webhook = $event->event;

    Log::warning("Paiement échoué: {$webhook->failureReason}", [
        'external_id' => $webhook->externalId,
        'reference' => $webhook->reference,
    ]);
});

// ❌ Paiement annulé
Event::listen(PayoutFailed::class, function (PayoutFailed $event) {
    // Gérer l'échec du retrait
    $webhook = $event->event;
    Log::error("Retrait échoué: {$webhook->failureReason}");
});

// ✅ Retrait réussi
Event::listen(PayoutCompleted::class, function (PayoutCompleted $event) {
    $webhook = $event->event;

    // Mettre à jour votre ledger interne
    Ledger::where('external_id', $webhook->externalId)
        ->update(['status' => 'completed']);
});
```

### Étape 4 : Queue (recommandé)

[](#étape-4--queue-recommandé)

Le traitement webhook est dispatché en queue par défaut pour répondre rapidement à KPay (200 OK). Assurez-vous que votre worker queue est actif :

```
php artisan queue:work --queue=kpay
```

### Étape 5 : Tester le webhook

[](#étape-5--tester-le-webhook)

```
php artisan kpay:test-webhook https://mysite.com/kpay/webhook --event=payment.completed --status=COMPLETED
```

Utilitaires
-----------

[](#utilitaires)

```
use VnusWilliams\KPay\Facades\KPay;

// 💰 Vérifier les soldes de tous les portefeuilles
$balances = KPay::balance();
foreach ($balances as $wallet) {
    echo "{$wallet->currency}: {$wallet->availableBalance} disponible";
}

// 🌍 Vérifier la disponibilité des opérateurs par pays
$availability = KPay::availability();
foreach ($availability->countries as $country) {
    echo "Pays: {$country->country}\n";
    foreach ($country->providers as $provider) {
        echo "  {$provider->provider}: {$provider->operationTypes[0]['status']}\n";
    }
}

// 💱 Obtenir le taux de change entre deux devises
$rate = KPay::exchangeRate('XAF', 'XOF');
$montantConverti = $rate->convert(10000);  // → 10153.0

// 📱 Prédire l'opérateur à partir d'un numéro de téléphone
$prediction = KPay::predictProvider('237670000001');
// $prediction->provider → "MTN_MOMO_CMR"
// $prediction->country  → "CMR"

// 🏢 Infos de l'application (pour obtenir l'applicationId)
$appInfo = KPay::applicationInfo();
echo $appInfo->id;  // UUID de votre application
```

Gestion des erreurs
-------------------

[](#gestion-des-erreurs)

```
use VnusWilliams\KPay\Exceptions\ConflictException;
use VnusWilliams\KPay\Exceptions\InsufficientBalanceException;
use VnusWilliams\KPay\Exceptions\ValidationException;
use VnusWilliams\KPay\Exceptions\KPayException;
use VnusWilliams\KPay\Exceptions\AuthenticationException;

try {
    $payment = KPay::payment()
        ->amount(5000)
        ->provider('MTN_MOMO_CMR')
        ->phoneNumber('237670000001')
        ->externalId('ORDER-12345')
        ->create();

} catch (ConflictException $e) {
    // L'externalId est déjà utilisé (idempotence)
    // → Récupérer le paiement existant avec KPay::paymentStatus()

} catch (InsufficientBalanceException $e) {
    // Solde insuffisant dans le portefeuille KPay
    // → Notifier l'admin ou réessayer plus tard

} catch (ValidationException $e) {
    // Paramètres invalides (montant trop bas, numéro invalide, etc.)
    // → Corriger les paramètres et réessayer

} catch (AuthenticationException $e) {
    // Clés API invalides ou expirées
    // → Vérifier KPAY_API_KEY et KPAY_SECRET_KEY dans .env

} catch (KPayException $e) {
    // Erreur API générale
    Log::error('Erreur KPay', [
        'status' => $e->getStatusCode(),
        'message' => $e->getMessage(),
    ]);
}
```

Modèles Eloquent
----------------

[](#modèles-eloquent)

Les paiements et retraits sont automatiquement stockés en base de données via les migrations publiées.

```
use VnusWilliams\KPay\Models\KPayPayment;
use VnusWilliams\KPay\Models\KPayPayout;

// Rechercher par statut
$completed = KPayPayment::completed()->get();
$pending = KPayPayout::pending()->get();

// Rechercher par external_id
$payment = KPayPayment::where('external_id', 'ORDER-12345')->first();

// Vérifier si un paiement est terminé
if ($payment->isTerminal()) {
    // Le paiement est COMPLETED, FAILED ou CANCELLED
}

// Vérifier si c'est un paiement réussi
if ($payment->isSuccess()) {
    // Le paiement est COMPLETED
}

// Vérifier si c'est un paiement Gateway
if ($payment->isGateway()) {
    echo $payment->gateway_url;
}
```

Commandes Artisan
-----------------

[](#commandes-artisan)

```
# Afficher les soldes de tous les portefeuilles
php artisan kpay:check-balance

# Vérifier la disponibilité des opérateurs par pays
php artisan kpay:check-availability

# Envoyer un webhook test signé à votre endpoint
php artisan kpay:test-webhook https://mysite.com/kpay/webhook
php artisan kpay:test-webhook https://mysite.com/kpay/webhook --event=payout.completed --status=COMPLETED
```

Exemple complet : Intégrer KPay dans un contrôleur Laravel
----------------------------------------------------------

[](#exemple-complet--intégrer-kpay-dans-un-contrôleur-laravel)

Voici un exemple concret d'intégration dans un contrôleur de e-commerce :

```
