Quickstart
A test payment and its webhook, end to end: your server creates a checkout session, the customer pays it, and your server fulfils the order from the signed webhook.
1. Get test keys
In the developer console, create an API app in test mode. Copy its publishable key (pk_test_…) and secret key (sk_test_…, shown once) into your server's environment. A test key never moves money.
2. Install an SDK
npm install @watenga/nodeUntil the packages are on their registries, each SDK page shows how to install it from the Watenga repository. Raw HTTP works too: send Authorization: Bearer sk_test_… and Watenga-Version: 2026-10-29.
3. Create a checkout session on your server
The amount is decided by your server; the browser or app only pays the session.
import express from 'express';
import Watenga from '@watenga/node';
const watenga = new Watenga(process.env.WATENGA_SECRET_KEY!); // sk_test_...
const app = express();
app.post('/checkout-session', async (_req, res) => {
const session = await watenga.checkoutSessions.create({
amount: '25.00', // your server decides the amount
currency: 'USD',
reference: 'ORD-1001',
return_url: 'http://localhost:3000/thank-you',
});
res.json({ client_secret: session.client_secret, url: session.url });
});4. Let the customer pay
Redirect to the session's url (a Watenga-hosted page), or show the checkout in your page with Watenga.js and the session's client_secret:
<script src="https://js.watenga.africa/v1/watenga.js"></script>
<div id="payment-form"></div>
<script>
// Your server created the checkout session (step 1) and returned its client_secret.
fetch('/checkout-session', { method: 'POST' })
.then((r) => r.json())
.then((session) => {
Watenga('pk_test_your_publishable_key').mount('#payment-form', {
clientSecret: session.client_secret,
amount: 25.00,
currency: 'USD',
reference: 'ORD-1001',
onSuccess: function (txn) { window.location.href = '/thank-you?payment=' + txn.id; },
onError: function (err) { console.error(err.code, err.message); }
});
});
</script>Pay with a test card (any future expiry, any CVC):
| Card number | Brand | What happens |
|---|---|---|
4111 1111 1111 1111 | Visa | Approved: payment.capture.completed |
4000 0000 0000 0002 | Visa | Declined by the issuer (do not retry): payment.capture.declined |
4000 0000 0000 9995 | Visa | Declined, insufficient funds (the buyer may retry) |
4000 0000 0000 0119 | Visa | Provider timeout: stays pending, then fails |
Expiry: any future month, e.g. 12/34. CVC: any 3 digits. Any name and address. Test keys only: these numbers never reach a card network.
5. Receive the webhook
Fulfil the order when payment.capture.completed arrives, never from onSuccess or the return address alone. Verify the Watenga-Signature header against the raw body:
import express from 'express';
import { Webhooks } from '@watenga/node';
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = Webhooks.constructEvent(req.body, req.headers['watenga-signature'] as string, process.env.WATENGA_WEBHOOK_SECRET!);
} catch {
return res.sendStatus(400);
}
if (event.type === 'payment.capture.completed') {
// fulfil the order for event.data.object (a transaction)
}
res.sendStatus(200);
});On your machine, forward your test app's events with the CLI and use the secret it prints:
watenga listen --forward-to http://localhost:3000/webhooksIn production, add your HTTPS endpoint under the app's webhook endpoints in the developer console and use its signing secret.