剛剛參加實習,很多東西都不懂,微信支付是最近才接觸到的,開始去看官方文檔的時候,一大堆文字東西說明看的有點蒙圈,好像很難的樣子,但是隨著慢慢的熟悉,微信支付其實也就那樣(事實上我卻做了兩天
,渣渣...),然后這個做了之后我覺得有必要把自己遇到的坑給記一下,再次做到微信支付的開發任務的時候便能夠游刃有余了。
微信支付交互過程(這里我照抄了官方文檔,原文在這里):
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
步驟2:商戶后臺收到用戶支付單,調用微信支付統一下單接口。參見【統一下單API】。
步驟3:統一下單接口返回正常的PRepay_id,再按簽名規范重新生成簽名后,將數據傳輸給APP。參與簽名的字段名為appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式為Sign=WXPay
步驟4:商戶APP調起微信支付。api參見本章節【app端開發步驟說明】
步驟5:商戶后臺接收支付通知。api參見【支付結果通知API】
步驟6:商戶后臺查詢支付結果。,api參見【查詢訂單API】
我踩的大坑
第一坑:調起微信支付接口沒有反應,但是boolean result = api.sendReq(req);卻返回了true
原因:在AndroidManifest.xml文件中沒有注冊WXPayEntryActivity,因此我們只要在將其在AndroidManifest.xml文件進行注冊就可以了,如下示例
<activity android:name=".wxapi.WXPayEntryActivity" android:exported="true" android:launchMode="singleTop"/>第二坑:從服務器后臺成功獲取了預支付單號prepayid之后,成功調起微信支付接口之后,resp.errCode 的值總是為 -1。
查了微信支付的開發文檔之后,返回-1的原因可能是:簽名錯誤、未注冊APPID、項目設置APPID不正確、注冊的APPID與設置的不匹配、其他異常等。對于這樣的說法真的是很懵逼,完全摸不著頭腦,不過經過反復的才坑,我總結出了以下兩個主要原因(網上有人說是微信緩存啊,appid阿或者要重新設置商戶的key這些的,我試過了,不是........):
原因1:調起微信支付的參數簽名sign不正確,對于這個很多人是沒有仔細的看微信支付的開發文檔的,像我就是其中的一個(瞎了),在開發文檔中調起微信支付接口的關鍵代碼是,在這里需要注意的有packageValue和timeStamp,packageValue的值只能填"Sign=WXPay",時間戳的單位是秒(固定10數),在Android中一般獲取的系統當前時間是以毫秒為單位的,所以我們需要做下單位換算。
IWXAPI api;PayReq request = new PayReq();request.appId = "wxd930ea5d5a258f4f";request.partnerId = "1900000109";request.prepayId= "1101000000140415649af9fc314aa427",;request.packageValue = "Sign=WXPay";request.nonceStr= "1101000000140429eb40476f8896f4c9";request.timeStamp= "1398746574";request.sign= "7FFECB600D7157C5AA49810D2D8F28BC2811827B";api.sendReq(req);關于sign這個是什么呢?不清楚的看這里(戳戳戳),然后sign生成字段名列表是?戳(反正我是掉這坑了......),哦,對了,還有一個,怎么驗證我們生成的sign是否正確的呢,其實很簡單,微信已經給我們提供了一個在線驗證sign是否正確的校驗工具,啦在這里(校驗工具),就是下面這張圖
(1)生成sign方法
/** * 生成簽名 * * @param characterEncoding * @param parameters * @return */ private static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有參與傳參的參數按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + ConstantOther.KEY);//這個key是商戶key String sign = md5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); return sign; }MD5簽名(這個在微信的官方demo中有):
package com.sans.taxigo.utils;import java.security.MessageDigest;public class MD5 { private MD5() {} /** * 微信自帶的MD5加密算法 * @param buffer * @return */ public final static String getMessageDigest(byte[] buffer) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(buffer); byte[] md = mdTemp.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { return null; } }}(2)調起微信支付接口
String prepayId = response.getString("prepayId"); IWXAPI api = WXAPIFactory.createWXAPI(context, null); PayReq req = new PayReq(); req.appId = ConstantOther.APP_ID; req.partnerId = ConstantOther.MCH_ID; req.prepayId = prepayId; req.nonceStr = getNonceStr(); req.timeStamp = getTimeStamp(); req.packageValue = ConstantOther.PACKAGE_VALUE; SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", req.appId); parameters.put("partnerid", req.partnerId); parameters.put("noncestr", req.nonceStr); parameters.put("prepayid", req.prepayId); parameters.put("package", req.packageValue); parameters.put("timestamp", req.timeStamp); //生成簽名 req.sign = createSign("UTF-8", parameters);//簽名; // 將該app注冊到微信 api.registerApp(ConstantOther.APP_ID); boolean result = api.sendReq(req);原因2:應用的包簽名不正確。這個坑踩的有點大,一開始在做微信支付的時候并沒有發現,等到排查了其他的原因之后試著去驗證包簽名的正確性,結果問題真的就出在了這里,我們包的簽名是在測試的時候和正式發布的時候是不一樣的,而我一開始在測試的時候使用的包簽名是正式發布后的包簽名,這樣在測試的時候自然不能夠成功的調起微信支付接口,不知道包簽名生成就看這里(這里),微信支付開發文檔里寫的很清楚,在調試的手機上安裝一個簽名工具后運行輸入包名便可生成調試的包簽名。生成的簽名是填在下圖的(圖來自微信支付開發文檔):
其實,對于我踩到的坑完全是可以避免的,由于我本身的經驗不足以及沒有很仔細的閱讀官方文檔才會導致我不斷往坑里去,所以呢,抱著認真負責的態度去做事是可以少走很多彎路的(看文檔要仔細、看文檔要仔細、看文檔要仔細)
下面是我的在實際項目中用到的代碼,雖然代碼不多,但其中肯定是有很多不足的,在這里我發出來給像我這樣的新手借鑒一下,也希望有人可以指出其中的不足,好讓我改正,謝謝........
package com.sans.taxigo.utils;/** * Created by chenjianrun on 2017/1/21. */import android.content.Context;import android.net.wifi.WifiInfo;import android.net.wifi.WifiManager;import android.text.format.Formatter;import com.loopj.android.http.AsyncHttpResponseHandler;import com.loopj.android.http.RequestParams;import com.sans.taxigo.common.ConstantHttp;import com.sans.taxigo.common.ConstantOther;import com.sans.taxigo.http.MyHttpClient;import com.tencent.mm.sdk.modelpay.PayReq;import com.tencent.mm.sdk.openapi.IWXAPI;import com.tencent.mm.sdk.openapi.WXAPIFactory;import org.json.JSONException;import org.json.JSONObject;import java.util.Iterator;import java.util.Map;import java.util.Random;import java.util.Set;import java.util.SortedMap;import java.util.TreeMap;import cz.msebera.android.httpclient.Header;public class WeiXinPayUtil { public static void sendToWinXinPay(Context context) { // makePost(context); askServerToPay(context,0.01f); } /** * 向服務器后臺發起下單請求成功后調起微信支付接口 * @param context * @param totalFee */ private static void askServerToPay(final Context context, float totalFee){ String time = String.valueOf(System.currentTimeMillis()); RequestParams params = new RequestParams(); params.put("spbill_create_ip",getLocalIPAddress(context)); params.put("total_fee",totalFee); params.put("userId", SharePreferenceHelper.getInt("ID",-1)); params.put("apiSendTime",time); params.put("apiToken", ValidateAPITokenUtil.ctreatTokenStringByTimeString(time)); MyHttpClient.postNew(ConstantHttp.WEIXIN_URL, params, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { try { String tem= new String(responseBody); JSONObject response = new JSONObject(tem); String prepayId = response.getString("prepayId"); IWXAPI api = WXAPIFactory.createWXAPI(context, null); PayReq req = new PayReq(); req.appId = ConstantOther.APP_ID; req.partnerId = ConstantOther.MCH_ID; req.prepayId = prepayId; req.nonceStr = getNonceStr(); req.timeStamp = getTimeStamp(); req.packageValue = ConstantOther.PACKAGE_VALUE; SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", req.appId); parameters.put("partnerid", req.partnerId); parameters.put("noncestr", req.nonceStr); parameters.put("prepayid", req.prepayId); parameters.put("package", req.packageValue); parameters.put("timestamp", req.timeStamp); //生成簽名 req.sign = createSign("UTF-8", parameters);//簽名; // 將該app注冊到微信 api.registerApp(ConstantOther.APP_ID); boolean result = api.sendReq(req); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { ShowToast.Long("test"); } @Override public void onFinish() { super.onFinish(); } @Override public void onUserException(Throwable error) { super.onUserException(error); } }); } /** * 生成隨機字符串 * * @return */ private static String getNonceStr() { int length = 16; String base = "abcdefghijklmnopqrstuvwxyz0123456789"; int len = base.length(); Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(len); sb.append(base.charAt(number)); } return sb.toString(); } /** * 生成時間戳字符串 * 標準北京時間,時區為東八區,自1970年1月1日 0點0分0秒以來的秒數 * * @return */ private static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000L); } /** * 獲取本地ip地址 * * @param context * @return */ private static String getLocalIPAddress(Context context) { WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); String ipAddress = Formatter.formatIpAddress(wifiInfo.getIpAddress()); //String ipAddress = FormatIP(wifiInfo.getIpAddress()); return ipAddress; } /** * 生成簽名 * * @param characterEncoding * @param parameters * @return */ private static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有參與傳參的參數按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + ConstantOther.KEY); String sign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); //String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; }}
新聞熱點
疑難解答