一、介绍:

在个人开发的app上架到AppStore后,苹果官方允许我们将自己的app在appstore上进行付费使用,也就是所谓的内购。其中,支付方式规定的必须是苹果的支付方式:应用内支付。

二、流程:

1、后台设置

(1)配置Developer.apple.com,为应用建立一个不带通配符的App ID

(2)用该应用的App ID生成和安装相应的Provisioning Profile文件

2、配置iTunes Connect

(1)用该App ID创建一个新的应用;

(2)在该应用中,创建应用内付费项目,选择付费类型,通常可选的是可重复消费的(Consumenable)和永久有效的(Non-Consumenable)两种,然后设置好价格、Product ID、购买介绍和截图,这里的Product ID是必须记住的,后面开发的时候要用到;

(3)添加一个用于在sandbox付费的测试用户,注意苹果对测试用户的密码要求和正是账号一样,至少8位,并且包包含数字和大小写字母;

(4)填写相关的税务。银行和联系人

3、iOS端开发

(1)在工程中引入storeKit.framework和#import <storeKit/storeKit.h>;

(2)获取所有的付费Product ID列表。这个可以用常量存储到本地,也可以由自己的服务器返回;

(3)制作一个界面(如tableView),显示所有的应用内付费项目。这些应用内付费项目的价格和介绍信息可以是自己的服务器返回。但如果是不带服务器的单机游戏应用或者工具类应用,则可以通过向App Store查询所得;

(4)当用户点击一个IAP项目,我们需要先查询用户是否允许应用内付费,如果不允许则不进行接下来的步骤;

(5)先通过该IAP的ProductID向AppStore查询,获取SKPayment实例,然后通过SKPaymentQueue的addPayment方法发起一个购买的操作;

(6)在ViewdidLoad方法中,将购买页面设置成购买额observe;

(7)当用户购买的操作有结果时,就会触发调用回调函数,相应的进行处理;

(8)服务器验证凭证(可选项)。如果购买成功,我们需要将凭证发送到服务器上进行验证。考虑到网路异常情况,iOS端的发送凭证操作应该可以持久化,如果程序退出、崩溃或者网络异常,可以恢复重试。

4、服务端开发

(1)接收iOS端发过来的购买凭证;

(2)判断凭证是否已经存在,是否验证过,然后进行存储;

(3)将该凭证发送到苹果的服务器验证,并将结果返回给客户端;

(4)如果需要,修改用户相应的会员权限。

注意:考虑到网络异常的情况,服务器的验证应该是一个可恢复的队列,如果失败了,应该进行重试。

苹果AppStore线上的购买凭证验证地址:https://buy.itunes.apple.com/verifyreceipt

测试的验证地址:https://sandbox.itunes.apple.com/verifyreceipt

三、iOS基本代码如下:

//  ViewController.m
//
// Created by 夏远全 on 16/11/20.
// Copyright © 2016年 广州市东德网络科技有限公司. All rights reserved.
// #import "ViewController.h"
#import <StoreKit/StoreKit.h> @interface ViewController ()<SKProductsRequestDelegate,SKPaymentTransactionObserver> @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; //监听购买结果
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
} -(void)dealloc{ //移除购买监听
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
} //用户点击一个IAP项目时,首先查询用户是否允许应用内付费(tableViewCell点击时,传递内购商品ProductId,ProductID可以提前存储到本地,用到时直接获取即可)
-(void)validateIsCanBought{ if ([SKPaymentQueue canMakePayments]) {
[self getProductInfo:@[@"ProductIds"]];
}else{
NSLog(@"失败,用户禁止应用内付费购买");
}
} //通过该IAP的Product ID向App Store查询,获取SKPayment实例,接着通过SKPaymentQueue的addPayment方法发起一个购买的操作
//下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目,否则会查询失败
-(void)getProductInfo:(NSArray *)productIds{ NSSet *set = [NSSet setWithArray:productIds];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];
} #pragma mark - SKProductsRequestDelegate
//查询的回调函数
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ //获取到的所有内购商品
NSArray *myProducts = response.products; //判断个数
if (myProducts.count==) {
NSLog(@"无法获取产品信息,购买失败。");
return;
} //发起一个购买操作
SKPayment *payment = [SKPayment paymentWithProduct:myProducts[]];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} #pragma mark - SKPaymentTransactionObserver
//当用户购买的操作有结果时,就会触发下面的回调函数,相应进行处理
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{ for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: //交易完成
NSLog(@"transactionIdentifier = %@",transaction.transactionIdentifier);
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed: //交易失败
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored: //已经购买过该商品
[self restoreTransaction:transaction];
break;
case SKPaymentTransactionStatePurchasing: //商品添加进列表
NSLog(@"商品添加进列表");
break;
default:
break;
}
}
} //交易完成后的操作
-(void)completeTransaction:(SKPaymentTransaction *)transaction{ NSString *productIdentifier = transaction.payment.productIdentifier;
NSData *transactionReceiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
NSString *receipt = [transactionReceiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; if ([productIdentifier length]>) {
//向自己的服务器验证购买凭证
NSLog(@"%@",receipt);
} //移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} //交易失败后的操作
-(void)failedTransaction:(SKPaymentTransaction *)transaction{ if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(@"购买失败");
}else{
NSLog(@"用户取消交易");
}
//移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} //已经购买过该商品
-(void)restoreTransaction:(SKPaymentTransaction *)transaction{ //对于已购买商品,处理恢复购买的逻辑
//移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} @end

三、个人参考demo

https://github.com/xiayuanquan/AppleStoreKitDemo.git

效果图:

 

打印日志:

-- ::08.009166 StoreKit[:] [MC] Reading from private effective user settings.
-- ::08.012 StoreKit[:] ---------请求对应的产品信息------------
-- ::08.015 StoreKit[:] 允许程序内付费购买
-- ::09.193 StoreKit[:] -----------收到产品反馈信息--------------
-- ::09.193 StoreKit[:] 产品Product ID:(
)
-- ::09.194 StoreKit[:] 产品付费数量:
-- ::09.194 StoreKit[:] product info
-- ::09.194 StoreKit[:] SKProduct 描述信息<SKProduct: 0x600000004950>
-- ::09.194 StoreKit[:] 产品标题 1元=10金币
-- ::09.195 StoreKit[:] 产品描述信息: 通过虚拟金币充值,获取会员资格
-- ::09.195 StoreKit[:] 价格: 0.99
-- ::09.195 StoreKit[:] Product id: www.biaojiepay.com.StoreKit01
-- ::09.195 StoreKit[:] ---------发送购买请求------------
-- ::09.196 StoreKit[:] -----paymentQueue--------
-- ::09.196 StoreKit[:] -----商品添加进列表 --------
-- ::09.196 StoreKit[:] ----------反馈信息结束--------------
-- ::10.125470 StoreKit[:] subsystem: com.apple.BackBoardServices.fence, category: Observer, enable_level: , persist_level: , default_ttl: , info_ttl: , debug_ttl: , generate_symptoms: , enable_oversize: , privacy_setting: , enable_private_data:
-- ::10.127183 StoreKit[:] subsystem: com.apple.BackBoardServices.fence, category: Workspace, enable_level: , persist_level: , default_ttl: , info_ttl: , debug_ttl: , generate_symptoms: , enable_oversize: , privacy_setting: , enable_private_data:
-- ::10.127707 StoreKit[:] subsystem: com.apple.BackBoardServices.fence, category: Trace, enable_level: , persist_level: , default_ttl: , info_ttl: , debug_ttl: , generate_symptoms: , enable_oversize: , privacy_setting: , enable_private_data:
-- ::12.044 StoreKit[:] -----paymentQueue--------
-- ::12.045 StoreKit[:] 失败
-- ::12.048 StoreKit[:] -----交易失败 --------

iOS: 实现苹果的内购的更多相关文章

  1. 苹果应用内购 ios 开发者根据用户提供的邮件中的订单号查看该订单是否支付成功

    苹果应用内购 ios 开发者根据用户提供的邮件中的订单号查看该订单是否支付成功 这是苹果wwdc2021 推出的新功能 参考官网链接 App Store Server API | Apple Deve ...

  2. JAVA项目之苹果IAP内购JAVA服务器验证流程详解

    1.前言 本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员,  认真看,  定能完美解决苹果内购问题. 苹果IAP内购支付实际上是"将客户端支 ...

  3. [IPA]IOS In App Purchase(内购)验证

    参考我之前的笔记 苹果内购笔记,在客户端向苹果购买成功之后,我们需要进行二次验证. 二次验证 IOS在沙箱环境下购买成功之后,向苹果进行二次验证,确认用户是否购买成功. 当应用向Apple服务器请求购 ...

  4. iOS开发——高级技术&内购服务

    内购服务 大家都知道做iOS开发本身的收入有三种来源:出售应用.内购和广告.国内用户通常很少直接 购买应用,因此对于开发者而言(特别是个人开发者),内购和广告收入就成了主要的收入来源.内购营销模式,通 ...

  5. iOS开发支付篇-内购(IAP)

    一,前言 经典文章参考: . http://yimouleng.com/2015/12/17/ios-AppStore/ 内购流程 . http://www.jianshu.com/p/b199a46 ...

  6. C# [IPA]IOS In App Purchase(内购)验证(asp.net 版本)

    之前没有做过IOS 内购服务器验证这块,所以找了不少参考资料,网上大多php和java版本,然后自己搞了一个C#版本,希望能给大家一些参考,下面步入正题 在客户端向苹果购买成功之后,我们需要进行二次验 ...

  7. iOS开发支付篇——内购(IAP)详解

    1 <em>内购所需要的资料整理总结,史上最完整的,哈哈哈哈哈哈</em> 思维导图 重点总结: 1 2 3 4 5 6 7 8 9 10 11 12 13 1.获取内购列表( ...

  8. 苹果APP内购客户付款成功,没收到相应虚拟产品的解决办法

    一.引导用户走申请苹果的退款 1.告知用户新版本可以使用支付宝.微信支付,更划算 2.苹果可申请90天以内的退款,一般情况申请后48小时内就有反馈. 参考链接 https://jingyan.baid ...

  9. iOS:苹果内购实践

    iOS 苹果的内购 一.介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝等),当然开发 ...

随机推荐

  1. 【C语言】08-数组

    为了让大家更好地学习和理解数组,我们先来认识一下内存中的"地址". 地址 1.计算机中的内存是以字节为单位的存储空间.内存的每一个字节都有一个唯一的编号,这个编号就称为地址.凡存放 ...

  2. 关于Stock Quant与生产内容的理解

    Stock Quant对象是8.0中新增加的内容,主要用于核算库存,与Stock Move相关联,没个Stock Move动作都会关联一个或者多个Stock Quant对象.库存移动的过程实际上就是将 ...

  3. android之Chronometer

    首先定义activity_main.xml文件 代码如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/re ...

  4. 本函数用来改变目前 php 执行的目录到新的 directory 目录中

    chdir : 改变目录. dir : 目录类别类. closedir : 关闭目录 handle. opendir : 打开目录 handle. readdir : 读取目录 handle. rew ...

  5. [转]asp.net mvc 从数据库中读取图片

    本文转自:http://www.cnblogs.com/mayt/archive/2010/05/20/1740358.html 首先是创建一个类,继承于ActionResult,记住要引用Syste ...

  6. HTML第一课总结

    1.图片热点: 规划出图片上的一个区域,可以做出超链接,直接点击图片区域就可以完成跳转的效果. 示例: 2.网页划区: 在一个网页里,规划出一个区域用来展示另一个网页的内容. 示例: 3.网页的拼接: ...

  7. Leetcode | substr()

    求子串当然最经典的就是KMP算法了.brute force算法在leetcode上貌似也有一些技巧. brute force: char* StrStr(const char *str, const ...

  8. Bootstrap兼容

    转:http://blog.csdn.net/chenhongwu666/article/details/41513901 如有雷同,不胜荣幸,若转载,请注明让IE6 IE7 IE8 IE9 IE10 ...

  9. mysql 事务是专门用来管理insert,update,delete语句的,和select语句一点不相干

    1.mysql 事务是专门用来管理insert,update,delete语句的,和select语句一点不相干 2.一般来说,事务是必须满足4个条件(ACID): Atomicity(原子性).Con ...

  10. 升级xcode6和ios8后,unity遇到的一些小问题

    升级最新的Xocde6后,如果不是最新版本的unity,虽然也可以也可以正常的build,但如果想通过unity连真机进行profile的话,就会在xocde中报错,这个的主要原因是unity的配置里 ...