iOS程序生命周期,苹果内购、微信支付、支付宝支付
iOS程序生命周期,苹果内购、微信支付、支付宝支付。开发4年了,很少写博客,主要是懒,哈哈。遇到不清晰的就翻翻以前的代码。有时还找不到,现在发现博客可以更方便查找知识点,所以用博客做笔记吧。也有助于大家学习、交流,先写一些基础的吧。
一、iOS程序常识
1. 生命周期
程序启动时,加载xib、storyboard、plist等各种资源配置;各个控制器的load方法
+(void)load{ NSLog(@"11--%s",__func__); },然后才去运行main.m内main函数
int main(int argc, char * argv[]) { NSLog(@"%s",__func__); @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }紧接着去找Appdelegate、window、rootViewController 其中在控制器内有以下几个方法加载需要注意:
+ (void)initialize{ [super initialize]; NSLog(@"%s",__func__);// 实例化对象调用之前 } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { NSLog(@"%s",__func__);// 一般在此做资源预配置 } return self; } - (instancetype)init { self = [super init];// 去调用initWithNibName if (self) { NSLog(@"%s",__func__); } return self; } - (void)loadView{ [super loadView]; // 初始化self.View,除非有特别需要修改View的加载,在其他页面推出新控制器时,如果调用新控制器的view,那么即走此方法,viewDidLoad也会走,根据项目要求来把握。 NSLog(@"%s",__func__); } - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%s",__func__); // loadView一旦加载完毕self.view,就走此方法。 } 下面方法依次调用 - (void)viewWillLayoutSubviews;//可能调用多次 - (void)viewDidLayoutSubviews;//可能调用多次 - (void)viewWillAppear:(BOOL)animated; - (void)viewDidAppear:(BOOL)animated; - (void)viewWillDisappear:(BOOL)animated; - (void)viewDidDisappear:(BOOL)animated; - (void)dealloc;
2. 事件传递
touch屏幕产生event事件,交给 UIApplication–>AppDelegate –>UIWindow–>rootViewController–>rootView。
window 调用- (UIView )hitTest:(CGPoint)point withEvent:(UIEvent )event查找。rootView也是调用- (UIView )hitTest:(CGPoint)point withEvent:(UIEvent )event查找合适处理事件的View,在查找内由subViews最后一个控件向前遍历控件,见代码:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ // NSLog(@"%@ -- %s",[self class],__func__); if (self.alpha < 0.1 || self.hidden == YES || self.userInteractionEnabled == NO) return nil; if (![self pointInside:point withEvent:event]) return nil; NSArray *subViews = self.subviews; NSInteger count = subViews.count; for (NSInteger i = count - 1 ; i>0; i--) { UIView *subView = subViews[count]; CGPoint subViewPoint = [self convertPoint:point toView:subView]; if ([subView hitTest:subViewPoint withEvent:event]) { return subView; } } return self; }
返回合适响应事件的控件.使用场景如:View内多个子控件,无论点击哪个子控件,都要View响应,调用View的某个方法统一处理。可能有的小伙伴还是挨个写出子控件的响应方法吧?这个方法能让你简化很多代码 。
2. 事件响应
子控件逐个向上传递响应方法,最顶层是UIApplication,如果UIApplication还无法响应,就抛出异常。如果有某个控件响应(包括自己)即可处理… 。时间紧急,以后再详细描述事件的传递和响应。
二、苹果内购流程
程序向服务器发送请求,获得一份产品列表。 服务器返回包含产品标识符的列表。 程序向App Store发送请求,得到产品的信息。 App Store返回产品信息。 程序把返回的产品信息显示给用户(App的store界面) 用户选择某个产品 程序向App Store发送支付请求 App Store处理支付请求并返回交易完成信息。 程序从信息中获得数据,并发送至服务器。 服务器纪录数据,并进行审(我们的)查。 服务器将数据发给App Store来验证该交易的有效性。 App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。 服务器读取返回的数据,确定用户购买的内容。 服务器将购买的内容传递给程序。
代码如下:
storeKit框架,代码内有些提示信息,内购日志等可忽略
1.初始化
+ (instancetype)SharedPayManager { static XYPayManager *payManager; static dispatch_once_t once = 0; dispatch_once(&once, ^{ payManager = [[XYPayManager alloc] init]; // 注册苹果内购 [[SKPaymentQueue defaultQueue] addTransactionObserver:payManager]; }); return payManager; }
2.根据标识,请求商品详细信息
该商品标识是在控制器的initWithNibName方法,请求server拿到的一系列商品标识(该标识是事先在iTunes Connect –>我的App–>App内购项目内设置的) 拿到该标识,去和苹果交互- (void)requestAppleStoreProductDataWithString:(NSString *)productIdentifier payComplete:(payCompleteBlock)payCompletionBlock { if(![SKPaymentQueue canMakePayments]) { WriteAppleStorePayLogFile(@"不允许程序内付费");// 友情提示 [APPCONTEXT.hudHelper showHudOnWindow:@"不允许程序内付费" image:nil acitivity:NO autoHideTime:DEFAULTTIME]; return; } if (!productIdentifier || productIdentifier.length <= 0) { WriteAppleStorePayLogFile(@"内购标识读取出错");// 写入本地内购日志 return; } // 将标识传入参数,去苹果server请求 self.payComplete = payCompletionBlock; self.appleProductIdentifier = productIdentifier; NSArray *product = [[NSArray alloc] initWithObjects:productIdentifier, nil]; NSSet *nsset = [NSSet setWithArray:product]; SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset]; request.delegate = self; [request start]; }
3.收到返回的商品详细信息
success
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ WriteAppleStorePayLogFile(@"--------------收到产品反馈消息---------------------%@",response); if (!response || response.products.count == 0) { WriteAppleStorePayLogFile(@"未请求到商品信息,请稍后再试。"); [APPCONTEXT.hudHelper showHudOnWindow:@"未请求到商品信息,请稍后再试。" image:nil acitivity:NO autoHideTime:DEFAULTTIME]; return; } NSArray *product = response.products; WriteAppleStorePayLogFile(@"productID:%@", response.invalidProductIdentifiers); WriteAppleStorePayLogFile(@"产品付费数量:%lu",(unsigned long)[product count]); SKProduct *p = nil; for (SKProduct *pro in product) { WriteAppleStorePayLogFile(@"%@", [pro description]); WriteAppleStorePayLogFile(@"%@", [pro localizedTitle]); WriteAppleStorePayLogFile(@"%@", [pro localizedDescription]); WriteAppleStorePayLogFile(@"%@", [pro price]); WriteAppleStorePayLogFile(@"%@", [pro productIdentifier]); if([pro.productIdentifier isEqualToString:self.appleProductIdentifier]){ p = pro; } } if (p == nil) { WriteAppleStorePayLogFile(@"未请求到商品信息,请稍后再试。"); [APPCONTEXT.hudHelper showHudOnWindow:@"未请求到商品信息,请稍后再试。" image:nil acitivity:NO autoHideTime:DEFAULTTIME]; }else {// 发送购买请求 SKPayment *payment = [SKPayment paymentWithProduct:p]; WriteAppleStorePayLogFile(@"发送购买请求"); [[SKPaymentQueue defaultQueue] addPayment:payment]; } }
error
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{}
Finish
- (void)requestDidFinish:(SKRequest *)request{}
4.监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction { WriteAppleStorePayLogFile(@"购买队列中产品数量-%ld",(long)transaction.count); for(SKPaymentTransaction *paymentTransactionp in transaction){ WriteAppleStorePayLogFile(@"当前购买标识-%@",paymentTransactionp.payment.productIdentifier); switch (paymentTransactionp.transactionState) { case SKPaymentTransactionStatePurchased: { WriteAppleStorePayLogFile(@"交易完成-restoreCompletedTransactions"); [self buyAppleStoreProductSucceedWithPaymentTransactionp:paymentTransactionp]; [self completeTransaction:paymentTransactionp]; } break; case SKPaymentTransactionStatePurchasing: WriteAppleStorePayLogFile(@"商品添加进列表"); break; case SKPaymentTransactionStateRestored: WriteAppleStorePayLogFile(@"已经购买过商品"); break; case SKPaymentTransactionStateFailed: { WriteAppleStorePayLogFile(@"交易失败-苹果支付未成功-%@",paymentTransactionp.error); [APPCONTEXT.hudHelper showHudOnWindow:@"苹果支付未成功" image:RS_HUD_ICON_WARNING acitivity:NO autoHideTime:DEFAULTTIME]; [self completeTransaction:paymentTransactionp]; } break; } } }
5.交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{ NSString *completeTransactionStr; switch (transaction.error.code) { case SKErrorClientInvalid: completeTransactionStr = @"SKErrorClientInvalid - client is not allowed to issue the request, etc."; break; case SKErrorPaymentCancelled: //取消购买 completeTransactionStr = @"SKErrorPaymentCancelled - user cancelled the request, etc."; break; case SKErrorPaymentInvalid: completeTransactionStr = @"SKErrorPaymentInvalid - purchase identifier was invalid, etc."; break; case SKErrorPaymentNotAllowed: completeTransactionStr = @"SKErrorPaymentNotAllowed - this device is not allowed to make the payment"; break; case SKErrorStoreProductNotAvailable: completeTransactionStr = @"SKErrorStoreProductNotAvailable - Product is not available in the current storefront"; break; case SKErrorCloudServicePermissionDenied: completeTransactionStr = @"SKErrorCloudServicePermissionDenied - user has not allowed access to cloud service information"; break; case SKErrorCloudServiceNetworkConnectionFailed: completeTransactionStr = @"SKErrorCloudServiceNetworkConnectionFailed - the device could not connect to the nework"; break; default: // SKErrorUnknown completeTransactionStr = @"SKErrorUnknown - 可视为内购成功"; break; } WriteAppleStorePayLogFile(@"\n\n 购买结果completeTransaction : %@\n\n", completeTransactionStr); WriteAppleStorePayLogFile(@"交易结束\n"); [APPCONTEXT.logFileManager uploadAppleStorePayLogFile]; // 内购友盟统计 NSDictionary *dict = @{@"userid":APPCONTEXT.mainUser.userid, @"nickname":APPCONTEXT.mainUser.nickname, @"completeTransaction":completeTransactionStr}; [MobClick event:kMobClickEvent_iap_complete_transaction attributes:dict]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; }
6.苹果内购支付成功
- (void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)paymentTransactionp { NSString *base64Str = [paymentTransactionp.transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; [[NSUserDefaults standardUserDefaults] setValue:base64Str forKey:AppStorePayTradeNoKey]; [[NSUserDefaults standardUserDefaults] synchronize]; // 一定要将支付结果的交易凭据,发送后台验证,防止假支付。 [self checkAppStorePayResultWithBase64String:base64Str]; }
7.server验证交易凭据,server拿到交易凭据后去和苹果交互验证
- (void)checkAppStorePayResultWithBase64String:(NSString *)base64String { NSString *method = kRequestMethodAppleStorePayCheck; __weak typeof(self) weak_self = self; XYBaseRequest *baseRequest = [[XYBaseRequest alloc] init]; NSNumber *sandbox; #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG)) sandbox = @(0);// 辨别测试账号还是线上发布的用户支付 #else sandbox = @(1); #endif NSMutableDictionary *prgam = [[NSMutableDictionary alloc] init];; [prgam setValue:sandbox forKey:@"sandbox"]; [prgam setValue:base64String forKey:@"reciept"]; [baseRequest sendRequestWithMethod:method param:prgam onComplete:^(NSInteger errorNum, id info, XYError *errorMsg) { NSLog(@"%@",info); [APPCONTEXT.hudHelper hideHud]; if (errorNum == kXYBaseRequestSuccess) { // 依据返回值 code 判断 // 后台验证失败 if ([info[@"code"] intValue] != 0) { if (weak_self.payComplete) { weak_self.payComplete(info[@"data"],NO); } // 后台有已经记录相应的数据,清除记录的苹果支付凭证 [[NSUserDefaults standardUserDefaults] removeObjectForKey:AppStorePayTradeNoKey]; return; } WriteAppleStorePayLogFile(@"支付成功"); [APPCONTEXT.hudHelper showHudOnWindow:@"支付成功" image:RS_HUD_ICON_OK acitivity:NO autoHideTime:DEFAULTTIME]; // 加载数据正常显示 if (weak_self.payComplete) { weak_self.payComplete(info[@"data"],YES); } // 后台有已经记录相应的数据,清除记录的苹果支付凭证 [[NSUserDefaults standardUserDefaults] removeObjectForKey:AppStorePayTradeNoKey]; }else { if (weak_self.payComplete) { weak_self.payComplete(info[@"data"],NO); } } }]; }
二、支付宝支付流程
流程参考苹果内购,下面贴出.m代码
1.向自己服务器发出支付请求,拿回商品信息.
// 参数是产品 id ,目前默认定值@"1" type:1,标示支付宝支付 [HttpRequestManger payWithServerGoodId:@"3000" type:@1 andResult:^(id data) { MDJLog(@"data:%@",data); // 阿里支付时只需调用者一句话就ok [[AliPayObject shareInstance] alipayWithParam:(NSDictionary *)data]; }];AliPayObject.m
+ (AliPayObject *)shareInstance { static AliPayObject *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //线程安全 sharedInstance = [[AliPayObject alloc] init]; }); return sharedInstance; } #pragma mark - alipay action // 生成订单信息及签名 - (void)alipayWithParam:(NSDictionary *)param { // 1.回调地址 URL NSString *appScheme = ZhiFuBaoAppId; // 2.订单信息 NSString *orderInfo = [self getOrderInfo:param]; // 3.验证签名(此处不验证,支付完成后,也可让后台验证支付结果) if (orderInfo == nil) { UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"server签名失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alertView show]; return; } // 4.订单签名 NSString *signedStr = [self doRsa:orderInfo]; NSString *orderStr = nil; if (signedStr != nil) { orderStr = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"", orderInfo, signedStr, @"RSA"]; [[AlipaySDK defaultService] payOrder:orderStr fromScheme:appScheme callback:^(NSDictionary *resultDic) { [[AliPayObject shareInstance] paymentResult:resultDic]; }]; } } // 校验后台发送的数据,并初始化订单信息, - (NSString*)getOrderInfo:(NSDictionary *)param { Order *order = [[Order alloc] init]; order.partner = param[@"partner"]; order.seller = param[@"seller_id"]; order.tradeNO = param[@"out_trade_no"]; order.productName = param[@"subject"];; //商品标题 order.productDescription = param[@"body"];; //商品描述 order.amount = [NSString stringWithFormat:@"%.2f",[param[@"total_fee"] floatValue]]; //商品价格 order.notifyURL = param[@"notify_url"]; //回调URL order.service = @"mobile.securitypay.pay"; order.paymentType = @"1"; order.inputCharset = @"utf-8"; order.itBPay = @"30m"; order.showUrl = @"m.alipay.com"; // 1.server 签名 NSString *serverSign = [self urlEncodedString:param[@"sign"]]; // 2.字符串拼接 NSMutableString *myString = [NSMutableString string]; [myString appendFormat:@"service=%@&partner=%@&_input_charset=utf-8¬ify_url=%@&out_trade_no=%@&payment_type=1&seller_id=%@&total_fee=%@",order.service,order.partner,order.notifyURL,order.tradeNO,order.seller,order.amount]; MDJLog(@"\n myString:%@ \n",myString); // 3.拼接后的字符串签名 NSString *mySign = [self doRsa:myString]; // 4.签名核对 BOOL flag = [mySign isEqualToString:serverSign]; if (flag) { return [order description]; }else { return nil; } } - (NSString *)doRsa:(NSString*)orderInfo { id signer; signer = CreateRSADataSigner(PartnerPrivKey); NSString *signedString = [signer signString:orderInfo]; return signedString; } - (NSString*)urlEncodedString:(NSString *)string { NSString * encodedString = (__bridge_transfer NSString*) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8 ); return encodedString; } - (void)aliPaymentResult:(AlixPayResult *)resultd { if (resultd) { if (resultd.statusCode == 9000) {//支付成功 /* * 用公钥验证签名 严格验证请使用result.resultString与result.signString验签 */ //支付成功后,发送通知 NSInteger goodsId = [[NSUserDefaults standardUserDefaults] integerForKey:@"goodsId"]; [[NSNotificationCenter defaultCenter] postNotificationName:AlipayPaySuccess object:nil userInfo:@{@"goodsId":[NSNumber numberWithInteger:goodsId]}]; //更新用户的购买状态 [[FMDBManger sharedFMDBManger] updateUserPayStatus]; /** 在这里可以跳转到相应的界面 */ } if (resultd.statusCode == 6001) {//用户中途取消 UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"您中途取消了订单" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; /** 在这里可以跳转到相应的界面 */ [alertView show]; } if (resultd.statusCode == 6002) {//网络连接出错 UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"网络连接出错" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; /** 在这里可以跳转到相应的界面 */ [alertView show]; } if (resultd.statusCode == 4000) {//订单支付失败 UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"您的订单支付失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; /** 在这里可以跳转到相应的界面 */ [alertView show]; } } else { } } - (void)paymentResult:(NSDictionary *)resultDict { MDJLog(@"resultDict:%@",resultDict); AlixPayResult *result = [[AlixPayResult alloc] initWithDict:resultDict]; [[AliPayObject shareInstance] aliPaymentResult:result]; }
三、微信支付流程
流程参考苹果内购,下面贴出.m代码
1.向自己服务器发出支付请求,拿回商品信息.
// 参数是产品 id ,目前默认定值@"1",type:2标示微信支付 [HttpRequestManger payWithServerGoodId:@"3000" type:@2 andResult:^(id data) { MDJLog(@"data:%@",data); // 微信支付时只需调用者一句话就ok [[UUXWeiXinPay sharedWeiXinPay] weiXinPayWithParam:(NSDictionary *)data]; }];UUXWeiXinPay.m
+ (instancetype)sharedWeiXinPay { static UUXWeiXinPay *sharedWeiXinPay = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedWeiXinPay = [[self alloc] init]; }); return sharedWeiXinPay; } - (void)weiXinPayWithParam:(NSDictionary *)dict { //调起微信支付 PayReq* req = [[PayReq alloc] init]; // 商户号 req.partnerId = [dict objectForKey:@"partnerid"]; // 预支付交易会话ID req.prepayId = [dict objectForKey:@"prepayid"]; // 随机字符串 req.nonceStr = [dict objectForKey:@"noncestr"]; // 时间戳 req.timeStamp = [[dict objectForKey:@"timestamp"] intValue]; // 扩展字段 req.package = [dict objectForKey:@"package"]; // 签名 req.sign = [dict objectForKey:@"sign"]; [WXApi sendReq:req]; } /** * 支付返回 */ - (void)onWXResp:(BaseResp *)resp { if([resp isKindOfClass:[PayResp class]]){ //支付返回结果,实际支付结果需要去微信服务器端查询 NSString * title = @"支付结果"; NSString * message = nil; switch (resp.errCode) { case WXSuccess: message = @"支付成功!"; NSInteger goodsId = [[NSUserDefaults standardUserDefaults] integerForKey:@"goodsId"]; [[NSNotificationCenter defaultCenter] postNotificationName:AlipayPaySuccess object:nil userInfo:@{@"goodsId":[NSNumber numberWithInteger:goodsId]}]; //更新用户的购买状态 [[FMDBManger sharedFMDBManger] updateUserPayStatus]; break; case WXErrCodeCommon: { message = @"普通错误类型"; } break; case WXErrCodeUserCancel: { message = @"用户点击取消并返回"; } break; case WXErrCodeSentFail: { message = @"发送失败 "; } break; case WXErrCodeAuthDeny: { message = @"授权失败 "; } break; case WXErrCodeUnsupport: { message = @"微信不支持"; } break; default: { message = @"支付失败"; } break; } UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alert show]; } }