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

FedEx API获取运费报价返回404错误排查求助

集成FedEx API获取运费报价时持续收到404错误求助

我正在Node.js应用中集成FedEx API以获取运费报价,已遵循官方文档操作,但持续收到404错误,错误信息为:

"The resource you requested is no longer available."

错误响应详情:

FedEx API request failed with status: 404
- Response body: {"transactionId": "c2dda9ea-61fa-411a-9e39-e70239ee9370","errors":[{"code":"NOT.FOUND.ERROR","message":"The resource you requested is no longer available. Please modify your request and try again."}]}

我已对照FedEx API文档验证了端点URL和请求负载,以下是index.js中的相关代码:

const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const path = require('path');

const app = express();
app.use(bodyParser.json());

const FEDEX_OAUTH_URL = 'https://apis.fedex.com/oauth/token';
const FEDEX_API_URL = 'https://apis.fedex.com/rate/v1/rates/quote';
const FEDEX_API_KEY = 'your_api_key';
const FEDEX_SECRET = 'your_api_secret';
const FEDEX_ACCOUNT_NUMBER = 'your_account_number';

async function getFedexToken() {
  const response = await fetch(FEDEX_OAUTH_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: FEDEX_API_KEY,
      client_secret: FEDEX_SECRET
    })
  });

  const responseBody = await response.text();
  if (!response.ok) {
    throw new Error(`Failed to get FedEx OAuth token: ${response.statusText}. Response Body: ${responseBody}`);
  }

  const data = JSON.parse(responseBody);
  return data.access_token;
}

app.post('/compare', async (req, res) => {
  const { originPostcode, originCountry, destinationPostcode, destinationCountry, weight } = req.body;

  const fedexRequest = {
    accountNumber: {
      value: FEDEX_ACCOUNT_NUMBER
    },
    requestedShipment: {
      shipper: {
        address: {
          postalCode: originPostcode,
          countryCode: originCountry.toUpperCase()
        }
      },
      recipient: {
        address: {
          postalCode: destinationPostcode,
          countryCode: destinationCountry.toUpperCase()
        }
      },
      shipDateStamp: new Date().toISOString().split('T')[0], // Today's date in YYYY-MM-DD format
      pickupType: 'DROPOFF_AT_FEDEX_LOCATION',
      serviceType: 'INTERNATIONAL_PRIORITY',
      rateRequestType: ['LIST', 'ACCOUNT'],
      customsClearanceDetail: {
        dutiesPayment: {
          paymentType: 'SENDER',
          payor: {
            responsibleParty: null
          }
        },
        commodities: [
          {
            description: 'Camera',
            quantity: 1,
            quantityUnits: 'PCS',
            weight: {
              units: 'KG',
              value: weight
            },
            customsValue: {
              amount: 100,
              currency: 'USD'
            }
          }
        ]
      },
      requestedPackageLineItems: [
        {
          weight: {
            units: 'KG',
            value: weight
          }
        }
      ]
    }
  };

  console.log('FedEx Request Payload:', JSON.stringify(fedexRequest, null, 2));

  try {
    const token = await getFedexToken();
    const response = await fetch(FEDEX_API_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-locale': 'en_US',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify(fedexRequest)
    });

    const responseBody = await response.text();
    if (!response.ok) {
      console.error('FedEx API request failed with status:', response.status);
      console.error('Response body:', responseBody); // Log the detailed error response
      throw new Error(`FedEx API request failed with status ${response.status}: ${responseBody}`);
    }

    const fedexData = JSON.parse(responseBody);
    console.log('FedEx Response Data:', JSON.stringify(fedexData, null, 2));

    if (!fedexData.output || !fedexData.output.rateReplyDetails) {
      throw new Error(`Unexpected FedEx API response structure: ${JSON.stringify(fedexData)}`);
    }

    const results = fedexData.output.rateReplyDetails.map(detail => ({
      courier: 'FedEx',
      cost: detail.ratedShipmentDetails[0].totalNetChargeWithDutiesAndTaxes.amount,
      service: detail.serviceType,
      transitTime: detail.commitDetails[0].commitTimestamp
    }));

    res.json({ results });
  } catch (error) {
    console.error('Error fetching data from FedEx API:', error);
    res.status(500).json({ error: error.message });
  }
});

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

const PORT = process.env.PORT || 3001;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
}).on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} is already in use.`);
  } else {
    console.error(err);
  }
});

我预期能获取对应货运详情的FedEx报价,但始终收到404错误,已验证端点和请求结构,仍不清楚原因。现求助:

  1. 获取报价的端点URL是否正确?
  2. 请求负载结构是否符合FedEx API要求?
  3. 是否存在我遗漏的必要请求头或参数?

问题排查与解决方案

1. 端点URL验证

你当前使用的https://apis.fedex.com/rate/v1/rates/quote是旧版Rate API的废弃端点,FedEx最新Rate API v1的正确端点为https://apis.fedex.com/rate/v1/rates/quotes(末尾为复数quotes)。404错误的核心原因就是端点路径错误,旧资源已被移除。

如果是测试环境,需替换为沙箱端点:https://apis-sandbox.fedex.com/rate/v1/rates/quotes,同时OAuth令牌也要从沙箱环境获取(https://apis-sandbox.fedex.com/oauth/token),禁止混用生产与沙箱环境的配置。

2. 请求负载结构修正

你的请求负载存在3处不符合FedEx API要求的问题:

  • customsClearanceDetail.payor.responsibleParty不能为null:即使由发货方支付关税,也需填写该字段的基本信息,至少包含账号或地址,示例:
    "payor": {
      "responsibleParty": {
        "accountNumber": {
          "value": FEDEX_ACCOUNT_NUMBER
        }
      }
    }
    
  • shipper缺少contact信息:FedEx API强制要求发货方地址关联联系人信息,至少包含姓名或电话,示例:
    "shipper": {
      "address": {
        "postalCode": originPostcode,
        "countryCode": originCountry.toUpperCase()
      },
      "contact": {
        "personName": "Shipper Name",
        "phoneNumber": "1234567890"
      }
    }
    
  • requestedPackageLineItems缺少包装类型:需添加packagingType字段,可使用官方指定代码如"packagingType": "YOUR_PACKAGING"。

3. 必要请求头与参数检查

  • 补充X-Customer-Transaction-Id请求头:FedEx API要求该字段用于请求追踪,可生成一个UUID作为值,示例:
    headers: {
      'Content-Type': 'application/json',
      'X-locale': 'en_US',
      'Authorization': `Bearer ${token}`,
      'X-Customer-Transaction-Id': '随机UUID字符串'
    }
    
  • 验证API密钥权限:登录FedEx开发者控制台,确认你的API密钥已开通Rate API的访问权限,部分测试密钥可能仅开放特定API权限。
  • 确认账号有效性:确保FEDEX_ACCOUNT_NUMBER是已激活的有效账号,且与API密钥关联。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:32:04