java实现微信h5支付
2017-12-09 16:51
525 查看
微信h5支付需要在微信商户平台-》产品中心开通h5支付。
官网提供的开发文档中需要的参数:
h5支付主要是scene_info中的参数wap_url必须是可以访问到的地址。spbill_create_ip的获取必须和调起微信支付的ip一致。
代码实现如下:
action中代码:
微信支付签名代码:
向微信发送post数据并解析返回的微信订单号
解析微信返回的xml
封装支付参数的实体类:(参数名与官网参数名一致,此类封装了h5、app支付和退款的参数)
前端获取ip:
在h5支付时出现的一些问题
这个页面就是你传递的ip和微信获取的发起支付的ip不一致,这个时候你就要检查获取ip的代码
出现这个情况的原因有两个1.你的scene_info中的wap_url与你配置的授权域名不一致,需要去商户平台的产品中心->开发配置中修改 2. 配置的redirect_url与授权域名不一致,需要修改。
这个地方还有其他的一些错误,我没遇到,以上两个错误我遇到很多次,后来查看官方文档才看见的(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4)。
官网提供的开发文档中需要的参数:
h5支付主要是scene_info中的参数wap_url必须是可以访问到的地址。spbill_create_ip的获取必须和调起微信支付的ip一致。
代码实现如下:
action中代码:
private static DecimalFormat df = new DecimalFormat("0.00"); @Action("weChatUnifiedorder") public String weChatUnifiedorder(){ try { df.setRoundingMode(RoundingMode.HALF_UP); // 获取用户ID String memberId = Struts2Utils.getRequest().getParameter("memberId"); String integer = Struts2Utils.getRequest().getParameter("integer"); String type = Struts2Utils.getRequest().getParameter("type"); //我这里的ip直接从前端页面获取部分手机不能获取 String spbill_create_ip = Struts2Utils.getRequest().getParameter("ip"); if (StringUtils.isNotBlank(memberId) && StringUtils.isNotBlank(integer) && StringUtils.isNotBlank(type)) { Integer score = Integer.valueOf(integer); // 产生订单号 String outTradeNo = UuIdUtils.getUUID(); // WeixinConfigUtils config = new WeixinConfigUtils(); // 参数组 String appid = config.appid; String mch_id = config.mch_id; String nonce_str = RandCharsUtils.getRandomString(16); String body = ""; if (type.equals("0")) { body = "购买" + score + "积分,支付" + df.format(score / 100) + "元"; } else if (type.equals("1")) { body = "购买商品支付" + String.valueOf(score / 100) + "元"; } String attach = "暂时无用(这个用于区分不同版本)"; String out_trade_no = outTradeNo; int total_fee = score;// 单位是分,现在按照ios传递过来的参数进行 String notify_url = config.notify_url; String trade_type = "MWEB"; // 参数:开始生成第一次签名 parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("body", body); parameters.put("nonce_str", nonce_str); parameters.put("attach", attach); parameters.put("out_trade_no", out_trade_no); parameters.put("total_fee", total_fee); parameters.put("notify_url", notify_url); parameters.put("trade_type", trade_type); parameters.put("spbill_create_ip", spbill_create_ip); parameters.put("scene_info", "'h5_info':{'type':'Wap','wap_url':'www.abc.com(必须可以直接访问,**这里不要带http://**)','wap_name': '应用名(会在微信订单页显示)'}"); String sign = WXSignUtils.createSign("UTF-8", parameters); // 微信统一下单 unifiedorder.setAppid(appid); unifiedorder.setMch_id(mch_id); unifiedorder.setNonce_str(nonce_str); unifiedorder.setSign(sign); unifiedorder.setBody(body); unifiedorder.setAttach(attach); unifiedorder.setOut_trade_no(out_trade_no); unifiedorder.setTotal_fee(total_fee); unifiedorder.setSpbill_create_ip(spbill_create_ip); unifiedorder.setNotify_url(notify_url); unifiedorder.setTrade_type(trade_type); unifiedorder.setScene_info("'h5_info':{'type':'Wap','wap_url':'http://www.abc.com(必须可以直接访问**这里不要带http://**)','wap_name': '应用名(会在微信订单页显示)'}"); Map<String, String> msgMap = HttpXmlUtils.getUrl(unifiedorder); if (msgMap.get("return_code").equals("SUCCESS") && msgMap.get("result_code").equals("SUCCESS")) { aLiPay.setApp_id(appid); aLiPay.setSubject(body); aLiPay.setScore(integer); aLiPay.setGmt_create(simpleDateFormat.format(new Date())); if (null != memberId && !"".equals(memberId)) { aLiPay.setMember(memberService.getMemberById(Integer .valueOf(memberId))); } aLiPay.setOut_trade_no(out_trade_no); aLiPay.setPayStatus(PayStatusEnum.WAITPAY.getComment()); aLiPay.setPayType(PayTypeEnum.WEIXIN.getComment()); aLiPay.setState(0); aLiPay.setTotal_amount(String.valueOf(total_fee / 100)); aLiPay.setSign(sign); aLiPay.setTotalFee(total_fee); aLiPayService.addALiPay(aLiPay); msg.put("ok", true); //将回调地址发回前端,支付完成后会自动跳转该页面(回调url必须带http://) msg.put("url", msgMap.get("mweb_url")+"&redirect_url=http://www.jabc.com/member/toMemberIntegral.do"); Struts2Utils.renderJson(msg); }else { msg.put("ok", false); } } return NONE; } catch (Exception e) { log.error(e.getMessage()); return "500"; } }
微信支付签名代码:
public class WXSignUtils { /** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("rawtypes") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + weixinConstant.KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } }
向微信发送post数据并解析返回的微信订单号
public class HttpXmlUtils { /** * h5支付时 解析返回的值并返回prepareid * @throws IOException * @throws JDOMException */ public static Map<String, String> getUrl(Unifiedorder unifiedorder) throws JDOMException, IOException{ //构造xml String xmlInfo = HttpXmlUtils.xmlH5Info(unifiedorder); String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; String method = "POST"; //获取微信返回的结果 String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString(); //解析返回的xml ParseXMLUtils.jdomParseXml(weixinPost); StringReader read = new StringReader(weixinPost); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根节点 List<org.jdom.Element> list = root.getChildren(); String prepayId =null; Map<String, String> msg = new HashMap<String, String>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { msg.put(element.getName(), element.getText()); } } return msg; } /** * 构造xml参数 * @param xml * @return */ public static String xmlH5Info(Unifiedorder unifiedorder){ if(unifiedorder!=null){ StringBuffer bf = new StringBuffer(); bf.append("<xml>"); bf.append("<appid><![CDATA["); bf.append(unifiedorder.getAppid()); bf.append("]]></appid>"); bf.append("<mch_id><![CDATA["); bf.append(unifiedorder.getMch_id()); bf.append("]]></mch_id>"); bf.append("<nonce_str><![CDATA["); bf.append(unifiedorder.getNonce_str()); bf.append("]]></nonce_str>"); bf.append("<sign><![CDATA["); bf.append(unifiedorder.getSign()); bf.append("]]></sign>"); bf.append("<body><![CDATA["); bf.append(unifiedorder.getBody()); bf.append("]]></body>"); bf.append("<attach><![CDATA["); bf.append(unifiedorder.getAttach()); bf.append("]]></attach>"); bf.append("<out_trade_no><![CDATA["); bf.append(unifiedorder.getOut_trade_no()); bf.append("]]></out_trade_no>"); bf.append("<total_fee><![CDATA["); bf.append(unifiedorder.getTotal_fee()); bf.append("]]></total_fee>"); bf.append("<spbill_create_ip><![CDATA["); bf.append(unifiedorder.getSpbill_create_ip()); bf.append("]]></spbill_create_ip>"); bf.append("<notify_url><![CDATA["); bf.append(unifiedorder.getNotify_url()); bf.append("]]></notify_url>"); bf.append("<trade_type><![CDATA["); bf.append(unifiedorder.getTrade_type()); bf.append("]]></trade_type>"); bf.append("<scene_info><![CDATA["); bf.append(unifiedorder.getScene_info()); bf.append("]]></scene_info>"); bf.append("</xml>"); return bf.toString(); } return ""; } /** * post请求并得到返回结果 * @param requestUrl * @param requestMethod * @param output * @return */ public static String httpsRequest(String requestUrl, String requestMethod, String output) { try{ URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer.toString(); }catch(Exception ex){ ex.printStackTrace(); } return ""; } }
解析微信返回的xml
/** * 3、JDOM解析XML * 解析的时候自动去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static void jdomParseXml(String xml){ try { StringReader read = new StringReader(xml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根节点 List<org.jdom.Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { } } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } }
封装支付参数的实体类:(参数名与官网参数名一致,此类封装了h5、app支付和退款的参数)
package com.wellness.platfront.entity.weixin; import java.io.Serializable; import com.wellness.platfront.entity.member.Member; /** * 统一下单提交为微信的参数 * @author * @date 2017年08月11日 */ public class Unifiedorder implements Serializable{ private static final long serialVersionUID = 1L; //微信支付表id private Integer weixinId; //微信分配的公众账号ID(企业号corpid即为此appId) private String appid; //商户id private String mch_id; //终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" private String device_info; //随机字符串:数字+大写字母的组合,32位 private String nonce_str; //签名 private String sign; //商品或支付单简要描述 private String body; //商品名称明细列表 private String detail; //附加参数(例如:用于区别本商户不同的分店) private String attach; //商户系统内部的订单号 private String out_trade_no; //货币类型:符合ISO 4217标准的三位字母代码,默认人民币:CNY private String fee_type; //总金额 private int total_fee; //APP和网页支付提交[用户端ip],Native支付填调用微信支付API的机器IP。 private String spbill_create_ip; //订单生成时间,格式为yyyyMMddHHmmss, private String time_start; //订单失效时间,格式为yyyyMMddHHmmss,最短失效时间间隔必须大于5分钟[支付宝是30分钟,同样30分钟] private String time_expire; //商品标记,代金券或立减优惠功能的参数 private String goods_tag; //接收微信支付异步通知回调地址 private String notify_url; //交易类型:JSAPI,NATIVE,APP h5为 MWEB private String trade_type; //trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 private String product_id; //no_credit--指定不能使用信用卡支付 private String limit_pay; //trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识 private String openid; //商户内部自己的退款单号 private String out_refund_no; //退款总金额单位为分 private int refund_fee; //操作员的id默认为mch_id private String op_user_id; //微信官方提供的订单号 private String prepayid; //记录所对应的member private Member member; //返回给微信的状态码(用于支付回调时) public String return_code; //微信h5支付时候的场景信息官方的信息模板 {"h5_info"://h5支付固定传"h5_info" //{"type":"",//场景类型 "wap_url":"",//WAP网站URL地址"wap_name": ""//WAP 网站名}} public String scene_info; public String getScene_info() { return scene_info; } public void setScene_info(String scene_info) { this.scene_info = scene_info; } public String getReturn_code() { return return_code; } public void setReturn_code(String return_code) { this.return_code = return_code; } public String getAppid() { return appid; } public String getMch_id() { return mch_id; } public String getDevice_info() { return device_info; } public String getNonce_str() { return nonce_str; } public String getSign() { return sign; } public String getBody() { return body; } public String getDetail() { return detail; } public String getAttach() { return attach; } public String getOut_trade_no() { return out_trade_no; } public String getFee_type() { return fee_type; } public int getTotal_fee() { return total_fee; } public String getSpbill_create_ip() { return spbill_create_ip; } public String getTime_start() { return time_start; } public String getTime_expire() { return time_expire; } public String getGoods_tag() { return goods_tag; } public String getNotify_url() { return notify_url; } public String getTrade_type() { return trade_type; } public String getProduct_id() { return product_id; } public String getLimit_pay() { return limit_pay; } public String getOpenid() { return openid; } public void setAppid(String appid) { this.appid = appid; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public void setDevice_info(String device_info) { this.device_info = device_info; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public void setSign(String sign) { this.sign = sign; } public void setBody(String body) { this.body = body; } public void setDetail(String detail) { this.detail = detail; } public void setAttach(String attach) { this.attach = attach; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public void setFee_type(String fee_type) { this.fee_type = fee_type; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public void setTime_start(String time_start) { this.time_start = time_start; } public void setTime_expire(String time_expire) { this.time_expire = time_expire; } public void setGoods_tag(String goods_tag) { this.goods_tag = goods_tag; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public void setProduct_id(String product_id) { this.product_id = product_id; } public void setLimit_pay(String limit_pay) { this.limit_pay = limit_pay; } public void setOpenid(String openid) { this.openid = openid; } public String getOut_refund_no() { return out_refund_no; } public void setOut_refund_no(String out_refund_no) { this.out_refund_no = out_refund_no; } public int getRefund_fee() { return refund_fee; } public void setRefund_fee(int refund_fee) { this.refund_fee = refund_fee; } public Integer getWeixinId() { return weixinId; } public void setWeixinId(Integer weixinId) { this.weixinId = weixinId; } public Member getMember() { return member; } public void setMember(Member member) { this.member = member; } public String getPrepayid() { return prepayid; } public void setPrepayid(String prepayid) { this.prepayid = prepayid; } public String getOp_user_id() { return op_user_id; } public void setOp_user_id(String op_user_id) { this.op_user_id = op_user_id; } }
前端获取ip:
<script src="http://pv.sohu.com/cityjson?ie=utf-8"> var ip = returnCitySN.cip;(有些手机不行,目前发现的是魅族和三星)判断正不正确可以查自己的ip地址,微信获取的是你所处网络的ip。也可以在java后台获取 </script>
在h5支付时出现的一些问题
这个页面就是你传递的ip和微信获取的发起支付的ip不一致,这个时候你就要检查获取ip的代码
出现这个情况的原因有两个1.你的scene_info中的wap_url与你配置的授权域名不一致,需要去商户平台的产品中心->开发配置中修改 2. 配置的redirect_url与授权域名不一致,需要修改。
这个地方还有其他的一些错误,我没遇到,以上两个错误我遇到很多次,后来查看官方文档才看见的(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4)。
相关文章推荐
- Java-实现微信H5支付
- java实现微信H5支付方法详解
- 微信公众号开发---实现微信扫一扫支付 (java)
- java实现微信App支付服务端
- Java SpringMVC实现PC端网页微信扫码支付完整版
- Java 关于支付的实现(微信、支付宝)
- 微信公众号开发---微信服务号支付实现(java)
- JAVA微信扫码支付模式二功能实现以及回调
- 【JAVA笔记】JAVA后端实现统一扫码支付:微信篇
- 【JAVA笔记】JAVA后端实现统一扫码支付:微信篇_0
- JAVA微信扫码支付模式二线上支付功能实现以及回调
- 微信APP支付-Java后台实现
- 微信APP支付-Java后台实现
- 详解JAVA后端实现统一扫码支付:微信篇
- Java SpringMVC实现PC端网页微信扫码支付完整版
- JAVA微信扫码支付模式二功能实现以及回调
- 微信 pc 公众号(微信内浏览器)支付 实现 (h5调用 微信 还没实现 )
- Java SpringMVC实现PC端网页微信扫码支付完整版
- 【Java】实现微信扫一扫支付
- JAVA微信扫码支付模式二功能实现以及回调