Idempotency
Prevent duplicate charges when network errors interrupt your payment requests.
What Is Idempotency
Idempotency means that making the same API call multiple times produces the same result as making it once. For payment APIs, idempotency is critical: if your server sends a payment request and a network error occurs before you receive the response, you do not know whether the payment succeeded. If you simply retry, you might charge the customer twice.
Watenga solves this with the merchantTransactionId field. Every payment creation request must include a unique merchantTransactionId. If Watenga receives two requests with the same merchantTransactionId from the same merchant, the second request returns the result of the first — no duplicate charge is created.
How It Works
First request
POST /v1/checkout/create
{
"merchantTransactionId": "ORDER-2847",
"amount": "50.00",
...
}
→ Watenga creates checkout. Returns checkoutId.Network error — you never receive the response.
Retry the same request
POST /v1/checkout/create
{
"merchantTransactionId": "ORDER-2847",
"amount": "50.00",
...
}
→ Watenga recognises ORDER-2847. Returns the same checkoutId from before.
→ No new checkout is created. No duplicate charge.Different amount with the same ID
POST /v1/checkout/create
{
"merchantTransactionId": "ORDER-2847",
"amount": "99.00",
...
}
→ Watenga returns error: DUPLICATE_REQUEST
→ The merchantTransactionId is already associated with $50.00.First POST with ORDER-2847
Creates checkout → checkoutId
Network error before response
Retry POST with same ORDER-2847
Returns same checkoutId — no duplicate
POST with ORDER-2847 but different amount
DUPLICATE_REQUEST error
Generating Good Idempotency Keys
A good merchantTransactionId is unique, stable, and meaningful to your system. The best approach is to use your own order or invoice ID:
curl -X POST https://api.watenga.africa/v1/checkout/create \
-H "Authorization: Bearer sk_test_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"merchantTransactionId": "ORD-8472",
"amount": "50.00",
"currency": "USD",
"returnUrl": "https://yoursite.com/thank-you",
"notificationUrl": "https://yoursite.com/webhooks/watenga"
}'Never use a random string as your merchantTransactionId — if you generate a new random ID on each retry you lose the idempotency benefit entirely. Never use a timestamp — timestamps are not unique under load. Always use your own order, invoice, or reference ID from your database.
Which Endpoints Support Idempotency
| Endpoint | Idempotency Field | Notes |
|---|---|---|
POST /v1/checkout/create | merchantTransactionId | Required. Primary idempotency key. |
POST /v1/payment-links | — | Safe to retry — creates new link each time |
POST /v1/payouts/request | — | Watenga deduplicates by amount + time window |
POST /v1/transactions/:id/refund | — | Idempotent by transaction ID — safe to retry |
Testing Idempotency in Sandbox
To verify idempotency is working in your integration:
- Create a checkout with
merchantTransactionId: 'TEST-IDEM-001' - Note the
checkoutIdreturned - Create another checkout with the same
merchantTransactionId - Confirm you receive the same
checkoutId - Try with a different amount — confirm you receive
DUPLICATE_REQUESTerror
This test should be part of your integration test suite before going live.
const watenga = new Watenga('sk_test_YOUR_SANDBOX_KEY');
const first = await watenga.checkout.create({
merchantTransactionId: 'TEST-IDEM-001',
amount: 10.0,
currency: 'USD',
returnUrl: 'https://example.com',
notificationUrl: 'https://example.com/webhook',
});
const second = await watenga.checkout.create({
merchantTransactionId: 'TEST-IDEM-001',
amount: 10.0,
currency: 'USD',
returnUrl: 'https://example.com',
notificationUrl: 'https://example.com/webhook',
});
console.assert(first.checkoutId === second.checkoutId, 'Idempotency is working');Get notified when InnBucks and OneMoney go live.
