前言
最近做的項目有對接微信支付的需求,於是開始了一個人的摸索。本文的前提是公司已經申請了商戶號和appid,設置了商戶號對應的key,即appId,mchId,key三個參數。以下爲開發步驟:
1 閱讀微信官方開發文檔
微信官方文檔鏈接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
這裏選擇流程更爲簡單的模式二,主要是看模式二的時序圖:
作爲開發者,此模式跟微信支付系統直接相關的是步驟2,步驟10,步驟11
(2)用戶確認支付後調用微信支付【統一下單API】生成預支付交易;
(10)微信支付系統通過發送異步消息通知商戶後臺系統支付結果。商戶後臺系統需回覆接收情況,通知微信後臺系統不再發送該單的支付通知。
(11)未收到支付通知的情況,商戶後臺系統調用【查詢訂單API】。
2 導入相關依賴
2.1 添加pom.xml依賴
由於採用maven開發,故在pom.xml文件加上如下依賴:
<!-- wxpay -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
也可以在官方開發文檔網站下載sdk和demo
2.2 maven依賴的使用
只需新建一個Java配置類,實現WXPayConfig接口,配置appId,mchId,key三個主要參數
public class WxPayConfig implements WXPayConfig {
@Override
public String getAppID() {
return "xxxxxxxxxxxxxxxxxx";
}
@Override
public String getMchID() {
return "xxxxxxxxxx";
}
@Override
public String getKey() {
return "xxxxxxxxxxxxxxxxxxxxx";
}
/*此證書是退款才需要配置*/
@Override
public InputStream getCertStream() {
return this.getClass().getResourceAsStream("/cert/apiclient_cert.p12");
}
@Override
public int getHttpConnectTimeoutMs() {
return 6000;
}
@Override
public int getHttpReadTimeoutMs() {
return 8000;
}
}
3 調用統一下單接口
3.1 接口地址
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
// new一個新建的配置類
WxPayConfig wxPayConfig = new WxPayConfig();
// WXPay即爲maven依賴的jar包封裝好的調用官方接口的
WXPay wxPay = new WXPay(wxPayConfig);
// 調用統一下單接口
Map<String, String> returnMap = wxPay.unifiedOrder(dataMap);
3.2 此處應注意參數傳遞notify_url這個參數(在第4節會細說)
3.3 接口的返回值
根據官方文檔得知下單接口的返回值,可以判斷returnMap的屬性return_code和result_code都爲SUCCESS時,返回二維碼鏈接code_url
if(WXPayConstants.SUCCESS.equals(returnMap.get("return_code")) &&
WXPayConstants.SUCCESS.equals(returnMap.get("result_code"))){
return returnMap.get("code_url");
}
3.4 二維碼插件
由於項目是前後端分離,這裏使用前端插件生成qrcode,具體使用請參考http://code.ciaoca.com/javascript/qrcode/
<script type="text/javascript" src="qrcode.js"></script>
<script type="text/javascript">
// 設置參數方式
var qrcode = new QRCode('qrcode', {
// 只需替換這一串內容即可生成二維碼
text: 'weixin://wxpay/bizpayurl?pr=xxxxxx',
width: 256,
height: 256,
colorDark : '#000000',
colorLight : '#ffffff',
correctLevel : QRCode.CorrectLevel.H
});
</script>
ps:此處也可使用zxing的工具類直接在後端生成二維碼圖片返回給前端顯示,暫不詳細展開。
4 設置支付結果通知的notify_url
開發者在調用統一下單接口時,需要傳遞notify_url給微信支付系統。使得用戶掃碼付款成功後,微信會主動調用開發者設置的接口,對支付結果進行通知,開發者可對支付結果進行判斷,並結合業務需求,實現項目業務。
4.1 使用內網穿透工具ngrok實現內網穿透
若是在本地測試開發,則需要使用內網穿透工具,方便測試
這樣微信訪問馬賽克的鏈接,就相當於訪問了本地的8080端口的程序接口
4.2 配置notify_url
在項目配置文件application.properties文件或者自定義文件中配置notify_url
wechat.notify_url=http://xxxx.ngrok.xxxxx.cn/test/payNotify
4.3 新增payNotify接口,以便微信回調
controller層:提供微信回調接口
@PostMapping(value = "/payNotify")
public void payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
String notify = payService.payNotify(request);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
response.getWriter().println(notify);
}
service層:接收處理參數,並返回指定內容給微信支付系統
// 返回微信格式
private static final String RESULT_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
private static final String RESULT_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[參數爲空]]></return_msg></xml>";
// 異步接收到微信的支付結果
String requestBody = getRequestBody(request);
// 使用maven依賴工具類對結果進行轉化
Map<String, String> map = WXPayUtil.xmlToMap(requestBody);
LOGGER.debug("微信支付結果通知:{}",requestBody);
// 返回成功
if(!WXPayConstants.SUCCESS.equals(map.get("return_code"))){
LOGGER.error("支付失敗");
return RESULT_FAIL;
}
// 簽名
String sign = map.get("sign");
// 業務結果
String result_code = map.get("result_code");
// 商戶訂單號
String out_trade_no = map.get("out_trade_no");
// 訂單金額
String total_fee = map.get("total_fee");
// 微信支付訂單號
String transaction_id = map.get("transaction_id");
// 1,驗證簽名是否正確
WxPayConfig config = new WxPayConfig();
String signature = WXPayUtil.generateSignature(map, config.getKey());
LOGGER.info("重新生成的簽名:{}",signature);
if(!signature.equals(sign)){
LOGGER.info("簽名驗證失敗");
return RESULT_FAIL;
}
LOGGER.info("驗籤成功");
// 驗證支付金額和訂單金額是否一致,此處省略代碼
// ...
// 驗證通過,則返回成功結果
return RESULT_SUCCESS;
獲取request輸入流,解析參數
/**
* 接收微信傳過來的結果
* @param request req
* @return String格式
* @throws IOException
*/
private String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder inputLine = new StringBuilder();
String line;
try {
while ((line = reader.readLine())!=null){
inputLine.append(line);
}
} catch (Exception e){
LOGGER.debug(e.getMessage());
e.printStackTrace();
} finally {
reader.close();
inputStream.close();
}
return inputLine.toString();
}
4.4 注意事項
微信回調此處有坑,微信回調是定時重試地調用,若因爲網絡原因或者其他因素,可能導致即使是返回成功了,也會不斷調用回調接口,廢話不多說,上代碼:
// 1獲取訂單號,查詢該訂單在商戶系統中的訂單狀態
Map<String, Object> order = orderInfoMapper.queryOrder(out_trade_no);
// 2判斷訂單是否支付成功,成功則直接返回
Integer statu = Integer.parseInt(order.get("statu") + "");
// 1爲已支付
if(1 == statu){
LOGGER.info("該訂單已支付,訂單號爲:{}",out_trade_no);
return RESULT_SUCCESS;
}
5 查詢訂單狀態接口調用
接口鏈接:https://api.mch.weixin.qq.com/pay/orderquery
參照api文檔,傳遞參數直接調用:
/**
* 查詢訂單
* @param dataMap 數據參數
* @return 返回結果集合
* @throws Exception 異常
*/
public static Map<String,String> queryOrder(Map<String,String> dataMap) throws Exception {
WxPayConfig wxPayConfig = new WxPayConfig();
WXPay wxPay = new WXPay(wxPayConfig);
Map<String, String> orderQuery = wxPay.orderQuery(dataMap);
return orderQuery;
}
6 退款接口
退款接口需下載證書,並配置證書路徑,未完待續...