简介第三方API代付系统源码集成QQ、微信、支付宝三家主流代付通道同时支持自定义对接其他平台适合个人开发者、中小商户及企业搭建安全便捷的下发与余额充值功能。资源共2005个文件以PHP处理业务逻辑、JS实现前端交互、CSS与SVG构建页面样式和图标附带SQL数据库文件和部署脚本压缩包仅47.33MB目录结构清晰便于快速定位。后台入口及初始账号密码均有提供部署后即可试用已有186人学习下载。功能上覆盖汇款邮件通知、手续费承担方自定义、最低最高金额限制、延迟到账保护、异常风控限制等并集成易支付、微付、码支付、官方通道等余额充值接口整体代付成功率稳定适合需要快速接入第三方API代付或自建代付平台的开发者参考使用。1. 代付系统不是支付网关接入前先把职责分清做过资金下发业务的都知道微信、支付宝、QQ 的商户平台只支持退款到自己渠道的订单无法直接向任意银行账户或他人账户打款。第三方 API 代付系统正好补这个缺口它把多个支付渠道的下发能力聚合成一个统一的 HTTP 接口业务方只需要按一套 JSON 格式提交订单系统自动路由到对应通道完成出款。这套源码的设计思路是「聚合通道 余额账户 风控策略」三层结构后台默认地址 /admin账号 admin密码 123456适合个人开发者、小型电商、平台方快速搭建自己的代付服务也适合想研究支付渠道接入原理的技术人员拆解。需要注意的代付系统处理的是资金划拨和支付网关是两套体系线上汇款和通道方结算的边界要提前理清楚。2. 核心链路设计余额账户、通道路由与出款状态机2.1 账户体系为什么代付系统先做余额扣减再做真实打款代付系统的第一层是商户账户模型。常见的做法是平台为每个接入商户建立独立资金账户商户先充值获得余额出款时系统先冻结再扣减余额随后才调用下游通道真实划拨资金。这套源码在数据库设计中用一张 account 表记录商户余额用另一张 flow 表记录每笔资金变动出款前检查 balance 与冻结金额避免并发时出现超扣。-- 出款前锁定商户账户行防止并发超扣 BEGIN; SELECT id, balance, frozen_balance FROM merchant_account WHERE merchant_id ? AND balance - frozen_balance ? FOR UPDATE; UPDATE merchant_account SET frozen_balance frozen_balance ? WHERE merchant_id ?; INSERT INTO capital_flow (merchant_id, order_no, type, amount, status) VALUES (?, ?, freeze, ?, pending); COMMIT;这段 SQL 的关键在于FOR UPDATE行锁事务提交前其他事务对该商户账户的读写会被阻塞避免两个出款请求同时读取到同一个余额快照。冻结金额计入 frozen_balance 而不是直接扣减 balance目的是如果下游 API 调用失败可以快速解冻回滚保留余额修改痕迹便于后续对账。查询余额时页面展示的是balance - frozen_balance的可用值后台「最低到最高汇款金额」的校验也基于这个可用余额做判断。2.2 通道路由按渠道参数、金额区间、当前状态选路通道路由是聚合系统的核心。微信、支付宝、QQ 三种代付通道虽然都叫「代付」但接口参数差异很大微信走商户号 API v3 证书支付宝用 app_id 私钥签名QQ 钱包的报文格式又不一样。这套源码用一张 channel 配置表维护通道参数每个通道独立设置 API 地址、app_id、商户号、密钥、单笔限额区间和开关状态。提交出款订单时路由模块按商户指定的通道类型优先匹配再检查金额是否落在该通道的 min_amount 和 max_amount 之间最后校验通道的 switch_status 是否为开启状态。public function routeChannel(int $merchantId, float $amount, string $payType): array { $channels ChannelModel::where(pay_type, $payType) -where(switch_status, 1) -where(min_amount, , $amount) -where(max_amount, , $amount) -orderBy(priority, desc) -get(); if ($channels-isEmpty()) { throw new ChannelException(当前金额区间无可用通道); } return $channels-first()-toArray(); }路由逻辑里 priority 字段是通道优先级配置数值越大越优先自适应不同通道的优先策略。这里的 pay_type 支持 alipay、wechat、qq 三种枚举值对应「支付宝、微信、QQ 三大主流代付接口」的通道分类。需要留意「可自定义对接其他平台」的原理就是在此表添加新行下游 API 调用类实现同一个 PayInterface 接口即可源码中的 Driver 目录下每个通道一个类文件继承统一接口后即可被路由模块识别。2.3 出款状态机下单、回调、补单三态流转真实代付场景中API 返回「受理成功」并不代表资金已经到账。微信、支付宝的异步回调才是最终状态来源。这套源码的出款表设计了如下状态pending 表示已受理等待回调success 表示出款成功failed 表示失败待处理review 表示被风控拦截需要人工审核。回调到达时系统根据 out_trade_no 找到原始单判断当前状态是否为 pending是则更新终态同时解冻或扣减余额。public function handleCallback(string $outTradeNo, string $status, string $channelMsg): void { DB::transaction(function () use ($outTradeNo, $status, $channelMsg) { $order PayoutOrder::where(out_trade_no, $outTradeNo)-lockForUpdate()-first(); if ($order-status ! pending) { return; // 幂等处理重复回调不覆盖终态 } if ($status success) { $order-status success; MerchantAccount::where(merchant_id, $order-merchant_id) -decrement(frozen_balance, $order-amount); } else { $order-status failed; $order-fail_reason $channelMsg; MerchantAccount::where(merchant_id, $order-merchant_id) -decrement(frozen_balance, $order-amount); } $order-save(); }); }lockForUpdate()配合事务防止两条回调并发时重复处理。状态流转中一个容易踩坑的点success 状态下只减少冻结金额不扣减余额failed 状态下同样只解冻不扣减余额因为余额在实际调用出款 API 时已经扣掉了。这样设计的好处是资金流与订单流保持一致性代付成功率的统计可以直接用 success 单数除以总出款单数计算每一笔出款都有详细列表支付宝和微信通道的每笔出款金额统计也来自这张订单表。3. 余额充值接口易支付等多渠道聚合的实现细节3.1 充值下单与跳转逻辑商户余额怎么进来这套源码集成「易支付、微付、码支付、官方通道」四种充值方式前端选择充值通道后后端生成一笔充值订单并跳转到对应收银台。易支付这类第三方聚合支付本质上是把你的订单参数转发到其平台用户在该平台完成付款后平台向你的回调地址 POST 一个签名结果。下单时的基本参数包括 pid、type、out_trade_no、notify_url、return_url、money 和 sign其中 sign 是 MD5 签名。以易支付为例签名规则是 方式拼接参数键值加上商户密钥后再做 MD5public function buildSign(array $params, string $secretKey): string { ksort($params); $signStr urldecode(http_build_query($params)) . $secretKey; return md5($signStr); }ksort按参数名 ASCII 升序排序是为了保证签名串稳定排序后 http_build_query 时如果参数值中有中文或特殊字符需要先 urldecode 再拼接密钥否则两端签名会不一致。这里的 PID 是易支付商户 IDtype 是支付方式money 为充值金额。回调验签时必须拿到平台原始 POST 参数不能依赖前端数据验签一致后再更新 capital_flow 表同时给 merchant_account 增加 balance。3.2 回调通知与幂等处理充值回调是资金进账的唯一凭证在实现回调通知处理时要防止重复通知导致商户余额翻倍增加。这里常见的做法是在回调入口先查充值订单状态如果已经是 paid直接返回成功标记不再执行账户加钱逻辑。源码中的处理逻辑是每次回调都会重新计算签名再对比平台推送过来的 sign 字段同时要求订单状态必须是 pending 才允许更新。public function notify(Request $request): string { $params $request-post(); $order RechargeOrder::where(out_trade_no, $params[out_trade_no])-first(); if (!$order || $order-status paid) { return success; } if ($this-buildSign($params, $order-channel_secret) ! $params[sign]) { return fail; } if (abs($params[money] - $order-amount) 0.01) { return fail; } DB::transaction(function () use ($order) { $order-status paid; $order-save(); MerchantAccount::where(merchant_id, $order-merchant_id) -increment(balance, $order-amount); }); return success; }金额校验使用abs($params[money] - $order-amount) 0.01是因为浮点数比较不能用等号保留两位小数的金额在传输中可能出现 100.00 变成 100.0 的情况。返回 success 是告诉支付平台不要再重发通知如果返回其他内容平台会连续重试。这里还需要一个 cron 每分钟扫描超过 5 分钟未支付但在平台侧已成功的充值订单主动向平台查询订单状态做补偿避免回调网络抖动导致订单卡在未支付状态。易支付、微付、码支付、官方通道的差异只体现在请求地址和签名算法上官方通道一般走支付宝开放平台或微信支付 Native 下单其余聚合支付平台协议几乎一致。3.3 可自定义手续费承担方商户付还是平台付充值手续费默认从商户充值金额中扣即用户充值 100 元实际到账余额为 100 减去手续费。这套源码在后台配置中区分了「商户承担手续费」和「平台承担手续费」两种模式。商户承担时订单表中记录 fee_amount商户实到余额为 amount 减去 fee_amount平台承担时商户实到金额等于 amount平台在结算时自行承担手续费成本。两种模式的实现区别只在入账语句商户承担用increment(balance, $order-amount - $order-fee_amount)而平台承担用increment(balance, $order-amount)。配置项位于后台的充值费率设置里选择哪种取决于平台的实际利润模型。4. 安全与风控频率限制、密码锁定、限额与延迟入账4.1 同账户频繁汇款限制代付系统的安全重点在防滥用。真实场景中商户账号被盗后攻击者会在极短时间内批量出款因此需要从接口层做频控。这套源码实现了一个基于 Redis 的滑动窗口计数器以商户 ID 加时间窗为 key统计同一账户在指定周期内的出款次数超过阈值直接拒绝请求并返回提示。# 在 Redis 中维护出款频率计数器 127.0.0.1:6379 INCR payout:frequency:{merchant_id}:{current_minute} # 返回 1 表示该分钟内首次出款返回超过阈值则拒绝 127.0.0.1:6379 EXPIRE payout:frequency:{merchant_id}:{current_minute} 300INCR 的原子性保证高并发下不会出现多个请求同时读到同一个计数。EXPIRE 设置 300 秒覆盖多个 60 秒窗口这样每分钟的 key 到期后自动清理不需要额外写定时任务。后台配置项「同账户汇款频繁限制」的参数就是这里的分钟阈值默认为 5 次 / 分钟如果业务方确实有高频出款需求需要先评估通道方的风控策略再调整参数否则通道侧容易触发资金冻结限制。4.2 密码错误次数限制与禁封汇款后台管理员的密码防护在这个源码中也有独立设计。连续输错密码 5 次后账户会被锁定 30 分钟锁定期内即使输入正确密码也无法登录需要在数据表中手动清除锁定时间戳。此外「充值订单投诉禁封汇款功能」的实现方式是商户表增加一个 status 字段置为 banned 时所有出款请求在路由前就会被拦截提示商户联系客服处理。public function checkMerchantStatus(int $merchantId): void { $merchant Merchant::find($merchantId); if ($merchant-status banned) { throw new PayoutException(商户已被禁封请联系客服处理); } if ($merchant-password_error_count 5 $merchant-lock_until time()) { throw new PayoutException(密码错误次数过多账户已锁定); } }锁定状态同时记录 lock_until 时间戳采用服务器时间判断而不是简单依赖计数防止前端绕过校验。需要注意的是密码错误计数清零时机登录成功后清零达到阈值后即使输入正确密码也不能通过校验必须先等 lock_until 过期或由管理员手动清除。这套逻辑不仅保护了后台管理方自身也避免了攻击者对商户账号的暴力破解。4.3 延迟到账与最低最高限额的联动逻辑这套源码有一个重要特性「可开启充值金额延迟到账有效保证商户安全」。实现方式是当后台开启 delay_settle 配置后充值订单回调成功后不在回调处理器里直接增加余额而是先写入一个待入账队列到设定的延迟时间可配置分钟数后再由命令行任务扫描入账。这样做可以在充值资金存在争议时给平台留出处理时间避免商户充值后立即出款造成平台垫付风险。// 命令行任务每 5 分钟执行一次 public function handleSettle(): void { $delayMinutes Config::get(recharge_delay_minutes, 30); $orders RechargeOrder::where(status, paid_settle_pending) -where(paid_at, , now()-subMinutes($delayMinutes)) -get(); foreach ($orders as $order) { DB::transaction(function () use ($order) { $order-status settled; $order-save(); MerchantAccount::where(merchant_id, $order-merchant_id) -increment(balance, $order-amount); }); } }这里的核心是 paid_at 与延迟窗口配合下单时间晚于窗口的订单不会被扫描到。与之联动的「自定义设置最低到最高汇款金额」则是在出款下单接口做前置校验金额小于等于后台配置的最低限额时拒绝出款防止测试单和薅羊毛金额大于最高限额时提示拆分订单。延迟到账配合出款限额共同构成资金池的防御纵深充值延迟让可疑资金无法立刻流动出款限额控制单笔损失上限。5. 通道对接的边界条件与三个线上常见问题5.1 微信、支付宝、QQ 通道的差异处理微信代付需要加载商户证书文件API v3 的请求头中要携带 Authorization 和 Wechatpay-Serial 串号序列号与证书文件不一致会直接报错。支付宝代付使用的是 RSA2 签名字段私钥格式一般为 PKCS8公钥上传到开放平台后业务请求中必须包含 app_id、method、sign 三个关键字段。QQ 钱包的报文则是 form 表单提交而非 JSON且金额单位为分。这套源码的 Driver 层把三者的差异做了隔离但接入自定义平台时你仍然需要复核两件事请求头 Content-Type 是否为对方要求金额单位是元还是分。// 微信 v3 回调头信息示例用于验签 const headers { Wechatpay-Timestamp: req.headers[wechatpay-timestamp], Wechatpay-Nonce: req.headers[wechatpay-nonce], Wechatpay-Signature: req.headers[wechatpay-signature], Wechatpay-Serial: req.headers[wechatpay-serial] };后端在收到微信支付回调推送时不要只验证请求体中的参数还要用微信支付平台证书公钥对上述四个头字段拼接的字符串做验签。很多对接微信代付的常见坑在于只验 body 参数忽略了头部签名验证导致回调地址被人构造伪造。支付宝回调则不同它是靠参数中单独传递的 sign 字段配合支付宝公钥验签。因此在编写自定义通道时一定要区分「头部签」和「参数签」两类验签策略一套通用验签逻辑无法覆盖所有通道。5.2 出款投诉与失败补单机制推送出款订单到微信、支付宝通道后对方并不是实时返回最终结果。如果上游通道返回「受理成功」但实际最终失败需要通过主动查询或等待回调来发现。这套源码在回调无法送达时会启动定时任务查询通道侧订单状态查询频率建议 1 分钟一次而非连续查询避免高频率请求导致通道方 IP 被限流。查询返回已扣款且状态成功时按成功回调逻辑走查询返回退款或失败时将余额解冻回商户可用余额同时写入投诉记录。// 查询账单状态保存到日志表做人工复核依据 $queryResult ChannelDriver::make($order-channel) -query($order-out_trade_no); OperationLog::create([ order_no $order-order_no, channel $order-channel, query_result json_encode($queryResult), query_at now(), ]);5.3 后台配置项与部署排错速查表配置项位置默认值说明汇款最低/最高金额后台-系统设置1 / 50000 元超出范围直接拦截出款同账户频繁汇款限制后台-风控设置5 次/分钟基于 Redis 计数延迟到账开关后台-充值设置关开启后按分钟延迟入账手续费承担方后台-费率设置平台承担影响商户实际到账金额密码错误锁定次数后台-管理员5 次锁定 30 分钟因为密码错误次数过多而被锁定无法登录时检查数据库 merchant_account 表或 admin_user 表中的 lock_until 字段常见处理是把 lock_until 改回 NULL同时清空 password_error_count。另一个高频问题是安装后打开后台地址发现样式丢失这套源码的静态资源是 css 文件名为 main.css.bak 和 aik.min.css 等如果 Nginx 没有正确处理静态资源目录需要检查站点 conf 中 root 路径是否指向项目根目录PHP 的伪静态规则也要选择 thinkPHP 或 Laravel 对应的配置。邮件通知失败时查看 config/mail.php 中的 SMTP 配置项QQ 邮箱需要开启 SMTP 授权码不能直接用账号密码。本文还有配套的精品资源点击获取