iOS 应用内支付

作者: sjzuo | 来源:发表于2017-06-07 14:19 被阅读264次

    参考链接
    iOS开发之内购-AppStore
    iOS App提交指南(二)-协议、税务和银行业务
    iOS应用内付费(IAP)开发步骤列表

    一、创建App

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


    ItunesConnection

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

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

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

    新建App

    点新建 App,会出现新建窗口;


    新建窗口

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

    申请BundleID

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


    申请一个新的Identifiers

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


    选项

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

    二、协议、税务和银行业务

    进入协议、税务和银行业务页面


    协议、税务和银行业务页面

    进入协议、税务和银行业务页面后,会有3种合同类型,如果你之前没有主动申请过去合同,那么一般你现在激活的合同只有iOS Free Application一种。

    页面内容分为两块:

    • Request Contracts(申请合同)
    • Contracts In Effect(已生效合同)。

    合同类型分为3种:

    • iOS Free Application(免费应用合同)
    • iOS Paid Application(付费应用合同)
    • iAd App NetNetwork(广告合同)

    笔者暂时只申请过付费应用合同,所以下面主要讲一下付费应用合同的申请流程。


    付费应用合同

    当我们点击申请iOS Paid Application合同后,该合同的状态会变成如下的样子,我们可以看到其中Status为Pending Tax, Bank, Contact。意思是联系方式、银行和税务信息没有填写。


    缺少信息

    1、填写联系方式

    我们点击Contact Info下方的Set Up按钮可以进入联系方式填写页面,如下图:


    填写联系方式

    如果你没有添加过联系人,你需要通过Add New Contact按钮来添加一个新的联系人。然后指定联系人的职务,职务如下:

    • Senior Management:高管
    • Financial:财务
    • Technical:技术支持
    • Legal:法务
    • Marketing:市场推广

    如果你是独立开发者,可以全部填你自己一个人。

    2、填写银行卡信息

    我们点击Bank Info下方的Set Up按钮可以进入联系方式填写页面,如下图:


    添加银行卡信息

    选择你的银行账户,如果你没有,点击旁边的Add Bank Account添加一个账户。
    下面是添加一个账户的流程。

    2-1、选择银行所在的国家
    选择银行所在的国家
    2-2、填写银行CNAPS Code

    如果你不知道CNAPS Code是多少,可以点击Look up Transit Number来查询,查询时会根据3个关键信息来查询,如下:

    • Bank Name:银行的英文名称(不能是拼音)
    • City:银行所在的城市英文名称(中国的城市用拼音)
    • Postal Code:邮编
    • 然后在下面就会出来备选的银行,选择正确的银行后,点击next,进入下一步。


      填写银行CNAPS Code
      银行信息
    2-3、确认银行信息
    确认银行信息
    2-4、填写银行账号信息
    • Bank Account Number:银行账号
    • Confirm Bank Account Number:再次输入银行账号
    • Account Holder Name:持卡人姓名,中文名用拼写,名在前,姓在后
    • Bank Account Currency:货币类型,一般国内的开发者选择CNY


      填写银行账号信息
    2-5、确认所有信息
    确认所有信息

    3、填写税务信息

    税务信息这一块了解不是很多,不过因为是国内开发者,可以不用太费心,税务信息分3种:

    • U.S Tax Forms: 美国税务
    • Australia Tax Forms:澳大利亚税务
    • Canada Tax Forms: 加拿大税务


      填写税务信息

      笔者选择的是U.S Tax Forms,选择后会问你两个问题,第一个问题如下:询问你是否是美国居民,有没有美国伙伴关系或者美国公司,如果没有直接选择No。


      是否美国居民
      接下来第二个问题如下:询问你有没有在美国的商业性活动,没有也直接选No。
      是否在美国的商业性活动

    然后填写你的税务信息,包括以下几点:

    • Individual or Organization Name:个人或者组织名称
    • Country of incorporation: 所在国家
    • Type of Beneficial Owner:受益方式,独立开发者选个人
    • Permanent Residence:居住地址
    • Mailing address:邮寄地址
    • Name of Person Making this Declaration:声明人
    • Title:头衔

    填写完这些信息后就可以提交了


    提交审核

    4、等待审核

    当你填写完所有资料后,合同状态就会变成Processing,笔者凌晨1点左右提交,下午就通过了。


    等待审核

    三、添加内购买项目

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


    内购买项目

    虚拟物品添加分为如下几种:

    1. 消耗品(Consumable products):比如游戏内金币等。
    2. 不可消耗品(Non-consumable products):简单来说就是一次购买,终身可用(用户可随时从App Store restore)。
    3. 自动更新订阅品(Auto-renewablesubscriptions):和不可消耗品的不同点是有失效时间。比如一整年的付费周刊。在这种模式下,开发者定期投递内容,用户在订阅期内随时可以访问这些内容。订阅快要过期时,系统将自动更新订阅(如果用户同意)。
    4. 非自动更新订阅品(Non-renewablesubscriptions):一般使用场景是从用户从IAP购买后,购买信息存放在自己的开发者服务器上。失效日期/可用是由开发者服务器自行控制的,而非由AppStore控制,这一点与自动更新订阅品有差异。
    5. 免费订阅品(Free subscriptions):在Newsstand中放置免费订阅的一种方式。免费订阅永不过期。只能用于Newsstand-enabled apps。

    类型2、3、5都是以Apple ID为粒度的。比如小张有三个iPad,有一个Apple ID购买了不可消耗品,则三个iPad上都可以使用。

    类型1、4一般来说则是现买现用。如果开发者自己想做更多控制,一般选4

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


    选择类型

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

    • 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买
    • 非消耗型项目 就像你在App Store购买App,买了一次之后就不用再买第二次,你拥有永久使用权。
      在我们的app中,是充值会员,所以选择的是第一种,可以无限次购买。


      填写信息

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

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


      内置购买详细信息

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


      添加语言
      审核备注,根据实际情况填写,可以不填。而下面的屏幕快照,则是商品图片,以像素为单位,最低尺寸为321,390,尺寸需求如下图,上传即可。
      审核备注
      到这里为止, 我们的内购项目则添加完成。接下来则是测试阶段了。

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

    这个账号,是利用苹果的沙盒测试环境来模拟AppStore的购买流程,你肯定不会想要用真实RMB去购买测试吧?
    首先我们回到iTunes Connect中,在这里我们选择用户和职能。


    用户与职能

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


    添加测试员
    在信息填写页面只简单说两句。
    所有信息都可以随意填写,不用管是否真实。
    App Store地区选择,一定要选对,它对应的是你创建的App的地区, 你App是中国的话, 在这里我们依然选择中国。

    此账号只能用来测试,不要在正式的appstore上使用
    填写完毕,点击保存后,我们则生成一个测试账号,当然这个账号是可以随时删除和添加的。


    生成测试账号

    五、大致流程

    IAP流程分为两种,一种是直接使用Apple的服务器进行购买和验证,另一种就是自己假设服务器进行验证。由于国内网络连接Apple服务器验证非常慢,而且也为了防止黑客伪造购买凭证,通用做法是自己架设服务器进行验证。

    下面我们通过图来看看两种方式的差别:

    5.1、使用Apple服务器

    使用Apple服务器

    5.2、自己架设服务器

    自己架设服务器

    简单说下第二中情况的流程:

    1. 用户进入购买虚拟物品页面,App从后台服务器获取产品列表然后显示给用户
    2. 用户点击购买购买某一个虚拟物品,APP就发送该虚拟物品的productionIdentifier到Apple服务器
    3. Apple服务器根据APP发送过来的productionIdentifier返回相应的物品的信息(描述,价格等)
    4. 用户点击确认键购买该物品,购买请求发送到Apple服务器
    5. Apple服务器完成购买后,返回用户一个完成购买的凭证
    6. APP发送这个凭证到后台服务器验证
    7. 后台服务器把这个凭证发送到Apple验证,Apple返回一个字段给后台服务器表明该凭证是否有效
    8. 后台服务器把验证结果在发送到APP,APP根据验证结果做相应的处理

    六、大致代码

    #import "ViewController.h"
    #import <StoreKit/StoreKit.h>
    
    @interface ViewController () <SKPaymentTransactionObserver, SKProductsRequestDelegate>
    
    @property (nonatomic, strong) NSString * receipt;
    
    @end
    
    @implementation ViewController
    
    /**
     *  开发流程:
     *  1、引入头文件 #import <StoreKit/StoreKit.h>
     *  2、检查用户是否允许使用 内置购买 [SKPaymentQueue canMakePayments]
     *  3、如果允许:获取所有付费Product ID(productionIdentifier)列表。可以用常量保存在本地,也可以自己服务器返回(原因:连接苹果服务器比较慢)
     *  4、先查询是否有该productionIdentifier的商品信息,获取SKPayment实例,然后通过SKPaymentQueue的 addPayment方法发起一个购买的操作。
     *  5、监听购买结果 [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
     *  6、购买完成回调- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
     *  7、服务器验证凭证(Optional)。如果购买成功,我们需要将凭证发送到服务器上进行验证。
     *      服务器工作:
     *      7.1 接收ios端发过来的购买凭证。
     *      7.2 判断凭证是否已经存在或验证过,然后存储该凭证。
     *      7.3 将该凭证发送到苹果的服务器验证,并将验证结果返回给客户端。
     *      7.4 如果需要,修改用户相应的会员权限。
     *  8、网络异常、崩溃等导致无法验证,将购买凭证持久化。用户下次启动的时候,再发一次验证
     *      8.1 保存数据(日期、用户、凭证receipt)
     *      8.2 重新启动,读取数据,发送网络请求
     *      8.3 如果成功,删除文件
     */
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 监听购买结果
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    }
    
    - (void)buyProdutin:(UIButton *)sender
    {
        // 确认用户是否允许使用 内置购买
        if([SKPaymentQueue canMakePayments]){
            // 获取商品的productionIdentifier
            NSString * productIdentifier = @"";     //用户选择
            [self getProductInfo:productIdentifier];
        }else{
            
        }
    }
    
    #pragma mark - 查询productIdentifier 的商品信息
    - (void)getProductInfo:(NSString *)productIdentifier
    {
        NSArray * product = [[NSArray alloc] initWithObjects:productIdentifier, nil];
        NSSet * set = [NSSet setWithArray:product];
     
        // 请求苹果服务器,是否有该商品
        SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
        request.delegate = self;
        [request start];
        
        // 这里可弹出提示信息 : 正在购买,请稍后
    }
    
    // 查询该产品信息
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    {
        NSArray * myProduct = response.products;
        if(myProduct.count == 0){
            // 说明没有该商品,或查询失败
            
            return;
        }
        
        // 如果有,发起购买操作
        SKPayment * payment = [SKPayment paymentWithProduct:myProduct[0]];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    
    // 查询失败
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error
    {
        // 提示信息
    }
    
    #pragma mark - 购买回调
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
    {
        for(SKPaymentTransaction * transaction in transactions){
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased:
                    // 交易成功
                    [self completeTransaction:transaction];
                    break;
                case SKPaymentTransactionStateFailed:
                    // 交易失败
                    [self failedTransaction:transaction];
                    break;
                    
                case SKPaymentTransactionStateRestored:
                    // 已经购买过该商品
                    [self restoreTransaction:transaction];
                    break;
                    
                case SKPaymentTransactionStatePurchasing:
                    // 商品添加进列表
                    
                    break;
                default:
                    break;
            }
        }
    }
    
    //沙盒测试环境验证
    #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
    //正式环境验证
    #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
    /**
     *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
    
    // 交易结束后,验证票据信息是否正确,之后才可以给用户商品
    - (void)completeTransaction:(SKPaymentTransaction *)transaction {
        // 测试环境
        self.receipt = [[NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]] base64EncodedStringWithOptions:0];
        NSError *error;
        NSDictionary *requestContents = @{
                                          @"receipt-data": self.receipt
                                          };
        NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                              options:0
                                                                error:&error];
        // 设置URL
        NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
        NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
        [storeRequest setHTTPMethod:@"POST"];
        [storeRequest setHTTPBody:requestData];
        
       // 网络请求,验证
        
        
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    }
    
    // 交易失败后,回调
    - (void)failedTransaction:(SKPaymentTransaction *)transaction {
        if(transaction.error.code != SKErrorPaymentCancelled) {
            // 购买失败
            
        } else {
            // 用户取消交易
            
        }
        
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    }
    
    // 已购买过商品回调
    - (void)restoreTransaction:(SKPaymentTransaction *)transaction {
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    }
    
    #pragma mark - 发送凭证失败的处理
    /**
     *  1 保存数据(日期、用户、凭证receipt)
     *  2 重新启动,读取数据,发送网络请求
     *  3 如果成功,删除文件
     */
    
    - (void)dealloc
    {
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    
    @end
    
    

    需要注意以下几点:

    • 代码中的self.profuctIdArr所填写的是你的购买项目的的ID,我这里是当时填写的ID 123。
    • 在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。
    • 沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
    • 请务必使用真机来测试,一切以真机为准。
    • 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
    • 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号
    • 二次验证,请注意区分宏, 测试用沙盒验证

    相关文章

      网友评论

      • 穷途墨路:喜欢蒙奇.D路飞的程序员👍😀
      • aaa000:有没有遇到 大部分购买都能成功 只有极少数用户购买 付了钱但是 道具没有到帐!这种情况!
        aaa000:@sjzLovecj 谢谢啦 !
        sjzuo: @buttonTouch 没,我们公司说让做,我只是研究了下,然后把文档给了同事,最后我就离职了,建议看下唐巧的博客,总结的比较好,有流程和遇到的坑,分两篇写的。不好意思,没能回答你的问题

      本文标题:iOS 应用内支付

      本文链接:https://www.haomeiwen.com/subject/aunofxtx.html