iOS 内购最新版(埋坑)

最近项目中要加进去虚拟币和会员的功能 本以为特别简单不就是几十行代码嘛 但是内购做下来 可把我坑惨了 有什么问题尽可留言 一定帮你们顺利通过内购

文章末尾有内购遇见的bug 以及解决办法

一 首先区分一下 Apple Pay 和内购完全是两码事

本来说集成内购的 但我们后台大哥说可以使用apple pay 也是苹果的支付系统 没做过内购我以为就是一样的 然后和我们超哥 三下五除二的把applepay集成进来的 然后也是线下测试 线上测试 一大堆 终于好了 开始提测 果然 因为applepay悲剧了 呵呵呵呵了

二 开始集成内购

1、先到iTunes Connect上填写协议、税务和银行业务

如果你是外包公司,那么你可以让你的客户填写这一堆信息;如果你只是是产品公司的技术开发人员,那么你可以让项目负责人填写这一堆信息;如果没有如果,兄弟辛苦了,自己动手来吧。


Paste_Image.png
Paste_Image.png

先点击Contact Info 的Set Up

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

进行十二步的时候可能有些银行通过下面的Look up CNAPS Code方法查不到,就需要借助百度了,一定要准确查询,否则会有问题。推荐一个地址
https://e.czbank.com/CORPORBANK/query_unionBank_index.jsp

Paste_Image.png

这一步需要注意的是,货币类型可能有歧义,看你是想收美元还是人民币了,都说美元合适。不过,我做的时候为了避免事情,还是选择了CNY,支持国产。还有一点,银行账号如果是对公的账号,需要填写公司的英文名称,如果没有的话,上拼音!然后点击保存银行信息就算ok了,然后退回到最开始的页面

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

2 为app添加内购产品

在iTunes Connect在你要添加内购的app中,进入到功能页面

Paste_Image.png

在你点击添加内购产品按钮后会有弹框,提示你选择类型,这个就要看你app的需求了

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

填写完审核信息后,点击右上角的“存储”按钮,就添加了一个内购产品~

3添加沙盒技术测试员

在iTunes Connect的用户和智能中选择“沙盒技术测试员”,填写信息保存以后就有一个测试员了

Paste_Image.png

直接鲁代码

#import <StoreKit/StoreKit.h>

@interface LLMIneCoinRechargeController ()<UIAlertViewDelegate,SKProductsRequestDelegate,SKPaymentTransactionObserver>

@property(nonatomic, strong)NSArray<LLMIneCoinRechargeModel *> *coinModelArr;

@property(nonatomic,copy)NSString * bvip;

@property(nonatomic,copy)NSString * coin;

@property(nonatomic, strong)SKProductsRequest *request;

@property(nonatomic,copy)NSString * productID;
@implementation LLMIneCoinRechargeController{
    
    UIAlertView* _alertView;
    NSString *coinStr;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    //一定要 开启内购检测
    [[SKPaymentQueue defaultQueue]addTransactionObserver:self];

}

// 在点击事件中去添加代码
- (void)payBtnClick:(UIButton *)sender{
    
    __block  LLMIneCoinRechargeModel *model = nil;
    
    [self.coinModelArr enumerateObjectsUsingBlock:^(LLMIneCoinRechargeModel * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.selected) {
            model = obj;
        }
    }];
    if (!model) {
        [HudManager showMessage:@"请选择金额"];
        return;
    }
    
    NSString *proID = [NSString stringWithFormat:@"charge%@",model.coinID];
    
    WS(weakSelf);
    if ([SKPaymentQueue canMakePayments]) {
        [weakSelf requestProductData:proID];
        weakSelf.productID = proID;
        
    }else{
        [HudManager showMessage:@"不允许程序内付费"];
    
    }
}

// 收到请求信息
- (void)requestProductData:(NSString *)productID{

    NSLog(@"-------------请求对应的产品信息----------------");
    
    [SVProgressHUD showWithStatus:nil maskType:SVProgressHUDMaskTypeBlack];
    
    NSArray *product = [[NSArray alloc] initWithObjects:productID,nil];
    
    NSSet *nsset = [NSSet setWithArray:product];
    _request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsset];
    
    _request.delegate = self;
    
    [_request start];
}


// 收到返回信息

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    NSArray *product = response.products;
    
    if (product.count == 0) {
        
        [SVProgressHUD dismiss];
        [HudManager showMessage:@"购买失败"];
        return;
    }
    
    SKProduct *prod = nil;
    for (SKProduct *pro in product) {
        
        if ([pro.productIdentifier isEqualToString:self.productID]) {
            prod = pro;
        }
    }
    
    // 发送购买请求
    if (prod != nil) {
        SKPayment *payment = [SKPayment paymentWithProduct:prod];
        [[SKPaymentQueue defaultQueue]addPayment:payment];
    }
}

// 失败回调
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    
    [SVProgressHUD dismiss];
    [HudManager showMessage:@"购买失败"];
    
}

// 支付后的反馈信息
- (void)requestDidFinish:(SKRequest *)request{

    [SVProgressHUD dismiss];
    
}

// 监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    
    for (SKPaymentTransaction *tran in transactions) {
        
        switch (tran.transactionState) {
                
            case SKPaymentTransactionStatePurchased:
                
                [self verifyPurchaseWithPaymentTransactionWith:tran];
                
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品已经添加进列表");
                break;
                
            case SKPaymentTransactionStateRestored:
                NSLog(@"已经购买过商品");
                
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                
                break;
            case SKPaymentTransactionStateFailed:
                
                NSLog(@"购买失败");
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [HudManager showMessage:@"购买失败"];
                
                break;
            default:
                break;
        }
    }
}

/**
 *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
 *
 */
-(void)verifyPurchaseWithPaymentTransactionWith:(SKPaymentTransaction *)tran{
    
    //从沙盒中获取交易凭证并且拼接成请求体数据
    NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];

    NSString  *receiptString = [receiptData base64EncodedStringWithOptions:0];
    
    if (!receiptString) {
        return;
    }

// 将base64 编码 发给后台服务器 做验证 以及是否是沙箱环境的参数
    
    NSDictionary *dict = [NSDictionary universalParameteWithDictionary:@{@"receipt":receiptString,
                                                                         @"sandbox":BOOLSandBox,
                                                                         }];
    
    [[LFBHTTPSessionManager manager]POSTwithURLString:KApplePay_Docheck parameters:dict success:^(id data) {
        
        [self loadData];
        [[SKPaymentQueue defaultQueue] finishTransaction:tran];
        
        
    } failed:^(NSError *error) {
//        [HudManager showMessage:error.description];
    }];
   
}


- (void)dealloc{
    
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    
}


代码基本上就上边的了

四 、要注意的事项!

1.bundleID要与iTunes Connect上你App的相同,不然是请求不到产品信息的

2.在沙盒环境进行测试内购的时候,要使用没有越狱的苹果手机。

3.在沙盒环境下真机测试内购时,请去app store中注销你的apple ID,不然发起支付购买请求后会直接case:SKPaymentTransactionStateFailed。使用沙盒测试员的账号时不需要真正花钱的。

4.如果只添加了一个沙盒测试员账号,当一个真机已经使用了这个账号,另一个真机再使用这个账号支付也是会发生错误的。那就去多建几个沙盒测试员账号使用不同的,反正也是免费的,填写也很快。

5.监听购买结果,当失败和成功时代码中要调用:

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

该方法通知苹果支付队列该交易已完成,不然就会已发起相同 ID 的商品购买就会有此项目将免费恢复的提示。

1

uideline 3.1.1 - Business
We noticed that your app enables the purchase of content, services, or functionality in the app by means other than the in-app purchase API, which is not appropriate for the App Store.

解决办法 这个被拒的原因很简单了 是要要使用内购了

2 切记 内购和Apple Pay 是两回事
3

We noticed that your in-app purchase product is set to an incorrect product type.

VIP is set to consumable.

Next Steps

Since the service offered by your app requires the user to make an advance payment to access the content or receive the service, please use the non-renewable subscription in-app purchase product type. Non-renewable subscription content must be made available to all iOS devices owned by a single user, as indicated in guideline 3.1.2 of the App Store Review Guidelines.

Note: The product type cannot be changed once an in-app purchase product has been created. Therefore, you will need to create a new in-app purchase product with the correct product type.

解决办法 : 虚拟币是消耗类型 但是一定要记住 会员VIP不是消耗类型的的 是非订阅类型的 更改你的VIP类型

4

Guideline 3.1.2 - Business

Your app offers a content subscription but does not have a mechanism in place to support the requirement that the subscription content be available to the user on all of their iOS devices.

Next Steps

To resolve this issue, please modify your app to include an optional user registration feature to deliver subscription content to all of the user's iOS devices. Such user registration must be made optional, not required. We also recommend indicating that registering is required to access the subscription content from their other iOS devices - and providing a way to register later, if users wish to have access to this content at a future time.

这个被拒的原因就感觉比较奇葩了 不知道是不是因为vip是费续期订阅的问题 因为我的项目中有游客入口 也就是不用登陆 也可以进去看到部分内容以及使用部分功能 : 所以应该是这个游客入口 和内购的一些规则冲突了
有两种解决办法:

1 就是游客模式 不能出现购买虚拟币以及购买VIP的页面 (我是这么改的 然后重新提交审核 就通过了)
2 有朋友是这么做的 也通过审核了 但个人感觉相对麻烦一点

就是 游客模式可以出现购买虚拟币以及购买VIP的页面 而且允许游客购买 购买后的凭证要保存在本地 然后当用户登录的时候 把这个凭证在发给服务器去验证 验证成功后 将虚拟币或者是VIp加到当前账户上

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,827评论 1 317
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,511评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,318评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,108评论 0 189
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,112评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,387评论 1 185
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,905评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,657评论 0 177
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,144评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,744评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,100评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,565评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,041评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,769评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,301评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,173评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,313评论 2 242

推荐阅读更多精彩内容