资讯详情

H5十四合一代付系统源码详解:架构、部署与二次开发指南

📅 2026/10/26 9:40:08 | 华诺云谱 👁 阅读
H5十四合一代付系统源码详解:架构、部署与二次开发指南
简介全开源可二次开发的H5十四合一代付系统源码适用PHP7.4与MySQL5.7环境面向需要快速搭建聚合代付平台的开发者或运营者。程序内置美团、京东、拼多多、滴滴、携程、抖音、淘宝闪购、得物等14套模板支持账号密码登录、代理分成、用户提现、商品审核上架、后台运营大屏及多支付接口对接。近期更新修复了分享卡片显示、本地化JS/CSS加载、商品展示逻辑等问题并增强订单筛选、后台手动添加用户、卡片图片自定义等能力。资源共112个文件以25个PHP业务脚本为主辅以HTML页面、JS/CSS前端样式、图片素材、SQL数据库文件及部署说明文档压缩包仅9.46MB部署便捷。已有231人学习使用。源码完全开放便于按自身业务定制模板、支付流程与分成规则适合学习支付系统架构或直接搭建运营。1. H5十四合一代付系统是什么它解决的是结算侧的最后几公里做了这么多年支付相关的业务系统我最怕听到的需求就是“给用户批量打款”。如果你也接过类似“每日结算佣金、退款、分成”这类开发单大概率会搜到“H5十四合一代付系统源码”这个方向。它不是支付网关而是一套把十四种代付渠道聚合到同一个后台、对外提供统一 H5 页面和接口的资金结算系统全套源码开源附搭建教程能按自己业务改这正好切中“要可二次开发”的团队。这套方案解决的最后一公里问题是业务方手里有钱但需要把零碎资金打到用户银行卡或钱包逐笔手工操作不现实接一家渠道又怕被单一接口限制住。它适合内部研发资源有限、又要同时对接多个支付通道的小型业务方也适合做聚合支付的开发团队拿来做底座。先说清楚边界它定位是企业自有业务的资金结算工具不是牌照业务别拿去做两清或公共收款合规这关过不去技术再好也白搭。2. 源码结构先看这四个地方通道层、路由层、回调层与对账任务拿到一套这类项目的源码别急着打包运行先看目录里那套代码的组织方式。代付系统比普通 CRUD 敏感它动的是真金白银架构设计直接决定你后续能不能安稳睡好觉。常见的项目包无论叫什么名字核心模块基本都绕不开四个通道层、路由层、回调层和支撑任务层。前三个决定资金流是否顺畅第四个决定账目是否可靠。2.1 通道层每个支付渠道都是一个独立适配器十四合一的“十四”在代码里通常体现为十四个类而不是十四段散落的 if-else。每一个支付渠道被封装成一个适配器对外暴露统一接口。这样做的理由很直接不同渠道的接口报文、签名方式、同步返回格式完全不一样你把差异局部封装后上层业务感知不到渠道区别。这类系统的通道接口通常长这样public interface PayChannelAdapter { /** * 渠道编码要全局唯一比如 alipay、wxpay、unionpay */ String channelCode(); /** * 提交一笔代付申请 * order 里至少要包含订单号、金额(单位:分)、收款账号、真实姓名 */ PayResult submit(WithdrawOrder order); /** * 主动查单用于定时任务补偿 */ QueryResult query(String channelOrderNo); /** * 回调验签不同渠道验签逻辑差异很大 */ boolean verifyNotify(NotifyRequest request); }submit 是提交代付query 是主动查单verifyNotify 是回调验签。注意金额统一用“分”做整数这是代付系统最常见的设计约束后面会专门展开。接口里不写任何渠道特有字段渠道参数从配置文件或数据库字典里取避免渠道差异污染核心流程。每个渠道实现一个 Adapter 类例如 AlipayAdapter、WxpayAdapter。新增渠道时你只需要新增类并注册不需要改动提现代付和回调处理的公共逻辑。这是“可二次开发”在这个项目里最重要的体现做加法不改核心。2.2 路由层十四合一不是十四个接口各调各的有了通道层接下来就是路由层。路由层解决的是“这笔代付应该走哪个渠道”的问题。系统里配置了十二个可用渠道可不代表每笔单子都可以随便挑一个。有的渠道单笔限额五万有的单笔限额五百有的渠道费率低但清算慢有的费率贵但秒到。路由层把这些差异变成规则而不是靠人在后台手工选。常见做法是建一张路由配置表字段包含渠道编码、适用的金额区间、优先级、时段、状态。系统提交代付前按路由规则过滤和排序逐个尝试ListChannelPriority routes channelRouteService.getRoutes(merchantId, order.getAmount()); for (ChannelPriority priority : routes) { PayChannelAdapter adapter adapterFactory.get(priority.getChannelCode()); PayResult result adapter.submit(order); if (result.isSuccess()) { return result; } }这段代码是路由的骨架逻辑。getRoutes 先把不可用渠道过滤掉再按优先级排列submit 失败就尝试下一个渠道。这里有个细节很容易被忽略渠道返回失败不代表一定失败可能是超时、可能是报文不符合要求所以“失败即换渠道”的策略要谨慎后面查单机制会兜底。路由规则不是死的我一般建议把“优先级”和“启用状态”做成后台可配置而不是每次改代码重启。这样运营同学可以根据渠道故障情况临时切换不用半夜叫醒开发。2.3 回调层与状态机代付单状态不能靠猜代付单的状态流转比支付收款要长。支付收款是“用户付钱给你回调确认即结束”代付则是“你提交单子、渠道受理、渠道打款、回调通知结果”。整个过程不是一瞬间完成的中间可能卡住几分钟甚至几小时。所以代码里必须有一个清晰的订单状态机。常见的代付状态是这样的public enum WithdrawStatus { INIT, // 已创建待提交 PROCESSING, // 已提交渠道等回调 SUCCESS, // 代付成功 FAILED, // 代付失败 MANUAL // 人工介入处理 }INIT 表示用户发起了提现申请但还没提交给渠道PROCESSING 是提交给渠道后等待回执SUCCESS 和 FAILED 是终态MANUAL 是出现异常单时运营介入后的状态。回调到达后只有在 PROCESSING 状态才允许被更新成 SUCCESS 或 FAILED其他状态直接忽略。这样设计有一个实际好处防止乱覆盖。如果运营手动把一笔单子改成 MANUAL 并线下打款此时渠道回调晚到一步如果系统没有状态校验就会把人工处理结果覆盖掉账就乱了。2.4 支撑模块与那个 H5 前端分别负责什么全开源项目里通常有两套前端一套是 H5 用户端一套是管理后台。很多人第一次看代码容易把这两套弄混。H5 用户端面向的是手机浏览器用户在这里绑卡、发起提现申请、查看进度管理后台面向的是运营和财务负责审核、打款、渠道配置和对账。H5 端通常是轻量页面列表页加提现表单加几页状态展示用 Vue 或 React 写的居多打包后是纯静态文件。它和用户系统打通后用户进来能看到可提现余额提交提现申请后生成一张代付单后续就是后端的事。管理后台则是重逻辑渠道参数、路由优先级、费率都在这里配。理解这个分工后你部署时就不会纠结 H5 前端该不该连数据库——它不该它只需要调后端接口。支撑模块里还有一块容易被忽视定时任务。代付系统一定要有定时查单和自动对账任务。定时任务把处于 PROCESSING 状态的单子捞出来循环调 query 查渠道结果对账任务则把本地订单和渠道账单做比对。这两块在初版源码里有的有、有的只是留了接口二次开发时最值得先补的就是它。3. 从零搭建到上线环境准备、数据库导入与前后端启动的完整步骤这类项目我搭建过不少第一步从来不是急着跑代码而是确认环境。整套系统是 Java 技术栈不是那种用 PHP 写的单文件源码类型约束和并发处理上更稳当但对应的部署门槛也高一点。下面按我实际的部署顺序走一遍每一步的参数都给你标清楚。3.1 环境选型JDK、MySQL、Redis、Nginx 这套四件套怎么配先准备一台 Linux 云服务器配置不用太高2 核 4G 起步就能跑动整套系统。JDK 建议 8 或者 17具体看项目 pom 文件里指定的编译版本别拿高版本 JDK 跑旧项目容易出反射异常MySQL 用 5.7 或 8.x 都可以但字符集务必设置为 utf8mb4Redis 装最新稳定版即可存登录态和接口幂等键用Nginx 做 H5 静态页服务和后端反向代理。这里有个容易踩坑的点JDK 版本和 Spring Boot 版本要匹配。如果项目用的是 Spring Boot 2.x用 JDK 8 最省事如果你非要上 JDK 17需要确认 pom 里的依赖有没有兼容问题。我一般直接用项目管理工具自带的版本锁定不手动升级大版本。3.2 数据库初始化先建库再改 application.yml项目包里通常带一个 SQL 脚本文件名一般是 init.sql 或者 install.sql。先创建独立数据库再用命令行导入脚本mysql -uroot -p -e CREATE DATABASE pay_merger DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -uroot -p pay_merger init.sql导入成功后别急着继续先登录数据库看一眼核心表代付订单表、商户表、渠道配置表、路由规则表。如果表都不存在说明脚本没导对如果表存在但没有任何字典数据那你后面还要手工补渠道参数这个后面会讲。接下来改后端配置文件。常见路径是src/main/resources/application.yml需要改三处连接信息spring: datasource: url: jdbc:mysql://127.0.0.1:3306/pay_merger?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: pay password: ChangeMe123 redis: host: 127.0.0.1 port: 6379 database: 0注意 URL 里那串参数不要删characterEncodingutf8 保证中文不乱码serverTimezoneAsia/Shanghai 保证时间不错八小时。数据库密码建议单独用环境变量注入别硬编码进 yml源码要提交到 Git 的时候密钥和密码都要避免出现在配置文件里。这不是做样子是防止仓库泄露后被人拖库。3.3 后端打包启动两条命令但顺序不能乱后端如果是 Maven 项目打包就一条命令mvn clean install -DskipTests第一次执行会下载很多依赖耗时取决于服务器带宽。打包完成后根据模块名找到对应的 jar比如有的项目叫 merchant-admin有的叫 xxx-pay-service。以后台模块为例java -jar merchant-admin/target/merchant-admin.jar --spring.profiles.activeprod启动顺序有讲究。必须先保证 MySQL 和 Redis 都启动了再启动后端的这个模块如果后端启动就在报连不上数据库回头检查数据库时候会浪费时间。看启动日志里出现 “Started” 关键词才算起来成功不要看到进程没报错就当没事。启动成功后验证一下后端管理台的登录接口。用 curl 在服务器本地访问一下健康检查路径很多项目会暴露 /actuator/health 端口返回 JSON 里 status 是 UP 就说明没问题。3.4 H5 前端打包与 Nginx 反向代理H5 前端是独立工程目录里常见的是h5或web-h5。先安装依赖再打包npm install npm run build打包产物会生成到 dist 目录。把 dist 目录里的静态文件传到服务器假设放到/data/www/h5然后配置 Nginx 把 H5 和后端接口串起来server { listen 443 ssl; server_name h5.pay.example.com; ssl_certificate /etc/nginx/ssl/server.pem; ssl_certificate_key /etc/nginx/ssl/server.key; root /data/www/h5; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }关键在 location /api/ 这一段转发到后端 Java 服务的端口。这里有个新手翻车点H5 页面能打开但接口全部 404十有八九是 proxy_pass 后面的路径写错了。注意/api/结尾的斜杠和http://127.0.0.1:8080/api/结尾的斜杠要保持一致的路径语义后端要求的上下文路径不同这里的处理也不同以项目里的后端约定为准。HTTPS 证书建议直接用云服务商免费的因为很多支付渠道要求回调地址必须是 HTTPS不然签名校验通过后通知发不出来。别在这块省钱申请证书加配置 Nginx 也就是半小时的事。4. 你一定会碰到的关键参数商户号、RSA 密钥、回调地址与路由规则环境跑通只是第一步真正的配置工作在支付渠道参数。这套系统能不能跑出第一笔真实代付单取决于你在管理后台填的参数对不对。很多搭建帖跳过这部分导致项目跑起来后一提交代付就报签名错误我今天把这几个参数捋清楚。4.1 四个必填参数与一对密钥的生成命令每个支付渠道接入时你至少要在渠道管理里填以下几项参数名含义取值示例merchantId渠道分配给平台方的商户号一串数字编码appId应用标识常和商户号绑定数字或字符串混合merchantPrivateKey平台私钥用于上行请求签名PKCS8 格式platformPublicKey渠道公钥用于验证回调签名PKCS8 格式notifyUrl渠道回调地址https://h5.pay.example.com/api/pay/notify四个参数里最容易出错的是密钥。这类系统用 RSA 签名生成密钥对用 openssl 一条命令openssl genrsa -out private.pem 2048 openssl rsa -in private.pem -pubout -out public.pem然后把私钥内容粘贴到系统的商户私钥配置项。但这里有两个坑一是有渠道要求私钥是 PKCS#1 格式有渠道要求 PKCS#8你粘贴进去如果格式不匹配就报错二是粘贴时不要把-----BEGIN PRIVATE KEY-----换行符弄丢常见做法是把整段带表头的字符串保持原样存入数据库。判断格式其实一句话就能解决复制粘贴前先看渠道接口文档。文档里如果示例是开头带BEGIN RSA PRIVATE KEY那就是 PKCS#1如果开头只写BEGIN PRIVATE KEY就是 PKCS#8。Java 代码里很多类库更习惯 PKCS#8遇到报错不要慌用 openssl 转换一下格式再填。4.2 金额、订单号与幂等键的硬约束代付系统对金额和订单号的要求比普通业务严格得多。金额绝对不能在前端用 float 或 double 传给后端也不能在 Java 里用 double 做计算。常见的可靠做法是在数据库里用 decimal 存在接口层直接接收字符串金额后端转成以“分”为单位的 long 再处理。订单号必须全局唯一。提交代付时以订单号作为幂等键提交给渠道防止渠道重复受理。我见过不少翻车现场同一笔代付单因为网络超时被重复提交结果用户收到两笔钱追回来极其困难。所以代码里提交前一定要有防重判断Redis 里存一个 key或者查本地订单表状态两者必有其一。4.3 路由优先级与费率参数的配置落点管理后台的“渠道路由”页面通常可以配置每个渠道的启用状态、优先级和限额。不要把优先级理解成排序数字那么简单它其实是代付系统里的资金调度策略。比如 A 渠道单笔限额一千你一笔代付金额是两千那这笔单子无论优先级多高都该被跳过换成额度合适的 B 渠道。配置路由时建议遵循这样顺序先用额度过滤再用费率排序最后用成功率的动态加权。很多源码实现了前两种第三种可能是留了个空接口。如果你对成功率有强诉求二次开发里第一个扩展点就可以选这里把最近一小时的成功率实时统计并加权到路由排序里。4.4 回调地址的配置本地联调为什么收不到通知回调地址是渠道主动请求你的服务器所以本地开发环境直接写http://localhost:8080/notify大概率收不到。渠道服务器在公网访问不到你本机的端口。我一般不会在本地全链路联调代付回调而是把后端先部署到测试服务器用测试域名配好 HTTPS等回调验证通过了再把功能合入主分支。如果你实在要在本地调试也有一个折中做法把渠道回调地址先配置成测试服务器的某个接口该接口收到回调后原样转发到你的本地机器。这种方式能保留本地断点调试能力但别把这个转发接口写到生产配置里它会引入额外的故障点。5. 避坑清单回调丢失、金额精度、状态覆盖等六个高频问题这一章是我最想写给读者看的因为代付系统的坑不像普通 CRUD 那样一眼能看出来大部分坑要等资金真正流动起来才暴露。下面的每一条都是真实项目里反复出现过的按照“现象、原因、解决”写清楚你只要对照着排查能省出好几个通宵。5.1 回调丢失订单一直“处理中”怎么办现象用户发起了提现订单状态停在 PROCESSING过了一两个小时还是不动用户追问客服客服来问开发。原因渠道回调因为网络波动、回调地址临时不可用、或者渠道侧压根没触发回调而丢失。解决不要等回调一定要有主动查单任务。写一个定时任务每五分钟扫描处理中超过十分钟的订单调渠道 query 接口查状态查到终态就更新本地订单。这个任务在初期搭建时最容易被省略但它恰恰是代付系统的保底机制没有它一半以上的客户投诉都从此来。5.2 金额用 double 的教训为什么必须以“分”为单位现象账目总是差几分钱对账对不平检查代码发现金额字段用的 Double。原因double 在二进制里无法精确表示某些十进制小数比如 0.1 加 0.2 不是正好 0.3。累计多了之后账目差异会变得神出鬼没。解决所有接口传输金额用 String数据库金额字段用 decimal(10,2)内部计算统一转成 long 型“分”。一旦代码里出现 float 或者 double 参与金额计算直接改掉不要心存侥幸。5.3 RSA 密钥格式报错PKCS#1 和 PKCS#8 的差别现象配置完渠道参数后提交代付接口立刻返回签名错误或密钥格式错误。原因很多渠道接口文档默认给 Java 示例示例里的密钥是 PKCS#8有的渠道只给 PKCS#1 格式示例你不做转换直接粘贴就报错。解决先确认渠道要求的密钥格式然后用 openssl 转换格式私钥转换命令是openssl pkcs8 -topk8 -inform PEM -in private.pem -outform PEM -nocrypt。转换后再填进配置签名验证就能过。记住系统里填的是密钥字符串本身还是密钥路径也要看清配置项设计别填错位置。5.4 返回成功但没到账渠道挂账与查单补偿现象系统记录代付成功但用户反馈银行卡没有收到钱。原因渠道返回成功有时只是受理成功资金清算有延迟也有极少数情况是渠道侧支付出现问题需要人工介入。解决看到成功回调后不要直接停在那建立日终对账机制。每天拉取渠道对账单与本地订单逐笔比对成功状态但渠道侧没有流水记录的订单要标记异常并人工复核。代付系统做好对账比你加多少个监控告警都有用。5.5 手动修正以后又被回调覆盖状态机为什么要锁终态现象一笔订单渠道一直没回调运营等不及在后台手动把状态改为失败并给用户退了款。结果第二天渠道回调到了系统又把订单改成成功用户和运营都蒙了。原因处理回调的代码没有判断订单当前状态无论什么状态都直接更新。解决更新订单状态前,先使用条件更新语句带上状态参数只在 PROCESSING 状态下才允许更新到终态SQL 的 update 影响行数为 0 就表明当前状态不允许流转。这样手动终态就永远不会被晚到的回调覆盖。5.6 数据库时区和连接串编码两个让新手熬夜的配置现象订单创建时间早了八小时用户姓名或备注出现乱码。原因MySQL 连接串缺了 serverTimezone 参数时区默认成了 UTC存进去的时间比北京时间慢八小时连接串缺了 characterEncodingutf8字符集解析乱套。解决在 application.yml 的连接串里加上serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8数据库创建时指定 utf8mb4。这两个配置每个项目都要检查一次不要图省事复制旧项目配置就完事。6. 二次开发怎么加第 15 个渠道扩展写法、联调与验证技巧当你把系统跑通、避坑也看完了下一步大概率是往里面加自己的业务逻辑。这套源码所谓“可二次开发”最典型的落地场景就是接入新支付渠道下面是我实际的扩展流程。6.1 三步接入一个新支付渠道第一步新建适配器类实现 PayChannelAdapter按渠道文档实现 submit、query、verifyNotify 三个方法。第二步在渠道枚举类里加上新渠道编码保证全局不重复。第三步在管理后台添加渠道参数和路由规则启用后就能测试。通道层接口基于统一抽象新增渠道时最需要注意的是签名逻辑差异。有的渠道用 MD5 签名有的用 RSA有的用 HMAC这些差异只写在 Adapter 内部不要泄露到 Service 层Component public class NewpayChannelAdapter implements PayChannelAdapter { Override public String channelCode() { return newpay; } Override public PayResult submit(WithdrawOrder order) { // 1. 组装渠道要求的报文 // 2. 用 newpay 渠道的私钥签名 // 3. 发起 HTTP 请求记录渠道返回单号 // 4. 渠道受理成功返回 PROCESSING明确失败返回 FAILED return null; } }新增渠道后一定要先在测试环境把 Submit、Query、Notify 三条链路都跑通再切生产。没跑过真实资金流的渠道适配器上线几乎必出幺蛾子。6.2 本地联调怎么模拟回调验签真实渠道的回调难以在本地触发我一般会写一个模拟回调的脚本在测试环境把渠道验签逻辑单独测透curl -X POST https://your.test.server/api/pay/notify/newpay \ -H Content-Type: application/x-www-form-urlencoded \ --data orderNoTEST202501010001amount100statusSUCCESSsignxxx这里的 sign 要用渠道文档给的签名算法算出来不能用明文代替。如果你用随便写的 sign 验证代码走不到验签成功那一步等于没测。测试重点有三项验签失败时返回渠道需要的应答、验签成功后订单状态正确流转、重复回调不会改变终态。这三个点顺了真实渠道的联调就只剩两边联调时间了。6.3 上线前的对账脚本把昨天的账自己先对一遍新渠道上线后头三天一定要人工盯对账。我通常写一个简单的 Python 脚本从本地库和对账文件里拉数据做比对跑完输出差异单import pymysql conn pymysql.connect(host127.0.0.1, userpay, passwordxxx, databasepay_merger, charsetutf8mb4) cur conn.cursor() cur.execute(SELECT order_no, amount, channel_code FROM withdraw_order WHERE channel_codenewpay AND date(create_time)%s, (2025-01-01,)) local {row[0]: row[1] for row in cur.fetchall()} # 渠道对账单解析逻辑略假设得到 billed_order_no - billed_amount billed {NEW20250101001: 5000} for order_no, amount in local.items(): if order_no not in billed: print(缺失渠道流水:, order_no, amount) elif billed[order_no] ! amount: print(金额不一致:, order_no, local[order_no], billed[order_no])脚本逻辑很简单但如果每天能跑一遍资金安全隐患基本都能在萌芽期被发现。记住代付系统出问题不可怕可怕的是出了问题你不知道隔了半个月才对账发现资金缺口那时候谁都救不了你。我带团队上线过好几套代付通道最后悔的一次是只信回调、没做查单任务结果半夜一单挂起没人管第二天商户电话被打爆。此后凡是接代付项目第一件事先把定时查单和对账补上再谈功能上线。这套全开源代付系统代码只是起点真正值钱的是你对资金流的敬畏心。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑