一文快速实现微信公众号支付功能
微信支付类型
刷卡支付:用户打开微信钱包的刷卡的界面,商户扫码后提交完成支付。
公众号支付:用户在微信内进入商家H5页面,页面内调用JSSDK完成支付
扫码支付:用户打开"微信扫一扫“,扫描商户的二维码后完成支付
APP支付:商户APP中集成微信SDK,用户点击后跳转到微信内完成支付
H5支付:用户在微信以外的手机浏览器请求微信支付的场景唤起微信支付
小程序支付:用户在微信小程序中使用微信支付的场景
网页授权:https://www.jianshu.com/p/94b0e53cccc3
微信JS-SDK:https://www.jianshu.com/p/b3c4450f845e
公众号支付相关配置
1.需要一个已经进行微信认证的公众号
2.该公众号需要开通微信支付功能
3.到微信商户平台https://pay.weixin.qq.com 注册一个商户账号,并关联你的公众号,如果需要实现小程序支付的,需要关联小程序。
微信支付涉及的私密数据比较多,不允许使用natapp,花生壳之类的内网穿透工具实现,需要有正式的服务器环境,并且要注册域名,不能使用IP。
比如:http://www.baidu.com
5.相关配置
5.1 配置支付授权目录,登录商户平台——>产品中心——>开发配置
图中配置的例子,代表在项目根路径下,以及web目录下的页面都有支付权限,如果不在该路径的页面,则无法调用支付功能。
若页面地址为:http://mywexx.xxxx.com/web/pay.html
则需要配置为:http://mywexx.xxxx.com/web/
配置网页授权域名:主要用于获取用户的openId,需要识别这是哪个人。
若对openID不了解的同学可先参考微信公众号开发文档:https://mp.weixin.qq.com/wiki
公众号支付实现流程
当用户第一次打开主页,默认没有code参数,此时会先重定向到获取授权的地址
(如果只需要获取openid,可以使用scope为snsapi_base静默授权的方式)
经过授权地址再重定向到我们的index.do时,会带上code参数,此时即可通过接口获取用户的openid
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
[ / size][ / align][align = left][size = 17 px]@Controller[ / size][ / align][align = left][size = 17 px]public class IndexController { [ / size][ / align][align = left][size = 17 px] @RequestMapping ( "index" ) [ / size][ / align][align = left][size = 17 px] public void index ( String code , Model model , HttpServletResponse response , HttpServletRequest request ) { [ / size][ / align][align = left][size = 17 px] / / 如果有code就可以去获取用户的 open id [ / size][ / align][ / b] if ( code! = null ) { / / 通过code来换取access_token
JSONObject json = WeChatUtil.getWebAccessToken ( code ) ; / / 获取用户openid
String openid = json.getString ( "openid" ) ; / / 设置到会话中
request.getSession ( ) .setAttribute ( "openid" , openid ) ; / / 重定向到主页
response.sendRedirect ( "/index.html" ) ;
} else { / / 重定向到授权页面
response.sendRedirect ( WeChatUtil.WEB_REDIRECT_URL.replace ( "APPID" , WeChatUtil.APPID )
.replace ( "REDIRECT_URI" , RequestUtil.getUrl ( request ) ) ) ;
}
}
}
注意: 1. WeChatUtil.getWebAccessToken 方法在网页授权的文章中有介绍。
2. WEB_REDIRECT_URL 是网页授权的地址常量:
public static final String WEB_REDIRECT_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base#wechat_redirect" ;
|
点击立即购买按钮跳转到后台下单地址,并带上当前商品的id。
1
2
3
4
5
6
7
|
< script >
$ ( function ( ) { / / 立即购买按钮
$ ( "#orderBtn" ) .click ( function ( ) { / / 获取商品 id
var id = $ ( "#productId" ) .val ( ) ; / / 提交到下单
window . location .href = "/order.do?productId=" + id ;
} )
} ) < / script >
|
调用接口成功后返回的结果也封装成实体类:
该结果中最重要的是prepay_id参数,在页面中弹出支付窗口时需要用到。
注意:下单的业务逻辑,正常是需要抽取到业务层的,但是此处为了方便阅读代码,直接写到了控制器上。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
[/b]
[b] @Controller [/b]
[b] public class OrderController { [/b]
[b] @RequestMapping ( "order" ) [/b]
[b] public String save(Long productId,Model model,HttpServletRequest request) throws Exception { //根据商品id查询商品详细信息(假数据)
//productService.getProductById(productId)
double price = 0.01 ; //(0.01元)
String productName = "SweetCity" ; //生成订单编号
int number = ( int )((Math.random()* 9 )* 1000 ); //随机数
DateFormat dateFormat = new SimpleDateFormat( "yyyyMMddHHmmss" ); //时间
String orderNumber = dateFormat.format( new Date()) + number; //获取openId
String openId = (String) request.getSession().getAttribute( "openid" ); //准备调用接口需要的参数
WxOrderEntity order = new WxOrderEntity(); //公众号appid
order.setAppid(WeChatUtil.APPID); //商户号
order.setMch_id(WeChatUtil.MCH_ID); //商品描述
order.setBody(productName); //设备号,公众号支付直接填WEB
order.setDevice_info( "WEB" ); //交易类型
order.setTrade_type( "JSAPI" ); //商户订单号
order.setOut_trade_no(orderNumber); //支付金额(单位:分)
order.setTotal_fee(( int )(price* 100 )); //用户ip地址
order.setSpbill_create_ip(RequestUtil.getIPAddress(request)); //用户openid
order.setOpenid(openId); //接收支付结果的地址
order.setNotify_url( "http://www.wolfcode.com/receive.do" ); // 32 位随机数(UUID去掉-就是 32 位的)
String uuid = UUID.randomUUID().toString().replace( "-" , "" );
order.setNonce_str(uuid); //生成签名
String sign = WeChatUtil.getPaySign(order);
order.setSign(sign); //调用微信支付统一下单接口,让微信也生成一个预支付订单
String xmlResult = HttpUtil.post(GET_PAY_URL,XMLUtil.toXmlString(order)); //把返回的xml字符串转成对象
WxOrderResultEntity entity = XMLUtil.toObject(xmlResult,WxOrderResultEntity. class ); //如果微信预支付单成功创建,就跳转到支付订单页进行支付
if (entity.getReturn_code().equals( "SUCCESS" )&&entity.getResult_code().equals( "SUCCESS" )){ //jssdk权限验证参数
TreeMap<Object, Object> map = new TreeMap<>();
map.put( "appId" ,WeChatUtil.APPID); long timestamp = new Date().getTime();
map.put( "timestamp" ,timestamp); //全小写
map.put( "nonceStr" ,uuid);
map.put( "signature" ,WeChatUtil.getSignature(timestamp,uuid,RequestUtil.getUrl(request)));
model.addAttribute( "configMap" ,map); //微信支付权限验证参数
String prepayId = entity.getPrepay_id();
TreeMap<Object, Object> payMap = new TreeMap<>();
payMap.put( "appId" ,WeChatUtil.APPID);
payMap.put( "timeStamp" ,timestamp); //驼峰
payMap.put( "nonceStr" ,uuid);
payMap.put( "package" , "prepay_id=" +prepayId);
payMap.put( "signType" , "MD5" );
payMap.put( "paySign" ,WeChatUtil.getPaySign(payMap));
payMap.put( "packageStr" , "prepay_id=" +prepayId);
model.addAttribute( "payMap" ,payMap);
} //跳转到查看订单页面
return "order" ;
}
}
|
config签名:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
pay签名:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
/**
* 计算jssdk-config的签名
* @param timestamp
* @param noncestr
* @param url
* @return
*/
public static String getSignature(Long timestamp,String noncestr,String url ){ [/b]
[b] //对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
Map<String,Object> map = new TreeMap<>();
map.put( "jsapi_ticket" ,getTicket());
map.put( "timestamp" ,timestamp);
map.put( "noncestr" ,noncestr);
map.put( "url" ,url); //使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1
StringBuilder sb = new StringBuilder();
Set<String> set = map.keySet(); for (String key : set) {
sb.append(key+ "=" +map.get(key)).append( "&" );
} //去掉最后一个&符号
String temp = sb.substring( 0 ,sb.length()- 1 ); //使用sha1加密
return SecurityUtil.SHA1(temp);
} /**
* 计算微信支付的签名
* @param obj
* @return
* @throws IllegalAccessException
*/
public static String getPaySign(Object obj) throws IllegalAccessException, IOException {
StringBuilder sb = new StringBuilder(); //把对象转为TreeMap集合(按照key的ASCII 码从小到大排序)
TreeMap<String, Object> map; if (!(obj instanceof Map)) {
map = ObjectUtils.objectToMap(obj);
} else {
map = (TreeMap)obj;
}
Set<Map.Entry<String, Object>> entrySet = map.entrySet(); //遍历键值对
for (Map.Entry<String, Object> entry : entrySet) { //如果值为空,不参与签名
if (entry.getValue()!= null ) { //格式key1=value1&key2=value2…
sb.append(entry.getKey() + "=" + entry.getValue() + "&" );
}
} //最后拼接商户的API密钥
String stringSignTemp = sb.toString()+ "key=" +WeChatUtil.KEY; //进行md5加密并转为大写
return SecurityUtil.MD5(stringSignTemp).toUpperCase();
}
|
4.提供订单展示页面
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
< ! --jquery-->
< script src = "/js/jquery.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< ! --微信的JSSDK-->
< script src = "http://res.wx.qq.com/open/js/jweixin-1.2.0.js" > < / script >
< script >
$ ( function ( ) {
< ! --通过config接口注入权限验证配置-->
wx.config ( { debug : true , / / 开启调试模式
appId : '$ { configMap.appId } ' , / / 公众号的唯一标识
timestamp : '$ { configMap.timestamp } ' , / / 生成签名的时间戳
nonceStr : '$ { configMap.nonceStr } ' , / / 生成签名的随机串
signature : '$ { configMap.signature } ' , / / 签名
jsApiList : ['chooseWXPay'] / / 填入需要使用的JS接口列表,这里是先声明我们要用到支付的JS接口
} ) ; < ! -- config验证成功后会调用ready中的代码 -->
wx.ready ( function ( ) {
/ / 点击马上付款按钮
$ ( "#payBtn" ) .click ( function ( ) {
/ / 弹出支付窗口
wx.chooseWXPay ( {
timestamp : '$ { payMap.timeStamp } ' , / / 支付签名时间戳,
nonceStr : '$ { payMap.nonceStr } ' , / / 支付签名随机串,不长于 32 位
package : '$ { payMap.packageStr } ' , / / 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id = xxxx)
signType : '$ { payMap.signType } ' , / / 签名方式,默认为'SHA 1 ',使用新版支付需传入'MD 5 '
paySign : '$ { payMap.paySign } ' , / / 支付签名
success : function ( res ) {
/ / 支付成功后的回调函数
alert ( "支付成功!" ) ;
}
} ) ;
} )
} ) ;
} ) ; < / script >
|
点击马上付款后可弹出支付窗口:
支付完成:
当用户支付后,微信会把支付结果发送到我们前面指定的notify_url地址,我们可以根据支付结果的参数来做相关的业务逻辑,但这里暂不实现,具体支付通知结果的参数可参考官方文章:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
作者:陈惠
链接:https://www.jianshu.com/p/9c322b1a5274
一文快速实现微信公众号支付功能相关推荐
- 一文快速实现微信公众号支付功能(详细版,建议收藏备用)
进阶架构精品专题 Mysql优化专题(★★★★) 网络协议专题(★★★★) 其余18大专题,请在主页菜单栏查看 后台回复[加群],获取BAT真实面经 微信支付类型 微信支付实际上有很多种不同的类型,具 ...
- 微信公众平台开发[3] —— 微信公众号支付功能(PHP)
直言无讳,我就是一个初涉微信开发的小白,写这篇博客的原因:一是为了给自己做下备忘记录,以便以后能回忆这条程序猿的坎坷路:二是希望能帮助到同是自学开发的小白们:三是对那些不屑一顾于我等尘埃的大牛们的控诉 ...
- 前端微信公众号支付功能实现
前端在拉起微信支付页面之前会有一个预支付的 onLoad(options) {this.obj = JSON.parse(options.obj);console.log("返回的订单数据& ...
- 支付宝H5,微信H5,微信公众号支付回调
业务场景 应公司需求,需要在项目中接入支付宝h5支付,微信h5支付和微信公众号支付功能,本编主要讲述支付踩坑和h5支付后跳转的回调问题 微信h5支付 微信h5支付时需要校验下单域名,微信从refere ...
- php调用微信公众号支付接口,Thinkphp微信公众号支付接口
本文实例为大家分享了Thinkphp微信公众号支付接口,供大家参考,具体内容如下 第一步 先把文件夹的那两个图片 配置成一样的路径 除了域名要改 其他保持一致. 第二步 把 Weixinpay 这个 ...
- php微信公众号支付接口案例,Thinkphp微信公众号支付接口
本文实例为大家分享了Thinkphp微信公众号支付接口,供大家参考,具体内容如下 第一步 先把文件夹的那两个图片 配置成一样的路径 除了域名要改 其他保持一致. 第二步 把 Weixinpay 这个文 ...
- yii2嵌入微信公众号支付
序言 随着微信被越来越多的人使用,微信商城成为如今的热门.每一个商城都需要有自己的支付方式,微信商城也不例外.微信公众号支付就是微信商城的一种支付方式,微信支付随着微信的推广使用也被广泛应用.今天我主 ...
- 微信公众号页面支付接口java,[Java教程]微信公众号支付(三):页面调用微信支付JS并完成支付...
[Java教程]微信公众号支付(三):页面调用微信支付JS并完成支付 0 2015-09-15 15:00:30 一.调用微信的JS文件 1.首先要绑定[JS接口安全域名],"公众号设置&q ...
- vue 微信公众号支付接口_基于vue的h5项目之支付宝支付与微信支付
本文仅记录基于vue开发h5项目过程中使用支付宝和微信支付过程中的重点与槽点,仅为前端部分,如有疏漏不正之处,请于文末评论探讨.注意:标红部分灰常重要,仔细阅读官方文档非常重要,耐心非常重要,细心非常 ...
最新文章
- 目标检测的常用数据处理方法!
- 2019年1月计算机书籍JavaScript新书
- SAP CRM RDS快速部署解决方案
- android判断参数非空,Android Studio注释模板Live Templates参数获取不到为null的一些
- linux 解压rar密码,linux下rar包的压缩与解压方案
- requests爬取免费代理2
- linux删除用户删不了怎么办,Linux下完全删除用户的两种方法
- Chrome development tools学习笔记(3)
- obs计算机丢失,安装obs时提醒没法启动此程序,因为计算机丢失
- Duplicate methods named spliterator with the parameters () and () are inherited from the types Colle
- Web3.0中国峰会将于7月在成都召开
- python编辑数学公式_最好用的文字与公式编辑器,这套数学笔记神器送给你
- openresty性能调优
- 【IoT】产品设计:如何挖掘产品需求
- 几个可以整蛊你朋友的 Python 程序
- Windows 7笔记本创建wifi热点供手机上网教程
- 苹果手机屏幕上有白点怎么办
- java窗体怎么实现下拉菜单_java之swing下拉菜单实现方法
- win10 关闭自动维护计划任务
- C语言练习:该存多少钱
热门文章
- 网课查题公众号怎么搭建制作怎么弄
- 第4章 信息系统管理
- outlook 日历共享_如何与他人共享Google日历
- echarts x轴像直尺一样设置刻度_Python matplotlib画图y轴数值不按大小排列问题
- linux 在线模拟器
- 永辉超级物种,走到了十字路口?
- LInux查看文件时怎么让文件显示行号
- sql_mode严格模式(ANSI/TRADITIONAL/STRICT_TRANS_TABLES)
- 什么是缓存穿刺和缓存雪崩?如何解决缓存穿透,缓存雪崩
- Allegro软件中沉板的器件封装应该怎么处理呢?