PHP code example of thales / wave

1. Go to this page and download the library: Download thales/wave library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

thales / wave example snippets


$wave = new Thales\Wave\WaveConnector($apiKey);

$session = $wave->checkout()->create(new CreateCheckoutSessionData(
    amount: '1000',
    currency: Currency::XOF,
    successUrl: 'https://shop.example/thanks',
    errorUrl: 'https://shop.example/oops',
    clientReference: 'order-42',
));

header('Location: '.$session->waveLaunchUrl);

use Thales\Wave\WaveConnector;

$wave = new WaveConnector(
    apiKey: getenv('WAVE_API_KEY'),
    signingSecret: getenv('WAVE_SIGNING_SECRET') ?: null, // only if signing is enabled on the wallet
    defaultAggregatedMerchantId: null,                    // aggregators only
);

use Thales\Wave\Laravel\Facades\Wave;

$session = Wave::checkout()->create($data);

public function __construct(private readonly WaveConnector $wave) {}

$wave->checkout()->create($data, $idempotencyKey);       // CheckoutSession
$wave->checkout()->find('cos-18qq25rgr100a');            // CheckoutSession
$wave->checkout()->findByTransactionId('TAWFTGCESD7K');  // CheckoutSession
$wave->checkout()->search('order-42');                   // list<CheckoutSession>
$wave->checkout()->refund('cos-18qq25rgr100a');          // void
$wave->checkout()->expire('cos-18qq25rgr100a');          // void

$session->isPaid();          // funds landed — the only safe gate for fulfilment
$session->isOpen();          // still payable
$session->isExpired();
$session->paymentStatus;     // PaymentStatus enum
$session->checkoutStatus;    // CheckoutStatus enum
$session->whenCompleted;     // ?DateTimeImmutable
$session->lastPaymentError?->errorCode();  // ?ErrorCode

$wave->payouts()->create($data, $idempotencyKey);      // Payout
$wave->payouts()->find('pt-185sewgm8100t');            // Payout
$wave->payouts()->search('FAH.4827.1734');             // list<Payout>
$wave->payouts()->createBatch([$a, $b, $c]);           // string — the batch id
$wave->payouts()->findBatch('pb-185skxq8g1006');       // PayoutBatch
$wave->payouts()->reverse('pt-185sewgm8100t');         // void
$wave->payouts()->verifyRecipient($data);              // RecipientVerification
$wave->payouts()->createB2B($data);                    // B2BPayout  @experimental

$payout = $wave->payouts()->create($data);   // does not throw

if ($payout->isSucceeded()) {
    // money actually moved
} elseif ($payout->isFailed()) {
    $payout->payoutError?->code();           // ?ErrorCode, e.g. RecipientLimitExceeded
} elseif ($payout->isProcessing()) {
    // still in flight — re-check later, do not assume either outcome
}

$payout->receiveAmount;   // '15000'
$payout->fee;             // '150'  (null while still processing)
$payout->totalDebited();  // '15150'

$batchId = $wave->payouts()->createBatch([
    new CreatePayoutData('1000', Currency::XOF, '+221555110219', name: 'Fatou Ndiaye'),
    new CreatePayoutData('1200', Currency::XOF, '+221555110233', name: 'Moustapha Mbaye'),
]);

$batch = $wave->payouts()->findBatch($batchId);

$batch->isComplete();   // every payout attempted — NOT all succeeded
$batch->succeeded();    // list<Payout>
$batch->failed();       // list<Payout>  ← the ones needing attention
$batch->processing();   // list<Payout>

if ($payout->isReversible()) {
    $wave->payouts()->reverse($payout->id);
}

$check = $wave->payouts()->verifyRecipient(new VerifyRecipientData(
    mobile: '+221761110010',
    name: 'Alice Adams',
    amount: '1000',            // amount and currency must travel together,
    currency: Currency::XOF,   // or withinLimits comes back null
));

$check->looksSafe();          // false only if Wave actively disagreed
$check->nameMatches();        // MATCH
$check->nameMismatches();     // NO_MATCH — a real red flag

// Recipient receives exactly 100000; the fee is charged to you on top.
$wave->payouts()->createB2B(new CreateB2BPayoutData('100000', Currency::XOF, 'am-…'));

// You are debited exactly 100000; the fee comes out of the recipient's share.
$wave->payouts()->createB2B(new CreateB2BPayoutData(
    '100000', Currency::XOF, 'am-…', FeePaymentMethod::RecipientPays,
));

$wave->balance()->get();                             // Balance
$wave->balance()->transactions('2026-08-04');        // iterable<Transaction>, all pages
$wave->balance()->transactionsPage('2026-08-04');    // TransactionPage, one page + cursor
$wave->balance()->refundTransaction('T_VZSWJF5MMQ'); // void

$balance = $wave->balance()->get();
$balance->amount;         // '10245'  — a string, always
$balance->currency;       // 'XOF'
$balance->minorUnits();   // 10245

$wave->balance()->get(

foreach ($wave->balance()->transactions('2026-08-04') as $transaction) {
    $transaction->amount;        // '-99'  (signed)
    $transaction->isDebit();
    $transaction->minorUnits();  // -99
}

$transaction->dedupeKey();   // id + reversal flag + timestamp — actually unique

$cursor = $store->get('wave.cursor');

do {
    $page = $wave->balance()->transactionsPage('2026-08-04', after: $cursor);

    foreach ($page->items as $transaction) { /* … */ }

    $cursor = $page->nextCursor();   // null on the last page
    $store->put('wave.cursor', $cursor);
} while ($cursor !== null);

$wave->balance()->refundTransaction('T_VZSWJF5MMQ');

$wave->aggregatedMerchants()->all();                    // iterable<AggregatedMerchant>, all pages
$wave->aggregatedMerchants()->page();                   // AggregatedMerchantPage, one page + cursor
$wave->aggregatedMerchants()->create($data);            // AggregatedMerchant
$wave->aggregatedMerchants()->find('am-7lks22ap113t4'); // AggregatedMerchant
$wave->aggregatedMerchants()->update($id, $data);       // AggregatedMerchant
$wave->aggregatedMerchants()->delete($id);              // void

$merchant = $wave->aggregatedMerchants()->create(new AggregatedMerchantData(
    name: 'Moustaphas Groceries',              // must be unique across your merchants
    businessDescription: 'A grocery store in the heart of the city.',
    businessType: BusinessType::Other,
    websiteUrl: 'https://groceries.example.com',
    managerName: 'Moustapha',
));

$wave->checkout()->create(new CreateCheckoutSessionData(
    amount: '1000',
    currency: Currency::XOF,
    successUrl: 'https://…/ok',
    errorUrl: 'https://…/ko',
    aggregatedMerchantId: $merchant->id,
));

if ($merchant->isEditable()) { /* safe to offer an edit form */ }

$merchant->isLocked;             // true once reviewed
$merchant->hasFeeStructures();   // false until Wave assigns them

$merchant->payoutFeeStructure?->percentage();    // 1.5 for one_fifty_bps
$merchant->checkoutFeeStructure?->basisPoints(); // 150 — prefer this for exact arithmetic

$merchant = $wave->aggregatedMerchants()->find($id);

$wave->aggregatedMerchants()->update(
    $id,
    $merchant->toRequestData(name: 'Moustaphas Candle Shop'),
);

use Thales\Wave\Data\Webhook\WebhookEvent;
use Thales\Wave\Enums\WebhookEventType;

$event = WebhookEvent::fromRequest(
    rawBody: $rawRequestBody,          // the exact bytes — see the warning below
    signatureHeader: $waveSignature,   // the Wave-Signature header
    secret: $webhookSecret,
);

match ($event->type) {
    WebhookEventType::CheckoutSessionCompleted => $this->fulfil($event->checkout()),
    WebhookEventType::MerchantPaymentReceived  => $this->credit($event->merchantPayment()),
    WebhookEventType::B2BPaymentReceived       => $this->record($event->b2bPayment()),
    default => null,                   // acknowledge anything else
};

use Thales\Wave\Laravel\Http\Middleware\VerifyWaveSignature;

Route::post('/wave/webhook', WebhookController::class)
    ->middleware(VerifyWaveSignature::class);

public function __invoke(Request $request)
{
    $event = VerifyWaveSignature::event($request);   // already verified and parsed

    ProcessWaveWebhook::dispatch($event->toArray());

    return response()->noContent();
}

$checkout = $event->checkout();

$checkout->isPaid();
$checkout->hasFailed();
$checkout->lastPaymentError?->errorCode();   // ?ErrorCode
$checkout->toCheckoutSession();              // full DTO, or null if too partial
$checkout->raw();                            // anything unmodelled

if (! $event->isKnown()) {
    Log::info('Unrecognised Wave event', ['type' => $event->rawType]);

    return response()->noContent();   // 2xx — stop the retries
}

Signature::verify($header, $rawBody, $secret);            // bool, never throws
Signature::verify($header, $rawBody, $secret, 600);       // wider tolerance

use Thales\Wave\Support\Amount;

Amount::fromInt(1000);                          // '1000'
Amount::fromMinorUnits(1050, Currency::GMD);    // '10.50'
Amount::toMinorUnits('10.10', Currency::GMD);   // 1010  — exactly, not 1009

use Thales\Wave\Enums\ErrorCode;
use Thales\Wave\Exceptions\{WaveException, ValidationException, NotFoundException};

try {
    $session = $wave->checkout()->create($data);
} catch (NotFoundException $e) {
    // 404
} catch (ValidationException $e) {
    $e->details();      // field-level failures from Wave
} catch (WaveException $e) {
    report($e);
}

$e->errorCode();                            // ?ErrorCode enum
$e->rawCode();                              // the string Wave sent, always available
$e->is(ErrorCode::InsufficientFunds);
$e->getStatus();
$e->getResponse();                          // the Saloon Response

$wave->checkout()->create($data, idempotencyKey: 'order-42');

$wave = new WaveConnector(apiKey: $key, signingSecret: $secret);

use Thales\Wave\Support\Signature;

Signature::verify($request->header('Wave-Signature'), $rawRequestBody, $secret);

foreach ($wave->paginate($request)->items() as $item) {
    // one page fetched at a time
}

use Saloon\Http\Faking\{MockClient, MockResponse};

$mock = new MockClient([
    CreateCheckoutSessionRequest::class => MockResponse::make($sessionJson, 200),
]);

$wave->withMockClient($mock)->checkout()->create($data);
bash
php artisan vendor:publish --tag=wave-config