可以先看一下微信支付api文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_4
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317853&token=&lang=zh_cn
建议先看一下微信的开发者文档,虽然有点坑 。。。。
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
应用场景
所需jar包
<!-- 微信支付需要的jar包 --> <dependency> <groupid>xmlpull</groupid> <artifactid>xmlpull</artifactid> <version>1.1.3.1</version> </dependency> <dependency> <groupid>xpp3</groupid> <artifactid>xpp3</artifactid> <version>1.1.4c</version> </dependency> <dependency> <groupid>com.thoughtworks.xstream</groupid> <artifactid>xstream</artifactid> <version>1.4.7</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>fluent-hc</artifactid> <version>4.3.5</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpclient</artifactid> <version>4.3.5</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpclient-cache</artifactid> <version>4.3.5</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpcore</artifactid> <version>4.3.2</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpmime</artifactid> <version>4.3.5</version> </dependency>
公众号支付所需参数
public class weixinmatchpayconfigure { /** * 域名 项目域名,根据需求自行配置 */ public static final string rooturl = weixinpayconfigure.rooturl; /** * 订单域名 项目域名,根据需求自行配置 */ public static final string order_rooturl = weixinpayconfigure.order_rooturl; /** * 赛事 域名 项目域名,根据需求自行配置 */ public static final string matchurl = "http://www.baidu.com"; /** * 公共账号id 必填 (18位数↓) */ public static final string appid = weixinpayconfigure.appid; /** * 商户id 商户账号 必填 */ public static final string mch_id = "11111111"; /** * 应用秘钥 必填(可在微信商户平台上查找) */ public static final string app_secret = "fd87878fsf87fsf8cvsd8"; /**api秘钥*/ 必填(可在微信商户平台上查找) public static final string api_key = "fsdfn23482njdvjw23455555"; /** * 统一下单url 微信官方提供 */ public static final string pay_unified_order_api = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** * 微信公众号交易类型 (扫码支付类型:native,公众号支付类型:jsapi) */ public static final string trade_type = "jsapi"; /** * 获取code的回调地址 你项目要展示的首页路径 */ public static final string redirect_uri = "http://order.uxuexi.com/pay/apply.html"; /**微信h5支付结果通知页*/ public static final string notify_url = rooturl + "/api/pay/weixin/notify.html"; /** * 不弹出授权页面,直接跳转,只能获取用户openid */ public static final string scope = "snsapi_base"; /** * 弹出授权页面,需要用户确认,可以获取用户的较多信息 */ public static final string userinfoscope = "snsapi_userinfo"; /** * 获取微信code的url(登录授权) */ public static final string get_code_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=appid&redirect_uri=redirect_uri&response_type=code&scope=scope&state=state#wechat_redirect"; /** * 获取用户的openid的url(必须先获得code之后调用) */ public static final string get_openid_url = "https://api.weixin.qq.com/sns/oauth2/access_token"; /** * 微信支付成功之后的回调 */ public static final string notify_activity_url = weixinpayconfigure.order_rooturl + "/pay/wxnotify.json"; }
获取微信用户的openid
大致步骤:获取用户授权(获取code)------------->根据code获取openid(用户的基本信息)
1、配置授权域
这个则是在公众号登陆平台上面配置的↓
2、发起api请求获取用户授权
url:https://open.weixin.qq.com/connect/oauth2/authorize?appid=appid&redirect_uri=redirect_uri&response_type=code&scope=scope&state=state#wechat_redirect
需要拼接三个参数:appid、redirect_uri、scope,代码如下:
//项目入口,如果这个项目是在公众号中,那么公众号菜单下面配置的就是这个接口的路径↓ @at @ok("jsp:match.entrance") @notsso public object entrance() { string codeurl = weixinapiurlutil.getmatchurl(); return codeurl; } public static string getmatchurl() { string url = weixinmatchpayconfigure.get_code_url; url = url.replace("appid", urlenodeutf8(weixinmatchpayconfigure.appid)); url = url.replace("redirect_uri", weixinmatchpayconfigure.redirect_uri); url = url.replace("scope", weixinmatchpayconfigure.userinfoscope); return url; }
返回前端的是个url路径,同个这个路径来获取微信用户授权,然后跳转我们自己的首页(redirect_uri) 前端页面 <script type="text/javascript"> $(document).ready(function(){ //其实这个时候跳转的url它会跟着一连串属性,入下图 ↓ window.location.href='${obj}'; }); </script>
3、微信用户openid是什么?
在微信用户关注公众号时,会对应的产生一个openid(openid:加密后的用户微信号),一个微信用户对应一个公众号产生的openid是唯一,当然,如果该微信号去关注另一个公众号所产生openid肯定和当前这个不一样的,openid 是最对『微信应用』的用户唯一值,同一个『微信开发者账号』下的不同应用中,使用同一个『微信用户』登录,此值会不一样,
废话不说,上代码↓
①、上文表明此接口请求带有参数code,那么需在这里接受code参数
@at @ok("jsp:match.apply") @notsso public object apply(@param("code") string code) { return matchpayviewservice.apply(code); }
②、获取openid放到session中去
public object apply(string code) { //创建空map map<string, object> map = maputil.map(); //获取session httpsession session = mvcs.getreq().getsession(); //获取session中oppenid string oppendid = convertutil.obj2str(session.getattribute("oppendid")); //非空校验oppenid if (!util.isempty(oppendid)) { map.put("iswechat", "yes"); return map; } //校验code是否为空,为空说明不是微信公众号支付 if (util.isempty(code)) { map.put("iswechat", "no"); return map; } //获取访问用户的token,(工具类1) userinfoaccesstokendt accesstokendt = userinfoaccesstokendtbaseservice.getuseraccesstoken(code); //获取微信用户信息,(工具类2) userinfodt userinfo = userinfodtbaseservice.getuserinfo(accesstokendt); exceptionutil.checkempty(userinfo, "获取微信用户信息失败"); map.put("userinfo", userinfo); int sessioninactive = 30 * 60; //把相关数据放到session中去 session.setmaxinactiveinterval(sessioninactive); session.setattribute("oppendid", userinfo.getopenid()); session.setattribute("userinfo", userinfo); map.put("iswechat", "yes"); //获取当前用户 map.put("userid", fetchuser.getcurrentuserid()); //-----------------------------------------------------权限校验,可根据自己的项目进行业务操作 map.put("isgxltuser", gxunicombusinessservice.ispermission(fetchuser.getcurrentuserid())); return map; }
注:上面这些是获取微信用户授权、获取用户的基本信息,公众号支付需要用户的openid,所以。。。。。。
创建预支付url
1、创建订单
①、此处不解释
@at @ok("jsp:match.createorder") @notsso public object createorder(@param("..") final orderaddform orderaddform, final string gradename) { return matchpayviewservice.createorder(orderaddform, gradename); }
②、创建订单
public map<string, object> createorder(orderaddform orderaddform, string gradename) { exceptionutil.checkid(orderaddform.getmatchid(), "赛事id不能为空"); map<string, object> map = maputil.map(); orderaddform.setcustomprice(matchbaseservice.getprice(orderaddform.getmatchid()).getprice()); ordermatchentity order = dbdao.insert(orderaddform.toentity()); map.put("order", order); map.put("gradename", gradename); //拼接预支付订单 string result = sendreqgetpreorder(order); //转化为jsapiparam对象,(工具类3) jsapiparam jap = dowithwxreturn(result); map.put("jap", jap); return map; }
③、拼接预支付订单参数
private string sendreqgetpreorder(ordermatchentity order) { exceptionutil.checkempty(order.getid(), "订单id不能为空"); map<string, object> params = maputil.map(); params.put("appid", weixinmatchpayconfigure.appid); params.put("mch_id", weixinmatchpayconfigure.mch_id); params.put("notify_url", weixinmatchpayconfigure.notify_activity_url); params.put("trade_type", weixinmatchpayconfigure.trade_type);//单次订单为jsapi方式 int randomnumlength = 32; params.put("nonce_str", randomutil.randomstring(randomnumlength)); //获取openid params.put("openid", getwxuserinfowithex().getopenid()); string body = "赛事报名"; params.put("body", body); params.put("out_trade_no", order.getid()); long total_fee = amountutils.changey2f(order.getcustomprice()); params.put("total_fee", total_fee); params.put("device_info", "web"); //加密签名,工具类(微信支付pc端文档中有) string sign = signature.getsign(params, weixinmatchpayconfigure.api_key); params.put("sign", sign); //httprequest 工具类(微信支付pc端文档中有) return httprequest.sendpost(weixinmatchpayconfigure.pay_unified_order_api, params); } //获取微信标识 private userinfodt getwxuserinfowithex() { object userinfo = mvcs.getreq().getsession().getattribute("userinfo"); if (util.isempty(userinfo)) { throw exceptionutil.bex("获取你的微信身份的标识失败请重新退出再次进入"); } //类型转换 return convertutil.cast(userinfo, userinfodt.class); }
④、处理调用微信预支付订单的返回值
private jsapiparam dowithwxreturn(string result) { //该类见(工具类3) jsapiparam jsapiparam = new jsapiparam(); map<string, object> weixinprepayinfo = maputil.map(); try { //------------------解析xml(工具类:微信支付pc端文档中有) weixinprepayinfo = xmlparser.getmapfromxml(result); string return_code = (string) weixinprepayinfo.get("return_code"); if ("success".equals(return_code)) { string prepay_id = (string) weixinprepayinfo.get("prepay_id"); //给jsapiparam对象赋值 jsapiparam.setprepay_id(prepay_id); jsapiparam.setpackageinfo("prepay_id=" + prepay_id); jsapiparam.setpaysign(getjsapipaysign(jsapiparam)); return jsapiparam; } else { throw exceptionutil.bex("预支付失败"); } } catch (exception e) { exceptionutil.bex("调用微信预支付接口出错"); } return jsapiparam; }
⑤、调用jsapiparam对象打回前台后,则需要调用微信内部的提供的js方法getbrandwcpayrequest
function onbridgeready(){ //微信内部提供的js方法 weixinjsbridge.invoke( 'getbrandwcpayrequest', { "appid":"${obj.jap.appid}", //公众号名称,由商户传入 "timestamp":"${obj.jap.timestamp}", //时间戳,自1970年以来的秒数 "noncestr":"${obj.jap.noncestr}", //随机串 "package":"${obj.jap.packageinfo}", "signtype":"md5", //微信签名方式: "paysign":"${obj.jap.paysign}" //微信签名 }, function(res){ // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 if (res.err_msg == "get_brand_wcpay_request:ok") { //跳转到成功页面 window.location.href = "http://lannong.uxuexi.com/register/success.html"; } else if (res.err_msg == "get_brand_wcpay_request:cancel") { weixinjsbridge.call('closewindow'); } else { } } ); }
⑥、调用⑤后页面则会弹出微信支付框,如图↓
支付成功
1、支付成功后则调用成功后的回调函数
@at @filters public void wxnotify() throws exception { matchpayviewservice.wechatpayviewservice(); }
2、签名校验
public void wechatpayviewservice() throws exception { httpservletrequest request = mvcs.getreq(); httpservletresponse response = mvcs.getresp(); //获取微信响应的内容 string responsestring = getweixinresponsecontent(request); printwriter out = response.getwriter(); string resp = ""; string signkey = weixinpayconfigure.api_key; //------------------------------签名校验↓,(工具类:微信支付pc端文档中有) boolean verify = signature.checkissignvalidfromresponsestring(responsestring, signkey); if (!verify) { logger.error("签名验证失败"); resp = "签名验证失败"; out.write(resp); out.close(); //签名失败直接返回 return; } //解析xml map<string, object> map = xmlparser.getmapfromxml(responsestring); string result_code = convertutil.obj2str(map.get("result_code")); if (!"success".equalsignorecase(result_code)) { resp = paycommonutil.getresponsexml("error", "error"); out.write(resp); out.close(); //支付失败直接返回 return; } //处理订单 resp = handleorder(map); out.write(resp); out.close(); } //获取微信响应内容 private string getweixinresponsecontent(httpservletrequest request) throws ioexception, unsupportedencodingexception { inputstream instream = request.getinputstream(); bytearrayoutputstream outstream = new bytearrayoutputstream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = instream.read(buffer)) != -1) { outstream.write(buffer, 0, len); } //获取微信调用我们notify_url的返回信息 string responsestring = new string(outstream.tobytearray(), "utf-8"); outstream.close(); instream.close(); return responsestring; }
3、处理订单,此处业务不再讲解,不懂得可以去看pc端微信支付文档
@aop("txdb") private string handleorder(map<string, object> map) throws exception { string resp = paycommonutil.getresponsexml("success", "ok"); string transaction_id = convertutil.obj2str(map.get("transaction_id")); string time_end = convertutil.obj2str(map.get("time_end")); string out_trade_no = (string) map.get("out_trade_no"); if (util.isempty(transaction_id) || util.isempty(time_end) || util.isempty(out_trade_no)) { resp = paycommonutil.getresponsexml("error", "参数错误,微信支付订单号、支付完成时间、订单号均不能为空"); return resp; } ordermatchentity order = dbdao.fetch(ordermatchentity.class, convertutil.obj2long(out_trade_no)); if (util.isempty(order)) { resp = paycommonutil.getresponsexml("error", "订单不存在"); return resp; } int orderstatus = order.getstatus(); if (orderstatusenum.finished.intkey() == orderstatus) { return resp; } if (orderstatusenum.waiting_pay.intkey() == orderstatus) { //此处写你所需的业务即可 //更新订单为完成状态 //实际支付金额(分) //添加支付记录 } return resp; }
4、公众号微信支付,支付后微信会返回三种状态,如图↓
那么,关于三种状态我们所需跳转的页面则可以在前台用js来实现
function(res){ // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 if (res.err_msg == "get_brand_wcpay_request:ok") { //跳转到成功页面 window.location.href = "http://lannong.uxuexi.com/register/success.html"; } else if (res.err_msg == "get_brand_wcpay_request:cancel") { weixinjsbridge.call('closewindow'); } else { } }
工具类
1、获取访问用户的token
@iocbean public class userinfoaccesstokendtbaseservice { /** * 通过code获取用户的openid * * @param code 编号 * * @return 用户的openid */ public userinfoaccesstokendt getuseraccesstoken(string code) { exceptionutil.checkempty(code, "用户同意授权,获取的code不能为空"); //获取用户openid的连接 string url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + weixinh5payconfigure.appid + "&secret=" + weixinh5payconfigure.app_secret + "&code=" + code + "&grant_type=authorization_code"; response res = http.get(url); string content = res.getcontent(); //----------------------------------从 json 字符串中,根据获取某种指定类型的 json 对象 userinfoaccesstokendt accesstokendt = jsonutil.fromjson(content, userinfoaccesstokendt.class); return accesstokendt; } }
2、获取微信用户基本信息
@iocbean public class userinfodtbaseservice { /** * 获取用户信息 * * @param accesstokendt 获取用户信息的token * * @return 用户信息对象 */ public userinfodt getuserinfo(userinfoaccesstokendt accesstokendt) { exceptionutil.checkempty(accesstokendt, "访问用户的accesstoken不能为空"); string url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accesstokendt.getaccess_token() + "&openid=" + accesstokendt.getopenid() + "&lang=zh_cn"; response res = http.get(url); string content = res.getcontent(); //------------------------------------- 从 json 字符串中,根据获取某种指定类型的 json 对象。↓ userinfodt userinfo = jsonutil.fromjson(content, userinfodt.class); return userinfo; } } @data public class userinfoaccesstokendt { //todo(注释去这个链接下找:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html) private string access_token; private string expires_in; private string refresh_token; private string openid; private string scope; private string unionid; }
3、微信公众号支付参数
@data public class jsapiparam { /** * 公众号appid */ private string appid = weixinh5payconfigure.appid; /** * 时间戳 */ private string timestamp = system.currenttimemillis() + ""; /** * 随机字符串 */ private string noncestr = randomutil.randintstring(32); /** * 签名方式 */ private string signtype = "md5"; /** * 预支付id */ private string packageinfo; /** * 支付签名 */ private string paysign; /** * 订单号 */ private string orderno; /** * 微信预付单号 */ private string prepay_id; }
以上就是微信支付之公众号支付详解的详细内容。
