如何在CodeIgniter4中实现与Node.js/Express一致的JSON.stringify以完成Delyva Webhook的SHA256验签?
JSON.stringify Behavior in CodeIgniter4 for Delyva Webhook Signature Validation Problem Analysis
Your core issue is that you're first parsing the JSON request body in CodeIgniter4 and then re-serializing it—a process that changes the string from the raw payload Delyva sends. While the Node.js example uses express.json() followed by JSON.stringify, this works only because Express's parsing combined with JSON.stringify happens to match the original payload (not a reliable best practice). PHP's json_encode has subtle differences from Node's JSON.stringify (like key ordering, whitespace handling, and empty value representation) that break the signature match.
Solution
To pass the SHA256 signature validation, you need to use the raw, unmodified POST request body string sent by Delyva to compute the HMAC, not a parsed-and-re-serialized version. Here's the corrected CodeIgniter4 code:
public function postTracking(): ResponseInterface { // Set your API secret $apiSecret = 'dx6ed8365ad507451595c151b6b61e3dbdcfbb987f'; // Get the raw POST request body (critical: skip parsing/serializing) $payload = file_get_contents('php://input'); // Alternatively, use CodeIgniter4's built-in method: // $payload = $this->request->getRawInput(); // Retrieve the signature from request headers $signature = $this->request->getHeaderLine('X-Delyvax-Hmac-Sha256'); // Calculate expected signature (matches Node.js logic exactly) $expected = base64_encode(hash_hmac('sha256', $payload, $apiSecret, true)); // Validate the signature if ($signature !== $expected) { return $this->respond([ 'status' => 'error', 'message' => 'Invalid signature', 'received_signature' => $signature, 'computed_signature' => $expected ], 401); } // Parse the payload only if you need to process the event data $eventData = json_decode($payload, true); return $this->respond([ 'status' => 'success', 'event_data' => $eventData ]); }
Key Details to Note
Use the Raw Request Body
Delyva computes the signature based on the exact string they send in the HTTP request body. Any parsing and re-serializing can alter whitespace, key order, or value formatting—even in unnoticeable ways—and break the signature match. Readingphp://inputor usinggetRawInput()ensures you work with the exact same string Delyva used.Match Node.js HMAC Logic
- Node.js's
crypto.createHmac('sha256', secret).update(payload).digest('base64')is equivalent to PHP'sbase64_encode(hash_hmac('sha256', $payload, $apiSecret, true)) - The
trueparameter inhash_hmactells PHP to return raw binary data, which we then encode to base64—matching Node.js'sdigest('base64')behavior perfectly.
- Node.js's
Why Parsing/Re-Serializing Fails
Even if you tried to forcejson_encodeto mimicJSON.stringify, you'd run into edge cases:- PHP 7.3 and below sorts object keys alphabetically, while Node.js preserves insertion order
- Floating-point precision handling can differ
- Whitespace and formatting (like trailing commas in arrays) are handled differently
Test the Fix
To verify, use your provided test data:
- Plug the exact payload string into
$payload - Compute
$expected—it should matchf8vncFdUbn+STSRVnS3oa6Rr+50tEGevZaYc7LrRncw= - If the values match, your signature logic is correct.
内容的提问来源于stack exchange,提问作者NM AA

