EPaySe
Authentication

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.

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

  1. Log in to your EPaySe dashboard
  2. Navigate to Settings โ†’ API Keys
  3. Click "Generate New Key" and give the key a name
  4. Copy the Secret Key now (it is shown once) and note the API Key ID

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 key
  • X-Signature: HMAC-SHA256 signature
  • X-Timestamp: Unix timestamp
  • X-Nonce: Unique random string, new for every request
  • X-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.

Bash
Worked example (real values)
# 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)
           = 459983afd8b42d0ea5ece82606a782345b2b07c6caa0de44c38da32edcb71371

Example Request

HTTP
Example API Request with HMAC Headers
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

Bash
Complete cURL Request
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

Bash
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

  1. 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.
  2. Compute HMAC-SHA256 of that string, using your secret key (not the API Key ID) as the key
  3. Hex-encode the digest in lowercase and use it as is โ€” there is no prefix or algorithm label in front of it
  4. 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 HMAC Generation - PHP
<?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

JavaScript
Manual HMAC Generation - JavaScript
// 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

โฐ
Timestamp Validation: Requests with timestamps older than 5 minutes will be rejected to prevent replay attacks.
๐Ÿ”ข
Nonce: Each nonce can only be used once. Reusing a nonce will result in request rejection. With X-Signature-Version: 3 the nonce is part of the signed string, so a captured request cannot be replayed with a different nonce.
๐Ÿ”
Path Format: The path in signature generation has NO leading slash (use 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.
๐Ÿ“
Body Format: For GET requests, use an empty string for the body in signature generation. For POST/PUT requests, use the exact JSON string (no formatting).

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.

Next Steps

Now that you understand authentication, let's create your first transaction.

ยฉ 2026 EPaySeโ€ข Support โ€ข GitHub
v2.1.0โ€ขLast updated: 9/29/2026