全面詳細的微信支付思路流程以及專案程式碼分享
之前一直沒有接觸微信支付這方面的業務,現在因專案需要,需要用到此功能,開始各種百度,稍微瞭解了一下,微信支付分為:
支付寶支付、APP支付、掃碼支付,但是對於H5支付和支付寶支付現在還是沒有徹底搞明白他兩的區別,希望大佬們可以稍微提點一二,小弟先在此謝過!
大體思路如下:
1】.獲取code(使用者進入微信公眾號即可獲得code,直接通過request.getParameter("code")獲得),
一般情況下前臺會傳過來,後臺直接拿;
或者自己去獲取code值,參考連結https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
裡邊appid要改變換成自己專案中的appid即可,redirect_uri要改變,其餘不用改變。
其中可能出錯的地方是redirect_uri,我自己也遇到過這個坑,在此把解決方案也發出來,方便大家參考
獲取code時,出現微信的redirect_uri引數錯誤解決辦法
參考連結:https://www.cnblogs.com/xbj-2016/p/7086149.html
若沒有錯誤,在微信客戶端點開下面連結,會開啟的頁面就是你上圖中域名的地址,其頁面中的地址就會包含code值,現在就獲取到code了,棒棒噠!
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
2】.根據code獲取openid
code獲取到的是access_token,json字串,裡面有個引數為openid,這是我們需要的;
openid即就是每個使用者在同一個公眾號appid中的唯一標識,他就是用來識別不同使用者的;
Code值只可以使用一次,然後就過期失效,若使用同一個code值去獲取openid,顯示已被使用,報錯,
即使同一個人使用也不行;Code獲取openid,同一個使用者所獲取到的openid永遠是同一個,不會改變。
Access_token兩個小時就會失效, 但是Openid不會失效。
3】.客戶端頁面中點選支付按鈕時,給資料庫中儲存訂單,(如此單多少錢等等)
4】.調微信支付下單介面(最終是要拿到預付單資訊prepay_id,說明就成功了)
裡面傳入各種引數,和上面生成的openid,來生成sign簽名,然後把這些引數(包括openid、sign簽名)轉成xml格式,
呼叫微信伺服器,生成xml字串,把他轉成map格式,然後拿取預付單資訊prepay_id,把其當成引數,再次獲取生成sign
簽名,然後把這些引數傳給前端(json格式),到這塊已經支付成功了;
5】.回撥方法
回撥方法中作相應的業務邏輯處理,到了回撥方法中,就說明已經支付成功了,現在就是把資料庫中的對應訂單改變其狀態啥的,例如為已支付,完了後返回給前端;
程式碼:
(1):通過code獲取openid
/**
* 獲取openId
* @param code
* @return
*/
@ApiOperation(value="根據code獲取openid",notes="")
@PostMapping("/getOpenId")
public ResBuilder<?> getOpenId(@RequestParam("code") String code){
Map<String,Object> map = new HashMap<String,Object>();
try {
map.put("status", 0);//預設失敗
map.put("appId", WxPayConfig.APPID);//為了給前端用
//根據code獲取openid(使用者進入微信公眾號即可獲得code,前臺直接傳過來)
if(!StringUtil.isNullOrEmpty(code)){//有code
//請求api獲取openid
String str = HttpInvoker.sendGetRequest(StringUtil.format(WxPayConfig.GET_OPENID, new Object[]{WxPayConfig.APPID,WxPayConfig.APPSECRET,code}));
//將json轉map
Map<String, Object> toMap = WeixinUtil.jsonStrToMap(str);
System.out.println(toMap.toString());
if(!StringUtil.isNullOrEmpty(toMap.get("openid"))){//獲取openid成功
map.put("status", 1);//成功
map.put("openId", toMap.get("openid").toString());
return new ResBuilder<>("ok",map);
}else {
return new ResBuilder<>("error","獲取openid失敗");
}
}else {
return new ResBuilder<>("error1","沒有傳入code值");
}
} catch (Exception e) {
e.printStackTrace();
return new ResBuilder<>("no","系統異常");
}
}
(2):統一下單介面程式碼
/**
* 統一下單 (預支付)
* @author zl
* @date 2018年9月29日 上午11:10:22
* @param map openId--微信id;orderId--·預存訂單id body--商品名;deviceId--組櫃裝置id;total_fee--金額(分) -- 實際金額需乘以 100
* @return
*/
@ApiOperation(value="統一下單介面",notes="")
@PostMapping("/unifiedOrder")
public ResBuilder<?> unifiedOrder(@RequestBody Indents indents,HttpServletRequest request){
Map<String,Object> rsMap = new HashMap<String, Object>();
try {
Client client=clientRepository.findById(indents.getUserid());//客戶
if(StringUtil.isNullOrEmpty(client.getOpenId())) {
return new ResBuilder<>("no","openId為空");
}
Map<String,String> paramMap = new HashMap<String, String>();
paramMap.put("appid", WxPayConfig.APPID);//appid
paramMap.put("mch_id", WxPayConfig.MCHID);//商戶號
paramMap.put("nonce_str", WeixinUtil.createNoncestr(32));//隨機字串
StoreGoods storeGoods=storeGoodsRepository.findById(indents.getStoreid());//商品
paramMap.put("body", storeGoods.getName());//商品名稱
paramMap.put("out_trade_no", indents.getIndents());//訂單號
paramMap.put("total_fee",indents.getBigmoney()+"");//總金額
paramMap.put("spbill_create_ip", WeixinUtil.getRemoteHost(request));//客戶(下單者的)ip地址
paramMap.put("notify_url", WxPayConfig.NOTIFY_URL);//回撥地址
//paramMap.put("trade_type", "JSAPI");//交易型別為公眾號支付
paramMap.put("trade_type", "MWEB");//(交易型別)H5支付必須為MWEB
paramMap.put("openid", client.getOpenId());//使用者openid
paramMap.put("sign", WeixinUtil.getSign(paramMap));//簽名 ,預設sign_type是MD5
//呼叫統一下單時多了一個sign的引數,其他引數名稱需要全部相同。
String unifiedStr = HttpInvoker.sendPostRequest(WxPayConfig.UNIFIED_ORDER, WeixinUtil.arrayToXml(paramMap));
//微信端生成的是xml格式,現在轉成map格式
Map<String, String> preMap = WeixinUtil.xmltoMap(unifiedStr);
//微信生成的預支付回話標識,用於後續介面呼叫中使用,該值有效期為2小時
String prepayId = preMap.get("prepay_id");
if(StringUtil.isNullOrEmpty(prepayId)){
throw new Exception("獲取prepay_id錯誤:" + preMap.get("return_msg"));
}
//微信內H5調起支付,網頁端介面請求引數列表
/*返回結果值說明
返回值 描述
get_brand_wcpay_request:ok 支付成功
get_brand_wcpay_request:cancel 支付過程中使用者取消
get_brand_wcpay_request:fail 支付失敗
呼叫支付JSAPI缺少引數:total_fee
1、請檢查預支付會話標識prepay_id是否已失效
2、請求的appid與下單介面的appid是否一致*/
Map<String,String> jsApiMap = new HashMap<String,String>();
jsApiMap.put("appId", WxPayConfig.APPID);
jsApiMap.put("timeStamp", WeixinUtil.getTimestamp(new Date()) + "");
jsApiMap.put("nonceStr", WeixinUtil.createNoncestr(32));
jsApiMap.put("package", "prepay_id=" + prepayId);
jsApiMap.put("signType", "MD5");
jsApiMap.put("paySign", WeixinUtil.getSign(jsApiMap));
rsMap.put("status", 1);//成功
rsMap.put("param", JSONObject.fromObject(jsApiMap));
JSONObject json = JSONObject.fromObject(rsMap);
//out.print(json);把這些引數傳送給前端,前端給微信伺服器發起一個微信支付請求,此時前端頁面顯示的是使用者輸入密碼介面,
//然後輸完密碼返回給前端是支付成功還是失敗.
return new ResBuilder<>("ok",json);
} catch (Exception e) {
e.printStackTrace();
return new ResBuilder<>("no","系統異常");
}
}
(3):回撥方法
/**
*
* 回撥方法 此處作業務邏輯處理,到此處說明已支付成功,在這塊需要改變訂單狀態等邏輯處理的資訊
* @param request
* @param response
* @return
* @throws IOException
*/
@ApiOperation(value="回撥業務處理",notes="")
@PostMapping("/notifyUrl")
public String notifyUrl(HttpServletRequest request,HttpServletResponse response) throws IOException {
String resXml="";
String xmlBack="";
System.err.println("進入非同步通知");
InputStream is = request.getInputStream();
//將InputStream轉換成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
resXml=sb.toString();
System.err.println(resXml);
Map<String, String> notifyMap = new HashMap<String, String>();
try {
notifyMap = WXPayUtil.xmlToMap(resXml);
String return_code = notifyMap.get("return_code");//狀態
String out_trade_no = notifyMap.get("out_trade_no");//訂單號
if("SUCCESS".equals(return_code)){
Indents indents=indentsRepository.findByIndents(out_trade_no);
if(indents==null) {
xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
return xmlBack;
}
indents.setState(1);//修改狀態為1:已支付
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
indents.setPaytime(sdf.format(new Date()));//設定支付日期
indentsRepository.save(indents);
xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}else {
xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
}
} catch (Exception e) {
}
return xmlBack;
}
(4):把微信配置檔案類在此也發出來
package org.travel.config;
/**
* 微信相關引數配置
* @author Administrator
*
*/
public class WxPayConfig {
//=======【基本資訊設定】=====================================
//微信公眾號身份的唯一標識。稽核通過後,在微信傳送的郵件中檢視
public static final String APPID = "*************";//需改,給客戶要
//受理商ID,身份標識
public static final String MCHID = "**********";//需改,給客戶要
//商戶支付金鑰Key。稽核通過後,在微信傳送的郵件中檢視
public static final String KEY = "**********";//需改,給客戶要
//JSAPI介面中獲取openid,稽核後在公眾平臺開啟開發模式後可檢視
public static final String APPSECRET = "*******";//需改,給客戶要
//=======【非同步通知url設定】===================================
//非同步通知url,商戶根據實際開發過程設定
private static String SERVER_URL = "http://*.*.*.*:****";//需改專案在公網上的地址
(192伺服器不可以,只是區域網,59伺服器可以)
//自己寫的回撥方法的地址
public static final String NOTIFY_URL = SERVER_URL + "/WxGetOpenId/notifyUrl";
//編碼方式
public static final String ENCODE = "UTF-8";
//微信uri-根據code獲取openid(獲取access_token,access_token裡面帶有openid)
public static String GET_OPENID =
"https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret=
{1}&code={2}&grant_type=authorization_code";//不變
//微信uri-請求預支付介面
public static String UNIFIED_ORDER =
"https://api.mch.weixin.qq.com/pay/unifiedorder";//不變
}
(5):專案所需sdk
<!-- 微信 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
至此:大功告成!