• app微信支付java后台实现(spring mvc)


                    前言

      由于项目需要做微信支付和支付宝支付,自己花时间研究了一下,也百度了很久,最

    后发现百度上很多都讲得不是很详细,对于新手小白来说,还是比较难得,所以自己整理

    了一下自己写的,也有很多是参考的,希望能给大家带来帮助

    一 图示(乱画的,方便看)

    二 工具类准备

       2.1由于微信支付发送的https的post请求,所以需要写个工具类来实现https的发送

       发送https需要自定义一个TrustManager实现X509TrustManager类

    package com.rubbish.jinzhu.utils;
    
    import javax.net.ssl.X509TrustManager;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    public class TrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    
        }
    
        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    
        }
    
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }
    

      

       发送https请求工具类

    package com.rubbish.jinzhu.utils;
    
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.URL;
    
    public class HttpsUtil {
    
    	/**
    	 *
    	 * @param requestUrl
    	 * @param requestMethod
    	 * @param outputStr
    	 * @return
    	 * 发送https请求
    	 */
    	public static String httpsRequest(String requestUrl,String requestMethod,String outputStr){
    		StringBuffer buffer=null;
    		try{
    			SSLContext sslContext=SSLContext.getInstance("SSL");
    			TrustManager[] tm={new TrustManager()};
    			sslContext.init(null, tm, new java.security.SecureRandom());;
    			SSLSocketFactory ssf=sslContext.getSocketFactory();
    			URL url=new URL(requestUrl);
    			HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
    			conn.setDoOutput(true);
    			conn.setDoInput(true);
    			conn.setUseCaches(false);
    			conn.setRequestMethod(requestMethod);
    			conn.setSSLSocketFactory(ssf);
    			conn.connect();
    			if(null!=outputStr){
    				OutputStream os=conn.getOutputStream();
    				os.write(outputStr.getBytes("utf-8"));
    				os.close();
    			}
    
    			//读取服务器端返回的内容
    			InputStream is=conn.getInputStream();
    			InputStreamReader isr=new InputStreamReader(is,"utf-8");
    			BufferedReader br=new BufferedReader(isr);
    			buffer=new StringBuffer();
    			String line=null;
    			while((line=br.readLine())!=null){
    				buffer.append(line);
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    		return buffer.toString();
    	}
    }
    

      

      2.2 微信统一下单需要的参数(接口文档地址https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)

      2.2.1 随机字符串生成,签名,xml转换工具类

    package com.rubbish.jinzhu.utils;
    
    import org.jdom.JDOMException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.xml.sax.SAXException;
    
    import javax.xml.parsers.ParserConfigurationException;
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Random;
    import java.util.SortedMap;
    
    public class PayCommonUtil {
    	private static Logger logger = LoggerFactory.getLogger(PayCommonUtil.class);
    	
    	/**
    	 * 自定义长度随机字符串
    	 * @param length
    	 * @return
    	 */
    	public static String createConceStr(int length) {
    		String strs = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    		String str = "";
    		for (int i = 0; i < length; i++) {
    			// str +=strs.substring(0, new Random().nextInt(strs.length()));
    			char achar = strs.charAt(new Random().nextInt(strs.length() - 1));
    			str += achar;
    		}
    		return str;
    	}
        
    	/**
    	 * 默认16 位随机字符串
    	 * @return
    	 */
    	public static String CreateNoncestr() {
    		String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    		String res = "";
    		for (int i = 0; i < 16; i++) {
    			Random rd = new Random();
    			res += chars.charAt(rd.nextInt(chars.length() - 1));
    		}
    		return res;
    	}
    
    	/**
    	 * 签名工具
    	 * @date 2014-12-5下午2:29:34
    	 * @Description:sign签名
    	 * @param characterEncoding
    	 *            编码格式 UTF-8
    	 * @param parameters
    	 *            请求参数
    	 * @return
    	 */
    	public static String createSign(String characterEncoding,
    			Map<String, Object> parameters) {
    		StringBuffer sb = new StringBuffer();
    		Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
    		while (it.hasNext()) {
    			Entry <String,Object>entry = (Entry<String,Object>) it.next();
    			String key = (String) entry.getKey();
    			Object value = entry.getValue();//去掉带sign的项
    			if (null != value && !"".equals(value) && !"sign".equals(key)
    					&& !"key".equals(key)) {
    				sb.append(key + "=" + value + "&");
    			}
    		}
    		sb.append("key=" + ConfigUtil.API_KEY);
    		//注意sign转为大写
    		return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    	}
    
    	/**
    	 * @date
    	 * @Description:将请求参数转换为xml格式的string
    	 * @param parameters
    	 *            请求参数
    	 * @return
    	 */
    	public static String getRequestXml(SortedMap<String, Object> parameters) {
    		StringBuffer sb = new StringBuffer();
    		sb.append("<xml>");
    		Iterator<Entry<String, Object>> iterator = parameters.entrySet().iterator();
    		while (iterator.hasNext()) {
    			Entry<String,Object> entry = (Entry<String,Object>) iterator.next();
    			String key = (String) entry.getKey();
    			String value = (String) entry.getValue();
    			sb.append("<" + key + ">" + value + "</" + key + ">");
    		}
    		sb.append("</xml>");
    		return sb.toString();
    	}
    
    	public static String setXML(String return_code, String return_msg) {
    		return "<xml><return_code><![CDATA[" + return_code
    				+ "]]></return_code><return_msg><![CDATA[" + return_msg
    				+ "]]></return_msg></xml>";
    	}
    	
    	
        /**
         * 检验API返回的数据里面的签名是否合法
         *
         * @param responseString API返回的XML数据字符串
         * @return API签名是否合法
         * @throws ParserConfigurationException
         * @throws IOException
         * @throws SAXException
         */
        public static boolean checkIsSignValidFromResponseString(String responseString) {
    
            try {
            	SortedMap<String, Object> map = XMLUtil.doXMLParse(responseString);
                logger.debug(map.toString());
                String signFromAPIResponse = map.get("sign").toString();
                if ("".equals(signFromAPIResponse) || signFromAPIResponse == null) {
                    logger.debug("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
                    return false;
                }
                logger.debug("服务器回包里面的签名是:" + signFromAPIResponse);
                map.put("sign", "");
                String signForAPIResponse = PayCommonUtil.createSign("UTF-8", map);
                if (!signForAPIResponse.equals(signFromAPIResponse)) {
                    logger.debug("数据签名验证不通过");
                    return false;
                }
                logger.debug("恭喜,数据签名验证通过!!!");
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }
    

     

    微信的一些固定参数

    package com.rubbish.jinzhu.utils;
    
    public class ConfigUtil {
    /**
    * 服务号相关信息
    */
    public final static String APPID = "xxxxxxx";// 应用号
    public final static String APP_SECRECT = "xxxxx";// 应用密码
    public final static String MCH_ID = "xxxxx";// 商户号 xxxx 公众号商户id
    public final static String API_KEY = "xxxxxx";// API密钥
    public final static String SIGN_TYPE = "MD5";// 签名加密方式
    public final static String TRADE_TYPE = "APP";// 支付类型
    public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 微信支付统一接口(POST)
     }

    xml读取的工具类

    package com.rubbish.jinzhu.utils;
    
    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.JDOMException;
    import org.jdom.input.SAXBuilder;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Iterator;
    import java.util.List;
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    public class XMLUtil {
        /**
         * 解析xml,返回第一级元素键值对。
         * 如果第一级元素有子节点,
         * 则此节点的值是子节点的xml数据。
         * 
         * @param strxml
         * @return
         * @throws JDOMException
         * @throws IOException
         */
        public static SortedMap<String, Object> doXMLParse(String strxml)
                throws JDOMException, IOException {
            strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");
            if (null == strxml || "".equals(strxml)) {
                return null;
            }
            SortedMap<String, Object> map = new TreeMap<String, Object>();
            InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
            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 key = e.getName();
                String value = "";
                List children = e.getChildren();
                if (children.isEmpty()) {
                    value = e.getTextNormalize();
                } else {
                    value = XMLUtil.getChildrenText(children);
                }
                map.put(key, value);
            }
            // 关闭流
            in.close();
            return map;
        }
    
        /**
         * 获取子结点的xml
         * @param children
         * @return
         */
        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(XMLUtil.getChildrenText(list));
                    }
                    sb.append(value);
                    sb.append("</" + name + ">");
                }
            }
            return sb.toString();
        }
    
    }

    根据key值对map进行ascii排序

    package com.rubbish.jinzhu.utils;
    
    import java.util.*;
    import java.util.Map.Entry;
    
    public class MapUtils {
    
        /**
         * 对map根据key进行排序 ASCII 顺序
         * 
         * @param 无序的map
         * @return
         */
        public static SortedMap<String, Object> sortMap(Map<String, Object> map) {
    
            List<Entry<String, Object>> infoIds = new ArrayList<Entry<String, Object>>(
                    map.entrySet());
            Collections.sort(infoIds, new Comparator<Entry<String, Object>>() {
                public int compare(Entry<String, Object> o1,
                        Entry<String, Object> o2) {
                    // return (o2.getValue() - o1.getValue());//value处理
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            SortedMap<String, Object> sortmap = new TreeMap<String, Object>();
            for (int i = 0; i < infoIds.size(); i++) {
                String[] split = infoIds.get(i).toString().split("=");
                sortmap.put(split[0], split[1]);
            }
            return sortmap;
        }
    }

      

    三  controller 类

    package com.rubbish.jinzhu.controller;
    
    import com.google.common.base.Charsets;
    import com.google.common.base.Strings;
    import com.google.common.collect.ImmutableMap;import com.rubbish.jinzhu.utils.*;import org.springframework.web.bind.annotation.*;
    import java.io.UnsupportedEncodingException;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Map;
    import java.util.SortedMap;
    
    import static com.rubbish.jinzhu.utils.MapUtils.sortMap;
    
    @RestController
    public class TradeController {
    
       @RequestMapping("/trade/prepare_pay")
       public SortedMap<String, Object> preparePay(@RequestParam String ip,
                                                   @RequestParam String tradeId,
                                                   @RequestParam int price) {
          if (Strings.isNullOrEmpty(ip)) {
             try {
                InetAddress addr = InetAddress.getLocalHost();
                ip = addr.getHostAddress().toString();
             } catch (UnknownHostException e) {
                e.printStackTrace();
             }
          }
          SortedMap<String, Object> parameters = prepareOrder(ip, tradeId, price);
          parameters.put("sign", PayCommonUtil.createSign(Charsets.UTF_8.toString(), parameters));// sign签名 key
          String requestXML = PayCommonUtil.getRequestXml(parameters);// 生成xml格式字符串
          String responseStr = HttpUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);
          try {
             SortedMap<String, Object> resultMap = XMLUtil.doXMLParse(responseStr);
             SortedMap<String, Object> map = buildClientJson(resultMap);
             return map;
          } catch (Exception e) {
             e.printStackTrace();
             return null;
          }
       }

      //预支付成功,返回给app的参数 private SortedMap<String, Object> buildClientJson( Map<String, Object> resutlMap) throws UnsupportedEncodingException { Map<String, Object> params = ImmutableMap.<String, Object> builder() .put("appid", ConfigUtil.APPID)//应用号 .put("noncestr", PayCommonUtil.CreateNoncestr())//随机字符串 .put("package", "Sign=WXPay")//固定的字符串,不需要改变 .put("partnerid", ConfigUtil.MCH_ID)//商户号 .put("prepayid", resutlMap.get("prepay_id"))//预支付微信返回的id .put("timestamp", DateUtils.getTimeStamp()) // 10 位时间戳 .build(); SortedMap<String, Object> sortMap = sortMap(params); sortMap.put("package", "Sign=WXPay"); String paySign = PayCommonUtil.createSign(Charsets.UTF_8.toString(), sortMap); sortMap.put("sign", paySign); return sortMap; }
      //预支付参数准备
    private SortedMap<String, Object> prepareOrder(String ip, String tradeId, int price) { Map<String, Object> oparams = ImmutableMap.<String, Object> builder() .put("appid", ConfigUtil.APPID)//应用号 .put("mch_id", ConfigUtil.MCH_ID)// 商户号 .put("nonce_str", PayCommonUtil.CreateNoncestr())// 16随机字符串(大小写字母加数字) .put("body", "金株互联支付")// 商品描述 .put("out_trade_no", tradeId)// 商户订单号 .put("total_fee", price) .put("spbill_create_ip", ip)// IP地址 .put("notify_url", "http://127.0.0.1:8082/api/trade/paid/wx") // 微信回调地址 .put("trade_type", ConfigUtil.TRADE_TYPE)// 支付类型 APP .build();//支付金额 return sortMap(oparams); } private String callback(String responseStr) { try { Map<String, Object> map = XMLUtil.doXMLParse(responseStr); if (!PayCommonUtil.checkIsSignValidFromResponseString(responseStr)) { return PayCommonUtil.setXML(WeixinConstant.FAIL, "invalid sign"); } if (WeixinConstant.FAIL.equalsIgnoreCase(map.get("result_code") .toString())) { return PayCommonUtil.setXML(WeixinConstant.FAIL, "weixin pay fail"); } if (WeixinConstant.SUCCESS.equalsIgnoreCase(map.get("result_code") .toString())) { // 对数据库的操作 String outTradeNo = (String) map.get("out_trade_no"); String transactionId = (String) map.get("transaction_id"); String totlaFee = (String) map.get("total_fee"); Integer totalPrice = Integer.valueOf(totlaFee) / 100;//服务器这边记录的是钱的元 // Trade trade = tradeBiz.get(Integer.valueOf(outTradeNo)); // trade.setTransactionId(transactionId); //boolean isOk = tradeBiz.paid(trade); // if (isOk) { // return PayCommonUtil.setXML(WeixinConstant.SUCCESS, "OK"); // } else { // return PayCommonUtil // .setXML(WeixinConstant.FAIL, "update bussiness outTrade fail"); //} } } catch (Exception e) { return PayCommonUtil.setXML(WeixinConstant.FAIL, "weixin pay server exception"); } return PayCommonUtil.setXML(WeixinConstant.FAIL, "weixin pay fail"); } }
  • 相关阅读:
    关于SpringBoot的外部化配置使用记录
    深入理解Mybatis插件
    MySQL JDBC Driver 8.0+设置服务器时区
    重新认识Java注解
    深入理解Java枚举
    Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
    Spring Cloud系列教程第九篇-Eureka自我保护机制
    安装vsftp服务器的时候遇到的问题
    spring cloud系列教程第八篇-修改服务名称及获取注册中心注册者的信息
    idea 启动命令行的时候提示不能创建PTY
  • 原文地址:https://www.cnblogs.com/wangpenglen/p/8866159.html
Copyright © 2020-2023  润新知