背景:最近做項目,甲方提出一個需要要求在手機端直接微信注冊成功後,直接登入并發起微信支付。再三思考後,才決定使用jsapi微信支付。
微信支付官方文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
首先微信普通商戶版有NATIVE支付、JSAPI支付、APP支付、H5支付、付款碼支付、小程式支付;其中我認為作為web開發的最常用的是native、jsapi、h5支付了。但是jsapi支付在官方微信支付文檔中,留下了很多坑,導緻在使用過程中無比麻煩,下面具體來說說,希望能對各位看客有點幫助。
1.微信支付最麻煩的便是公衆平台和商戶平台的配置,将公衆号APPID、APPSECRET,商戶号appid、秘鑰key配置好。
商戶平台:(https://pay.weixin.qq.com/)
1)微信商戶平台->産品中心->我的産品,檢視你要的支付類型是否開通,未開通,則申請開通,按照開通流程走,主要是申請安裝證書即可。

2)微信商戶平台->産品中心->開發配置,檢視支付配置是否已經配置成功。native支付則配置掃碼支付回調位址,jsapi支付則配置公衆号支付授權目錄,該目錄最多可添加5個,如發起支付頁面為:http://baidu.com/index.html,則目錄配置為:http://baidu.com/;如發起支付頁面為:http://baidu.com/wxpay/index.html,則目錄配置為:http://baidu.com/wxpay/;(這一步很關鍵,不然會在發起支付時提示注冊url無效)
3)微信商戶平台->賬戶中心->賬戶設定->API安全,檢視是否已經設定了商戶密鑰key,此密鑰在後面生成簽名sign中特别重要,必須設定,設定成功後記住密鑰;
以上是商戶平台的配置;
微信公衆平台:(https://mp.weixin.qq.com/)
1)微信公衆平台->開發->基本配置,檢視開發者密碼AppSecret是否已經設定;
2)微信公衆平台->設定->公衆号設定,檢視網頁回調位址是否已經配置好,在這裡我将所有的域名配置都配置好了。需要将MP_verify_MHYOHtHKmJzSkCj0.txt檔案放置到項目的根目錄下,隻要通路域名後可以通路到就可以,如配置域名:baidu.com,則通路http://baidu.com/MP_verify_MHYOHtHKmJzSkCj0.txt時通路得到就表示配置成功。
以上是公衆平台需要的配置。
2.配置完後,檢視商戶平台的jsapi微信支付開發文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1,在微信浏覽器内發起支付之前,必須先獲得預支付訂單号prepay_id,也就是這個訂單号,需要大費周折的去拼接各種資料;
1)由于擷取prepay_id的必填參數中有openid,是以先擷取openid,通過公衆平台網頁授權來擷取。
微信公衆平台->開發->接口權限->網頁服務->網頁授權(網頁授權擷取使用者基本資訊 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842)
第一步:使用者同意授權,擷取code:
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:回調位址,在配置時需要配置域名,此處的回調位址域名必須與配置的一緻否則無效。
SCOPE:預設擁有scope參數中的snsapi_base和snsapi_userinfo,此處是jsapi支付,必須使用snsapi_userinfo,否則會出現“此公衆号并沒有這些scope的權限,錯誤碼:10005”的提示;
STATE:随機參數,可以用來區分或者攜帶其他參數到回調位址中;
傳回值:code作為換取access_token的臨時票據和state随機參數。
第二步:通過code換取網頁授權access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
參數說明:APPID:微信公衆平台AppId;
SECRET:微信公衆号密鑰,需要配置;
CODE:第一步獲得的code參數;
傳回值:access_token和openid,此處隻寫明這兩個較為重要的參數。
到目前為止便得到了openid。可以進行統一下單了。
2).檢視API清單->統一下單(調用該接口是為了在微信支付服務背景生成預支付交易單)
接口連結:https://api.mch.weixin.qq.com/pay/unifiedorder
請求參數:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 檢視官方文檔,此處重點點名:openid,此次為JSAPI,是以必填;sign簽名是整個請求參數中最難生成的。
當傳回結果return_code 和result_code都為SUCCESS,可以獲得prepay_id.
3)得到預訂單号後,就可以在微信内置浏覽器中發起支付。将統一下單接口傳回的資料中拿到appid、nonce_str、prepay_id,并結合signType、timeStamp、key(商戶号)通過MD5加密傳回paySign支付簽名,将這些資料一并傳回前端頁面調用微信内置對象WeixinJSBridge,發起支付。(微信内h5調起支付連結:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 )
整個支付就到此結束了。
在這個過程中,我遇到的最慘痛的難點就是支付簽名paySign的生成,因為在微信統一下單接口傳回的資料中還包含了sign簽名的傳回,我一直以為可以使用這個作為支付簽名。然而在網上找了很多大佬的總結後,都說統一下單接口傳回的sign的簽名加密方式不是MD5,然而支付簽名必須是MD5加密而造成的。但是我覺得不是,因為我已經明确在下單接口中的參數signType寫明就是MD5了,是以表示下單接口傳回的sign并不能作為微信發起支付的簽名paySign。必須自己重新根據傳回的prepay_id生成新的支付簽名。
代碼的實作方式如下:
(後端為java,前端使用vue)
- controller層的代碼實作:
/**
* 第三方發起微信授權登入請求,微信使用者允許授權第三方應用後,
* 微信會拉起應用或重定向到第三方網站,并且帶上授權臨時票據code參數
* appid: 微信公衆号唯一辨別id
* redirect_uri: 回調位址,此處回調位址為/WXRegisterPay
* 該接口傳回微信請求url
*
* @title 微信授權登入請求臨時票據code
* @resp code_url第三方請求臨時票據code的url|String|必填
* @return 第三方請求臨時票據code的位址
* @Author chenxm
*/
@RequestMapping(value = "getWXCode",method = RequestMethod.GET)
@OpLog(value = "擷取code",module = "微信支付")
@TokenIgnore
BaseResult getWXCode(){
Map<String,Object> map = new HashMap<String,Object>();
// 公衆平台
String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + PropertyUtil.WX_MP_APP_ID +
"&redirect_uri=" + URL.encode(PropertyUtil.WX_REGISTERPAY) +
"&response_type=code" +
"&scope=snsapi_userinfo" +
"&state=" + MREGISTER +
"#wechat_redirect";
map.put("code_url",url);
return BaseResult.succResult(map);
}
/**
* 擷取code後,微信平台發起的回調接口
* @return
*/
@RequestMapping(value = "WXRegisterPay", method = RequestMethod.GET)
@OpLog(value = "手機微信注冊支付", module = "Vip子產品")
BaseResult WXRegisterPay(@LoginUser Member member, String code, String state, HttpServletRequest request){
// 微信支付參數
Map<String,Object> pay = new HashMap<>();
// 支付簽名
StringBuilder paySign = new StringBuilder();
WebChatDto webChatAccess = weixinPayService.getAccessToken(code, 2);
//擷取目前的ip位址
String ip = WxPayToolUtil.getRemoteHost(request);
try {
Map payMap = weixinPayService.weixinRegPay(member.getId(),ip,webChatAccess.getOpenId());
// 拼接微信支付參數
pay.put("appId",payMap.get("appid"));
pay.put("timeStamp",Long.toString(System.currentTimeMillis() / 1000));
pay.put("nonceStr",payMap.get("nonce_str"));
pay.put("signType",PropertyUtil.WX_SIGN_TYPE);
pay.put("package","prepay_id="+payMap.get("prepay_id"));
// 生成支付簽名
paySign.append("appId=").append(pay.get("appId"));
paySign.append("&nonceStr=").append(pay.get("nonceStr"));
paySign.append("&package=").append(pay.get("package"));
paySign.append("&signType=").append(pay.get("signType"));
paySign.append("&timeStamp=").append(pay.get("timeStamp"));
paySign.append("&key=").append(PropertyUtil.WX_API_KEY);
pay.put("paySign", MD5Util.MD5Encode(paySign.toString(), "UTF-8").toUpperCase());
logger.info("URL,{}",payMap);
logger.info("paySign,{}",pay.get("paySign"));
} catch (Exception e) {
logger.info("URL,{}",e.getMessage());
}
return BaseResult.succResult(pay);
}
/**
* 發起支付後,微信平台發起的回調方法
*
* @title 微信回調方法
*/
@RequestMapping("weixinNotify")
@OpLog(value = "微信平台發起的回調方法", module = MODUEL)
@TokenIgnore
void weixinNotify(HttpServletRequest request, HttpServletResponse response) throws JDOMException, Exception {
//擷取輸入流
InputStream inputStream = request.getInputStream();
StringBuffer sb = new StringBuffer();
String s = null;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> m = new HashMap<>();
m = XMLUtil4jdom.doXMLParse(sb.toString());
//過濾空 設定 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<>();
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);
}
//判斷簽名是否正确
if (WxPayToolUtil.isTenpaySign("UTF-8", packageParams, PropertyUtil.WX_API_KEY)) {
String resXml = "";
if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
//商戶訂單号
String out_trade_no = (String) packageParams.get("out_trade_no");
//微信訂單号
String transaction_id = (String) packageParams.get("transaction_id");
//現金支付金額
float price = Float.valueOf((String) packageParams.get("cash_fee"));
//擷取微信查詢訂單傳回的資料
String str = weixinPayService.weixinOrderquery(transaction_id);
logger.info(str);
//通知微信.異步确認成功.必寫.不然會一直通知背景.八次之後就認為交易失敗了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
logger.info("支付成功!");
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[封包為空]]></return_msg>" + "</xml> ";
logger.info("支付失敗!");
}
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
}catch (Exception e){
logger.info("微信平台發起的回調方法,{}", e.getMessage());
}finally {
if (null != out){
out.flush();
out.close();
}
}
} else {
logger.info("通知簽名驗證失敗");
}
}
-
service層的代碼實作:
(1)接口:
/**
* 請求擷取access_token
* @param code
* @return
*/
WebChatDto getAccessToken( String code, Integer type);
/**
* 微信jsapi支付
* @return
* @throws Exception
*/
Map weixinRegPay(String userId,String ip, String openid) throws Exception;
/**
* 微信查詢訂單
* @param transaction_id 微信訂單号
* @return
* @throws Exception
*/
String weixinOrderquery(String transaction_id) throws Exception;
(2)實作類:
/*擷取access_token的url*/
public static final String AccessToken_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code&type";
@Override
public WebChatDto getAccessToken(String code, Integer type) {
// 請求擷取access_token
// 開放平台
String url = String.format(AccessToken_url,PropertyUtil.WX_OPEN_APPID,PropertyUtil.WX_OPEN_APPSECRET,code);
if(type == 2){
// 公衆平台
url = String.format(AccessToken_url,PropertyUtil.WX_MP_APP_ID,PropertyUtil.WX_MP_APPSECRET,code);
}
// 擷取請求接口傳回的資訊
JSONObject jsonObject = null;
try {
jsonObject = WeChatUtil.doGetJson(url);
} catch (IOException e) {
LOGGER.info("擷取TOKEN,{}",e.getMessage());
}
// 微信使用者唯一辨別id
String openId = jsonObject.getString("openid");
// 許可token
String accessToken = jsonObject.getString("access_token");
WebChatDto webChat = new WebChatDto();
if(EmptyUtils.isNotEmpty(openId) && EmptyUtils.isNotEmpty(accessToken)){
webChat.setAccessToken(accessToken);
webChat.setOpenId(openId);
}else{
throw new ValidateException("微信授權登入失敗");
}
return webChat;
}
@Override
public Map weixinRegPay(String userId,String spbill_create_ip, String openid) throws Exception {
//有序的Map
SortedMap<Object, Object> params = WxPayToolUtil.getPayParams(userId, openid, spbill_create_ip, PropertyUtil.WX_trade_type_JSAPI);
//格式化成XML形式,微信通信是借助XML
String requestXML = WxPayToolUtil.getRequestXml(params);
//得到微信傳回的資料
String resXml = HttpUtil.postData(PropertyUtil.WX_UFDODER_URL, requestXML);
Map map = XMLUtil4jdom.doXMLParse(resXml);
return map;
}
@Override
public String weixinOrderquery(String transaction_id) throws Exception {
//有序的Map
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
//公衆賬号ID
packageParams.put("appid", PropertyUtil.WX_MP_APP_ID);
//商戶号
packageParams.put("mch_id", PropertyUtil.WX_MCH_ID);
//微信訂單号
packageParams.put("transaction_id", transaction_id);
//擷取目前日期和時間
String currTime = WxPayToolUtil.getCurrTime();
//獲得目前時間
String strTime = currTime.substring(8, currTime.length());
//取出一個指定長度大小的随機正整數.
String strRandom = WxPayToolUtil.buildRandom(4) + "";
//随機字元串,長度要求在32位以内。
String nonce_str = strTime + strRandom;
//随機字元串
packageParams.put("nonce_str", nonce_str);
//簽名類型
packageParams.put("sign_type", PropertyUtil.WX_SIGN_TYPE);
//生成簽名
String sign = WxPayToolUtil.createSign("UTF-8", packageParams, PropertyUtil.WX_API_KEY);
packageParams.put("sign", sign);
//格式化成XML形式,微信通信是借助XML
String requestXML = WxPayToolUtil.getRequestXml(packageParams);
//得到微信傳回的資料
String resXml = HttpUtil.postData(PropertyUtil.WX_ORDERQUERY_URL, requestXML);
return resXml;
}
- 輔助工具類utils的代碼實作:
/**
* @desc 微信支付相關的工具類
**/
public class WxPayToolUtil implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(WxPayToolUtil.class);
/**
* 是否簽名正确,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
* @return boolean
*/
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.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(!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
//算出摘要
String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
return tenpaySign.equals(mysign);
}
/**
* @Description:sign簽名
* @param characterEncoding 編碼格式
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
logger.info(sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* @Description:将請求參數轉換為xml格式的string
* @param parameters 請求參數
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.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 ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 取出一個指定長度大小的随機正整數.
*
* @param length int 設定所取出随機數的長度。length小于11
* @return int 傳回生成的随機數。
*/
public static int buildRandom(int length) {
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < length; i++) {
num = num * 10;
}
return (int) ((random * num));
}
/**
* 擷取目前時間 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
/**
* 目前時間多加多少毫米 yyyyMMddHHmmss
*
* @return String
*/
public static String getAddCurrTime(Integer s) {
Date now = new Date(System.currentTimeMillis() + s);
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String str = outFormat.format(now);
return str;
}
/**
* 擷取目前ip
* @param request
* @return
*/
public static String getRemoteHost(HttpServletRequest request){
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("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
}
/**
* 擷取微信支付參數
*/
public static SortedMap<Object,Object> getPayParams(String userId,String openid, String spbill_create_ip,String trade_type){
//擷取目前日期和時間
String currTime = getCurrTime();
//獲得目前時間
String strTime = currTime.substring(8, currTime.length());
//取出一個指定長度大小的随機正整數.
String strRandom = buildRandom(4) + "";
//随機字元串,長度要求在32位以内。
String nonce_str = strTime + strRandom;
//有序的Map
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
//公衆賬号ID
packageParams.put("appid", PropertyUtil.WX_MP_APP_ID);
//商戶号
packageParams.put("mch_id", PropertyUtil.WX_MCH_ID);
//使用者辨別
packageParams.put("openid", openid);
//随機字元串
packageParams.put("nonce_str", nonce_str);
//商品描述
packageParams.put("body", "VIP會員" );
//價格 機關為分
packageParams.put("total_fee",“1”);
//終端IP
packageParams.put("spbill_create_ip", spbill_create_ip);
//标價币種 CNY:人民币
packageParams.put("fee_type", "CNY");
//通知位址(回調位址)此處為/weixinNotify
packageParams.put("notify_url", PropertyUtil.WX_NOTIFY_URL);
//交易類型 JSAPI支付、NATIVE支付、APP支付
packageParams.put("trade_type", trade_type);
//簽名類型
packageParams.put("sign_type", PropertyUtil.WX_SIGN_TYPE);
//交易結束時間 time_expire (格式為yyyyMMddHHmmss,最短失效時間間隔必須大于5分鐘)
packageParams.put("time_expire", getAddCurrTime(120000));
//附加資訊
packageParams.put("attach", userId);
//生成簽名
String sign = createSign("UTF-8", packageParams, PropertyUtil.WX_API_KEY);
packageParams.put("sign", sign);
return packageParams;
}
}
MD5加密,微信支付的加密方式還有HMAC-SHA256;
/**
* MD5加密工具類
**/
public class MD5Util implements Serializable {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
}
else {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
}
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
http請求工具;
/**
* 微信http工具類
*/
public class WeChatUtil {
public static JSONObject doGetJson(String url) throws IOException {
JSONObject jsonObject = null;
DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpEntity != null){
String result = EntityUtils.toString(httpEntity,"UTF-8");
jsonObject = JSONObject.parseObject(result);
}
httpGet.releaseConnection();
return jsonObject;
}
}
/**
* http工具類,負責發起post請求并擷取的傳回
*/
public class HttpUtil implements Serializable{
//url連接配接時間
private final static int CONNECT_TIMEOUT = 5000;
//預設編碼
private final static String DEFAULT_ENCODING = "UTF-8";
/**
* 發起post請求微信支付并擷取的傳回
*
* @param urlStr 微信統一下單位址
* @param data 微信支付所提供的資訊
* @return 傳回微信傳回的資料
*/
public static String postData(String urlStr, String data){
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
//打開連接配接
URLConnection conn = url.openConnection();
//URL 連接配接可用于輸入和/或輸出。如果打算使用 URL 連接配接進行輸入,則将 DoInput 标志設定為 true;
// 如果不打算使用,則設定為 false。預設值為 true。
conn.setDoOutput(true);
//設定連接配接時間
conn.setConnectTimeout(CONNECT_TIMEOUT);
//設定讀取時間
conn.setReadTimeout(CONNECT_TIMEOUT);
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) {
e.getMessage();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
}
}
return null;
}
}
解析xml;
public class XMLUtil4jdom implements Serializable{
/**
* 解析xml,傳回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml資料。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
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 = XMLUtil4jdom.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(XMLUtil4jdom.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
-
前端vue代碼實作:
mtranster.html:從注冊頁面注冊登入成功後跳轉過來,判斷是否有code(因為是擷取code的回調位址),沒有便去請求,有則直接發起支付;
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="//unpkg.com/[email protected]/lib/theme-chalk/index.css" target="_blank" rel="external nofollow" />
<link rel="icon" href="static/images/common/icon.gif" target="_blank" rel="external nofollow" type="image/x-icon">
<title>微信支付</title>
</head>
<body style="zoom: 200%;">
<div id="common">
<div v-if="paying">
<h1>正在進行微信支付...</h1>
<small>請不要關閉</small>
</div>
<div v-if="!paying">
<h1>{{payStr}}</h1>
</div>
</div>
<script type="text/javascript" src="static/js/polyfill.min.js" charset="utf-8"></script>
<script type="text/javascript" src="static/js/vue.js"></script>
<script type="text/javascript" src="static/js/element-ui.js"></script>
<script type="text/javascript" src="static/js/jquery-2.min.js" charset="utf-8"></script>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" charset="utf-8"></script>
<script type="text/javascript" src="static/js/time.js"></script>
<script type="text/javascript" src="static/js/cookie.js"></script>
<script type="text/javascript" src="static/js/Api.js"></script>
<script>
var vm = new Vue({
el : '#common',
data :function() {
return{
codeUrl: "",
webChat : {
code : "",
state : ""
},
payurl:"",
loading:"",
paying:true,// 支付中
payStr:"" // 支付結束
}
},
methods : {
WXRegisterPay : function(code, state) {
var self = this;
self.loading = this.$loading({
lock: true,
text: loadingText,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
APIS.get(APIS.API_WXRegisterPay, {
"code" : code,
"state" : state
}).then(function(res) {
var pay = res.data.data;
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener){
document.addEventListener('WeixinJSBridgeReady', self.onBridgeReady(pay), false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', self.onBridgeReady(pay));
document.attachEvent('onWeixinJSBridgeReady', self.onBridgeReady(pay));
}
}else{
self.onBridgeReady(pay);
}
self.onBridgeReady(pay);
})
},
onBridgeReady:function (pay){
var self = this;
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":pay.appId, //公衆号名稱,由商戶傳入
"timeStamp":pay.timeStamp, //時間戳
"nonceStr":pay.nonceStr, //随機串
"package":pay.package,
"signType":pay.signType, //微信簽名方式
"paySign":pay.paySign //微信簽名
},function(res){
self.paying = false;
self.loading.close();
if(res.err_msg == "get_brand_wcpay_request:ok" ){
self.payStr = "支付成功!請到PC端檢視詳情。";
self.$confirm('支付成功!請到PC端檢視詳情。', '提示', {
confirmButtonText: '确定',
showClose:false,
showCancelButton:false,
closeOnClickModal:false,
type: 'success'
}).then(function(){
location.href = "payNotify.html?pay=ok";
})
}else{
if(res.err_msg == "get_brand_wcpay_request:fail"){
self.payStr = "支付失敗!"
var url = "payNotify.html?pay=fail"; // 支付失敗
}else{
self.payStr = "支付已取消!"
var url = "payNotify.html?pay=cancel"; // 取消支付
}
self.$confirm(JSON.stringify(res.err_desc), '提示', {
confirmButtonText: '确定',
showClose:false,
showCancelButton:false,
closeOnClickModal:false,
type: 'error'
}).then(function(){
location.href = url;
})
}
});
}
},
created : function() {},
mounted() {
var self = this;
var params = GetRequest();
if (null != params.code && undefined != params.code && '' != params.code) {
this.webChat.code = params.code;
this.webChat.state = params.state;
if (this.webChat.code !== "" && this.webChat.state !== "") {
// 擷取openid,直接跳轉支付
self.WXRegisterPay(this.webChat.code,this.webChat.state);
}
}else{
// 擷取code
APIS.get(APIS.API_getWXCode).then(function (res) {
self.codeUrl = res.data.data.code_url;
location.href = self.codeUrl;
})
}
}
})
</script>
</body>
</html>
微信jsapi支付的流程大概就是這樣,結合自己項目的業務邏輯實作。
以上便是我在做微信支付過程中的總結,若有描述不當的請評論提出哦!