您的当前位置:首页苹果内购(订购为主)

苹果内购(订购为主)

2024-12-13 来源:哗拓教育

苹果支付

一:绑定银行卡visa卡

二:创建内购产品 :app里面的功能

三:创建沙盒测试账户:Itunes里面用户和职能。

四:购买代码块

+(instancetype)share{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        applePay = [[AHApplePay alloc]init];
    });
    if (applePay.myProducts.count == 0) {
        [applePay getPurchaseGoodsList];
    }
    return applePay;
}

//获取商品列表
-(void)getPurchaseGoodsList{
    if ([SKPaymentQueue canMakePayments]) {
        //此处内购商品编码是虚构的 ?????
        NSString *productPath = [[NSBundle mainBundle] pathForResource:@"productIDS" ofType:@"plist"];
        NSArray *products = [NSArray arrayWithContentsOfFile:productPath];
        [self getProductInfo:products];
    }else{
        if (_cannotPurchaseBlock) {
            _cannotPurchaseBlock(nil,@"失败,用户禁止应用内付费购买");
        }
    }
}

//购买商品
-(void)purchaseGoods:(id)goods{
    
    [self purchaseGoodsWithApplePurchase:goods];
}

//苹果支付
-(void)purchaseGoodsWithApplePurchase:(id)goods{
    NSString *identify = (NSString*)goods;
    SKProduct *paymentPay = nil;
    for (SKProduct *payment in self.myProducts) {
        if ([payment.productIdentifier isEqualToString:identify]) {
            paymentPay = payment;
        }
    }
    if (paymentPay == nil) {
        return;
    }
    //1.发起一个购买操作
    SKPayment *payment = [SKPayment paymentWithProduct:paymentPay];
    if (payment == nil) {
        return;
    }
    // 2.将票据加到到交易队列中
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    //3.观察交易队列中交易发生的改变
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}

//购买 根据在plist文件位置
-(void)applePay:(NSInteger)idx{
    NSString *productPath = [[NSBundle mainBundle] pathForResource:@"productIDS" ofType:@"plist"];
    NSArray *products = [NSArray arrayWithContentsOfFile:productPath];
    if (idx<products.count) {
        NSString *identify = products[idx];
        [self.myProducts enumerateObjectsUsingBlock:^(SKProduct *payment, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([payment.productIdentifier isEqualToString:identify]) {
                //1.发起一个购买操作
                SKPayment *paymentPro = [SKPayment paymentWithProduct:payment];
                // 2.将票据加到到交易队列中
                [[SKPaymentQueue defaultQueue] addPayment:paymentPro];
                //3.观察交易队列中交易发生的改变
                [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
                
            }
            
        }];
        
    }
    
}

//通过该IAP的Product ID向App Store查询,获取SKPayment实例,接着通过SKPaymentQueue的addPayment方法发起一个购买的操作
//下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目,否则会查询失败
-(void)getProductInfo:(NSArray *)productIds{
    NSSet *set = [NSSet setWithArray:productIds];
    _applePurchaseRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    _applePurchaseRequest.delegate = self;
    [_applePurchaseRequest start];
}

#pragma mark - SKProductsRequestDelegate

//查询的回调函数 -----获取可销售的商品,并且排序
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    
    //获取到的所有内购商品
    NSArray *myProducts = response.products;
    //判断个数
    if (myProducts.count==0) {
        if (_failureBlock) {
            _failureBlock(nil,@"无法获取产品信息,购买失败。");
        }
        NSLog(@"无法获取产品信息,购买失败。");
        return;
    }else{
        self.myProducts = myProducts;
    }
    //刷新、配置UI ???
#warning 发起一个购买操作
    //        //1.发起一个购买操作
    //        SKPayment *payment = [SKPayment paymentWithProduct:myProducts[self.purchaseGoodsNumber]];
    //        // 2.将票据加到到交易队列中
    //        [[SKPaymentQueue defaultQueue] addPayment:payment];
    //        //3.观察交易队列中交易发生的改变
    //        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}

#pragma mark - SKPaymentTransactionObserver

//当用户购买的操作有结果时,就会触发下面的回调函数,相应进行处理
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    
    for (SKPaymentTransaction *transaction in transactions) {
        
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:  //交易完成
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:     //交易失败
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:  //已经购买过该商品
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing: //商品添加进列表
                LOG(@"商品添加进列表");
                break;
            default:
                break;
        }
    }
}

//交易完成后的操作
-(void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSString * productIdentifier = transaction.payment.productIdentifier;
//    NSURL *url = [[NSBundle mainBundle] appStoreReceiptURL];
//    NSData *transactionReceiptData = [NSData dataWithContentsOfURL:url];
//    NSString *receipt = [transactionReceiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    
    NSData *data = transaction.transactionReceipt;
    NSString * receipt = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

    [[NSUserDefaults standardUserDefaults]setObject:receipt forKey:@"receipt"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    if ([productIdentifier length]>0 && !TestStringIsBlank(receipt)) {
        //向自己的服务器验证购买凭证????
        AHPersonInfoModel *infoModel = [AHPersonInfoManager share].infoModel;
        NSDictionary *dic = @{@"uid":@(infoModel.uid),@"token":infoModel.token,@"orderid":receipt};
        [AHRequestTool applepay:dic succussBlock:^(AHBaseModel *baseModel) {
            LOG(@"%@",baseModel);
            
            if (self.successfulBlock) {
                self.successfulBlock(nil, nil);
                self.successfulBlock = nil;
            }
        } failureBlock:^(NSString *errorStr) {
            if (self.failureBlock) {
                self.failureBlock(nil, nil);
                self.failureBlock  = nil;
            }
        }];
        //创建描述请求的JSON对象
        NSError *error;
        NSDictionary * requestContents = @{@"receipt-data":receipt,@"password":@"7ad47dcb6de94b08aa3ac3b7dd0d55f8",@"exclude-old-transactions":@(YES)};
        NSData * requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                               options:0 error:&error];
        if(!requestData){
            //错误处理
        }
        //创建带有收据数据的POST请求。
        NSURL * storeURL = [NSURL 
        NSMutableURLRequest * storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
        [storeRequest setHTTPMethod:@"POST"];
        [storeRequest setHTTPBody:requestData];
        
        //在后台队列上连接到iTunes Store。
        NSOperationQueue * queue = [[NSOperationQueue alloc] init];
        [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                               completionHandler:^(NSURLResponse * response,NSData * data,NSError * connectionError){
                                   if(connectionError){
                                       //错误处理
                                   } else {
                                       NSError *error;
                                       NSDictionary * jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                       if(jsonResponse){
                                           //验证
                                           LOG(@"%@",jsonResponse);
                                       }
                                   }}];

    }
    //移除transaction购买操作
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

//交易失败后的操作
-(void)failedTransaction:(SKPaymentTransaction *)transaction{
    
    if (transaction.error.code != SKErrorPaymentCancelled) {
        if (_failureBlock) {
            _failureBlock(nil,@"购买失败");
        }
        LOG(@"购买失败");
        
    }else{
        if (_failureBlock) {
            _failureBlock(nil,@"用户取消交易");
        }
        LOG(@"用户取消交易");
    }
    //移除transaction购买操作
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

//已经购买过该商品
-(void)restoreTransaction:(SKPaymentTransaction *)transaction{
    //对于已购买商品,处理恢复购买的逻辑
    //移除transaction购买操作
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

五:苹果支付验证代码块,建议后台验证。

NSString *recept =  [[NSUserDefaults standardUserDefaults]objectForKey:@"receipt"];
    if (!recept) {
        return;
    }
    //创建描述请求的JSON对象 只有订阅型才有密码设置。
    NSError *error;
    NSDictionary * requestContents = @{@"receipt-data":recept,@"password":@"7ad47dcb6de94b08aa3ac3b7dd0d55f8"};
    NSData * requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                           options:0 error:&error];
    if(!requestData){
        //错误处理
    }
    //创建带有收据数据的POST请求。
    NSURL * storeURL = [NSURL 
    NSMutableURLRequest * storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];

    //在后台队列上连接到iTunes Store。
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse * response,NSData * data,NSError * connectionError){
                               if(connectionError){
                                   //错误处理
                               } else {
                                   NSError *error;
                                   NSDictionary * jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                   if(jsonResponse){
                                       //验证
                                       LOG(@"%@",jsonResponse);
                                   }
                               }}];

内购沙盒测试续订周期时间不一样,且一天测试次数大概6次,且自动续费一般失败。

时间对应关系

六 :上线注意事项(这里是以订购为例)

购买协议连接或弹框一定要有 购买协议具体内容

购买协议具体内容:

一定要有:自动续费,可随时取消。提前24续费,下个周期第一天可取消。app设置里面可取消订购。协议里最好加上自己的公司。


NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:13],NSForegroundColorAttributeName:[UIColor blackColor]};
    NSString *contentStr = @"* The cost of subscription for Super Powers is 2.99 USD for 7 days, 7.99 USD for 1 month, 19.99 USD for 3 months, and 49.99 USD for 1 year.\n\n* If you choose to purchase Super Powers, the payment will be charged to your iTunes account at confirmation of purchase.\n\n* Your subscription automatically renews unless auto-renew is turned off at least 24-hours before the end of the current period.\n\n* Account will be charged for renewal within 24-hours prior to the end of the current period, and the cost of the renewal would be the same as your first payment.\n\n* You can manage your auto-renewal and auto-renewal may be turned off by going to your Account Settings after purchase.\n\n* No cancellation of the current subscription is allowed during the active subscription period.\n\n* If you don't want to purchase Super Powers, you can simply continue using Dittor for free.\n\n* Any unused portion of a free trial period, if offered, will be forfeited when you purchase a subscription to that 
    NSMutableAttributedString *contentAttr = [[NSMutableAttributedString alloc]initWithString:contentStr attributes:dic];
    NSRange range = [contentStr 
    NSString *lin1 = 
    NSString *lin2 = 
    NSRange range1 = [contentStr rangeOfString:lin1];
    NSRange range2 = [contentStr rangeOfString:lin2];
    self.range1 = range1;
    self.range2 = range2;
    [contentAttr addAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor]} range:range];
    [contentAttr addAttributes:@{NSLinkAttributeName:lin1}
               range:range1];
    [contentAttr addAttributes:@{NSLinkAttributeName:lin2}
                         range:range2];
    self.textView.attributedText = contentAttr;
    
    self.textView.delegate =self;
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
    if (characterRange.location == self.range1.location && characterRange.length == self.range1.length) {
        [self bt_pushTrermAction:nil];
    }else if(characterRange.location == self.range2.location && characterRange.length == self.range2.length){
        [self bt_privacyAction:nil];
    }
    return NO;
}

app上线app描述包含商品名字,价格,最好加购买协议。

app上线的时候要在app描述里面加入内购信息

可参考爱奇艺,等有内购功能app在appstore上的描述。

爱奇艺

七 :深坑记录。

  • 第一次加内购或者有新商品,发布成功后,服务器并没有立刻同步,必须要等上一段时间(12小时左右)才能拉取到产品列表。
  • 点击发布后等待时间较久,大概12小时才能在appstore上搜索到。(官方说24之内可以看到)
显示全文