最近开发了微信支付功能模块,就趁热打铁把微信支付(JSAPI支付)相关知识整理一下吧。
1、JSAPI支付JSAPI支付是指用户在微信中打开商家的H5页面,商家在H5页面调用微信支付提供的JSAPI接口,调用微信支付模块完成支付。应用场景有:
(1)用户在微信公众号中进入商家微信官方账号,打开主页面,完成支付。
(2)用户的好友在朋友圈、聊天窗口等分享业务页面连接。用户单击该链接打开业务页面并完成支付。
(3)将商家页面转换成二维码,用户扫描二维码,在微信浏览器中打开页面,完成支付。
微信支付业务流程时序图:
要支持微信支付,你必须有两个账户:
(1)微信微信官方账号认证的服务号,需开通微信支付功能(仅限企业申请)
微信官方账号APPID:appi
APPSECEPT:appsecret
(2)微信商家平台账号
商家标识:妇幼保健标识
API密钥:paternerKey
官网下载了微信官方账号支付的sdk,如图:
主要使用了WXPayUtil工具类中的一些方法和WXPay中的实现方法。
3、微信统一下单接口调用应用场景:
除了支付码支付场景,商家系统首先在微信支付服务后台调用该接口生成预付交易单,返回正确的预付交易会话标识,然后根据JSAPI的场景生成交易字符串发起支付。
前提条件:
微信的统一点餐接口需要openId,获取openId的方法有扫码实现微信跳转,分享给好友实现微信跳转。我们已经讲过网页授权获取用户openId,有需要的朋友再看看。
如果需要其他方法获取openid,可以参考开发文档。(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?第4章=第4章)
调用微信统一单接口:
/** *微信统一订购界面 */ @ post @ path(value = & # 34;微信-create insurance & # 34;) @产生(MediaType。APPLICATION _ JSON) 公众响应微信create insurance(@ Context http servlet request servlet request){ Map & lt;字符串,对象& gtresult = new HashMap & lt& gt(); 试试{ Map & lt;String,String & gtparamMap = new HashMap & lt& gt(); param map . put(& # 34;appid & # 34,appid);//公共账号ID param map . put(& # 34;mch _ id & # 34,MC hid);//商户号 param map . put(& # 34;nonce _ str & # 34,wxpayutil . generatenoncestr());//random string param map . put(& # 34;正文& # 34;,shareno info . getstring(& # 34;productName & # 34));//产品描述或付费名称 param map . put(& # 34;out _ trade _ no & # 34,outTradeNo);//自己后台提供的唯一订单号 param map . put(& # 34;总费用& # 34;,价格);//支付金额,默认单位为分钟 param map . put(& # 34;spbill _ create _ ip & # 34,servlet request . get header(& # 34;x-Real-IP & # 34;));//终端IP param map . put(& # 34;通知_网址& # 34;, "Http://XXXX.com/项目名称/微信-通知& # 34;);//微信支付回调地址(具体实现见第五章) param map . put(& # 34;trade _ type & # 34, "JSAPI & # 34);//支付类型 param map . put(& # 34;openid & # 34,openId);//付款人微信官方账号对应的唯一标识符[/h //其他10个必需参数和API秘钥,得到sign string sign = wxpayutil。使用generateSignature方法生成签名(参数映射,pater nerkey); param map . put(& # 34;标志& # 34;,签字);//记得在映射中放入sign,得到11个参数 String请求XML String = wxpayutil。mapto XML(param map);//转换成XML String String unified order = & # 34;https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一订购接口 String XML str = http request . send post(统一订购_ URL,XML); if(XML str . index of(& # 34;成功& # 34;)& lt= 0){ throw new runtime exception(& # 34;调用微信统一点餐接口报错!"); } Map & lt;String,String & gtmap = wxpayutil . XML tomap(XML str); String prepay _ id =(String)map . get(& # 34;prepay _ id & # 34);//预付款交易会话ID map < String,String & gtpayMap = new HashMap & ltString,String & gt(); pay map . put(& # 34;appId & # 34,appid); pay map . put(& # 34;时间戳& # 34;,wxpayutil . getcurrenttimestamp()+& # 34;"); pay map . put(& # 34;非核心& # 34;,wxpayutil . generatenoncestr()); pay map . put(& # 34;套餐& # 34;, "prepay _ id = & # 34+prepay _ id);//固定格式 pay map . put(& # 34;signType & # 34, "MD5 & # 34); String pay sign = wxpayutil . generatesignature(pay map,pater nerkey); pay map . put(& # 34;paySign & # 34,paySign); //...........................................................................................................................................................代码& # 34;,成功); result . put(& # 34;结果& # 34;,pay map); } catch(Exception e){ e . printstacktrace(); result . put(& # 34;代码& # 34;,错误); result . put(& # 34;msg & # 34, "系统异常& # 34;); } return Response.ok(结果)。build(); }注:
Notify_url回调地址:微信支付成功后,会多次用大量参数(XML格式)请求这个地址。该地址将用于我们的业务处理,如支付成功后修改订单状态、赠送积分等操作。需要公共地址才能访问它。
http request类的结构如下:
public class http request { /* * *向指定的URL发送GET方法的请求 * @param url发送请求的URL * @param请求参数,应该是name 1 = value 1 & name 2 = value 2的形式。 * @return URL表示远程资源的响应结果 */ 公共静态字符串sendget (string URL,string param){ string result = & # 34;"; buffered reader in = null; try { String URL name String = URL+& # 34;?"+param; system . out . println(URL name string); URL real URL = new URL(urlNameString); //打开和URL之间的连接 URL Connection = real URL . Open Connection(); //设置通用请求属性 connection . setrequestproperty(& # 34;接受& # 34;, "*/*"); connection . setrequestproperty(& # 34;连接& # 34;, "保持活力& # 34;); connection . setrequestproperty(& # 34;用户代理& # 34;, & # 34;Mozilla/4.0(兼容;MSIE 6.0Windows NT 5.1SV1)& # 34;); //建立实际连接 connection . connect(); //获取所有响应头字段 map < String,List & lt字符串& gt& gtmap = connection . getheaderfields(); //遍历(string key:map . keyset()){ system . out . println(key+& # 34;-& gt;"+map . get(key)); } /定义BufferedReader输入流以在= new buffered reader( connection中读取URL响应 。GetInputStream())); 字符串行; while ((line = in.readLine())!= null){ result+= line; } } catch(Exception e){ system . out . println(& # 34;发送GET请求时出现异常!"+e); e . printstacktrace(); } /使用finally块关闭输入流 finally { try { if(in!= null){ in . close(); } } catch(Exception E2){ E2 . printstacktrace(); } } 返回结果; } /** *将POST方法的请求发送到指定的URL * @param url发送请求的URL * @param请求参数,应该是name 1 = value 1 & name 2 = value 2的形式。 * @return表示远程资源 */ 公共静态字符串send post (string URL,string param){ printwriter out = null的响应结果; buffered reader in = null; 字符串结果= & # 34;"; try { URL real URL = new URL(URL); //打开与URL URL connection conn = real URL . Open connection()之间的连接; //设置通用请求属性 conn . setrequestproperty(& # 34;接受& # 34;, "*/*"); conn . setrequestproperty(& # 34;连接& # 34;, "保持活力& # 34;); conn . setrequestproperty(& # 34;用户代理& # 34;, & # 34;Mozilla/4.0(兼容;MSIE 6.0Windows NT 5.1SV1)& # 34;); //必须设置下面两行 conn . setdoooutput(true)才能发送POST请求; conn . setdoinput(true); //获取URLConnection对象对应的输出流 out = new printwriter(conn . get output stream()); //发送请求参数 out . print(param);flush输出流的 /buffer[/h //flush(); //定义BufferedReader输入流读取URL响应 in = new buffered reader( new input streamreader(conn . get inputstream())); 字符串行; while ((line = in.readLine())!= null){ result+= line; } } catch(Exception e){ system . out . println(& # 34;发送发布请求异常!"+e); e . printstacktrace(); } //使用finally块关闭输出流和输入流 finally { try { if(out!= null){ out . close(); } if(in!= null){ in . close(); } } catch(io exception ex){ ex . printstacktrace(); } } 返回结果; } }4。微信支付首页调用$(& # 39;# btn & # 39).click(function(){ var param = {//传入参数 ... }; $。Ajax({ type:& # 34;邮政& # 34;, 网址:& # 39;Http://XXXX.com/项目名称/微信-create insurance & # 39;, content type:& # 39;应用/JSON;charset = UTF-8 & # 39;, 数据类型:& # 39;json & # 39, data: JSON.stringify(param), SUCCESS:function(result){ if(result . code = = SUCCESS){ function onBridgeReady(){ weixinsbridge . invoke( & # 39;getBrandWCPayRequest & # 39,{ & # 34;appId & # 34:result.result.appId,//公共账号ID & # 34;时间戳& # 34;:result.result.timeStamp,// timestamp,1970年以来的秒数 & # 34;非核心& # 34;:result . result . non castr,//随机字符串 & # 34;套餐& # 34;:result.result.package, & # 34;signType & # 34:result.result.signType,//微信签名方式: & # 34;paySign & # 34:result.result.paySign //微信签名 }, function(RES){ if(RES . err _ msg = = & # 34;get _ brand _ WC pay _ request:ok & # 34;){ //使用上述方法判断前端返回,微信团队郑重提醒:[/h/ //res.err_msg在用户支付成功后会返回ok,但不保证绝对可靠。 window . location . href = & # 39;success . html & # 39;;//支付成功后跳转到下一页 } else { alert(& # 34;微信支付失败& # 34;); } } ); } if(type of weixinsbridge = = & # 34;未定义& # 34;){ if(document . addevent listener){ document . addevent listener(& # 39;WeixinJSBridgeReady & # 39,onBridgeReady,false); } else if(document . attach event){ document . attach event(& # 39;WeixinJSBridgeReady & # 39,onBridgeReady); document . attach event(& # 39;onWeixinJSBridgeReady & # 39,onBridgeReady); } } else { onBridgeReady(); } } else { alert(result . msg); } }, 错误:function(err){ alert(& # 34;登录失败& # 34;); console . log(err); } }); });单击按钮。界面访问成功后,微信的h5页面会自动弹出微信自带的支付页面。支付完成后,点击确定,微信会自动回调用户编写微信支付回调接口。(微信支付回调接口具体实现见第五章)
5、微信支付回调接口应用场景:
支付完成后,微信会将相关的支付结果和用户信息以数据流的形式发送给商家,商家需要接收处理,并根据文档规范返回响应。
微信通知参数的格式:(详见微信支付结果通知)
& ltxml & gt & lt;appid & gt& lt![CDATA[wx 2421 B1 c 4370 EC 43 b]]& gt;& lt/appid & gt; & lt;附加& gt& lt![CDATA[支付测试]]& gt;& lt/attach & gt; & lt;bank _ type & gt& lt![CDATA[CFT]]& gt;& lt/bank _ type & gt; & lt;fee _ type & gt& lt![CDATA[CNY]]& gt;& lt/fee _ type & gt; & lt;is _ subscribe & gt& lt![CDATA[Y]]& gt;& lt/is _ subscribe & gt; & lt;mch _ id & gt& lt![CDATA[10000100]]& gt;& lt/mch _ id & gt; & lt;nonce _ str & gt& lt![CDATA[5 D2 b 6 C2 A8 db 53831 f 7 EDA 20 af 46 e 531 c]]& gt;& lt/nonce _ str & gt; & lt;openid & gt& lt![CDATA[oupf 8 umeb 4 qrxf 22 he 3x 68 tekuke]]& gt;& lt/OpenID & gt; & lt;out _ trade _ no & gt& lt![CDATA[1409811653]]>& lt/out _ trade _ no & gt; & lt;结果代码& gt& lt![CDATA[成功]]& gt;& lt/result _ code & gt; & lt;return _ code & gt& lt![CDATA[成功]]& gt;& lt/return _ code & gt; & lt;符号& gt& lt![CDATA[b 552 ed6b 279343 CB 493 C5 DD 0d 78 ab 241]]& gt;& lt/sign & gt; & lt;time _ end & gt& lt![CDATA[20140903131540]]& gt;& lt/time _ end & gt; & lt;total _ fee & gt1 & lt/total _ fee & gt; & lt;优惠券_费用& gt& lt![CDATA[10]]>& lt/coupon _ fee & gt; & lt;优惠券_计数& gt& lt![CDATA[1]]& gt;& lt/coupon _ count & gt; & lt;优惠券类型& gt& lt![CDATA[现金]]& gt;& lt/coupon _ type & gt; & lt;coupon _ id & gt& lt![CDATA[10000]]>& lt/coupon _ id & gt; & lt;优惠券_费用& gt& lt![CDATA[100]]>& lt/coupon _ fee & gt; & lt;贸易类型& gt& lt![CDATA[JSAPI]]& gt;& lt/trade _ type & gt; & lt;transaction _ id & gt& lt![CDATA[1004400740201409030005092168]]& gt;& lt/transaction _ id & gt; & lt;/XML & gt;微信支付回调接口的实现:
@ POST @ Path(value = & # 34;微信-通知& # 34;) 公共响应微信通知(@Context HttpServletRequest请求,@Context HttpServletResponse响应) 抛出IOException,文档异常{ Try { //微信发送的xml格式的数据转换成Map格式 map
(1)链接由[统一顺序]中提交的参数notify_url设置。如果链接无法访问,商家就收不到微信通知。
(2)通知url必须是可直接访问的url,并且不能携带参数。
(3)商户系统必须通过签名验证支付结果通知的内容,并检查返回的订单金额是否与商户端订单金额一致,防止因数据泄露、资金损失而导致的“假通知”。
(4)技术人员可登录微信商户后台扫描,加入界面报警群,获取界面报警信息。
(5)同一个通知可以多次发送到商户系统。商户系统必须能够正确处理重复通知。
(6)在后台通知交互中,如果微信收到不符合规范的商家响应或超时,微信会判定本次通知失败,重新发送通知,直至成功,但微信不保证通知最终会成功。
(7)如果订单状态不明或未收到微信支付结果通知,建议商家主动拨打微信支付【查询订单】确认订单状态。(请参考第六章,注意可以通过轮询的方式监控支付指令是否已经支付。)
6、查询订单接口应用场景:
该接口提供了所有微信支付订单的查询,商家可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
需要调用查询接口:
(1)当商户后台、网络、服务器异常时,商户系统最终无法收到支付通知;
(2)调用支付接口后,返回系统错误或未知交易状态;
(3)调用支付码支付API,返回USERPAYING状态;
(4)在调用通关或撤销接口API前确认支付状态;
询价订单接口实现:
@GET @Path(value = "wechat-orderquery") @Produces(MediaType.APPLICATION_JSON) public Response WechatOrderquery(@QueryParam("outTradeNo") String outTradeNo){ Map<String, Object> result = new HashMap<String, Object>(); try { HashMap<String, String> data = new HashMap<String, String>(); data.put("out_trade_no", outTradeNo);// 商户订单号 data.put("mch_id", mchid);// 商户号 data.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串 data.put("appid", appid);// 公众账号ID String sign = WXPayUtil.generateSignature(data, paternerKey); data.put("sign", sign);// 签名 WXPay wxpay = new WXPay(MyConfig.getInstance()); //导入微信的下载的包 Map<String, String> resp = wxpay.orderQuery(data); if("FAIL".equals(resp.get("return_code"))){ throw new RuntimeException("return_code为fail, " + resp.get("return_msg")); } if("FAIL".equals(resp.get("result_code"))){ throw new RuntimeException("result_code为fail, " + resp.get("err_code_des")); } if(!"SUCCESS".equals(resp.get("trade_state"))){ throw new RuntimeException("trade_state为" + resp.get("trade_state")); } //.......业务流程...... result.put("paperno", paperno); result.put("code", SUCCESS); }catch(Exception e){ e.printStackTrace(); result.put("code", ERROR); result.put("msg", "订单查询异常"); } return Response.ok(result).build(); }