1. 程式人生 > >iOS整合微信支付的坑,讓人摸不透的sign

iOS整合微信支付的坑,讓人摸不透的sign

整合iOS微信支付時遇到一個坑,記錄一下。

由於公司業務需求,需要在現有App中新增微信支付功能。於是就開始整合微信的支付功能,先看了一遍官方的文件。然後當然是看官方提供的Demo工程了,於是就下了官方提供的示例工程:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=11_1下的https://pay.weixin.qq.com/wiki/doc/api/download/SDKSample_ios9_v2.0.2_V3_pay.zip)。下載下來,看了一下,果然簡單,發現,支付最終是呼叫了WXApiRequestHandler中的這個方法

+ (NSString *)jumpToBizPay {

    //============================================================
    // V3&V4支付流程實現
    // 注意:引數配置請檢視伺服器端Demo
    // 更新時間:2015年11月20日
    //============================================================
    NSString *urlString   = @"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios";
        //解析服務端返回json資料
        NSError *error;
        //載入一個NSURL物件
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
        //將請求的url資料放到NSData物件中
        NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
        if ( response != nil) {
            NSMutableDictionary *dict = NULL;
            //IOS5自帶解析類NSJSONSerialization從response中解析出資料放到字典中
            dict = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
            
            NSLog(@"url:%@",urlString);
            if(dict != nil){
                NSMutableString *retcode = [dict objectForKey:@"retcode"];
                if (retcode.intValue == 0){
                    NSMutableString *stamp  = [dict objectForKey:@"timestamp"];
                    
                    //調起微信支付
                    PayReq* req             = [[[PayReq alloc] init]autorelease];
                    req.partnerId           = [dict objectForKey:@"partnerid"];
                    req.prepayId            = [dict objectForKey:@"prepayid"];
                    req.nonceStr            = [dict objectForKey:@"noncestr"];
                    req.timeStamp           = stamp.intValue;
                    req.package             = [dict objectForKey:@"package"];
                    req.sign                = [dict objectForKey:@"sign"];
                    [WXApi sendReq:req];
                    //日誌輸出
                    NSLog(@"appid=%@\npartid=%@\nprepayid=%@\nnoncestr=%@\ntimestamp=%ld\npackage=%@\nsign=%@",[dict objectForKey:@"appid"],req.partnerId,req.prepayId,req.nonceStr,(long)req.timeStamp,req.package,req.sign );
                    return @"";
                }else{
                    return [dict objectForKey:@"retmsg"];
                }
            }else{
                return @"伺服器返回錯誤,未獲取到json物件";
            }
        }else{
            return @"伺服器返回錯誤";
        }
}
最終也就是把伺服器傳過來的幾個引數賦值,呼叫微信SDK介面就完事了。

於是就立刻跟伺服器端同學按照demo的形式開發介面,除錯。開始一切都挺順利,呼叫伺服器端下單介面,伺服器下單,將結果訂單號等引數返給客戶端,都沒問題,可是,最後一步,問題來了。而且是很詭異的問題:能調起微信App,但是,,在微信中卻沒有出現期待中的支付頁面,而只是空白的頁面中間有一個確定按鈕,其他的啥都沒有。點選該按鈕,就會調回自己的App,返回錯誤碼-2(使用者取消的意思)。

相信很多同學也都遇到過類似的問題,可是我是頭一次遇到啊。研究了伺服器返回的各欄位,該有的都有啊,包括sign欄位(這個就是微信的坑所在,坑了我好長時間。。。),而且跟Demo中的測試資料格式都一樣啊,沒問題啊。於是,想盡各種辦法,驗證是不是少配置了什麼東西,是不是哪裡引數型別不對,等等。結果都一一排除,百思不得其解。沒辦法,去網上搜吧,肯定有人遇到過同樣的問題。果然,有同學出現過類似問題,說問題所在是sign值生成的有問題。也就是sign生成的不對唄,可是我這裡sign是伺服器傳過來的,於是就逼著伺服器端的同學各種檢查嘛。包括檢查sign值生成演算法,中間經歷各種曲折,嘗試。

最後,終於發現問題所在。不得不吐槽一下,真他媽坑人!!!

問題是這樣的:開始的時候,伺服器端的同學生成這個sign是用了好多欄位,說白了就是他把下單的所有欄位都參與了簽名;而客戶端所需的簽名欄位只有appId,partnerId,prepayId,nonceStr,timeStamp,package這些欄位(注意這些欄位名的大小寫,這他媽簡直就是坑中坑!!!!我哪來的這些欄位名?官方文件上說的:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3   步奏3是不是這麼寫的?)。好麼,發現問題了,讓伺服器端同學改唄,應該沒有問題了。可是,還是不對!!!這時候我已經快瘋了,把微信支付SDK的開發者問候了個遍,你們能體會我當時的心情嗎,這點程式碼,不到100行,弄了兩天了。

發洩過後,平靜下來,生活還是要繼續(哈哈,有點裝了,就是活還是得幹啊)。於是又再次拿出官方的Demo研究,這次果然有了新收穫。列印了一下Demo中下單介面返回的資料,跟我們服務的返回結果仔細對比一下,終於發現了問題所在啊。Demo中介面(http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios)返回的資料是這樣的

 {
    appid = wxb4ba3c02aa476ea1;
    noncestr = b56a56818039abe71c0d287ea67b19ce;
    package = "Sign=WXPay";
    partnerid = 10000100;
    prepayid = wx20151209170117f46f5ed25f0130700650;
    sign = 09F74D7C3F1BF3C0C329ED3C3EC2AC17;
    timestamp = 1449651677;
}
而我們的是這樣的:
{
                appId = wx13******4cde9472;
                nonceStr = 1005d93c01faf80cbc2225c87314c033;
                packageValue = "Sign=WXPay";
                partnerId = 12******01;
                prepayId = wx20151209132826120c234c850066732593;
                sign = F83687699E7293828FE098F6DA2CA1CF;
                timeStamp = 1449638906;
            };

(忽視那些*,為了安全,我後續故意處理的)

發現沒有,引數名大小寫!引數名大小寫!引數名大小寫! 這XX就是關鍵所在啊!!!    於是後面的事大家應該都能想到了:讓伺服器同學全用小寫的引數名,重新生成簽名;伺服器介面更新,客戶端訪問,解析,調起微信客戶端,微信彈出支付頁面,支付,完成。

事件就這麼過去了,但是經歷了這些坑,我不得不發出一些感慨:原來微信有些時候也是靠不住的;在各種抱怨微信SDK文件寫的不專業,Demo給的不靠譜後,靜下心來想想,發現其實有些時候自己還是太浮躁了,還是不夠細心。於是決定寫下這篇日誌,以幫助其他同樣被坑的同學,也提醒自己,記下曾摔的跟頭。 程式設計路漫漫,吾將上下而求索,騷年繼續努力吧!