Files
frontend-app/docs/douyin-pay-integration.md
T

5.2 KiB
Raw Blame History

抖音 App 支付接入与打包

2026-09-09,基于 develop / b350f1c 修复。

本次故障

普通 uni-app App 中调用 uni.requestPayment({ provider: "toutiao" }) 无法找到该通道, 会报 requestPayment:fail service not found。配置 Scheme 白名单不会注册支付 SDK。 后端 prepayId 不能由前端直接当成 pay_token 拼接收银台 URL。

统一调用

订单页、订单补付、充值使用 utils/douyin-pay.js 的 appDypayFun(payment),传入 /order/payment 等接口的 data 对象(含 orderNum、douyin),不要传整个 HTTP response。 utils/payUtils.js 保留 douyinPayFun(douyin, orderId, orderNum) 兼容入口, 也委托给同一原生调用与查单逻辑。

适配器支持 douyin 的旧扁平字段以及新 orderInfo 对象/JSON, 最终传给 SDK 的字段为 appid、partnerid、prepayid、package、noncestr、timestamp、sign。 签名仍由服务端产生,前端不自行签名或改写签名字段。

uni_modules/tb-douyin-pay 是本地源码 UTS 插件,不是 uni.requestPayment provider; 无需将它添加成 manifest 的 toutiao 支付通道。 Android 原生依赖由 utssdk/app-android/config.json 声明; iOS SDK 位于 utssdk/app-ios/Frameworks/DypaySDK.xcframework。 需要将整个插件目录纳入版本控制,不能只提交 utils 和页面。

打包与验收

  1. 在支持插件配置的 HBuilderX(插件声明最低 4.36)打开 frontend-app。
  2. 安装对应 UTS 编译与 App 打包扩展,Android 打包需能解析插件 Maven 依赖。
  3. 制作包含本插件的自定义调试基座或完整 Android/iOS 安装包,使用平台登记的包名和签名。
  4. 安装新的原生包。单独更新 WGT 或服务器部署无法给旧安装包加入 SDK。
  5. 从真实订单调用支付;测试页也可粘贴 /order/payment 成功返回 JSON。
  6. 覆盖成功、取消、网络失败、重复点击、回前台、冷启动恢复场景。

仅服务端 /payment/queryResult 返回 state=2 才提示支付成功。 SDK 成功或跳回 App 本身不作为到账依据。结果未知时保留待查订单并阻止重复发起。 人工续付接口返回 paid 时仍走原查单接口,以便补同步业务订单;closed 只解除本地待查记录,不标记成功。 测试页不再提供模拟 pay_token 链接,不在页面日志输出完整签名。 H5/小程序不会调用原生 App SDK。

验证边界

2026-09-09 实测:14 项自动化测试通过,6 个 Vue 文件的 SFC/脚本/模板检查通过。 HBuilderX 5.24 执行 App appResource 导出成功,产物位于 unpackage/resources, 包含 tb-douyin-pay 的 Android 原生源码/配置和 iOS SDK 源码/框架。 编译有既有 CSS Autoprefixer 注释警告,不影响此次资源导出。 这次没有构建、安装完整 APK/IPA,也没有进行真机交易。

npm run test:douyin 覆盖参数适配、旧调用入口、服务端确认、重复支付及异常恢复。 这些自动化测试使用模拟 SDK/接口,不会产生真实交易,不能替代完整原生包与真机验收。

关闭 App 后继续未支付订单(2026-09-09 补丁)

旧实现把保存的订单一直当作“结果未知”,只查单不允许重新打开收银台。 现在冷启动仍然只查单;用户再次点击抖音支付时,调用 POST /payment/douyin/resume(表单参数 orderNum),由当前登录身份校验订单归属。

  • 服务端确认 NOTPAY 且本地订单仍可支付、未过期:复用原支付订单号、金额、到期时间, 获取新的 SDK 签名参数;用户确认“继续上一笔支付”后打开收银台。
  • 返回 paid 或 closed:不调 SDK,只清理该账户对应的本地待查记录。
  • USERPAYING、未知渠道状态、查单失败、续付接口不可用:仍保留记录,不创建新支付。
  • 取消弹窗、账号切换、重复点击:不会发起第二笔支付。
  • 只保存原订单号,不缓存签名;兼容修复前已保存的订单号,覆盖购物及充值。

接口返回 data = { state, orderNum, payment },其中 state 为 unpaid/paid/closed/pending, 只有 unpaid 才含 payment(与原 /order/payment 的 SDK 参数结构一致)。 续付接口不创建支付订单、不重新扣抵扣券,也不会自动取消、退款或更改订单到期时间。

发布顺序:先部署后端续付接口,再更新 App。只更新 App 无法解除旧后端的锁定。 本次无 SQL、无新增密钥配置。现有原生插件未变化,但仍需使用已包含 SDK 的安装包。

本补丁涉及 App 的 utils/douyin-pay.js、tests/douyin-integration.test.cjs 及本文档; 后端涉及 DouyinPaymentResumeService、DouyinPayResumeDto、DouyinPayService、 QueryOrderResult、PayController 和 DouyinPaymentResumeServiceTest。 本地验证:npm run test:douyin 的 22 项测试通过;后端 mvn -o -pl hashmall-frontend -am test -Dtest=DouyinPaymentResumeServiceTest,DouyinPayInfoDtoTest,DouyinAppPaySignerTest -Dsurefire.failIfNoSpecifiedTests=false 编译成功,所选 10 项测试通过(含新增 7 项续付测试)。 提交目标:后端 hashmall/hashmall 的 dev_douyin_tmp,App 为 charles/frontend-app 的 dev_codex。 尚未部署或进行真机实付;以上测试均使用模拟渠道,不产生真实交易。