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"); } }
原文地址:https://www.cnblogs.com/wangpenglen/p/8866159.html