(Java版)微信小程序预支付,返回前端发起支付参数,支付成功回调

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import net.sf.json.JSONObject;
import springcloud.producer.dao.BaseDAO;
import springcloud.producer.service.PayService;
import springcloud.producer.utils.HttpRequest;
import springcloud.producer.utils.MathUtil;
import springcloud.producer.utils.Redis;
import springcloud.producer.utils.RedisConstant;
import springcloud.producer.utils.UtilState;
import springcloud.producer.utils.Utils;
import springcloud.producer.utils.wechat.PayConfigUtil;
import springcloud.producer.utils.wechat.WxPayUtil;

@Service("payService")
public class PayServiceImpl implements PayService{
	
	Logger log = Logger.getLogger(this.getClass());
	
	@Autowired
	private BaseDAO<?> baseDAO;

    //预支付
	@Override
	public String miniProgramPay(String fee, String code, Integer companyId,String payType) throws Exception {
		// 获取openid
		String openId = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session?appid=" + PayConfigUtil.APP_ID
				+ "&secret=" + PayConfigUtil.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code", code);
		JSONObject json = JSONObject.fromObject(openId);
		openId = json.getString("openid");
		if (StringUtils.isBlank(openId))
			throw new Exception("code错误");
		// 商户号
		String mchId = PayConfigUtil.MCH_ID;
		// 支付密钥
		String key = "&key=" + PayConfigUtil.API_KEY;
		// 交易类型
		String tradeType = "JSAPI";
		// 随机字符串
		String nonceStr = WxPayUtil.getNonceStr();
		// 小程序id
		String appid = PayConfigUtil.APP_ID;
		// 商品订单号(保持唯一性)
		String outTradeNo = mchId + WxPayUtil.getNonceStr();
		// 支付金额
		String totalFee = WxPayUtil.getMoney(fee);
		// 发起支付设备ip
		String spbillCreateIp = PayConfigUtil.CREATE_IP;
		// 商品描述
		String body = "小程序充值";
		// 附加数据,商户携带的订单的自定义数据 (原样返回到通知中,这类我们需要系统中订单的id 方便对订单进行处理)
		// 我们后面需要键值对的形式,所以先装入map
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("appid", appid);
		sParaTemp.put("attach", payType);
		sParaTemp.put("body", body);
		sParaTemp.put("mch_id", mchId);
		sParaTemp.put("nonce_str", nonceStr);
		sParaTemp.put("notify_url", PayConfigUtil.NOTIFY_URL);
		sParaTemp.put("openid", openId);
		sParaTemp.put("out_trade_no", outTradeNo);
		sParaTemp.put("spbill_create_ip", spbillCreateIp);
		sParaTemp.put("total_fee", totalFee);
		sParaTemp.put("trade_type", tradeType);
		// 去掉空值 跟 签名参数(空值不参与签名,所以需要去掉)
		Map<String, String> map = WxPayUtil.paraFilter(sParaTemp);
		// 按照 参数=参数值&参数2=参数值2 这样的形式拼接(拼接需要按照ASCII码升序排列)
		String mapStr = WxPayUtil.createLinkString(map);
		// MD5运算生成签名
		String sign = WxPayUtil.sign(mapStr, key, "utf-8").toUpperCase();
		sParaTemp.put("sign", sign);
		String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<attach>" + payType + "</attach>" + "<body>" + body
				+ "</body>" + "<mch_id>" + mchId + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>"
				+ "<notify_url>" + PayConfigUtil.NOTIFY_URL + "</notify_url>" + "<openid>" + openId + "</openid>"
				+ "<out_trade_no>" + outTradeNo + "</out_trade_no>" + "<spbill_create_ip>" + spbillCreateIp
				+ "</spbill_create_ip>" + "<total_fee>" + totalFee + "</total_fee>" + "<trade_type>" + tradeType
				+ "</trade_type>" + "<sign>" + sign + "</sign>" + "</xml>";
		// 统一下单url,生成预付id
		String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		String result = WxPayUtil.httpRequest(url, "POST", xml);
		String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
		// 得到预支付id
		String prepay_id = "";
		try {
			prepay_id = WxPayUtil.getPayNo(result);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		String packages = "prepay_id=" + prepay_id;
		String nonceStr1 = WxPayUtil.getNonceStr();
		// 开始第二次签名
		String mapStr1 = "appId=" + appid + "&nonceStr=" + nonceStr1 + "&package=prepay_id=" + prepay_id
				+ "&signType=MD5&timeStamp=" + timeStamp;
		String paySign = WxPayUtil.sign(mapStr1, key, "utf-8").toUpperCase();
		Map<String, String> resultMap = new HashMap<>();
		resultMap.put("timeStamp", timeStamp);
		resultMap.put("nonceStr", nonceStr1);
		resultMap.put("package", packages);
		resultMap.put("paySign", paySign);
		resultMap.put("fee", fee);
		resultMap.put("companyId", companyId + "");
		Redis.getRedis().addHashMapToJedis(RedisConstant.R_OPENID + outTradeNo, resultMap, RedisConstant.R_OPENID_T,
				true);
		return Utils.jsonUtilCode(UtilState.SUCCESS, resultMap);
	}
}
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class WxPayUtil {
	/**
     * 获取随机字符串 (采用截取8位当前日期数  + 4位随机整数)
     * @return 
     */
    public static String getNonceStr() {
        //获得当前日期
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String currTime = outFormat.format(now);
        //截取8位
        String strTime = currTime.substring(8, currTime.length());
        //得到4位随机整数
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < 4; i++) {
            num = num * 10;
        }
        num = (int)random * num;
        return strTime + num;
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @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 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")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * MD5 加密,转为指定类型
     * @param text
     * @param key
     * @param input_charset
     * @return
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }


    /**
     * 元转换成分
     * @param money
     * @return
     */
    public static String getMoney(String amount) {
        if(amount==null){
            return "";
        }
        // 金额转化为分为单位
        String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额  
        int index = currency.indexOf(".");  
        int length = currency.length();  
        Long amLong = 0l;  
        if(index == -1){  
            amLong = Long.valueOf(currency+"00");  
        }else if(length - index >= 3){  
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
        }else if(length - index == 2){  
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
        }else{  
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
        }  
        return amLong.toString(); 
    }

    
    /**
     * 解析xml得到 prepay_id 预支付id
     * @param result
     * @return
     * @throws DocumentException 
     */
    public static String getPayNo(String result) throws DocumentException{
        Map<String, String> map = new HashMap<String, String>();
        InputStream in = new ByteArrayInputStream(result.getBytes());
        SAXReader read = new SAXReader();
        Document doc = read.read(in);
        //得到xml根元素
        Element root = doc.getRootElement();
        //遍历  得到根元素的所有子节点 
        @SuppressWarnings("unchecked")
        List<Element> list =root.elements();
        for(Element element:list){
            //装进map
            map.put(element.getName(), element.getText());
        }
        //返回码
        String return_code = map.get("return_code");
        //返回信息
        String result_code = map.get("result_code");
        //预支付id
        String prepay_id = "";
        //return_code 和result_code 都为SUCCESS 的时候返回 预支付id
        if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")){
            prepay_id = map.get("prepay_id");
        }
        return prepay_id;
    }
    /**
     * 解析 回调时的xml装进map 返回
     * @param result 
     * @return
     * @throws DocumentException 
     */
    public static Map<String, String> getNotifyUrl(String result) throws DocumentException{
        Map<String, String> map = new HashMap<String, String>();
        InputStream in = new ByteArrayInputStream(result.getBytes());
        SAXReader read = new SAXReader();
        Document doc = read.read(in);
        //得到xml根元素
        Element root = doc.getRootElement();
        //遍历  得到根元素的所有子节点 
        @SuppressWarnings("unchecked")
        List<Element> list =root.elements();
        for(Element element:list){
            //装进map
            map.put(element.getName().toString(), element.getText().toString());
        }
        return map;
    }

    /**
     * 验证签名,判断是否是从微信发过来
     * 验证方法:接收微信服务器回调我们url的时候传递的xml中的参数 然后再次加密,看是否与传递过来的sign签名相同
     * @param map
     * @return
     */
    public static boolean verifyWeixinNotify(Map<String, String> map,String key) {  
        //根据微信服务端传来的各项参数 进行再一次加密后  与传过来的 sign 签名对比
        String mapStr = createLinkString(map);
        String signOwn = WxPayUtil.sign(mapStr, key, "utf-8").toUpperCase();         //根据微信端参数进行加密的签名
        String signWx = map.get("sign");                //微信端传过来的签名
        if(signOwn.equals(signWx)){
            //如果两个签名一致,验证成功
            return true;
        }
        return false;
    }  

    /**
     * 
     * @param requestUrl请求地址
     * @param requestMethod请求方法
     * @param outputStr参数
     */
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
        // 创建SSLContext
        StringBuffer buffer=null;
        try{
        URL url = new URL(requestUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(requestMethod);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        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();
        }
 
}
public class PayConfigUtil {
	//初始化
	public final static String APP_ID = ""; //公众账号appid(改为自己实际的)
	public final static String APP_SECRET = "";
	public final static String MCH_ID = ""; //商户号(改为自己实际的)
	public final static String API_KEY = ""; //(改为自己实际的)key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
	
	
	//有关url
	public final static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	public final static String NOTIFY_URL = ""; //微信支付回调接口,就是微信那边收到(改为自己实际的)
	//企业向个人账号付款的URL
	public final static String SEND_EED_PACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
	
	public final static String CREATE_IP = "127.0.0.1";//发起支付ip(改为自己实际的)
	
}
    //支付成功回调
	@Override
	public void miniProgramNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		// sb为微信返回的xml
		String notityXml = sb.toString();
		String resXml = "";
		log.info("接收到的报文:" + notityXml);
		Map<String, String> map = WxPayUtil.getNotifyUrl(notityXml);
		String returnCode = (String) map.get("return_code");
		String outTradeNo = map.get("out_trade_no") + "";
		String totalFee = map.get("total_fee") + "";
		String attach = map.get("attach") + "";
		if ("SUCCESS".equals(returnCode)) {
			// 验证签名是否正确
			if (returnCode.equals("SUCCESS")) {
				/** 此处添加自己的业务逻辑代码start **/

			}
		} else {
			resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
					+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
		}
		resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>"
				+ "</xml> ";
		log.info("微信支付回调数据结束");
		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		out.write(resXml.getBytes());
		out.flush();
		out.close();
	}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值