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'
});Popup Blocked
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/jsonResponse:
{
"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_tokenResponse:
{
"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 overlaypopup_closed- User closed popup windowuser_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 Message | When It Happens | Solution |
|---|---|---|
"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 exist | Check container selector |
SDK Auto-Handled Errors (Show Alerts)
| Scenario | SDK Behavior | User Action |
|---|---|---|
| Popup blocked | Shows alert, hides overlay | Enable popups, try again |
| Payment intent fails | Shows alert, re-enables button | Check token, try again |
HTTP Status Codes (Backend API)
| Status | Meaning | Common Cause |
|---|---|---|
| 400 | Bad Request | Invalid session data, amount mismatch |
| 401 | Unauthorized | Invalid API key or session token |
| 404 | Not Found | Transaction doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | API 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
- Wrap SDK calls in try-catch - Handle initialization and load errors gracefully
- Check error.message - SDK uses plain Error objects, not error codes
- Show user-friendly messages - Avoid showing technical errors to users
- Log errors to console - Keep technical details for debugging
- Verify all payments - Always verify transaction status on backend
- Handle all callback events - Both
payment_flow_completedandpayment_flow_cancelled - Provide retry options - Let users try again after failures
- 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_tokenExpected: 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
- Enable popup blocker in browser settings (Chrome → Settings → Privacy)
- Click payment button
- SDK shows alert: "Popup blocked. Please allow popups for this site."
- No error is thrown to your code
Test Expired Session Token
- Create a session token
- Wait over 1 hour (or use an old token)
- Click payment button
- SDK shows alert: "Payment failed: Payment intent creation failed: 401 - ..."
- Button is re-enabled for retry
Test Network Errors
During Payment Verification:
- Complete a payment successfully
- Open DevTools → Network tab
- Set throttling to "Offline"
- Your payment callback should trigger
- 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:
- Use your test API key (not live key)
- Create session and click payment button
- In payment popup, you'll see two buttons:
- Simulate Success - Returns
APPROVEDstatus - Simulate Failed - Returns
PENDINGstatus
- Simulate Success - Returns
- 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
- Click payment button
- Close the payment popup (click X or close window)
- Verify
payment_flow_cancelledevent is received - 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.
}
};