Xero Webhook验证失败:点击Intent to Receive无法返回200 OK
以下是针对性的修复步骤,帮你通过验证并正常接收Webhook:
确认Webhook密钥完全匹配
确保代码中XERO_WEBHOOK_KEY的值和Xero后台配置的Webhook密钥完全一致,包括大小写、特殊字符和空格,复制时避免多粘或少粘内容。建议用环境变量存储密钥,不要硬编码在代码里。修正请求头的获取方式(针对Rails等框架)
部分Web框架会将HTTP请求头转换为特定格式,比如Rails会把x-xero-signature转为HTTP_X_XERO_SIGNATURE。如果直接用小写头名获取,可能拿到nil导致验证失败,要改成对应框架的正确获取方式:signature = request.headers['HTTP_X_XERO_SIGNATURE'] # Rails环境下的正确写法修复请求体读取后的重置问题
request.body.read会一次性清空请求体内容,后续若需解析JSON会出错,同时要保证计算HMAC时的payload和Xero发送的完全一致,读取后要重置指针:payload = request.body.read request.body.rewind # 重置请求体指针,方便后续处理调整HBase64编码方式
Base64.encode64会自动添加换行符,而Xero的签名是无换行的Base64字符串,改用Base64.strict_encode64生成标准格式的编码,无需额外strip():calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', key, payload))优化验证逻辑的异常处理
把异常处理范围调整到合适位置,避免无关逻辑被错误捕获,同时增加签名存在性检查:def ensuring_security? key = ENV['XERO_WEBHOOK_KEY'] payload = request.body.read request.body.rewind calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', key, payload)) signature = request.headers['HTTP_X_XERO_SIGNATURE'] return false unless signature calculated_hmac == signature rescue OpenSSL::OpenSSLError, StandardError => e false end单独测试Intent请求场景
Xero的Intent to Receive请求发送的是空JSON payload({}),你可以本地模拟:用空字符串作为payload,用自己的密钥计算HMAC,对比Xero发送的签名是否一致,验证计算逻辑的正确性。
验证通过后,确保端点在验证成功时返回200 OK,失败时返回403 Forbidden,即可完成Intent验证,后续就能正常接收Contacts和Invoices的Webhook更新。
内容的提问来源于stack exchange,提问作者Muhammad Junaid Iqbal

