Search by

money-pulse / money-pulse-php

Nocyl

Official Money-Pulse PHP SDK — Accept payments and process payouts across Africa.

Package info

github.com/Nocyl/money-pulse-php

Homepage

pkg:composer/money-pulse/money-pulse-php

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.0.3 2026-06-26 00:47 UTC

This package is auto-updated.

Last update: 2026-09-20 02:55:21 UTC


README

Official PHP SDK for Money-Pulse. Compatible with PHP 7.4+, Laravel, Symfony, WordPress / WooCommerce.

Installation

composer require money-pulse/money-pulse-php

Quick Start

<?php
require 'vendor/autoload.php';

use MoneyPulse\MoneyPulseClient;

$client = new MoneyPulseClient('mp_live_votre_cle_api');

// Créer un paiement
$payment = $client->payments->create([
    'amount'       => 10000,
    'currency'     => 'XOF',
    'country'      => 'CI',
    'customer'     => ['email' => 'client@email.com', 'phone' => '+22507000000'],
    'callback_url' => 'https://votre-site.com/webhook',
]);

// Rediriger vers le checkout hosted
header('Location: ' . $payment['checkout_url']);

Payouts (transferts sortants)

$payout = $client->payouts->create([
    'amount'    => 50000,
    'currency'  => 'XOF',
    'country'   => 'CI',
    'recipient' => [
        'type'  => 'mobile_money',
        'phone' => '+22507000000',
        'name'  => 'Jean Kouassi',
    ],
    'description' => 'Retrait marchand #4521',
]);

echo $payout['payout_id'];

Idempotence

Chaque appel à payments->create() ou payouts->create() génère automatiquement une clé d'idempotence. En cas d'erreur réseau, renvoyer exactement la même requête ne créera pas d'opération en double côté serveur. Pour contrôler vous-même cette clé (par exemple pour regrouper plusieurs tentatives d'une même opération logique) :

$payment = $client->payments->create([
    'amount'          => 10000,
    'currency'        => 'XOF',
    'country'         => 'CI',
    'customer'        => ['phone' => '+22507000000'],
    'idempotency_key' => 'order-4521-attempt-1',
]);

Webhooks — vérification de signature (HMAC SHA-256)

Money-Pulse signe chaque webhook avec votre webhook_secret (visible dans le dashboard). Toujours vérifier la signature avant de traiter le payload.

Le payload reçu a la forme suivante :

{
  "event": "payment.succeeded",
  "created": 1755000000000,
  "data": {
    "transactionId": "tx_xxx",
    "status": "completed",
    "amount": 10000,
    "currency": "XOF",
    "netAmount": 9800,
    "fee": 200
  }
}
<?php
$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_MONEYPULSE_SIGNATURE'] ?? '';
$secret    = getenv('MP_WEBHOOK_SECRET');

$expected = hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Signature invalide');
}

$event = json_decode($payload, true);

switch ($event['event']) {
    case 'payment.succeeded':
        $transactionId = $event['data']['transactionId'];
        // → marquer la commande comme payée, retrouvée par $transactionId
        break;
    case 'payment.failed':
        // → notifier le client
        break;
}
http_response_code(200);
echo 'OK';

Le webhook ne transmet que transactionId pour identifier l'opération — associez-le à votre commande dès la création du paiement, plutôt que de compter sur des métadonnées en retour.

Exemple Laravel (route + middleware + controller)

routes/api.php

use App\Http\Controllers\MoneyPulseWebhookController;

Route::post('/webhooks/money-pulse', [MoneyPulseWebhookController::class, 'handle'])
     ->middleware('moneypulse.signature');

app/Http/Middleware/VerifyMoneyPulseSignature.php

<?php
namespace App\Http\Middleware;

use Closure;

class VerifyMoneyPulseSignature
{
    public function handle($request, Closure $next)
    {
        $expected = hash_hmac(
            'sha256',
            $request->getContent(),
            config('services.moneypulse.webhook_secret')
        );

        if (!hash_equals($expected, $request->header('X-MoneyPulse-Signature', ''))) {
            abort(401, 'Invalid Money-Pulse signature');
        }
        return $next($request);
    }
}

app/Http/Controllers/MoneyPulseWebhookController.php

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Order;

class MoneyPulseWebhookController extends Controller
{
    public function handle(Request $request)
    {
        $event = $request->json()->all();

        if ($event['event'] === 'payment.succeeded') {
            $transactionId = $event['data']['transactionId'];
            Order::where('money_pulse_transaction_id', $transactionId)->update([
                'status'      => 'paid',
                'paid_amount' => $event['data']['amount'],
                'paid_at'     => now(),
            ]);
        }

        return response()->json(['received' => true]);
    }
}

Initier un paiement depuis un controller Laravel :

use MoneyPulse\MoneyPulseClient;

public function checkout(Order $order)
{
    $client = new MoneyPulseClient(config('services.moneypulse.api_key'));
    $payment = $client->payments->create([
        'amount'       => $order->total,
        'currency'     => 'XOF',
        'country'      => 'CI',
        'customer'     => ['email' => $order->customer_email, 'phone' => $order->customer_phone],
        'callback_url' => route('webhooks.moneypulse'),
        'return_url'   => route('orders.success', $order),
    ]);

    $order->update(['money_pulse_transaction_id' => $payment['transaction_id'] ?? $payment['id']]);

    return redirect($payment['checkout_url']);
}

Exemple WordPress / WooCommerce

Créez wp-content/plugins/money-pulse/money-pulse.php :

<?php
/*
Plugin Name: Money-Pulse for WooCommerce
*/
defined('ABSPATH') || exit;

add_action('plugins_loaded', function () {
    if (!class_exists('WC_Payment_Gateway')) return;

    class WC_MoneyPulse_Gateway extends WC_Payment_Gateway {
        public function __construct() {
            $this->id = 'moneypulse';
            $this->method_title = 'Money-Pulse';
            $this->title = 'Mobile Money & Cartes';
            $this->has_fields = false;
            $this->init_form_fields();
            $this->init_settings();
            add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']);
        }

        public function init_form_fields() {
            $this->form_fields = [
                'enabled'        => ['title' => 'Activer', 'type' => 'checkbox', 'default' => 'yes'],
                'api_key'        => ['title' => 'Clé API', 'type' => 'text'],
                'webhook_secret' => ['title' => 'Webhook secret', 'type' => 'password'],
            ];
        }

        public function process_payment($order_id) {
            $order = wc_get_order($order_id);
            require_once __DIR__ . '/vendor/autoload.php';
            $client = new \MoneyPulse\MoneyPulseClient($this->get_option('api_key'));
            $payment = $client->payments->create([
                'amount'       => $order->get_total(),
                'currency'     => $order->get_currency(),
                'country'      => $order->get_billing_country(),
                'customer'     => [
                    'email' => $order->get_billing_email(),
                    'phone' => $order->get_billing_phone(),
                ],
                'callback_url' => home_url('/?moneypulse_webhook=1'),
                'return_url'   => $this->get_return_url($order),
            ]);
            $order->update_meta_data('_moneypulse_payment_id', $payment['transaction_id'] ?? $payment['id']);
            $order->save();
            return ['result' => 'success', 'redirect' => $payment['checkout_url']];
        }
    }

    add_filter('woocommerce_payment_gateways', function ($gw) {
        $gw[] = 'WC_MoneyPulse_Gateway';
        return $gw;
    });
});

// Webhook handler
add_action('init', function () {
    if (!isset($_GET['moneypulse_webhook'])) return;
    $payload = file_get_contents('php://input');
    $signature = $_SERVER['HTTP_X_MONEYPULSE_SIGNATURE'] ?? '';
    $settings = get_option('woocommerce_moneypulse_settings');
    $expected = hash_hmac('sha256', $payload, $settings['webhook_secret']);
    if (!hash_equals($expected, $signature)) {
        status_header(401); exit('bad signature');
    }
    $event = json_decode($payload, true);
    if ($event['event'] === 'payment.succeeded') {
        $transactionId = $event['data']['transactionId'];
        $orders = wc_get_orders([
            'meta_key'   => '_moneypulse_payment_id',
            'meta_value' => $transactionId,
            'limit'      => 1,
        ]);
        if (!empty($orders)) {
            $orders[0]->payment_complete($transactionId);
        }
    }
    status_header(200); echo 'ok'; exit;
});

Erreurs courantes

try {
    $payment = $client->payments->create([...]);
} catch (\MoneyPulse\MoneyPulseException $e) {
    echo $e->getMessage();      // description lisible
    echo $e->getErrorCode();    // ex: 'invalid_amount', 'insufficient_balance'
    echo $e->getHttpCode();     // 400, 401, 422...
}
Code Cause Action
invalid_api_key Clé absente ou révoquée Régénérer dans le dashboard
invalid_signature Webhook mal signé Vérifier webhook_secret
insufficient_balance Solde marchand insuffisant pour payout Recharger compte
gateway_unavailable Aucune passerelle dispo Réessayer (failover auto côté MP)
validation_error Champ manquant ou invalide Vérifier le détail dans error.fields

Liens

License

MIT © NOCYL-PULSE