获取微信预支付订单,返回给前端就行。
@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;
}
}