开发环境
第一步:开通JSAPI支付
注册微信商户号请参考官方文档,参考地址如下:
https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal#none
开通微信支付后即可在微信商户平台(pay.weixin.qq.com)开通JSAPI支付。登录商品平台:
进入产品中心,开通JSAPI支付:
设置JSAPI支付目录,注意:支付授权目录为公网域名且备案通过。
以下图片来源于网络:
获取openId地址(由于是网页调用,所以采用网页授权的方式获取):https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
获取接口所需参数地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
JSAPI接口地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
统一下单接口定义:
红色:程序设置
蓝色:微信sdk(开发工具包)自动配置
第二步:获取openid
获取openid分为两步走,第一步先获取code,第二步获取openId
访问URL:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_re
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
获取code后,请求以下链接获取access_token,在响应数据中包含openid: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
<dependency>
<groupId>com.github.tedzhdzgroupId>
<artifactId>wxpay-sdkartifactId>
<version>3.0.10version>
dependency>
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-payartifactId>
<version>3.4.0version>
dependency>
@Controller
public class WxPayController {
String appID = "wxd2bf2dba2e86a8c7";
String mchID = "1502570431";
String appSecret = "cec1a9185ad435abe1bced4b93f7ef2e";
String key = "95fe355daca50f1ae82f0865c2ce87c8";
//申请授权码地址
String wxOAuth2RequestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
//授权回调地址
String wxOAuth2CodeReturnUrl = "http://ces123/transaction/wx-oauth-code-return";
String state="";
//获取授权码
@GetMapping("/getWXOAuth2Code")
public String getWXOAuth2Code(HttpServletRequest request, HttpServletResponse response){
//https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect",
appID, wxOAuth2CodeReturnUrl
);
return "redirect:"+url;
}
/**
* //授权码回调,传入授权码和state,/wx-oauth-code-return?code=授权码&state=
* @param code 授权码
* @param state 申请授权码传入微信的值,被原样返回
* @return
*/
@GetMapping("/wx-oauth-code-return")
public String wxOAuth2CodeReturn(@RequestParam String code,@RequestParam String state){
//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appID, appSecret, code
);
//申请openid,请求url
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//申请openid接口响应的内容,其中包括了openid
String body = exchange.getBody();
log.info("申请openid响应的内容:{}",body);
//获取openid
String openid = JSON.parseObject(body).getString("openid");
return openid;
}
}
第三步:统一下单
第二步走完后,下单所需的所有参数都获取到了,下面开始支付:
后端代码,将所有参数返回到前端:
@Controller
public class WxPayController {
String appID = "wxd2bf2dba2e86a8c7";
String mchID = "1502570431";
String appSecret = "cec1a9185ad435abe1bced4b93f7ef2e";
String key = "95fe355daca50f1ae82f0865c2ce87c8";
//申请授权码地址
String wxOAuth2RequestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
//授权回调地址
String wxOAuth2CodeReturnUrl = "http://ces123/transaction/wx-oauth-code-return";
String state="";
//获取授权码
@GetMapping("/getWXOAuth2Code")
public String getWXOAuth2Code(HttpServletRequest request, HttpServletResponse response){
//https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect",
appID, wxOAuth2CodeReturnUrl
);
return "redirect:"+url;
}
/**
* //授权码回调,传入授权码和state,/wx-oauth-code-return?code=授权码&state=
* @param code 授权码
* @param state 申请授权码传入微信的值,被原样返回
* @return
*/
@GetMapping("/wx-oauth-code-return")
public String wxOAuth2CodeReturn(@RequestParam String code,@RequestParam String state){
//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appID, appSecret, code
);
//申请openid,请求url
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//申请openid接口响应的内容,其中包括了openid
String body = exchange.getBody();
log.info("申请openid响应的内容:{}",body);
//获取openid
String openid = JSON.parseObject(body).getString("openid");
//重定向到统一下单接口
return "redirect:http://ces123/transaction/wxjspay?openid=" + openid;
}
//统一下单,接收openid
@GetMapping("/wxjspay")
public ModelAndView wxjspay(HttpServletRequest request,HttpServletResponse response) throws Exception {
//创建sdk客户端
WXPay wxPay = new WXPay(new WXPayConfigCustom());
//构造请求的参数
Map<String,String> requestParam = new HashMap<>();
requestParam.put("out_trade_no","10029293889");//订单号
requestParam.put("body", "iphone8");//订单描述
requestParam.put("fee_type", "CNY");//人民币
requestParam.put("total_fee", String.valueOf(1)); //金额
requestParam.put("spbill_create_ip", "127.0.0.1");//客户端ip
requestParam.put("notify_url", "none");//微信异步通知支付结果接口,暂时不用
requestParam.put("trade_type", "JSAPI");
//从请求中获取openid
String openid = request.getParameter("openid");
requestParam.put("openid",openid);
//调用统一下单接口
Map<String, String> resp = wxPay.unifiedOrder(requestParam);
//准备h5网页需要的数据
Map<String,String> jsapiPayParam = new HashMap<>();
jsapiPayParam.put("appId",appID);
jsapiPayParam.put("timeStamp",System.currentTimeMillis()/1000+"");
jsapiPayParam.put("nonceStr", UUID.randomUUID().toString());//随机字符串
jsapiPayParam.put("package","prepay_id="+resp.get("prepay_id"));
jsapiPayParam.put("signType","HMAC-SHA256");
jsapiPayParam.put("paySign", WXPayUtil.generateSignature(jsapiPayParam,key,WXPayConstants.SignType.HMACSHA256));
//将h5网页响应给前端
return new ModelAndView("wxpay",jsapiPayParam);
}
class WXPayConfigCustom extends WXPayConfig{
@Override
protected String getAppID() {
return appID;
}
@Override
protected String getMchID() {
return mchID;
}
@Override
protected String getKey() {
return key;
}
@Override
protected InputStream getCertStream() {
return null;
}
@Override
protected IWXPayDomain getWXPayDomain() {
return new IWXPayDomain() {
@Override
public void report(String s, long l, Exception e) {
}
@Override
public DomainInfo getDomain(WXPayConfig wxPayConfig) {
return new DomainInfo(WXPayConstants.DOMAIN_API,true);
}
};
}
}
}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="renderer" content="webkit">
<meta http-equiv="Expires" content="0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>同步通知title>
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : "${appId}", //公众号名称,由商户传入
"timeStamp" : "${timeStamp}", //时间戳,自1970年以来的秒数
"nonceStr" : "${nonceStr}", //随机串
"package" : "${package}",
"signType" : "${signType}", //微信签名方式:
"paySign" : "${paySign}" //微信签名,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。
},
function(res) {
if(res.err_msg == "get_brand_wcpay_request:ok" ) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
alert('支付成功!');
} else {
alert('支付失败:' + res.err_msg);
}
WeixinJSBridge.call('closeWindow');
}
);
}
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>
head>
<body>
<div id="app">
div>
body>
html>
至此,完成!!!