首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >外卖系统接入配送API方案:如何实现订单与配送服务高效对接

外卖系统接入配送API方案:如何实现订单与配送服务高效对接

原创
作者头像
万岳教育Lili
发布2026-09-02 17:25:11
发布2026-09-02 17:25:11
60
举报

随着外卖业务不断发展,平台除了需要具备用户下单、商家接单、订单管理等基础能力,还需要解决一个核心问题:订单如何快速、高效地进入配送环节。

对于自建外卖平台、同城配送平台以及连锁餐饮系统来说,直接自建完整的配送体系往往需要投入较多的人力和技术成本。因此,越来越多外卖系统会通过接入第三方配送API,将订单系统与配送服务进行连接,实现订单自动创建配送任务、配送状态实时同步以及骑手信息回传。

本文将从系统架构、接口设计、核心流程以及代码实现几个方面,介绍外卖系统接入配送API的一套完整方案。

外卖系统
外卖系统

一、什么是外卖系统配送API

配送API可以理解为外卖系统与配送服务之间的数据桥梁。

用户完成下单后,外卖系统产生订单数据,然后通过API将订单信息提交给配送服务。配送服务根据配送地址、订单重量、配送距离等信息创建配送任务,并将配送状态返回给外卖系统。

基本流程可以简化为:

代码语言:javascript
复制
用户下单
   ↓
外卖系统生成订单
   ↓
商家确认接单
   ↓
调用配送API
   ↓
创建配送任务
   ↓
配送服务派单
   ↓
骑手接单
   ↓
骑手取货
   ↓
配送中
   ↓
配送完成
   ↓
配送状态回传外卖系统

这样可以将订单系统与配送系统进行解耦,让外卖平台专注于业务管理,而配送服务负责具体履约。

二、外卖系统接入配送API的整体架构

在实际开发过程中,可以将系统拆分成用户端、商家端、配送服务以及平台后台几个部分。

代码语言:javascript
复制
┌──────────────┐
│    用户端     │
│ H5 / 小程序 / APP │
└──────┬───────┘
       │ 下单
       ↓
┌──────────────┐
│   外卖业务系统  │
│ PHP + MySQL   │
└──────┬───────┘
       │
       │ 配送API
       ↓
┌──────────────┐
│   配送服务平台  │
└──────┬───────┘
       │
       │ 派单
       ↓
┌──────────────┐
│     骑手端     │
└──────┬───────┘
       │
       │ 状态回传
       ↓
┌──────────────┐
│   外卖系统后台  │
└──────────────┘

其中最重要的是中间的配送API服务层。

建议不要让订单业务代码直接大量依赖第三方接口,而是单独封装一个配送服务类。

例如:

代码语言:javascript
复制
interface DeliveryServiceInterface
{
    public function createOrder(array $order);

    public function cancelOrder(string $deliveryNo);

    public function queryOrder(string $deliveryNo);
}

后续如果需要更换配送服务,只需要替换具体实现即可,不需要大面积修改订单业务代码。

三、配送API需要对接哪些核心接口

一个完整的外卖配送接口体系通常包含以下几个核心功能。

1. 创建配送订单

商家确认订单后,外卖系统向配送服务提交配送任务。

通常需要传递:

代码语言:javascript
复制
{
    "order_no": "WM202609020001",
    "shop_name": "XX餐饮店",
    "shop_phone": "13800000000",
    "shop_address": "XX路100号",
    "user_name": "张先生",
    "user_phone": "13900000000",
    "user_address": "XX小区5号楼",
    "goods_amount": 38.5,
    "delivery_fee": 5,
    "remark": "请放在门口"
}

配送平台成功创建后,会返回一个配送单号。

代码语言:javascript
复制
{
    "code": 0,
    "message": "success",
    "delivery_no": "PS202609020001"
}

外卖系统需要保存这个配送单号,用于后续查询和状态同步。

2. 查询配送订单

当平台需要查看当前配送状态时,可以调用配送查询接口。

例如:

代码语言:javascript
复制
GET /api/delivery/order/detail

请求:

代码语言:javascript
复制
{
    "delivery_no": "PS202609020001"
}

返回:

代码语言:javascript
复制
{
    "delivery_no": "PS202609020001",
    "status": "delivering",
    "rider_name": "李师傅",
    "rider_phone": "13812345678"
}

然后将配送状态同步到外卖订单表。

四、PHP封装配送API调用

以PHP为例,可以对HTTP请求进行统一封装。

代码语言:javascript
复制
class DeliveryApi
{
    private string $baseUrl;
    private string $appKey;
    private string $appSecret;

    public function __construct()
    {
        $this->baseUrl = 'https://api.example.com';
        $this->appKey = 'your_app_key';
        $this->appSecret = 'your_app_secret';
    }

    private function request(string $method, string $uri, array $data = [])
    {
        $timestamp = time();

        $signString = $this->appKey
            . $timestamp
            . json_encode($data, JSON_UNESCAPED_UNICODE)
            . $this->appSecret;

        $sign = hash('sha256', $signString);

        $headers = [
            'Content-Type: application/json',
            'X-App-Key: ' . $this->appKey,
            'X-Timestamp: ' . $timestamp,
            'X-Sign: ' . $sign
        ];

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . $uri);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt(
                $ch,
                CURLOPT_POSTFIELDS,
                json_encode($data, JSON_UNESCAPED_UNICODE)
            );
        }

        $response = curl_exec($ch);

        if ($response === false) {
            throw new Exception(curl_error($ch));
        }

        curl_close($ch);

        return json_decode($response, true);
    }

    public function createOrder(array $order)
    {
        return $this->request(
            'POST',
            '/api/delivery/order/create',
            $order
        );
    }
}

实际项目中,baseUrlappKeyappSecret等信息应该放在环境变量或者系统配置中,不建议直接写死在业务代码里。

五、订单创建配送任务

当商家确认订单后,可以执行创建配送任务。

例如订单数据:

代码语言:javascript
复制
$order = [
    'order_no'      => 'WM202609020001',
    'shop_name'     => 'XX餐饮店',
    'shop_phone'    => '13800000000',
    'shop_address'  => 'XX路100号',
    'user_name'     => '张先生',
    'user_phone'    => '13900000000',
    'user_address'  => 'XX小区5号楼',
    'goods_amount'  => 38.5,
    'delivery_fee'  => 5,
    'remark'        => '请放在门口'
];

$deliveryApi = new DeliveryApi();

$result = $deliveryApi->createOrder($order);

if ($result['code'] === 0) {

    // 保存配送单号
    $deliveryNo = $result['delivery_no'];

    // 更新订单配送信息
    saveDeliveryNo(
        $order['order_no'],
        $deliveryNo
    );
}

数据库可以设计一个配送信息表:

代码语言:javascript
复制
CREATE TABLE `order_delivery` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `order_id` BIGINT UNSIGNED NOT NULL,
    `order_no` VARCHAR(64) NOT NULL,
    `delivery_no` VARCHAR(64) DEFAULT NULL,
    `rider_name` VARCHAR(50) DEFAULT NULL,
    `rider_phone` VARCHAR(30) DEFAULT NULL,
    `delivery_status` VARCHAR(30) DEFAULT 'pending',
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这样可以把订单业务数据和配送业务数据进行分离。

六、配送状态回调是对接的核心

外卖系统接入配送API时,不能只依靠主动查询。

更合理的方式是:

代码语言:javascript
复制
配送平台
   ↓
状态发生变化
   ↓
调用外卖系统回调接口
   ↓
外卖系统验证请求
   ↓
更新订单配送状态
   ↓
更新用户端订单页面

例如配送状态:

代码语言:javascript
复制
pending      待配送
accepted     骑手已接单
picked_up    已取货
delivering   配送中
completed    配送完成
cancelled    配送取消

配送平台可以向:

代码语言:javascript
复制
POST /api/delivery/callback

发送:

代码语言:javascript
复制
{
    "delivery_no": "PS202609020001",
    "order_no": "WM202609020001",
    "status": "delivering",
    "rider_name": "李师傅",
    "rider_phone": "13812345678",
    "timestamp": 1788326400
}

七、PHP实现配送状态回调

后端接收到回调后,需要先验证签名,再处理订单。

代码语言:javascript
复制
public function callback()
{
    $body = file_get_contents('php://input');

    $data = json_decode($body, true);

    if (!$data) {
        return json([
            'code' => 400,
            'message' => 'invalid request'
        ]);
    }

    // 验证签名
    if (!$this->verifySign($data)) {
        return json([
            'code' => 401,
            'message' => 'invalid sign'
        ]);
    }

    $deliveryNo = $data['delivery_no'];
    $status     = $data['status'];

    $delivery = DeliveryOrder::where(
        'delivery_no',
        $deliveryNo
    )->first();

    if (!$delivery) {
        return json([
            'code' => 404,
            'message' => 'delivery order not found'
        ]);
    }

    $delivery->delivery_status = $status;
    $delivery->rider_name = $data['rider_name'] ?? '';
    $delivery->rider_phone = $data['rider_phone'] ?? '';
    $delivery->updated_at = date('Y-m-d H:i:s');

    $delivery->save();

    // 同步外卖订单状态
    $this->syncOrderStatus(
        $delivery->order_no,
        $status
    );

    return json([
        'code' => 0,
        'message' => 'success'
    ]);
}

这里需要特别注意幂等处理。

例如配送平台因为网络问题重复发送3次“配送完成”通知,外卖系统不能因此重复执行订单完成后的业务逻辑。

可以通过配送单号+状态+事件ID等方式实现幂等。

八、配送状态如何同步到用户端

配送状态更新后,可以进一步同步到用户端。

例如:

代码语言:javascript
复制
商家已接单
    ↓
等待骑手接单
    ↓
骑手已接单
    ↓
骑手正在取货
    ↓
骑手配送中
    ↓
订单已送达

如果系统使用WebSocket,可以在状态发生变化时实时推送:

代码语言:javascript
复制
$data = [
    'type' => 'delivery_status',
    'order_no' => 'WM202609020001',
    'status' => 'delivering',
    'rider_name' => '李师傅'
];

WebSocket::push(
    $userId,
    json_encode($data, JSON_UNESCAPED_UNICODE)
);

如果没有WebSocket,也可以采用短轮询方式,让用户端定时查询订单状态。

九、配送API需要考虑地图与距离计算

配送费用通常与配送距离存在直接关系。

因此外卖系统在设计配送模块时,可以将配送距离、配送区域、基础配送费以及额外费用进行拆分。

例如:

代码语言:javascript
复制
配送费 =
基础配送费
+ 距离费用
+ 时段附加费
+ 特殊区域费用

后端可以抽象成一个配送费计算方法:

代码语言:javascript
复制
function calculateDeliveryFee(
    float $baseFee,
    float $distance,
    float $distanceUnitPrice,
    float $timeFee = 0,
    float $areaFee = 0
): float {

    $distanceFee = 0;

    if ($distance > 3) {
        $extraDistance = $distance - 3;
        $distanceFee = $extraDistance * $distanceUnitPrice;
    }

    return round(
        $baseFee
        + $distanceFee
        + $timeFee
        + $areaFee,
        2
    );
}

例如:

代码语言:javascript
复制
$fee = calculateDeliveryFee(
    5,
    6.5,
    1.5,
    2,
    0
);

echo $fee;

如果基础配送费为5元,3公里以后每公里1.5元,当前距离6.5公里,并存在2元时段附加费,则可以根据业务规则计算最终配送费用。

实际项目中,费用规则应当放在后台配置,而不是固定写在代码里。

十、第三方配送API的异常处理

API对接最容易被忽略的就是异常情况。

例如:

代码语言:javascript
复制
接口超时
接口返回错误
配送区域不支持
配送服务暂停
骑手无法接单
订单创建失败
订单取消失败
回调重复
回调丢失
网络异常

因此建议增加重试机制。

例如:

代码语言:javascript
复制
function requestWithRetry(callable $request, int $maxRetry = 3)
{
    $retry = 0;

    while ($retry < $maxRetry) {

        try {

            $result = $request();

            if (
                isset($result['code'])
                && $result['code'] === 0
            ) {
                return $result;
            }

        } catch (Throwable $e) {

            // 写入日志
            error_log($e->getMessage());
        }

        $retry++;

        sleep(2);
    }

    throw new Exception(
        '配送API请求失败'
    );
}

不过需要注意,创建订单类接口不能简单地无限重试。

否则可能出现:

代码语言:javascript
复制
第一次请求成功
↓
外卖系统没有收到响应
↓
系统再次创建配送订单
↓
产生两个配送任务

因此创建配送任务时应该使用业务订单号作为幂等键。

十一、建议建立配送API适配层

如果未来可能接入多个配送服务,可以进一步设计统一配送接口。

例如:

代码语言:javascript
复制
interface DeliveryInterface
{
    public function create(array $order);

    public function cancel(string $deliveryNo);

    public function detail(string $deliveryNo);

    public function calculateFee(array $params);
}

不同配送服务分别实现:

代码语言:javascript
复制
class DeliveryProviderA implements DeliveryInterface
{
    public function create(array $order)
    {
        // 服务A接口
    }

    public function cancel(string $deliveryNo)
    {
        // 服务A取消接口
    }

    public function detail(string $deliveryNo)
    {
        // 服务A查询接口
    }

    public function calculateFee(array $params)
    {
        // 服务A计费接口
    }
}

另外一个配送服务:

代码语言:javascript
复制
class DeliveryProviderB implements DeliveryInterface
{
    public function create(array $order)
    {
        // 服务B接口
    }

    public function cancel(string $deliveryNo)
    {
        // 服务B取消接口
    }

    public function detail(string $deliveryNo)
    {
        // 服务B查询接口
    }

    public function calculateFee(array $params)
    {
        // 服务B计费接口
    }
}

业务层只调用:

代码语言:javascript
复制
$deliveryService->create($order);

而不需要关心具体使用哪一家配送服务。

这种架构对于后续扩展配送渠道非常有帮助。

十二、外卖系统接入配送API的关键点

从实际开发角度来看,配送API对接并不是简单地调用几个HTTP接口,而是需要考虑整个订单生命周期。

核心需要解决以下几个问题:

1. 订单数据统一

外卖系统中的订单字段与配送平台字段可能不同,需要建立数据映射关系。

2. 配送订单幂等

避免因为网络重试造成重复配送。

3. 状态统一

不同配送平台可能采用不同的状态名称,需要转换成外卖系统自己的标准状态。

4. 回调安全

所有回调接口都应该进行签名验证,防止非法请求修改订单状态。

5. 异常重试

网络超时、接口异常等情况需要支持自动重试和人工补偿。

6. 日志记录

建议记录:

代码语言:javascript
复制
订单号
配送单号
请求参数
响应数据
请求时间
响应时间
接口耗时
错误信息
重试次数

方便出现问题时快速定位。

十三、一个完整的配送业务流程

最终,一个较为完整的外卖配送业务流程可以设计为:

代码语言:javascript
复制
用户提交订单
      ↓
订单支付成功
      ↓
商家接单
      ↓
计算配送费用
      ↓
创建配送任务
      ↓
配送平台接收订单
      ↓
骑手接单
      ↓
骑手到店取货
      ↓
开始配送
      ↓
配送状态实时同步
      ↓
用户收到商品
      ↓
配送完成
      ↓
外卖订单完成

与此同时,平台后台可以通过数据看板统计:

代码语言:javascript
复制
配送订单量
配送完成率
平均配送时长
平均配送距离
配送取消率
骑手接单率
异常订单数量

通过这些数据,可以进一步优化配送区域、配送规则以及骑手调度策略。

外卖系统
外卖系统

十四、总结

外卖系统接入配送API,本质上是通过标准化接口打通订单系统与配送服务,让订单从“商家接单”能够自动进入“配送履约”环节。

在开发过程中,除了完成创建配送订单、查询配送状态、取消配送以及状态回调等基础接口,还需要重点考虑接口安全、订单幂等、异常重试、数据同步以及多配送渠道适配等问题。

对于需要搭建外卖平台的企业来说,更合理的方式不是简单增加一个配送接口,而是将配送能力设计成独立的服务模块。这样既能够降低系统之间的耦合,也方便后续扩展不同配送渠道。

最终形成:

代码语言:javascript
复制
用户下单
   ↓
商家接单
   ↓
订单中心
   ↓
配送服务
   ↓
骑手履约
   ↓
状态回传
   ↓
用户收货

通过外卖系统与配送API的深度结合,可以进一步实现订单、商家、配送、骑手和用户之间的数据互通,为外卖平台建立更加完整的订单履约体系。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、什么是外卖系统配送API
  • 二、外卖系统接入配送API的整体架构
  • 三、配送API需要对接哪些核心接口
    • 1. 创建配送订单
    • 2. 查询配送订单
  • 四、PHP封装配送API调用
  • 五、订单创建配送任务
  • 六、配送状态回调是对接的核心
  • 七、PHP实现配送状态回调
  • 八、配送状态如何同步到用户端
  • 九、配送API需要考虑地图与距离计算
  • 十、第三方配送API的异常处理
  • 十一、建议建立配送API适配层
  • 十二、外卖系统接入配送API的关键点
    • 1. 订单数据统一
    • 2. 配送订单幂等
    • 3. 状态统一
    • 4. 回调安全
    • 5. 异常重试
    • 6. 日志记录
  • 十三、一个完整的配送业务流程
  • 十四、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档