Backend Integration
API endpoints for creating sessions and verifying payments
Backend Integration
Your backend calls two AlyaPay API endpoints: one to create payment sessions, and one to verify transactions.
Important: Never expose your API key in frontend code. All API calls must be made from your backend.
API Base URLs
Use the appropriate base URL for your environment:
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox-api.alyapay.com |
| Production | https://api.alyapay.com |
Use development URL with test API keys during development, and production URL with live keys when going live.
Endpoint 1: Create Session
Create a payment session for checkout.
POST /api/v1/public/sessions
Headers
X-API-Key: YOUR_API_KEY_HERE
Content-Type: application/jsonRequest Body
{
"total": "1799.00",
"currency": "MAD",
"items": [
{
"id": "PRODUCT_123",
"name": "Wireless Headphones",
"price": 1799,
"quantity": 1
}
]
}Parameters
| Field | Type | Required | Description |
|---|---|---|---|
total | string | Yes | Total order amount |
currency | string | Yes | Currency code: MAD |
items | array | Yes | Array of order items |
items[].id | string | Yes | Unique product identifier |
items[].name | string | Yes | Product name |
items[].price | number | Yes | Unit price |
items[].quantity | number | Yes | Quantity |
Success Response (200 OK)
{
"session_token": "eyJraWQiOiJzZXNzaW9uLTIwMjUtMDEi...",
"expires_in": 3600,
"session_id": "sess_abc123xyz789"
}Response Fields:
session_token- Pass this to your frontend (valid for 1 hour)expires_in- Token lifetime in seconds (3600 = 1 hour)session_id- Session identifier for reference
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | INVALID_AMOUNT | Amount must be greater than 0 |
| 400 | INVALID_CURRENCY | Unsupported currency code |
| 400 | INVALID_ITEMS | Items array is missing or invalid |
| 400 | AMOUNT_MISMATCH | Total doesn't match sum of items |
| 401 | INVALID_API_KEY | API key is invalid or missing |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests (max 100 per 15 min) |
Endpoint 2: Verify Transaction
Verify transaction status after payment completes.
GET /api/v1/public/transactions/{transaction_id}/status
Headers
X-Session-Token: SESSION_TOKEN // from step 1URL Parameters
| Parameter | Description |
|---|---|
transaction_id | Transaction ID from payment callback |
Success Response (200 OK)
{
"status": "APPROVED",
"transaction_id": "c335bbf0-ae49-42e7-9a7e-c1a6888406dd",
"payment_intent_id": "8d17b9b9-aea9-4c7e-b753-8223938d7de6",
"amount": 1799.00,
"currency": "MAD",
"merchant_name": "Your Store",
"store_name": "Your Store",
"customer_phone": "212650296165",
"created_at": "12/10/25 16:28:21",
"updated_at": "12/10/25 16:28:21"
}Response Fields:
status- Transaction status (APPROVED,PENDING, orCANCELED)transaction_id- Unique transaction identifierpayment_intent_id- Payment intent identifieramount- Transaction amount (number)currency- Currency code (e.g.,MAD)merchant_name- Your merchant namestore_name- Your store namecustomer_phone- Customer's phone numbercreated_at- Transaction creation timestampupdated_at- Last update timestamp
Transaction Statuses
| Status | Description | What to Do |
|---|---|---|
APPROVED | Payment successful | ✅ Complete and ship order |
PENDING | Payment not approved | ❌ Don't fulfill order |
CANCELED | Payment not approved | ❌ Don't fulfill order |
Error Responses
| Status | Error | Description |
|---|---|---|
| 401 | INVALID_SESSION_TOKEN | Session token is invalid or expired |
| 404 | TRANSACTION_NOT_FOUND | Transaction doesn't exist |
Webhooks: Transaction Expired & Cancelled
When a transaction is cancelled by the customer or expires (e.g. after 30 minutes), AlyaPay sends a webhook to your backend so you can update order status in real time.
Webhooks require setup: You must provide us with a webhook URL so we can send you events.
Webhook Events
| Event | When It's Sent |
|---|---|
transaction.cancelled | Customer cancels the payment |
transaction.expired | Payment window expires (e.g. 30 minutes without payment) |
Payload Structure
Both webhooks use the same structure. Example for cancelled:
{
"event": "transaction.cancelled",
"timestamp": "2026-02-17T14:30:00.000Z",
"data": {
"id": "c335bbf0-ae49-42e7-9a7e-c1a6888406dd",
"vendorReference": "order_123",
"orderReference": "ORD-456",
"status": "CANCELED",
"amount": 1799,
"currency": "MAD",
"customerPhone": "212650296165",
"merchantName": "Your Store",
"storeName": "Your Store",
"createdAt": "2026-02-17T14:00:00.000Z"
}
}For expired, the only differences are:
"event": "transaction.expired""data.status": "EXPIRED""data.expiredAt"is present (when the transaction expired)
{
"event": "transaction.expired",
"timestamp": "2026-02-17T14:35:00.000Z",
"data": {
"id": "c335bbf0-ae49-42e7-9a7e-c1a6888406dd",
"vendorReference": "order_123",
"orderReference": "ORD-456",
"status": "EXPIRED",
"amount": 1799,
"currency": "MAD",
"customerPhone": "212650296165",
"merchantName": "Your Store",
"storeName": "Your Store",
"createdAt": "2026-02-17T14:00:00.000Z",
"expiredAt": "2026-02-17T14:30:00.000Z"
}
}Data Fields
| Field | Description |
|---|---|
event | transaction.cancelled or transaction.expired |
timestamp | When the webhook was sent (ISO 8601) |
data.id | Transaction ID |
data.vendorReference | Your order/transaction reference |
data.orderReference | Order reference |
data.status | CANCELED or EXPIRED |
data.amount | Transaction amount |
data.currency | Currency code (e.g. MAD) |
data.customerPhone | Customer phone |
data.merchantName | Merchant name |
data.storeName | Store name |
data.createdAt | Transaction creation time |
data.expiredAt | (Only in expired) When the transaction expired |
Handling Webhooks
-
Return 200 OK quickly
Respond with HTTP 200 as soon as you receive the webhook. Process the payload asynchronously. If you respond slowly or with an error, AlyaPay may retry. -
Update your order status
- On
transaction.cancelled: mark the order as cancelled (customer abandoned). - On
transaction.expired: mark the order as expired (payment window closed).
- On
-
Match by
vendorReferenceordata.id
Usedata.id(transaction ID) ordata.vendorReferenceto find the corresponding order in your system. -
Idempotency
The same webhook can be sent more than once. Make your handler idempotent: check if you have already processed this transaction before updating the order. -
Validate the webhook (optional)
If you configured a webhook secret when creating your API key, verify the request signature before processing. (See your API key setup for details.)
Transaction Statuses (including webhooks)
| Status | Description | What to Do |
|---|---|---|
| APPROVED | Payment successful | Complete and ship order |
| PENDING | Payment not yet completed | Do not fulfill order |
| CANCELED | Customer cancelled | Mark order cancelled; do not fulfill |
| EXPIRED | Payment window expired | Mark order expired; do not fulfill |
Security Best Practices
1. Store API Key Securely
# Use environment variables
ALYAPAY_API_KEY=sk_live_your_key_hereNever commit API keys to version control or expose them in frontend code.
2. Validate Input
Before creating a session:
- Verify total is positive
- Check items array is not empty
- Ensure total matches sum of items
- Validate currency is supported
3. Always Verify Transactions
Never trust frontend events alone. Always verify transaction status with the API before fulfilling orders.
❌ Bad: Frontend says "paid" → Ship order
✅ Good: Frontend event → Backend verifies → Ship order4. Use HTTPS
Always use HTTPS in production to protect API keys and session tokens in transit.
5. Handle Errors
Implement proper error handling:
- Log errors for debugging
- Return user-friendly messages
- Retry on transient failures
- Monitor for repeated failures
Need Help?
- See Redirect Flow for frontend integration
- Check Components for payment button options
- View Quick Start for a complete example
Email support: support@alyapay.com