java实现微信h5支付详解编程语言

微信h5支付需要在微信商户平台-》产品中心开通h5支付。
这里写图片描述
官网提供的开发文档中需要的参数:
这里写图片描述这里写图片描述这里写图片描述
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支付和退款的参数)

import java.io.Serializable; 
 
/** 
 * 统一下单提交为微信的参数 
 * @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)。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/13976.html

(0)
上一篇 2021年7月19日 16:26
下一篇 2021年7月19日 16:26

相关推荐

发表回复

登录后才能评论