AlyaPay Documentation
Online Checkout

Error Handling

Handle errors and edge cases gracefully

Error Handling

Handle common errors to provide a better user experience.

The AlyaPay SDK throws standard JavaScript Error objects (not error codes). Check the error message to identify the issue.

SDK Errors

SDK Not Initialized

Called load() or startFlow() before calling init().

Error Message: "Call init() first"

try {
  AlyaPay.Payments.load({ container: '#button' });
} catch (error) {
  if (error.message === 'Call init() first') {
    console.error('SDK not initialized. Call init() before load().');
  }
}

Solution: Always call init() before other SDK methods.

// Correct order
AlyaPay.Payments.init({ session_token: SESSION_TOKEN, locale: 'en' });
AlyaPay.Payments.load({ container: '#alyapay-container' });

Container Not Found

Container element doesn't exist in the DOM.

Error Message: "Container not found"

try {
  AlyaPay.Payments.load({ container: '#wrong-id' });
} catch (error) {
  if (error.message === 'Container not found') {
    console.error('Container element does not exist in the page.');
  }
}

Solution: Verify the container exists before loading the SDK.

<!-- Make sure this element exists -->
<div id="alyapay-container"></div>

<script>
  // Load SDK after DOM is ready
  document.addEventListener('DOMContentLoaded', function() {
    AlyaPay.Payments.init({ session_token: SESSION_TOKEN, locale: 'en' });
    AlyaPay.Payments.load({ container: '#alyapay-container' });
  });
</script>

Missing Session Token

Called init() without providing a session token.

Error Message: "session_token is required"

try {
  AlyaPay.Payments.init({ locale: 'en' }); // Missing session_token
} catch (error) {
  if (error.message === 'session_token is required') {
    console.error('Session token is required to initialize SDK.');
  }
}

Solution: Always provide a session token from your backend.

// Correct initialization
AlyaPay.Payments.init({
  session_token: SESSION_TOKEN, // Required!
  locale: 'en'
});

Browser blocked the payment popup window.

The SDK handles this automatically by showing an alert to the user. You cannot catch this error.

SDK Behavior:

  • Popup fails to open
  • SDK shows alert: "Popup blocked. Please allow popups for this site."
  • Payment overlay is hidden
  • No error is thrown

User Action Required:

  • User must enable popups in browser settings
  • User can click the payment button again

Payment Intent Creation Failed

API error when creating payment intent (network issues, invalid token, etc.).

Error Message: "Payment intent creation failed: [status] - [details]"

The SDK handles this automatically by showing an alert. You cannot catch this error in your code.

SDK Behavior:

  • Request to create payment intent fails
  • SDK shows alert: "Payment failed: [error message]"
  • Button is re-enabled
  • No error is thrown to your code

Common Causes:

  • Invalid or expired session token
  • Network connection issues
  • API is down
  • Invalid session data

Solution: Check your session token is valid and not expired (tokens expire after 1 hour).

Backend API Errors

These errors occur when your backend calls AlyaPay's API. Handle them on your server.

Invalid API Key (401)

API key is wrong or expired.

Request:

POST /api/v1/public/sessions
X-API-Key: wrong_api_key_here
Content-Type: application/json

Response:

{
  "error": "Invalid API key"
}

Solution: Verify your API key in the merchant dashboard.


Invalid Session Data (400)

Session data is malformed or invalid (e.g., amount mismatch, missing fields).

Common Causes:

1. Amount Mismatch - Total doesn't match sum of items:

{
  "total": 999,
  "items": [
    { "id": "1", "name": "Product", "price": 500, "quantity": 2 }
  ]
}

Total = 999, but 500 × 2 = 1000 ✗ Error!

Solution: Ensure total equals the sum of all (price × quantity).

2. Missing Required Fields - total, currency, or items missing.

3. Invalid Currency - Currency not supported (only MAD currently).

4. Amount Out of Range - Amount must be between 500 MAD and 15,000 MAD.


Rate Limited (429)

Too many requests in short time.

Response:

{
  "error": "Rate limit exceeded",
  "retry_after": 60
}

Solution: Wait 60 seconds and retry. Implement exponential backoff in your backend.


Invalid Session Token (401)

Session token is invalid or expired when verifying transaction.

Request:

GET /api/v1/public/transactions/{transaction_id}/status
X-Session-Token: expired_or_invalid_token

Response:

{
  "error": "Invalid or expired session token"
}

Common Causes:

  • Session token expired (> 1 hour old)
  • Session token format is invalid
  • Session token doesn't match transaction

Solution: Session tokens expire after 1 hour. Create a new session if needed.

Frontend Payment Errors

These errors happen in your payment callback after the user interacts with the payment flow.

Payment Not Approved

Customer completed the payment flow, but payment was not approved (PENDING or CANCELED status).

window.PaymentCallback = async function(response) {
  if (response.event === 'payment_flow_completed') {
    if (!response.transaction_id) {
      alert('No transaction was created');
      return;
    }
    
    // Verify with backend
    const result = await fetch(`/api/verify-payment?transaction_id=${response.transaction_id}`);
    const data = await result.json();
    
    if (data.status === 'PENDING' || data.status === 'CANCELED') {
      alert('Payment was not approved. Please try another payment method.');
    } else if (data.status === 'APPROVED') {
      alert('Payment successful!');
      window.location.href = '/success';
    }
  }
};

What to do:

  • Show clear message to user
  • Offer alternative payment methods
  • Do NOT fulfill the order

Network Errors During Verification

Network request failed when verifying payment status with your backend.

window.PaymentCallback = async function(response) {
  if (response.event === 'payment_flow_completed') {
    if (!response.transaction_id) {
      alert('No transaction was created');
      return;
    }
    
    try {
      const result = await fetch(`/api/verify-payment?transaction_id=${response.transaction_id}`);
      
      if (!result.ok) {
        throw new Error('Verification request failed');
      }
      
      const data = await result.json();
      
      if (data.status === 'APPROVED') {
        window.location.href = '/success';
      } else {
        alert('Payment was not approved');
      }
      
  } catch (error) {
    console.error('Network error:', error);
      alert('Unable to verify payment. Please contact support with transaction ID: ' + response.transaction_id);
    }
  }
};

What to do:

  • Show user-friendly error message
  • Log transaction ID for support
  • Allow user to contact support with transaction ID

Payment Flow Cancelled

User cancelled payment or closed the popup.

window.PaymentCallback = function(response) {
  if (response.event === 'payment_flow_cancelled') {
    console.log('Payment cancelled:', response.reason);
    // Reasons: 'overlay_close', 'popup_closed', 'user_cancelled'
    
    // Show message to user
    alert('Payment cancelled. You can try again when ready.');
  }
};

Cancellation Reasons:

  • overlay_close - User clicked close button on overlay
  • popup_closed - User closed popup window
  • user_cancelled - User clicked cancel in payment flow

What to do:

  • Show friendly message
  • Keep payment button visible
  • Allow user to retry

Quick Reference

SDK Error Messages

Error MessageWhen It HappensSolution
"session_token is required"Missing session token in init()Provide session token
"Call init() first"Called load() or startFlow() before init()Call init() first
"Container not found"Container element doesn't existCheck container selector

SDK Auto-Handled Errors (Show Alerts)

ScenarioSDK BehaviorUser Action
Popup blockedShows alert, hides overlayEnable popups, try again
Payment intent failsShows alert, re-enables buttonCheck token, try again

HTTP Status Codes (Backend API)

StatusMeaningCommon Cause
400Bad RequestInvalid session data, amount mismatch
401UnauthorizedInvalid API key or session token
404Not FoundTransaction doesn't exist
429Too Many RequestsRate limit exceeded
500Server ErrorAPI issue, try again later

User-Friendly Error Messages

Show clear messages to users instead of technical errors:

// Handle SDK initialization errors
try {
  AlyaPay.Payments.init({ session_token: token, locale: 'en' });
  AlyaPay.Payments.load({ container: '#alyapay-container' });
} catch (error) {
  let userMessage = 'Unable to load payment system. Please refresh the page.';
  
  if (error.message === 'Container not found') {
    userMessage = 'Payment button could not be displayed. Please refresh the page.';
  } else if (error.message === 'session_token is required') {
    userMessage = 'Payment system configuration error. Please refresh the page.';
  }
  
  alert(userMessage);
  console.error('SDK Error:', error); // Log for debugging
}

// Handle payment verification errors
window.PaymentCallback = async function(response) {
  if (response.event === 'payment_flow_completed') {
    try {
      const result = await fetch(`/api/verify?tx=${response.transaction_id}`);
      const data = await result.json();
      
      if (data.status === 'APPROVED') {
        window.location.href = '/success';
      } else {
        alert('Payment was not approved. Please try another payment method.');
      }
    } catch (error) {
      alert('Unable to verify payment. Please contact support with your transaction ID.');
      console.error('Verification error:', error);
    }
  } else if (response.event === 'payment_flow_cancelled') {
    // Optional: show a message
    alert('Payment cancelled. You can try again when ready.');
  }
};

Best Practices:

  • Use simple, non-technical language
  • Provide actionable next steps
  • Log technical details to console for debugging
  • Show empathy in error messages

Best Practices

  1. Wrap SDK calls in try-catch - Handle initialization and load errors gracefully
  2. Check error.message - SDK uses plain Error objects, not error codes
  3. Show user-friendly messages - Avoid showing technical errors to users
  4. Log errors to console - Keep technical details for debugging
  5. Verify all payments - Always verify transaction status on backend
  6. Handle all callback events - Both payment_flow_completed and payment_flow_cancelled
  7. Provide retry options - Let users try again after failures
  8. Test error scenarios - Test all error cases before going live

Testing Errors

Test these scenarios during development:

Test SDK Initialization Errors

1. Missing Container:

<!-- DON'T create the container -->
<script>
  AlyaPay.Payments.init({ session_token: token, locale: 'en' });
  AlyaPay.Payments.load({ container: '#nonexistent' }); // Should throw error
</script>

Expected: Error message "Container not found"

2. Missing Session Token:

AlyaPay.Payments.init({ locale: 'en' }); // Missing session_token

Expected: Error message "session_token is required"

3. Load Before Init:

AlyaPay.Payments.load({ container: '#button' }); // Called before init()

Expected: Error message "Call init() first"


Test Popup Blocker

  1. Enable popup blocker in browser settings (Chrome → Settings → Privacy)
  2. Click payment button
  3. SDK shows alert: "Popup blocked. Please allow popups for this site."
  4. No error is thrown to your code

Test Expired Session Token

  1. Create a session token
  2. Wait over 1 hour (or use an old token)
  3. Click payment button
  4. SDK shows alert: "Payment failed: Payment intent creation failed: 401 - ..."
  5. Button is re-enabled for retry

Test Network Errors

During Payment Verification:

  1. Complete a payment successfully
  2. Open DevTools → Network tab
  3. Set throttling to "Offline"
  4. Your payment callback should trigger
  5. Verify your catch block handles the network error
window.PaymentCallback = async function(response) {
  if (response.event === 'payment_flow_completed') {
    try {
      const result = await fetch(`/api/verify?tx=${response.transaction_id}`);
      // ... handle response
    } catch (error) {
      console.log('✓ Network error caught successfully');
      alert('Unable to verify payment. Please contact support.');
    }
  }
};

Test Payment Failures

Use test mode to simulate payment outcomes:

  1. Use your test API key (not live key)
  2. Create session and click payment button
  3. In payment popup, you'll see two buttons:
    • Simulate Success - Returns APPROVED status
    • Simulate Failed - Returns PENDING status
  4. Verify your code handles both statuses correctly
window.PaymentCallback = async function(response) {
  if (response.event === 'payment_flow_completed') {
    const result = await fetch(`/api/verify?tx=${response.transaction_id}`);
    const data = await result.json();
    
    if (data.status === 'APPROVED') {
      console.log('✓ Success case handled');
    } else if (data.status === 'PENDING' || data.status === 'CANCELED') {
      console.log('✓ Failed payment handled');
    }
  }
};

Test Payment Cancellation

  1. Click payment button
  2. Close the payment popup (click X or close window)
  3. Verify payment_flow_cancelled event is received
  4. Check the cancellation reason
window.PaymentCallback = function(response) {
  if (response.event === 'payment_flow_cancelled') {
    console.log('✓ Cancellation handled');
    console.log('Reason:', response.reason); // 'popup_closed', 'overlay_close', etc.
  }
};