Sessions & security
API keys, session tokens, callback URLs, and the recommended server-first integration pattern.
API keys
| Key | Prefix | Where |
|---|---|---|
| Publishable | pk_test_… / pk_live_… | Browser only — Wajub(pk), optional createPayment() |
| Secret | sk_test_… / sk_live_… | Server only — POST /payments, webhooks |
| Session token | authorization_token | Browser — sessionId for mount / components |
// ✅ Correct
const client = Wajub('pk_test_abc123');
// ❌ Rejected by the SDK
Wajub('sk_test_abc123');
// WajubError: Secret key cannot be used in the browserRecommended flow (production)
- Your backend creates a payment with
sk_…(via @wajub/node orPOST /payments) and returnsauthorization_token. - Your frontend passes that token as
sessionIdand mounts checkout.
// Frontend — after your API route returns the token
const { sessionToken } = await fetch('/api/checkout/session', { method: 'POST' })
.then(r => r.json());
const rt = await loadWajub();
rt?.wajub.mount('#checkout', { sessionId: sessionToken });Create a session
Use @wajub/node or the REST API with your secret key. Optional
theming sets logo and primary color at creation — see
Branding & theming.
https://api.wajub.com/paymentscurl https://api.wajub.com/payments \
-H "Authorization: sk_test.xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 35000,
"currency": "XOF",
"reference": "ORDER-123",
"description": "Order #123",
"customer": {
"email": "buyer@example.com",
"name": "Amina N.",
"phone": "+2250700000000"
},
"callback": "https://shop.example.com/order/complete",
"metadata": { "order_id": "123" }
}'{
"authorization_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"authorization_url": "https://pay.wajub.com/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"transaction": { "id": "trx_01JXXXXXXXXXXXXX", "status": "pending" }
}Pass authorization_token to Wajub Components as sessionId.
Client-side create (prototypes only)
For demos you may call createPayment() with a publishable key in the browser. In production,
keep session creation on your server.
const client = Wajub('pk_test_...');
const { sessionId } = await client.createPayment({
amount: 35000,
currency: 'XOF',
reference: 'ORDER-123',
customer: { email: 'buyer@example.com' },
callback: 'https://shop.example.com/complete',
});
client.mount('#checkout');Callback URL
When creating a session, pass an HTTPS base URL (no status query param). Wajub redirects
the customer after payment with:
| Query param | Values |
|---|---|
status | complete · cancelled · failed · expired |
reference | Transaction reference (when available) |
trxref | Merchant reference (when available) |
Example redirect:
https://shop.example.com/order/complete?status=complete&reference=trx_01JXXX&trxref=ORDER-123Validate server-side
Treat ?status=complete as a UX hint. Confirm with
webhooks or GET /payments/{id} before
shipping goods or unlocking access.
Session token lifecycle
- Valid while the payment is not terminal;
- Expires after a short grace period once
succeeded,failed,expired, orcancelled; - Re-use the same token for embed remounts until the session ends.
Security checklist
| Rule | Detail |
|---|---|
| HTTPS | Your site and callback must use HTTPS in production |
| Secret key | sk_… never in frontend code or NEXT_PUBLIC_* env vars |
| Session token | Only authorization_token goes to the browser |
| SDK only | Use mount() / open() / components() — never hand-build checkout URLs |
| Webhooks | Authoritative confirmation — not onSuccess alone |
For multi-domain shops, pass embedOrigin:
wajub.mount('#checkout', {
sessionId,
embedOrigin: 'https://shop.example.com',
});