01_微信小程序支付
【支付流程】
1.小程序内调用登录接口,获取到用户的openid(我们这一步骤让前端去获取)
2.服务端代码这边生成订单
3.服务端调用支付统一下单的api
4.服务端将再次签名,返回5个参数(前端得到数据后可以调起支付)
5.微信后台会回调我们服务端,我们通过回调更新订单状态
6.前端也会调用服务端订单查询接口,服务端查询订单状态(防止微信回调这边的一个时间差),如果成功了,在这个接口里会向用户发送一个小程序的模板消息(会消耗一个第3步的prepay_id,后续写模板消息的时候会说)
[ 概述 ]
重点是步骤3和4,特别是签名那块的格式要求务必按照微信的要求来。
【流程详解】
【 1.统一下单 】
目的:在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付。
接口:
https://api.mch.weixin.qq.com/pay/unifiedorder
请求参数
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
小程序ID | appid | 是 | String(32) | wxd678efh567hg6787 | 微信分配的小程序ID |
商户号 | mch_id | 是 | String(32) | 1230000109 | 微信支付分配的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB" |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,长度要求在32位以内。推荐随机数生成算法 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 通过签名算法计算得出的签名值,详见签名生成算法 |
签名类型 | sign_type | 否 | String(32) | MD5 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 |
商品描述 | body | 是 | String(128) | 腾讯充值中心-QQ会员充值 |
商品简单描述,该字段请按照规范传递,具体请见参数规定 |
商品详情 | detail | 否 | String(6000) | 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明” | |
附加数据 | attach | 否 | String(127) | 深圳分店 | 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 |
商户订单号 | out_trade_no | 是 | String(32) | 20150806125346 | 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号 |
标价币种 | fee_type | 否 | String(16) | CNY | 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型 |
标价金额 | total_fee | 是 | Int | 88 | 订单总金额,单位为分,详见支付金额 |
终端IP | spbill_create_ip | 是 | String(16) | 123.12.12.123 | APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 |
交易起始时间 | time_start | 否 | String(14) | 20091225091010 | 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 |
交易结束时间 | time_expire | 否 | String(14) | 20091227091010 |
订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则 建议:最短失效时间间隔大于1分钟 |
订单优惠标记 | goods_tag | 否 | String(32) | WXG | 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠 |
通知地址 | notify_url | 是 | String(256) | http://www.weixin.qq.com/wxpay/pay.php | 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 |
交易类型 | trade_type | 是 | String(16) | JSAPI | 小程序取值如下:JSAPI,详细说明见参数规定 |
商品ID | product_id | 否 | String(32) | 12235413214070356458058 | trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。 |
指定支付方式 | limit_pay | 否 | String(32) | no_credit | 上传此参数no_credit--可限制用户不能使用信用卡支付 |
用户标识 | openid | 否 | String(128) | oUpF8uMuAJO_M2pxb1Q9zNjWeS6o | trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。 |
[ 统一下单的注意点 ]
1.签名sign生成算法,详见https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
2.随机字符串nonce_str,可以用加入当前时间戳+随机数的方法来生成
3.交易类型trade_type,小程序是JSAPI
4.统一下单和接下来的再次签名的签名类型sign_type,都统一使用MD5加密,不然可能会出现验证签名失败的情况,注意这是一个大坑。
[简易代码示例]
//第1步:拼接对应的参数
Map<String, String> param = new TreeMap<>();
param.put("appid", Config.MINI_PROGRAM_APP_ID); //像这些值,最好有个微信config类来存
param.put("mch_id", Config.MCH_ID);
param.put("openid", openId);
param.put("attach", orderDesc);
param.put("body", orderDesc);
param.put("detail", orderDesc);
param.put("limit_pay", Config.NO_CREDIT_LIMIT_PAY);
param.put("nonce_str", nonceStr);
param.put("notify_url", Config.UNIFIED_ORDER_NOTIFY_RELATIVE_URL);
param.put("out_trade_no", outTradeNo);
param.put("spbill_create_ip", clientIp);
param.put("total_fee", String.valueOf(totalFee));
param.put("trade_type", tradeType);
param.put("sign_type","MD5");
//第2步:生成签名
StringBuilder urlParam = new StringBuilder();
for (String paramKey : param.keySet()) { //排序
urlParam.append(paramKey).append("=").append(param.get(paramKey)).append("&");
}
String preParam = urlParam.toString().substring(, urlParam.toString().length() - ); //拼接的urlParam最后有一个&,删除
String finalParam = preParam + "&key=" + WeiXinConfig.PRIVATE_KEY; //key不参与排序
String sign = MD5.sign(finalParam, Const.Charset.UTF8).toUpperCase(); //排序后的参数+key 进行MD5签名,并且全部大写 //第三步:方便后续生成xml格式请求体,简易用一个类来封装对应的参数,然后返回
request.setAppId(param.get("appid"));
request.setSign(sign);
//request.set...其他参数,但要额外加一个成员变量存储sign
[ 返回结果 ]
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
返回状态码 | return_code | 是 | String(16) | SUCCESS |
SUCCESS/FAIL 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 |
返回信息 | return_msg | 否 | String(128) | 签名失败 |
返回信息,如非空,为错误原因 签名失败 参数格式校验错误 |
以下字段在return_code为SUCCESS的时候有返回
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
小程序ID | appid | 是 | String(32) | wx8888888888888888 | 调用接口提交的小程序ID |
商户号 | mch_id | 是 | String(32) | 1900000109 | 调用接口提交的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 自定义参数,可以为请求支付的终端设备号等 |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 微信返回的随机字符串 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 微信返回的签名值,详见签名算法 |
业务结果 | result_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL |
错误代码 | err_code | 否 | String(32) | SYSTEMERROR | 详细参见下文错误列表 |
错误代码描述 | err_code_des | 否 | String(128) | 系统错误 | 错误信息描述 |
以下字段在return_code 和result_code都为SUCCESS的时候有返回
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
交易类型 | trade_type | 是 | String(16) | JSAPI | 交易类型,取值为:JSAPI,NATIVE,APP等,说明详见参数规定 |
预支付交易会话标识 | prepay_id | 是 | String(64) | wx201410272009395522657a690389285100 | 微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时 |
二维码链接 | code_url | 否 | String(64) | URl:weixin://wxpay/s/An4baqw | trade_type为NATIVE时有返回,用于生成二维码,展示给用户进行扫码支付 |
【2.再次签名】
统一下单后,需要再次签名后,才能将数据返回给前端,不然会出现前端能调起微信支付,但是在支付过程中一直报签名失败的提示。
再次签名无需请求微信后台,只是自己对统一下单的一些参数做一次生成签名而已。
[ 再起签名需要的参数 ]
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
小程序ID | appId | 是 | String | wxd678efh567hg6787 | 微信分配的小程序ID |
时间戳 | timeStamp | 是 | String | 1490840662 | 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 |
随机串 | nonceStr | 是 | String | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,不长于32位。推荐随机数生成算法 |
数据包 | package | 是 | String | prepay_id=wx2017033010242291fcfe0db70013231072 | 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=wx2017033010242291fcfe0db70013231072 |
签名方式 | signType | 是 | String | MD5 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致 |
举例如下:(必须和这里的格式一致,红色部分的也一样!)
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111)
[ 简易代码示例 ]
//第1步:拼接对应的参数
Map<String, String> param = new TreeMap<>();
String nonceStr = WeiXinPayUtils.generateNonceStr(); //专门写了个方法来生成随机字符串
param.put("appId", Config.MINI_PROGRAM_APP_ID);
param.put("package", "prepay_id="+prepayId); //从统一下单响应的结果中取的参数
param.put("nonceStr", nonceStr);
param.put("timeStamp", String.valueOf(System.currentTimeMillis() / )); //秒级别的时间戳,应该毫秒也OK
param.put("signType","MD5"); //小程序,这里和统一下单的签名类型保持一致
//第2步:再次生成签名
StringBuilder urlParam = new StringBuilder();
for (String paramKey : param.keySet()) {
urlParam.append(paramKey).append("=").append(param.get(paramKey)).append("&");
}
String preParam = urlParam.toString().substring(, urlParam.toString().length() - );
String finalParam = preParam + "&key=" + Config.PRIVATE_KEY;
String sign = MD5.sign(finalParam, Const.Charset.UTF8).toUpperCase();
//同理,这里建议做一个响应参数给前端
需要返回给前端的参数有:
response.setSign(sign);
response.setAppId(param.get("appId"));
response.setPrepayId(weiXinUnifiedOrderResponse.getPrepayId); //统一下单的响应数据
response.setNonceStr(param.get("nonceStr"));
response.setTimeStamp(param.get("timeStamp"));
response.setPayWay(Const.PayWay.WEIXIN);
response.setAmount(amount);
response.setSignType("MD5");
response.setOrderNo(outTradeNo);
response.setCodeUrl(weiXinUnifiedOrderResponse.getCodeUrl());//统一下单的响应数据
再次签名之后,前端就可以根据服务端返回的参数发起正常的微信支付了。
【3.前端调微信支付需要的参数(即服务端响应参数必传参数)】
参考文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
前端调用wx.requestPayment(OBJECT)发起微信支付
注意:里面的时间戳timeStamp,nonceStr,package,signType,paySign都是服务端的参数,不是前端自己生成的随机数,注意!!!
要理解微信那边也只是根据这些数据来生成签名,会与我们再次签名生成的签名(即paySign)做一次比较,而这个paySign就是由这几个参数加密生成的,自己做项目时没有清楚意识到这一点,时间戳传给前端有误导致一直签名失败,这里是个坑,要注意。
Object参数说明:
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
timeStamp | String | 是 | 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 |
nonceStr | String | 是 | 随机字符串,长度为32个字符以下。 |
package | String | 是 | 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=* |
signType | String | 是 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致 |
paySign | String | 是 | 签名,具体签名方案参见微信公众号支付帮助文档; |
success | Function | 否 | 接口调用成功的回调函数 |
fail | Function | 否 | 接口调用失败的回调函数 |
complete | Function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
回调结果:
回调类型 | errMsg | 说明 |
---|---|---|
success | requestPayment:ok | 调用支付成功 |
fail | requestPayment:fail cancel | 用户取消支付 |
fail | requestPayment:fail (detail message) | 调用支付失败,其中 detail message 为后台返回的详细失败原因 |
示例代码(前端代码):
wx.requestPayment(
{
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){},
'fail':function(res){
},
'complete':function(res){}
})
【微信小程序官方参考】
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
01_微信小程序支付的更多相关文章
- 微信小程序支付步骤
http://blog.csdn.net/wangsf789/article/details/53419781 最近开发微信小程序进入到支付阶段,一直以来从事App开发,所以支付流程还是熟记于心的.但 ...
- 微信小程序支付及退款流程详解
微信小程序的支付和退款流程 近期在做微信小程序时,涉及到了小程序的支付和退款流程,所以也大概的将这方面的东西看了一个遍,就在这篇博客里总结一下. 首先说明一下,微信小程序支付的主要逻辑集中在后端,前端 ...
- Java 后端微信小程序支付demo (网上说的坑里面基本上都有)
Java 后端微信小程序支付 一.遇到的问题 1. 商户号该产品权限未开通,请前往商户平台>产品中心检查后重试 2.签名错误 3.已经调起微信统一下单接口,可以拿到预支付ID,但是前端支付的时候 ...
- php对接微信小程序支付
前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...
- 微信小程序支付遇到的坑
1,微信公众号支付和微信小程序支付有差异 微信公众号:可以直接跳转走h5的微信支付 微信小程序:在测试环境.沙箱环境使用微信公众号的跳转支付没有问题,在线上存在支付异常 最后商讨的解决方法 openi ...
- 微信小程序支付接入注意点
一.微信支付后台服务器部署 服务器采用ubuntu16.04 + php7.0 + apache2.0. 微信支付后台服务使用了curl 和 samplexml ,因此php.ini配置中必须开启这两 ...
- 微信小程序支付开发之申请退款
微信小程序支付跟微信公众号支付类似,这里不另做记录,如果没有开发过支付,可以查看我关于微信支付的文章 重点记录微信小程序申请退款开发过程中遇到一些坑. 退款接口比支付接口接口多了一个 双向证书 证书介 ...
- 微信小程序支付接入实战
1. 微信小程序支付接入实战 1.1. 需求 最近接到一个小程序微信支付的需求,需要我写后台支持,本着能不自己写就不自己写的cv原则,在网上找到了些第三方程序,经过尝试后,最后决定了这不要脸作者的 ...
- 微信小程序支付异常:requestPayment:fail no permission
今天在调试微信小程序支付时碰到了这个问题,支付参数都正常生成了,在调用 wx.requestPayment 进行支付时遇到了这个报错,查了一下发现是开发者工具中 AppID 写错了,用的 AppID ...
随机推荐
- Java学习笔记01
1.原型设计: 将页面的模块.元素.人机交互的形式,利用线框描述的方法,将产品脱离皮肤状态下更加具体跟生动的进行表达. 2.下面的是使用PowerDesigner进行设计的持久层的层次结构图: 虚线三 ...
- nodemon详解
1.介绍 Nodemon是一个使用工具,它将会见监视源文件中任何的更改并自动重启服务器.Nodemon不会对你的代码产生额外的更改,它只是node命令的替代品.因为当你修改源文件后,如果你用的是原来的 ...
- Linux平台总线设备驱动
1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性. 2. 平台总 ...
- 微信 vue中使用video.js播放m3u8视频,解决安卓自动全屏的问题。
最近一个项目中需要在微信中播放m3u8格式的视频,刚开始用了 vue-video-player 这个插件,在IOS手机体验良好,本以为完事了, 结果安卓手机一点播放就自动全屏,心态略崩.查了资料说是安 ...
- python excel读写数据
python 读取excel内容,包含表格日期处理 # -*- coding: utf- -*- import xlrd #读取excel表格 workbook=xlrd.open_workbook( ...
- HTTP请求的常用方法有哪些
HTTP请求的常用方法有:GET方法.POST方法.HEAD方法.PUT方法.DELETE方法.CONNECT方法.OPTIONS方法.TRACE方法.下面本篇文章就给大家介绍具体介绍一下HTTP请求 ...
- Python处理文件和查漏
import os,re def change_filename(root): ''' 批量修改excel文件名 ''' for root_,_, fs in os.walk(root): for f ...
- Fiddler模拟发送post请求
fiddler在进行接口测试时,会模拟post请求,发送不同的请求参数,返回不同的结果,今天我们就来分享一下,怎么用Fiddler工具模拟post请求: 打开Fiddler工具,在右侧点击“compo ...
- eclipse相关问题汇总
安装subclipse svn工具: 配置JavaHL:http://subclipse.tigris.org/wiki/JavaHL#head-5bf26515097c3231c1b04dfdb22 ...
- 存储器的保护(二)——《x86汇编语言:从实模式到保护模式》读书笔记19
接着上一篇博文说. 5.代码段执行时的保护 每个代码段都有自己的段界限.同栈段一个道理,有效界限和G位相关. G=0:有效界限 = 描述符中的段界限 G=1:有效界限 = 描述符中的段界限值 * 0x ...