外卖系统对接三方接口方案:如何实现订单、支付与配送数据互通

简介: 本文详解外卖系统对接支付、配送、地图等三方接口的完整方案,涵盖架构设计、订单/支付/配送状态同步、幂等与签名验证、日志记录及适配器模式等核心实践,助力构建稳定、安全、可扩展的外卖平台。(239字)

随着同城外卖、校园配送、商家自营配送等业务不断发展,外卖系统已经不再只是简单的“商品展示+下单+支付”。

一个相对完整的外卖系统,通常还需要与支付服务、配送服务、短信服务、地图服务以及其他第三方业务系统进行接口对接。

因此,在外卖系统定制开发过程中,“外卖系统对接三方接口”已经成为比较常见的技术需求。

尤其是订单、支付和配送三个环节,如果系统之间无法实现稳定的数据互通,就容易出现支付成功但订单状态没有更新、骑手配送状态不同步、订单重复推送等问题。

本文就从系统架构、接口设计、订单同步、支付回调以及配送状态同步几个方面,对外卖系统对接三方接口的实现方案进行介绍。
外卖系统.png

一、外卖系统为什么需要对接三方接口?

传统外卖系统可以将业务全部放在自己的服务器中完成。

例如:

用户选择商品 → 提交订单 → 系统生成订单 → 用户支付 → 商家接单 → 骑手配送 → 订单完成。

但实际业务中,很多环节需要依赖外部服务。

例如:

  • 支付需要调用第三方支付接口
  • 配送可能需要调用第三方配送接口
  • 地图服务需要调用地图API
  • 短信通知需要调用短信服务
  • 电子发票可能需要调用发票接口
  • 部分商家可能需要与自身ERP、POS等系统进行数据同步

因此,一个比较合理的系统架构应该将自身业务系统与第三方服务进行解耦。

可以采用下面这种结构:

                 ┌──────────────┐
                 │   用户端     │
                 │ H5 / 小程序  │
                 └──────┬───────┘
                        │
                        ▼
                ┌───────────────┐
                │   外卖业务系统 │
                │   PHP + MySQL │
                └───────┬───────┘
                        │
          ┌─────────────┼─────────────┐
          │             │             │
          ▼             ▼             ▼
     ┌─────────┐   ┌─────────┐   ┌─────────┐
     │支付接口 │   │配送接口 │   │地图接口 │
     └─────────┘   └─────────┘   └─────────┘

这样做的好处是,即使某一个第三方服务发生变化,也不会直接影响整个外卖系统的核心业务。

二、外卖系统对接三方接口的核心流程

以一个普通外卖订单为例,整个流程可以拆分为几个步骤。

用户提交订单
      ↓
系统创建本地订单
      ↓
生成待支付订单
      ↓
调用支付接口
      ↓
用户完成支付
      ↓
支付平台回调
      ↓
系统验证支付结果
      ↓
更新订单为已支付
      ↓
通知商家接单
      ↓
创建配送订单
      ↓
第三方配送平台接单
      ↓
同步配送状态
      ↓
骑手完成配送
      ↓
更新本地订单为已完成

这里有一个非常重要的设计原则:

第三方接口返回成功,并不代表整个业务流程已经完成。

系统必须以自己的订单状态作为最终业务依据。

三、订单系统如何设计?

首先需要建立自己的订单表。

例如MySQL可以设计为:

CREATE TABLE `orders` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `order_no` VARCHAR(64) NOT NULL,
    `user_id` BIGINT NOT NULL,
    `shop_id` BIGINT NOT NULL,
    `total_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
    `pay_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
    `pay_status` TINYINT NOT NULL DEFAULT 0,
    `order_status` TINYINT NOT NULL DEFAULT 0,
    `delivery_status` TINYINT NOT NULL DEFAULT 0,
    `third_order_no` VARCHAR(128) DEFAULT NULL,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_order_no` (`order_no`)
);

其中:

pay_status
0 = 未支付
1 = 已支付
2 = 已退款

order_status
0 = 待支付
1 = 待接单
2 = 配送中
3 = 已完成
4 = 已取消

delivery_status
0 = 未创建
1 = 已创建
2 = 配送中
3 = 已送达
4 = 配送异常

实际项目中可以根据具体业务增加更多状态。

例如:

商家已接单
骑手已接单
骑手已到店
骑手取餐
骑手送达
用户确认收货

四、订单号设计

订单号是整个系统与第三方进行数据关联的重要字段。

例如PHP可以这样生成订单号:

function createOrderNo()
{
   
    return date('YmdHis') . mt_rand(100000, 999999);
}

$orderNo = createOrderNo();

echo $orderNo;

生成结果类似:

20260901172535123456

建议不要直接使用数据库自增ID作为对外订单号。

可以使用:

业务订单号
+
数据库ID
+
第三方订单号

分别管理。

例如:

本地订单号:20260901172535123456

数据库ID:10235

第三方订单号:THIRD202609010001

这样后续排查接口问题会更加方便。

五、支付接口如何对接?

支付接口一般分为两个阶段:

第一阶段是创建支付订单。

第二阶段是接收支付回调。

例如后端创建支付订单:

$order = [
    'order_no' => $orderNo,
    'amount'   => '39.90',
    'title'    => '外卖订单'
];

$response = http_post(
    'https://api.example.com/pay/create',
    $order
);

$result = json_decode($response, true);

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

    $payUrl = $result['data']['pay_url'];

    echo json_encode([
        'code' => 200,
        'pay_url' => $payUrl
    ]);

}

前端获取支付地址之后,可以引导用户完成支付。

例如:

async function payOrder(orderNo) {
   

    const result = await fetch('/api/order/pay', {
   
        method: 'POST',
        headers: {
   
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
   
            order_no: orderNo
        })
    });

    const data = await result.json();

    if (data.code === 200) {
   
        window.location.href = data.pay_url;
    }
}

但是,支付成功以后,不能仅仅根据前端页面显示来判断订单是否支付成功。

真正可靠的方式是等待支付平台的服务器回调。

六、支付回调如何处理?

例如:

public function payNotify()
{
   
    $data = $_POST;

    if (!$this->verifySign($data)) {
   
        return 'SIGN_ERROR';
    }

    $orderNo = $data['order_no'];
    $status  = $data['status'];

    if ($status !== 'SUCCESS') {
   
        return 'FAIL';
    }

    $order = $this->findOrder($orderNo);

    if (!$order) {
   
        return 'ORDER_NOT_FOUND';
    }

    if ($order['pay_status'] == 1) {
   
        return 'SUCCESS';
    }

    $this->updateOrder($orderNo, [
        'pay_status' => 1,
        'order_status' => 1
    ]);

    return 'SUCCESS';
}

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

因为第三方平台可能重复发送支付回调。

例如:

第一次回调 → SUCCESS
第二次回调 → SUCCESS
第三次回调 → SUCCESS

如果没有幂等机制,就可能造成:

重复发货
重复增加余额
重复创建配送订单
重复发送通知

因此应该先判断订单当前状态。

if ($order['pay_status'] == 1) {
   
    return 'SUCCESS';
}

已经处理过的订单直接返回成功即可。

七、支付成功后如何创建配送订单?

当订单完成支付,并且商家确认接单以后,就可以进入配送流程。

系统可以调用第三方配送接口:

$deliveryData = [
    'order_no' => $order['order_no'],
    'shop' => [
        'name' => '示例餐厅',
        'address' => '示例商家地址',
        'phone' => '13800000000'
    ],
    'customer' => [
        'name' => '用户',
        'address' => '用户收货地址',
        'phone' => '13900000000'
    ],
    'goods' => [
        [
            'name' => '套餐',
            'quantity' => 1
        ]
    ]
];

$response = http_post(
    'https://api.example.com/delivery/order/create',
    $deliveryData
);

$result = json_decode($response, true);

第三方返回成功以后:

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

    $thirdOrderNo = $result['data']['order_no'];

    updateOrder($order['order_no'], [
        'third_order_no' => $thirdOrderNo,
        'delivery_status' => 1
    ]);
}

这样本地系统就可以保存第三方配送订单编号。

八、为什么一定要保存第三方订单号?

这是外卖系统对接三方接口非常重要的一点。

本地系统一般有:

order_no

第三方配送系统一般有:

third_order_no

两个订单号不能混为一谈。

建议数据库设计成:

本地订单号
第三方订单号
第三方服务商
第三方订单状态

例如:

ALTER TABLE `orders`
ADD COLUMN `third_provider` VARCHAR(50) DEFAULT NULL,
ADD COLUMN `third_order_no` VARCHAR(128) DEFAULT NULL,
ADD COLUMN `third_status` VARCHAR(50) DEFAULT NULL;

这样以后更换配送服务商时,也不会影响本地订单结构。

九、配送状态如何同步?

配送订单创建以后,第三方平台可能不断产生状态变化。

例如:

待接单
↓
骑手已接单
↓
骑手到店
↓
骑手取货
↓
配送中
↓
已送达

第三方可以通过Webhook向你的服务器发送通知。

例如:

public function deliveryNotify()
{
   
    $data = json_decode(
        file_get_contents('php://input'),
        true
    );

    if (!$this->verifySign($data)) {
   
        return 'SIGN_ERROR';
    }

    $thirdOrderNo = $data['order_no'];
    $status = $data['status'];

    $order = findOrderByThirdNo($thirdOrderNo);

    if (!$order) {
   
        return 'ORDER_NOT_FOUND';
    }

    updateOrder($order['order_no'], [
        'third_status' => $status,
        'delivery_status' => mapDeliveryStatus($status)
    ]);

    return 'SUCCESS';
}

其中:

function mapDeliveryStatus($status)
{
   
    $map = [
        'WAITING' => 1,
        'ACCEPTED' => 2,
        'DELIVERING' => 3,
        'COMPLETED' => 4
    ];

    return $map[$status] ?? 0;
}

这样第三方系统的状态就可以转换成自己系统能够理解的状态。

十、为什么不能直接使用第三方状态?

不同平台的状态定义可能完全不同。

例如:

平台A:

10 = 待接单
20 = 已接单
30 = 配送中
40 = 已完成

平台B可能是:

WAITING
ACCEPTED
DELIVERING
COMPLETED

如果业务代码直接使用第三方状态,后期更换第三方服务时,就需要修改大量业务代码。

更合理的方式是建立统一状态层。

第三方状态
      ↓
状态转换层
      ↓
系统统一状态
      ↓
用户端 / 商家端 / 管理后台

例如:

function normalizeDeliveryStatus($provider, $status)
{
   
    $maps = [

        'provider_a' => [
            '10' => 'WAITING',
            '20' => 'ACCEPTED',
            '30' => 'DELIVERING',
            '40' => 'COMPLETED'
        ],

        'provider_b' => [
            'WAITING' => 'WAITING',
            'ACCEPTED' => 'ACCEPTED',
            'DELIVERING' => 'DELIVERING',
            'COMPLETED' => 'COMPLETED'
        ]
    ];

    return $maps[$provider][$status] ?? 'UNKNOWN';
}

这样系统内部只需要处理统一状态。

十一、建议采用接口适配器模式

如果外卖系统未来可能接入多个第三方服务,可以进一步使用接口适配器。

例如:

interface DeliveryProvider
{
   
    public function createOrder($order);

    public function cancelOrder($order);

    public function queryOrder($order);

    public function calculateFee($order);
}

然后不同配送服务分别实现:

class ProviderA implements DeliveryProvider
{
   
    public function createOrder($order)
    {
   
        // 调用服务商A接口
    }

    public function cancelOrder($order)
    {
   
        // 调用服务商A取消接口
    }

    public function queryOrder($order)
    {
   
        // 查询服务商A订单
    }

    public function calculateFee($order)
    {
   
        // 服务商A配送费计算
    }
}

另一个服务商:

class ProviderB implements DeliveryProvider
{
   
    public function createOrder($order)
    {
   
        // 调用服务商B接口
    }

    public function cancelOrder($order)
    {
   
        // 调用服务商B接口
    }

    public function queryOrder($order)
    {
   
        // 查询服务商B订单
    }

    public function calculateFee($order)
    {
   
        // 服务商B配送费计算
    }
}

业务层不需要关心具体服务商。

$provider = DeliveryFactory::make($providerName);

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

这种方式非常适合需要对接多个第三方配送服务的外卖系统。

十二、第三方接口一定要做好签名验证

第三方接口一般会涉及订单、金额、用户、地址等业务数据。

因此接口不能简单地接收请求。

可以通过:

时间戳
+
随机数
+
业务参数
+
密钥

生成签名。

例如:

$params = [
    'order_no' => $orderNo,
    'timestamp' => time()
];

ksort($params);

$string = http_build_query($params);

$sign = hash_hmac(
    'sha256',
    $string,
    $secret
);

请求时:

order_no
timestamp
sign

服务端收到请求以后重新计算签名。

$serverSign = hash_hmac(
    'sha256',
    $string,
    $secret
);

if (!hash_equals($serverSign, $requestSign)) {
   
    return 'SIGN_ERROR';
}

这样可以降低接口被伪造调用的风险。

十三、第三方接口调用还需要考虑超时和重试

现实环境中,第三方接口并不是百分之百稳定。

可能出现:

网络超时
接口响应慢
服务器异常
返回格式错误
服务商维护

因此不能简单写成:

$result = http_post($url, $data);

然后失败以后直接提示用户。

可以增加超时时间和重试机制。

例如:

function requestWithRetry($url, $data, $maxRetry = 3)
{
   
    for ($i = 0; $i < $maxRetry; $i++) {
   

        try {
   

            $result = http_post($url, $data);

            if ($result) {
   
                return $result;
            }

        } catch (Exception $e) {
   

            if ($i === $maxRetry - 1) {
   
                throw $e;
            }

            sleep(1);
        }
    }

    return false;
}

但是需要注意:

“重试”并不是所有接口都适合。

例如创建配送订单这种操作,如果第一次请求已经成功,只是响应没有返回,再次创建可能造成重复订单。

因此最好结合业务幂等号使用。

十四、使用幂等号避免重复创建订单

例如:

$idempotentKey = hash(
    'sha256',
    $order['order_no'] . '_delivery'
);

提交第三方接口时:

$deliveryData['request_id'] = $idempotentKey;

第三方服务可以根据这个唯一请求号判断:

第一次请求:
request_id = ABC123

第二次请求:
request_id = ABC123

如果已经创建过,就直接返回原来的配送订单。

如果第三方接口支持幂等机制,这种设计应该优先使用。

十五、建议增加第三方接口日志

外卖系统在实际运营过程中,接口问题是比较难排查的一类问题。

例如:

用户说已经支付
商家说没有收到订单

这时候后台需要知道:

什么时候调用了支付接口?
发送了什么参数?
第三方返回了什么?
有没有收到回调?
回调验证是否成功?
订单最终修改成了什么状态?

因此建议建立接口日志表。

CREATE TABLE `api_logs` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `provider` VARCHAR(50) NOT NULL,
    `api_name` VARCHAR(100) NOT NULL,
    `request_id` VARCHAR(100) DEFAULT NULL,
    `request_data` TEXT,
    `response_data` TEXT,
    `status` VARCHAR(30) DEFAULT NULL,
    `created_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`)
);

记录:

接口名称
请求时间
请求参数
响应结果
订单号
第三方订单号
请求ID
错误信息

出现问题时,管理员就可以直接查看接口日志。

十六、前端不要直接调用敏感的第三方接口

这是外卖系统开发过程中非常重要的一点。

不推荐:

fetch('https://third-party.com/api/order', {
   
    method: 'POST',
    body: JSON.stringify(order)
});

因为这样可能暴露:

API Key
Secret
业务参数
第三方接口地址

正确的方式应该是:

前端
 ↓
自己的业务API
 ↓
业务服务器
 ↓
第三方接口

例如:

fetch('/api/order/create', {
   
    method: 'POST',
    headers: {
   
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
   
        shop_id: 1001,
        goods: cart
    })
});

然后由PHP后端完成第三方接口调用。

这样能够更好地保护第三方接口密钥。

十七、三方接口对接建议采用统一服务层

随着系统功能越来越多,可以将第三方服务统一管理。

例如:

Service
│
├── Payment
│   ├── ProviderA
│   └── ProviderB
│
├── Delivery
│   ├── ProviderA
│   └── ProviderB
│
├── Map
│   ├── ProviderA
│   └── ProviderB
│
└── Sms
    ├── ProviderA
    └── ProviderB

业务代码只需要调用:

$paymentService->create($order);

$deliveryService->create($order);

$mapService->geocode($address);

$smsService->send($phone, $message);

而不需要在订单业务代码里面直接写大量第三方接口请求。

这种架构更加方便维护和扩展。

十八、外卖系统对接三方的整体架构

综合来看,可以设计成:

                  用户端
             H5 / 小程序 / APP
                     │
                     ▼
               API业务层
                     │
        ┌────────────┼────────────┐
        │            │            │
        ▼            ▼            ▼
     订单服务      支付服务      配送服务
        │            │            │
        │            ▼            ▼
        │         第三方支付    第三方配送
        │
        ├──────────────┐
        │              │
        ▼              ▼
      MySQL          Redis
        │
        ▼
     接口日志
        │
        ▼
     管理后台

通过这样的架构,可以把自己的核心业务和第三方服务进行隔离。

十九、外卖系统对接三方时需要重点关注哪些问题?

在实际开发过程中,建议重点考虑以下几个方面。

1. 接口稳定性

第三方服务出现异常时,自己的订单系统不能跟着直接瘫痪。

2. 数据一致性

支付成功、订单状态、配送状态等数据需要保持一致。

3. 幂等处理

支付回调、配送回调、订单创建等接口都应该考虑重复请求。

4. 签名验证

所有涉及订单、支付、用户等数据的接口,都应该进行身份认证和签名校验。

5. 接口日志

建议完整记录请求、响应、订单号和错误信息。

6. 超时机制

第三方接口不能无限等待,需要设置合理的请求超时时间。

7. 重试机制

对于可安全重试的接口,可以增加自动重试。

8. 状态转换

不要让第三方状态直接渗透到核心业务系统。

9. 服务商切换

如果未来可能更换第三方服务,应提前设计适配器层。

10. 敏感信息保护

API Key、Secret、支付密钥等信息不能直接写入前端代码。

外卖系统.png

二十、总结

外卖系统对接三方接口,本质上是解决不同系统之间的数据通信和业务协同问题。

其中订单、支付和配送属于最核心的三个环节。

比较合理的技术方案应该是:

统一订单中心
+
支付服务层
+
配送服务层
+
第三方接口适配器
+
Webhook回调
+
幂等机制
+
签名验证
+
接口日志
+
异常重试

通过这种方式,可以让外卖系统具备更好的扩展能力。

当业务需要接入新的支付服务、配送服务或其他第三方系统时,只需要增加对应的接口适配器,而不需要大规模修改原有订单业务。

对于企业而言,如果后期还需要增加校园外卖、同城配送、商家自配送、跑腿配送等业务,也可以在统一订单中心的基础上继续扩展。

因此,在进行外卖系统定制开发时,与其只考虑“能不能对接三方”,更应该从系统架构层面考虑“如何让三方接口稳定、安全、可扩展地接入自己的业务系统”。

相关文章
|
18天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
12965 81
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
6天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
11天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1669 3
|
人工智能 JavaScript 开发工具
DeepSeek Harness 本地安装与使用指南
DeepSeek Harness(DSH)是DeepSeek AI开源的Agent运行框架,支持本地文件操作、命令执行与工具调用。基于Cordis插件架构,具备高扩展性与强可控性,适合开发者搭建可控Agent环境或开展模型基准测试。当前为开发者预览版,需Node.js环境,推荐先用`npx @deepseek-ai/dsh web`快速体验。
5074 0
|
12天前
|
人工智能 Java BI
【AI】DeepSeek Harness 安装、运行、管理插件
本文介绍了如何运行DeepSeek开源的Agent框架DeepSeek Harness(dsh)。主要内容包括:使用nvm安装适配的Node版本;通过代理加速克隆GitHub源码;使用pnpm安装依赖并启动项目;配置DeepSeek API Token;安装扩展功能的插件。该框架自带Web界面,支持模型适配、文件编辑等插件化功能
1829 1
|
14天前
|
人工智能 JavaScript 测试技术
保姆级教程:DeepSeek Harness从安装到跑通测试,30分钟上手
DeepSeek Harness是DeepSeek开源的AI Agent运行时,主打“一行命令安装、5分钟跑通”。它让模型真正动手干活——读代码、跑测试、分析失败、生成修复方案。本文手把手教你30分钟从零上手,覆盖安装、配置、实测及避坑指南,助你快速掌握下一代AI编程范式。
|
16天前
|
开发工具 Swift git
DeepSeek Harness 插件推荐:4 款开源神器让写代码直接起飞
DeepSeek Harness 插件推荐:ModLens 视觉、Web UI 全家桶、Mac 原生与 GenUI 渲染,4 款开源插件给纯文本模型补齐短板。
2044 6
DeepSeek Harness 插件推荐:4 款开源神器让写代码直接起飞
|
13天前
|
人工智能 JavaScript 测试技术
从 0 到 1,DeepSeek Harness 保姆级安装与使用教程!
DeepSeek Harness是DeepSeek推出的开源Agent运行框架,秉持“一切皆插件”理念,支持模型、工具、技能、工作流等全模块自由替换与扩展。其核心Cordis内核实现动态插件管理,赋能Agent自进化。已成GitHub史上增速最快开源项目(15w+ Star),标志着国内大模型从拼价格转向重架构与生态的新拐点。
1318 6
从 0 到 1,DeepSeek Harness 保姆级安装与使用教程!

热门文章

最新文章