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
- JavaScript
- Python
- Kotlin
- Go
- C#
- cURL
<?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";
async function getAccessToken(clientId, clientSecret) {
const response = await fetch(
'https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
}),
}
);
if (!response.ok) {
throw new Error(`Failed to obtain access token: ${await response.text()}`);
}
const data = await response.json();
return data.access_token;
}
// Usage
const accessToken = await getAccessToken('your-client-id', 'your-client-secret');
console.log('Token obtained successfully!');
import requests
def get_access_token(client_id: str, client_secret: str) -> str:
response = requests.post(
'https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token',
data={
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
},
headers={'Content-Type': 'application/x-www-form-urlencoded'},
)
response.raise_for_status()
return response.json()['access_token']
# Usage
access_token = get_access_token('your-client-id', 'your-client-secret')
print('Token obtained successfully!')
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import kotlinx.serialization.json.*
fun getAccessToken(clientId: String, clientSecret: String): String {
val client = HttpClient.newHttpClient()
val formData = "grant_type=client_credentials&client_id=$clientId&client_secret=$clientSecret"
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(formData))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
throw Exception("Failed to obtain access token: ${response.body()}")
}
val json = Json.parseToJsonElement(response.body()).jsonObject
return json["access_token"]?.jsonPrimitive?.content
?: throw Exception("No access_token in response")
}
// Usage
val accessToken = getAccessToken("your-client-id", "your-client-secret")
println("Token obtained successfully!")
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
func getAccessToken(clientID, clientSecret string) (string, error) {
data := url.Values{}
data.Set("grant_type", "client_credentials")
data.Set("client_id", clientID)
data.Set("client_secret", clientSecret)
req, _ := http.NewRequest("POST",
"https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token",
strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return "", fmt.Errorf("failed to obtain access token: %s", string(body))
}
var result map[string]interface{}
json.Unmarshal(body, &result)
return result["access_token"].(string), nil
}
// Usage
func main() {
accessToken, _ := getAccessToken("your-client-id", "your-client-secret")
fmt.Println("Token obtained successfully!")
}
using System.Net.Http;
using System.Text.Json;
public class PayseraAuth
{
private readonly HttpClient _httpClient = new HttpClient();
public async Task<string> GetAccessTokenAsync(string clientId, string clientSecret)
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret
});
var response = await _httpClient.PostAsync(
"https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token",
content);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Failed to obtain access token: {responseBody}");
}
var json = JsonDocument.Parse(responseBody);
return json.RootElement.GetProperty("access_token").GetString()!;
}
}
// Usage
var auth = new PayseraAuth();
var accessToken = await auth.GetAccessTokenAsync("your-client-id", "your-client-secret");
Console.WriteLine("Token obtained successfully!");
curl -X POST https://api.paysera.com/auth/realms/Paysera/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
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_infield 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);
}
}
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​
- JavaScript
- Python
- Kotlin
- Go
- C#
- cURL
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);
import requests
response = requests.get(
'https://api.paysera.com/checkout-project/integration/v1/methods',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
},
)
response.raise_for_status()
data = response.json()
print(data)
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import kotlinx.serialization.json.*
val client = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.paysera.com/checkout-project/integration/v1/methods"))
.header("Authorization", "Bearer $accessToken")
.header("Content-Type", "application/json")
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
throw Exception("Request failed: ${response.body()}")
}
val data = Json.parseToJsonElement(response.body()).jsonObject
println(data)
req, _ := http.NewRequest("GET",
"https://api.paysera.com/checkout-project/integration/v1/methods", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
log.Fatalf("Request failed: %s", string(body))
}
var data map[string]interface{}
json.Unmarshal(body, &data)
fmt.Println(data)
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.GetAsync(
"https://api.paysera.com/checkout-project/integration/v1/methods");
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Request failed: {responseBody}");
}
var data = JsonDocument.Parse(responseBody);
Console.WriteLine(data.RootElement);
curl -X GET https://api.paysera.com/checkout-project/integration/v1/methods \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
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:
| Header | Description |
|---|---|
X-RateLimit-Limit | Total requests allowed in the current time window |
X-RateLimit-Remaining | Requests remaining in the current time window |
X-RateLimit-Reset | Seconds 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​
- Cache your access token - Re-request on every call is wasteful; cache with a 300-second refresh buffer before expiry.
- Never expose credentials - Keep client secrets server-side only.
- Store credentials securely - Use environment variables or secrets managers.
- Handle auth errors - On
401orinvalid_tokenresponses, 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