Setup HMAC Authentication in PHP
This guide walks you through implementing HMAC authentication for the Mekari POS API using PHP 8 and Composer.
Prerequisites
Install Dependencies
Ensure you have a composer.json file in your working directory. If not, create one:
{
"require": {}
}
Install three dependencies:
- GuzzleHTTP — HTTP client
- Carbon — datetime formatting for RFC 7231
- phpdotenv — environment variable loader
composer require guzzlehttp/guzzle:^7.0 nesbot/carbon vlucas/phpdotenv
Your composer.json should now include:
{
"require": {
"guzzlehttp/guzzle": "^7.0",
"nesbot/carbon": "^2.54",
"vlucas/phpdotenv": "^5.3"
}
}
Set Up Environment Variables
Create a .env file in your project root with your Mekari API credentials:
# .env
MEKARI_API_CLIENT_ID=YOUR_MEKARI_API_CLIENT_ID
MEKARI_API_CLIENT_SECRET=YOUR_MEKARI_CLIENT_SECRET
MEKARI_API_BASE_URL=https://api.mekari.com
MEKARI_POS_USER_ID=<A_USER_ID_FROM_GET_USERS>
Use https://sandbox-api.mekari.com as MEKARI_API_BASE_URL for the sandbox. MEKARI_POS_USER_ID is the staff user your requests act as (an id from GET /users).
Never commit credentials to version control. Add .env to your .gitignore file.
Generate the HMAC Signature
The signature is an HMAC-SHA256 hash of the request line and Date header, encoded in Base64. See HMAC Authentication for the full specification.
<?php
use Carbon\Carbon;
$datetime = Carbon::now()->toRfc7231String();
$request_line = "POST /v2/mekari-pos/v2/categories HTTP/1.1";
$payload = implode("\n", ["date: {$datetime}", $request_line]);
$digest = hash_hmac('sha256', $payload, $_ENV['MEKARI_API_CLIENT_SECRET'], true);
$signature = base64_encode($digest);
Complete Implementation
Create a file called main.php with the full implementation:
<?php
require 'vendor/autoload.php';
use Carbon\Carbon;
use Dotenv\Dotenv;
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\ClientException;
// Load .env file
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
/**
* Generate HMAC authentication headers.
*
* @param string $method HTTP method (GET, POST, PUT, DELETE)
* @param string $path Request path without the query string
* @return array<string, string>
*/
function generate_headers(string $method, string $path): array {
$datetime = Carbon::now()->toRfc7231String();
$request_line = "{$method} {$path} HTTP/1.1";
$payload = implode("\n", ["date: {$datetime}", $request_line]);
$digest = hash_hmac('sha256', $payload, $_ENV['MEKARI_API_CLIENT_SECRET'], true);
$signature = base64_encode($digest);
return [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Date' => $datetime,
'Authorization' => "hmac username=\"{$_ENV['MEKARI_API_CLIENT_ID']}\", algorithm=\"hmac-sha256\", headers=\"date request-line\", signature=\"{$signature}\""
];
}
// Configure the request
$client = new GuzzleHttp\Client([
'base_uri' => $_ENV['MEKARI_API_BASE_URL']
]);
$method = 'GET';
$path = '/v2/mekari-pos/v2/customers'; // sign the path only
$query = ['limit' => 50]; // any query works with the same signature
// Send the request
try {
$response = $client->request($method, $path, [
'headers' => generate_headers($method, $path),
'query' => $query
]);
echo $response->getBody();
} catch (ClientException $e) {
echo Psr7\Message::toString($e->getRequest());
echo Psr7\Message::toString($e->getResponse());
echo PHP_EOL;
}
Run the Script
php main.php
If the credentials are valid, you receive the API response. Otherwise, GuzzleHTTP prints the request and error response for debugging.
Adapt the $method, $path, and $query variables (and add a json body for write requests) to match the Mekari POS endpoint you are calling. Refer to the API reference for available endpoints. Write requests also need the X-User-ID and Idempotency-Key headers (see the header table).
Reference Repository
See the complete example code on GitHub: mekari-api-hmac-example-php