Java实现微信JSAPI支付

官方文档 微信JSAPI支付

步骤:

  1. 商户后台系统下单,发起支付
  2. 生成第商户后台系统订单
  3. 请求下单接口,创建订单
  4. 微信支付系统会生成预支付订单
  5. 商户后台系统获取到预支付订单
  6. 生成签名支付信息
  7. 发起支付

代码示例:

获取微信预支付订单,返回给前端就行。

	@ResponseBody
	@RequestMapping(value = "/pay",method = RequestMethod.POST,produces = "application/json;charset=utf-8")
	public Map<String,Object> weChatPay(HttpServletRequest request) {
		String ip = IpUtils.getIpAddr(request);
		String[] ipArr = ip.split(",");
				//调用微信统一下单接口
		Map<String, String> map = new HashMap<>();
		//公众账号ID
		map.put("appid", WeChatUtils.weChatAppid);
		//商户号
		map.put("mch_id", WeChatUtils.weChatPayMchId);
		//随机字符串
		map.put("nonce_str", WeChatUtils.generateNonceStr());
		//商品描述
		map.put("body", "pay");
		//商户订单号
		map.put("out_trade_no", WeChatUtils.generateNonceStr());
		//标价金额
		map.put("total_fee", 100+"");
		//交易类型
		map.put("trade_type", "JSAPI");
		//终端IP
		map.put("spbill_create_ip", ipArr[0]);
		//通知地址
		map.put("notify_url", WeChatUtils.weChatPayNotifyUrl);
		//openid 如何获取查询微信支付官方文档
		map.put("openid", "openid");
		//签名类型
		map.put("sign_type", "MD5");
		//签名
		try {
			map.put("sign", WeChatUtils.generateSignature(map, WeChatUtils.weChatPayKey, "MD5"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		String resultXml = null;
		Map<String, String> resultMap = null;
		try {
			resultXml = HttpUtils.postData(WeChatUtils.weChatPayUrl, WeChatUtils.mapToXml(map),"utf-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			resultMap = WeChatUtils.xmlToMap(resultXml);
		} catch (Exception e) {	
			e.printStackTrace();
		}
		if(resultMap.get("return_code").equals("SUCCESS") && resultMap.get("result_code").equals("SUCCESS")) {
			//成功之后保存自己的业务逻辑
			Map<String, String> dataMap = new HashMap<>();
			dataMap.put("appId", WeChatUtils.weChatAppid);
			dataMap.put("timeStamp", System.currentTimeMillis()/1000+"");
			dataMap.put("nonceStr", WeChatUtils.generateNonceStr());
			dataMap.put("package", "prepay_id=" + resultMap.get("prepay_id"));
			dataMap.put("signType", "MD5");
			try {
				dataMap.put("paySign", WeChatUtils.generateSignature(dataMap, WeChatUtils.weChatPayKey, "MD5"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			return dataMap;
		}else {
			Map<String, String> map= new HashMap<>();
			map.put("code","error");
			return map;
		}
	}

支付回调,除了支付回调微信还提供查询订单接口,建议两种都使用。

	@ResponseBody
	@RequestMapping("/weChatPayNotify")
	public String weChatPayNotify(HttpServletRequest request) {
		String resultStr = "";
		Map<String, String> returnMap = new HashMap<>();
		try {
			resultStr = new String(WeChatUtils.readInput(request.getInputStream()),"utf-8");
			System.out.println("支付回调:"+resultStr);
			Map<String, String> resultMap = WeChatUtils.xmlToMap(resultStr);
			String resultCode = resultMap.get("result_code");
			String returnCode = resultMap.get("return_code");
			if("SUCCESS".equals(returnCode)&&"SUCCESS".equals(resultCode)) {
				Map<String, Object> map = new HashMap<>();
				map.put("out_trade_no =?", resultMap.get("out_trade_no"));
				/** 自己的业务逻辑 根据out_trade_no  查询订单信息**/
				returnMap.put("return_code", "SUCCESS");
				returnMap.put("return_msg", "OK");
				return WeChatUtils.mapToXml(returnMap);
			}else {
				return "";
			}
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}

工具类

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
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.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class WeChatUtils {

	//appid
	public static String weChatAppid = "APPID";
	
	//appsecret
	public static String weChatAppsecret ="Appsecret";
	
	//微信支付商户号
	public static String weChatPayMchId = "MchId";
	
	//支付key
	public static String weChatPayKey = "PayKey";
	
	public static String weChatRedirectUri = "http://www.baidu.com";
	
	//支付回调地址
	public static String weChatPayNotifyUrl = "http://www.baidu.com";
			
	public static String weChatAuthorizeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
	
	public static String weChatAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
	
	public static String weChatUserinfoUrl = "https://api.weixin.qq.com/sns/userinfo";
	
	public static String weChatPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	
	public static String weChatPayOrderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
	
	//private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	
	private static final String SYMBOLS2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	
    private static final Random RANDOM = new SecureRandom();

	
    /**
     * 获取微信Jsapi的accessToken
     */
    public static String getAccessToken() throws IOException{
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        url = url.replace("APPID",weChatAppid).replace("APPSECRET",weChatAppsecret);
        String result = HttpUtils.sendGet(url);
        JSONObject jsonObject = JSON.parseObject(result);
        String accessToken = jsonObject.getString("access_token");
        return accessToken;
    }
    
    /**
    *
    * @Title: getNonceStr
    * @Description: 生成随机字符串
    * @param @return
    * @return String    返回类型
    * @throws
    */
    public static String getNonceStr() {
        String currT = getCurrTime();
        String strT = currT.substring(8, currT.length());
        String strRandom = buildRandom(4) + "";
        return strT + strRandom;
    }
   


   /**
    *
    * @Title: buildRandom
    * @Description: 生成随机数
    * @param @param length
    * @param @return
    * @return int    返回类型
    * @throws
    */
    public static int buildRandom(int length) {
        int mm= 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            mm= mm* 10;
        }
        return (int) ((random * mm));
    }


   /**
    *
    * @Title: getCurrTime
    * @Description: 获取当前时间
    * @param @return
    * @return String    返回类型
    * @throws
    */
    public static String getCurrTime() {
        Date date = new Date();
        SimpleDateFormat of= new SimpleDateFormat("yyyyMMddHHmmss");
        String s = of.format(date);
        return s;
    }


   /**
    * 随机字符串
    * @return
    */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

   /**
    *
    * @Title: createSignBySha1
    * @Description: 生成签名
    * @param @param params
    * @param @return
    * @return String    返回类型
    * @throws
    */
    @SuppressWarnings("rawtypes")
    public static String createSignBySha1(SortedMap<Object, Object> params) {
        StringBuffer sb = new StringBuffer();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (v != null && !v.equals("")) {
                sb.append(k + "=" + v + "&");
            }
        }
        String result = sb.toString().substring(0, sb.toString().length()-1);
        return getSHA1(result);
    }
   /**
    *
    * @Title: getTimestamp
    * @Description: 获取时间戳(秒)
    * @param @return    参数
    * @return String    返回类型
    * @throws
    */
    public static String getTimestamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }


   /**
    *
    * @Title: getSHA1
    * @Description: SHA1签名生成
    * @param @param str
    * @param @return    参数
    * @return String    返回类型
    * @throws
    */
    public static String getSHA1(String str){
        StringBuffer hexstr = new StringBuffer();
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            } 
        } catch (NoSuchAlgorithmException e) {
		      e.printStackTrace();
        }
        return hexstr.toString();
    }
   
     /**
     *
     * @Title: getJsapiTicket
     * @Description: 获取JsapiTicket
     * @param @param access_token
     * @param @return
     * @return String    返回类型
     * @throws
     */
    public static String getJsapiTicket(String access_token)  throws IOException{
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi".replace("ACCESS_TOKEN",access_token);
        String result = HttpUtils.sendGet(url);
        JSONObject jsonObject = JSON.parseObject(result);
        String ticket= jsonObject.getString("ticket");
        return ticket;
    }
  
  
    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);
//          try {
//              value = URLEncoder.encode(value, "UTF-8");
//          }catch (Exception e){
//              e.printStackTrace();
//          }
            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }
    
    public static String generateSignature(final Map<String, String> data, String key, String signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (data.get(k).trim().length() > 0) {
            	//参数值为空,则不参与签名
            	sb.append(k).append("=").append(data.get(k).trim()).append("&");
            }
               
        }
        sb.append("key=").append(key);
        if ("MD5".equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }else if ("HMACSHA256".equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }
    
    public static String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    
    public static String mapToXml(Map<String, String> map) throws Exception {
   	    StringBuffer sb = new StringBuffer();
        sb.append("");
        Set<String> set = map.keySet();
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String key = it.next();
            sb.append("<" + key + ">").append(map.get(key)).append(" + key + ">");
        }
        sb.append("");
        return sb.toString();

    }
    
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }
    
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }
    
    
    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
    
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }
    
    /**
     * @TODO : 
     * @AUTH : linfeng
     * @DATE : 2020年10月19日 上午9:34:49
     * @return_type : String
     * @return
     */
	public static String generateNonceStr5() {
	   char[] nonceChars = new char[6];
	   for (int index = 0; index < nonceChars.length; ++index) {
	       nonceChars[index] = SYMBOLS2.charAt(RANDOM.nextInt(SYMBOLS2.length()));
	   }
	   return new String(nonceChars);
	}
	
    public static byte[] readInput(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        out.close();
        in.close();
        return out.toByteArray();
    }
}

工具类

import javax.servlet.http.HttpServletRequest;

public class IpUtils {
	
	/**
	 * @TODO : 获取ip
	 * @AUTH : linfeng
	 * @DATE : 2020年9月18日 下午3:12:20
	 * @return_type : String
	 * @param request
	 * @return
	 */
    public static String getIpAddr(HttpServletRequest request) {
		 
        if (request == null){
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
	}
	
    /**
     * @TODO : 获取浏览器类型
     * @AUTH : linfeng
     * @DATE : 2020年9月18日 下午3:20:56
     * @return_type : String
     * @param request
     * @return
     */
    public static String getBrowser(HttpServletRequest request) {
        String userAgent = request.getHeader("user-agent");
		if (userAgent.contains("Firefox")){
			return "火狐浏览器";
		}else if (userAgent.contains("Chrome")){
			return "谷歌浏览器";
		}else if (userAgent.contains("Trident")){
			return "IE浏览器";
		}else{
			return "浏览器";
		}
    }
}

工具类

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;

public class HttpUtils {
	
    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds  
    private final static String DEFAULT_ENCODING = "UTF-8"; 
    
    public static String sendGet(String url) {
        String result = "";
        BufferedReader in = null;
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            // 遍历所有的响应头字段
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }
    
    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!" + e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }
    
    public static String postData(String urlStr, String data, String contentType){  
        BufferedReader reader = null;  
        try {  
            URL url = new URL(urlStr);  
            URLConnection conn = url.openConnection();  
            conn.setDoOutput(true);  
            conn.setConnectTimeout(CONNECT_TIMEOUT);  
            conn.setReadTimeout(CONNECT_TIMEOUT);  
            if(contentType != null)  
                conn.setRequestProperty("content-type", contentType);  
            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);  
            if(data == null)  
                data = "";  
            writer.write(data);   
            writer.flush();  
            writer.close();    
  
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));  
            StringBuilder sb = new StringBuilder();  
            String line = null;  
            while ((line = reader.readLine()) != null) {  
                sb.append(line);  
                sb.append("\r\n");  
            }  
            return sb.toString();  
        } catch (IOException e) {  
            //logger.error("Error connecting to " + urlStr + ": " + e.getMessage());  
        } finally {  
            try {  
                if (reader != null)  
                    reader.close();  
            } catch (IOException e) {  
            }  
        }  
        return null;  
    }
}

你可能感兴趣的:(微信,java,小程序)