zoukankan      html  css  js  c++  java
  • 支付宝,微信支付问题

    1. 新版的支付宝支付简化了代码,省去了自己去签名配置参数,拼接参数的烦恼,简单明了。

        public static String buildAlipayRequest(AlipayVo alipayVo) {
            String alipayInfo = null;
            //实例化客户端
            AlipayClient alipayClient = new DefaultAlipayClient(ALIPAY_URL, alipayVo.getAppId(), alipayVo.getAppPrivateKey(),
                    FORMAT, CHARSET_UTF8, alipayVo.getAlipayPublicKey(), SIGN_TYPE);
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody("AlipayTrade");
            model.setSubject(alipayVo.getSubject());
            model.setOutTradeNo(alipayVo.getOutTradeNo());
            model.setTimeoutExpress("30m");
            model.setTotalAmount(alipayVo.getTotalAmount());
            model.setProductCode("QUICK_MSECURITY_PAY");
            request.setBizModel(model);
            request.setNotifyUrl(alipayVo.getNotifyUrl());
            try {
                //这里和普通的接口调用不同,使用的是sdkExecute
                AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
                log.info(response.getBody());//就是orderString 可以直接给客户端请求,无需再做处理。
                alipayInfo = response.getBody();
            } catch (AlipayApiException e) {
                e.printStackTrace();
            }
            return alipayInfo;
        }

    2. 因为项目使用到了过滤器,导致签名的sign数据中“+”被替换成了“ ”,无法获取原本的sign

    java.security.SignatureException: Signature length not correct: got 253 but was expecting 256
    

        简单处理,就是替换,将空格替换成加号

    sign = sign.replace(" ", "+");

    3.  相对于支付宝文档,微信的开发比较繁琐,很多都需要自己开发实现。微信工具类,仅供参考。

    public class WxpayUtil {
    
        private static final Logger log = LoggerFactory.getLogger(WxpayUtil.class);
        private static final String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    
    
        /**
         * 获取微信预支付prepayid
         * @param weixinpayVo
         * @return
         */
        public static JSONObject buildWxpayRequest(WeixinpayVo weixinpayVo){
    
            String noncestr = getNonceStr();
    
            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
            parameters.put("appid", weixinpayVo.getAppid());// 分车企--微信分配的公众账号ID(企业号corpid即为此appId)
            parameters.put("mch_id", weixinpayVo.getMchid());// 分车企--微信商户号
            parameters.put("nonce_str", noncestr);// 随机字符串
            parameters.put("body", weixinpayVo.getSubject());// 支付内容
            parameters.put("out_trade_no", weixinpayVo.getOutTradeNo());// 商户订单号
            parameters.put("total_fee", change2Fen(weixinpayVo.getTotalAmount()));// 支付金额,单位为分
            parameters.put("spbill_create_ip", weixinpayVo.getSpbillCreateIp());// 终端IP
            parameters.put("notify_url", weixinpayVo.getNotifyUrl());// 异步请求地址
            parameters.put("trade_type", "APP");// 交易类型App支付
            String sign = creatingSign(weixinpayVo.getPartnerKey(), parameters);//分车企--
            parameters.put("sign", sign);
    
            // 调用统一接口返回的值转换为XML格式
            log.info("微信支付请求参数:"+parameters.toString());
            String xmlString = sendPost(GATEURL, parameters);
            Map<String, String> map = doXMLParse(xmlString);
            log.info("微信支付请求参数:"+(map==null?null:map.toString()));
            String prepayid = map.get("prepay_id");// 获取prepayId
    
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("prepayid", prepayid);
            jsonObject.put("noncestr", noncestr);
            return jsonObject;
        }
    
        /**
         * POST请求,Map形式数据
         * @param url 请求地址
         * @param param 请求数据
         * @param charset 编码方式
         */
        public static String sendPost(String url, Map<Object, Object> param) {
    
            StringBuffer buffer = new StringBuffer();
            buffer.append("<xml>");
            if (param != null && !param.isEmpty()) {
                for (Map.Entry<Object, Object> entry : param.entrySet()) {
                    if(entry.getKey().equals("attach") || entry.getKey().equals("body") || entry.getKey().equals("sign")){
                        buffer.append("<"+entry.getKey()+">");
                        buffer.append("<![CDATA["+entry.getValue()+"]]>");
                        buffer.append("</"+entry.getKey()+">");
                    }else{
                        buffer.append("<"+entry.getKey()+">");
                        buffer.append(entry.getValue());
                        buffer.append("</"+entry.getKey()+">");
                    }
                }
            }
            buffer.append("</xml>");
            PrintWriter out = null;
            BufferedReader in = null;
            String result = "";
            try {
                URL realUrl = new URL(url);
                // 打开和URL之间的连接
                URLConnection conn = realUrl.openConnection();
                // 设置通用的请求属性
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                // 获取URLConnection对象对应的输出流
                out = new PrintWriter(conn.getOutputStream());
                // 发送请求参数
                out.print(buffer);
                // flush输出流的缓冲
                out.flush();
                // 定义BufferedReader输入流来读取URL的响应
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                String line;
                while ((line = in.readLine()) != null) {
                    result += line;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 使用finally块来关闭输出流、输入流
            finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            return result;
        }
    
        /**
         * 生成签名
         * @param PartnerKey
         * @param characterEncoding
         * @param parameters
         * @return https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
         */
        public static String creatingSign(String PartnerKey, SortedMap<Object, Object> parameters) {
            StringBuffer sb = new StringBuffer();
            Iterator it = parameters.entrySet().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=" + PartnerKey);
            return  MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        }
        /**
         * 金额元-> 分
         * @param money
         * @return
         */
        public static int change2Fen(String money){
            if(null==money){
                return 0;
            }
            return new BigDecimal(money).multiply(new BigDecimal(100)).intValue();
        }
    
        /**
         * <P>(将分为单位的转换为元 (除100) )</P>
         */
        /**金额为分的格式 */
        public static final String CURRENCY_FEN_REGEX = "\-?[0-9]+";
        public static String change2Yuan(String amount) throws Exception{
            return String.valueOf(new BigDecimal(amount).divide(new BigDecimal(100)));
        }
    
        /**
         * <P>(32位内的随机串,防重发)</P>
         */
        public static String getNonceStr() {
            Random random = new Random();
            return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
        }
    
        /**
         * 获取时间戳
         * @return
         */
        public static String getTimeStamp() {
            return String.valueOf(System.currentTimeMillis() / 1000);
        }
    
        /**
         * 获取ip地址
         * @param request
         * @return
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ip = request.getHeader("x-forwarded-for");
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            return ip;
        }
    
        /**
         * 解析String类型的xml流对象InputStream
         * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
         * @param strxml
         * @return
         * @throws JDOMException
         * @throws IOException
         */
        public static Map<String,String> doXMLParse(String strxml){
            if(null == strxml || "".equals(strxml)) {
                return null;
            }
    
            Map<String,String> map = new HashMap<String,String>();
            InputStream in =null;
           try{
               in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
               SAXBuilder builder = new SAXBuilder();
               builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
               builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
               builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
               builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
               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);
                   }
                   map.put(k, v);
               }
           }catch (Exception e){
                e.printStackTrace();
           }finally {
                if(in!=null){
                    //关闭流
                    IOUtils.closeQuietly(in);
                }
           }
           return map;
        }
    
        /**
         * 获取子结点的xml
         * @param children
         * @return String
         */
        private 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 String setResultXML(String return_code, String return_msg) {
            return "<xml><return_code><![CDATA[" + return_code
                    + "]]></return_code><return_msg><![CDATA[" + return_msg
                    + "]]></return_msg></xml>";
        }
    }
    View Code

    4. 微信支付的过程,统一下单获取prepayid,然后组装请求参数返回手机端发起支付。

    • 需要注意的是,noncestr随机字符串参数在两次请求中是一样的
    • 微信签名参数partnerkey, key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
    • 微信的金额是以为单位的
  • 相关阅读:
    【Java集合】试读LinkedList源码
    【Java集合】试读ArrayList源码
    类加载器ClassLoader的理解
    Spring中Bean的不同配置方式
    Spring中Bean的生命周期
    关于反射的一点理解
    Vector与ArrayList 的理解
    java多态的实现原理(JVM调用过程)(综合多篇文章,参考见文末)
    并发编程的模型分类(转载于https://link.zhihu.com/?target=http%3A//www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/)强烈推荐!
    Thread线程类
  • 原文地址:https://www.cnblogs.com/yangjiming/p/11410282.html
Copyright © 2011-2022 走看看