Skip to content

🔔 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

ParameterTypeRequiredDescription
typestringWebhook type (e.g., "recurring", "paymentOrder")
iduuidOperation UUID

Headers

http
Content-Type: application/json
X-Webhook-Signature: {hash}

Request: CompleteCardStorageWebhookRequest

json
{
  "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

ParameterTypeRequiredDescription
ownerIdstringCard owner ID
cardIduuidSaved card UUID
tenantIduuidTenant UUID
hashstringSecurity hash (HMAC-SHA256)
hashFieldsstringFields used in hash calculation
timestampintegerUnix timestamp (milliseconds)

Response

Successful Request (200 OK)

json
{
  "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
// 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

csharp
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)

javascript
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)

csharp
[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:

  1. Log in to the Klogs Dashboard
  2. Go to Settings > Webhook Configuration
  3. Enter your endpoint in the Card Storage Webhook URL field
  4. Note the webhook secret key
  5. 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

SituationDescriptionSolution
401 UnauthorizedHash verification failedCheck secret key
401 UnauthorizedTimestamp expiredCheck server time
404 Not FoundWebhook endpoint not foundCheck URL configuration
500 Server ErrorWebhook processing errorCheck 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:

javascript
// 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:

bash
# 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
  }'