Search by

wulopay / wulopay-php

athi@wulopay.com

PHP library for the WuloPay Hosted Checkout API: checkout sessions, refunds and signed webhooks.

Package info

github.com/WuloPay/wulopay-php

pkg:composer/wulopay/wulopay-php

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-27 19:50 UTC

This package is auto-updated.

Last update: 2026-10-27 21:10:19 UTC


README

Take card payments on WuloPay's secure payment page from any PHP application. Needs PHP 8.3 or newer and the curl and json extensions. We recommend PHP 8.4 or 8.5 on their latest patch release: older releases have critical security flaws (e.g. "PHP2Shell", fixed in 8.3.36, 8.4.27 and 8.5.12 on 21 October 2026).

composer require wulopay/wulopay-php

Take a payment

$wulopay = new \WuloPay\Client(getenv('WULOPAY_KEY_ID'), getenv('WULOPAY_SECRET'));

$session = $wulopay->checkoutSessions->create([
    'amount'    => 149.99,
    'currency'  => 'ZAR',
    'reference' => (string) $order->id,             // your order id
    'description' => 'Order ' . $order->id,
    'returnUrl' => 'https://shop.example.co.za/checkout/return',
    'cancelUrl' => 'https://shop.example.co.za/cart',
    'metadata'  => ['orderKey' => $order->key],
], 'order-' . $order->id);                          // Idempotency-Key: retries make no duplicates

header('Location: ' . $session['url']);             // WuloPay's payment page

The shopper pays on WuloPay's page (card details and 3-D Secure are handled there) and comes back to your returnUrl with ?session_id=cs_…&status=paid. Don't trust the redirect: confirm on your server.

$session = $wulopay->checkoutSessions->retrieve($_GET['session_id']);
if ($session['status'] === 'paid' && $session['reference'] === (string) $order->id) {
    // mark the order paid
}

Webhooks

Add a webhook endpoint in the WuloPay merchant portal (Product Configuration, Webhooks) and keep its signing secret.

try {
    $event = \WuloPay\Webhook::constructEvent(
        file_get_contents('php://input'),            // the raw body, unchanged
        $_SERVER['HTTP_WULOPAY_SIGNATURE'] ?? '',
        getenv('WULOPAY_WEBHOOK_SECRET')
    );
} catch (\WuloPay\Exception\SignatureVerificationException $e) {
    http_response_code(400);
    exit;
}

switch ($event['type']) {
    case 'checkout.session.completed':
        $session = $event['data']['object'];         // status "paid", payment, metadata
        // mark the order paid (ignore an event id you already handled)
        break;
    case 'checkout.session.expired':
        break;
}
http_response_code(200);

Events: checkout.session.completed, checkout.session.expired, checkout.session.payment_failed, refund.succeeded, refund.failed. Retries run for about 3 days, so handle each event id once.

Refunds and cancelling

$wulopay->checkoutSessions->refund($sessionId);                 // everything still refundable
$wulopay->checkoutSessions->refund($sessionId, 20.00, 'Damaged'); // part of it
$wulopay->checkoutSessions->cancel($sessionId);                 // an unpaid session

Errors

Every error extends WuloPay\Exception\WuloPayException (getMessage() is the API's explanation, getHttpStatus() the status): InvalidRequestException (400), AuthenticationException (401), PermissionException (403, the key lacks a scope), NotFoundException (404), ConflictException (409), RateLimitException (429, see getRetryAfter()), ApiConnectionException (network; safe to retry with the same Idempotency-Key).

Tokens

The client gets an access token with your key and secret, keeps it for its lifetime and renews it when needed. In applications where each request is a new PHP process, pass a shared cache so tokens are reused:

$wulopay = new \WuloPay\Client($keyId, $secret, ['tokenCache' => new MyRedisTokenCache()]); // implements TokenCacheInterface

Sandbox and live

The client talks to the sandbox (https://sandbox.wulopay.com/api/v1) by default. Test cards are in the API specification. WuloPay gives you the live URL when your account goes live: new \WuloPay\Client($keyId, $secret, ['baseUrl' => '…']).

Security

Keep the key, secret and webhook signing secret on your server (environment variables or a secrets store), never in code you commit, a web page or a mobile app. Report security problems as described in SECURITY.md.

Development

composer install
vendor/bin/phpunit

MIT licensed.