前言:
支付,分享,第三方登錄,即時通訊這些在現在的APP當中慢慢的變成了最基本,當這些技術已經慢慢的融入到人們的生活中的時候,這些也就成了APP最基本的需求,以前這部分的內容也是經常做,但就是沒有好好的總結過,首先說說這幾個點,支付的話我們就說說支付寶和微信吧,把我看到的收藏的好點的文章也全都分享出來,第三方登錄和分享我用的一直都是友盟的,不為別的,只是友盟的統計和第三方登錄分享這些都是一套SDK,就直接用友盟,友盟和微信的回調這里也有一些我們注意的,下面也會和大家說。
微信和支付寶:
其實這些支付麻煩的地方就是這些簽名的操作,你要是把這些放在前端App的話的確是不安全的,所以這些最好都放在服務端,這些放在服務端其實你所要寫的代碼,估計也就十多行了!就像下面一樣,下面方法是一個完整的微信請求后台操作,里面的參數你可以不用管,你看一下這個過程,你把后台需要的參數傳給后台,你取到后台返回給你的信息,你再把這些信息給微信就OK,可我想說的並不是建議你粘貼這十多行代碼就往事大吉了,想說的是希望把這整個過程和當中我們需要注意的一些地方給大家說說;
注意:這里說一點,如果你APP的微信支付功能審核的時候,你是先在微信平台添加了功能再提交申請的,可審核沒通過,后來糾結幾次才過了的。你在APP當中使用支付功能的時候,先在微信平台APP 的移動支付功能先刪除再添加一次!不然它永遠都是用不了!
// 調用微信支付 [MBPSecondary showMBProgressHUDWithText:@"請稍等..." ToView:self.view]; [WXPayViewModel RequestWXpayWithBody:@"明星私募網微信支付" andJsonString:bodyString andTotalfee:priceString withRequestSuccess:^(NSDictionary *payDictionary) { [MBPSecondary hideMBProgressHUDWith:self.view]; NSMutableString * stamp = [payDictionary objectForKey:@"timestamp"]; //調起微信支付 PayReq* request = [[PayReq alloc] init]; request.openID = [payDictionary objectForKey:@"appid"]; request.partnerId = [payDictionary objectForKey:@"mch_id"]; request.prepayId = [payDictionary objectForKey:@"prepay_id"]; request.nonceStr = [payDictionary objectForKey:@"nonce_str"]; request.timeStamp = stamp.intValue; request.package = @"Sign=WXPay"; // 這個參數可以直接寫死 request.sign = [payDictionary objectForKey:@"sign"]; //微信支付 [WXApi sendReq:request]; } andRequestFail:^(NSString *message) { [MBPSecondary hideMBProgressHUDWith:self.view]; [MBPSecondary showMBProgressHUDWithText:message andType:FAILED]; }];
我們先看看微信的整個支付的流程圖:
這里需要注意的幾個點:
一:簽名過程不要放在前端
二:返回結果最好去服務端驗證,你要回調之后直接使用。
下面是我們在本地測試微信支付的時候的源碼,這個過程就是把服務端做的事放在了我們移動前端,你要想試試我們自己在本地做微信支付,不經過服務端的話里面有些文件比如XML格式數據轉字典,MD5加密這些文件你要需要的可以加我Q我發給你!。
#pragma mark - 微信支付相關測試方法 - (void)weixinChooseAct { NSString *appid,*mch_id,*nonce_str,*sign,*body,*out_trade_no,*total_fee,*spbill_create_ip,*notify_url,*trade_type,*partner,*attach; //應用APPID appid = WX_AppID; attach = @"test"; //微信支付商戶號 mch_id = MCH_ID; //產生隨機字符串,這里最好使用和安卓端一致的生成邏輯 nonce_str = [self generateTradeNO]; body = @"test"; //隨機產生訂單號用於測試,正式使用請換成你從自己服務器獲取的訂單號 out_trade_no = [self generateTradeNO]; //交易價格1表示0.01元,10表示0.1元 total_fee = @"1"; //獲取本機IP地址,請再wifi環境下測試,否則獲取的ip地址為error,正確格式應該是8.8.8.8 spbill_create_ip = [getIPhoneIP getIPAddress]; //spbill_create_ip = @"0.0.0.1"; //交易結果通知網站此處用於測試,隨意填寫,正式使用時填寫正確網站 notify_url = @"http://120.76.214.93:8080/mxsm_app_api/common/zfb/callbacks.do"; trade_type = @"APP"; //商戶密鑰 partner = WX_PartnerKey; //獲取sign簽名 DataMD5 * data = [[DataMD5 alloc] initWithAppid:appid mch_id:mch_id nonce_str:nonce_str partner_id:partner body:body out_trade_no:out_trade_no total_fee:total_fee spbill_create_ip:spbill_create_ip notify_url:notify_url trade_type:trade_type]; // sign = [data getSignForMD5]; //設置參數並轉化成xml格式 NSMutableDictionary * dic = [NSMutableDictionary dictionary]; [dic setValue:appid forKey:@"appid"]; //公眾賬號ID [dic setValue:mch_id forKey:@"mch_id"]; //商戶號 [dic setValue:nonce_str forKey:@"nonce_str"]; //隨機字符串 [dic setValue:body forKey:@"body"]; //商品描述 [dic setValue:out_trade_no forKey:@"out_trade_no"]; //訂單號 [dic setValue:total_fee forKey:@"total_fee"]; //金額 [dic setValue:spbill_create_ip forKey:@"spbill_create_ip"];//終端IP [dic setValue:notify_url forKey:@"notify_url"]; //通知地址 [dic setValue:trade_type forKey:@"trade_type"]; //交易類型 [dic setValue:sign forKey:@"sign"]; //簽名 // 轉換成xml字符串 NSString * string = [dic XMLString]; [self http:string]; } #pragma mark - 拿到轉換好的xml發送請求 - (void)http:(NSString *)xml { AFHTTPSessionManager * manager = [AFHTTPSessionManager manager]; // 這里傳入的xml字符串只是形似xml,但是不是正確是xml格式,需要使用af方法進行轉義 manager.responseSerializer = [[AFHTTPResponseSerializer alloc] init]; [manager.requestSerializer setValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; [manager.requestSerializer setValue: @"https://api.mch.weixin.qq.com/pay/unifiedorder" forHTTPHeaderField:@"SOAPAction"]; [manager.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) { return xml; }]; // post 請求微信 [manager POST:@"https://api.mch.weixin.qq.com/pay/unifiedorder" parameters:xml progress:^(NSProgress * _Nonnull uploadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] ; // 將微信返回的xml數據解析轉義成字典 NSDictionary *dic = [NSDictionary dictionaryWithXMLString:responseString]; // 判斷返回的許可 if ([[dic objectForKey:@"result_code"] isEqualToString:@"SUCCESS"] && [[dic objectForKey:@"return_code"] isEqualToString:@"SUCCESS"]) { // 發起微信支付,設置參數 PayReq *request = [[PayReq alloc] init]; request.openID = [dic objectForKey:@"appid"]; request.partnerId = [dic objectForKey:@"mch_id"]; request.prepayId = [dic objectForKey:@"prepay_id"]; request.package = @"Sign=WXPay"; request.nonceStr = [dic objectForKey:@"nonce_str"]; // 將當前事件轉化成時間戳 NSDate *datenow = [NSDate date]; NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]]; UInt32 timeStamp = [timeSp intValue]; request.timeStamp = timeStamp; // 簽名加密 DataMD5 *md5 = [[DataMD5 alloc] init]; request.sign = [md5 createMD5SingForPay:request.openID partnerid:request.partnerId prepayid:request.prepayId package:request.package noncestr:request.nonceStr timestamp:request.timeStamp]; // 調用微信 [WXApi sendReq:request]; }else{ // NSSLog(@"參數不正確,請檢查參數"); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { // NSSLog(@"error is %@",error); }]; } #pragma mark - 產生隨機訂單號 -(NSString *)generateTradeNO{ static int kNumber = 15; NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSMutableString *resultStr = [[NSMutableString alloc] init]; srand((unsigned)time(0)); for (int i = 0; i < kNumber; i++){ unsigned index = rand() % [sourceStr length]; NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)]; [resultStr appendString:oneStr]; } return resultStr; }
上面就是我們說的微信的,再說說這個支付寶的,我們先看看支付寶的一個流程圖:
支付寶注意點:
一:如果你是在SDK的接入過程當中有問題的話你可以看看這篇博客 iOS接入支付寶支付(小白都能看懂的支付寶支付)
二:支付寶的一些注意事項和微信的有點相同,該放服務端的放在服務端
三:檢測配置好你的公鑰和私鑰,你可以利用支付寶給的檢測工具進行一下驗證!確保他們沒問題再上傳!這一點很重要。
下面是可以本地測試源碼,給大家看看:
//**** 重要說明 **** //這里只是為了方便直接展示支付寶的整個支付流程;所以Demo中加簽過程直接放在客戶端完成; //真實App里,privateKey等數據嚴禁放在客戶端,加簽過程務必要放在服務端完成; //防止商戶私密數據泄露,造成不必要的資金損失,及面臨各種安全風險; /*============================================================================*/ /*=============================支付寶流程測試================*/ -(void)doAlipayPay{ NSString *appID = @""; // AppId NSString *privateKey = @""; // 用戶私鑰 //partner和seller獲取失敗,提示 if ([appID length] == 0 ||[privateKey length] == 0){ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"缺少appId或者私鑰。" delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil]; [alert show]; return; } //生成訂單信息及簽名 //將商品信息賦予AlixPayOrder的成員變量 Order * order = [Order new]; // NOTE: app_id設置 order.app_id = appID; // NOTE: 支付接口名稱 order.method = @"alipay.trade.app.pay"; // NOTE: 參數編碼格式 order.charset = @"utf-8"; // NOTE: 當前時間點 NSDateFormatter* formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; order.timestamp = [formatter stringFromDate:[NSDate date]]; // NOTE: 支付版本,固定格式,暫時不需要修改 order.version = @"1.0"; // NOTE: sign_type設置 order.sign_type = @"RSA"; // NOTE: 商品數據 order.biz_content = [BizContent new]; order.biz_content.body = @"我是測試數據"; order.biz_content.subject = @"1"; order.biz_content.out_trade_no = [self generateTradeNO]; //訂單ID(由商家自行制定) order.biz_content.timeout_express = @"30m"; //超時時間設置 order.biz_content.total_amount = [NSString stringWithFormat:@"%.2f", 0.01]; //商品價格 //將商品信息拼接成字符串 NSString *orderInfo = [order orderInfoEncoded:NO]; NSString *orderInfoEncoded = [order orderInfoEncoded:YES]; NSLog(@"orderSpec = %@",orderInfo); // NOTE: 獲取私鑰並將商戶信息簽名,外部商戶的加簽過程請務必放在服務端,防止公私鑰數據泄露; // 需要遵循RSA簽名規范,並將簽名字符串base64編碼和UrlEncode id<DataSigner> signer = CreateRSADataSigner(privateKey); NSString *signedString = [signer signString:orderInfo]; // NOTE: 如果加簽成功,則繼續執行支付 if (signedString != nil) { //應用注冊scheme,在AliSDKDemo-Info.plist定義URL types NSString *appScheme = @"alisdkdemo"; // NOTE: 將簽名成功字符串格式化為訂單字符串,請嚴格按照該格式 NSString *orderString = [NSString stringWithFormat:@"%@&sign=%@", orderInfoEncoded, signedString]; // NOTE: 調用支付結果開始支付 [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) { NSLog(@"reslut = %@",resultDic); }]; } } // 隨機產生一個訂單號 - (NSString *)generateTradeNO { static int kNumber = 15; NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSMutableString *resultStr = [[NSMutableString alloc] init]; srand((unsigned)time(0)); for (int i = 0; i < kNumber; i++){ unsigned index = rand() % [sourceStr length]; NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)]; [resultStr appendString:oneStr]; } return resultStr; }
再說說這篇博客的重點,支付寶、微信和友盟在一起時候的回調!
說說微信和友盟的注意事項,注意下面代理中的注釋,那些注意的點全都加載代碼的注釋里面了;
/****** 支付和第三方登錄分享回調注意事項 還有方法不過這個方法的系統版本最低要求是9.0 (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options // NOTE:微信回調在9.0之后還是會進入這里的,但這里就有一個微信回調和友盟回調沖突的問題,按照最開始這樣寫回調的話會造成微信支付成功不回調的現象,主要原因是微信支付,通過友盟判斷,返回的結果是YES BOOL UMShareResult = [[UMSocialManager defaultManager] handleOpenURL:url]; 返回的結果示例: (lldb) po url 微信支付 wx47e8322e53c76163://pay/?returnKey=&ret=0 (lldb) po url 第三方登錄 wx47e8322e53c76163://oauth?code=001J9HPU1SYnGS05xpOU1hpHPU1J9HPS&lang=zh_HK&country=CN&state= (lldb) po url 微信分享 wx47e8322e53c76163://platformId=wechat // NOTE:錯誤方法如下 -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ // 友盟SDK的回調 BOOL UMShareResult = [[UMSocialManager defaultManager] handleOpenURL:url]; if (!UMShareResult) { // 其他如支付等SDK的回調 // 微信支付SDK回調 BOOL WXPauResult = [WXApi handleOpenURL:url delegate:self]; if (!WXPauResult) { //不是微信支付回調,那就是支付寶 //跳轉支付寶錢包進行支付,處理支付結果 if ([url.host isEqualToString:@"safepay"]) { [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) { if ([resultDic[@"resultStatus"] isEqualToString:@"9000"]) { // 支付寶支付成功 } }]; return YES; } } return WXPauResult; } return UMShareResult; } END *****/ // APP跳轉完之后的回調,這個回調接口是所有的系統類型都適用的 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ // 首先判斷是不是微信支付的回調 // NOTE:上面的回調示例可以看住出,微信的支付回調就是按下面格式開頭的,前面是APPId,判斷URL是不是這個開頭 if ([[url absoluteString]hasPrefix:@"wx47e8322e53c76163://pay"]) { BOOL WXPauResult = [WXApi handleOpenURL:url delegate:self]; return WXPauResult; }else{ // 友盟SDK的回調 BOOL UMShareResult = [[UMSocialManager defaultManager] handleOpenURL:url]; if (!UMShareResult) { //跳轉支付寶錢包進行支付,處理支付結果 if ([url.host isEqualToString:@"safepay"]) { [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) { //NSLog(@"result = %@",resultDic); if ([resultDic[@"resultStatus"] isEqualToString:@"9000"]) { // 后台驗證支付結果 [[NSNotificationCenter defaultCenter]postNotificationName:ALIPAYRESULT object:nil userInfo:@{@"result":resultDic[@"result"]}]; } }]; return YES; } } return UMShareResult; } }
最后說說支付寶回調的注意事項:
支付寶和微信相比,支付寶要是客戶沒有安裝APP它是可以調用網頁版本的,微信卻不行!所以這里要特別注意支付寶網頁版本的回調。在支付寶的SDK里面,有這兩連個方法大家注意:
/** * 支付接口 * * @param orderStr 訂單信息 * @param schemeStr 調用支付的app注冊在info.plist中的scheme * @param compltionBlock 支付結果回調Block,用於wap支付結果回調(非跳轉錢包支付) */ - (void)payOrder:(NSString *)orderStr fromScheme:(NSString *)schemeStr callback:(CompletionBlock)completionBlock; /** * 處理錢包或者獨立快捷app支付跳回商戶app攜帶的支付結果Url * * @param resultUrl 支付結果url * @param completionBlock 支付結果回調 */ - (void)processOrderWithPaymentResult:(NSURL *)resultUrl standbyCallback:(CompletionBlock)completionBlock;
注意點:
一:支付寶網頁版本的回調就是在支付接口completionBlock里面回調的,不會去SDK里面的 processOrderWithPaymentResult 方法回調。
二:App通過支付寶App支付完成之后的回調缺恰恰是在 processOrderWithPaymentResult 里面。