PHP
Le package wajub/wajub-php, ses ressources et son intégration dans une application Laravel.
wajub/wajub-php est le client Wajub de votre serveur : avec votre clé secrète, il crée des
paiements, lit leur statut et vérifie les signatures des webhooks, avec ou sans framework.
wajub/wajub-php
Stable · GAPackagist
- Version
- 2.0.1
- Runtime
- PHP 8.4+, Guzzle 8.2+, ext-json
Couvre
- Paiements
- Facturation
- Transferts
- Sync
- Shield
- Taxes
Installer
composer require wajub/wajub-phpCréer le client
Le constructeur accepte un tableau. api_key n'a aucune valeur de repli dans l'environnement,
lisez donc vous-même la variable. Seul webhook_secret retombe sur WAJUB_WEBHOOK_SECRET quand il
est omis.
<?php
use Wajub\Wajub;
$wajub = new Wajub([
'api_key' => getenv('WAJUB_API_KEY'),
'webhook_secret' => getenv('WAJUB_WEBHOOK_SECRET'),
]);| Clé | Fonction |
|---|---|
api_key | Votre clé sk. ou sk_test.. Obligatoire, une valeur vide génère une erreur |
webhook_secret | Secret whsec_, uniquement nécessaire pour webhooks->constructEvent(). WAJUB_WEBHOOK_SECRET par défaut |
http_client | Votre propre GuzzleHttp\Client, pour un proxy ou un handler simulé dans les tests. Donnez à un vrai client base_uri https://api.wajub.com et http_errors false |
max_network_retries | Nombre de nouvelles tentatives après un échec temporaire |
timeout | Délai d'expiration d'une requête en secondes, si vous ne transmettez pas http_client |
Dans une application Laravel
Associez-le une fois comme singleton, puis injectez-le.
<?php
// app/Providers/AppServiceProvider.php
use Wajub\Wajub;
public function register(): void
{
$this->app->singleton(Wajub::class, fn () => new Wajub([
'api_key' => config('services.wajub.secret'),
'webhook_secret' => config('services.wajub.webhook_secret'),
]));
}Dans Laravel, ajoutez les deux clés à config/services.php au lieu d'appeler env() hors d'un
fichier de configuration. Sinon, php artisan config:cache vous donnera une clé nulle en production.
'wajub' => [
'secret' => env('WAJUB_API_KEY'),
'webhook_secret' => env('WAJUB_WEBHOOK_SECRET'),
],Premier appel
$payment = $wajub->payments->create([
'amount' => 25000,
'currency' => 'XAF',
'email' => 'amina@example.com',
'description' => 'Order 4172',
'reference' => 'order-4172',
'callback' => route('checkout.complete'),
]);
return redirect()->away($payment->authorization_url);Les paramètres sont transmis dans un tableau associatif avec les noms de champs exacts de l'API.
La réponse est un ApiObject. Chaque champ est donc une propriété et aucune donnée n'est perdue.
$payment->id; // trx_test_8kQ2mW9vB4nL6hR1cY3d
$payment->status; // pending
$payment->authorization_url; // https://pay.wajub.com/test_xxxxx
$payment->authorization_token; // test_xxxxxLes montants utilisent l'unité principale
25000 en XAF représente vingt-cinq mille francs. Une devise décimale utilise une valeur
décimale : 'amount' => 12.50 en GHS.
Toutes les ressources du client
| Propriété | Méthodes |
|---|---|
| $wajub->global | ping, channels, countries, currencies |
| $wajub->payments | create, retrieve, list, cancel, process, processSplit, listRefunds |
| $wajub->customers | create, retrieve, update, delete, list, block, unblock, activate, deactivate, listTaxIds, createTaxId, deleteTaxId |
| $wajub->refunds | create, retrieve, list |
| $wajub->transfers | create, retrieve, list |
| $wajub->beneficiaries | create, retrieve, update, delete, list |
| $wajub->links | create, retrieve, update, delete, list |
| $wajub->invoices | create, retrieve, update, delete, list, send, markPaid, cancel |
| $wajub->accounts | create, retrieve, update, delete, list, regenerateToken |
| $wajub->webhookEndpoints | create, retrieve, update, delete, list, rotateSecret |
| $wajub->balance | retrieve |
| $wajub->events | list, retrieve, resend |
| $wajub->disputes | list, retrieve, submitEvidence, accept, close, sendMessage |
| $wajub->identity | resolve, validate |
| $wajub->tax | getSettings, updateSettings, rates, calculate, reports, listCodes, retrieveCode, listRegistrations, createRegistration, retrieveRegistration, updateRegistration, deleteRegistration, jurisdictions, thresholds, thresholdAlerts |
| $wajub->shield | getSettings, updateSettings, stats, listBlocklist, addToBlocklist, removeFromBlocklist |
| $wajub->webhooks | constructEvent |
- La vérification de signature de
webhookss'exécute en local, sans appel HTTP. Toutes les autres ressources appellent l'API REST. links,invoices,taxetshieldne fonctionnent qu'en live. Une clé sandbox reçoit403 This feature is only available in live mode.sur chacune de leurs méthodes.refundsettransfersne proposent que la création, la lecture et la liste.
Parcourir une liste paginée
list() renvoie un PagedResult qui implémente Iterator. Un foreach sur l'objet parcourt donc
la page actuelle, tandis que autoPagingIterator() parcourt toutes les pages.
$page = $wajub->payments->list(['status' => 'succeeded', 'per_page' => 50]);
foreach ($page->data as $payment) {
echo $payment['id'].' '.$payment['amount'];
}
if ($page->hasMore) {
$page = $page->getNextPage();
}
// Or let it fetch the following pages for you.
foreach ($wajub->payments->list()->autoPagingIterator() as $payment) {
$this->reconcile($payment);
}$page->data contient de simples tableaux, pas des ApiObject. Les lignes utilisent donc l'accès
par tableau. $page->meta contient les champs de pagination de la liste, décrits dans
Pagination.
Idempotence et nouvelles tentatives
Les échecs temporaires sont réessayés pour vous ; max_network_retries en change le nombre. Pour
qu'une création puisse aussi être répétée sans risque de votre côté, après un redémarrage ou dans
une tâche en file d'attente réessayée, transmettez votre propre clé d'idempotence, le numéro de
commande par exemple.
use Wajub\RequestOptions;
$wajub->payments->create(
$params,
new RequestOptions(idempotencyKey: "order-{$order->id}"),
);Agir pour un compte connecté
use Wajub\RequestOptions;
$wajub->payments->create(
['amount' => 25000, 'currency' => 'XAF', 'email' => $buyer->email],
new RequestOptions(sync: $seller->wajub_account_id),
);La configuration figure dans Sync.
Webhooks
constructEvent vérifie la signature et renvoie l'événement analysé. Il nécessite le corps exact reçu.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Wajub\Exception\WebhookSignatureVerificationError;
use Wajub\Wajub;
class WajubWebhookController extends Controller
{
public function __invoke(Request $request, Wajub $wajub)
{
try {
$event = $wajub->webhooks->constructEvent(
$request->getContent(),
$request->header('X-Wajub-Signature', ''),
$request->header('X-Wajub-Timestamp', ''),
);
} catch (WebhookSignatureVerificationError) {
return response()->noContent(400);
}
if ($event['event'] === 'payment.succeeded') {
HandleWajubEvent::dispatch($event);
}
return response()->noContent(200);
}
}Utilisez getContent, jamais all ou json
La signature est calculée sur le corps brut. $request->all() et $request->json() l'analysent,
et un corps réencodé ne lui correspond plus. $request->getContent() renvoie les octets tels
qu'ils ont été livrés.
Une route de webhook Laravel nécessite deux autres précautions. Excluez-la de la protection CSRF,
car Wajub ne possède aucun jeton de session, et répondez rapidement. Accusez réception avec 200,
puis confiez le travail à une tâche en file d'attente. La livraison est réessayée si votre endpoint répond trop lentement.
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: ['webhooks/wajub']);
})Le nom de l'événement se trouve dans event, pas dans type. Si vos horloges dérivent,
constructEvent accepte la tolérance en secondes comme quatrième argument. Consultez Vérification de signature.
Erreurs
use Wajub\Exception\InvalidRequestException;
use Wajub\Exception\RateLimitException;
use Wajub\Exception\WajubError;
try {
$wajub->payments->create($params);
} catch (InvalidRequestException $e) {
// $e->errors vaut ['amount' => 'The amount must be at least 25.']
return response()->json(['fields' => $e->errors], 422);
} catch (RateLimitException $e) {
return response()->noContent(503)
->header('Retry-After', (string) ($e->retryAfter ?? 5));
} catch (WajubError $e) {
Log::error('wajub failed', ['code' => $e->errorCode, 'status' => $e->httpStatus]);
throw $e;
}Classe dans Wajub\Exception | Cause |
|---|---|
AuthenticationException | 401 |
PermissionException | 403 |
NotFoundException | 404 |
InvalidRequestException | 400 et 422 |
RateLimitException | 429, avec retryAfter en secondes |
WajubError | Tout autre statut et classe parente de toutes les erreurs ci-dessus |
ApiConnectionException | Aucune réponse, problème réseau ou délai d'expiration |
WebhookSignatureVerificationError | Échec de vérification d'un webhook |
getCode renvoie toujours zéro
Lisez le code d'erreur dans $e->errorCode, avec $e->httpStatus, $e->errors et $e->raw :
$e->getCode() vaut 0 pour chaque échec Wajub.
Tester sans appeler l'API
L'option http_client accepte tout client Guzzle. Un handler simulé permet donc d'utiliser tout le SDK hors ligne.
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Wajub\Wajub;
$mock = new MockHandler([
new Response(201, [], json_encode([
'code' => 201,
'authorization_url' => 'https://pay.wajub.com/test_xxxxx',
'authorization_token' => 'test_xxxxx',
'transaction' => ['id' => 'trx_test', 'status' => 'pending', 'amount' => 25000],
])),
]);
$wajub = new Wajub([
'api_key' => 'sk_test.fake',
'http_client' => new Client(['handler' => HandlerStack::create($mock)]),
]);
$payment = $wajub->payments->create(['amount' => 25000, 'currency' => 'XAF']);
$this->assertSame('test_xxxxx', $payment->authorization_token);Débiter sans la page hébergée
$payment = $wajub->payments->create([
'amount' => 25000,
'currency' => 'XAF',
'phone' => '+237670000000',
]);
$wajub->payments->process($payment->id, [
'channel' => 'cm.mtn',
'phone' => '+237670000000',
]);Un canal suit le format country.operator. cm.mtn désigne donc MTN Mobile Money au Cameroun. La
liste complète figure dans Mobile Money. Le payeur doit
encore approuver sur son téléphone. Le résultat arrive donc dans le webhook.
Effectuer un payout
use Wajub\RequestOptions;
$transfer = $wajub->transfers->create(
[
'amount' => 100000,
'currency' => 'XAF',
'beneficiary' => [
'name' => 'Amina Diallo',
'channel' => 'cm.mtn',
'phone' => '+237670000000',
],
'reference' => 'payout-892',
],
new RequestOptions(idempotencyKey: 'payout-892'),
);beneficiary accepte aussi l'identifiant ben_… d'un bénéficiaire enregistré. Préférez cette forme
dès que vous payez deux fois la même personne.
Développement local
wajub listen --forward-to localhost:8000/webhooks/wajub
wajub trigger payment.succeededConsultez la CLI pour plus de détails.
Pages associées
- Démarrage rapide des SDKsLe même premier appel dans cinq langages présentés côte à côte.
- WebhooksTous les événements et leurs garanties de livraison.
- Wajub.jsLa partie navigateur, montée avec le jeton renvoyé par ce SDK.
- IdempotenceCe que protège une clé et pendant combien de temps.
- Gestion des erreursLes échecs à réessayer et ceux à afficher.
- Vue d'ensemble de la référence APILes endpoints utilisés par toutes les méthodes ci-dessus.