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

CodeIgniter 4更换FedEx账号调用Rates API遇400错误及异常处理问题

关于FedEx Rates and Transit API调用的两个问题

我正在开发一个调用FedEx Rates and Transit API获取详情的程序,用开发者门户中与API项目关联的账号时API能正常工作,但换成其他账号后,API返回以下错误:

file_get_contents(https://apis.fedex.com/rate/v1/rates/quotes): Failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request

我在API文档和网络搜索里没找到足够相关信息,有两个问题:

  1. API是不是有限制,只允许调用关联账号的数据?还是需要调整请求参数?
  2. 我已经用try-catch块处理错误,但这个错误还是导致程序崩溃,有没有可行的处理方式?
public function callAPI()
{
    $this->accountNumber->set_value('11111111');

    //set shipper postalcode and country code 
    $this->shipper->set_postalCode(12345);
    $this->shipper->set_countryCode('US');
    $this->shipper->set_residential(false);

    //set recipient postalcode and country code 
    $this->recipient->set_postalCode(456789);
    $this->recipient->set_countryCode('US');
    $this->recipient->set_residential(true);

    //set weight and units 
    $this->weight->set_units('LB');
    $this->weight->set_value(26);

    $this->requestedShipment->set_shipper(['address' => $this->shipper]);
    $this->requestedShipment->set_recipient(['address' => $this->recipient]);
    $this->requestedShipment->set_requestedPackageLineItems(array(['weight' => $this->weight]));

    //set pickup type 
    //types: "CONTACT_FEDEX_TO_SCHEDULE", "DROPOFF_AT_FEDEX_LOCATION", "USE_SCHEDULED_PICKUP"
    $this->requestedShipment->set_pickupType("USE_SCHEDULED_PICKUP");

    // set type of rate to be returned 
    //types: "LIST", "INCENTIVE", "ACCOUNT", "PREFERRED" (see documentation for explanations)
    $this->requestedShipment->set_rateRequestType(['ACCOUNT']);

    //set shipment date 
    $this->requestedShipment->set_shipDateStamp("2023-08-17");

    $this->rateRequestControlParameters->set_return_tranist(true);
    $accountNumberJSON = json_encode($this->accountNumber);
    $requestedShipmentJSON = json_encode($this->requestedShipment);

    $url = "https://apis.fedex.com/oauth/token";

    //client id is api key and client secret is secret key
    $data = [
        'grant_type' => 'client_credentials',
        'client_id' => 'my_clinet_id',
        'client_secret' => 'my_clinet_secret'
    ];

    $options = array(
        'http' => array(
            'header' => "Content-type: application/x-www-form-urlencoded",
            'method' => 'POST',
            'content' => http_build_query($data)
        )
    );

    $context = stream_context_create($options);
    $resp = file_get_contents($url, false, $context);
    $response_data = json_decode($resp);
    $access_token = $response_data->access_token;
    
    //calls function if token is working (token changes every minute)
    if (isset($access_token)) {
        echo $this->get_rates($access_token, $this->accountNumber, $this->requestedShipment, $this->rateRequestControlParameters); 
    }
}

function get_rates($token, $acct, $ship, $rateReqCtrlPara)
{
    $url = "https://apis.fedex.com/rate/v1/rates/quotes";

    $data = ['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ];
    $options = array(
        'http' => array(
            'header' => [
                "Content-type: application/json",
                "Authorization: Bearer {$token}",
                "X-locale: en_US"
            ],
            'method' => 'POST',
            'content' => json_encode($data)
        )
    );

    $context = stream_context_create($options);
    try{ 
            $resp_data = file_get_contents($url, false, $context); //error here
    }
    catch (exception $e) { echo $e; }
  return $resp_data;
}

问题1解答:API限制与请求参数调整

FedEx的API确实存在账号关联限制:

  • 开发者门户创建的API项目(对应client_id和client_secret)默认仅关联一个FedEx账号,使用其他未关联账号调用时会触发权限校验失败,返回400错误。
  • 若要使用多个账号,需在FedEx开发者门户中为当前API项目添加额外关联账号,或确保目标账号已被授权通过当前API密钥访问。

同时需检查以下请求参数关键点:

  1. 账号结构:确认accountNumber是否包含countryCode字段,部分场景下API要求完整账号信息而非仅数值。
  2. 取件类型匹配:若新账号未开通USE_SCHEDULED_PICKUP服务,该参数会导致请求失败,可先替换为DROPOFF_AT_FEDEX_LOCATION测试。
  3. 费率类型权限:rateRequestType设为ACCOUNT时,目标账号需具备对应协议费率权限,否则会触发参数无效。

问题2解答:try-catch无法捕获错误的处理方式

file_get_contents在HTTP请求失败时默认仅返回false并触发PHP警告,不会主动抛出异常,因此你的try-catch块无法捕获该错误。推荐两种解决方式:

方式1:将警告转换为可捕获异常

在调用file_get_contents前,临时修改错误处理逻辑,将警告转为异常:

function get_rates($token, $acct, $ship, $rateReqCtrlPara)
{
    $url = "https://apis.fedex.com/rate/v1/rates/quotes";

    $data = ['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ];
    $options = array(
        'http' => array(
            'header' => [
                "Content-type: application/json",
                "Authorization: Bearer {$token}",
                "X-locale: en_US"
            ],
            'method' => 'POST',
            'content' => json_encode($data),
            'ignore_errors' => true // 避免直接抛出警告
        )
    );

    $context = stream_context_create($options);
    
    // 临时将警告转为异常
    set_error_handler(function($errno, $errstr) {
        throw new Exception($errstr);
    });

    try{ 
        $resp_data = file_get_contents($url, false, $context);
        // 检查HTTP状态码
        foreach($http_response_header ?? [] as $header) {
            if(strpos($header, 'HTTP/1.1') === 0) {
                $status = substr($header, 9, 3);
                if($status != 200) {
                    throw new Exception("API请求失败,状态码:{$status},响应内容:{$resp_data}");
                }
            }
        }
    }
    catch (Exception $e) { 
        echo $e->getMessage();
        $resp_data = null;
    } finally {
        restore_error_handler(); // 恢复默认错误处理
    }
    return $resp_data;
}

方式2:改用cURL替代file_get_contents

cURL对HTTP错误的处理更灵活,无需依赖错误转换,直接捕获请求异常:

function get_rates($token, $acct, $ship, $rateReqCtrlPara)
{
    $url = "https://apis.fedex.com/rate/v1/rates/quotes";
    $data = json_encode(['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ]);
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Content-type: application/json",
        "Authorization: Bearer {$token}",
        "X-locale: en_US"
    ]);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    
    $resp_data = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if($http_code != 200) {
        $error = curl_error($ch);
        echo "请求失败:{$error},状态码:{$http_code},响应:{$resp_data}";
        $resp_data = null;
    }
    
    curl_close($ch);
    return $resp_data;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 03:24:49