Skip to content

SDK Quickstart

A key, an install line, one call, and a payment page you can open.

Get a key, install one package and make one call: what comes back is a Wajub payment page you can open, and a payment you then confirm on your server.

1. Get a key

Keys live in the Dashboard, under Settings, then Developers, then API keys. Every account has two sets from the first day, and the prefix is what tells them apart.

PrefixWhere it belongsWhat it can do
sk_test.Your server, sandboxEverything, on test money
sk.Your server, liveEverything, on real money
pk_test. / pk.The browserRead a session, nothing else
rk_test. / rk.A script or a CI jobOnly the scopes you grant it

Server SDKs take the secret key.

2. Store it

Python, Ruby, Go, Java and C# read WAJUB_API_KEY from the environment, and every server SDK reads WAJUB_WEBHOOK_SECRET, so a .env file is usually the whole configuration step.

.env
WAJUB_API_KEY=sk_test.xxxxxxxxxxxxxxxx…
WAJUB_WEBHOOK_SECRET=whsec_test_xxxxxxxxxxxxxxxx

Node.js and PHP are the exceptions for the key: they do not read WAJUB_API_KEY, so you pass it explicitly, as the examples below do.

3. Install

One package per language, from the registry you already use.

LanguageInstallNeeds at least
Node.jsnpm install @wajub/nodeNode.js 18
Pythonpip install "wajub>=1.2.1"Python 3.10
PHPcomposer require wajub/wajub-phpPHP 8.4
Gogo get github.com/wajubhq/wajub-goGo 1.22
Rubygem install wajubRuby 3.1
Javacom.wajub:wajub-java:1.2.1Java 17
C#dotnet add package Wajub.NET 8

The API version is pinned on your account, not in the SDK

Upgrading an SDK never changes the API version you get: your account is pinned at creation. To try another version on one request, send the X-Wajub-Version header yourself. Details on Versioning.

4. Create a payment

This is the call. An amount, a currency, a way to reach the customer, and a URL to come back to.

curl https://api.wajub.com/payments \
  -H "Authorization: $WAJUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "currency": "XAF",
    "email": "amina@example.com",
    "description": "Order 4172",
    "reference": "order-4172",
    "callback": "https://shop.example.com/complete"
  }'

Only amount and currency are truly required. Beyond those you need one way to identify the payer, which is email, phone or customer_id, any one of the three. callback is what makes the redirect flow work, and reference is your own order number coming back on every webhook.

5. Read what came back

You get one object with the transaction fields and the two authorization fields together.

The fields that matter
{
"id": "trx_test_8kQ2mW9vB4nL6hR1cY3d",
"reference": "order-4172",
"amount": 25000,
"currency": "XAF",
"status": "pending",
"authorization_url": "https://pay.wajub.com/test_xxxxx",
"authorization_token": "test_xxxxx",
"sandbox": true
}
FieldWhat you do with it
authorization_urlSend the customer here, or open it yourself to test
authorization_tokenThe session token, if you embed the checkout instead of redirecting
idYour handle on the payment, for retrieve and for refunds. trx_test_ in sandbox, trx_ live
referenceYours, echoed back on the object and on every webhook
statuspending on creation. It is not the final word

Open authorization_url in a browser now. That page is the checkout, already carrying your account branding, and the sandbox accepts the test cards and numbers.

6. Confirm it server side

The redirect brings the customer back to your callback URL with a status parameter. Use it to pick the page you show them, never to release the goods.

curl https://api.wajub.com/payments/trx_test_8kQ2mW9vB4nL6hR1cY3d \
  -H "Authorization: $WAJUB_API_KEY"

The browser is not a reliable reporter. A customer can close the tab on a successful payment, and a Mobile Money confirmation can land minutes after the redirect. The webhook is the event that is guaranteed to arrive, so fulfilment belongs there and this call is the fallback.

When the call fails

Every SDK raises a typed error rather than returning a status. The class is the decision: retry, fix the request, or tell the payer something.

HTTPClassWhat it means
401AuthenticationErrorWrong key, or a live key against sandbox data
403PermissionErrorThe key is valid but lacks the scope
404NotFoundErrorNo such payment, customer or account
400, 422InvalidRequestErrorMalformed request. errors names the field
429RateLimitErrorToo many requests. retry_after carries the wait
otherWajubErrorThe base class, also the one to catch broadly
noneApiConnectionErrorNetwork or timeout, no response at all

Three spellings differ from that list. PHP suffixes its subclasses with Exception, so the row above reads InvalidRequestException, while the base stays WajubError. Java suffixes every class with Exception, the base included: WajubException, InvalidRequestException, ApiConnectionException, all unchecked. Go capitalizes the initialism, so its connection class is APIConnectionError.

Whatever the class, four fields are always there: the message, a code, the HTTP status, and an errors map keyed by field name. That last one is what you show back on a form.

import { WajubError, WajubInvalidRequestError, WajubRateLimitError } from '@wajub/node';

try {
  await wajub.payments.create({ amount, currency: 'XAF', email });
} catch (error) {
  if (error instanceof WajubInvalidRequestError) {
    return res.status(422).json({ fields: error.errors });
  }
  if (error instanceof WajubRateLimitError) {
    return res.status(503).set('Retry-After', String(error.retryAfter ?? 5)).end();
  }
  if (error instanceof WajubError) {
    logger.error({ code: error.code, status: error.httpStatus });
  }
  throw error;
}

Where to go next

What did you think of this content?