Testing Guide
Comprehensive guide to testing your EPaySe integration before going live.
Thorough testing is critical for a successful payment integration. Use our sandbox environment to test all scenarios before processing real payments.
Sandbox Environment
Accessing Sandbox
The sandbox environment provides full API functionality with test data
Sandbox Dashboard
https://sandbox-merchant.epayse.comAPI Base URL
https://sandbox-gateway.epayse.com/api/v1API Keys
Get sandbox API keys from the dashboard under Settings → API Keys
Sandbox Limitations
Test Scenarios
Test these critical scenarios to ensure your integration handles all payment flows correctly.
Successful Payment
Test successful payment flow with various card types
Failed Payment
Test declined cards and insufficient funds scenarios
3D Secure Flow
Test 3D Secure authentication process
Webhook Delivery
Test webhook notification reception and handling
Webhook Testing
Webhooks require a publicly accessible HTTPS URL. For local development, use ngrok to create a secure tunnel.
Use ngrok for Local Testing
<?php
// Use ngrok to expose your local server for webhook testing:
// 1. Start ngrok: ngrok http 8000
// 2. Copy the HTTPS URL (e.g., https://abc123.ngrok.io)
// 3. In your EPaySe merchant dashboard → Settings → Webhooks → add https://abc123.ngrok.io/webhooks/payment
// EPaySe sends webhooks to your configured dashboard endpoints, not per-request URLs.
// Your webhook endpoint
// POST /webhooks/payment
$rawBody = file_get_contents('php://input'); // the EXACT bytes EPaySe sent — never re-encode
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$secret = getenv('EPAYSE_WEBHOOK_SECRET'); // the whsec_... secret of this webhook endpoint
// Log for debugging
error_log('Webhook received: ' . $rawBody);
error_log('Signature: ' . $signature);
// Signed string = timestamp immediately followed by the raw body (no separator)
$expected = hash_hmac('sha256', $timestamp . $rawBody, $secret); // lowercase hex
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($rawBody, true);
error_log('Event type: ' . $event['type']); // e.g. payment.paid
error_log('Event data: ' . json_encode($event['data']));
// Handle event...
http_response_code(200); // acknowledge quickly, then process asynchronouslyWebhook Testing Tips
- Always verify webhook signatures before processing
- Return HTTP 200 status code within 30 seconds (the delivery timeout), then process the event asynchronously
- Handle webhook retries (EPaySe retries a failed delivery up to 5 times with the default schedule; each endpoint can override it)
- Log all webhook payloads for debugging
Debugging
Log the HTTP status and the JSON body of every API response to get detailed information about your requests.
<?php
// Enable detailed error logging
ini_set('display_errors', 1);
ini_set('log_errors', 1);
error_reporting(E_ALL);
// $url, $headers and $body are built exactly as on the Authentication page
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
error_log('Transport error: ' . curl_error($ch)); // DNS, TLS, timeout...
} else {
// Log the status and the JSON error the API returned (never log secret keys)
error_log('HTTP status: ' . $status);
error_log('Response body: ' . $response);
}
curl_close($ch);Common Errors
Unauthorized
Invalid API key or signature
Solution: Verify your API credentials and HMAC signature generation
IP Not Whitelisted
Response code IP_NOT_WHITELISTED — the calling IP is not on your merchant IP whitelist
Solution: Add the public IP of your server under Settings → IP Whitelist (enforced on staging and production, not in the sandbox)
Bad Request
Invalid or missing parameters
Solution: Check the API documentation for required parameters
Validation Error
Parameters failed validation
Solution: Review validation error messages in the response
Rate Limit
Too many requests
Solution: Implement exponential backoff and respect rate limits
API Testing Tools
Testing with Postman
Use Postman to test API endpoints without writing code. Import our collection to get started.
{
"info": {
"name": "EPaySe API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Create Transaction",
"request": {
"method": "POST",
"header": [
{
"key": "X-Api-Key-Id",
"value": "{{api_key_id}}"
},
{
"key": "X-Signature",
"value": "{{signature}}"
},
{
"key": "X-Timestamp",
"value": "{{timestamp}}"
},
{
"key": "X-Nonce",
"value": "{{nonce}}"
},
{
"key": "X-Signature-Version",
"value": "3"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"ref\": \"ORDER-1001\",\n \"amount\": 100.5,\n \"currency\": \"USD\",\n \"websiteUrl\": \"https://example.com\",\n \"redirectUrl\": \"https://example.com/payment/success\",\n \"cancelUrl\": \"https://example.com/payment/cancel\",\n \"description\": \"Premium subscription - monthly\",\n \"customerDetails\": {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"[email protected]\",\n \"country\": \"US\",\n \"ip\": \"203.0.113.10\",\n \"phoneCode\": \"US\",\n \"phoneNumber\": \"5551234567\"\n }\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/transaction/create",
"host": [
"{{base_url}}"
],
"path": [
"api",
"v1",
"transaction",
"create"
]
}
}
}
],
"variable": [
{
"key": "base_url",
"value": "https://sandbox-gateway.epayse.com"
}
]
}Testing with cURL
Use cURL commands to test API endpoints from the command line.
# Create a transaction against the sandbox with cURL (signature version 3)
SECRET_KEY="your_secret_key" # sk_test_... — used only to sign, never sent
API_KEY_ID="your_api_key_id" # shown next to the key in Settings → API Keys
BASE_URL="https://sandbox-gateway.epayse.com"
API_PATH="api/v1/transaction/create" # no leading slash
BODY='{"ref":"ORDER-1001","amount":100.5,"currency":"USD","websiteUrl":"https://example.com","redirectUrl":"https://example.com/payment/success","cancelUrl":"https://example.com/payment/cancel","description":"Premium subscription - monthly","customerDetails":{"firstName":"John","lastName":"Doe","email":"[email protected]","country":"US","ip":"203.0.113.10","phoneCode":"US","phoneNumber":"5551234567"}}'
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16) # new value for every request
# METHOD|PATH|TIMESTAMP|NONCE|BODY → HMAC-SHA256, lowercase hex, no prefix
SIGNATURE=$(printf '%s' "POST|$API_PATH|$TIMESTAMP|$NONCE|$BODY" \
| openssl dgst -sha256 -hmac "$SECRET_KEY" | awk '{print $NF}')
curl -X POST "$BASE_URL/$API_PATH" \
-H "Content-Type: application/json" \
-H "X-Api-Key-Id: $API_KEY_ID" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $NONCE" \
-H "X-Signature-Version: 3" \
-d "$BODY"Testing Best Practices
Test All Scenarios
Test successful payments, failures, refunds, and edge cases
Verify Webhooks
Always verify webhook signatures before processing
Handle Errors
Implement proper error handling and logging
Use Idempotency
Include idempotency keys to prevent duplicate transactions