Laravel integration

Accept payments in a Laravel application using the official Watenga PHP SDK, Blade views, and signed webhooks.

Installation

Install the Watenga PHP package from Composer. The package name is watenga/watenga-php and it wraps the same REST API as the Node SDK.

bash
composer require watenga/watenga-php

Add your API credentials to .env. Use sandbox keys while developing; switch to live keys only after KYC approval and a live API app in the dashboard.

bash
WATENGA_SECRET_KEY=sk_test_...
WATENGA_PUBLIC_KEY=pk_test_...
WATENGA_WEBHOOK_SECRET=whsec_...

Register the keys in config/services.php so controllers and service providers can resolve them with config().

php
'watenga' => [
    'secret_key' => env('WATENGA_SECRET_KEY'),
    'public_key' => env('WATENGA_PUBLIC_KEY'),
    'webhook_secret' => env('WATENGA_WEBHOOK_SECRET'),
],

Service provider (recommended)

For larger applications, bind Watenga\Watenga as a singleton so you can inject it into controllers instead of instantiating the client in every action.

php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Watenga\Watenga;

class WatengaServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(Watenga::class, function () {
            return new Watenga(config('services.watenga.secret_key'));
        });
    }
}

Register the provider in your application configuration.

php
// config/app.php — add to the providers array:
App\Providers\WatengaServiceProvider::class,

After registration, type-hint Watenga in any controller constructor and Laravel will resolve the configured client automatically.

Accepting a payment

Create a checkout session on the server when the customer starts checkout. Return the checkout_id and payment_url to your frontend, or embed Watenga.js with your public key for an on-page widget.

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Watenga\Watenga;

class PaymentController extends Controller
{
    public function __construct(private Watenga $watenga) {}

    public function createCheckout(Request $request): JsonResponse
    {
        $order = Order::findOrFail($request->input('order_id')); // the amount comes from your records

        $checkout = $this->watenga->checkout->create([
            'amount' => $order->total,
            'currency' => $order->currency,
            'merchant_transaction_id' => 'INV-' . $request->input('order_id'),
            'return_url' => route('payment.success'),
            'customer_name' => $request->user()?->name,
            'customer_email' => $request->user()?->email,
        ]);

        return response()->json([
            'checkout_id' => $checkout['checkout_id'],
            'payment_url' => $checkout['payment_url'],
        ]);
    }
}

In your Blade template, load Watenga.js and mount the widget using the public key from configuration. Never put the secret key in views or JavaScript.

php
{{-- resources/views/checkout.blade.php: $session comes from $watenga->checkoutSessions->create([...]) in your controller --}}
<div id="watenga-payment"></div>

<script src="https://js.watenga.africa/v1/watenga.js"></script>
<script>
  Watenga(@json(config('services.watenga.public_key'))).mount('#watenga-payment', {
    clientSecret: @json($session['client_secret']),
    amount: {{ $session['amount'] }},
    currency: @json($session['currency']),
    reference: @json($session['reference']),
    onSuccess: function () {
      window.location.href = @json(route('payment.success')); // the webhook confirms the order
    },
  });
</script>

Handling webhooks

Watenga notifies your application when a payment completes or fails. Verify every payload with Watenga::constructEvent() before updating orders or inventory.

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Watenga\Watenga;

class WatengaWebhookController extends Controller
{
    public function handle(Request $request): JsonResponse
    {
        $rawBody = $request->getContent();
        $signature = $request->header('Watenga-Signature');

        try {
            $event = Watenga::constructEvent($rawBody, $signature, config('services.watenga.webhook_secret'));
        } catch (\Watenga\SignatureVerificationException $e) {
            return response()->json(['error' => 'bad signature'], 400);
        }

        if ($event['type'] === 'payment.capture.completed') {
            // Mark the order paid using $event['data']['object'] (the transaction)
        }

        if ($event['type'] === 'payment.capture.declined') {
            // Notify customer or release inventory
        }

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

Register a POST route for the webhook endpoint.

php
// routes/api.php
Route::post('/webhooks/watenga', [WatengaWebhookController::class, 'handle'])
    ->name('webhooks.watenga');

CSRF exemption required

Exclude the Watenga webhook route from Laravel's CSRF protection. Add it to the $except array in app/Http/Middleware/VerifyCsrfToken.php.
php
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
    'api/webhooks/watenga',
];

WooCommerce and WHMCS

If your Laravel app uses the Watenga WooCommerce or WHMCS plugin, you do not need this guide — the plugins handle checkout, webhooks, and reconciliation automatically.

Get notified when InnBucks and OneMoney go live.