Skip to main content
Handle payment errors gracefully to give customers a smooth experience.

Error Response Format

All API errors follow this structure:
{
  "success": false,
  "error": "Human-readable error message",
  "details": ["Specific validation error 1", "Specific validation error 2"]
}

HTTP Status Codes

CodeMeaningWhat to Do
400Bad RequestCheck your request parameters
401UnauthorizedVerify your API keys
404Not FoundSession doesn’t exist
409ConflictSession already completed/cancelled
410GoneSession expired
500Server ErrorRetry the request

Two Types of Errors

Ozura Checkout handles errors differently based on whether the customer can fix them:

Fixable Errors (Customer Stays on Checkout)

These errors allow the customer to try again without leaving the checkout page:
  • Card declined
  • Insufficient funds
  • Invalid card number
  • Expired card
  • Wrong CVV
  • Address verification failed
What happens: An inline error message appears. The customer can correct their information and try again.

Unrecoverable Errors (Redirect to errorUrl)

These errors require redirecting the customer away from checkout:
  • Server/system errors
  • Authentication failures
  • Network connectivity issues
  • Payment processor unavailable
What happens: The customer is redirected to your errorUrl with error details in the URL.

Redirect Behavior Summary

SituationWhat HappensSession Status
Payment successfulRedirect to successUrlcompleted
Fixable error (card declined)Stays on checkout – inline errorpending
Unrecoverable errorRedirect to errorUrlfailed
Customer cancelsRedirect to cancelUrlcancelled
Session expiresOzura expiration page → cancelUrlexpired

Error URL Parameters

When an unrecoverable error occurs, the customer is redirected to your errorUrl:
https://yoursite.com/error?success=false&errorCode=SYSTEM_ERROR&error=Payment+service+unavailable&cancelUrl=https://yoursite.com/cart
ParameterDescriptionExample
successAlways falsefalse
errorCodeMachine-readable error typeSYSTEM_ERROR, AUTH_ERROR
errorHuman-readable messagePayment service unavailable
cancelUrlYour cancelUrl (for “return” navigation)https://yoursite.com/cart

Error Codes

CodeMeaning
SYSTEM_ERRORServer or connectivity issue
AUTH_ERRORPayment service authentication failed
SESSION_FAILEDSession marked as failed in database

Building Your Error Page

// Parse URL parameters
const params = new URLSearchParams(window.location.search);
const errorCode = params.get('errorCode');
const errorMessage = params.get('error');
const returnUrl = params.get('cancelUrl');

// Display to user
document.getElementById('error-message').textContent = 
  errorMessage || 'Something went wrong. Please try again.';

// Set up return button
if (returnUrl) {
  document.getElementById('return-btn').href = returnUrl;
}
Instead of showing a dead-end error page, redirect customers back to their cart with a popup message: Set your errorUrl to your cart page with a flag:
errorUrl: "https://yoursite.com/cart?showError=true"
Then on your cart page:
const params = new URLSearchParams(window.location.search);
if (params.get('showError')) {
  const errorMessage = params.get('error') || 'Something went wrong during checkout';
  showErrorPopup(errorMessage);
  
  // Clean up URL
  window.history.replaceState({}, '', '/cart');
}
This keeps customers in their shopping flow instead of showing a confusing error page.

Retry Logic for API Calls

If you get a 500 error, it’s safe to retry with exponential backoff:
async function createSessionWithRetry(data, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://checkout.ozura.io/api/sessions/create', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-API-KEY': process.env.VAULT_API_KEY,
          'X-OZURA-API-KEY': process.env.MERCHANT_API_KEY
        },
        body: JSON.stringify(data)
      });
      
      if (response.ok) {
        return await response.json();
      }
      
      // Don't retry client errors (4xx)
      if (response.status < 500) {
        throw new Error(`Request failed: ${response.status}`);
      }
      
      // Wait before retrying (exponential backoff)
      await new Promise(r => setTimeout(r, 1000 * attempt));
      
    } catch (error) {
      if (attempt === maxRetries) throw error;
    }
  }
}

Network Retry Protection

To prevent duplicate sessions from network issues, use an idempotency key:
{
  "merchantId": "your_merchant_id",
  "merchantName": "My Store",
  "amount": "25.00",
  "idempotencyKey": "order_12345"
}
If you send the same idempotencyKey twice, you’ll get back the original session instead of creating a duplicate.