Guide d'implementation API
Tout ce qu'il faut pour integrer PayHub dans votre application : authentification, paiements, webhooks et exemples de code.
Base URL
Developpement : http://localhost:8888 — Remplacez par l'URL de votre instance en production.
Authentification HMAC
Toutes les requetes API doivent etre signees avec HMAC-SHA256. Chaque tenant possede une paire API_KEY / API_SECRET.
Headers requis sur chaque requete
| X-API-KEY | Cle publique du tenant (key_id) |
| X-SIGNATURE | HMAC-SHA256 de timestamp.body signe avec le secret_key |
| X-TIMESTAMP | Timestamp Unix en secondes (tolerance : ±5 min) |
| X-NONCE | Valeur unique anti-rejeu (UUID ou hex aleatoire) |
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
$body = json_encode(['amount' => 5000, 'currency' => 'XAF']);
$signature = hash_hmac('sha256', $timestamp . '.' . $body, $apiSecret);
$headers = [
'X-API-KEY' => $apiKey,
'X-SIGNATURE' => $signature,
'X-TIMESTAMP' => $timestamp,
'X-NONCE' => $nonce,
'Content-Type' => 'application/json',
];Idempotence
Ajoutez le header Idempotency-Key sur les requetes POST pour eviter les doublons en cas de retry reseau.
409 ConflictPaiements (PaymentIntent)
Le flux suit le modele Stripe : PaymentIntent → Charge → Refund/Payout.
redirect_url vers lequel rediriger le client. Apres approbation, appelez POST /api/v1/payment-intents/{id}/capture pour finaliser.
Creer un paiement
// Requete
{
"amount": 5000,
"currency": "XAF",
"provider": "mtn_momo",
"customer_identifier": "+242064000000",
"description": "Commande #1234",
"external_reference": "ORDER-2026-001",
"environment": "sandbox",
"metadata": { "order_id": "1234" }
}
// Reponse 201
{
"id": "01952f3a-7abc-...",
"status": "succeeded",
"amount": 5000,
"currency": "XAF",
"provider": "mtn_momo",
"environment": "sandbox",
"created_at": "2026-03-09T10:00:00+00:00"
}Consulter un paiement
Lister les paiements
Annuler un paiement en attente
Capturer un paiement (PayPal / 2Checkout)
Apres l'approbation de l'utilisateur sur la page du provider, capturez le paiement :
Environnement (Sandbox / Production)
Chaque requete API accepte un parametre environment pour determiner quel jeu de credentials utiliser. Un meme provider peut etre configure en sandbox et en production simultanement.
sandbox (defaut)
Environnement de test. Aucun mouvement d'argent reel. Utilisez les credentials sandbox du provider.
production
Environnement reel. Les paiements sont effectues et les montants debites. Credentials de production requis.
Exemple d'utilisation
// Paiement en production
{
"amount": 5000,
"currency": "XAF",
"provider": "mtn_momo",
"customer_identifier": "+242064000000",
"environment": "production"
}
// Paiement en sandbox (defaut si omis)
{
"amount": 5000,
"currency": "XAF",
"provider": "mtn_momo",
"customer_identifier": "+242064000000",
"environment": "sandbox"
}Important : Si le parametre environment est omis, la valeur par defaut est sandbox. Assurez-vous de passer "environment": "production" en production.
Endpoints concernes :
POST /api/v1/payment-intentsPaiementsPOST /api/payments/createPaiements (simplifie)POST /api/v1/payoutsRetraitsPOST /api/v1/emails/sendEmailsPOST /api/v1/sms/sendSMS
API simplifiee
Endpoint alternatif avec des noms de champs simplifies, compatible avec le cahier des charges initial.
{
"amount": 10000,
"currency": "XAF",
"payment_method": "MTN",
"customer_phone": "242060000000",
"callback_url": "https://example.com/webhook",
"reference": "CMD-2026-001",
"description": "Commande en ligne",
"environment": "sandbox"
}
// Methodes acceptees : MTN, AIRTEL, PAYPAL, 2CHECKOUT
// environment : "sandbox" (defaut) ou "production"Verifier le statut par reference
Remboursements
// Remboursement total
{ "payment_intent_id": "01952f3a-7abc-..." }
// Remboursement partiel
{
"payment_intent_id": "01952f3a-7abc-...",
"amount": 2500,
"reason": "Client insatisfait"
}
// Reponse 201
{
"id": "01952f3b-...",
"status": "succeeded",
"amount": 2500,
"payment_intent_id": "01952f3a-7abc-..."
}Consulter un remboursement
Email & SMS transactionnels
PayHub permet d'envoyer des emails et SMS transactionnels via les providers configures pour votre tenant. Le provider par defaut est utilise si non specifie.
Envoyer un email
// Requete
{
"to": "[email protected]",
"subject": "Confirmation de paiement",
"body_html": "<h1>Merci !</h1><p>Votre paiement a ete confirme.</p>",
"body_text": "Merci ! Votre paiement a ete confirme.",
"from_email": "[email protected]",
"from_name": "Mon Application",
"provider": "smtp",
"environment": "production"
}
// Reponse 200
{
"success": true,
"message_id": "abc123",
"provider": "smtp"
}to, subject, body_html ou body_text.
Optionnels : from_email, from_name, reply_to, cc, bcc, provider, environment.
Envoyer un SMS
// Requete
{
"to": "+242064000000",
"message": "Votre code de validation est : 123456",
"from": "PAYHUB",
"provider": "allmysms",
"environment": "production"
}
// Reponse 200
{
"success": true,
"message_id": "sms_xyz",
"provider": "allmysms"
}to, message.
Optionnels : from, provider, environment.
Providers disponibles
smtp— SMTP natifmailgun— Mailgun APImailtrap— Mailtrap (test/staging)mailcatcher— MailCatcher (dev)
SMS
allmysms— AllMySmsclickatell— Clickatellbandwidth— Bandwidthfakesms— FakeSms (dev)
Webhooks
PayHub envoie des notifications HTTP POST a vos URLs de webhook pour chaque evenement. Les webhooks sont signes avec HMAC-SHA256.
Enregistrer un endpoint
POST /api/v1/webhook-endpoints
{
"url": "https://example.com/webhooks/payhub",
"events": ["payment_intent.succeeded", "payment_intent.failed"]
}
// events vide = recevoir tous les evenementsEvenements disponibles
payment_intent.created
PaymentIntent cree
payment_intent.succeeded
Paiement confirme, montant credite
payment_intent.failed
Paiement echoue
refund.created
Demande de remboursement initiee
refund.succeeded
Remboursement effectue
payout.succeeded
Retrait effectue
Verification de la signature
Le header X-PayHub-Signature contient : t=timestamp,v1=signature
$header = $_SERVER['HTTP_X_PAYHUB_SIGNATURE'];
preg_match('/t=(\d+),v1=(.+)/', $header, $m);
$timestamp = $m[1];
$receivedSig = $m[2];
$payload = file_get_contents('php://input');
$expected = hash_hmac('sha256',
$timestamp . '.' . $payload, $endpointSecret);
if (!hash_equals($expected, $receivedSig)) {
http_response_code(401);
exit('Signature invalide');
}
// Verifier la fraicheur (< 5 min)
if (abs(time() - (int)$timestamp) > 300) {
http_response_code(401);
exit('Timestamp expire');
}
$event = json_decode($payload, true);
// Traiter l'evenement...Exemple de payload
{
"event": "payment_intent.succeeded",
"timestamp": "2026-03-09T10:05:00+01:00",
"data": {
"id": "01952f3a-7abc-...",
"status": "succeeded",
"amount": 5000,
"currency": "XAF",
"provider": "mtn_momo",
"customer_identifier": "+242064000000"
}
}Retentatives : En cas d'echec (non-2xx), PayHub retente jusqu'a 5 fois avec backoff exponentiel (5s, 20s, 80s, 320s, ~1h). Repondez avec un code 2xx pour confirmer la reception.
Providers supportes
MTN Mobile Money
Congo-Brazzaville
provider: "mtn_momo"Airtel Money
Congo-Brazzaville
provider: "airtel_money"PayPal
International
provider: "paypal"2Checkout
Cartes bancaires
provider: "two_checkout"Gestion des erreurs
Format standard
{ "error": "error_type", "message": "Description lisible" }Codes HTTP
Types d'erreurs
| error | Description |
|---|---|
| auth_failed | Cle API ou signature HMAC invalide |
| invalid_nonce | Nonce deja utilise (rejeu detecte) |
| validation_error | Champs requis manquants ou invalides |
| invalid_transition | Changement d'etat non autorise |
| provider_error | Erreur retournee par le provider de paiement |
| idempotency_conflict | Cle d'idempotence reutilisee avec un payload different |
| not_found | Ressource introuvable |
SDKs & exemples
PHP (SDK PayHub)
Installez avec : composer require senprog/payhub-sdk
use PayHub\PayHubClient;
$client = new PayHubClient('pk_live_xxx', 'sk_live_xxx',
'https://pay.example.com');
// Creer un paiement
$payment = $client->createPayment(
amount: 5000,
currency: 'XAF',
provider: 'mtn_momo',
customerIdentifier: '242060000000',
options: ['description' => 'Commande #1234',
'environment' => 'production']
);
echo $payment['status']; // "succeeded"
// Rembourser
$refund = $client->createRefund($payment['id'],
amount: 2500, reason: 'Retour produit');
// Verifier un webhook
$valid = PayHubClient::verifyWebhookSignature(
$rawBody, $signatureHeader, $endpointSecret);JavaScript / Node.js (SDK PayHub)
Installez avec : npm install @senprog/payhub-sdk
const PayHub = require('@senprog/payhub-sdk');
const client = new PayHub('pk_live_xxx', 'sk_live_xxx',
'https://pay.example.com');
// Creer un paiement
const payment = await client.createPayment(
5000, 'XAF', 'mtn_momo', '242060000000',
{ description: 'Commande #1234', environment: 'production' }
);
console.log(payment.status); // "succeeded"
// Rembourser
const refund = await client.createRefund(payment.id,
2500, 'Retour produit');
// Verifier un webhook (Express.js)
app.post('/webhook', (req, res) => {
const valid = PayHub.verifyWebhookSignature(
req.rawBody, req.headers['x-payhub-signature'],
process.env.WEBHOOK_SECRET);
if (!valid) return res.status(401).end();
// Traiter l'evenement...
res.status(200).end();
});Python (requests)
import hmac, hashlib, time, secrets, json, requests
API_KEY = 'pk_live_xxx'
API_SECRET = 'sk_live_xxx'
BASE_URL = 'https://pay.example.com'
def payhub_request(method, path, body=None):
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_str = json.dumps(body) if body else ''
signature = hmac.new(
API_SECRET.encode(),
f'{timestamp}.{body_str}'.encode(),
hashlib.sha256
).hexdigest()
resp = requests.request(method, BASE_URL + path,
headers={
'X-API-KEY': API_KEY,
'X-SIGNATURE': signature,
'X-TIMESTAMP': timestamp,
'X-NONCE': nonce,
'Content-Type': 'application/json',
},
data=body_str or None)
return resp.json()
# Creer un paiement
payment = payhub_request('POST', '/api/v1/payment-intents', {
'amount': 5000,
'currency': 'XAF',
'provider': 'mtn_momo',
'customer_identifier': '242060000000',
'environment': 'production',
})
print(f"ID: {payment['id']}, Status: {payment['status']}")cURL
TIMESTAMP=$(date +%s)
BODY='{"amount":5000,"currency":"XAF","provider":"mtn_momo","customer_identifier":"242060000000","environment":"production"}'
SIGNATURE=$(echo -n "${TIMESTAMP}.${BODY}" | openssl dgst -sha256 -hmac "$API_SECRET" | cut -d' ' -f2)
curl -X POST https://pay.example.com/api/v1/payment-intents \
-H "Content-Type: application/json" \
-H "X-API-KEY: $API_KEY" \
-H "X-SIGNATURE: $SIGNATURE" \
-H "X-TIMESTAMP: $TIMESTAMP" \
-H "X-NONCE: $(uuidgen)" \
-d "$BODY"