app取消内购功能 ios开发
2014-03-20 10:41:07 harder321 阅读数 1042

现在有很多应用都使用了In-App Purchase,虽然对于很多用户来说,可能并不喜欢甚至讨厌这个模式,以为一点击就要从账户里扣钱。但是,应用内购买对于开发者而言不失为一种好的商业模式,而且人们也将越来越接受这种购买模式。

下面开始介绍一下应用内购买的基本原理和编程方法。

1、基本原理

这里参考了Apple的开发文档In-App Purchase Programming Guide


简要介绍一下整个流程:

Pre 0:在iTunesConnect中对于的App创建相应的产品,并在应用信息中加入这些产品。具体步骤之后介绍。

Step 1:应用内根据创建的产品的bundle identifier来获取产品的List。

Step 2:应用请求产品的信息。产品信息为SKProduct对象。

Step 3:App Store返回信息。在实际编程中,Step1,2,3是在一起的。通过创建SKProductsRequest得到SKProductsResponse。SKProducts信息就在SKProductsResponse的对象中,是其Property。

Step 4:在应用中显示产品信息给用户

Step 5:用户点击了一个产品。

Step 6:应用向App Store发出一个购买请求 Payment Request

Step 7:App Store处理请求,完成交易,并返回信息到应用。

Step 8:  应用获取信息然后根据交易情况将购买的内容解锁给用户使用。


这是In-App Purchase的一个基本的过程描述。在我们实际的编程过程中。对于这个产品列表,我们可能会选择直接提供给用户,而不是通过App Store获取信息。只有当用户点击了某个产品后,我们才开始去获取产品信息并完成购买。另一方面,在购买过程中,我们在应用中应该显示足够的提示信息,因此交易过程中的Notification也很重要。


下面开始StepByStep介绍整个具体的实现过程。这里只是介绍最基本的实现方法,以non-consumable产品为例。

Step 1:创建产品

首先要说明一下为了实现应用内购买,你的AppID就是com.companyname.appname必须是唯一的,不能带*。

在iTunesConnect中Manage My Applications中选择Manage In-App Purchases



有四种产品类型,具体详见开发文档。这里选择non-consumable,就是一次购买终身受用的产品。consumable就是消费类可以不断购买的,这种在游戏中比较常见。


上面是产品的详细信息填写。这里特别注意的是ProductID的填写,其实就是ProductIdentifier,这个和应用的BundleIdentifier类似,必须独一无二,一般的做法是填写成com.companyname.appname.productname,当然从本质上讲可以是任意字符串,只要独一无二就可以了。这个ProductID是之后在程序中获取产品信息的依据。其他方面的信息填写很简单,这里不在费述。

Step 2:在应用版本信息中加入产品

进入到应用页面,点击View Detail,然后在下面可以看到

In-App Purchases,点击Edit然后加入之前创建的Products。

Step 3:创建测试User

为了在开发阶段测试In-App Purchase,Apple为我们提供了Test User功能,通过它,可以在开发时用这个账号免费实现应用内购买。

具体就是在iTunesConnect首页,点击Manage Users

点击Test User进行创建。

Step 4:开始编程。在Xcode中要加入StoreKit.framework,通过它来实现功能

Step 5:一般我们会单独创建一个类来实现应用内购买的功能。由于这个是教程,而不是案例,所以不打算把整个类的编写都搬进来。只是介绍一下重要的东西和流程。

在类中要加入

<SKProductsRequestDelegate,SKPaymentTransactionObserver>

对于SKPaymentTransactionObserver这个东西可以监测交易的整个过程,即使交易时退出应用,交易也可以继续进行,当然要回到应用内的页面才能最后完成交易,显示产品相应内容。类的初始化应加入

[[SKPaymentQueuedefaultQueueaddTransactionObserver:self];

加入这句代码来实现TransactionObserver的功能,后面有相应的Methods可以加入

paymentQueue:开头

Step 6:下面的介绍不局限在一个类的编写,而是按照购买流程。假设我们已经编写好了一个应用内购买的类,然后我们要实现购买。首先就是Request Products。

_productRequest = [[SKProductsRequestalloc]initWithProductIdentifiers:_productIdentifiers];

_productRequest.delegate =self;

[_productRequest start];


一个完整的请求如上,对于productsIdentifiers,这是一个Set,就是在这里创建一个set加入各个ProductIdentifer

 NSSet *productIdentifiers = [NSSetsetWithObjects:

                                    K_CAMERA_ANGLE_MODE,

                                    K_SLOPE_ANGLE_MODE,

                                    K_DIHEDRAL_ANGLE_MODE,

                                    K_LINE_PLANE_ANGLE_MODE,

                                    nil];

然后就是request delegate的methods

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

   NSArray *skProducts = response.products;

    // process....

}


- (void)request:(SKRequest *)request didFailWithError:(NSError *)error

{

   // process....   

}


请求成功,就能获取Products,一个NSArray,里面就是SKProduct 对象的产品信息了。产品信息有名称,价格,等等,很容易找到。这些东西只是在显示信息时有用,购买时不需要,只要用SKProduct就行了

Step 7:购买

SKPayment *payment = [SKPaymentpaymentWithProduct:product];

[[SKPaymentQueuedefaultQueueaddPayment:payment];

代码如上。然后就开始连接App Store了。主要看下面

#pragma mark - SKPaymentTransactionObserver

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

{

   for (SKPaymentTransaction *transactionin transactions) {

       switch (transaction.transactionState) {

            caseSKPaymentTransactionStatePurchased:

                [selfcompleteTransaction:transaction];

               break;

            caseSKPaymentTransactionStateFailed:

                [selffailedTransaction:transaction];

               break;

            caseSKPaymentTransactionStateRestored:

                [selfrestoreTransaction:transaction];

                

           default:

               break;

        }

    }

}


这里说一下上面的第三种交易状态Restore,恢复。这个是这样的。如果有些人在iPhone上用一个账号购买了一个产品,那么在iPad上又下载了这个应用,还要再重新购买吗?不用了,通过Restore在App Store中检测你这个账号的购买记录,如果有购买记录存在,那就不用再次购买了,直接restoreTransaction。


接下来就是根据购买的状态分别进行处理

- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
    // Your application should implement these two methods.
    [self recordTransaction:transaction];
    [self provideContent:transaction.payment.productIdentifier];
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
一般我们用NSUserDefaults来进行交易的记录就行了。

注意程序中finishTransaction这一行代码,这样TransactionObserver就不再监测这个交易了。

其他状态Restore,failed都是差不多的处理,这些代码在开发文档中有。


当然,对于restore,还有一个Method要注意

- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error

如果restore失败,可以显示相应的信息提示


Step 8:附加

在整个购买过程中,我们一般要给用户一下提示信息,比如等待,比如正在连接,比如交易已完成。要实现这些功能,就应该用notification,在上面的交易环节加入postNotification,然后对notification进行有效处理。本文只讲In-App Purchase,关于notification的编程不做介绍。


基本上,通过上面的环节就能完成整个应用内购买了。当然,从安全性上考虑,Apple在交易完成后会发送验证信息,通过发送验证信息给App Store来判断这笔交易是否出自App Store,从而确认交易的合法性。

关于这方面,Apple 有一个代码包提供了validationController来实现很方便的验证。

2015-12-10 17:47:24 ismilesky 阅读数 705

一、 常用产品介绍

非消耗品(Nonconsumable)

指的是在游戏中一次性购买并拥有永久访问权的物品或服务。非消耗品物品可以被用户再次下载,并且能够在用户的所有设备上使用

消耗品(Consumable)

专为支持可消耗的物品或服务设计的,消耗品购买不可被再次下载,根据其特点,消耗品不能在用户的设备之间跨设备使用,除非自定义服务在用户的账号之间共享这些信息

二、配置iTunes Connect

1⃣在Itunes Connect里面创建自己的应用

这里写图片描述

PS: 套装ID是应用的BundleID,SKU最好和套装ID一致

2⃣创建应用内付费项目

这里写图片描述

之后设置好价格和Product ID以及购买介绍和截图即可,这里的Product ID是需要记住的,后面开发的时候需要。

3⃣添加sandbox付费的测试用户

这里写图片描述

这里写图片描述

PS: 这个测试账号不要在非测试情况下使用,切记啊!!!!!这个测试账号不要在非测试情况下使用,切记啊!!!!!这个测试账号不要在非测试情况下使用,切记啊!!!!! (重要事情说三遍)

4⃣填写相关的税务,银行,联系人信息

这里写图片描述

三、大致流程

这里写图片描述

下面上代码:

#define kProductID @"buy_ID"

#import <StoreKit/StoreKit.h>
#import "InAppPurchaseViewController.h"

#import "TYAlertView.h"

@interface InAppPurchaseViewController () <SKPaymentTransactionObserver, SKProductsRequestDelegate>
@property (weak, nonatomic) IBOutlet UITextField *productTetxField;
@property (weak, nonatomic) IBOutlet UILabel *fhCoinAmountLabel;

@property (strong, nonatomic) NSString *fhCoin;

@property (assign, nonatomic) int newFhCoin;
@property (assign, nonatomic) int remainFhCoin;

@end

@implementation InAppPurchaseViewController

- (void)viewDidLoad {
    [self showBackButton];
    [super viewDidLoad];
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    self.productTetxField.text = kProductID;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 判断设置里的内购开关是否打开
    if (![SKPaymentQueue canMakePayments]) {
        [TYAlertView alertViewWithTitle:@"温馨提示" message:@"您可能没有打开App内购项目,是否进行设置?" confirmButtonTitle:@"前往" cancelButtonTitle:@"不,算了" confirmBlock:^{
            NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
            if ([[UIApplication sharedApplication] canOpenURL:url]) {
                [[UIApplication sharedApplication] openURL:url];
            }
        } cancelBlock:nil];
    }
}

#pragma mark - actions
- (IBAction)purchaseProduct:(UIButton *)sender {
    NSString *product = self.productTetxField.text;
    if ([SKPaymentQueue canMakePayments]) {
        [self requestProductData:product];
    }
}

/**
 *  点击之后,恢复购买
 */
- (IBAction)restoreBuy:(id)sender {
    // 恢复所有的购买
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

#pragma mark - Methods
//请求商品
- (void)requestProductData:(NSString *)type {
    // 取出所有想要销售商品的productId,放入到NSSet当中
    NSArray *product = [[NSArray alloc] initWithObjects:type, nil];
    NSSet *nsset = [NSSet setWithArray:product];
    // 创建SKProductsRequest请求对象
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    // 开始请求
    [request start];
}

/*
 *  请求可销售的商品,当请求回来的时候,调用该方法
 *
 *  @param response 请求的回应(里面存放所有的可销售商品)
 */
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSArray *product = response.products;
      if([product count] == 0) return;
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
    SKProduct *p = nil;
    for (SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        self.fhCoin = [pro.localizedTitle substringToIndex:2];
        NSLog(@"%@", [pro localizedTitle]); 
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]); 
        NSLog(@"%@", [pro productIdentifier]); // 产品标示
        if([pro.productIdentifier isEqualToString:self.productTetxField.text]) {
            p = pro;
        }
    }
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    NSLog(@"发送购买请求");
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
}

- (void)requestDidFinish:(SKRequest *)request {
}

// 成功
- (void)completeTransactions:(SKPaymentTransaction *)transaction {
    self.newFhCoin = [NSString stringWithFormat:@"%@",self.fhCoin].intValue;
    self.remainFhCoin = [NSString stringWithFormat:@"%@",self.fhCoinAmountLabel.text].intValue;
    _remainFhCoin += _newFhCoin;
    self.fhCoinAmountLabel.text = [NSString stringWithFormat:@"%d",_remainFhCoin];

    NSString *product = transaction.payment.productIdentifier;
    if ([product length] > 0) {
        NSArray *tt = [product componentsSeparatedByString:@"."];
        NSString *bookid = [tt lastObject];
        if ([bookid length] > 0) {
            [self recordTransaction:bookid];
            [self provideContent:bookid];
        }
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

// 失败
- (void)failedTransaction: (SKPaymentTransaction *)transaction {
    if (transaction.error.code != SKErrorPaymentCancelled) {

    }
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

//记录交易
-(void)recordTransaction:(NSString *)product{
    NSLog(@"-----记录交易--------");
}

//处理下载内容
-(void)provideContent:(NSString *)product{
    NSLog(@"-----下载--------");
}

#pragma mark - SKPaymentTransactionObserver
/**
 *  当交易队列中的交易状态发生改变的时候,会调用该方法
 *
 *  @param transactions 交易队列中的所有交易
 */

//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {

    /*
     SKPaymentTransactionStatePurchasing, 正在购买
     SKPaymentTransactionStatePurchased, 已经购买,购买成功时,需要终止交易,否则会一直停留在队列中
     SKPaymentTransactionStateFailed, 购买失败,由于网络等原因购买失败,无需终止交易
     SKPaymentTransactionStateRestored, 恢复购买,恢复购买成功后,需要终止交易,否则交易会一直停留在队列中
     SKPaymentTransactionStateDeferred 用户未决定的,可能由于账号问题,未购买,也未失败
     */

    for(SKPaymentTransaction *tran in transactions) {
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:
                [self completeTransactions:tran];
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品添加进列表");
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"已经购买过商品");
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:tran];
                break;
            default:
                break;
        }
    }
}


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

@end

可能会遇到的坑

 **错误提示:**
  error Domain=SKErrorDomain Code=0 “Cannot connect to iTunes Store
为什么会这样 ??? 
  • 一般遇到这个错误要么是你的 Purchase Identifier填写错误. (不过这种一般报错No Products Available
    也就是你压根还进不购买这个代理 )

  • 还有一个坑能就是你手机的AppID没有注销, 你测试必须使用沙盒账号.

解决方法 :

进入设置 ---> iTunes Store  AppStore --> 注销你当前的账号
然后再继续触发内购的Action,这个时候会弹出让你选择AppID, 输入你在iTunes Connect 注册的沙盒账号和密码. 之后它机会弹出你的内购价格和一些你的内购描述.
2018-09-19 17:04:00 weixin_34310785 阅读数 29
最近帮别人做内购开发,之前也做过内购开发,内购开发也是iOS开发人员必备技能之一,说说开发用的内容。
开发申请流程这些东西不介绍了,这个一般都是负责人去弄,我们做的主要就是代码部分,这里小编要说一下要和服务器那边沟通好,沟通是很有必要的,注意 苹果二次验证一定要在服务器去做,这也是趋于安全考虑,测试可以在前端做,正式的必须在后台,新的产品负责可能会要求开发人员在前端做二次验证,这时候我们可以给他们提这个问题,(注意方式)代码如下:

pragma mark ===================== 内购支付的

/*
stringType=2购买课程
payCode ,商品id payCode就对应着苹果后台的商品ID

*/

 -(void)applePay:(NSString *)payCode
   {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

if([SKPaymentQueue canMakePayments])
{
    ////请求苹果商品
    NSArray *product = [[NSArray alloc] initWithObjects:payCode,nil];
    
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
    
}
else
{
    // NSLog(@"不允许程序内付费");
}
}

//收到产品返回信息

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

NSArray *product = response.products;

if([product count] == 0)//-没有商品
{
    //        NSLog(@"--------------没有商品------------------");
    return;
}

SKProduct *p = nil;
// appleID ,商品id appleID就对应着苹果后台的商品ID
NSString *appleID=self.applePayDic[@"orderNo"];

for (SKProduct *pro in product)
{
    //后台消费条目的ID与我这里需要请求的一样(用于确保订单的正确性)
    if([pro.productIdentifier isEqualToString:appleID])
    {
        p = pro;
    }
}
if (p !=nil) {
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    
    //发送购买请求
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}
 }

//请求失败

     - (void)request:(SKRequest *)request didFailWithError:(NSError *)error
      {
   NSLog(@"请求失败");
//    [SVProgressHUD showErrorWithStatus:@"支付失败"]    
         }

//反馈请求的产品信息结束后

 - (void)requestDidFinish:(SKRequest *)request{

NSLog(@"反馈请求的产品信息结束后");
  }

//监听购买结果

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction)
{
    switch (tran.transactionState)
    {
        case SKPaymentTransactionStatePurchased:
        {
            // 发送到苹果服务器验证凭证
            //让服务器做二次认证,服务器发送到苹果服务器验证凭证
            [self completeTransaction:tran];
            NSLog(@"让服务器做二次认证,服务器发送到苹果服务器验证凭证");
            [[SKPaymentQueue defaultQueue] finishTransaction:tran];

        }
            break;
        case SKPaymentTransactionStatePurchasing:
        {
        }
            break;
        case SKPaymentTransactionStateRestored:
        {
            [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            
        }
            break;
        case SKPaymentTransactionStateFailed://购买失败
        {
            [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            
        }
            break;
        default:
            break;
    }
}
            }

//交易结束 然后把凭证传给服务器的数据,让服务器做二次认证

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

[self creatIphonePayToSeavesTwoCertification];

     }

pragma mark ===== 传给服务器的数据,让服务器做二次认证

     -(void)creatIphonePayToSeavesTwoCertification{
    //=======传给服务器的数据,让服务器做二次认证-,返回结果展示给用户===========================
//从沙盒中获取交易凭证并且拼接成请求体数据
NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
          //    //交易凭证
NSString *Receipt_data =[receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

//        NSLog(@"传给服务器的数据,让服务器做二次认证-,返回结果展示给用户-========%@",Receipt_data);

[self creatTwoSeavesVerifyWithString:Receipt_data];


      }

pragma mark===== 服务器做二次认证

/*
VerifyStr 交易凭证

post方式
money:金额
subject:购买内容
businessId :订单编号
userId :用户id
type:类型(1:报名 2:课程)
tradeNo:苹果支付流水号
以上都是必须参数 少一个会报参数错误
*/

  -(void)creatTwoSeavesVerifyWithString:(NSString *)VerifyStr{
   // 这里服务器做二次认证
              }

这是我做的,网上也有很多相关内容,如果有疑问可以致信小编,欢迎提问题........

2016-11-10 13:10:02 HDFQQ188816190 阅读数 1608

一. 创建测试App


创建App-1


首先你需要登录 App的ItunesConnection,你会看到如下界面

http://t.cn/Rcj7Nes

简单的介绍一下这几个选项


  • 1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等。

  • 2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考。

  • 3.付款和财务报告显示的是你的收入以及付款等相关信息。

  • 4.iAd主要是跟广告有关,开发者可以登录到Workbench,通过iAd对应用的广告进行控制。

  • 5.用户和职能用于生成相应账号,例如苹果沙河测试账号。

  • 6.协议,税务和银行业务则是你银行相关账户的信息设置。


在这里我们选择第一个选项,我的App, 然后点击左上角的加号,新建一个用来测试用的App。


我们会看到弹出的窗口


在这里有几个需要填写的地方,名称自己取,平台IOS,语言选择了简体中文,套装ID也就是你的Bundle Identifier,需要你在Certificates页面(https://developer.apple.com/account/ios/certificate/certificateList.action) 申请BundleID,在这里简单的给大家介绍一下。


申请BundleID


打开Certificates页面 ,在左侧选择 Identifiers,并点击加号,申请一个新的Identifiers。



在这里Name可以随意填写,我填写的是TestAppStroeTestDemo,而用来使用的BundleID,我们在这里必须选择第一个选项唯一的,不用选择通配。在下面的选项中, 我们只需要勾选一个 Apple Pay即可,其他选项看自己需求,我在这里只选择了它。

之后一路Done即可。


创建App-2


之后我们回到创建App,选择好自己刚创建的 BundleID ,填写SKU, SKU是你App的专用ID,我在这里随意填写,直接复制了App名。点击创建,我们的测试App则创建成功。


二.添加内购


App创建好之后,我们打开创建的App,在左上角选择功能,会看到左侧的App 内购买项目。我们点击右下角的加号,为App添加内购项目。

之后我们会看到类型的选项,如下图


官方的注释写的很清楚了,只在这里简单的说下前两种


消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买
非消耗型项目 就像你在App Store购买App,买了一次之后就不用再买第二次,你拥有永久使用权。


在这里为了方便测试,我们选择第一种 消耗型项目 。来到内购项目填写页面,如下图。

这里有几个选项,需要填写商品名称,产品ID以及价格等级,简单说明一下


1.商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。
2.产品ID是比较重要的,由项目自定义,只要唯一即可,因为测试,我在这里随便填写的123,在实际应用中,一定要认真填写。
3.价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择


接下来是语言选择,和上传快照如下图


点击添加语言,填写名称和描述,这里我们依然选择简体中文,如下


三.申请沙盒测试账号(用来测试购买项目)


这个账号,是利用苹果的沙盒测试环境来模拟AppStore的购买流程,你肯定不会想要用真实RMB去购买测试吧?

首先我们回到iTunes Connect(https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/)中,在这里我们选择用户和职能。



所有信息都可以随意填写,不用管是否真实。
App Store地区选择,一定要选对,它对应的是你创建的App的地区, 你App是中国的话, 在这里我们依然选择中国。
此账号只能用来测试,不要在正式的appstore上使用


填写完毕,点击保存后,我们则生成一个测试账号,当然这个账号是可以随时删除和添加的。


四.核心代码


之后终于到了我们撸代码的时候了,点开你的Xcode创建你的项目!


首先我们需要在项目工程中加入“storekit.framework”,加入头文件#import
在.h文件中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”监听机制
代码很简单,直接在.m文件在中填写,添加了二次验证,防止越狱手机等内购。如下,


    //

    //  ViewController.m

    //  内购

    //

    //  Created by Ely on 15/12/15.

    //  Copyright © 2015年 Ely. All rights reserved.

    //

     

    #import "ViewController.h"

    #import 

    #import "SVProgressHUD.h"

     

    @interface ViewController ()SKPaymentTransactionObserver,SKProductsRequestDelegate>

    @property (nonatomic,strong) NSArray *profuctIdArr;

    @property (nonatomic,copy) NSString *currentProId;

     

    @end

     

    @implementation ViewController

     

    - (void)viewDidLoad {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        

        [self createPay];

    }

     

     

    - (void)createPay

    {

        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

        self.profuctIdArr = @[@"123"];

        

        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

        button.frame = CGRectMake(100, 100, 100, 100);

        button.backgroundColor = [UIColor greenColor];

        [button setTitle:@"6元" forState:UIControlStateNormal];

        [button addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];

        button.tag = 100;

        [self.view addSubview:button];

    }

     

    - (void)btnClick:(UIButton *)button

    {

        NSString *product = self.profuctIdArr[button.tag-100];

        _currentProId = product;

        if([SKPaymentQueue canMakePayments]){

            [self requestProductData:product];

        }else{

            NSLog(@"不允许程序内付费");

        }

    }

     

    //请求商品

    - (void)requestProductData:(NSString *)type{

        NSLog(@"-------------请求对应的产品信息----------------");

        

        [SVProgressHUD showWithStatus:nil maskType:SVProgressHUDMaskTypeBlack];

        

        NSArray *product = [[NSArray alloc] initWithObjects:type,nil];

        

        NSSet *nsset = [NSSet setWithArray:product];

        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];

        request.delegate = self;

        [request start];

        

    }

     

    //收到产品返回信息

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

        

        NSLog(@"--------------收到产品反馈消息---------------------");

        NSArray *product = response.products;

        if([product count] == 0){

            [SVProgressHUD dismiss];

            NSLog(@"--------------没有商品------------------");

            return;

        }

        

        NSLog(@"productID:%@", response.invalidProductIdentifiers);

        NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);

        

        SKProduct *p = nil;

        for (SKProduct *pro in product) {

            NSLog(@"%@", [pro description]);

            NSLog(@"%@", [pro localizedTitle]);

            NSLog(@"%@", [pro localizedDescription]);

            NSLog(@"%@", [pro price]);

            NSLog(@"%@", [pro productIdentifier]);

            

            if([pro.productIdentifier isEqualToString:_currentProId]){

                p = pro;

            }

        }

        

        SKPayment *payment = [SKPayment paymentWithProduct:p];

        

        NSLog(@"发送购买请求");

        [[SKPaymentQueue defaultQueue] addPayment:payment];

    }

     

    //请求失败

    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{

        [SVProgressHUD showErrorWithStatus:@"支付失败"];

        NSLog(@"------------------错误-----------------:%@", error);

    }

     

    - (void)requestDidFinish:(SKRequest *)request{

        [SVProgressHUD dismiss];

        NSLog(@"------------反馈信息结束-----------------");

    }

    //沙盒测试环境验证

    #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"

    //正式环境验证

    #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"

    /**

     *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题

     *

     */

    -(void)verifyPurchaseWithPaymentTransaction{

        //从沙盒中获取交易凭证并且拼接成请求体数据

        NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];

        NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];

        

        NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串

        

        NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据

        NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];

        

        

        //创建请求到苹果官方进行购买验证

        NSURL *url=[NSURL URLWithString:SANDBOX];

        NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];

        requestM.HTTPBody=bodyData;

        requestM.HTTPMethod=@"POST";

        //创建连接并发送同步请求

        NSError *error=nil;

        NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];

        if (error) {

            NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);

            return;

        }

        NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

        NSLog(@"%@",dic);

        if([dic[@"status"] intValue]==0){

            NSLog(@"购买成功!");

            NSDictionary *dicReceipt= dic[@"receipt"];

            NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];

            NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识

            //如果是消耗品则记录购买数量,非消耗品则记录是否购买过

            NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];

            if ([productIdentifier isEqualToString:@"123"]) {

                int purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量

                [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];

            }else{

                [defaults setBool:YES forKey:productIdentifier];

            }

            //在此处对购买记录进行存储,可以存储到开发商的服务器端

        }else{

            NSLog(@"购买失败,未通过验证!");

        }

    }

    //监听购买结果

    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{

        

        

        for(SKPaymentTransaction *tran in transaction){

            

            

            

            switch (tran.transactionState) {

                case SKPaymentTransactionStatePurchased:{

                    NSLog(@"交易完成");

                    [self verifyPurchaseWithPaymentTransaction];

                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];

                    

                }

                   

                    

     

                    

                    break;

                case SKPaymentTransactionStatePurchasing:

                    NSLog(@"商品添加进列表");

                    

                    break;

                case SKPaymentTransactionStateRestored:{

                    NSLog(@"已经购买过商品");

                    

                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];

                }

                    break;

                case SKPaymentTransactionStateFailed:{

                    NSLog(@"交易失败");

                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];

                    [SVProgressHUD showErrorWithStatus:@"购买失败"];

                }

                    break;

                default:

                    break;

            }

        }

    }

     

    //交易结束

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

        NSLog(@"交易结束");

        

        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    }

     

     

    - (void)dealloc{

        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

    }

     

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

        // Dispose of any resources that can be recreated.

    }

     

    @end

在这里需要注意几点,


  • 代码中的self.profuctIdArr所填写的是你的购买项目的的ID,我这里是当时填写的ID 123。

  • 在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。

  • 沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。

  • 请务必使用真机来测试,一切以真机为准。

  • 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。

  • 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号

  • 二次验证,请注意区分宏, 测试用沙盒验证


在这里附上截图:


点击购买按钮


选择使用现有Apple ID,填写测试账号


确认是否购买


购买成功


最后打印输出日志




2016-06-10 09:11:30 wsdxsYB 阅读数 1439

本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助。 
本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统。 
部分地方直接摘自网络,基本上是我的逻辑,省时省心省力。

一. 创建测试App

首先你需要登录 App的ItunesConnection,你会看到如下界面 
这里写图片描述

简单的介绍一下这几个选项 
1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等。 
2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考。 
3.付款和财务报告显示的是你的收入以及付款等相关信息。 
4.iAd主要是跟广告有关,开发者可以登录到Workbench,通过iAd对应用的广告进行控制。 
5.用户和职能用于生成相应账号,例如苹果沙河测试账号。 
6.协议,税务和银行业务则是你银行相关账户的信息设置。 
在这里我们选择第一个选项,我的App, 然后点击左上角的加号,新建一个用来测试用的App。 
这里写图片描述

点新建 App,会出现新建窗口; 
这里写图片描述

在这里有几个需要填写的地方,名称自己取,平台IOS,语言选择了简体中文,套装ID也就是你的Bundle Identifier,需要你在Certificates页面 申请BundleID,SKU可以理解为用户看一看到的唯一标示,会体现在你的app的App Store的链接中。

二.添加内购

App创建好之后,我们打开创建的App,在左上角选择功能,会看到左侧的App 内购买项目。我们点击右下角的加号,为App添加内购项目。 
这里写图片描述

之后我们会看到类型的选项,如下图

这里写图片描述

官方的注释写的很清楚了,只在这里简单的说下前两种: 
- 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买 
- 非消耗型项目 就像你在App Store购买App,买了一次之后就不用再买第二次,你拥有永久使用权。 
在我们的app中,是充值会员,所以选择的是第一种,可以无限次购买。

这里写图片描述

这里有几个选项,需要填写商品名称,产品ID以及价格等级,简单说明一下 
1. 商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。 
2. 产品ID是比较重要的,由项目自定义,只要唯一即可,因为测试,我在这里随便填写的123,在实际应用中,一定要认真填写。 
3. 价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择 
接下来是语言选择,和上传快照如下图

这里写图片描述

点击添加语言,填写名称和描述,这里我们依然选择简体中文,如下

这里写图片描述

审核备注,根据实际情况填写,可以不填。而下面的屏幕快照,则是商品图片,以像素为单位,最低尺寸为321,390,尺寸需求如下图,上传即可。

这里写图片描述

到这里为止, 我们的内购项目则添加完成。接下来则是测试阶段了。

三.申请沙盒测试账号(用来测试购买项目)

这个账号,是利用苹果的沙盒测试环境来模拟AppStore的购买流程,你肯定不会想要用真实RMB去购买测试吧? 

首先我们回到iTunes Connect中,在这里我们选择用户和职能。

这里写图片描述

然后在上面的第三个选项沙箱技术测试员中点击加号,添加测试员。

这里写图片描述

在信息填写页面只简单说两句。 
所有信息都可以随意填写,不用管是否真实。 
App Store地区选择,一定要选对,它对应的是你创建的App的地区, 你App是中国的话, 在这里我们依然选择中国。 
此账号只能用来测试,不要在正式的appstore上使用 
填写完毕,点击保存后,我们则生成一个测试账号,当然这个账号是可以随时删除和添加的。

这里写图片描述

之后终于到了写代码的时候了,点开你的Xcode创建你的项目! 
大部分代码都可以在.m文件中实现。

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;">"ViewController.h"</span></span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;"><StoreKit/StoreKit.h></span></span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;">"SVProgressHUD.h"</span></span>

<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span> ()<<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">SKPaymentTransactionObserver</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">SKProductsRequestDelegate</span>></span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>,copy) <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *currentProId;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span>

<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@implementation</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span></span>

- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)viewDidLoad {
    [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span> viewDidLoad];

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIButton</span> *button = [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIButton</span> buttonWithType:UIButtonTypeCustom];
    button<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.frame</span> = CGRectMake(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>);
    button<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.backgroundColor</span> = [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIColor</span> greenColor];
    [button setTitle:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"6元"</span> forState:UIControlStateNormal];
    [button addTarget:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> action:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@selector</span>(btnClick:) forControlEvents:UIControlEventTouchDown];
    [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.view</span> addSubview:button];
}


- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)btnClick:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIButton</span> *)button
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>];
    _currentProId = @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"123"</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>([SKPaymentQueue canMakePayments]){
        [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> requestProductData:product];
    }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"不允许程序内付费"</span>);
    }
}

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//去苹果服务器请求商品</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)requestProductData:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *)type{
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"-------------请求对应的产品信息----------------"</span>);

    [SVProgressHUD showWithStatus:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span> maskType:SVProgressHUDMaskTypeBlack];

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> *product = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> alloc] initWithObjects:type,<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];

    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>;
    [request start];

}

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//收到产品返回信息</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"--------------收到产品反馈消息---------------------"</span>);
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> *product = response<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.products</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>([product count] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>){
        [SVProgressHUD dismiss];
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"--------------没有商品------------------"</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
    }

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"productID:%@"</span>, response<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.invalidProductIdentifiers</span>);
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"产品付费数量:%lu"</span>,(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span>)[product count]);

    SKProduct *p = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (SKProduct *pro in product) {
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, [pro description]);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, [pro localizedTitle]);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, [pro localizedDescription]);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, [pro price]);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, [pro productIdentifier]);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>([pro<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.productIdentifier</span> isEqualToString:_currentProId]){
            p = pro;
        }
    }

    SKPayment *payment = [SKPayment paymentWithProduct:p];

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"发送购买请求"</span>);
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//请求失败</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)request:(SKRequest *)request didFailWithError:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSError</span> *)error{
    [SVProgressHUD showErrorWithStatus:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"支付失败"</span>];
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"------------------错误-----------------:%@"</span>, error);
}

- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)requestDidFinish:(SKRequest *)request{
    [SVProgressHUD dismiss];
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"------------反馈信息结束-----------------"</span>);
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//沙盒测试环境验证</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//正式环境验证</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
 *
 */</span>
-(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)verifyPurchaseWithPaymentTransaction{
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从沙盒中获取交易凭证并且拼接成请求体数据</span>
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSURL</span> *receiptUrl=[[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSBundle</span> mainBundle] appStoreReceiptURL];
    NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//转化为base64字符串</span>

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *bodyString = [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> stringWithFormat:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"{\"receipt-data\" : \"%@\"}"</span>, receiptString];<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//拼接请求数据</span>
    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];


    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建请求到苹果官方进行购买验证</span>
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSURL</span> *url=[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSURL</span> URLWithString:SANDBOX];
    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
    requestM<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.HTTPBody</span>=bodyData;
    requestM<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.HTTPMethod</span>=@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"POST"</span>;
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建连接并发送同步请求</span>
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSError</span> *error=<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;
    NSData *responseData=[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSURLConnection</span> sendSynchronousRequest:requestM returningResponse:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span> error:&error];
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (error) {
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"验证购买过程中发生错误,错误信息:%@"</span>,error<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.localizedDescription</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
    }
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>,dic);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>([dic[@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"status"</span>] intValue]==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>){
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"购买成功!"</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *dicReceipt= dic[@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"receipt"</span>];
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *dicInApp=[dicReceipt[@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"in_app"</span>] firstObject];
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *productIdentifier= dicInApp[@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"product_id"</span>];<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//读取产品标识</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果是消耗品则记录购买数量,非消耗品则记录是否购买过</span>
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSUserDefaults</span> *defaults=[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSUserDefaults</span> standardUserDefaults];
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ([productIdentifier isEqualToString:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"123"</span>]) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> purchasedCount=[defaults integerForKey:productIdentifier];<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//已购买数量</span>
            [[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSUserDefaults</span> standardUserDefaults] setInteger:(purchasedCount+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) forKey:productIdentifier];
        }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
            [defaults setBool:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> forKey:productIdentifier];
        }
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//在此处对购买记录进行存储,可以存储到开发商的服务器端</span>
    }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"购买失败,未通过验证!"</span>);
    }
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//监听购买结果</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> *)transaction{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(SKPaymentTransaction *tran in transaction){
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (tran<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.transactionState</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> SKPaymentTransactionStatePurchased:{
                <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"交易完成"</span>);
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 发送到苹果服务器验证凭证</span>
                [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> verifyPurchaseWithPaymentTransaction];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];

            }
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> SKPaymentTransactionStatePurchasing:
                <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"商品添加进列表"</span>);

                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> SKPaymentTransactionStateRestored:{
                <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"已经购买过商品"</span>);

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> SKPaymentTransactionStateFailed:{
                <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"交易失败"</span>);
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [SVProgressHUD showErrorWithStatus:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"购买失败"</span>];
            }
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span>:
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
        }
    }
}

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//交易结束</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)completeTransaction:(SKPaymentTransaction *)transaction{
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"交易结束"</span>);

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>];
}

- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)didReceiveMemoryWarning {
    [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span> didReceiveMemoryWarning];
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Dispose of any resources that can be recreated.</span>
}

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li><li style="box-sizing: border-box; padding: 0px 5px;">140</li><li style="box-sizing: border-box; padding: 0px 5px;">141</li><li style="box-sizing: border-box; padding: 0px 5px;">142</li><li style="box-sizing: border-box; padding: 0px 5px;">143</li><li style="box-sizing: border-box; padding: 0px 5px;">144</li><li style="box-sizing: border-box; padding: 0px 5px;">145</li><li style="box-sizing: border-box; padding: 0px 5px;">146</li><li style="box-sizing: border-box; padding: 0px 5px;">147</li><li style="box-sizing: border-box; padding: 0px 5px;">148</li><li style="box-sizing: border-box; padding: 0px 5px;">149</li><li style="box-sizing: border-box; padding: 0px 5px;">150</li><li style="box-sizing: border-box; padding: 0px 5px;">151</li><li style="box-sizing: border-box; padding: 0px 5px;">152</li><li style="box-sizing: border-box; padding: 0px 5px;">153</li><li style="box-sizing: border-box; padding: 0px 5px;">154</li><li style="box-sizing: border-box; padding: 0px 5px;">155</li><li style="box-sizing: border-box; padding: 0px 5px;">156</li><li style="box-sizing: border-box; padding: 0px 5px;">157</li><li style="box-sizing: border-box; padding: 0px 5px;">158</li><li style="box-sizing: border-box; padding: 0px 5px;">159</li><li style="box-sizing: border-box; padding: 0px 5px;">160</li><li style="box-sizing: border-box; padding: 0px 5px;">161</li><li style="box-sizing: border-box; padding: 0px 5px;">162</li><li style="box-sizing: border-box; padding: 0px 5px;">163</li><li style="box-sizing: border-box; padding: 0px 5px;">164</li><li style="box-sizing: border-box; padding: 0px 5px;">165</li><li style="box-sizing: border-box; padding: 0px 5px;">166</li><li style="box-sizing: border-box; padding: 0px 5px;">167</li><li style="box-sizing: border-box; padding: 0px 5px;">168</li><li style="box-sizing: border-box; padding: 0px 5px;">169</li><li style="box-sizing: border-box; padding: 0px 5px;">170</li><li style="box-sizing: border-box; padding: 0px 5px;">171</li><li style="box-sizing: border-box; padding: 0px 5px;">172</li><li style="box-sizing: border-box; padding: 0px 5px;">173</li><li style="box-sizing: border-box; padding: 0px 5px;">174</li><li style="box-sizing: border-box; padding: 0px 5px;">175</li><li style="box-sizing: border-box; padding: 0px 5px;">176</li><li style="box-sizing: border-box; padding: 0px 5px;">177</li><li style="box-sizing: border-box; padding: 0px 5px;">178</li><li style="box-sizing: border-box; padding: 0px 5px;">179</li><li style="box-sizing: border-box; padding: 0px 5px;">180</li><li style="box-sizing: border-box; padding: 0px 5px;">181</li><li style="box-sizing: border-box; padding: 0px 5px;">182</li><li style="box-sizing: border-box; padding: 0px 5px;">183</li><li style="box-sizing: border-box; padding: 0px 5px;">184</li><li style="box-sizing: border-box; padding: 0px 5px;">185</li><li style="box-sizing: border-box; padding: 0px 5px;">186</li><li style="box-sizing: border-box; padding: 0px 5px;">187</li><li style="box-sizing: border-box; padding: 0px 5px;">188</li><li style="box-sizing: border-box; padding: 0px 5px;">189</li><li style="box-sizing: border-box; padding: 0px 5px;">190</li><li style="box-sizing: border-box; padding: 0px 5px;">191</li><li style="box-sizing: border-box; padding: 0px 5px;">192</li><li style="box-sizing: border-box; padding: 0px 5px;">193</li><li style="box-sizing: border-box; padding: 0px 5px;">194</li><li style="box-sizing: border-box; padding: 0px 5px;">195</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li><li style="box-sizing: border-box; padding: 0px 5px;">140</li><li style="box-sizing: border-box; padding: 0px 5px;">141</li><li style="box-sizing: border-box; padding: 0px 5px;">142</li><li style="box-sizing: border-box; padding: 0px 5px;">143</li><li style="box-sizing: border-box; padding: 0px 5px;">144</li><li style="box-sizing: border-box; padding: 0px 5px;">145</li><li style="box-sizing: border-box; padding: 0px 5px;">146</li><li style="box-sizing: border-box; padding: 0px 5px;">147</li><li style="box-sizing: border-box; padding: 0px 5px;">148</li><li style="box-sizing: border-box; padding: 0px 5px;">149</li><li style="box-sizing: border-box; padding: 0px 5px;">150</li><li style="box-sizing: border-box; padding: 0px 5px;">151</li><li style="box-sizing: border-box; padding: 0px 5px;">152</li><li style="box-sizing: border-box; padding: 0px 5px;">153</li><li style="box-sizing: border-box; padding: 0px 5px;">154</li><li style="box-sizing: border-box; padding: 0px 5px;">155</li><li style="box-sizing: border-box; padding: 0px 5px;">156</li><li style="box-sizing: border-box; padding: 0px 5px;">157</li><li style="box-sizing: border-box; padding: 0px 5px;">158</li><li style="box-sizing: border-box; padding: 0px 5px;">159</li><li style="box-sizing: border-box; padding: 0px 5px;">160</li><li style="box-sizing: border-box; padding: 0px 5px;">161</li><li style="box-sizing: border-box; padding: 0px 5px;">162</li><li style="box-sizing: border-box; padding: 0px 5px;">163</li><li style="box-sizing: border-box; padding: 0px 5px;">164</li><li style="box-sizing: border-box; padding: 0px 5px;">165</li><li style="box-sizing: border-box; padding: 0px 5px;">166</li><li style="box-sizing: border-box; padding: 0px 5px;">167</li><li style="box-sizing: border-box; padding: 0px 5px;">168</li><li style="box-sizing: border-box; padding: 0px 5px;">169</li><li style="box-sizing: border-box; padding: 0px 5px;">170</li><li style="box-sizing: border-box; padding: 0px 5px;">171</li><li style="box-sizing: border-box; padding: 0px 5px;">172</li><li style="box-sizing: border-box; padding: 0px 5px;">173</li><li style="box-sizing: border-box; padding: 0px 5px;">174</li><li style="box-sizing: border-box; padding: 0px 5px;">175</li><li style="box-sizing: border-box; padding: 0px 5px;">176</li><li style="box-sizing: border-box; padding: 0px 5px;">177</li><li style="box-sizing: border-box; padding: 0px 5px;">178</li><li style="box-sizing: border-box; padding: 0px 5px;">179</li><li style="box-sizing: border-box; padding: 0px 5px;">180</li><li style="box-sizing: border-box; padding: 0px 5px;">181</li><li style="box-sizing: border-box; padding: 0px 5px;">182</li><li style="box-sizing: border-box; padding: 0px 5px;">183</li><li style="box-sizing: border-box; padding: 0px 5px;">184</li><li style="box-sizing: border-box; padding: 0px 5px;">185</li><li style="box-sizing: border-box; padding: 0px 5px;">186</li><li style="box-sizing: border-box; padding: 0px 5px;">187</li><li style="box-sizing: border-box; padding: 0px 5px;">188</li><li style="box-sizing: border-box; padding: 0px 5px;">189</li><li style="box-sizing: border-box; padding: 0px 5px;">190</li><li style="box-sizing: border-box; padding: 0px 5px;">191</li><li style="box-sizing: border-box; padding: 0px 5px;">192</li><li style="box-sizing: border-box; padding: 0px 5px;">193</li><li style="box-sizing: border-box; padding: 0px 5px;">194</li><li style="box-sizing: border-box; padding: 0px 5px;">195</li></ul>

在这里需要注意几点, 
1. 代码中的_currentProId所填写的是你的购买项目的的ID,这个和第二步创建的内购的productID要一致;本例中是 123。 
2. 在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。 
3. 沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。 
4. 请务必使用真机来测试,一切以真机为准。 
5. 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。 
6. 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号 
7. 二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的时候需要判断返回Status Code决定是否去沙盒进行二次验证,为了线上用户的使用,验证的顺序肯定是先验证正式环境,此时若返回值为21007,就需要去沙盒二次验证,因为此购买的是在沙盒进行的。

附:苹果支付错误目录

Status Code Description
21000 The App Store could not read the JSON object you provided.
21002 The data in the receipt-data property was malformed or missing.
21003 The receipt could not be authenticated.
21004 The shared secret you provided does not match the shared secret on file for your account.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21005 The receipt server is not currently available.
21006 This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21007 This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
21008 This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
转自:http://blog.csdn.net/darling_shadow/article/details/51538267

没有更多推荐了,返回首页