一 準備
1 配置ngrok
將ngrok映射到本地8170端口,並啓動
2 添加工具類
在common_util中添加工具類StreamUtils
package com.atguigu.guli.service.trade.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class StreamUtils {
private static int _buffer_size = 1024;
/**
* InputStream流轉換成String字符串
* @param inStream InputStream流
* @param encoding 編碼格式
* @return String字符串
*/
public static String inputStream2String(InputStream inStream, String encoding){
String result = null;
ByteArrayOutputStream outStream = null;
try {
if(inStream != null){
outStream = new ByteArrayOutputStream();
byte[] tempBytes = new byte[_buffer_size];
int count = -1;
while((count = inStream.read(tempBytes, 0, _buffer_size)) != -1){
outStream.write(tempBytes, 0, count);
}
tempBytes = null;
outStream.flush();
result = new String(outStream.toByteArray(), encoding);
outStream.close();
}
} catch (Exception e) {
result = null;
} finally {
try {
if(inStream != null) {
inStream.close();
inStream = null;
}
if(outStream != null) {
outStream.close();
outStream = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
二 支付回調
1 回調方法
該鏈接是通過【統一下單API】中提交的參數notify_url設置,如果鏈接無法訪問,商戶將無法接收到微信通知。
參考文檔:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
2 控制器
/**
* 功能描述:微信回調通知
*
* @author cakin
* @date 2021/1/13
* @param request 請求
* @return response 響應
*/
@PostMapping("callback/notify")
public String wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("\n callback/notify 被調用");
ServletInputStream inputStream = request.getInputStream();
String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8");
log.info("\n notifyXml = \n " + notifyXml);
// 驗籤:驗證簽名是否正確
if (WXPayUtil.isSignatureValid(notifyXml, weixinPayProperties.getPartnerKey())) {
// 解析返回結果
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
// 判斷支付是否成功
if ("SUCCESS".equals(notifyMap.get("result_code"))) {
// 金額校驗
String totalFee = notifyMap.get("total_fee"); //支付結果返回的訂單金額
String outTradeNo = notifyMap.get("out_trade_no");//訂單號
Order order = orderService.getOrderByOrderNo(outTradeNo);//查詢本地訂單
// 校驗返回的訂單金額是否與商戶側的訂單金額一致
if (order != null && order.getTotalFee().intValue() == Integer.parseInt(totalFee)) {
// 接口調用的冪等性:無論接口被調用多少次,最後所影響的結果都是一致的
if (order.getStatus() == 0) {
// 更新訂單狀態
orderService.updateOrderStatus(notifyMap);
}
// 支付成功:給微信發送我已接收通知的響應
// 創建響應對象
Map<String, String> returnMap = new HashMap<>();
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "OK");
String returnXml = WXPayUtil.mapToXml(returnMap);
response.setContentType("text/xml");
log.info("支付成功,通知已處理");
return returnXml;
}
}
}
// 創建響應對象:微信接收到校驗失敗的結果後,會反覆的調用當前回調函數
Map<String, String> returnMap = new HashMap<>();
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
String returnXml = WXPayUtil.mapToXml(returnMap);
response.setContentType("text/xml");
log.info("校驗失敗");
return returnXml;
}
3 服務層
接口
void updateOrderStatus(Map<String, String> notifyMap);
實現
@Transactional(rollbackFor = Exception.class)
@Override
public void updateOrderStatus(Map<String, String> notifyMap) {
// 更新訂單狀態
String outTradeNo = notifyMap.get("out_trade_no");
Order order = this.getOrderByOrderNo(outTradeNo);
order.setStatus(1);//支付成功
baseMapper.updateById(order);
// 記錄支付日誌
PayLog payLog = new PayLog();
payLog.setOrderNo(outTradeNo);
payLog.setPayTime(new Date());
payLog.setPayType(1);//支付類型:微信支付
payLog.setTotalFee(Long.parseLong(notifyMap.get("total_fee")));
payLog.setTradeState(notifyMap.get("result_code"));
payLog.setTransactionId(notifyMap.get("transaction_id"));
payLog.setAttr(new Gson().toJson(notifyMap));
payLogMapper.insert(payLog);
//更新課程銷量
eduCourseService.updateBuyCountById(order.getCourseId());
}