Skip to main content

Authentication

Set up secure OAuth2 authentication to connect to the Paysera Checkout API.

Paysera Checkout uses standard OAuth2 for secure API authentication. This guide covers how to obtain and use access tokens.

Overview​

All API requests (except public endpoints) require a Bearer token in the Authorization header:

Authorization: Bearer <access_token>

OAuth2 Client Credentials Flow​

Paysera Checkout uses the client credentials grant type, suitable for server-to-server communication.

Token Endpoint​

https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token

Obtaining an Access Token​

<?php

function getAccessToken(string $clientId, string $clientSecret): string
{
$ch = curl_init('https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token');

curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $clientId,
'client_secret' => $clientSecret,
]),
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode !== 200) {
throw new Exception('Failed to obtain access token: ' . $response);
}

$data = json_decode($response, true);
return $data['access_token'];
}

// Usage
$accessToken = getAccessToken('your-client-id', 'your-client-secret');
echo "Token obtained successfully!\n";

Response​

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "profile email"
}

Token Management​

Token Lifetime​

  • Access tokens expire after 3600 seconds (1 hour) — check the expires_in field of the token response.
  • There are no OAuth refresh tokens (refresh_expires_in: 0) — request a fresh token with the client credentials grant when the current one is close to expiry.

Token Caching​

Cache the access token and refresh it with a safety buffer (we recommend 300 seconds / 5 minutes before expiry) to avoid racing against expiration:

<?php

class TokenManager
{
private const TOKEN_REFRESH_BUFFER_SECONDS = 300; // 5 minutes before expiry
private $cache;

public function getToken(string $clientId, string $clientSecret): string
{
$cacheKey = 'paysera_token_' . md5($clientId);

// Check cache - refresh if within buffer window
$cached = $this->cache->get($cacheKey);
if ($cached && $cached['expires_at'] > time()) {
return $cached['access_token'];
}

// Request new token
$response = $this->requestToken($clientId, $clientSecret);

// Cache with 5-minute refresh buffer
$this->cache->set($cacheKey, [
'access_token' => $response['access_token'],
'expires_at' => time() + $response['expires_in'] - self::TOKEN_REFRESH_BUFFER_SECONDS,
]);

return $response['access_token'];
}

private function requestToken(string $clientId, string $clientSecret): array
{
$ch = curl_init('https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $clientId,
'client_secret' => $clientSecret,
]),
]);

$response = curl_exec($ch);
curl_close($ch);

return json_decode($response, true);
}
}
Refresh Window

With a 300-second (5-minute) buffer, tokens are refreshed when current_time >= expires_at - 300. Since tokens live for 3600 seconds (1 hour), a cached token is reused for approximately 55 minutes before you request a new one.

Making Authenticated Requests​

const response = await fetch(
'https://api.paysera.com/checkout-project/integration/v1/methods',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
}
);

if (!response.ok) {
throw new Error(`Request failed: ${await response.text()}`);
}

const data = await response.json();
console.log(data);

Rate Limits​

All integration API endpoints are rate limited to 50 requests per minute per client IP address.

When the limit is exceeded, the API returns HTTP 429 Too Many Requests. Every response includes rate limit headers:

HeaderDescription
X-RateLimit-LimitTotal requests allowed in the current time window
X-RateLimit-RemainingRequests remaining in the current time window
X-RateLimit-ResetSeconds until the rate limit window resets

Error Handling​

Invalid Credentials​

{
"error": "invalid_client",
"error_description": "Invalid client credentials"
}

Expired Token​

{
"error": "invalid_token",
"error_description": "Token is expired"
}

Best Practices​

  1. Cache your access token - Re-request on every call is wasteful; cache with a 300-second refresh buffer before expiry.
  2. Never expose credentials - Keep client secrets server-side only.
  3. Store credentials securely - Use environment variables or secrets managers.
  4. Handle auth errors - On 401 or invalid_token responses, discard the cached token, re-authenticate, and retry once.

Handling Authentication Errors​

On a 401 response (or invalid_token error), discard the cached access token, request a fresh one with the client credentials grant, and retry the original call once. Don't loop — if the second attempt also fails, surface the error so bad credentials don't silently retry forever.

Security Considerations​

  • Always use HTTPS for all API calls
  • Store credentials securely (e.g., environment variables, secrets manager)
  • Never log access tokens, client secrets, or request/response bodies that may contain them
  • Rotate credentials periodically