{"record":{"id":"4ba66b9a55553e18","repo":"w7corp/easywechat","slug":"40005","errorCode":"-40005","errorMessage":"Invalid appId.","messagePattern":"Invalid appId\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Kernel/Encryptor.php","lineNumber":206,"sourceCode":"        if (! hash_equals($signature, $msgSignature)) {\n            throw new RuntimeException('Invalid Signature.', self::ERROR_INVALID_SIGNATURE);\n        }\n\n        $plaintext = Pkcs7::unpadding(\n            openssl_decrypt(\n                base64_decode($ciphertext, true) ?: '',\n                'aes-256-cbc',\n                $this->aesKey,\n                OPENSSL_NO_PADDING,\n                iv: substr($this->aesKey, 0, self::BLOCK_SIZE)\n            ) ?: '',\n            blockSize: strlen($this->aesKey)\n        );\n        $plaintext = substr($plaintext, self::BLOCK_SIZE);\n        $contentLength = (unpack('N', substr($plaintext, 0, 4)) ?: [])[1];\n\n        if ($this->receiveId && trim(substr($plaintext, $contentLength + 4)) !== $this->receiveId) {\n            throw new RuntimeException('Invalid appId.', self::ERROR_INVALID_APP_ID);\n        }\n\n        return substr($plaintext, 4, $contentLength);\n    }\n}\n","sourceCodeStart":188,"sourceCodeEnd":212,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Kernel/Encryptor.php#L188-L212","documentation":"After a successful AES decryption, Encryptor::decrypt() reads the 4-byte big-endian content length and requires the remaining tail of the plaintext to equal $this->receiveId (the app_id/corp_id passed to the constructor). A mismatch throws RuntimeException('Invalid appId.', -40005 ERROR_INVALID_APP_ID): the message was encrypted for a different WeChat account than the one you configured.","triggerScenarios":"An OpenPlatform component callback (authorized appid payload) being decrypted by an Encryptor built with the component app_id, or vice versa; app_id/corp_id from another environment (test vs prod); occasionally a wrong aes_key producing garbage plaintext with a bogus contentLength, making the tail comparison fail.","commonSituations":"OpenPlatform apps mixing component_id/app_id values, env config bleeding between deployments, one callback endpoint serving several official accounts but a single hardcoded app_id, wrong aes_key after key rotation.","solutions":["Verify the app_id (or corp_id / component app_id) used to build the Encryptor is the same account that WeChat encrypted the callback for","For OpenPlatform callbacks, make sure you use the authorizer's app_id context, not the open platform component app_id, when constructing the decryptor","Confirm the aes_key belongs to the same account — a wrong key decrypts to garbage and corrupts the length/tail parsing"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $message = $app->getServer()->handle($request);\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\RuntimeException $e) {\n    if ((int) $e->getCode() === \\EasyWeChat\\Kernel\\Encryptor::ERROR_INVALID_APP_ID) {\n        // callback arrived for a different account than this app is configured with\n        log_mismatched_callback($request);\n        return new Response('', 403);\n    }\n    throw $e;\n}","preventionTips":["Keep app_id, token and aes_key for one account together in a single config set; never mix values across sets","Give each WeChat account/env its own callback URL and handler","For OpenPlatform, route component vs authorizer callbacks to distinct handlers"],"tags":["php","easywechat","wechat-callback","appid","config","encryption"],"backgroundTag":"appid-mismatch","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}