iOS 微信支付,顯示一個確定按鈕的坑


這兩天在項目里面接入了微信支付,剛開始在項目里面引入了官方提供的sdk,這是下載地址https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1,填入了在微信開發平台申請的,應用的APP_ID APP_SECRET已經PARTNER_ID,自己生成一個訂單,發現可以跳轉到微信進行支付,但是和后台調試的時候,拿到后台給我的訂單信息,進入微信,發現只有一個確定按鈕,

IMG_1630.PNG

當時在網上搜了不少文章,但是沒有符合自己情況的,和后台一起按照步奏調試了好幾天,好在問題最后解決了,分享一下給遇到同樣問題的同學看看,也避免以后自己踩到同樣的坑,下面,我盡可能用通俗的語言,說一下我遇到問題的原因和解決方法。
3,首先看一下在客戶端,選擇一個要買的商品以后,點擊去微信支付,在沒有進入微信前經歷了什么
3 .1 下單 這里面呢需要提供三個參數,orderid是要買商品的id,ordertitle是在跳轉到微信支付里面,展示給用戶的訂單標題,orderprice是商品的價錢了,iOS里面對應下訂單的方法如下
     - ( NSMutableDictionary *)sendPay_demo:(NSString *)orderid title:(NSString *)ordertitle price:(NSString *)orderprice
{
//訂單標題,展示給用戶
NSString *order_name    = ordertitle;
//訂單金額,單位(分)
NSString *order_price   = orderprice;//1分錢測試
//================================
//預付單參數訂單設置
//================================
srand( (unsigned)time(0) );
NSString *noncestr  = [NSString stringWithFormat:@"%d", rand()];
NSMutableDictionary *packageParams = [NSMutableDictionary dictionary];
NSString *ii=@"079d9a74b95f41c58e636e344109ee1f";
[packageParams setObject: appid             forKey:@"appid"];       //開放平台appid
[packageParams setObject: mchid             forKey:@"mch_id"];      //商戶號
[packageParams setObject: @"APP-001"        forKey:@"device_info"]; //支付設備號或門店號
NSLog(@"第一次noncestr %@",noncestr);
[packageParams setObject: noncestr          forKey:@"nonce_str"];   //隨機串
[packageParams setObject: @"APP"            forKey:@"trade_type"];  //支付類型,固定為APP
[packageParams setObject: order_name        forKey:@"body"];        //訂單描述,展示給用戶
[packageParams setObject: NOTIFY_URL        forKey:@"notify_url"];  //支付結果異步通知
[packageParams setObject: orderid           forKey:@"out_trade_no"];//商戶訂單號
[packageParams setObject: @"196.168.1.1"    forKey:@"spbill_create_ip"];//發器支付的機器ip
[packageParams setObject: order_price       forKey:@"total_fee"];       //訂單金額,單位為分

//獲取prepayId(預支付交易會話標識)
NSString *prePayid;
prePayid            = [self sendPrepay:packageParams];
NSLog(@"獲取prepayId %@",prePayid);

if ( prePayid != nil) {
    //獲取到prepayid后進行第二次簽名
    
    NSString    *package, *time_stamp, *nonce_str;
    //設置支付參數
    time_t now;
    time(&now);
    time_stamp  = [NSString stringWithFormat:@"%ld", now];
    nonce_str	= [WXUtil md5:time_stamp];
    //重新按提交格式組包,微信客戶端暫只支持package=Sign=WXPay格式,須考慮升級后支持攜帶package具體參數的情況
    //package       = [NSString stringWithFormat:@"Sign=%@",package];
    package         = @"Sign=WXPay";
    //第二次簽名參數列表
    NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
    [signParams setObject: appid        forKey:@"appid"];
    NSLog(@"第二次noncestr %@ %@",nonce_str,time_stamp);
    [signParams setObject: nonce_str    forKey:@"noncestr"];
    [signParams setObject: package      forKey:@"package"];
    [signParams setObject: mchid        forKey:@"partnerid"];
    [signParams setObject: time_stamp   forKey:@"timestamp"];
    [signParams setObject: prePayid     forKey:@"prepayid"];
    //[signParams setObject: @"MD5"       forKey:@"signType"];
    //生成簽名
    NSString *sign  = [self createMd5Sign:signParams];
    //添加簽名
    [signParams setObject: sign         forKey:@"sign"];
    [debugInfo appendFormat:@"第二步簽名成功,sign=%@\n",sign];
    
    //返回參數列表
    return signParams;
    
}else{
    [debugInfo appendFormat:@"獲取prepayid失敗!\n"];
}
return nil;
 }
3.2這里面非常重要的一步是提交預支付獲取prepayid,我們的錯誤就出在這里,獲取prepayid的方法如下
    //提交預支付
   -(NSString *)sendPrepay:(NSMutableDictionary *)prePayParams
 {
NSString *prepayid = nil;
NSLog(@"提交預支付發送的數據%@",prePayParams);
//獲取提交支付
NSString *send      = [self genPackage:prePayParams];
NSLog(@"獲取提交支付 send %@",send);
//輸出Debug Info
[debugInfo appendFormat:@"API鏈接:%@\n", payUrl];
[debugInfo appendFormat:@"發送的xml:%@\n", send];
//發送請求post xml數據
NSData *res = [WXUtil httpSend:payUrl method:@"POST" data:send];
//輸出Debug Info
[debugInfo appendFormat:@"服務器返回:\n%@\n\n",[[NSString alloc] initWithData:res encoding:NSUTF8StringEncoding]];
XMLHelper *xml  = [[XMLHelper alloc] init];
//開始解析
[xml startParse:res];
NSMutableDictionary *resParams = [xml getDict];
NSString *return_code   = [resParams objectForKey:@"return_code"];
NSString *result_code   = [resParams objectForKey:@"result_code"];
if ( [return_code isEqualToString:@"SUCCESS"] )
{
    //生成返回數據的簽名
    NSString *sign      = [self createMd5Sign:resParams ];
    NSString *send_sign =[resParams objectForKey:@"sign"] ;
    //驗證簽名正確性
    if( [sign isEqualToString:send_sign]){
        if( [result_code isEqualToString:@"SUCCESS"]) {
            //驗證業務處理狀態
            prepayid    = [resParams objectForKey:@"prepay_id"];
            return_code = 0;
            [debugInfo appendFormat:@"獲取預支付交易標示成功!\n"];
        }
    }else{
        last_errcode = 1;
        [debugInfo appendFormat:@"gen_sign=%@\n   _sign=%@\n",sign,send_sign];
        [debugInfo appendFormat:@"服務器返回簽名驗證錯誤!!!\n"];
    }
}else{
    last_errcode = 2;
    [debugInfo appendFormat:@"接口返回錯誤!!!\n"];
}
return prepayid;
 }
3.3 這個方法進行了第一次MD5的加密,
 //獲取package帶參數的簽名包
-(NSString *)genPackage:(NSMutableDictionary*)packageParams
{
NSString *sign;
NSMutableString *reqPars=[NSMutableString string];
//生成簽名
sign        = [self createMd5Sign:packageParams];
//生成xml的package
NSArray *keys = [packageParams allKeys];
[reqPars appendString:@"<xml>\n"];
for (NSString *categoryId in keys) {
    [reqPars appendFormat:@"<%@>%@</%@>\n", categoryId, [packageParams objectForKey:categoryId],categoryId];
}
[reqPars appendFormat:@"<sign>%@</sign>\n</xml>", sign];

return [NSString stringWithString:reqPars];
}
就是通過上面的這個方法,獲得了向微信發送的生成訂單xml的內容,我們后台剛開始的錯誤是生成sign簽名的時候,他沒有找到系統提供給后台的生成簽名的方法,就自己寫了一個方法進行簽名,然后他把所有的參數,包括key進行ASCII碼從小到大排序,事實上應該把除key以外的參數進行排序,且參數名要區分大小寫,排序后,在得到的字符串后面在追加key.
在微信簽名的文檔規則在這里https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3,
iOS里面簽名的方法是下面這個
-(NSString*) createMd5Sign:(NSMutableDictionary*)dict
{
NSMutableString *contentString  =[NSMutableString string];
NSArray *keys = [dict allKeys];
//按字母順序排序
NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryId in sortedArray) {
    if (   ![[dict objectForKey:categoryId] isEqualToString:@""]
        && ![categoryId isEqualToString:@"sign"]
        && ![categoryId isEqualToString:@"key"]
        )
    {
        [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]];
    }
    }
//添加key字段
spkey=PARTNER_ID;
[contentString appendFormat:@"key=%@", spkey];
NSLog(@"數據排序前 %@ 數據排序后sortedArray %@ 先把參數排序后追加一個key后得到 %@",keys,sortedArray,contentString);
NSLog(@"添加key字段 %@",spkey);
//得到MD5 sign簽名
NSString *md5Sign =[WXUtil md5:contentString]; 
//輸出Debug Info
[debugInfo appendFormat:@"MD5簽名字符串:\n%@\n\n",contentString];
return md5Sign;
}
3.4在獲得prepayid以后,還要在進行第二次簽名 ios里面對應的方法是這個
if ( prePayid != nil) {
    //獲取到prepayid后進行第二次簽名
    
    NSString    *package, *time_stamp, *nonce_str;
    //設置支付參數
    time_t now;
    time(&now);
    time_stamp  = [NSString stringWithFormat:@"%ld", now];
    nonce_str	= [WXUtil md5:time_stamp];
    //重新按提交格式組包,微信客戶端暫只支持package=Sign=WXPay格式,須考慮升級后支持攜帶package具體參數的情況
    //package       = [NSString stringWithFormat:@"Sign=%@",package];
    package         = @"Sign=WXPay";
    //第二次簽名參數列表
    NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
    [signParams setObject: appid        forKey:@"appid"];
    NSLog(@"第二次noncestr %@ %@",nonce_str,time_stamp);
    [signParams setObject: nonce_str    forKey:@"noncestr"];
    [signParams setObject: package      forKey:@"package"];
    [signParams setObject: mchid        forKey:@"partnerid"];
    [signParams setObject: time_stamp   forKey:@"timestamp"];
    [signParams setObject: prePayid     forKey:@"prepayid"];
    //[signParams setObject: @"MD5"       forKey:@"signType"];
    //生成簽名
    NSString *sign  = [self createMd5Sign:signParams];
    //添加簽名
    [signParams setObject: sign         forKey:@"sign"];
    [debugInfo appendFormat:@"第二步簽名成功,sign=%@\n",sign];
    //返回參數列表
    return signParams;
}
3.5 這時候就生成了跳轉到微信所需要的signParams ,然后在iOS客戶端里面調用下面的方法就可以發起支付了
     NSMutableString *stamp  = [dict objectForKey:@"timestamp"];
    
    //調起微信支付
    PayReq* req             = [[PayReq alloc] init];
    req.openID              = [dict objectForKey:@"appid"];
    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"];
    BOOL status = [WXApi sendReq:req];
總結一下,我開始遇到跳轉微信只有一個確定按鈕,就是因為服務器那邊在進行簽名的時候,出錯了,微信支付流程就服務器在下訂單前要進行一次簽名,拿到prepayid以后要再進行一次簽名,不知道大家遇到的問題是否和我一樣,如果不一樣,你剛好也解決了,歡迎補充。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM