Authentication
Centiwise uses two complementary authentication mechanisms on every request:
| Mechanism | Applies To | Purpose |
|---|---|---|
| OAuth 1.0 / RSA-SHA256 | All requests | Proves the request came from your server |
| Control Hash (SHA-256) | Payin only | Proves the payload has not been tampered with |
OAuth 1.0 with RSA-SHA256
Every API call must include an Authorization header in the OAuth 1.0 format, signed using your RSA private key with the SHA-256 algorithm.
Required OAuth Header Fields
| Field | Value |
|---|---|
oauth_consumer_key | Your merchant_login |
oauth_nonce | A unique random string generated per request |
oauth_signature | Base64-encoded RSA-SHA256 signature of the base string |
oauth_signature_method | RSA-SHA256 |
oauth_timestamp | Unix timestamp (seconds since epoch) |
oauth_version | 1.0 |
Example Authorization Header
Authorization: OAuth oauth_consumer_key="your_login",
oauth_nonce="abc123xyz",
oauth_signature="<base64signature>",
oauth_signature_method="RSA-SHA256",
oauth_timestamp="1714000000",
oauth_version="1.0"Node.js Implementation
const crypto = require('crypto');
function buildOAuthHeader(method, url, bodyParams, privateKey) {
const nonce = crypto.randomBytes(16).toString('hex');
const timestamp = Math.floor(Date.now() / 1000).toString();
const oauthParams = {
oauth_consumer_key: process.env.CENTIWISE_LOGIN,
oauth_nonce: nonce,
oauth_signature_method: 'RSA-SHA256',
oauth_timestamp: timestamp,
oauth_version: '2.0',
};
// Combine body params + OAuth params for signature base string
const allParams = { ...bodyParams, ...oauthParams };
const sortedParamString = Object.keys(allParams)
.sort()
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(allParams[k])}`)
.join('&');
const baseString = [
method.toUpperCase(),
encodeURIComponent(url),
encodeURIComponent(sortedParamString),
].join('&');
const signer = crypto.createSign('RSA-SHA256');
signer.update(baseString);
oauthParams.oauth_signature = signer.sign(privateKey, 'base64');
return 'OAuth ' + Object.entries(oauthParams)
.map(([k, v]) => `${k}="${encodeURIComponent(v)}"`)
.join(', ');
}Python Implementation
import time, secrets, urllib.parse, hashlib
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def build_oauth_header(method, url, body_params, private_key_pem):
nonce = secrets.token_hex(16)
timestamp = str(int(time.time()))
oauth_params = {
"oauth_consumer_key": os.getenv("CENTIWISE_LOGIN"),
"oauth_nonce": nonce,
"oauth_signature_method": "RSA-SHA256",
"oauth_timestamp": timestamp,
"oauth_version": "1.0",
}
all_params = {**body_params, **oauth_params}
sorted_str = "&".join(
f"{urllib.parse.quote(k, safe='')}={urllib.parse.quote(str(v), safe='')}"
for k, v in sorted(all_params.items())
)
base_string = "&".join([
method.upper(),
urllib.parse.quote(url, safe=""),
urllib.parse.quote(sorted_str, safe=""),
]).encode()
private_key = serialization.load_pem_private_key(private_key_pem, password=None)
signature = private_key.sign(base_string, padding.PKCS1v15(), hashes.SHA256())
encoded_sig = urllib.parse.quote(base64.b64encode(signature).decode(), safe="")
oauth_params["oauth_signature"] = base64.b64encode(signature).decode()
header_parts = ', '.join(
f'{k}="{urllib.parse.quote(str(v), safe="")}"'
for k, v in oauth_params.items()
)
return f"OAuth {header_parts}"Control Hash (Payin Only)
Every Payin request must include a control field. This is a SHA-256 hash built from your request data and your merchant_control_key.
Formula
control = SHA256( merchant_login + client_orderid + amount + currency + merchant_control_key )Concatenate all five values as plain strings with no separators, then hash.
Node.js
const crypto = require('crypto');
function buildControlHash(clientOrderId, amount, currency) {
const raw = [
process.env.CENTIWISE_LOGIN,
clientOrderId,
String(amount),
currency,
process.env.CENTIWISE_CONTROL_KEY,
].join('');
return crypto.createHash('sha256').update(raw).digest('hex');
}
// Usage
const control = buildControlHash('ORD-001', 1000, 'KES');Python
import hashlib, os
def build_control_hash(client_orderid: str, amount: int, currency: str) -> str:
raw = (
os.getenv("CENTIWISE_LOGIN") +
client_orderid +
str(amount) +
currency +
os.getenv("CENTIWISE_CONTROL_KEY")
)
return hashlib.sha256(raw.encode()).hexdigest()
# Usage
control = build_control_hash("ORD-001", 1000, "KES")
Note:amountmust be converted to a string before concatenation. Do not add any spaces, commas, or separators between values.
Updated 4 months ago
What’s Next
Did this page help you?
