iOS开发 内购流程

注意必读
做了很多电商的项目了,都在用各大第三方支付,苹果已经在强制推行内购了,简单说明一下,如果你购买的商品,是在本app中使用和消耗的虚拟货币,就一定要用内购,否则会被拒绝上线,例如:游戏币,在线书籍,app中使用的道具等。如果购买的就是普通的商品,例如淘宝买东西等,就不需要用内购
现在苹果有一个 bug 就是你填写了银行卡信息之后修改不了只能发邮件给苹果等那边重置,发邮件给他们处理有多慢你懂得....

一.向appStore提交资料
第一步


1749914-225b1c250f5c5382.png

第二步


1749914-f446608344ff5775.png

第三步
1749914-36a3a9752f7f5f09.png

操作完第三步然后记得保存
1749914-880a5bfbd6dbcfd9.png

第四步


1749914-6f05cc110112bc39.png

第五步
先点击Contact Info 的Set Up
1749914-f154d3324adb13b8.png

第六步
1749914-2602d087074b8edc.png

第七步
都填写完记得保存,然后回到上一个页面
1749914-016ab4e0306b73ff.png

第八步
1749914-6187ffdd8a9be141.png

第九步


1749914-3b216495c5385c19.png

第十步
1749914-f4cd66f1a9ed0174.png

第十一步
1749914-3ade39e8d4e7829e.png

第十二步
填写银行卡信息

第十三步


1749914-3773f5c84faae624.png

第十四步


1749914-1defa02638af70b7.png

第十五步
1749914-e8dc793dacabe69c.png

第十六步


1749914-0f12a7caa8d83cae.png

第十七步
1749914-8bdcf11af6a38bd7.png

第十八步
1749914-523d414a44dc9293.png

第十九步
1749914-8345169d6d89b17a.png

第二十步
1749914-5a77feb067f7db7f.png

第二十一步


1749914-bc89f24567bc0b01.png

恭喜你现在你完成了添加的信息,下面创建内购项目
二.创建内购项目


1749914-7c4f22c9a13ac343.png

第一步


1749914-99a1f3573a5c1d9a.png

第二步
根据自己app的需要选择类型

第三步


1749914-e7b6de6a4931ea3c.png

第四步

一直下一步到结尾

三、添加项目内购测试帐号


1749914-84662a6fd31a7e7c.png

第一步


1749914-bdb0ef4137eb404f.png

四、上代码
第一步导入StoreKit.framework库

然后先看.h文件

#import <StoreKit/StoreKit.h>

typedef NS_ENUM(NSInteger, MyJellyShowType)
{
    MyJellyShowType_Push = 0,
    MyJellyShowType_Present = 1,
};

@interface MyJellyCurrencyViewController : BaseReuqestTableViewController<SKPaymentTransactionObserver,SKProductsRequestDelegate>
{
    int buyType;
}
@property (nonatomic) MyJellyShowType showType;

- (void)RequestProductData;

- (void)buy:(int)type;

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;

- (void)completeTransaction: (SKPaymentTransaction *)transaction;

- (void)failedTransaction: (SKPaymentTransaction *)transaction;

- (void)restoreTransaction: (SKPaymentTransaction *)transaction;

@end

然后看.m文件

//在内购项目中创的商品单号
#define ProductID_IAP0p600 @"guodong600"//60
#define ProductID_IAP1p1200 @"guodong1200" //1200
#define ProductID_IAP2p5000 @"guodong5000" //5000


enum{
    IAP0p600 = 0,
    IAP1p1200,
    IAP4p5000,
} buyCoinsT

#pragma mark -  点击购买按钮
-(void)buttonClickIndex:(NSInteger)index
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    if (index == 0)
    {
        buyCoinsTag = IAP0p600;
    }
    else if (index == 1)
    {
        buyCoinsTag = IAP1p1200;

    }else if (index == 2)
    {
        buyCoinsTag = IAP4p5000;
    }
    coinNum = [jellyMutAry objectAtIndex:index];
    [LoadingTool showLoadingTo:self.view isAfterDelay:NO];
    mainTableView.userInteractionEnabled = NO;
    [self buy:buyCoinsTag];
    

}

-(void)buy:(int)type{
    buyType = type;
    if ([SKPaymentQueue canMakePayments])
    {
        //允许程序内付费购买
        [self RequestProductData];
    }
    else
    {
        //不允许程序内付费购买
        mainTableView.userInteractionEnabled = YES;
        [LoadingTool hideProgressHud:self];
        UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"您的手机没有打开程序内付费购买"
                                                          delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
        [alerView show];
    }
}
#pragma mark - 请求对应的产品信息
-(void)RequestProductData
{
    NSArray *product = nil;
    switch (buyType)
    {
        case IAP0p600:
            product=[[NSArray alloc] initWithObjects:ProductID_IAP0p600,nil];
            break;
            
        case IAP1p1200:
            product=[[NSArray alloc] initWithObjects:ProductID_IAP1p1200,nil];
            break;
            
        case IAP4p5000:
            product=[[NSArray alloc] initWithObjects:ProductID_IAP2p5000,nil];
            break;
            
        default:
            break;
    }
    
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
    request.delegate=self;
    [request start];
}
#pragma mark - 收到产品反馈信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    
    
    SKProduct * selectedProduct = nil;
    selectedProduct = [response.products objectAtIndex:0];
    SKPayment *payment = [SKPayment paymentWithProduct:selectedProduct];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    
}
#pragma mark - 先商品添加进列表 再交易
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易结果
{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased://交易完成
            {
               
                [self  completeTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed://交易失败
            {
                //交易失败
                [self failedTransaction:transaction];
                [WarningTool showToastHintWithText:@"购买失败,请重试!"];
            }
                break;
            case SKPaymentTransactionStateRestored://已经购买过该商品
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing://商品添加进列表
                break;
            default:
                break;
        }
    }
    [LoadingTool hideProgressHud:self];
    mainTableView.userInteractionEnabled = YES;
}

#pragma mark - 交易完成调用
- (void) completeTransaction: (SKPaymentTransaction *)transaction{
    NSString *product = transaction.payment.productIdentifier;
    if ([product length] > 0)
    {
        NSArray *tt = [product componentsSeparatedByString:@"."];
        NSString *bookid = [tt lastObject];
        if ([bookid length] > 0)
        {
            [self recordTransaction:transaction];
        }
    }
    // Remove the transaction from the payment queue.结束交易
    [[SKPaymentQueue  defaultQueue] finishTransaction: transaction];
   
}

#pragma mark - 记录交易
-(void)recordTransaction:(SKPaymentTransaction *)transaction
{
    transactionIdentifier = transaction.transactionIdentifier;
    [self verifyPruchase];
    
//    NSString *product = transaction.payment.productIdentifier;
//    NSString *applicationUsername = transaction.payment.applicationUsername;
//    NSString *transactionIdentifier = transaction.transactionIdentifier;
   
//    // 验证凭据,获取到苹果返回的交易凭据
//    // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
//    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
//    // 从沙盒中获取到购买凭据
//    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
//    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//    NSString *result  =[[ NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
//    NSString *base64Str = [transaction.transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
}

- (void)verifyPruchase
{
    
    [LoadingTool showLoadingTo:self.view isAfterDelay:NO];
    // 验证凭据,获取到苹果返回的交易凭据
    // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 从沙盒中获取到购买凭据
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    // 发送网络POST请求,对购买凭据进行验证
    NSURL *url = [NSURL URLWithString:XTY_IAP_VerifyReceipt];
    // 国内访问苹果服务器比较慢,timeoutInterval需要长一点
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
    request.HTTPMethod = @"POST";
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    
    request.HTTPBody = payloadData;
    // 提交验证请求,并获得官方的验证JSON结果
    NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    // 官方验证结果为空验证失败
    if (result == nil)
    {
        [LoadingTool hideProgressHud:self];
        [WarningTool showToastHintWithText:@"验证失败"];
        return;
    }
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
    //验证成功
    if ([[dict objectForKey:@"status"] integerValue] == 0) {
        // 比对字典中以下信息基本上可以保证数据安全
        // bundle_id&application_version&product_id&transaction_id
        [self addJelly];
    }else if ([[dict objectForKey:@"status"] integerValue] == 21007){
        // 验证凭据,获取到苹果返回的交易凭据
        // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        // 从沙盒中获取到购买凭据
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
        // 发送网络POST请求,对购买凭据进行验证
        NSURL *url = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
        // 国内访问苹果服务器比较慢,timeoutInterval需要长一点
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
        request.HTTPMethod = @"POST";
        NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        
        NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
        NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
        
        request.HTTPBody = payloadData;
        // 提交验证请求,并获得官方的验证JSON结果
        NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
        // 官方验证结果为空验证失败
        if (result == nil)
        {
            [LoadingTool hideProgressHud:self];
            [WarningTool showToastHintWithText:@"验证失败"];
            return;
        }
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
        if ([[dict objectForKey:@"status"] integerValue] == 0) {
             [self addJelly];
        }else{
            [LoadingTool hideProgressHud:self];
            [WarningTool showToastHintWithText:@"验证失败"];
        }
        
        
    }
    else{
        [LoadingTool hideProgressHud:self];
        [WarningTool showToastHintWithText:@"验证失败"];

    }
}
//失败
- (void) failedTransaction: (SKPaymentTransaction *)transaction{
    if (transaction.error.code != SKErrorPaymentCancelled){}
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

//交易恢复处理
- (void) restoreTransaction: (SKPaymentTransaction *)transaction{
//    [self PurchasedTransaction:transaction];
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//     [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    
}
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{

}
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
    
    NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
    [ self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
    
}
-(void)dealloc{ 
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    //解除监听
    
}
- (void)addJelly
{
    NSMutableDictionary *mutDic = [[NSMutableDictionary alloc]init];
    [mutDic setObject:transactionIdentifier forKey:@"flowNO"];
    [mutDic setObject:coinNum forKey:@"coinNum"];
    [[NetworkRequests sharedClient]requestWithName:XTY_CoinCharge withParameters:mutDic successCallBack:^(MyJellyModel *myJellyModel) {
        [LoadingTool hideProgressHud:self];
        if ([myJellyModel.status integerValue] == 1)
        {
            [self beginRefreshing];
        }
        else
        {
            [WarningTool showToastHintWithText:myJellyModel.error];
        }
    } failCallback:^(NSString *error) {
        [LoadingTool hideProgressHud:self];
        [WarningTool showToastNetError];
    }];
}



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

推荐阅读更多精彩内容

  • 做了很多电商的项目了,都在用各大第三方支付,作为一名iOS开发,苹果已经在强制推行内购了,所以,你不得不了解一下啦...
    Xcode_6阅读 1,196评论 0 0
  • 做了很多电商的项目了,都在用各大第三方支付,作为一名iOS开发,苹果已经在强制推行内购了,所以,你不得不了解一下啦...
    睡不着的叶阅读 51,972评论 93 259
  • 为一名iOS开发,苹果已经在强制推行内购了,所以,你不得不了解一下啦。可能还有些人不知道哪种情况下一定要用到内购,...
    独酌丿红颜阅读 3,697评论 2 7
  • 早上起来,9.30叫男朋友起床,热水烧好了,10.00他要上班,我在床上躺着却莫名奇妙的哭了起来。原本自考的日子,...
    7dcc6255d03a阅读 253评论 0 1
  • 日子总是很忙碌,不知不觉又一天。晚上凉风习习,站在窗前做肩颈和焕然一新,想起邵老师的话,希望这个夏天舒服些。至于秋...
    misang阅读 165评论 0 3