PHPackages                             sunucode/afripay - 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. sunucode/afripay

ActiveLibrary[Payment Processing](/categories/payments)

sunucode/afripay
================

Unified payment gateway for Africa — Wave, Orange Money, PayDunya, PayTech, Stripe &amp; PayPal for Laravel

v1.0.0(1mo ago)00MITPHPPHP ^8.2

Since Mar 29Pushed 1mo agoCompare

[ Source](https://github.com/Sunucode/laravel-afripay)[ Packagist](https://packagist.org/packages/sunucode/afripay)[ RSS](/packages/sunucode-afripay/feed)WikiDiscussions main Synced 1mo ago

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

AfriPay - Unified Payment Gateway for Africa
============================================

[](#afripay---unified-payment-gateway-for-africa)

**Accept payments from Wave, Orange Money, PayDunya, PayTech, Stripe &amp; PayPal in your Laravel app with a single, clean API.**

[![Latest Version on Packagist](https://camo.githubusercontent.com/63bc9509540afcbd6cf3de21921d9cde51cfdd6bf2189b85d297eeb00c70c649/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73756e75636f64652f616672697061792e737667)](https://packagist.org/packages/sunucode/afripay)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/d840cef9807c8f76051ad687841d67f4d830c84e0d83236968e53124ef6742d5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e322d3838393242462e737667)](https://php.net)[![Laravel](https://camo.githubusercontent.com/5a05d3c9e420ab83f0af27d4cb7896ba703a8a6a7f0ad26ef6af052c8517b152/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d3131253230253743253230313225323025374325323031332d4646324432302e737667)](https://laravel.com)

---

[Francais](#francais) | [English](#english)

---

Francais
--------

[](#francais)

### Pourquoi AfriPay ?

[](#pourquoi-afripay-)

Les developpeurs en Afrique de l'Ouest integrent manuellement chaque passerelle de paiement dans chaque projet. Wave, Orange Money, PayDunya, PayTech... chacun avec son API, ses webhooks, ses signatures.

**AfriPay unifie tout ca en une seule interface :**

```
// Payer via Wave
$payment = AfriPay::via('wave')->charge([
    'amount'      => 15000,
    'currency'    => 'XOF',
    'description' => 'Abonnement Premium',
    'success_url' => route('payment.success'),
    'error_url'   => route('payment.error'),
]);

return redirect($payment['redirect_url']);
```

Changer de passerelle ? Une seule ligne :

```
AfriPay::via('stripe')->charge([...]);
AfriPay::via('paydunya')->charge([...]);
AfriPay::via('orange_money')->charge([...]);
```

### Passerelles supportees

[](#passerelles-supportees)

PasserellePaysTypeStatut**Wave**SN, CI, ML, BFMobile MoneyProduction**Orange Money**SN, CI, ML, BF, CM, GNMobile MoneyBeta**PayDunya**SN, CI, BJ, TG, BF, MLMulti-canalProduction**PayTech**SNMulti-canalProduction**Stripe**GlobalCarte bancaireProduction**PayPal**GlobalInternationalProduction### Installation

[](#installation)

```
composer require sunucode/afripay
```

Publier la configuration :

```
php artisan vendor:publish --tag=afripay-config
```

Lancer les migrations :

```
php artisan migrate
```

### Configuration

[](#configuration)

Ajoutez vos cles dans `.env` :

```
# Passerelle par defaut
AFRIPAY_DEFAULT_GATEWAY=wave
AFRIPAY_CURRENCY=XOF

# Securite : seuls les webhooks peuvent confirmer un paiement (recommande)
# Mettre a false en dev si les webhooks ne peuvent pas atteindre votre serveur
AFRIPAY_TRUST_WEBHOOK_ONLY=true

# Activer/desactiver les passerelles individuellement
AFRIPAY_WAVE_ENABLED=true
AFRIPAY_STRIPE_ENABLED=true
AFRIPAY_PAYDUNYA_ENABLED=true
AFRIPAY_PAYTECH_ENABLED=true
AFRIPAY_ORANGE_MONEY_ENABLED=false
AFRIPAY_PAYPAL_ENABLED=false

# Wave
WAVE_API_KEY=wave_sn_...
WAVE_API_SECRET=wave_sn_...
WAVE_WEBHOOK_SECRET=wave_sn_WHS_...

# Stripe
STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# PayDunya
PAYDUNYA_MASTER_KEY=...
PAYDUNYA_PRIVATE_KEY=...
PAYDUNYA_TOKEN=...
PAYDUNYA_MODE=test

# Orange Money
ORANGE_MONEY_CLIENT_ID=...
ORANGE_MONEY_CLIENT_SECRET=...
ORANGE_MONEY_MERCHANT_KEY=...

# PayPal
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_MODE=sandbox

# PayTech
PAYTECH_API_KEY=...
PAYTECH_API_SECRET=...
PAYTECH_ENV=test
```

### Utilisation

[](#utilisation)

#### Initier un paiement

[](#initier-un-paiement)

```
use SunuCode\AfriPay\Facades\AfriPay;

$payment = AfriPay::via('wave')->charge([
    'amount'      => 25000,
    'currency'    => 'XOF',
    'description' => 'Commande #1234',
    'success_url' => route('orders.payment.success'),
    'error_url'   => route('orders.payment.error'),
    'metadata'    => [
        'order_id' => 1234,
        'user_id'  => auth()->id(),
    ],
]);

// $payment['redirect_url']  -> URL de paiement (rediriger l'utilisateur)
// $payment['transaction']   -> Instance Transaction (sauvegardee en DB)

return redirect($payment['redirect_url']);
```

#### Lier a un modele (polymorphic)

[](#lier-a-un-modele-polymorphic)

```
$payment = AfriPay::via('paydunya')->charge([
    'amount'        => 9900,
    'success_url'   => route('subscription.success'),
    'error_url'     => route('subscription.error'),
    'payable_type'  => Subscription::class,
    'payable_id'    => $subscription->id,
]);
```

#### Ecouter les evenements (le plus important)

[](#ecouter-les-evenements-le-plus-important)

```
// app/Providers/EventServiceProvider.php
// ou dans un Listener dedie

use SunuCode\AfriPay\Events\PaymentCompleted;
use SunuCode\AfriPay\Events\PaymentFailed;
use SunuCode\AfriPay\Events\PaymentRefunded;

class HandlePaymentCompleted
{
    public function handle(PaymentCompleted $event): void
    {
        $transaction = $event->transaction;

        // Activer l'abonnement, envoyer un email, etc.
        $subscription = $transaction->payable;
        $subscription->activate();

        // Acceder aux metadonnees
        $orderId = $transaction->metadata['order_id'] ?? null;
    }
}
```

#### Verifier une transaction (fallback)

[](#verifier-une-transaction-fallback)

Si le webhook n'est pas encore arrive quand l'utilisateur revient :

```
// Route de succes
public function paymentSuccess(Request $request)
{
    $transaction = Transaction::where('reference', $request->reference)->first();

    if ($transaction->status->isPending()) {
        // Verifier aupres de la passerelle ET dispatcher l'event si confirme
        $transaction = AfriPay::verifyAndProcess($transaction);
    }

    if ($transaction->status->isCompleted()) {
        return view('payment.success');
    }

    return view('payment.pending');
}
```

#### Rembourser

[](#rembourser)

```
$transaction = AfriPay::refund($transaction, 'Client insatisfait');
// Dispatche PaymentRefunded
```

#### Lister les passerelles actives

[](#lister-les-passerelles-actives)

```
// Toutes les passerelles activees via .env
$gateways = AfriPay::enabledGateways();
// ['wave', 'stripe', 'paydunya', 'paytech']

// Verifier si une passerelle est active
if (AfriPay::isEnabled('orange_money')) {
    // ...
}
```

#### Mode webhook-only vs fallback (trust\_webhook\_only)

[](#mode-webhook-only-vs-fallback-trust_webhook_only)

```
# PRODUCTION (recommande) — seul le webhook peut confirmer un paiement
AFRIPAY_TRUST_WEBHOOK_ONLY=true

# DEVELOPPEMENT — l'URL de retour peut aussi confirmer
AFRIPAY_TRUST_WEBHOOK_ONLY=false
```

Quand `trust_webhook_only=true`, `verifyAndProcess()` verifie le statut aupres de la passerelle mais ne dispatche **aucun evenement**. Seul le webhook declenche `PaymentCompleted`. C'est plus sur car ca empeche un utilisateur de forger une URL de succes.

Quand `trust_webhook_only=false`, les deux chemins (webhook ET URL de retour) peuvent declencher les evenements. Utile en dev local quand les webhooks ne peuvent pas atteindre votre machine.

#### Ajouter une passerelle personnalisee

[](#ajouter-une-passerelle-personnalisee)

```
// Dans un ServiceProvider
use SunuCode\AfriPay\PaymentManager;

PaymentManager::extend('cinetpay', function (array $config) {
    return new CinetPayGateway($config);
});

// Utilisation
AfriPay::via('cinetpay')->charge([...]);
```

### Webhooks

[](#webhooks)

Les webhooks sont automatiquement enregistres a :

```
POST /afripay/webhooks/wave
POST /afripay/webhooks/stripe
POST /afripay/webhooks/paydunya
POST /afripay/webhooks/orange-money
POST /afripay/webhooks/paytech
POST /afripay/webhooks/paypal

```

Le chemin est configurable via `AFRIPAY_WEBHOOK_PATH`.

**Chaque webhook :**

- Verifie la signature (HMAC-SHA256 pour Wave/Stripe/PayTech, master\_key pour PayDunya)
- Verifie le montant (tolerance +/- 1 unite)
- Utilise `lockForUpdate()` pour eviter les doublons
- Dispatche `PaymentCompleted` ou `PaymentFailed`

### Securite

[](#securite)

- **Idempotence** : Le champ `processed_at` empeche le double-traitement
- **Verrouillage DB** : `lockForUpdate()` sur chaque transaction pendant le webhook
- **Verification de montant** : Tolerance +/- 1 unite avant d'accepter
- **Anti-replay** : Timestamps verifies (Wave, Stripe) avec tolerance de 5 min
- **Zero-decimal** : XOF/XAF geres automatiquement (pas de x100 pour Stripe)
- **Orange Money** : Contre-verification API obligatoire (pas de signature webhook)

### Evenements disponibles

[](#evenements-disponibles)

EvenementQuandDonnees`PaymentInitiated`Apres `charge()``$transaction`, `$gateway``PaymentCompleted`Webhook confirme`$transaction``PaymentFailed`Webhook echoue`$transaction``PaymentRefunded`Apres `refund()``$transaction`, `$reason`---

English
-------

[](#english)

### Why AfriPay?

[](#why-afripay)

West African developers manually integrate each payment gateway in every project. Wave, Orange Money, PayDunya, PayTech... each with its own API, webhooks, and signatures.

**AfriPay unifies everything into a single interface:**

```
$payment = AfriPay::via('wave')->charge([
    'amount'      => 15000,
    'currency'    => 'XOF',
    'description' => 'Premium Subscription',
    'success_url' => route('payment.success'),
    'error_url'   => route('payment.error'),
]);

return redirect($payment['redirect_url']);
```

### Installation

[](#installation-1)

```
composer require sunucode/afripay
php artisan vendor:publish --tag=afripay-config
php artisan migrate
```

### Listening to Events

[](#listening-to-events)

This is the primary way to react to payment outcomes:

```
use SunuCode\AfriPay\Events\PaymentCompleted;

class ActivateSubscription
{
    public function handle(PaymentCompleted $event): void
    {
        $transaction = $event->transaction;
        $subscription = $transaction->payable;
        $subscription->activate();
    }
}
```

### Custom Gateways

[](#custom-gateways)

Extend AfriPay with your own gateways:

```
use SunuCode\AfriPay\Contracts\GatewayInterface;
use SunuCode\AfriPay\PaymentManager;

class CinetPayGateway implements GatewayInterface
{
    // Implement the 4 methods: charge(), handleWebhook(), verify(), verifySignature()
}

PaymentManager::extend('cinetpay', fn($config) => new CinetPayGateway($config));
```

### Security

[](#security)

- **Idempotent processing** via atomic `processed_at` flag
- **Database locking** (`lockForUpdate`) prevents race conditions
- **Amount verification** with configurable tolerance
- **Replay protection** with timestamp validation (Wave, Stripe)
- **Zero-decimal currencies** (XOF, XAF) handled automatically
- **Orange Money**: Mandatory API counter-verification (no webhook signature)

---

Requirements
------------

[](#requirements)

- PHP &gt;= 8.2
- Laravel 11, 12, or 13
- A database supporting `lockForUpdate()` (MySQL, PostgreSQL)

Contributing
------------

[](#contributing)

Contributions are welcome! Please submit pull requests to the `main` branch.

Credits
-------

[](#credits)

- Built by [Sunu Code](https://sunucode.com) — Software agency based in Dakar, Senegal
- Extracted from [Semplio](https://semplio.com) — Business management SaaS for African SMEs

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b112190f7278156ffd6c602e373c7a14108c8649728fd05c0cfe26a01c266690?d=identicon)[Sunucode](/maintainers/Sunucode)

---

Top Contributors

[![Sunucode](https://avatars.githubusercontent.com/u/76840710?v=4)](https://github.com/Sunucode "Sunucode (1 commits)")

---

Tags

africafcfalaravellaravel-packagemobile-moneyorange-moneypaydunyapaymentpaypalpaytechsenegalstripewavexoflaravelstripepaymentpaypalmobile-moneyafricaOrange Moneypaydunyawavepaytechxoffcfa

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/sunucode-afripay/health.svg)

```
[![Health](https://phpackages.com/badges/sunucode-afripay/health.svg)](https://phpackages.com/packages/sunucode-afripay)
```

###  Alternatives

[recca0120/laravel-payum

Rich payment solutions for Laravel framework. Paypal, payex, authorize.net, be2bill, omnipay, recurring paymens, instant notifications and many more

741.5k](/packages/recca0120-laravel-payum)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[asciisd/knet

Knet package is provides an expressive, fluent interface to KNet's payment services.

141.1k](/packages/asciisd-knet)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
