Authentification
Every API request must be authenticated with a signature. This page explains exactly how.
Overview
GerminoPay uses HMAC-SHA256 to sign each request. Unlike simple API keys (which can be stolen and reused), HMAC signs the request content with your secret, ensuring:
- The request really comes from you
- The content has not been modified in transit
- Replay attacks are impossible (timestamp included)
API Keys
| Key | Format | Usage |
|---|---|---|
| Public Key | pk_live_xxxxxxxxxxxx |
Sent in every request via the X-Public-Key header |
| Secret Key | sk_live_xxxxxxxxxxxx |
Never sent. Used to compute the HMAC signature. |
Never expose your secret key
It must live only on your server (environment variable). Never put it in client-side JavaScript, mobile code, or Git repos.
Signature HMAC
To sign a request, concatenate these elements in this exact order:
- HTTP method in uppercase (
GET,POST, etc.) - Request path (without domain) — e.g.
/v1/paiement/initier - Raw request body (JSON, exactly as sent)
- Unix timestamp (seconds)
Then compute:
Pseudo-code
signature = HMAC_SHA256(
key = secret_key,
message = METHOD + PATH + BODY + TIMESTAMP
)
Important
The body must be exactly the same string that was sent. Even a single extra space changes the signature.
Required headers
| Header | Value | Required |
|---|---|---|
Content-Type |
application/json |
Yes |
X-Public-Key |
Your public key | Yes |
X-Timestamp |
Unix timestamp (seconds) | Yes |
X-Signature |
Hex HMAC-SHA256 signature | Yes |
Examples by language
<?php
function germinoSign(string $method, string $path, string $body, string $secret): array
{
$timestamp = time();
$message = $method . $path . $body . $timestamp;
$signature = hash_hmac("sha256", $message, $secret);
return [
"X-Timestamp" => $timestamp,
"X-Signature" => $signature,
];
}
// Utilisation
$body = json_encode(["montant" => 5000, "devise" => "XOF"]);
$headers = germinoSign("POST", "/v1/paiement/initier", $body, getenv("GERMINOPAY_SECRET"));
Anti-replay
Because the timestamp is part of the signature, an attacker who intercepts a request cannot reuse it later. Any request with a timestamp more than 5 minutes old is automatically rejected.
Clock synchronization
Your server clock must be synchronized (NTP). A drift of more than 5 minutes will invalidate all requests.
Best practices
- ✅ Store the secret in environment variables
- ✅ Rotate your keys regularly
- ✅ Use HTTPS exclusively
- ✅ Log every call for auditing
- ❌ Never commit your secret to Git
- ❌ Never expose the secret to the client