PHP SDK watenga/watenga-php

PHP 7.4 or later with ext-curl and ext-json.

Installation

bash
composer require watenga/watenga-php

Before the first release

The package is release-ready; until it is published, install it from a checkout of the Watenga repository:
bash
# composer.json
"repositories": [{ "type": "path", "url": "../watenga-payment-network/packages/watenga-php" }]
composer require watenga/watenga-php:@dev

Take a test payment

Use a test secret key (sk_test_…) from the developer console. Test keys never move money: use the test cards and wallet numbers.

php
use Watenga\Watenga;

$watenga = new Watenga(getenv('WATENGA_SECRET_KEY')); // sk_test_...

$checkout = $watenga->checkout->create([
    'amount' => '25.00',
    'currency' => 'USD',
    'merchant_transaction_id' => 'ORD-1001',
    'return_url' => 'https://shop.example/return',
]);
header('Location: ' . $checkout['payment_url']);

Receive the webhook

Verify the Watenga-Signature header against the raw request body before you trust an event. While you rotate the endpoint's signing secret, pass both secrets: either one verifies. On your machine, watenga listen --forward-to <your URL> (the CLI) forwards test events signed with a session secret.

php
use Watenga\SignatureVerificationException;
use Watenga\Webhook;

try {
    $event = Webhook::constructEvent(
        file_get_contents('php://input'),
        $_SERVER['HTTP_WATENGA_SIGNATURE'] ?? null,
        getenv('WATENGA_WEBHOOK_SECRET')
    );
} catch (SignatureVerificationException $e) {
    http_response_code(400);
    exit;
}
if ($event['type'] === 'payment.capture.completed') {
    // fulfil the order for $event['data']['object']
}
http_response_code(200);

Errors

Every failed call raises one error type with the stable code, the HTTP status, the request_id to quote to support, the field at fault (param) and the code's page in the error reference.

php
use Watenga\WatengaException;

try {
    $watenga->refunds->create('txn_...', ['amount' => '5.00', 'reason' => 'customer_request']);
} catch (WatengaException $e) {
    echo $e->getErrorCode(), $e->getStatusCode(), $e->getParam(), $e->getRequestId(), $e->getDocUrl();
}

Retries and idempotency

Network errors, 429 (after Retry-After) and 5xx answers are retried twice with backoff. Every write carries an Idempotency-Key, kept across those retries, so a retry is answered once and never applied twice; checkout.create uses checkout:<merchant_transaction_id>. See Idempotency.

php
// Every write gets an Idempotency-Key, kept across the SDK's own retries.
// When you retry a call yourself, pass the same key (last argument):
$watenga->refunds->create('txn_...', ['reason' => 'duplicate'], 'refund-ORD-1001');

Pagination

Every list answers one page, { object: "list", data, has_more, next_cursor }; the auto-paginating form reads them all. See Pagination.

php
$page = $watenga->transactions->list(['status' => 'completed', 'limit' => 50]);
// ['object' => 'list', 'data' => [...], 'has_more' => ..., 'next_cursor' => ...]

foreach ($watenga->transactions->listAll(['status' => 'completed']) as $txn) {
    echo $txn['id'], ' ', $txn['amount'], PHP_EOL;
}

Resources

The SDK sends Watenga-Version: 2026-10-29; fields use the API's names (snake_case) and money is a decimal string. The same method names in every language.

ResourceMethods
checkoutcreate, getStatus
checkoutSessionscreate, retrieve, expire
transactionslist, listAll, retrieve, getMobileStatus
refundscreate
paymentLinkscreate, list, listAll, retrieve, update, deactivate, clone
invoicescreate, list, listAll, retrieve, update, send, remind, void
quotationscreate, list, listAll, retrieve, update, issue, void, convertToInvoice
planscreate, list, listAll, retrieve, update, delete
subscriptionscreate, list, listAll, retrieve, cancel, pause, resume, upcomingInvoice
billingrecordUsage, reverseUsage, checkEntitlement
customerscreate, list, listAll, retrieve, update
paymentMethodscreate, list, listAll, retrieve, delete, charge
accountgetBalance, getProfile, getApiKey
WebhookconstructEvent, generateTestSignature

Resources come back as arrays; Watenga\Types names their fields (PHPStan/Psalm shapes generated from the OpenAPI document).

Refunds and payouts

A secret key refunds when its app's Refunds switch is on and an Admin has set refund limits; a restricted key needs the refunds scope (Refunds). Payouts to your bank are dashboard-only, with two-step verification: they are not in the API or the SDKs.