Authentication
Learn how to authenticate API requests using HMAC-SHA256 signatures for maximum security.
Overview
EPaySe uses HMAC-SHA256 signatures to authenticate API requests. Every request must include your API key and a signature generated using your API secret. This ensures that requests are authentic and haven't been tampered with.
SDKs are in development
API Keys
API keys are used to identify your account and authenticate requests. Each key consists of two parts:
- API Key ID - the identifier shown next to the key in your dashboard. Send it in the X-Api-Key-Id header. It is not secret.
- Secret Key -
sk_test_.../sk_live_...- used only to sign requests, never sent. It is shown once when the key is generated - save it immediately.
Keys you generate in the dashboard start with sk_test_ (sandbox) or sk_live_ (production). The Default API Key created together with a new account is sk_ followed by a hex string and is used the same way.
Key Types
- Sandbox Keys -
sk_test_...- For testing and development. No real money is processed. - Production Keys -
sk_live_...- For live transactions. Real payments are processed.
Generating API Keys
- Log in to your EPaySe dashboard
- Navigate to Settings โ API Keys
- Click "Generate New Key" and give the key a name
- Copy the Secret Key now (it is shown once) and note the API Key ID
Keep Your Keys Secret!
Whitelist your server IP first
HMAC Signature
HMAC (Hash-based Message Authentication Code) signatures ensure request integrity and authenticity. Each request must include these headers:
Required Headers
X-Api-Key-Id: Your API keyX-Signature: HMAC-SHA256 signatureX-Timestamp: Unix timestampX-Nonce: Unique random string, new for every requestX-Signature-Version: Signature version. Send 3 (the version documented on this page); it is accepted for every key.
๐ Signature Format
The HMAC signature is generated using the following format:
HMAC-SHA256(secret_key, "METHOD|PATH|TIMESTAMP|NONCE|BODY") Version 3 signs the nonce as well. Older keys may still use versions 1 and 2 (path only / path + query, no nonce): once a key has authenticated with a newer version it must keep sending it, and a request without the header gets a 401 that names the version it needs.
# 1. Build the string to sign (v3) โ five parts joined with "|"
POST|api/v1/transaction/create|1699564800|5f2c9e1a7b3d4c68a1e0b9d2c3f4a5b6|{"ref":"ORDER-1001","amount":100.5,"currency":"USD","websiteUrl":"https://example.com","redirectUrl":"https://example.com/payment/success","cancelUrl":"https://example.com/payment/cancel","description":"Premium subscription - monthly","customerDetails":{"firstName":"John","lastName":"Doe","email":"[email protected]","country":"US","ip":"203.0.113.10","phoneCode":"US","phoneNumber":"5551234567"}}
# 2. Sign it with your SECRET KEY (not the key ID) โ lowercase hex, sent as is (no prefix)
# secret_key used for this example: sk_test_docs_example_secret_0000000000000000000000000000000000000000
X-Signature = HMAC-SHA256(secret_key, string_to_sign)
= 459983afd8b42d0ea5ece82606a782345b2b07c6caa0de44c38da32edcb71371Example Request
POST /api/v1/transaction/create HTTP/1.1
Host: staging-gateway.epayse.com
Content-Type: application/json
X-Api-Key-Id: 01docsexampleapikeyid00000
X-Signature: 459983afd8b42d0ea5ece82606a782345b2b07c6caa0de44c38da32edcb71371
X-Timestamp: 1699564800
X-Nonce: 5f2c9e1a7b3d4c68a1e0b9d2c3f4a5b6
X-Signature-Version: 3
{
"ref": "ORDER-1001",
"amount": 100.5,
"currency": "USD",
"websiteUrl": "https://example.com",
"redirectUrl": "https://example.com/payment/success",
"cancelUrl": "https://example.com/payment/cancel",
"description": "Premium subscription - monthly",
"customerDetails": {
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"country": "US",
"ip": "203.0.113.10",
"phoneCode": "US",
"phoneNumber": "5551234567"
}
}cURL Example with HMAC Headers
curl -X POST "https://staging-gateway.epayse.com/api/v1/transaction/create" \
-H "Content-Type: application/json" \
-H "X-Api-Key-Id: 01docsexampleapikeyid00000" \
-H "X-Signature: 459983afd8b42d0ea5ece82606a782345b2b07c6caa0de44c38da32edcb71371" \
-H "X-Timestamp: 1699564800" \
-H "X-Nonce: 5f2c9e1a7b3d4c68a1e0b9d2c3f4a5b6" \
-H "X-Signature-Version: 3" \
-d '{"ref":"ORDER-1001","amount":100.5,"currency":"USD","websiteUrl":"https://example.com","redirectUrl":"https://example.com/payment/success","cancelUrl":"https://example.com/payment/cancel","description":"Premium subscription - monthly","customerDetails":{"firstName":"John","lastName":"Doe","email":"[email protected]","country":"US","ip":"203.0.113.10","phoneCode":"US","phoneNumber":"5551234567"}}'๐งช Test HMAC Authentication
Try making an authenticated request to test your API credentials. This example uses the Currencies API endpoint. The playground signs the request in your browser and sends it to the sandbox gateway (never production), so use a sandbox key; for a server-to-server call use the cURL example above.
GET /api/v1/currencies
Test HMAC authentication with a simple GET request
curl -X GET "https://sandbox-gateway.epayse.com/api/v1/currencies" \
-H "Content-Type: application/json"Copy this prompt to Claude, ChatGPT, Gemini, or any AI assistant for implementation help
Help me implement an API call to this endpoint:
**Endpoint Details:**
- Method: GET
- URL: https://sandbox-gateway.epayse.com/api/v1/currencies
- Content-Type: application/json
**Authentication:**
This endpoint requires HMAC-SHA256 authentication (signature version 3) with the following headers:
- X-Api-Key-Id: [Your API Key ID]
- X-Signature: [HMAC-SHA256 signature, lowercase hex, no prefix]
- X-Timestamp: [Unix timestamp in seconds]
- X-Nonce: [Unique random string, new for every request]
- X-Signature-Version: 3
**HMAC Signature Generation:**
The signature is computed as: HMAC-SHA256(secret_key, data_to_sign) โ the secret key, not the API Key ID
Where data_to_sign = METHOD|PATH|TIMESTAMP|NONCE|BODY
- PATH has no leading slash and includes the query string, if any
- NONCE is the same value sent in X-Nonce
- BODY is the exact JSON string you send (an empty string when there is no body)
Example: GET|api/v1/currencies|1699564800|5f2c9e1a7b3d4c68a1e0b9d2c3f4a5b6|
**Requirements:**
1. Implement proper error handling
2. Add request timeout (30 seconds recommended)
3. Generate HMAC signature correctly
4. Include all required authentication headers
5. Parse and return the JSON response
6. Handle different HTTP status codes (200, 400, 500, etc.)
**Expected Response Format:**
```json
{
"status": "SUCCESS" | "ERROR",
"message": "Success message or error description",
"data": { ... }
}
```
Please provide working code in [YOUR_LANGUAGE] with best practices and comments.๐ก Pro Tip:
After copying, paste this prompt into Claude, ChatGPT, or Gemini and specify your programming language. The AI will generate complete, working code with proper error handling and HMAC authentication.
Manual Implementation
If you're not using an SDK, you'll need to generate HMAC signatures manually.
Signature Generation Steps
- Build the string to sign by joining HTTP method, path, timestamp, nonce and request body with a pipe character (|): METHOD|PATH|TIMESTAMP|NONCE|BODY. The path has no leading slash and includes the query string, if any.
- Compute HMAC-SHA256 of that string, using your secret key (not the API Key ID) as the key
- Hex-encode the digest in lowercase and use it as is โ there is no prefix or algorithm label in front of it
- Send the hex digest in the X-Signature header, together with X-Api-Key-Id, X-Timestamp, X-Nonce and X-Signature-Version: 3
Manual Implementation Example
PHP Example
<?php
// Manual request signing in PHP (signature version 3)
$gateway = 'https://staging-gateway.epayse.com';
$apiKeyId = 'your_api_key_id'; // the ID shown next to the key in your dashboard
$secretKey = 'sk_test_...'; // shown once when you generate the key
$path = 'api/v1/transaction/create'; // no leading slash; add ?query if you send one
$body = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$timestamp = time();
$nonce = bin2hex(random_bytes(16)); // new value for every request
// METHOD|PATH|TIMESTAMP|NONCE|BODY
$stringToSign = implode('|', ['POST', $path, $timestamp, $nonce, $body]);
$signature = hash_hmac('sha256', $stringToSign, $secretKey);
$ch = curl_init($gateway . '/' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key-Id: ' . $apiKeyId,
'X-Signature: ' . $signature,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature-Version: 3',
],
]);
echo curl_exec($ch);
curl_close($ch);JavaScript/Node.js Example
// Manual request signing in JavaScript / Node.js (signature version 3)
import crypto from 'crypto';
const GATEWAY = 'https://staging-gateway.epayse.com';
const API_KEY_ID = 'your_api_key_id'; // the ID shown next to the key in your dashboard
const SECRET_KEY = 'sk_test_...'; // shown once when you generate the key
const path = 'api/v1/transaction/create'; // no leading slash; add ?query if you send one
const body = JSON.stringify(payload); // sign the EXACT string you send
const timestamp = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(16).toString('hex'); // new value for every request
// METHOD|PATH|TIMESTAMP|NONCE|BODY
const stringToSign = ['POST', path, timestamp, nonce, body].join('|');
const signature = crypto.createHmac('sha256', SECRET_KEY).update(stringToSign).digest('hex');
const response = await fetch(`${GATEWAY}/${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key-Id': API_KEY_ID,
'X-Signature': signature,
'X-Timestamp': String(timestamp),
'X-Nonce': nonce,
'X-Signature-Version': '3',
},
body,
});
console.log(await response.json());Important Security Notes
X-Signature-Version: 3 the nonce is part of the signed string, so a captured request cannot be replayed with a different nonce. api/v1/currencies in the string to sign, even though the URL you call is /api/v1/currencies) and includes the query string if there is one. Security Best Practices
Use HTTPS Only
Always use HTTPS for API requests. Never send API keys over unencrypted connections.
Rotate Keys Regularly
Rotate your API keys regularly and before they expire (the expiry date is shown under Settings โ API Keys), and immediately if you suspect they've been compromised.
Don't Expose Keys
Never commit API keys to version control or expose them in client-side code.
Don't Reuse Nonces
Always generate a unique nonce for each request to prevent replay attacks.