Skip to content

Troubleshooting Signature Verification

Common Issue: SIGNATURE_VERIFICATION_FAILED

If you're getting SIGNATURE_VERIFICATION_FAILED error when calling handleRedirectReturn, here are the most common causes and solutions:

1. Check API Secret

Problem: Using the wrong API secret or API key instead of secret.

Solution:

  • Ensure you're using the API Secret (starts with sk_), not the API Key (starts with pk_)
  • API secrets should be retrieved from your backend server, never stored in frontend code
  • Verify the secret matches your environment (sandbox vs live)
javascript
// ❌ WRONG - Using API key
const result = await kore.handleRedirectReturn(urlParams, 'pk_test_xxx');

// ✅ CORRECT - Using API secret
const apiSecret = await fetch('/api/get-secret').then(r => r.text());
const result = await kore.handleRedirectReturn(urlParams, apiSecret);

2. Enable Debug Mode (development only)

Use debug mode only when troubleshooting locally. Do not enable in production. Enable debug to see what payload is being generated:

javascript
const kore = new Kore({
  apiKey: 'pk_test_xxx',
  debug: true  // Enable debug logging
});

// Now when you call handleRedirectReturn, you'll see detailed logs
const result = await kore.handleRedirectReturn(urlParams, apiSecret);

This will log:

  • The parameters being used for signature
  • The payload being generated
  • The computed signatures (hex and base64)
  • The provided signature

3. Verify URL Parameters

Check that all required parameters are present in the URL:

javascript
const urlParams = new URLSearchParams(window.location.search);

// Log all parameters
console.log('URL Parameters:', Object.fromEntries(urlParams));

// Required parameters:
// - order_id
// - status
// - signature
// Optional but may be present:
// - payment_id
// - gateway
// - error_code
// - error_message

4. Signature Format

According to the specification, the signature format is:

  • HMAC-SHA256 algorithm
  • Hexadecimal string (lowercase)
  • Case-sensitive comparison

The SDK now implements the exact algorithm from the specification:

  1. Sort parameter keys alphabetically
  2. Create canonical string: key=value&key2=value2 (NO URL encoding)
  3. Compute HMAC-SHA256, output as hex lowercase
  4. Compare signatures (case-sensitive, constant-time)

5. Payload Construction

According to the specification, the payload format is:

  1. Filter out undefined, null, and empty string values
  2. Sort parameter keys alphabetically
  3. Canonicalize: key=value&key2=value2 (NO URL encoding - raw values)
  4. Example: gateway=checkout&order_id=123&status=captured

Important: The specification explicitly states NO URL encoding should be used in the canonical string. Values are used as-is from the URL parameters.

Best Practice: Verify signatures on your backend server instead of client-side.

Backend Endpoint Example

javascript
// Frontend: Send redirect params to backend
const urlParams = new URLSearchParams(window.location.search);
const result = await fetch('/api/verify-payment', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    payment_id: urlParams.get('payment_id'),
    order_id: urlParams.get('order_id'),
    status: urlParams.get('status'),
    gateway: urlParams.get('gateway'),
    signature: urlParams.get('signature')
  })
}).then(r => r.json());

if (result.success) {
  // Payment verified
} else {
  // Handle error
}

Backend Verification (Node.js Example)

javascript
const crypto = require('crypto');

function verifySignature(params, apiSecret) {
  const { signature, ...dataToSign } = params;
  
  // Filter empty values
  const filtered = {};
  for (const [key, value] of Object.entries(dataToSign)) {
    if (value !== undefined && value !== null && value !== '') {
      filtered[key] = value;
    }
  }
  
  // Create payload (adjust format to match backend)
  const payload = Object.keys(filtered)
    .sort()
    .map(key => `${key}=${encodeURIComponent(filtered[key])}`)
    .join('&');
  
  // Generate HMAC-SHA256
  const computed = crypto
    .createHmac('sha256', apiSecret)
    .update(payload)
    .digest('hex');
  
  // Compare (constant-time)
  return crypto.timingSafeEqual(
    Buffer.from(computed, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

7. Check Backend Documentation

Verify the exact signature format with your backend team:

  • What parameters are included in the signature?
  • What order are parameters in?
  • Are values URL-encoded or raw?
  • Is the signature hex or base64?
  • Is it case-sensitive?

8. Common Mistakes

Mistake 1: Including signature in payload

javascript
// ❌ WRONG - Don't include signature in the payload
const payload = `order_id=123&status=captured&signature=${signature}`;

// ✅ CORRECT - Signature is separate
const payload = `order_id=123&status=captured`;

Mistake 2: Wrong parameter order

javascript
// Backend might require specific order
// Check backend docs for exact order

Mistake 3: Extra whitespace

javascript
// ❌ WRONG - Extra spaces
const payload = `order_id = 123 & status = captured`;

// ✅ CORRECT - No spaces
const payload = `order_id=123&status=captured`;

Debug Checklist

  • [ ] API secret is correct (starts with sk_)
  • [ ] API secret matches environment (sandbox/live)
  • [ ] All required parameters are present
  • [ ] Signature parameter is included
  • [ ] Debug mode enabled to see logs
  • [ ] Check browser console for detailed error messages
  • [ ] Verify with backend team about signature format

Still Having Issues?

If you've tried all the above and still getting errors:

  1. Enable debug mode and share the logs with support
  2. Contact backend team to verify signature format
  3. Use server-side verification as a workaround
  4. Contact Kore support with:
    • Error message
    • Debug logs
    • Sample redirect URL (with sensitive data redacted)
    • Backend signature format documentation

Note: For production, always verify signatures server-side for security.