开发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还无法响应,就抛出异常。如果有某个控件响应(包括自己)即可处理… 。时间紧急,以后再详细描述事件的传递和响应。

二、苹果内购流程

  1. 程序向服务器发送请求,获得一份产品列表。
  2. 服务器返回包含产品标识符的列表。
  3. 程序向App Store发送请求,得到产品的信息。
  4. App Store返回产品信息。
  5. 程序把返回的产品信息显示给用户(App的store界面)
  6. 用户选择某个产品
  7. 程序向App Store发送支付请求
  8. App Store处理支付请求并返回交易完成信息。
  9. 程序从信息中获得数据,并发送至服务器。
  10. 服务器纪录数据,并进行审(我们的)查。
  11. 服务器将数据发给App Store来验证该交易的有效性。
  12. App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。
  13. 服务器读取返回的数据,确定用户购买的内容。
  14. 服务器将购买的内容传递给程序。

代码如下:

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];
    }];
  1. 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];
    }];
  1. 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];
    }
}

欢迎使用,提出你宝贵的意见!