微信公众号支付

时间:2022-09-24 17:52:20

小编公司的项目是公众号里嵌H5页面调起微信支付,这也属于公众号支付的内容。

业务流程图:
微信公众号支付

统一下单接口地址:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1这里写链接内容

小编项目业务流程:
点击支付按钮—-查询收获地址–若无,提示,若有提交订单—提交订单成功—调起支付—支付成功修改订单状态

在提交订单返回success时调起支付:

/*支付*/
$http.get(urlpath + '/weChat/pay.do', {
    params: {
        username: localStorage.getItem("username"),
        totalMoney: productInfo.nhprice * buyCount
        }
}).success(function(response) {
  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 {
            WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId": response.data.appid, //调后台pay方法返回的
                "timeStamp": response.data.timeStamp,
                "nonceStr": response.data.nonceStr,
                "package": response.data.packageValue,
                "signType": "MD5",
                "paySign": response.data.paySign
            },
        function(res) {
          if(res.err_msg == "get_brand_wcpay_request:ok") {
            /*更新订单状态*/
           }
        }
})

controller中Pay方法:

@RequestMapping(value="/pay")
    public String pay(HttpServletRequest request,HttpServletResponse response){
        /*根据username查询用户的openid*/
        String username=request.getParameter("username");
        User user = userH5Service.getUserByUsername(username);
        String openid="";
        if(user!=null){
             openid=user.getOpenid();
        }
        String money=request.getParameter("totalMoney");        
        response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有域名访问
        String GZHID = "";// 微信公众号id,对应自己的账号
        String GZHSecret = "";// 微信公众号密钥id,对应自己的账号
        String SHHID = "";// 财付通商户号,对应自己的账号
        String SHHKEY = "";// 商户号对应的密钥,对应自己的账号

        /*------1.获取参数信息-------*/
        //商户订单号 
        String out_trade_no = WXPayUtil.generateUUID();
        //金额转化为分为单位
        String finalmoney = WeChat.getMoney(money);
        /*------2.生成预支付订单需要的的package数据-------*/ 
        //随机数 
        String nonce_str= WXPayUtil.generateUUID();//MD5Encode.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes()).toUpperCase();
        //订单生成的机器 IP
        String spbill_create_ip = request.getRemoteAddr();      
        //交易类型 :jsapi代表微信公众号支付
        String trade_type = "JSAPI";
        //这里notify_url是 微信处理完支付后的回调的应用系统接口url。
        String notify_url =".../weixinNotify.do"; //自己项目回调的地址
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid",  GZHID);  
        packageParams.put("mch_id",  SHHID);  
        packageParams.put("nonce_str", nonce_str);  
        packageParams.put("body", "test");  
        packageParams.put("out_trade_no", out_trade_no);  
        packageParams.put("total_fee", finalmoney);  
        packageParams.put("spbill_create_ip", spbill_create_ip);  
        packageParams.put("notify_url", notify_url);  
        packageParams.put("trade_type", trade_type); 
        packageParams.put("openid", openid); 

        String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String prepay_id="";
        try {
            String xml1 = WXPayUtil.generateSignedXml(packageParams, SHHKEY);
            System.err.println(xml1);               
            prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml1);
            System.out.println("prepay_id===="+prepay_id);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
       /* ------3.将预支付订单的id和其他信息生成签名并一起返回 -------*/ 
         //随机数 
         nonce_str= WXPayUtil.generateUUID();
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String packages ="prepay_id="+prepay_id;
        finalpackage.put("appId",  GZHID);  
        finalpackage.put("timeStamp", timestamp);  
        finalpackage.put("nonceStr", nonce_str);  
        finalpackage.put("package", packages);  
        finalpackage.put("signType", "MD5");
        String finalsign="";
        try{
             finalsign=WXPayUtil.generateSignature(finalpackage, SHHKEY);
        }catch(Exception e1){
            e1.printStackTrace();
        }
        JSONObject jo = new JSONObject();
        JSONObject jos = new JSONObject(); // 声明对象
        jos.put("appid", GZHID);
        jos.put("timeStamp", timestamp);
        jos.put("nonceStr", nonce_str);
        jos.put("packageValue", packages);
        jos.put("paySign", finalsign);
        jo.put("code", 200);
        jo.put("msg","成功");
        jo.put("success",true);
        jo.put("data",jos.toString());
        String json=jo.toString();
        OutJson.outJson(response, json);
       return null;
    }

回调方法weixinNotify,小编也是写在controller里的:

/** * 提交支付后的微信异步返回接口 */
    @RequestMapping(value = "/weixinNotify")
    public void weixinNotify(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有域名访问
        String out_trade_no = null;
        String return_code = null;
        try {
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String resultStr = new String(outSteam.toByteArray(), "utf-8");
            System.out.println("支付成功的回调:" + resultStr);
            // logger.info("支付成功的回调:"+resultStr);
            Map<String, Object> resultMap = parseXmlToList(resultStr);
            String result_code = (String) resultMap.get("result_code");
            String is_subscribe = (String) resultMap.get("is_subscribe");
            String transaction_id = (String) resultMap.get("transaction_id");
            String sign = (String) resultMap.get("sign");
            String time_end = (String) resultMap.get("time_end");
            String bank_type = (String) resultMap.get("bank_type");

            out_trade_no = (String) resultMap.get("out_trade_no");
            return_code = (String) resultMap.get("return_code");

            request.setAttribute("out_trade_no", out_trade_no);
            // 通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
            response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
        } catch (Exception e) {
            // logger.error("微信回调接口出现错误:",e);
            try {
                response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        if (return_code.equals("SUCCESS")) {
            System.out.println("支付成功+SUCCESS");
            // 支付成功的业务逻辑
        } else {
            System.out.println("支付失败+SUCCESS");
            // 支付失败的业务逻辑
        }
    }

工具类:
WXPayUtil:

public class WXPayUtil {

    /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
// WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /** * 将Map转换为XML格式的字符串 * * @param data Map类型数据 * @return XML格式的字符串 * @throws Exception */
    public static String mapToXml(Map<String, String> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }


    /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @return 含有sign字段的XML */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, SignType.MD5);
    }

    /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名类型 * @return 含有sign字段的XML */
    public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
        String sign = generateSignature(data, key, signType);
        data.put(WXPayConstants.FIELD_SIGN, sign);
        return mapToXml(data);
    }


    /** * 判断签名是否正确 * * @param xmlStr XML格式数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = xmlToMap(xmlStr);
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }

    /** * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 * * @param data Map类型数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */
    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, SignType.MD5);
    }

    /** * 判断签名是否正确,必须包含sign字段,否则返回false。 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名方式 * @return 签名是否正确 * @throws Exception */
    public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /** * 生成签名 * * @param data 待签名数据 * @param key API密钥 * @return 签名 */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, SignType.MD5);
    }

    /** * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 * * @param data 待签名数据 * @param key API密钥 * @param signType 签名方式 * @return 签名 */
    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    /** * 获取随机字符串 Nonce Str * * @return String 随机字符串 */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }


    /** * 生成 MD5 * * @param data 待处理数据 * @return MD5结果 */
    public static String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /** * 生成 HMACSHA256 * @param data 待处理数据 * @param key 密钥 * @return 加密结果 * @throws Exception */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /** * 日志 * @return */
// public static Logger getLogger() {
// Logger logger = LoggerFactory.getLogger("wxpay java sdk");
// return logger;
// }

    /** * 获取当前时间戳,单位秒 * @return */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /** * 获取当前时间戳,单位毫秒 * @return */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

    /** * 生成 uuid, 即用来标识一笔单,也用做 nonce_str * @return */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

}

WeChat:

public class WeChat {

    /** * 元转换成分 * @param money * @return */
    public static String getMoney(String amount) {
        if(amount==null){
            return "";
        }
        // 金额转化为分为单位
        String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额 
        int index = currency.indexOf(".");  
        int length = currency.length();  
        Long amLong = 0l;  
        if(index == -1){  
            amLong = Long.valueOf(currency+"00");  
        }else if(length - index >= 3){  
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
        }else if(length - index == 2){  
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
        }else{  
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
        }  
        return amLong.toString(); 
    }

}

GetWxOrderno:

public class GetWxOrderno {
     public static DefaultHttpClient httpclient;

  static
  {
      httpclient = new DefaultHttpClient();
      httpclient = (DefaultHttpClient)HttpClientConnectionManager.getSSLInstance(httpclient);
  }


  /** *description:获取预支付id *@param url *@param xmlParam *@return * @author ex_yangxiaoyi * @see */
  public static String getPayNo(String url,String xmlParam){
      DefaultHttpClient client = new DefaultHttpClient();
      client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
      HttpPost httpost= HttpClientConnectionManager.getPostMethod(url);
      String prepay_id = "";
     try {
         httpost.setEntity(new StringEntity(xmlParam, "UTF-8"));
         HttpResponse response = httpclient.execute(httpost);
         String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
        if(jsonStr.indexOf("FAIL")!=-1){
            return prepay_id;
        }
        Map map = doXMLParse(jsonStr);
        prepay_id  = (String) map.get("prepay_id");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return prepay_id;
  }



  /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */
    public static Map doXMLParse(String strxml) throws Exception {
        if(null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }
    /** * 获取子结点的xml * @param children * @return String */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if(!children.isEmpty()) {
            Iterator it = children.iterator();
            while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }
  public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

}

解析微信通知xml,也是放在controller里的:

/** * description: 解析微信通知xml * * @param xml * @return * @author ex_yangxiaoyi * @see */
    @SuppressWarnings({ "unused", "rawtypes", "unchecked" })
    private static Map parseXmlToList(String xml) {
        Map retMap = new HashMap();
        try {
            StringReader read = new StringReader(xml);
            // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
            InputSource source = new InputSource(read);
            // 创建一个新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();
            // 通过输入源构造一个Document
            Document doc = (Document) sb.build(source);
            Element root = doc.getRootElement();// 指向根节点
            List<Element> es = root.getChildren();
            if (es != null && es.size() != 0) {
                for (Element element : es) {
                    retMap.put(element.getName(), element.getValue());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retMap;
    }

总结:
小编有写的不明白的地方或者少了那个工具类,欢迎留言!