SpringBoot实战项目精华总结 -- 微信公众号(三)
注:这里的总结仅供自己以后参考,游客还请以微信官方文档为主
一、微信特性
1.微信授权
1.1 网页域名授权配置
1.2 获取openid
手工方式
利用第三方SDK(github上 => weixin-java-tools)
1.3 前后端联调
二、微信支付
1.官网文档
2.公众号里发起支付
3.第三方SDK
4.支付时序
4.1 发起统一下单
4.2 利用预付单信息+freemarker完成动态参数注入js签名,唤起前端支付
4.3 最后通过微信内置对象WeixinJSBridge唤起支付密码输入弹窗
5.处理微信异步通知(API列表=>支付结果通知)
三、微信退款
1.基于SDK的方法代码
2.退款微信也会调用异步通知(与支付异步通知同一接口)需要处理
四、其他
1.基于Gson的Json格式化工具
一、微信特性
1.微信授权
官网文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
1.1 网页域名授权配置
-> 本地开发环境使用内网穿透:https://natapp.cn/ 将本地的8080端口映射到购买的可用于微信开发的二级域名,注意要买备案的
-> 在微信公众平台=>功能设置=>网页授权域名,下载文件存放至 resource/static 目录下
-> 启动natapp.exe(参考natapp1分钟教程),启动本地项目(临时去掉 server.context-path 设置)
-> 微信公众平台填写买到的二级域名,完成保存。设置后即可删除txt文件
1.2 获取openid
手工方式
-> 先调微信授权接口并指定回调地址,用户端弹出授权框,同意后可获取code
-> 回调地址接口接收到微信的回调,带回code,code只能使用一次,5分钟有效期
-> 后端微信回调的接口再使用code+appid+secret获取access_token+openid+refresh_token
-> 使用access_token+openid拉取用户基本信息,但注意拉取得信息编码是ISO-8859-1编码
-> 对用户信息的转码操作 new String(response.getBytes("ISO-8859-1"), "UTF-8")
利用第三方SDK(github上 => weixin-java-tools)
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.7.0</version>
</dependency>
-> 前端发起用户授权请求,入参为回调地址(前端接收授权后openid的地址)
1.3 前后端联调
-> 手机网页抓包工具charles及破解教程:https://www.cnblogs.com/jiayuchn-test/p/8875105.html
-> 10003 redirect_uri域名与后台配置不一致:公众号设置=>功能设置=>网页授权域名 必须要与 授权链接 https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx69fxxxxxxxxed8&redirect_uri=http%3a%2f%2fwww.xxx.com%2fvShop%2fdefault&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect 中redirect_uri一致!
二、微信支付
1.官网文档
https://pay.weixin.qq.com/wiki/doc/api/index.html
2.公众号里发起支付
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4
3.第三方SDK
https://github.com/Pay-Group/best-pay-sdk
4.支付时序
4.1 发起统一下单
notifyUrl(异步通知必填)设置为自己单独的接口地址给微信支付平台调用,传来预付单信息,此时微信端已经记录下“订单号+订单金额”信息(所以只改订单金额是不行的)
4.2 利用预付单信息+freemarker完成动态参数注入js签名,唤起前端支付
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
pay/create.ftl
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"${payResponse.appId}", //公众号名称,由商户传入
"timeStamp":"${payResponse.timeStamp}", //时间戳,自1970年以来的秒数
"nonceStr":"${payResponse.nonceStr}", //随机串
"package":"${payResponse.packAge}",
"signType":"MD5", //微信签名方式:
"paySign":"${payResponse.paySign}" //微信签名
},
function(res){
// if(res.err_msg == "get_brand_wcpay_request:ok" ) {
// } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
location.href = "${returnUrl}";
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
4.3 最后通过微信内置对象WeixinJSBridge唤起支付密码输入弹窗
5.处理微信异步通知(API列表=>支付结果通知)
/**
* 微信异步通知
* @param notifyData
*/
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData) {
payService.notify(notifyData);
//返回给微信处理结果
return new ModelAndView("pay/success");
}
pay/success.ftl 告诉微信订单处理成功,就不会再收到微信通知支付成功了
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
三、微信退款
官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
1.基于SDK的方法代码
需要封装一个退款请求类:RefundRequest(订单号,订单金额,支付类型)
/**
* 退款
* @param orderDTO
*/
@Override
public RefundResponse refund(OrderDTO orderDTO) {
RefundRequest refundRequest = new RefundRequest();
refundRequest.setOrderId(orderDTO.getOrderId());
refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_MP);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));
return refundResponse;
}
2.退款微信也会调用异步通知(与支付异步通知同一接口)需要处理
四、其他
1.基于Gson的Json格式化工具
/**
* @Description Json格式化处理工具
* @Date 2020/2/16 12:08
*/
public class JsonUtil {
public static String toJson(Object object) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
return gson.toJson(object);
}
}