Developer Resources
Error Handling
Wrap every API call and log enough context to reconcile later without exposing secrets or PII.
try {
const checkout = await client.payment.checkout.initialize(payload);
logger.info('cashonrails.checkout.created', {
reference: payload.reference,
transactionRef: checkout.data?.transactionRef
});
} catch (error) {
logger.error('cashonrails.checkout.failed', {
reference: payload.reference,
status: error.response?.status,
body: error.response?.data
});
throw error;
}Response Handling
The API uses both success and status envelopes depending on the endpoint. Always check HTTP status plus the response body.
function isSuccessful(response) {
return response?.success === true || response?.status === true;
}Idempotency
Use stable references and idempotency keys for retryable writes:
await client.transfer.toBank(payload, {
idempotencyKey: payload.reference
});If the same idempotency key is reused with a different payload, checkout creation can return HTTP 409.
Webhook Verification
For Express-style handlers, keep the raw body so you can verify payloadsignature correctly:
import crypto from 'node:crypto';
function verifyPayloadSignature(rawBody, signature, secretKey) {
const expected = crypto
.createHmac('sha512', secretKey)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature || ''));
}QA Checklist
- Missing or invalid secret key returns
401. - Unauthorized IP returns
403when allowlisting is enabled. - Checkout initialization returns
authorization_url,access_code,transactionRef, and yourreference. - Reusing a checkout reference with a different payload is rejected.
- Payout request with invalid RSA signature returns
403when RSA public key is configured. - Replayed payout request with the same
Idempotency-Keydoes not create a duplicate transfer. - Duplicate webhook does not double-fulfil an order or double-credit a wallet.
- Encrypted-account requests fail when sent as plain JSON.
Useful References
Last updated on