美团代付多模版三合一源码拆解:策略模式实现三种代付场景
简介这份资源是美团代付业务的多模版三合一源码包面向需要搭建代付系统的PHP开发者与二次开发人员帮助快速落地代付功能并理解多模板切换的实现思路。包内共4个文件以zip源码压缩包为主另含sql数据库文件、txt说明文档与html页面文件分别用于程序部署、数据导入与配置参考整体约43.09MB。测试环境基于Nginx 1.2、MySQL 5.7与PHP 7.4需安装ionCube、fileinfo、opcache、memcache、redis、swoole4、sg11、igbinary等扩展并设置TP伪静态、运行目录指向public导入数据库后修改.env文件完成数据库连接。资源附带教程涵盖安装文件搜索与批量修改等环节适合具备一定PHP与ThinkPHP基础的开发者参考。目前已有857人学习下载可作为代付类项目搭建与多模板改造的实践素材。1. 美团代付多模版三合一源码拆解一套代码怎么跑通三种代付场景外卖、团购、酒店这三个场景的代付逻辑表面看都是「A 下单、B 掏钱」实际落到代码里差别不小。外卖代付要处理配送地址和超时取消团购代付要处理券码核销和退款回滚酒店代付要处理预授权和到店核销。很多团队一开始只做外卖代付等业务要扩到团购和酒店时发现原来的代码根本改不动只能推倒重来。美团代付多模版三合一源码要解决的就是这个问题用一套统一的订单抽象层把三种代付场景的差异收敛到配置和策略类里公共的下单、支付回调、状态机、对账逻辑只写一遍。这篇笔记面向的是手里有这套源码、想把它跑起来并改造成自己业务的开发者也适合正在设计代付系统的后端同学参考。我会按「先跑通最小闭环、再拆多模版设计、最后讲改造和避坑」的顺序展开中间会给出可直接抄的配置和代码片段。2. 把源码跑起来环境、依赖与最小代付闭环2.1 先看清目录结构再动手装依赖拿到一套代付源码别急着composer install或npm install。先花十分钟把目录结构看一遍能省掉后面很多「文件找不到」的玄学问题。常见的三合一源码目录大致长这样project-root/ ├── app/ │ ├── controller/ # 入口控制器按场景分 │ ├── service/ # 业务逻辑层 │ ├── model/ # 数据模型 │ └── pay/ # 支付相关含代付策略 ├── config/ │ ├── pay.php # 支付渠道配置 │ └── scene.php # 三种场景的差异化配置 ├── public/ │ └── index.php # 入口文件 ├── runtime/ # 缓存和日志 └── sql/ └── install.sql # 建表语句重点看app/pay/和config/scene.php。前者决定代付请求怎么发出去后者决定三种场景的行为差异。如果源码里没有scene.php而是把场景判断硬编码在 service 里说明这套代码的多模版设计做得不够干净后面改造时要留意。依赖安装按技术栈来。PHP 项目常见的是 ThinkPHP 或 Laravel 骨架先确认composer.json里的 PHP 版本要求别用 PHP 8.2 去跑只兼容 7.4 的老代码会报一堆弃用警告。数据库一般用 MySQL 5.7 或 8.0建表语句在sql/install.sql里导入前先建好库并确认字符集是utf8mb4否则用户昵称里的 emoji 会存进去变问号。2.2 配置支付渠道与场景参数代付能不能跑通八成取决于配置对不对。以常见的微信代付和支付宝转账为例config/pay.php里通常需要填这些?php return [ wechat [ mch_id 你的商户号, app_id 你的应用ID, api_key API密钥, // 注意不是AppSecret cert_path /path/to/apiclient_cert.pem, key_path /path/to/apiclient_key.pem, ], alipay [ app_id 你的应用ID, private_key 应用私钥, public_key 支付宝公钥, ], // 代付场景开关三合一的核心 scene [ waimai [enable true, timeout 900], // 外卖15分钟未支付取消 tuangou [enable true, timeout 1800], // 团购30分钟 hotel [enable true, timeout 3600], // 酒店1小时 ], ];这里有几个参数容易填错。api_key在微信支付里是 APIv2 的密钥不是应用密钥填错了签名会一直失败但报错信息很模糊只提示「签名错误」排查起来很费时间。证书路径要用绝对路径相对路径在不同入口文件下解析结果不一样是典型的「本地能跑线上翻车」源头。timeout是代付订单的支付超时时间单位秒三种场景给不同值是合理的外卖讲究快团购可以等酒店因为涉及预授权可以更宽松。配置改完先别急着测支付把runtime/目录权限给足PHP 项目写不进日志会直接 500而且错误信息被框架吞掉只看到白屏。2.3 用一条测试订单验证代付闭环最小闭环的验证思路是创建一个代付订单拿到代付链接或二维码模拟支付回调看订单状态有没有正确流转。很多源码会带一个测试入口比如public/test.php或命令行脚本。如果没有可以自己写一个临时脚本?php // test_daifu.php 放在 public 目录下测完删掉 require __DIR__ . /../vendor/autoload.php; $app new \app\service\DaifuService(); // 1. 创建代付订单 $order $app-createOrder([ scene waimai, // 场景外卖 amount 0.01, // 金额测试用1分 payer_uid 1001, // 付款人 owner_uid 1002, // 下单人 out_trade_no TEST . time(), ]); echo 订单号: . $order[order_no] . \n; echo 代付链接: . $order[pay_url] . \n; // 2. 模拟支付成功回调实际由支付平台异步通知 $result $app-handleNotify([ out_trade_no $order[order_no], trade_status SUCCESS, ]); var_dump($result);跑通这个脚本看到订单状态从「待支付」变成「已支付」说明代付主链路是通的。如果卡在创建订单就报错先看数据库连接和表结构如果卡在回调重点查签名验证和订单号匹配逻辑。测试金额用 0.01 元别用 1 元代付接口一般有最低金额限制但测试时小额更容易通过风控。提示测试回调时如果源码用了file_get_contents(php://input)读取原始请求体用浏览器直接访问是拿不到数据的必须用 POST 且带正确的 Content-Type建议用 curl 模拟。3. 多模版三合一的设计策略模式怎么落地3.1 三种代付场景的差异到底在哪要把三合一讲清楚得先明确外卖、团购、酒店代付的差异点。我整理了一张对比表这些差异直接决定了代码里哪些部分要抽象、哪些部分要隔离维度外卖代付团购代付酒店代付订单超时15分钟超时自动取消30分钟可延长1小时涉及预授权退款规则未接单可全额退券未核销可退到店前可退到店后扣费状态流转待支付→已支付→配送中→完成待支付→已支付→待核销→已核销待支付→已支付→待入住→已入住分账对象商家骑手商家平台酒店平台回调处理需通知商家接单需发券码需通知酒店预留看这张表就明白如果三种场景共用一套状态机代码里会塞满if scene waimai这种判断改一处影响三处。三合一源码的正确做法是公共部分下单、支付、签名、对账抽到基类差异部分状态机、超时策略、退款规则用策略类实现通过配置决定加载哪个策略。3.2 策略类的代码骨架与注册方式一个可落地的策略模式骨架大概是这样。先定义接口?php namespace app\pay\strategy; interface DaifuStrategyInterface { // 订单创建后的初始化 public function onCreated(array $order): void; // 支付成功后的处理 public function onPaid(array $order): void; // 超时未支付的处理 public function onTimeout(array $order): void; // 退款规则校验 public function canRefund(array $order): bool; }然后三种场景各实现一个类比如外卖的?php namespace app\pay\strategy; class WaimaiStrategy implements DaifuStrategyInterface { public function onCreated(array $order): void { // 外卖下单后锁定配送时段 \app\model\Delivery::lockSlot($order[order_no]); } public function onPaid(array $order): void { // 通知商家接单 \app\service\NotifyService::toMerchant($order[merchant_id], new_order); } public function onTimeout(array $order): void { // 释放配送时段 \app\model\Delivery::releaseSlot($order[order_no]); } public function canRefund(array $order): bool { // 商家未接单才可退 return $order[status] paid !$order[accepted]; } }注册方式用工厂或容器绑定在config/scene.php里把场景名映射到类名scene_map [ waimai \app\pay\strategy\WaimaiStrategy::class, tuangou \app\pay\strategy\TuangouStrategy::class, hotel \app\pay\strategy\HotelStrategy::class, ],这样新增场景时只要加一个策略类加一行配置不用动主流程。这是三合一源码最值得学的地方也是判断一套代付源码质量高低的关键看它的场景差异是收敛在策略类里还是散落在 service 的各个角落。3.3 公共支付流程与场景钩子的衔接策略类定义好了主流程怎么调用它们核心是在订单状态变更的关键节点插入钩子。以支付成功为例?php namespace app\service; class DaifuService { public function handleNotify(array $notify): array { // 1. 验签公共逻辑 if (!$this-verifySign($notify)) { return [code FAIL, msg 签名错误]; } // 2. 查订单公共逻辑 $order \app\model\Order::where(order_no, $notify[out_trade_no])-find(); if (!$order || $order[status] ! pending) { return [code FAIL, msg 订单状态异常]; } // 3. 更新状态公共逻辑 $order-status paid; $order-paid_at time(); $order-save(); // 4. 场景钩子差异逻辑 $strategy $this-getStrategy($order[scene]); $strategy-onPaid($order-toArray()); return [code SUCCESS, msg OK]; } private function getStrategy(string $scene) { $map config(scene.scene_map); $class $map[$scene] ?? null; if (!$class) { throw new \Exception(未知场景: {$scene}); } return new $class(); } }这段代码的价值在于验签、查单、改状态这三步是三种场景完全一样的写在主流程里onPaid之后做什么交给策略类。这样外卖策略去通知商家团购策略去发券酒店策略去通知预留互不干扰。改造时如果发现某个场景需要额外的公共步骤优先考虑能不能抽到主流程而不是在策略类里重复写。注意策略类里不要直接操作数据库事务事务边界应该由主流程控制。否则策略类里抛异常主流程的事务回滚不掉会出现订单状态和业务数据不一致。4. 改造与二次开发把源码变成自己的代付系统4.1 替换支付渠道要动哪些文件源码默认接的支付渠道不一定适合你的业务替换渠道是二次开发里最常见也最容易出问题的操作。以把微信代付换成另一个渠道为例需要动的地方有三处config/pay.php里的渠道配置、app/pay/下的渠道实现类、以及回调入口的验签逻辑。渠道实现类通常要实现一个统一接口比如createDaifuOrder、queryOrder、refund、verifyNotify四个方法。替换时保持接口不变只换实现主流程就不用动。有个坑要提前说不同渠道的代付到账时间不一样有的实时到账有的 T1。如果你的业务逻辑假设「支付成功即到账」换成 T1 渠道后会出现用户已付款但收款方没收到的情况客诉就来了。正确做法是在订单表里加一个settle_status字段把「支付成功」和「结算完成」分开策略类的onPaid里只处理支付成功结算完成用另一个钩子。4.2 新增第四种代付场景的步骤三合一不是上限业务发展后可能要加第四种场景比如「跑腿代付」或「充值代付」。新增场景的步骤是固定的第一步在config/scene.php的scene_map里加一行映射第二步新建策略类实现DaifuStrategyInterface的四个方法第三步在数据库的场景字典表里加一条记录让前端能选到这个场景第四步如果新场景有额外的状态在订单表的状态字段里扩展枚举值并检查所有用到状态判断的地方有没有遗漏。第三步容易被忽略。很多源码的场景列表是硬编码在前端或配置里的新增场景后后端通了但前端选不到联调时才发现。建议把场景列表做成接口返回前端动态渲染这样加场景不用改前端。4.3 对账与日志代付系统不能省的后悔药代付涉及真金白银对账和日志是必须做的但很多源码为了「简洁」把这块省了后期出问题查都没法查。至少要保证三样东西每笔代付请求的原始报文落库、每次回调的原始报文落库、每天和支付渠道对一次账。原始报文落库用单独的pay_log表字段包括订单号、请求方向、报文内容、创建时间。对账脚本每天凌晨跑拉取渠道的对账文件和本地订单逐笔比对差异记录到reconcile_diff表并告警。日志这块有个血泪经验不要把支付相关的日志和业务日志混在一起。支付日志单独一个文件按天切割保留至少 180 天。出问题时第一时间看支付日志能省掉大量猜测。另外日志里不要打印完整的密钥和证书内容打印前几位做标识就行避免日志泄露导致的安全问题。5. 代付源码避坑排查五个真实踩坑记录5.1 回调重复触发导致重复发货现象用户支付一次商家收到两条接单通知团购场景下发了两次券。原因支付渠道的回调可能重试源码没有做幂等处理每次回调都执行一遍onPaid。解决在handleNotify里加幂等锁用订单号做 key处理前先SETNX处理完再释放或者更简单更新订单状态时用where status pending做条件更新影响行数为 0 就直接返回成功不执行后续逻辑。5.2 金额单位混用导致代付金额错 100 倍现象测试时代付 0.01 元正常上线后用户代付 100 元实际扣了 1 元。原因源码里有的地方用元有的地方用分创建订单时传的是元调渠道接口时没乘 100。解决全链路统一用分做单位数据库存分接口传分只在展示层转成元。改造时全局搜索金额相关字段确认单位一致这个坑一旦上线就是资损。5.3 超时取消任务把已支付订单也取消了现象用户支付成功后过了超时时间订单被自动取消钱退了但货也发了。原因超时取消任务只判断了created_at timeout now没判断订单当前状态。解决取消前先查状态只有pending状态才取消用条件更新where status pending保证原子性。同时把超时任务的执行频率调高比如每分钟一次减少状态判断和实际取消之间的时间窗口。5.4 证书路径在定时任务里失效现象网页端代付正常定时任务里的退款全部失败报证书找不到。原因证书路径用了相对路径网页端入口文件在public/下定时任务入口在项目根目录相对路径解析结果不同。解决证书路径一律用绝对路径在配置文件里用__DIR__或环境变量拼接不要写./cert/xxx.pem这种。5.5 场景配置漏改导致新场景走旧逻辑现象新增跑腿代付场景后下单正常但支付成功后没通知跑腿员。原因scene_map里加了映射但策略类里onPaid方法忘了实现通知逻辑或者复制了外卖策略但没改通知对象。解决新增策略类时四个接口方法都要有实现哪怕暂时为空也要写注释说明上线前用测试订单把每个场景的完整链路走一遍重点看onPaid和onTimeout的副作用是否符合预期。6. 进阶技巧用配置驱动把三合一扩成 N 合一前面讲的策略模式已经能支撑三合一但如果场景继续增多每加一个场景就写一个策略类维护成本还是会上升。进阶做法是把策略类里可配置的部分抽出来用配置驱动只有真正特殊的逻辑才写代码。比如超时时间、退款规则、通知对象这些都可以放到scene.php的配置里scenes [ waimai [ timeout 900, refund_rule before_accept, // 接单前可退 notify [merchant, rider], status_flow [pending, paid, delivering, done], ], paotui [ timeout 600, refund_rule before_pickup, notify [merchant, runner], status_flow [pending, paid, picking, done], ], ],然后写一个通用的ConfigStrategy根据配置里的refund_rule和notify执行对应逻辑。只有配置表达不了的场景才单独写策略类。这样新增一个常规场景改配置就行不用写代码、不用发版。验证这套机制是否可靠我一般用两个方法。一是写一个场景配置校验脚本检查每个场景的status_flow是否首尾正确、notify里的对象是否都有对应的通知实现、refund_rule是否是已知规则。二是做配置热更新测试改完配置不重启服务看新订单是否按新配置执行老订单是否不受影响。这两个测试过了配置驱动才算真正可用。最后说个我自己的习惯每次改完代付相关代码不管改动多小都用 0.01 元跑一遍三种场景的完整链路从下单到回调到对账。这个习惯帮我拦下过好几次「看起来没问题」的改动。代付系统没有小事一分钱的测试订单能省掉后面一万块的客诉。希望帮到你。本文还有配套的精品资源点击获取