# uni.requestPayment(OBJECT) 支付 uni.requestPayment是一个统一各平台的客户端支付API,不管是在某家小程序还是在App中,客户端均使用本API调用支付。 本API运行在各端时,会自动转换为各端的原生支付调用API。 注意支付不仅仅需要客户端的开发,还需要服务端开发。虽然客户端API统一了,但各平台的支付申请开通、配置回填仍然需要看各个平台本身的支付文档。 比如微信有App支付、小程序支付、H5支付等不同的申请入口和使用流程,对应到uni-app,在App端要申请微信的App支付,而小程序端则申请微信的小程序支付。 如果服务端使用[uniCloud](https://uniapp.dcloud.io/uniCloud/README),那么官方提供了[uniPay](https://uniapp.dcloud.io/uniCloud/unipay)云端统一支付服务,把App、微信小程序、支付宝小程序里的服务端支付开发进行了统一的封装。 前端统一的`uni.requestPayment`和云端统一的`uniPay`搭配,可以极大提升支付业务的开发效率,强烈推荐给开发者使用。`uniPay`的文档另见:[https://uniapp.dcloud.io/uniCloud/unipay](https://uniapp.dcloud.io/uniCloud/unipay) **平台差异说明** |App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|飞书小程序|QQ小程序|快手小程序|京东小程序| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |√|[说明](/api/plugins/payment?id=h5-payment)|√|√|√|√|x|√|√|x| **OBJECT 参数说明** |参数名|类型|必填|说明|平台差异说明| |:-|:-|:-|:-|:-| |provider|String|是|服务提供商,通过 [uni.getProvider](/api/plugins/provider) 获取。|| |orderInfo|String/Object|是|订单数据,[注意事项](/api/plugins/payment?id=orderinfo)|App、支付宝小程序、百度小程序、字节跳动小程序| |timeStamp|String|微信小程序必填|时间戳从1970年1月1日至今的秒数,即当前的时间。|微信小程序| |nonceStr|String|微信小程序必填|随机字符串,长度为32个字符以下。|微信小程序| |package|String|微信小程序必填|统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=xx。|微信小程序| |signType|String|微信小程序必填|签名算法,应与后台下单时的值一致|微信小程序| |paySign|String|微信小程序必填|签名,具体签名方案参见 [微信小程序支付文档](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3)|微信小程序| |bannedChannels|Array<String>|否|需要隐藏的支付方式,详见 [百度小程序支付文档](https://smartprogram.baidu.com/docs/develop/api/open_payment/#requestPolymerPayment/)|百度小程序| |service|Number|字节跳动小程序必填|固定值:5(拉起小程序收银台)开发者如果不希望使用字节跳动小程序收银台,service设置为3/4时,可以直接拉起微信/支付宝进行支付:service=3: 微信API支付,不拉起小程序收银台;service=4: 支付宝API支付,不拉起小程序收银台。其中service=3、4,仅在1.35.0.1+基础库(头条743+)支持|字节跳动小程序| |_debug|Number|否|仅限调试用,上线前去掉该参数。_debug=1时,微信支付期间可以看到中间报错信息,方便调试|字节跳动小程序| |getOrderStatus|Function|字节跳动小程序必填|商户前端实现的查询支付订单状态方法(该方法需要返回个Promise对象)。 service=3、4时不需要传。|字节跳动小程序| |success|Function|否|接口调用成功的回调|| |fail|Function|否|接口调用失败的回调函数|| |complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)| | 微信小程序 `signType` 说明 |合法值 |说明 | |:-|:-| |MD5 |仅在 v2 版本接口适用 | |HMAC-SHA256|仅在 v2 版本接口适用 | |RSA |仅在 v3 版本接口适用 | ### 注意事项 - APP端,如果你的应用在用户完成支付后;立即给支付的用户push消息通知。会与前端支付回调相互冲突,请延迟执行推送。 - 字节跳动小程序支付接口调整使用时请注意[发起头条支付](https://developer.toutiao.com/dev/cn/mini-app/develop/open-capacity/payment/pay) ### orderInfo 注意事项@orderInfo 1. 百度小程序的 orderInfo 为 Object 类型,详细的数据结构,参考:[百度收银台支付](https://smartprogram.baidu.com/docs/develop/api/open_payment/#requestPolymerPayment/)。 2. 支付宝小程序的 orderInfo(支付宝的规范为 tradeNO) 为 String 类型,表示支付宝交易号。 3. 字节跳动小程序的 orderInfo 为 Object 类型,详见:[发起头条支付](https://developer.toutiao.com/dev/cn/mini-app/develop/open-capacity/payment/pay) 4. 由于头条新版支付接口要求版本较高,在不支持新版支付接口的情况下仍会对应旧版支付接口,此时 orderInfo 对应字节跳动小程序 data, 详见:[头条支付旧版接口](https://developer.toutiao.com/dev/cn/mini-app/develop/open-capacity/payment/requestpayment-deprecated)。用户可以使用 tt.pay 判断是否支持新版接口。另外需要注意字节跳动小程序在`1.35.0+`版本基础库支持了 canIUse ,在`1.19.4+`版本基础库支持了新版支付接口 tt.pay ,所以应避免使用 canIUse 判断是否为新版接口。 5. App端,支付宝支付 orderInfo 为 String 类型。 6. App端,微信支付 orderInfo 为 Object 类型。 6. App端,苹果应用内支付 orderInfo 为Object 类型,{productid: 'productid'}。 ## H5 平台@h5-payment - 普通浏览器平台的支付,仍然是常规web做法。uni-app未封装。 - 在普通浏览器里也可以调起微信进行支付,这个在微信叫做H5支付,此功能未开放给普通开发者,需向微信单独申请,[详见](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1) - 微信内嵌浏览器运行H5版时,可通过js sdk实现微信支付,需要引入一个单独的js,[详见](https://ask.dcloud.net.cn/article/35380) **各平台支持的支付情况说明** - 微信小程序里只支持微信小程序支付,在 [微信商户平台](https://pay.weixin.qq.com) 申请支付时,选择公众号支付。 - App 里支持微信sdk支付、支付宝sdk支付、苹果iap应用内支付,在各平台申请支付时选择 App 支付。 - 其他支付(如银联)请使用web-view组件以H5方式实现。 - 支付宝小程序只支持支付宝支付。 - 百度小程序为百度支付,其二次封装了度小满、支付宝、微信支付。 - Hello uniapp 里演示了各种支付。 ## App平台支付流程 流程:支付平台功能申请 -> ``manifest.json`` 里配置支付参数 -> ``uni-app`` 里调用 API 进行支付 ### manifest.json里配置相关参数 1. 在`manifest.json - App模块权限选择` 中勾选 payment(支付) 2. 在 `manifest.json - App SDK配置` 中,勾选需要的支付平台,目前有微信支付、支付宝支付、苹果应用内支付(IAP),其中微信支付需要填写从微信开放平台获取的AppID ![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/b803e140-4f1d-11eb-8ff1-d5dcf8779628.png) 3. 这些配置需要打包生效,真机运行仍然是HBuilder基座的设置,可使用自定义基座调试。离线打包请参考离线打包文档在原生工程中配置。 4. 配置并打包后,通过`uni.getProvider`可以得到配置的结果列表,注意这里返回的是manifest配置的,与手机端是否安装微信、支付宝无关。 ## App支付 ### 示例 ```javascript uni.requestPayment({ provider: 'alipay', orderInfo: 'orderInfo', //微信、支付宝订单数据 【注意微信的订单信息,键值应该全部是小写,不能采用驼峰命名】 success: function (res) { console.log('success:' + JSON.stringify(res)); }, fail: function (err) { console.log('fail:' + JSON.stringify(err)); } }); ``` ## 支付宝App支付 ### 申请流程 登录支付宝账号,创建应用接入支付宝App支付能力,包括以下步骤: - 创建应用(获取appid) - 开通App支付功能 - 配置密钥(获取公钥、私钥) 具体可参考支付宝官方文档: [App支付快速接入](https://docs.open.alipay.com/204/105297/) 如果手机端未安装支付宝,调用时会启动支付宝的wap页面登录,如果已安装相应客户端,会启动其客户端登录。 ## 微信App支付 ### 申请流程 - 到 [微信开放平台](https://open.weixin.qq.com/) 申请移动应用并开通支付功能,申请应用后可以获取 AppID 和 AppSecret 值 - 应用接入 [微信商户平台](https://pay.weixin.qq.com),选择 App 支付 - 开通支付功能后可获取支付业务服务器配置数据:PARTNER(财付通商户号)、PARTNER_KEY(财付通密钥)、PAYSIGNKEY(支付签名密钥) - 需要将从微信开放平台申请的appid,填回到 manifest-App SDK配置-支付-微信支付 中。打包后生效。 具体可参考微信官方文档: [移动应用开发](https://open.weixin.qq.com/cgi-bin/frame?t=home/app_tmpl&lang=zh_CN) 注意微信的App支付、小程序支付、H5支付是不同的体系。微信小程序支付在 [微信商户平台](https://pay.weixin.qq.com) 申请支付时,选择公众号支付;普通浏览器里也可以调起微信进行支付,这个在微信叫做H5支付,此功能未开放给普通开发者,需向微信单独申请,[详见](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1) ### 示例代码 ```javascript uni.requestPayment({ "provider": "wxpay", "orderInfo": { "appid": "wx499********7c70e", // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致 "noncestr": "c5sEwbaNPiXAF3iv", // 随机字符串 "package": "Sign=WXPay", // 固定值 "partnerid": "148*****52", // 微信支付商户号 "prepayid": "wx202254********************fbe90000", // 统一下单订单号 "timestamp": 1597935292, // 时间戳(单位:秒) "sign": "A842B45937F6EFF60DEC7A2EAA52D5A0" // 签名,这里用的 MD5/RSA 签名 }, success(res) {}, fail(e) {} }) ``` ## 苹果应用内支付@iap ### 申请流程 使用苹果开发者账号登录 [App Store Connect](https://appstoreconnect.apple.com),在应用的功能选项卡页面,添加 App 内购项目。注意: - 内购项目的各信息需要填写完整,然后保存,此时内购项目的状态应该是准备提交,当提交应用通过审核后,状态则变为已批准 - 测试时,建议使用测试证书打一个自定义的 iOS 基座进行测试 - 在应用 TestFight 的选项卡添加 App Store Connect 用户,测试支付时可以使用此用户帐号进行测试 ### 支付流程 1. 获取支付通道 (uni.getProvider) 2. 通过支付通道获取产品列表 (iapChannel.requestProduct) 3. 检查是否存在未关闭的订单 (iapChannel.restoreCompletedTransactions, 可选在合适的时机检查) 4. 请求支付,传递产品信息 (uni.requestPayment) 5. 客户端接收苹果返回的支付票据发送到服务器,在服务器请求苹果服务器验证支付是否有效 6. 服务器验证票据有效后在客户端关闭订单 (iapChannel.finishTransaction) HBuilder 3.5.1 之前因自动关闭订单导致某些情况下丢单的问题 HBuilder 3.5.1 + 增加了手动关闭订单参数 `manualFinishTransaction`, 在合适的时机调用 `iapChannel.finishTransaction` 关闭订单 HBuilder 3.5.1+ 开始支持通过 `uni.getProvider` 获取IAP支付通道的方法 ### 获取IAP支付通道 ```js uni.getProvider({ service: 'payment', success: (res) => { const iapChannel = res.providers.find((channel) => { return (channel.id === 'appleiap') }) // 如果 iapChannel 为 null,说明当前包没有包含iap支付模块。注意:HBuilder基座不包含 iap 通道 } }); ``` #### **IAP支付通道相关方法** #### 向苹果服务器获取产品列表 `iapChannel.requestProduct( success, fail)` `success` 回调值类型 `Array` #### 获取苹果服务器已支付且未关闭的交易列表 `iapChannel.restoreCompletedTransactions( success, fail)` `success` 回调值类型 `Array` #### 关闭苹果服务器订单 `iapChannel.finishTransaction(Transaction, success, fail)` 所有 `fail` 回调格式为 `{ errCode: xxx, errMsg: '' }` ### 请求支付 `uni.requestPayment()` ```js uni.requestPayment({ provider: 'appleiap', orderInfo: {}, success: (e) => { // e 类型为 Transaction, 详见下面的描述 } }) ``` ### 参数说明 #### orderInfo |属性|类型|默认值|说明| |:-|:-|:-|:-| |productid|String||产品id,在苹果开发者中心配置| |username|String||透传参数,一般用于标记订单和用户的关系,向苹果服务器二次验证票据时返回此字段| |quantity|Number|1|购买数量,至少大于等于 `1`| |manualFinishTransaction|Boolean|false|3.5.1+ 支持,手动关闭订单,值为 `false` 时支付完成后自动关闭订单,`true`时不关闭订单,需要在合适的时机调用 `finishTransaction` 关闭订单。建议设置为 `true`, 默认值为 `false` 是为了向下兼容| #### Product |属性|类型|说明| |:-|:-|:-| |title|String|产品标题| |description|String|产品描述| |productid|String|产品id,在苹果开发者中心配置| |price|Number|价格| |pricelocal|String|币种,例如: `zh_CN@currency=CNY`| #### Transaction |属性|类型|说明| |:-|:-|:-| |payment|Object|支付信息,详见下面的说明| |transactionDate|String|交易日期,示例 `2022-01-01 08:00:00`| |transactionIdentifier|String|交易唯一标识| |transactionReceipt|String|支付票据,用于在开发者的服务器向苹果的服务器二次验证交易是否有效| |transactionState|String|交易状态码| #### Payment |属性|类型|说明| |:-|:-|:-| |productid|String|产品id| |quantity|String|购买数量| |username|String|透传参数| #### transactionState 值类型:String |值|说明| |:-|:-| |1|交易成功| 注意事项 - 相同订单,重复调用 `restoreCompletedTransactions` 后 `transactionReceipt` 会发生变化,并非唯一值 - 调用 `finishTransaction` 关闭订单可能不会立即生效,取决于苹果的服务器 ### 订单丢失场景 - 用户没有绑定 `AppStore` 支付方式,调用 `uni.requestPayment()` 准备支付,触发失败 `fail` 回调,errCode=2,用户未绑定支付方式,app内支付流程结束。 系统弹出框引导用户绑定支付方式,此过程将跳转到系统应用 `AppStore` 进行绑定支付方式,绑定成功同步支付成功,用户成功付款 ### 示例代码 ```html ``` ```js // uni iap const ProviderType = { IAP: 'iap' } const IapTransactionState = { purchasing: "0", // A transaction that is being processed by the App Store. purchased: "1", // A successfully processed transaction. failed: "2", // A failed transaction. restored: "3", // A transaction that restores content previously purchased by the user. deferred: "4" // A transaction that is in the queue, but its final status is pending external action such as Ask to Buy. }; class Iap { _channel = null; _channelError = null; _productIds = []; _ready = false; constructor({ products }) { this._productIds = products; } init() { return new Promise((resolve, reject) => { this.getChannels((channel) => { this._ready = true; resolve(channel); }, (err) => { reject(err); }) }) } getProduct(productIds) { return new Promise((resolve, reject) => { this._channel.requestProduct(productIds || this._productIds, (res) => { resolve(res); }, (err) => { reject(err); }) }); } requestPayment(orderInfo) { return new Promise((resolve, reject) => { uni.requestPayment({ provider: 'appleiap', orderInfo: orderInfo, success: (res) => { resolve(res); }, fail: (err) => { reject(err); } }); }); } restoreCompletedTransactions(username) { return new Promise((resolve, reject) => { this._channel.restoreCompletedTransactions({ username }, (res) => { resolve(res); }, (err) => { reject(err); }) }); } finishTransaction(transaction) { return new Promise((resolve, reject) => { this._channel.finishTransaction(transaction, (res) => { resolve(res); }, (err) => { reject(err); }); }); } getChannels(success, fail) { if (this._channel !== null) { success(this._channel) return } if (this._channelError !== null) { fail(this._channelError) return } uni.getProvider({ service: 'payment', success: (res) => { this._channel = res.providers.find((channel) => { return (channel.id === 'appleiap') }) if (this._channel) { success(this._channel) } else { this._channelError = { errMsg: 'paymentContext:fail iap service not found' } fail(this._channelError) } } }); } get channel() { return this._channel; } } export { Iap, IapTransactionState } ``` ## PayPal支付 [参考](https://uniapp.dcloud.io/app-payment-paypal) ## Stripe支付 [参考](https://uniapp.dcloud.io/app-payment-stripe) ## Google Pay支付 [参考](https://uniapp.dcloud.io/app-payment-google) ## 微信小程序支付 ```javascript // 仅作为示例,非真实参数信息。 uni.requestPayment({ provider: 'wxpay', timeStamp: String(Date.now()), nonceStr: 'A1B2C3D4E5', package: 'prepay_id=wx20180101abcdefg', signType: 'MD5', paySign: '', success: function (res) { console.log('success:' + JSON.stringify(res)); }, fail: function (err) { console.log('fail:' + JSON.stringify(err)); } }); ``` ## 服务器相关 ### uniCloud开发 - 前端:使用`unicloud.callfunction`调用指定的云函数。 - 服务端:使用[uniPay](https://uniapp.dcloud.io/uniCloud/unipay),该服务对应的演示工程在插件市场:[https://ext.dcloud.net.cn/plugin?id=1835](https://ext.dcloud.net.cn/plugin?id=1835),此示例为完整的前后端支付演示,使用`uniPay`可极快的完成支付业务开发。 ### php开发 - 前端:使用 ``uni.request`` 请求服务端接口,得到订单数据,使用 ``uni.requestPayment`` 向支付平台发起支付请求,拉起支付平台的客户端进行支付。在hello uni-app里详细代码。 - 服务端:PHP可参考 [https://github.com/dcloudio/H5P.Server/tree/master/payment](https://github.com/dcloudio/H5P.Server/tree/master/payment)。 ## FAQ - Q:如何使用ping++等聚合支付 A:uni-app的js API 已经完成跨端统一,客户端无需使用三方聚合支付。如果服务器选择`uniCloud`,也无需三方聚合支付。如果服务端使用php、java等传统服务器开发,可以在服务端使用三方聚合支付。 - Q:App端如何使用其他支付,比如银联、PayPal。 A:App 3.4+ 已支持 PayPal,App 3.4 以前的版本使用下面的方案 1. 可以在web-view组件里使用它们的wap版支付; 2. 可以集成原生sdk,插件市场均有,[详见](https://ext.dcloud.net.cn/search?q=%E6%94%AF%E4%BB%98)。也可以自行开发原生插件,开发文档见[https://ask.dcloud.net.cn/article/35428](https://ask.dcloud.net.cn/article/35428)。 - Q:Appstore审核报PGPay SDK不允许上架的问题 A:数字类产品(比如购买会员等不需要配送实物的商品),Apple规定必须使用苹果IAP应用内支付,给Apple分成30%。打包的时候不要勾选微信或支付宝等其他支付方式。如果你提交的包里包含了微信支付宝等支付的sdk,即使没使用,Appstore也会认为你有隐藏方式,以后会绕过IAP,不给Apple分成,因此拒绝你的App上线。云打包时,manifest里选上支付模块,但sdk配置里去掉微信支付和支付宝支付。很多开发者的Android版是包含微信和支付宝支付的,此时注意分开判断。详见[https://ask.dcloud.net.cn/article/36447](https://ask.dcloud.net.cn/article/36447)