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

关于.NET Core C#中RevenueCat Webhook返回400 BadRequest且无法获取JSON响应的技术求助

Alright, let's figure out why you're getting that 400 Bad Request and fix your RevenueCat Webhook implementation in .NET Core. I'll walk through the common pitfalls and share a complete, tested example that should resolve this issue.

Common Issues Causing the 400 Error

  • Incorrect Signature Validation: Your current code compares the Authorization header directly to your webhook secret, which isn't how RevenueCat's verification works. RevenueCat uses an HMAC-SHA256 signature of the request body (sent in the X-RevenueCat-Signature header), not a direct secret match in Authorization.
  • Request Body Reading Problems: HttpContext.Request.Body is a forward-only stream. If any middleware reads it before your code, you'll get an empty string when you try to read it, leading to unexpected errors that trigger a 400.
  • Improper Response Handling: RevenueCat expects a 200 OK response to confirm receipt. If your code throws exceptions without proper handling, it might return a 400/500 instead of the required success status.

Fixed Implementation Example

First, add a helper class to handle HMAC signature validation correctly:

using System.Security.Cryptography;
using System.Text;

public static class RevenueCatWebhookValidator
{
    public static bool IsValidSignature(string requestBody, string webhookSecret, string signatureHeader)
    {
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSecret));
        var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
        var computedSignature = Convert.ToHexString(computedHash).ToLowerInvariant();
        
        // RevenueCat's signature uses lowercase hex—compare case-insensitively to avoid mismatches
        return computedSignature.Equals(signatureHeader.ToLowerInvariant());
    }
}

Then update your controller endpoint to handle requests safely:

[ApiController]
[Route("api/revenuecat/webhook")]
public class RevenueCatWebhookController : ControllerBase
{
    private readonly IConfiguration _config;
    private readonly ILogger<RevenueCatWebhookController> _logger;

    public RevenueCatWebhookController(IConfiguration config, ILogger<RevenueCatWebhookController> logger)
    {
        _config = config;
        _logger = logger;
    }

    [HttpPost]
    public async Task<IActionResult> ProcessWebhook()
    {
        var webhookSecret = _config[Constants.RevenueCatWebHookSecret];
        if (string.IsNullOrWhiteSpace(webhookSecret))
        {
            _logger.LogError("RevenueCat webhook secret is missing from configuration.");
            return StatusCode(StatusCodes.Status500InternalServerError);
        }

        // Copy request body to memory stream to avoid issues with forward-only streams
        string requestBody;
        using (var memoryStream = new MemoryStream())
        {
            await Request.Body.CopyToAsync(memoryStream);
            memoryStream.Position = 0;
            requestBody = await new StreamReader(memoryStream).ReadToEndAsync();
        }

        // Retrieve the correct signature header from RevenueCat
        var signature = Request.Headers["X-RevenueCat-Signature"].FirstOrDefault();
        if (string.IsNullOrWhiteSpace(signature))
        {
            _logger.LogWarning("Received RevenueCat webhook without X-RevenueCat-Signature header.");
            return Unauthorized();
        }

        // Validate the signature before processing the payload
        if (!RevenueCatWebhookValidator.IsValidSignature(requestBody, webhookSecret, signature))
        {
            _logger.LogWarning("Invalid signature on RevenueCat webhook request.");
            return Unauthorized();
        }

        // Process the payload (add your business logic here)
        _logger.LogInformation("Received valid RevenueCat webhook payload: {Payload}", requestBody);
        
        // Example: Deserialize the payload (define your own model classes for RevenueCat events)
        // var webhookEvent = System.Text.Json.JsonSerializer.Deserialize<RevenueCatWebhookEvent>(requestBody);

        // Always return 200 OK to confirm receipt—RevenueCat will retry on non-200 responses
        return Ok();
    }
}

Key Tips to Avoid Future Issues

  1. Use the Correct Header: RevenueCat sends signatures in X-RevenueCat-Signature, not Authorization. Your original code checked the wrong header, which would reject all valid requests.
  2. Read the Body Safely: Copying to a MemoryStream ensures you can access the payload even if other middleware has interacted with the request stream.
  3. Return 200 OK: This is required for RevenueCat to mark the webhook as successfully delivered. Failing to do so will trigger retries and show errors in their dashboard.
  4. Exception Handling: Ensure your global exception handler correctly maps errors to HTTP status codes. If your APIException isn't handled properly, it might return a 400 instead of a 401 for unauthorized requests.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:17:36