談談那些年微信支付踩過的坑
很早的時候就想寫這篇文章了,作為BAT中的一員,還真不想吐槽它,免得被人身攻擊。有人說,微信支付很簡單嘛,官網有例子,網上也有現成的例子,不過誰用誰知道,本人也是在深入瞭解之後,真心覺得微信支付裡的坑太多,BAT的開發們太敷衍了事,結果給不少的其他開發者帶來諸多麻煩。我在這裡做個稍全一點的介紹,儘量減少其他同學們掉坑裡的概率。
在微信上建立你的應用
這裡特別強調一下,這一步很重要,不然微信支付整合除錯會出現莫名的錯誤。
1,在註冊之前對於Android客戶端,需要提供app應用的包名和應用簽名(md5值),這兩個東西問開發或產品同學要。儘量在註冊前提供。另外還需準備一個28x28和108x108的logo圖片,問設計或產品同學要。
2,在應用包名
和應用簽名
,儘量一次性填對了。應用建立成功後,將得到appid
(以wx開頭的一串數字)。然後再去申請微信支付(需做開發者認證並繳費)
3,在[微信支付商戶平臺]註冊或登入,申請app支付,申請通過後,將得到商戶idmch_id
(一串10位數字),然後在賬戶設定–>API安全–>金鑰設定中設定API金鑰key
(32位的字串)
注:關於微信支付申請,請在開放平臺申請(同公眾號支付申請流程,公眾號支付不在本文討論範圍之內)微信的工作人員稽核通過後,會發稽核通過的郵件。裡面包含微信支付商戶號mch_id
(一串10位數字)和APPID等資訊。然後根據郵件提示或者直接在微信商戶平臺中–>賬戶設定–>API安全–>金鑰設定中下載api證書並設定API金鑰key
(32位的字串)
在微信開放平臺建立應用成功後,APP支付也申請通過了。請提供給開發同學以下東西:
- APPID
appid
(以wx開頭的一串數字) - 商戶id
mch_id
(一串10位數字) - API金鑰
key
對於Android應用,還需要保證應用包名與應用簽名正確
只要上面的資訊正確無誤,下面就交給開發同學了。如果是直接使用微信支付sdk的同學,請準備好踩坑吧。
支付SDK和demo
微信支付SDK
SDK從4.0.2開始,已改為使用gradle方式。
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
}
- 1
- 2
- 3
以前是直接使用libammsdk.jar,相比起來,算是進步了。
demo
我建議直接忽略官方的demo,不信我的可以直接去Android資源下載下載相關demo
以下是我踩過的坑:
- 支付Demo在另外一個單獨的project中,儘管sdk demo與pay demo非常類似,搞不懂為啥不合為一個demo project。我昨天在微信網站下載的支付demo,今天在微信網站上就找不著了。昨天下載的支付demo,看版本是v3(sdk demo已經是5.0.2了),還是eclipse工程,不過匯入到eclipse之後,編譯不通過。sdk中原來的com.tencent.mm.sdk包換成了com.tencent.mm.opensdk,demo,導致src程式碼一片紅。
- 支付Demo中的服務端下單介面地址(http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=android) 不能訪問,就算修復了編譯錯誤,結果仍然執行不了。
然後我看了下其它的文件,sdk換了,但是相關的文件並沒有更新,尤其是混淆配置,直接影響到聯調,這也是一個隱藏的坑。
統一下單
統一下單是商戶系統(客戶端或服務端)向微信支付後臺傳送請求,以拿到預支付交易會話標識(prepayid
)等資訊。關於介面的請求與響應資訊,請參考統一下單介面描述。文件寫得還是蠻全的,不過,實際上如何,我就只能呵呵了。請看下面
- 介面的請求與響應都是xml格式,xml的解析不太方便,微信僅支援這個格式。算是小坑。
- 介面文件中定義了一個
NOT_UTF8
的錯誤碼,似乎微信要求的請求編碼為utf-8,不過使用官方demo的Util.httpPost時,如果引數中有中文,還需將請求的xml轉為ISO-8859-1編碼的字串才行。不然,微信直接返回給你一個空字串,保證讓你找不著北。 - 介面文件中定義了一個
LACK_PARAMS
的錯誤碼,指的是如果必填的引數為空,會返回此錯誤碼,可事實上呢,有一次我把total_fee引數搞混了,寫成了支付寶的引數名,結果介面返回一個空串,結果讓我一頓好找。 - Android從6.0開始,刪除了apache的http元件,於是乎我把Util中的apache http元件換成了HttpUrlConnection,關於請求頭,解析都和原來保持一致,結果返回個簽名錯誤。要不是我換http元件之前可以成功下單,我幾乎就信了它,真的去查簽名了(事實上根本不是簽名錯誤好吧)。後來我修改http的請求頭,嘗試各種請求方式與編碼,結果還是返回簽名錯誤,這讓我很是折騰,一度曾想改回apache的http client。最後,我把請求的xml轉為utf-8編碼,然後HttpUrlConnection全都預設設定,這才下單成功。(PS,這裡順帶講一個HttpUrlConnection的坑,設定請求方法為post,可是debug中看到的HttpUrlConnection物件,請求方法仍為get,當初還以為是這個導致的,後來才知道HttpUrlConnection真正的實現是在delegate中)
總結一下,統一下單的響應、錯誤碼和錯誤描述比較混亂,成功響應還好,一旦出現問題,準讓你摸不清東南西北。
簽名這塊,請參考簽名演算法,注意按字母升序組織請求引數。我用的是有序的TreeMap儲存引數並做簽名。沒掉坑裡。這裡附上微信提供的簽名檢驗工具
呼叫支付介面
這一步是客戶端拿到prepayid
之後,向微信傳送PayReq。請求引數有7個(詳細參考微信sdk中的PayReq類,其中partnerId
填商戶IDmch_id
;packageValue
固定填Sign=WXPay
;timestamp
為北京時間戳,單位為秒。其它的顧名思義,對號入座即可),缺一不可。這裡呢,也有坑
- PayResp(響應)定義了errCode和errStr,只要支付失敗,errCode都為-1,errStr永遠為null,所以,具體的錯誤原因:簽名錯誤、未註冊APPID、專案設定APPID不正確、註冊的APPID與設定的不匹配、其他異常等,一個一個查吧。
- 統一下單介面不返回timestamp,如果是我們自己設定一個,那麼需要對請求重新簽名,統一下單返回的sign就不能用了。不然呼叫支付介面肯定返回-1。
- 如果介面呼叫失敗,返回-1,是應用簽名(apk的簽名)不正確(比如debug和release版本使用的不同簽名)導致的,那麼需要清空微信快取,才能支付成功。不過,我可不敢隨便清微信快取,我建議可以將手機重啟,如果仍然不行,再清微信的快取。
libammsdk.jar衝突
因微信支付和分享是在同一個jar中,所以如果使用了第三方的分享sdk,極有可能會出現jar衝突。即使是相同模組中的jar完全一致也不行。解決的辦法是隻保留一個模組中的libammsdk.jar然後clen build。
建議
- apk的包名,簽名在工程初始化的時候就確定好,debug和release使用同一個簽名。
- 微信開放平臺在建立應用前,確認好apk包名和簽名。
- 如果是服務端下單,確保sign值是對的(需要再次簽名的再籤一次)。客戶端拿到七個引數後,可以直接支付。
- 建議在服務端下單,這樣更安全。
- 使用第三方的支付sdk,避免入坑。
af-pay
最後推薦一個Android上的支付sdk:af-pay,github地址為:https://github.com/Jamling/af-pay
af-pay是一個Android平臺上支付庫,支援支付寶和微信,使用af-pay可以讓支付變得更簡單,並且支援服務端與服務單下單。示例程式碼如下:
private void doWxpay(String orderInfo) {
final Activity activity = this;
// 獲取支付類
Wxpay wxpay = Wxpay.getInstance(activity);
// 設定支付回撥監聽
wxpay.setPayListener(new Wxpay.PayListener() {
@Override
public void onPaySuccess(BaseResp resp) {
showToast(activity, "支付成功");
}
@Override
public void onPayCanceled(BaseResp resp) {
showToast(activity, "支付取消");
}
@Override
public void onPayFailure(BaseResp resp) {
showToast(activity, "支付失敗");
}
});
// 這裡是服務端下單,內容是統一下單返回的xml
if (!TextUtils.isEmpty(orderInfo)) {
PayReq req = OrderInfoUtil.getPayReq(orderInfo);
wxpay.pay(req);
}
else { // 客戶端下單
Wxpay.DEBUG = true; // 開啟日誌
// API金鑰,在微信商戶平臺設定
Wxpay.Config.api_key = "32位的字串";
// APPID,在微信開放平臺建立應用後生成
Wxpay.Config.app_id = "wx...";
// 商戶ID,註冊商戶平臺後生成
Wxpay.Config.mch_id = "14...";
// 支付結果非同步通知介面,由後臺開發提供
Wxpay.Config.notify_url = "http://www.ieclipse.cn/app/pay/wxpay_notify.do";
// 建立統一下單非同步任務
Wxpay.DefaultOrderTask task = new Wxpay.DefaultOrderTask(wxpay);
// 這個商戶訂單號,由後臺返回,在這裡隨便生成一個
String outTradeNo = OrderInfoUtil2_0.genOutTradeNo();
// 設定統一下單的請求引數
task.setParams(OrderInfoUtil.buildOrderParamMap(outTradeNo, "測試支付", "", "1", null, null, null));
task.execute();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
如果是服務端下單,客戶端只需生成PayReq物件,然後呼叫wxpay.pay(PayReq)
即可。af-pay已經配置好回撥的WXPayActivity和Receiver。詳細資訊請訪問Github。
關於
QuickAF是一個Android平臺上的app快速開發框架,歡迎讀者在github star或fork。本人寫作水平有限,歡迎廣大讀者指正,如有問題,可與我直接聯絡或在我的官方部落格中給出評論。
參考
QuickAF: https://github.com/Jamling/QuickAF
微信支付開發文件:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
本文永久連結: http://www.ieclipse.cn/2017/05/24/Android/quickaf-wechat-pay/ 未經允許,禁止轉載,如有問題,請在我的部落格原始頁面提交評論。