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

使用Guzzle发起POST请求持续返回400 Bad Request求助

排查并解决Guzzle POST请求400 Bad Request错误

核心错误点分析

你的请求返回400,主要是以下几个明显问题导致:

  • Host头不匹配:请求目标域名是my-uat.testing.my,但你在headers里硬编码了secure.example.com,服务器会判定请求的域名非法,直接返回400。
  • POST参数格式完全错误:application/x-www-form-urlencoded要求参数用&分隔键值对,你却用|分隔(比如TEST_RETURN_URL=xxx|TEST_RETURN_URL2=xxx),服务器无法解析这种格式。
  • 手动设置Content-Length易出错:strlen($sPost)计算的是字符串长度,若参数包含中文或特殊字符,字节数会和实际传输的不一致,Guzzle会自动处理这个头,不需要手动设置。
  • 参数未URL编码:如果参数值里有特殊字符(比如&、=、空格),会破坏表单格式,必须对每个参数值做URL编码。

修正后的代码

public function sendcs($chk_arr)
{
    $TRX_TEST_RETURN_URL = $chk_arr['TEST_RETURN_URL'];
    $TRX_TEST_RETURN_URL2 = $chk_arr['TEST_RETURN_URL2'];
    $TRX_TEST_TRANSACTION_ID = $chk_arr['TEST_TRANSACTION_ID'];
    $TRX_TRX_PRODUCT_ID = $chk_arr['TRX_PRODUCT_ID'];
    $TRX_CS_PAYMENT_METHOD = 'CS';
    $TRX_XYZ_TEST_ID = $chk_arr['XYZ_TEST_ID'];
    $TRX_TEST_TRANSACTION_TIME = $chk_arr['TEST_TRANSACTION_TIME'];

    // 生成签名原始字符串(确保和接口要求的拼接规则一致)
    $strParts = [
        trim($TRX_TEST_RETURN_URL),
        trim($TRX_TEST_RETURN_URL2),
        trim($TRX_TEST_TRANSACTION_ID),
        trim($TRX_TRX_PRODUCT_ID),
        trim($TRX_CS_PAYMENT_METHOD),
        trim($TRX_XYZ_TEST_ID),
        trim($TRX_TEST_TRANSACTION_TIME)
    ];
    $str = implode('|', $strParts);

    // 加载私钥并生成签名
    $file = "mchnts/" . env('WEB_XYZ_TEST_ID') . '/privatekey.pem';
    if (!Storage::disk('local')->exists($file)) {
        abort(403);
    }
    $certloc = Storage::disk('local')->get($file);
    $key = openssl_pkey_get_private($certloc);
    
    // 注意:确认接口要求的签名算法,例如SHA256,默认是SHA1
    openssl_sign($str, $signature, $key, OPENSSL_ALGO_SHA256);
    $checkSum = strtoupper(bin2hex($signature));

    // 构造正确的表单参数(用&分隔,参数值URL编码)
    $postData = [
        'TEST_RETURN_URL' => urlencode($TRX_TEST_RETURN_URL),
        'TEST_RETURN_URL2' => urlencode($TRX_TEST_RETURN_URL2),
        'TEST_TRANSACTION_ID' => urlencode($TRX_TEST_TRANSACTION_ID),
        'TRX_PRODUCT_ID' => urlencode($TRX_TRX_PRODUCT_ID),
        'XYZ_TRANS_TYPE' => urlencode($TRX_CS_PAYMENT_METHOD),
        'XYZ_TEST_ID' => urlencode($TRX_XYZ_TEST_ID),
        'TEST_TRANSACTION_TIME' => urlencode($TRX_TEST_TRANSACTION_TIME),
        'XYZ_TRX_SIGNATURE' => urlencode($checkSum)
    ];

    // 发起Guzzle请求
    $client = new Client();
    $url = "https://my-uat.testing.my/in";
    try {
        $response = $client->post($url, [
            'headers' => [
                // 去掉错误的Host头,Guzzle会自动从URL提取
                'Accept' => '*/*',
            ],
            'verify' => false, // 仅测试环境使用,生产环境必须开启SSL验证
            'form_params' => $postData, // 直接传数组,Guzzle自动处理编码和头
        ]);

        $body = $response->getBody()->getContents();
        return $body;
    } catch (\GuzzleHttp\Exception\ClientException $e) {
        // 捕获错误,打印完整响应方便排查
        echo $e->getResponse()->getBody()->getContents();
        throw $e;
    }
}

额外排查建议

  1. 确认签名算法:联系接口方确认要求的签名哈希算法(如SHA256),默认的openssl_sign用的是SHA1,若接口要求其他算法必须指定。
  2. 验证签名原始字符串:打印$str确认拼接的内容、分隔符是否和接口文档完全一致,多一个或少一个|都会导致签名无效。
  3. 开启错误日志:捕获Guzzle的异常时,打印完整的响应内容,很多服务器会在400响应里返回具体的错误原因(比如参数缺失、签名无效)。
  4. 测试签名有效性:可以用接口方提供的测试工具验证你的签名是否正确,排除签名导致的400。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:44:50