這幾天一直在研究微信支付回調這個問題,發現之前微信支付回調都是正常的也沒怎麼在意,今天在自己項目上測試的時候發現相同的代碼在我這個項目上微信支付回調老是重複執行導致支付成功之後的回調邏輯一直在執行,很頭疼。回調邏輯都在執行,說明回調正常執行
網上有些給的答案:
微信沒有正常接收到SUCCESS消息建議將resXml:
resXml ="<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
修改爲:resXml = "SUCCESS";
也有人反映這個方法可行,但是我自己這邊沒能執行成功。
也有部分人說修改成
resXml ="<xml>" + "<return_code>SUCCESS</return_code>"+ "<return_msg>OK</return_msg>" + "</xml> ";
同樣部分人說可以 我修改了還是不行。
首先看下我的支付回調代碼(這邊只說微信支付回調,因爲到這裏應該都是微信支付通了的吧,我也有一篇是寫微信支付的,點擊這個鏈接)
/**
* @author SpringRoot
* @description 微信支付回調
* @date 2020/3/4-11:17
*/
@Service
public class WeiXinPayNotifyServiceImpl implements WeiXinPayNotifyService {
private final static Logger LOGGER = LoggerFactory.getLogger(WeiXinPayNotifyServiceImpl.class);
@Override
public void weixinpay_notify(HttpServletRequest request,
HttpServletResponse response) {
/**
這些是通過微信支付,返回可以得到一些值 具體如下截圖 這個代碼可以不用管 下面會繼續給出成功且不重複回調的代碼 這邊的支付都是JSAPA支付
*/
InputStream inputStream;
StringBuffer sb = new StringBuffer();
try {
inputStream = request.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(
inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
Map<String, String> m = new HashMap<String, String>();
m = WXUtil.doXMLParse(sb.toString());
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = m.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
String key = WxPayPojo.MCH_KEY; // 祕鑰 這個就是你的微信商戶平臺的祕鑰
if (WXUtil.isTenpaySign("UTF-8", packageParams, key)) {
String resXml = "";
if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
// 得到返回的參數
String openid = (String) packageParams.get("openid");
String transaction_id = (String) packageParams
.get("transaction_id");
String orderNumberMain = (String) packageParams
.get("out_trade_no");
// 這裏可以寫你需要的業務
LOGGER.debug("我是回調函數啊!---我執行了---------------------");
LOGGER.debug("openid---->" + openid);
LOGGER.debug("transaction_id---->" + transaction_id);
LOGGER.debug("out_trade_no---->" + orderNumberMain);
resXml =
"<xml>"
+ "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>"
+ "</xml> ";
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
return;
} else {
LOGGER.debug("回調失敗");
}
} else {
LOGGER.debug("回調失敗");
}
} catch (IOException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
}
}
}
下面是微信支付回調可以調用的參數,你看你項目會用到什麼,一般來說獲取到訂單編號 通過訂單編號操作(點擊這裏查看微信支付回調JSAPI文檔):
即上面的都可以通過packageParams.get(") 對應的到返回值。
下面是我參考一個博客之後修改的,符合我的這個要求:鏈接如下:https://blog.csdn.net/qq_37105358/article/details/81285779
/**
* 微信小程序支付成功回調函數
* @param request
* @param response
* @throws Exception
*/
@RequestMapping(value = "/weixinpay/notify")
public void wxNotify(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);
}
br.close();
//sb爲微信返回的xml
String notityXml = sb.toString();
String resXml = "";
System.out.println("接收到的報文:" + notityXml);
Map map = PayUtil.doXMLParse(notityXml);
String returnCode = (String) map.get("return_code");
if("SUCCESS".equals(returnCode)){
//驗證簽名是否正確
Map<String, String> validParams = PayUtil.paraFilter(map); //回調驗籤時需要去除sign和空值參數
String validStr = PayUtil.createLinkString(validParams);//把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串
String sign = PayUtil.sign(validStr, WxPayPojo.MCH_KEY, "utf-8").toUpperCase();//拼裝生成服務器端驗證的簽名
// 因爲微信回調會有八次之多,所以當第一次回調成功了,那麼我們就不再執行邏輯了
//根據微信官網的介紹,此處不僅對回調的參數進行驗籤,還需要對返回的金額與系統訂單的金額進行比對等
if(sign.equals(map.get("sign"))){
// 得到返回的參數
//這邊我上面也說過了 同理 需要什麼參數 直接通過map.get獲取 參數列表我上面也列舉了
String openid = (String) map.get("openid");
String transaction_id = (String) map.get("transaction_id");
String orderNumberMain = (String) map.get("out_trade_no");
/**回調邏輯代碼編寫*/
//通知微信服務器已經支付成功
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
System.out.println("微信支付回調失敗!簽名不一致");
}
}else{
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[報文爲空]]></return_msg>" + "</xml> ";
}
System.out.println(resXml);
System.out.println("微信支付回調數據結束");
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
用到的工具類:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
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.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
public class PayUtil {
/**
* * 簽名字符串
* * @param text 需要簽名的字符串
* * @param key 密鑰
* * @param input_charset 編碼格式
* * @return 簽名結果
*
*/
public static String sign(String text, String key, String input_charset) {
text = text + "&key=" + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* * 簽名字符串
* * @param text 需要簽名的字符串
* * @param sign 簽名結果
* * @param key 密鑰
* * @param input_charset 編碼格式
* * @return 簽名結果
*
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if (mysign.equals(sign)) {
return true;
} else {
return false;
}
}
/**
* * @param content
* * @param charset
* * @return
* * @throws SignatureException
* * @throws UnsupportedEncodingException
*
*/
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);
}
}
private static boolean isValidChar(char ch) {
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
return true;
if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
return true;// 簡體中文漢字編碼
return false;
}
/**
* * 除去數組中的空值和簽名參數
* * @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;
}
/**
* * 把數組所有元素排序,並按照“參數=參數值”的模式用“&”字符拼接成字符串
* * @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 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);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toString();
}
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml數據。
* * @param strxml
* * @return
* * @throws JDOMException
* * @throws IOException
*
*/
public static Map doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//關閉流
in.close();
return m;
}
/**
* * 獲取子結點的xml
* * @param children
* * @return String
*
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
public static InputStream String2Inputstream(String str) {
return new ByteArrayInputStream(str.getBytes());
}
}