微信支付示例

定义接口

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();
                }
            }
        }
    
        
        
        
        
}

收工 到此结束

posted @ 2020-05-07 13:29  又是一个bug  阅读(375)  评论(0)    收藏  举报