查看: 231|回复: 3

鸿蒙 uni-app x 支付接入:配置项与错误码排障

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙上跑 uni-app x 支付,真正卡住人的往往不是下单逻辑,而是几个平台专属参数:少一个 dataType、漏配一个模块,代码在 Android/iOS 上跑得好好的,到鸿蒙上就直接报错。本文围绕 uni.requestPayment 在鸿蒙平台的接入流程,把兼容性边界、manifest 配置、orderInfo 格式、错误码处理和常见坑位一次讲清。

一、先确认 requestPayment 在鸿蒙上的适用边界

uni.requestPayment 是 uni-app x 提供的统一支付 API,一套代码同时对接微信支付与支付宝支付,不需要分别集成两家的原生 SDK,框架层抹平了差异。但它的平台支持有明确范围:Web 端不支持,鸿蒙平台从 4.61 版本开始支持。也就是说,低于 4.61 的鸿蒙环境根本调不起来,遇到“支付无反应”时先核对基础版本,再去怀疑业务代码——这个版本问题很容易让人白排查半天。

API 签名为 uni.requestPayment(options: RequestPaymentOptions)。成功回调返回 RequestPaymentSuccess,失败回调返回 RequestPaymentFail,其中包含 errCode、errMsg 等字段,后续的错误分支判断都基于它。

二、把支付链路摆正:前端只负责“调起”

支付不是调用一次 API 就结束的事,完整链路分为七步:

1. 前端向自建服务端发起下单请求;
2. 服务端调用微信/支付宝的统一下单接口,生成预支付订单;
3. 服务端把 orderInfo 返回给前端;
4. 前端调用 uni.requestPayment,传入 orderInfo,调起微信/支付宝完成支付;
5. 微信/支付宝异步通知服务端支付结果;
6. 服务端收到通知后更新订单状态;
7. 前端通过轮询或 WebSocket 获取最终支付结果。

关键认知是:success 回调只代表客户端这一侧调起并完成了支付动作,订单是否真正到账要以服务端收到的异步通知为准。把发货、开通权益的逻辑放在前端回调里,是典型的架构错误。

三、鸿蒙专属配置三件事

第一件是 manifest 模块配置。鸿蒙平台需要在 manifest.json 的 modules 中声明 uni-payment 模块,不配置调用支付会直接报错。HBuilderX 4.71 及以上版本支持可视化界面配置,配置路径为 manifest.json → modules → uni-payment;也可以手动写入:
  1. {
  2.   "modules": {
  3.     "uni-payment": {}
  4.   }
  5. }
复制代码

第二件是 dataType 必须传 'text'。这是鸿蒙平台与其他平台差异最大的一处,同样是 requestPayment 调用,Android 和 iOS 不传 dataType 可以正常跑,鸿蒙平台会报错。所以这份代码从其他端迁过来时,最容易在这里翻车。
  1. uni.requestPayment({
  2.   provider: 'wxpay',
  3.   orderInfo: orderInfo,
  4.   dataType: 'text', // 鸿蒙平台必须传
  5. })
复制代码

第三件是应用标识取 bundleName。鸿蒙平台获取应用标识用的是 bundleName,既不是 packageName 也不是 bundleId,用条件编译区分平台可以避免取值错误:
  1. const getAppId = (): string => {
  2.   let appId = ''
  3.   // #ifdef APP-ANDROID
  4.   appId = uni.getAppBaseInfo().packageName ?? ''
  5.   // #endif
  6.   // #ifdef APP-IOS
  7.   appId = uni.getAppBaseInfo().bundleId ?? ''
  8.   // #endif
  9.   // #ifdef APP-HARMONY
  10.   appId = uni.getAppBaseInfo().bundleName ?? ''
  11.   // #endif
  12.   return appId
  13. }
复制代码

四、orderInfo 的格式随渠道变化

同一个 requestPayment,不同渠道对 orderInfo 的形态要求不一样。微信支付要求 JSON 字符串,前端拿到服务端返回的对象后要 JSON.stringify;支付宝要求以 & 连接的 key=value 字符串。

微信支付的 orderInfo 形如:
  1. {
  2.   "appid": "wxd569c7238830733b",
  3.   "noncestr": "6N47VnR42bqIm4xq",
  4.   "package": "Sign=WXPay",
  5.   "partnerid": "1230636401",
  6.   "prepayid": "wx26174750316675ac54b89c224eb3250000",
  7.   "timestamp": 1711446470,
  8.   "sign": "EE987459B9CFF6462462147130110D31"
  9. }
复制代码

支付宝的 orderInfo 形如:
  1. app_id=2015052600090779&biz_content=...&method=alipay.trade.app.pay&sign_type=RSA2&sign=...
复制代码

对应的调起写法,微信支付如下(注意金额在服务端下单时以“分”为单位):
  1. const requestWxpay = () => {
  2.   uni.request({
  3.     url: 'https://your-server.com/api/create-order',
  4.     method: 'POST',
  5.     data: {
  6.       productId: 'product_001',
  7.       amount: 99, // 金额,单位分
  8.     },
  9.     success: (res) => {
  10.       const orderInfo = JSON.stringify(res.data)
  11.       uni.requestPayment({
  12.         provider: 'wxpay',
  13.         orderInfo: orderInfo,
  14.         dataType: 'text', // 鸿蒙平台需要传 'text'
  15.         success: (res: RequestPaymentSuccess) => {
  16.           console.log('支付成功:', res.data)
  17.         },
  18.         fail: (err: RequestPaymentFail) => {
  19.           console.log('支付失败:', err.errCode, err.errMsg)
  20.         }
  21.       })
  22.     }
  23.   })
  24. }
复制代码

支付宝则直接使用服务端返回的字符串,金额在下单时按“元”为单位:
  1. const requestAlipay = () => {
  2.   uni.request({
  3.     url: 'https://your-server.com/api/create-alipay-order',
  4.     method: 'POST',
  5.     data: {
  6.       productId: 'product_001',
  7.       amount: 0.01, // 金额,单位元
  8.     },
  9.     success: (res) => {
  10.       const orderInfo = res.data as string
  11.       uni.requestPayment({
  12.         provider: 'alipay',
  13.         orderInfo: orderInfo,
  14.         dataType: 'text',
  15.         success: (res: RequestPaymentSuccess) => {
  16.           console.log('支付成功:', res.data)
  17.         },
  18.         fail: (err: RequestPaymentFail) => {
  19.           console.log('支付失败:', err.errCode, err.errMsg)
  20.         }
  21.       })
  22.     }
  23.   })
  24. }
复制代码

如果同一份代码还要在微信小程序里跑,orderInfo 的传法不同,需要用条件编译区分:小程序端用展开语法把订单字段平铺进参数,App 端仍然传 JSON 字符串并带上 dataType。
  1. uni.requestPayment({
  2.   provider: 'wxpay',
  3.   // #ifdef MP-WEIXIN
  4.   ...orderData,
  5.   // #endif
  6.   // #ifdef APP
  7.   orderInfo: JSON.stringify(orderData),
  8.   dataType: 'text',
  9.   // #endif
  10.   success: (res: RequestPaymentSuccess) => {},
  11.   fail: (err: RequestPaymentFail) => {}
  12. })
复制代码

五、错误码分支要提前写好

支付失败时把 errMsg 直接抛给用户没有意义,需要按 errCode 分类处理。实际开发中最常遇到的是这几个:700601 表示用户取消支付,应静默或轻提示,不要当成异常;700604 表示设备上未安装微信,提示语要引导安装;700602 表示网络连接出错;701100 表示订单支付失败。
  1. fail: (err: RequestPaymentFail) => {
  2.   if (err.errCode == 700601) {
  3.     paymentError.value = '用户取消支付'
  4.   } else if (err.errCode == 700604) {
  5.     paymentError.value = '请先安装微信'
  6.   } else if (err.errCode == 700602) {
  7.     paymentError.value = '网络连接出错'
  8.   } else if (err.errCode == 701100) {
  9.     paymentError.value = '订单支付失败'
  10.   } else {
  11.     paymentError.value = err.errMsg
  12.   }
  13. }
复制代码

六、调起之前先做两项可用性检查

第一项是检查设备上有哪些支付通道。uni.getProvider 传 service: 'payment' 可以拿到 providers 列表,逐项打印 id 和 name,据此决定支付方式列表是否要置灰。
  1. uni.getProvider({
  2.   service: 'payment',
  3.   success: (res) => {
  4.     res.providers.forEach((provider) => {
  5.       console.log('支付通道:', provider.id, provider.name)
  6.     })
  7.   },
  8.   fail: (err) => {
  9.     console.log('获取支付通道失败:', err.errMsg)
  10.   }
  11. })
复制代码

第二项是检查微信客户端是否安装。微信支付依赖微信客户端,鸿蒙设备上没装微信,调用会直接返回 700604。可以在发起前判断 provider 是否为 UniPaymentWxpayProvider 实例,并读取 isWeChatInstalled:
  1. const provider = res.providers.find((item): boolean => {
  2.   return item.id == 'wxpay'
  3. })
  4. if (provider != null && provider instanceof UniPaymentWxpayProvider) {
  5.   if (!provider.isWeChatInstalled) {
  6.     uni.showToast({ title: '请先安装微信', icon: 'none' })
  7.     return
  8.   }
  9. }
复制代码

七、一个可复用的支付页结构

把上面的点落成一个页面,核心是三个状态:选中的支付渠道、支付中标记 isPaying、支付结果与错误信息。点击支付时先 showLoading 拉取订单信息,按渠道决定 orderInfo 的序列化方式,再调 requestPayment,在 complete 中复位 isPaying,避免用户重复点击导致的重复下单。
  1. const startPayment = () => {
  2.   if (isPaying.value) {
  3.     return
  4.   }
  5.   isPaying.value = true
  6.   paymentResult.value = ''
  7.   paymentError.value = ''
  8.   uni.showLoading({ title: '获取订单信息...' })
  9.   uni.request({
  10.     url: 'https://your-server.com/api/create-order',
  11.     method: 'POST',
  12.     data: {
  13.       productId: 'product_001',
  14.       amount: 99,
  15.       payMethod: selectedPayMethod.value,
  16.     },
  17.     success: (res) => {
  18.       uni.hideLoading()
  19.       let orderInfo = ''
  20.       if (selectedPayMethod.value == 'wxpay') {
  21.         orderInfo = JSON.stringify(res.data)
  22.       } else {
  23.         orderInfo = res.data as string
  24.       }
  25.       uni.requestPayment({
  26.         provider: selectedPayMethod.value,
  27.         orderInfo: orderInfo,
  28.         dataType: 'text', // 鸿蒙平台必须传
  29.         success: (res: RequestPaymentSuccess) => {
  30.           paymentResult.value = 'success'
  31.         },
  32.         fail: (err: RequestPaymentFail) => {
  33.           paymentResult.value = 'fail'
  34.           paymentError.value = err.errMsg
  35.         },
  36.         complete: () => {
  37.           isPaying.value = false
  38.         }
  39.       })
  40.     },
  41.     fail: () => {
  42.       uni.hideLoading()
  43.       isPaying.value = false
  44.       uni.showToast({ title: '获取订单信息失败', icon: 'none' })
  45.     }
  46.   })
  47. }
复制代码

模板层只需渲染商品信息、支付方式单选列表和一个在 isPaying 时禁用的支付按钮,支付结果区域根据 paymentResult 决定文案与颜色。

八、鸿蒙接入自检清单

把踩过的坑收敛成一份清单,适配时逐条对照即可:

1. 鸿蒙基础版本是否达到 4.61,低于该版本 requestPayment 不可用;
2. manifest.json 的 modules 是否声明了 uni-payment,HBuilderX 4.71+ 可直接在界面勾选;
3. 每次调用是否都带上 dataType: 'text';
4. 微信支付是否用了 JSON 字符串形式的 orderInfo,支付宝是否用了 & 连接的 key=value 字符串;
5. 取应用标识是否使用 bundleName;
6. 是否在调用前判断了微信客户端安装情况,并对 700601 做了“取消支付”的独立分支;
7. 订单最终状态是否以服务端异步通知为准,而非客户端回调。

九、架构取舍:统一 API 还是原生 IAP Kit

如果服务端使用 uniCloud,可以配合云端统一支付服务 uniPay,把 App、微信小程序、支付宝小程序的服务端支付逻辑统一封装,前端 requestPayment 对接云端 uniPay,链路更短。另一种路线是鸿蒙原生开发,直接接入华为支付 IAP Kit,需要自行处理 iap.createPurchaseIntent 等 IAP Kit 的回调与参数配置,复杂度明显高于统一 API。选择依据很直接:应用只在鸿蒙单端发布、且需要华为支付能力时,原生方案更合适;否则用 uni.requestPayment 一套代码覆盖微信与支付宝,维护成本更低。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 uni-app x 支付接入:配置项与错误码排障

感谢分享,这篇把鸿蒙支付的几个关键点说得挺清楚。dataType 必须传 'text' 这个坑我们也踩过,Android 和 iOS 上跑通的代码直接搬到鸿蒙,就会在 requestPayment 这里失败,而且报错信息不太直观,确实容易让人查很久。还有版本边界那段提醒得很实在,低于 4.61 先核对基础版本,能避免在业务代码上反复排查。 我补充两个实际经验,仅供参考:一是 manifest 里加了 uni-payment 之后,最好重新打自定义基座再测,有时改了配置但基座没更新,会表现得像“配置没生效”;二是 errCode 分支建议按渠道分别记录日志,微信和支付宝的返回码含义不完全一样,真出问题时能省很多事。 另外特别认同“success 回调只代表客户端调起完成”这一点,订单状态一定以服务端异步通知为准,前端只用来做结果展示和轮询兜底。整篇从链路到配置再到错误码,顺序很清楚,先收藏了。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 uni-app x 支付接入:配置项与错误码排障

感谢分享,这篇整理得很实用。特别是鸿蒙上 dataType 必须传 text、manifest 里要声明 uni-payment 模块这两点,从 Android/iOS 迁过来的项目很容易忽略,往往就是“代码看着没问题但一调就报错”。用条件编译取 bundleName 的写法也很清晰,省得平台标识取错。另外把“success 只代表客户端调起成功、到账以服务端异步通知为准”单独拎出来讲很有必要,能避免不少把发货逻辑写在前端回调里的坑。 不过帖子好像到微信支付那段 success 回调就截断了,后面支付宝的调起写法和错误码处理、常见坑位部分没看到。如果方便的话可以补一下 errCode 的分支判断和排查思路,这部分应该也是大家最想看的。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 uni-app x 支付接入:配置项与错误码排障

这篇整理很实用,排障顺序也很清楚:先确认鸿蒙环境版本是否满足 4.61,再检查 manifest 的 modules 里有没有声明 uni-payment,接着确认 requestPayment 是否传了 dataType: 'text',最后再看 orderInfo 是否按微信和支付宝各自要求的格式处理。应用标识用 bundleName 并通过条件编译区分平台这个点也容易被忽略。还有 success 回调只代表客户端调起并完成支付动作、最终是否到账要以服务端异步通知为准,这一点很关键,能避免把发货或开通权益逻辑放错地方。整体按这个思路排查,会比一上来怀疑业务代码高效很多。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-30 03:36 , Processed in 0.029001 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部