Skip to main content

Error Handling

Best practices for handling errors and edge cases in your integration.

This guide covers error handling strategies for Paysera Checkout integrations.

Error Response Format​

All API errors return a consistent JSON structure:

{
"error": "error_code",
"message": "Human-readable description",
"details": [
{
"field": "field_name",
"message": "Field-specific error message"
}
]
}

HTTP Status Codes​

CodeMeaningRetry
200SuccessN/A
201CreatedN/A
400Bad RequestNo
401UnauthorizedMaybe*
403ForbiddenNo
404Not FoundNo
409ConflictNo
422Validation ErrorNo
429Rate LimitedYes (with backoff)
500Server ErrorYes (with backoff)
502Bad GatewayYes (with backoff)
503Service UnavailableYes (with backoff)

*Retry after refreshing access token

Common Errors​

Authentication Errors​

Invalid Credentials (401)​

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

Solution: Verify client_id and client_secret are correct.

Expired Token (401)​

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

Solution: Request a new access token.

Invalid Token (401)​

{
"error": "invalid_token",
"message": "Token validation failed"
}

Solution: Check token format and ensure you're using the correct environment.

Validation Errors (422)​

{
"error": "validation_error",
"message": "Invalid request parameters",
"details": [
{
"field": "purchase.amount.amount",
"message": "Amount must be greater than 0"
},
{
"field": "purchase.amount.currency",
"message": "Currency is required"
}
]
}

Solution: Fix the indicated fields and retry.

Not Found (404)​

{
"error": "not_found",
"message": "Order not found"
}

Solution: Verify the resource ID is correct.

Conflict (409)​

{
"error": "conflict",
"message": "Order with this reference already exists"
}

Solution: Use a unique reference or handle the existing order.

Rate Limited (429)​

{
"error": "rate_limited",
"message": "Too many requests"
}

Response Headers:

Retry-After: 60
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705320000

Solution: Implement exponential backoff and respect Retry-After header.

Error Handling Code Examples​

<?php

class PayseraClient
{
private string $baseUrl;
private ?string $accessToken = null;

public function request(string $method, string $endpoint, array $data = []): array
{
$maxRetries = 3;
$attempt = 0;

while ($attempt < $maxRetries) {
try {
return $this->doRequest($method, $endpoint, $data);
} catch (RateLimitException $e) {
$retryAfter = $e->getRetryAfter();
sleep($retryAfter);
$attempt++;
} catch (TokenExpiredException $e) {
$this->refreshToken();
$attempt++;
} catch (ServerException $e) {
// Exponential backoff
sleep(pow(2, $attempt));
$attempt++;
}
}

throw new MaxRetriesExceededException();
}

private function doRequest(string $method, string $endpoint, array $data): array
{
$ch = curl_init($this->baseUrl . $endpoint);

curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->accessToken,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $method !== 'GET' ? json_encode($data) : null,
]);

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

$body = json_decode($response, true);

return match (true) {
$httpCode >= 200 && $httpCode < 300 => $body,
$httpCode === 401 => throw new TokenExpiredException($body['message']),
$httpCode === 422 => throw new ValidationException($body['message'], $body['details']),
$httpCode === 429 => throw new RateLimitException($body['message']),
$httpCode >= 500 => throw new ServerException($body['message']),
default => throw new ApiException($body['message'], $httpCode),
};
}
}

// Usage with error handling
try {
$order = $client->request('POST', '/merchant-order/integration/v1/orders', [
'project_id' => $projectId,
'purchase' => ['reference' => 'ORDER-123', 'amount' => '2500', 'currency' => 'EUR'],
]);
} catch (ValidationException $e) {
foreach ($e->getDetails() as $detail) {
echo "Error in {$detail['field']}: {$detail['message']}\n";
}
} catch (ApiException $e) {
error_log("API error: " . $e->getMessage());
}

Troubleshooting​

Common Issues​

SymptomPossible CauseSolution
401 on all requestsWrong credentialsVerify client_id/secret
401 after workingToken expiredImplement token refresh
422 on valid dataWrong field formatCheck API documentation
429 frequentlyToo many requestsImplement rate limiting
500 errorsServer issueRetry with backoff

Debug Checklist​

  1. Check credentials - Correct client_id and client_secret?
  2. Check URL - Using correct API base URL?
  3. Check token - Is token expired? Is it included in header?
  4. Check request body - Is JSON valid? Are all required fields present?
  5. Check amount format - Is it a string in minor units ("1000" for €10.00) not a decimal ("10.00")?
  6. Check rate limits - Are you under the limit?