开发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<DataSigner> 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];
}
}
欢迎使用,提出你宝贵的意见!