zoukankan      html  css  js  c++  java
  • 微信APP支付

    最近公司在开发新项目,需要微信支付   经历了几天时间 终于写出来了  不过微信文档也不是很容易阅读  东西不全  很多东西全靠猜

    注意:所有带加密的都需要带上微信商户秘钥(32位)  即key

    这边主要是java服务端代码   :

            1.1.首先是统一下单接口

            一。

              微信提供的下单接口 https://api.mch.weixin.qq.com/pay/unifiedorder

              通过POST接口访问

              Url是访问地址   data是微信转成xml格式的传输参数   必须是UTF-8格式的参数

            

    微信要求的格式是这样     我们只需要填写必要参数就行了。

    <xml>
       <appid>wx2421b1c4370ec43b</appid>
       <attach>支付测试</attach>
       <body>APP支付测试</body>
       <mch_id>10000100</mch_id>
       <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
       <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
       <out_trade_no>1415659990</out_trade_no>
       <spbill_create_ip>14.23.150.211</spbill_create_ip>
       <total_fee>1</total_fee>
       <trade_type>APP</trade_type>
       <sign>0CB01533B8C1EF103065174F50BCA001</sign>
    </xml>

    ==============Post 请求封装方法===================

     public static String POST(String url, String data){
             CloseableHttpClient client = HttpClients.createDefault();
             CloseableHttpResponse response = null;
     
             try {
                 HttpPost method = new HttpPost(url);
                 method.addHeader(HTTP.CONTENT_TYPE, "application/xml");
                 StringEntity entity = new StringEntity(data,"UTF-8");
                 method.setEntity(entity);
                 response  = client.execute(method);
     
                 String s = EntityUtils.toString(response.getEntity(), "UTF-8");
                 return s;
             } catch (IOException e) {
                e.printStackTrace();
             }finally {
                try
                 {
                    response.close();
                }
                 catch (Exception e)
                 {
                    e.printStackTrace();
                 }
             }
     
     
             return null;
         }

    //返回的数据也是xml格式的

         https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1    微信文档写的很清楚

         1.2.生成随机字符串

            这里我是通过UUID生成的32位随机字符串

        

     /**
         * 生成32位随机数
         * @return
         */
        public static String  getRandomNumberAlgorithm(){
            String uuid = UUID.randomUUID().toString().toUpperCase();
            String replace = uuid.replace("-", "");
            return  replace;
        }

         1.3 将Map 转换成xml格式的数据字符串

        

     /**
         * @author
         * @Description:将请求参数转换为xml格式的string 不包含CDATA标签
         * @param parameters
         *            请求参数
         * @return
         */
        public static String getRequestXml2(SortedMap<String, Object> parameters) {
            StringBuffer sb = new StringBuffer();
            sb.append("<xml>");
            Set<?> es = parameters.entrySet();
            Iterator<?> it = es.iterator();
            while (it.hasNext()) {
                @SuppressWarnings("rawtypes")
                Map.Entry entry = (Map.Entry) it.next();
                String k = entry.getKey().toString();
                String v = entry.getValue().toString();
    
                    sb.append("<" + k + ">" + v + "</" + k + ">");
    
            }
            sb.append("</xml>");
            return sb.toString();
        }

        1.4 生成签名

    public static String createSign(SortedMap<String, Object> packageParams, String apiKey) {  
            StringBuffer sb = new StringBuffer();  
            Set<?> es = packageParams.entrySet();  
            Iterator<?> it = es.iterator();  
            while (it.hasNext()) {  
                @SuppressWarnings("rawtypes")
                Map.Entry entry = (Map.Entry) it.next();  
                String k = entry.getKey().toString();  
                String v = entry.getValue().toString();  
                if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {  
                    sb.append(k + "=" + v + "&");  
                }  
            }  
            sb.append("key=" + apiKey);  
            String sign = MD5.encoder(sb.toString()).toUpperCase();  
            return sign;  
        }

        这个时候你将下单返回的参数  传递过去  你以为就成功了?    相信我   幻觉 绝对是幻觉     这个时候浏览安卓支付起掉文档    OK  需要二次加密

    ok  我们按照参数  依次写进去

        

    //返回数据
                CreateWeiXinPayOrderResponse createWeiXinPayOrderResponse = payResult(stringObjectSortedMap);
                //或者封装到map中将数据
                SortedMap<String, String> secondaryEncryption=new TreeMap<>();
                String appid = stringObjectSortedMap.get("appid").toString();
                secondaryEncryption.put("appid",appid);
                String partnerid = request.getMch_id().toString();
                secondaryEncryption.put("partnerid",partnerid);
                String prepay_id = stringObjectSortedMap.get("prepay_id").toString();
                secondaryEncryption.put("prepayid",prepay_id);
                String nonce_str = stringObjectSortedMap.get("nonce_str").toString();
                secondaryEncryption.put("noncestr",nonce_str);
                secondaryEncryption.put("package","Sign=WXPay");
                secondaryEncryption.put("timestamp",String.valueOf(time/1000));
                String sign1 = createSign("UTF-8", secondaryEncryption, request.getKey());
                stringObjectSortedMap.put("sign",sign1);
                createWeiXinPayOrderResponse.setMap(stringObjectSortedMap);

    这个时候  APP 前端有报错  -1   来我们看看微信对这个描述是怎么样的

     坑爹的其他异常   搞了一天多    而后查看签名 规则 发现

        

    也就是说  不管是签名几次  什么签名    都需要带上key  即商户密匙!!!

     顺带贴上加密 代码:

    public static String createSign(String characterEncoding, SortedMap<String, String> parameters,String key) {
            StringBuffer sb = new StringBuffer();
            Set es = parameters.entrySet();
            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=" + key);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
            String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
            return sign;
        }
    
    public static String MD5Encode(String origin, String charsetname) {
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("MD5");
                if (charsetname == null || "".equals(charsetname))
                    resultString = byteArrayToHexString(md.digest(resultString
                            .getBytes()));
                else
                    resultString = byteArrayToHexString(md.digest(resultString
                            .getBytes(charsetname)));
            } catch (Exception exception) {
            }
            return resultString;
        }
    
     private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0)
                n += 256;
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
        private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
       private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();
            for (int i = 0; i < b.length; i++)
                resultSb.append(byteToHexString(b[i]));
    
            return resultSb.toString();
        }

    OK  完事!!!

    然后是最重要的回调

     回调  

    该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。

    通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”

    不能带参数这个回调接口   需要自己提供  需要有证书的    

     这样的话我们就只能从二进制流中取得我们需要的数据了

    将数据转换为xml格式的数据  后端进行验签

    @Override
        public String weiXinNotify(HttpServletRequest request, HttpServletResponse response) {
         
            StringBuffer xmlStr = new StringBuffer();
            try {
                BufferedReader reader = request.getReader();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    xmlStr.append(line);
                }
                reader.close();
                logger.info("微信支付回调返回参数:" + xmlStr + ", 即将调用支付模块回调接口");
                //调用支付模块的回调接口,处理返回出具
                NotifyRequest req = new NotifyRequest();
                req.setXmlStr(xmlStr.toString().trim());
                NotifyResponse resp = paymentClient.weiXinPayNotify(req);
                logger.info("微信支付回调返回给微信端的数据:" + resp.getResultXml());
                BufferedOutputStream out = new BufferedOutputStream(
                        response.getOutputStream());
                out.write(resp.getResultXml().getBytes());
                out.flush();
                out.close();
            } catch (Exception e) {
                logger.error("微信回调出错: " + e.getMessage());
                throw new WebApiCommonException(WebApiCommonErrorCodeType.PayBackError);
            }
    
            return xmlStr.toString();
        }

    后端取 验签  开发时间紧 我就没封装了

    public NotifyResponse weiXinPayNotify(String xmlStr) {
            NotifyResponse notifyResponse=new NotifyResponse();
            // 获取微信配置信息并赋值
            QueryAppliactionConfigRequest req = new QueryAppliactionConfigRequest();
            req.setConfigType("weixinPay");
            req.setConfigName("key");
            QueryAppliactionConfigResponse resp = appliactionConfigClient.queryAppliactionConfig(req);
            if (resp.getData()==null||resp.getData().getList().size()<0){
                throw new PayCommonException(PayCommonErrorCodeType.PAY_CONFIG_ERROR);
            }
            try {
                SortedMap<String, Object> map = xmlParse(xmlStr);
                logger.info(xmlStr);
                //返回结果
                String return_code = map.get("return_code").toString();
                if ("SUCCESS".equals(return_code)){
                    //签名
                    String sign1 = map.get("sign").toString().toUpperCase();
                    String sign = PayCommonUtil.createSign(map, resp.getData().getList().get(0).getConfigVal());
                    //回调验签
                    if(sign.equals(sign1)||runInTest){
                        //业务结果
                        String result_code = map.get("result_code").toString();
                        if ("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){
                            //商户订单号
                            String out_trade_no = map.get("out_trade_no").toString();
                            notifyResponse.setTradeNo(out_trade_no);
                            //根据商户订单号获取订单信息
                            PayOrderInfo payOrderInfo = iPayOrderService.queryPayOrderInfoByTradeNo(out_trade_no);
                            if ("5".equals(payOrderInfo.getCurrencyType())||"4".equals(payOrderInfo.getCurrencyType())){
                                notifyResponse.setResultXml(setXml("FAIL", "该订单已失败或超时"));
                                return notifyResponse;
                            }
                            //根据商户订单号查到的状态判断是否进行下一步骤
                            payOrderInfo.setCurrencyType("1");
                            //交易金额
                            String total_fee = map.get("total_fee").toString();
                            //商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
                            if (Integer.valueOf(total_fee)!= payOrderInfo.getTradeAmount()) {
                                throw new PayCommonException(PayCommonErrorCodeType.PAY_INCORRECT_AMOUNT);
                            }
                            payOrderInfo.setTradeAmount(Integer.valueOf(total_fee));
                            //支付完成时间
                            String time_end = map.get("time_end").toString();
                            if (time_end!=null){
                                payOrderInfo.setTradeEndTime(new Date());
                            }
                            //充值方式
                            payOrderInfo.setTradeChannel(1004);
                            //微信订单号
                            String transaction_id = map.get("transaction_id").toString();
                            notifyResponse.setTradeNo(transaction_id);
                            payOrderInfo.setTransactionId(transaction_id);
                            //加入订单信息
                            if(time_end!=null){
                                //成功
                                payOrderInfo.setTradeStatus(3);
                            }
                            iPayOrderService.updatePayOrderByOrderNo(payOrderInfo);
                            logger.info("支付成功!订单号为:"+payOrderInfo.getTradeNo()+"-----"+"第三方订单号:"+payOrderInfo.getTransactionId());
                            notifyResponse.setResultXml(setXml("SUCCESS", "OK"));
                            return notifyResponse;
                        }else {
                            // 支付出错
                            notifyResponse.setResultXml(setXml("FAIL","支付订单校验出错"));
                        }
    
                    }else {
                        //验签失败
                        notifyResponse.setResultXml(setXml("FAIL","支付验签失败"));
                    }
                }else {
                    notifyResponse.setResultXml(setXml("FAIL","支付不成功"));
                }
    
    
            } catch (Exception e) {
                logger.error("支付回调异常: " + e.getMessage());
                notifyResponse.setResultXml(setXml("FAIL","支付回调异常"));
                return notifyResponse;
            }
    
            return null;
        }

      

    搞定

  • 相关阅读:
    Dubbo简介
    Centos之关机和重启命令
    VirtualBox中CentOS7.2 网络配置(固定IP+联网)
    c#Post方法封装处理
    C# 异步方法处理
    将XMLrequest 改写成fetch
    AsyncCallback
    Promise
    FETCH
    HTML DOM Event 对象
  • 原文地址:https://www.cnblogs.com/liaohongbin/p/9264876.html
Copyright © 2011-2022 走看看