微信支付示例
定义接口
package com.chanxa.monitor.pay.service; import java.math.BigDecimal; import java.util.Map; public interface PayService { /** * 创建微信预支付订单--公共接口 * * @param tradeOrder * @param ip * IP地址 * @param appid * 微信分配的公众账号ID(企业号corpid即为此appId) * @param fee_type * 符合ISO 4217标准的三位字母代码,默认人民币:CNY * @param mch_id * 微信支付分配的商户号 * @param trade_type * 取值如下:JSAPI,NATIVE,APP,WAP * @param unified_order_url * 统一下单URL * @param weixinUrl * 付款成功后回调地址 * @return * @throws Exception */ public Map<String, String> WeixinPayment(String orderId, BigDecimal money, String appid, String fee_type, String mch_id, String trade_type, String unified_order_url, String weixinurl, String body, String ip,String openid) throws Exception; }
接口是现实
package com.chanxa.monitor.pay.service.impl; import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.chanxa.monitor.enums.ErrorType; import com.chanxa.monitor.exception.BusinessException; import com.chanxa.monitor.model.WeixinPayLog; import com.chanxa.monitor.pay.service.PayService; import com.chanxa.monitor.pay.service.WeixinPayLogInterface; import com.chanxa.monitor.pay.weixin.config.WeixinConfig; import com.chanxa.monitor.pay.weixin.util.RequestUtils; import com.chanxa.monitor.pay.weixin.util.WeixinNotify; import com.chanxa.monitor.pay.weixin.util.WeixinSubmit; import com.chanxa.monitor.pay.weixin.util.XmlUtils; import com.chanxa.monitor.utils.DateTimeUtils; @Service public class PayServices implements PayService { Logger logger = LoggerFactory.getLogger(PayServices.class); @Resource private WeixinPayLogInterface weixinPayLogService; /** * 创建微信预支付订单--公共接口 * * @param tradeOrder * @param ip IP地址 * @param appid 微信分配的公众账号ID(企业号corpid即为此appId) * @param fee_type 符合ISO 4217标准的三位字母代码,默认人民币:CNY * @param mch_id 微信支付分配的商户号 * @param trade_type 取值如下:JSAPI,NATIVE,APP,WAP * @param unified_order_url 统一下单URL * @param weixinUrl 付款成功后回调地址 * @param openid 小程序openid * @return * @throws Exception */ public Map<String, String> WeixinPayment(String orderId,BigDecimal money,String appid,String fee_type,String mch_id, String trade_type,String unified_order_url,String weixinurl,String body, String ip,String openid) throws Exception { // 添加日志 略。。。。
// 微信订单参数 Map<String, String> params = new HashMap<String, String>(); params.put("appid", appid);//设置微信分配的公众账号ID params.put("mch_id", mch_id);//设置微信支付分配的商户号 params.put("openid", openid);//设置订单id params.put("nonce_str", RequestUtils.createRandomStr());//创建随机字符串,32位,生成uuid,然后md5 params.put("body", body); params.put("out_trade_no", orderId+"");//订单ID params.put("fee_type", WeixinConfig.FEE_TYPE);// 符合ISO 4217标准的三位字母代码,默认人民币:CNY String moneys=money.multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString(); params.put("total_fee", moneys);//付款金额 单位是分 params.put("spbill_create_ip", ip);//当前IP地址 Date start = new Date(); params.put("time_start", DateTimeUtils.formatDate("yyyyMMddHHmmss", start)); Date end = DateTimeUtils.addDateMinutes(start, 30); params.put("time_expire", DateTimeUtils.formatDate("yyyyMMddHHmmss", end)); params.put("notify_url", weixinurl);//付款成功后回调地址 params.put("trade_type", trade_type);//取值如下:JSAPI,NATIVE,APP,WAP String xml = WeixinSubmit.buildRequest(params,unified_order_url);//模拟http请求,通过httpclient请求 并返回 Map<String, String> response = XmlUtils.parseResponseXml(xml);//将微信返回的数据解析成map String msg = WeixinNotify.getWeixinErrorMsg(response);//从微信的返回结果中获取错误编号,错误描述等信息,如果没有错误并且校验签名通过,则返回null if (msg != null) { logger.error("微信支付错误信息->" + msg); throw new BusinessException(ErrorType.PAY_ERROR); } logger.debug("统一下单参数====>" + response); return buildRequestPayInfo(response,appid,openid); } /** 创建微信请求支付的参数列表 */ protected Map<String, String> buildRequestPayInfo(Map<String, String> response,String appid,String openid) { Map<String, String> info = new HashMap<String, String>(); String prepay_id = response.get("prepay_id"); if(openid==null){ info.put("appid",appid); info.put("partnerid", WeixinConfig.MCH_ID);// 微信支付分配的商户号 info.put("prepayid", prepay_id); info.put("package", "");//这里看官方文档给的值 info.put("timestamp", DateTimeUtils.getCurrentTimeSeconds());//获取当前时间戳的秒数 的字符串表示形式 info.put("noncestr", RequestUtils.createRandomStr());//创建随机字符串,32位,生成uuid,然后md5 }else{ info.put("package", "prepay_id="+prepay_id); info.put("appId",appid); info.put("signType", "MD5"); info.put("timeStamp", DateTimeUtils.getCurrentTimeSeconds()); info.put("nonceStr", RequestUtils.createRandomStr()); } Map<String, String> sPara = RequestUtils.paraFilter(info); // 生成签名结果 String mysign = RequestUtils.buildRequestMysign(sPara); sPara.put("sign", mysign); return sPara; } }
工具类
package com.chanxa.monitor.pay.weixin.util; import java.io.InputStream; import java.security.KeyStore; import java.util.Map; import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.chanxa.monitor.pay.weixin.config.WeixinConfig; public class WeixinSubmit { /** * 模拟https请求,带证书 * * @param sParaTemp * @param url * @return * @throws Exception */ public static String buildRequestWithCertificate(Map<String, String> sParaTemp, String url) throws Exception { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream instream = WeixinSubmit.class.getResourceAsStream(WeixinConfig.CERTIFICATE_PATH);//读取证书文件 CERTIFICATE_PATH 为证书目录 try { trustStore.load(instream, WeixinConfig.MCH_ID.toCharArray());//设置商户号 } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); try { // 待请求参数数组 Map<String, String> sPara = buildRequestPara(sParaTemp); String xmlBody = XmlUtils.createRequestXml(sPara); HttpPost post = new HttpPost(url); post.setHeader("Content-Type", "text/xml;charset=utf8"); post.setEntity(new StringEntity(xmlBody, ContentType.create("text/xml", "utf8"))); System.out.println("executing request" + post.getRequestLine()); CloseableHttpResponse response = httpclient.execute(post); try { HttpEntity entity = response.getEntity(); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); } return EntityUtils.toString(entity); } finally { response.close(); } } finally { httpclient.close(); } } /** * 模拟http请求,通过httpclient请求 并返回 * * @param sParaTemp * @param url * @return 相应的内容 * @throws Exception */ public static String buildRequest(Map<String, String> sParaTemp, String url) throws Exception { // 待请求参数数组 Map<String, String> sPara = buildRequestPara(sParaTemp); CloseableHttpClient client = null; CloseableHttpResponse response = null; try { String xmlBody = XmlUtils.createRequestXml(sPara); client = HttpClients.createDefault(); HttpPost post = new HttpPost(url); post.setHeader("Content-Type", "text/xml;charset=utf8"); post.setEntity(new StringEntity(xmlBody, ContentType.create("text/xml", "utf8"))); response = client.execute(post); HttpEntity entity = response.getEntity(); String xml = EntityUtils.toString(entity, "utf8"); return xml; } finally { if (response != null) { response.close(); } if (client != null) { client.close(); } } } /** * 生成要请求的参数数组 * * @param sParaTemp * 请求前的参数数组 * @return 要请求的参数数组 */ public static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) { // 除去数组中的空值和签名参数 Map<String, String> sPara = RequestUtils.paraFilter(sParaTemp); // 生成签名结果 String mysign = RequestUtils.buildRequestMysign(sPara); // 签名结果与签名方式加入请求提交参数组中 sPara.put("sign", mysign); return sPara; } }
package com.chanxa.monitor.pay.weixin.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.codec.digest.DigestUtils; import com.chanxa.monitor.pay.weixin.config.WeixinConfig; public class RequestUtils { /** * 除去数组中的空值和签名参数 * * @param sArray * 签名参数组 * @return 去掉空值与签名参数后的新签名参数组 */ public static Map<String, String> paraFilter(Map<String, String> sArray) { Map<String, String> result = new HashMap<String, String>(); if (sArray == null || sArray.size() <= 0) { return result; } for (String key : sArray.keySet()) { String value = sArray.get(key); if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) { continue; } result.put(key, value); } return result; } /** * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 * * @param params * 需要排序并参与字符拼接的参数组 * @return 拼接后字符串 */ public static String createLinkString(Map<String, String> params) { List<String> keys = new ArrayList<String>(params.keySet()); Collections.sort(keys); String prestr = ""; for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key); if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } /** * 生成签名结果 * * @param sPara * 要签名的数组 * @return 签名结果字符串 */ public static String buildRequestMysign(Map<String, String> sPara) { String prestr = createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 //将密钥连接 prestr = prestr + "&key=" + WeixinConfig.KEY; String mysign = DigestUtils.md5Hex(prestr).toUpperCase(); return mysign; } /** * 创建随机字符串,32位,生成uuid,然后md5 * * @return */ public static String createRandomStr() { String uuid = UUID.randomUUID().toString(); return DigestUtils.md5Hex(uuid); } }
package com.chanxa.monitor.pay.weixin.util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import com.chanxa.dict.util.StringUtils; public class XmlUtils { /** * 创建微信支付需要的xml参数形式 * * @param params * @return */ public static String createRequestXml(Map<String, String> params) { StringBuffer buff = new StringBuffer(); buff.append("<xml>"); for (Map.Entry<String, String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (StringUtils.isNotEmpty(value)) { buff.append("<").append(key).append(">"); buff.append("<![CDATA[").append(value).append("]]>"); buff.append("</").append(key).append(">"); } } buff.append("</xml>"); return buff.toString(); } /** * 将微信返回的数据解析成map * * @param responseXml * @return */ @SuppressWarnings("unchecked") public static Map<String, String> parseResponseXml(String responseXml) throws Exception { try { Document doc = DocumentHelper.parseText(responseXml); Element root = doc.getRootElement(); Map<String, String> map = new HashMap<String, String>(); Iterator<Element> it = root.elements().iterator(); for (; it.hasNext();) { Element next = it.next(); String name = next.getName(); String value = next.getTextTrim(); map.put(name, value); } return map; } catch (Exception e) { e.printStackTrace(); throw new Exception("文本不是正确的xml格式", e); } } }
package com.chanxa.monitor.pay.weixin.util; import java.util.Map; public class WeixinNotify { /** * 校验微信支付返回的参数,校验sign 签名 * * @param params * @return 如果sign校验通过返回true 反之false */ public static boolean verify(Map<String, String> params) { boolean b = false; Map<String, String> sPara = RequestUtils.paraFilter(params); String mysign = RequestUtils.buildRequestMysign(sPara); String sign = params.get("sign"); b = mysign.equals(sign); return b; } /** * 从微信的返回结果中获取错误编号,错误描述等信息,如果没有错误并且校验签名通过,则返回null * * @param response * @return */ public static String getWeixinErrorMsg(Map<String, String> response) { String return_code = response.get("return_code"); String return_msg = response.get("return_msg"); String result_code = response.get("result_code"); String err_code = response.get("err_code"); String err_code_des = response.get("err_code_des"); if ("SUCCESS".equalsIgnoreCase(return_code) && "SUCCESS".equalsIgnoreCase(result_code)) { boolean b = verify(response); if (!b) { return "签名校验未通过"; } return null; } return "return_code:" + return_code + ",return_msg:" + return_msg + ",result_code:" + result_code + ",err_code:" + err_code + ",err_code_des:" + err_code_des; } /** * 判断微信返回的信息是否是成功。 即判断return_code 和 result_code 如果不是success 则返回false 反之 * 返回true * * @param response * @return */ public static boolean verityWeixinReturnCode(Map<String, String> response) { boolean b = false; String return_code = response.get("return_code"); String result_code = response.get("result_code"); if ("SUCCESS".equalsIgnoreCase(return_code) && "SUCCESS".equalsIgnoreCase(result_code)) { b = true; } return b; } public static boolean verityWeixinReturnCode1(Map<String, String> response) { boolean b = false; String return_code = response.get("return_code"); String result_code = response.get("result_code"); if ("SUCCESS".equalsIgnoreCase(return_code) || "SUCCESS".equalsIgnoreCase(result_code)) { b = true; } return b; } }
微信回调
package Test; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import com.chanxa.monitor.model.WeixinPayLog; import com.chanxa.monitor.pay.weixin.util.WeixinNotify; import com.chanxa.monitor.pay.weixin.util.XmlUtils; import com.chanxa.monitor.utils.DateTimeUtils; import com.chanxa.monitor.utils.Validator; @Controller @RequestMapping("/test/callBack/") public class Paypal { Logger logger = LoggerFactory.getLogger( PayCallbackController.class ); /** * 微信支付回调接口 * @param request * @param httpResponse */ @RequestMapping(value = "weixinPayment") public void weixinPayCallback(@RequestBody String request, HttpServletResponse httpResponse) { String success = "SUCCESS"; String fail = "FAIL"; try { logger.debug( "微信==>" + request ); Map<String, String> xml = XmlUtils.parseResponseXml(request);//将微信返回的数据解析成map if (WeixinNotify.verityWeixinReturnCode(xml)) {//判断微信返回的信息是否是成功。 即判断return_code 和 result_code 如果不是success 则返回false 反之 返回true boolean verify = WeixinNotify.verify(xml);//校验微信支付返回的参数,校验sign 签名 if (!verify) { logger.info( "微信回调失败" ); returnXmlResponse(httpResponse, fail, "签名校验失败"); } else { WeixinPayLog payLog = createPayLog(xml); //调用验证信息接口 进行数据处理.. logger.info( "微信回调成功" ); //返回成功 returnXmlResponse(httpResponse, success, ""); } } else { logger.error("微信支付回调参数返回错误,请检查调用程序,响应参数====>" + xml); } } catch (Exception e) { logger.error( "weixinPayment->", e ); returnXmlResponse(httpResponse, fail, e.getMessage()); } } /** * 将微信返回的xml信息转换成对象并校验参数是否为空 * @param xml * @return */ private WeixinPayLog createPayLog(Map<String, String> xml) { Map<String, Object> map = new HashMap<String, Object>(); map.putAll(xml); Validator validator = Validator.validateFor(map); validator.addRequired("appid", "bank_type", "fee_type", "time_end", "out_trade_no", "mch_id", "total_fee", "transaction_id").addNumber("total_fee"); if (validator.hasError()) { throw new RuntimeException(validator.getError()); } WeixinPayLog payLog = new WeixinPayLog(); payLog.setOrderId(xml.get("out_trade_no")); payLog.setAppId(xml.get("appid")); payLog.setBankType(xml.get("bank_type")); payLog.setFeeType(xml.get("fee_type")); payLog.setFinishTime(DateTimeUtils.parseDate("yyyyMMddHHmmss", xml.get("time_end"))); payLog.setMchId(xml.get("mch_id")); payLog.setTotalFee(Integer.valueOf(xml.get("total_fee"))); payLog.setTransactionId(xml.get("transaction_id")); payLog.setTradeState(xml.get("result_code")); return payLog; } /** * 返回xml格式的相应内容(微信) * @param httpResponse * @param code * @param msg */ private void returnXmlResponse(HttpServletResponse httpResponse, String code, String msg) { httpResponse.setContentType("text/xml;charset=utf8"); Map<String, String> map = new HashMap<String, String>(); map.put("return_code", code); map.put("return_msg", msg); String xml = XmlUtils.createRequestXml(map); try { httpResponse.getWriter().write(xml); } catch (IOException e) { e.printStackTrace(); try { httpResponse.getWriter().write(xml); } catch (IOException e1) { e1.printStackTrace(); } } } }
收工 到此结束

浙公网安备 33010602011771号