🔔 Webhook Operations
Endpoint
POST /api/return/webhook/complete-cardstorage/{type}/{id}
Description
Webhook endpoint called when card storage operation is completed. This endpoint is automatically called by the system after 3D Secure verification or card registration process is completed and notifies that card information has been successfully saved.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | ✅ | Webhook type (e.g., "recurring", "paymentOrder") |
| id | uuid | ✅ | Operation UUID |
Headers
Content-Type: application/json
X-Webhook-Signature: {hash}Request: CompleteCardStorageWebhookRequest
{
"ownerId": "OWN-123456",
"cardId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"tenantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"hash": "a1b2c3d4e5f6...",
"hashFields": "ownerId,cardId,tenantId,timestamp",
"timestamp": 1708084800000
}Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| ownerId | string | ❌ | Card owner ID |
| cardId | uuid | ✅ | Saved card UUID |
| tenantId | uuid | ✅ | Tenant UUID |
| hash | string | ❌ | Security hash (HMAC-SHA256) |
| hashFields | string | ❌ | Fields used in hash calculation |
| timestamp | integer | ✅ | Unix timestamp (milliseconds) |
Response
Successful Request (200 OK)
{
"success": true,
"message": "Card storage completed successfully"
}Hash Verification
To ensure webhook security, the hash value of incoming requests must be verified.
Hash Calculation Algorithm
// JavaScript example
const crypto = require('crypto');
function calculateHash(data, secret) {
const fields = data.hashFields.split(',');
const values = fields.map(field => data[field]).join('|');
const hmac = crypto.createHmac('sha256', secret);
hmac.update(values);
return hmac.digest('hex');
}
// Verification
const receivedHash = request.body.hash;
const calculatedHash = calculateHash(request.body, YOUR_WEBHOOK_SECRET);
if (receivedHash === calculatedHash) {
// Hash is valid, continue processing
} else {
// Hash is invalid, reject request
}C# Example
using System.Security.Cryptography;
using System.Text;
public string CalculateHash(CompleteCardStorageWebhookRequest data, string secret)
{
var fields = data.HashFields.Split(',');
var values = string.Join("|", fields.Select(f =>
typeof(CompleteCardStorageWebhookRequest)
.GetProperty(f)
.GetValue(data)
.ToString()
));
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(values));
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
}
// Verification
var receivedHash = request.Hash;
var calculatedHash = CalculateHash(request, YOUR_WEBHOOK_SECRET);
if (receivedHash == calculatedHash)
{
// Hash is valid
}Webhook Endpoint Setup
To receive webhooks from the system, you need to configure your own endpoint.
Example Webhook Handler (Node.js/Express)
const express = require('express');
const app = express();
app.post('/webhooks/card-storage/:type/:id', express.json(), (req, res) => {
const { type, id } = req.params;
const webhookData = req.body;
// Hash verification
const calculatedHash = calculateHash(webhookData, process.env.WEBHOOK_SECRET);
if (webhookData.hash !== calculatedHash) {
return res.status(401).json({ error: 'Invalid hash' });
}
// Timestamp check (reject requests older than 5 minutes)
const now = Date.now();
const difference = now - webhookData.timestamp;
if (difference > 5 * 60 * 1000) {
return res.status(401).json({ error: 'Request expired' });
}
// Save to database
console.log(`Card storage completed for ${type}:${id}`);
console.log(`Owner: ${webhookData.ownerId}, Card: ${webhookData.cardId}`);
// Successful response
res.json({ success: true });
});Example Webhook Handler (C# / ASP.NET Core)
[ApiController]
[Route("webhooks")]
public class WebhookController : ControllerBase
{
private readonly string _webhookSecret;
public WebhookController(IConfiguration configuration)
{
_webhookSecret = configuration["WebhookSecret"];
}
[HttpPost("card-storage/{type}/{id}")]
public IActionResult CompleteCardStorage(
string type,
Guid id,
[FromBody] CompleteCardStorageWebhookRequest request)
{
// Hash verification
var calculatedHash = CalculateHash(request, _webhookSecret);
if (request.Hash != calculatedHash)
{
return Unauthorized(new { error = "Invalid hash" });
}
// Timestamp check
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var difference = now - request.Timestamp;
if (difference > 5 * 60 * 1000) // 5 minutes
{
return Unauthorized(new { error = "Request expired" });
}
// Save operation
_logger.LogInformation(
"Card storage completed for {Type}:{Id}, Owner: {OwnerId}, Card: {CardId}",
type, id, request.OwnerId, request.CardId
);
return Ok(new { success = true });
}
}Webhook URL Configuration
You can configure your webhook URL from the Klogs management panel:
- Log in to the Klogs Dashboard
- Go to Settings > Webhook Configuration
- Enter your endpoint in the Card Storage Webhook URL field
- Note the webhook secret key
- Verify by sending a test webhook
Webhook URL Format
https://yourdomain.com/webhooks/card-storage/{type}/{id}Security Recommendations
- ✅ Always perform hash verification
- ✅ Prevent replay attacks with timestamp checking
- ✅ Use HTTPS
- ✅ Store webhook secret securely (environment variables)
- ✅ Implement rate limiting
- ✅ Consider using IP whitelist
- ❌ Do not process without hash verification
- ❌ Do not hardcode webhook secret in code
Error Situations
| Situation | Description | Solution |
|---|---|---|
| 401 Unauthorized | Hash verification failed | Check secret key |
| 401 Unauthorized | Timestamp expired | Check server time |
| 404 Not Found | Webhook endpoint not found | Check URL configuration |
| 500 Server Error | Webhook processing error | Check logs |
Retry Mechanism
If the webhook fails (returns an HTTP code other than 2xx), the system automatically retries:
- 1st retry: Immediately
- 2nd retry: After 5 minutes
- 3rd retry: After 15 minutes
- 4th retry: After 1 hour
- 5th retry: After 6 hours
⚠️ Note: After 5 attempts, the webhook is marked as failed and requires manual intervention.
Webhook Logging
It is recommended to log webhook requests:
// Logging example
console.log({
timestamp: new Date().toISOString(),
type: type,
id: id,
ownerId: webhookData.ownerId,
cardId: webhookData.cardId,
success: true
});Testing
You can send a test webhook from the Klogs Dashboard to test your webhook:
# Manual test with cURL
curl -X POST https://yourdomain.com/webhooks/card-storage/recurring/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
-H "Content-Type: application/json" \
-d '{
"ownerId": "OWN-TEST",
"cardId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"tenantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"hash": "test-hash-value",
"hashFields": "ownerId,cardId,tenantId,timestamp",
"timestamp": 1708084800000
}'
