You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带签名验证的Webhook接收器搭建:计算哈希偶尔差一个字符

Troubleshooting Xero Webhook Intent to Receive Verification Failures (Single Character Hash Mismatch)

I’ve seen this exact edge case pop up with Xero Webhooks before—especially when the Webhook Key contains slashes like your mrj/yJ7pZKejaRrN61vAJB example. The single-character hash mismatch usually boils down to one of these common issues, let’s walk through fixes:

1. Fix Webhook Key Corruption

The / in your key is a red flag here. If your app is storing or fetching the key incorrectly (e.g., auto-URL-encoding it, or treating it as a filesystem path that mangles slashes), you’ll compute an HMAC with the wrong key entirely—leading to that subtle hash difference.

  • How to verify:
    • Copy-paste your Webhook Key directly from the Xero Developer Portal into your code temporarily. If the verification works, your stored key is corrupted.
    • Ensure your code isn’t running any string manipulation (like urldecode() or addslashes()) on the key before passing it to hash_hmac.

2. Ensure Payload Integrity

Even one byte difference in the payload will create a drastically different HMAC, which can show up as a single character change in the Base64 output.

  • Common payload issues:
    • Using $_POST instead of reading the raw payload: $_POST can modify data (e.g., stripping newlines or encoding special characters). Always use php://input to get the exact payload Xero sends.
    • Character encoding mismatches: Xero sends payloads in UTF-8. If your PHP environment is using a different encoding (like ISO-8859-1) to process the payload, you’ll generate an HMAC from the wrong byte sequence.
  • Fix code snippet:
    // Read raw payload correctly
    $payload = file_get_contents('php://input');
    // Confirm encoding is UTF-8
    if (mb_detect_encoding($payload) !== 'UTF-8') {
        $payload = mb_convert_encoding($payload, 'UTF-8');
    }
    

3. Check Base64 Consistency

Xero uses standard Base64 (with +, /, and = padding) for the X-Xero-Signature header. Your code’s base64_encode(hash_hmac(... true)) is correct, but watch out for:

  • Accidental URL-safe Base64 conversion: Some libraries auto-replace / with _ and + with -—this will break the hash match.
  • Unintended string manipulation: Don’t trim(), uppercase, or lowercase the computed hash before comparing it to Xero’s signature.
  • Quick check: Echo both your computed hash and Xero’s signature to see if the / in one is replaced with another character (like Y in your case) — this will confirm if encoding is the issue.

4. Test with a Minimal Script

Isolate the problem with a tiny test script that uses your exact key and a sample Intent to Receive payload from Xero. This will rule out environment or middleware issues:

$webHookKey = 'mrj/yJ7pZKejaRrN61vAJB...'; // Your full key here
$samplePayload = '{"events":[],"firstEventSequence":0,"lastEventSequence":0,"entropy":"your-test-entropy"}'; // Use Xero's exact test payload
$computedHash = base64_encode(hash_hmac('sha256', $samplePayload, $webHookKey, true));

// Compare directly
echo "Your Hash: " . $computedHash . "\n";
echo "Xero Hash: " . $_SERVER['HTTP_X_XERO_SIGNATURE'] . "\n";

If none of these work, check if there’s a reverse proxy or server middleware (like Cloudflare, Nginx) modifying request headers or payloads. Sometimes proxies alter special characters in headers without warning.

内容的提问来源于stack exchange,提问作者Healyhatman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:08:01