2016年最新版App内购买详细指南
- 本文讲述了如何创建App内购买项目?
- 如何添加沙盒App内购买测试帐号?
- 如何封装一个内购买的管理类?
- 如何在完成付款之后,保证有效的提供增值服务?
如何创建内购买项目?
创建内购买之前,请确保已完成“协议、税务和银行业务”。打开iTunes Connect,进入app,选择"功能"-"App内购买项目",点击"+"创建一个内购买项目
选择所属类型:
按照提示填写名称,定价,产品id,以及描述信息,内购商品列表截图。
需要注意产品id的规范,它必须由:"bundle identifier"+编号
组成 如xxx01
,其中xxx
表示bundle identifier
。
每个内购项目需要提供一个内购买商品列表截图,如:
创建完内购项目之后,我们需要一个沙盒测试帐号。如何创建沙盒测试帐号?
进入“用户和职能”,选择“沙箱测试技术员”,点击“+”添加一个测试帐号:
按照提示,输入信息,创建测试帐号,用于内购买测试。接下来是代码部分。如何封装一个内购买管理类?
思路
- 这个管理类需要有一个支付的调起接口,至少要一个产品id参数;
- 若是非消耗型商品,需要提供一个恢复已购买商品的接口;
要保证内购付完款之后,要告诉我们自己的服务器,我买了什么东西,而要做到这一点,我们又要考虑:
搞一个管理类:@interface FCInAppPurchaseManager : NSObject
搞个单例类方法:/** * 单例类 * * @return 单例对象 */ +(instancetype)sharedManager;
搞一个购买商品的接口:
/** * 购买商品 * * @param identifier iTunes Connect商品ID * @param tradeId 交易id(自己服务器创建订单的交易id) * @param key 交易标识(自己服务器生成的key,防治杜撰交易) */ -(void)purchaseProductWithIdentifier:(NSString *)identifier tradeId:(NSString *)tradeId key:(NSString *)key;
在商品列表界面购买商品时,调用上面这个接口,从自己服务器创建交易,获取交易id和key
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{//创建交易 FCInnserPurchaseProductModel *model=_dataArray[indexPath.row]; FCFamousUser *currentUser=[[FCAccountSettings getCurrentAccountSetting] getCurrentUser]; NSString *token=[FCMainSettings getToken]; NSDictionary *params=@{kAfterLoginToken:token,@"userid":currentUser.userId,@"no":@(currentUser.no),@"productid":model.productId,@"price":model.price,@"coin":model.coin,@"quantity":@1,@"signature":[[NSUUID UUID] UUIDString],@"channel":@"ios",@"device_type":@"ios"}; HUDShowLoading [[FCWebServiceController sharedWebServiceController] sendRequestWithParameters:params action:@"trade/create" success:^(id json) {if(![json isKindOfClass:[NSDictionary class]]){HUDError(kServerInnerError)return;}HUDDismissNSString *tradeId=[[json objectForKey:@"trade"] objectForKey:@"_id"];NSString *key=[[json objectForKey:@"extras"] objectForKey:@"key"];//调起购买管理类的购买[[FCInAppPurchaseManager sharedManager] purchaseProductWithIdentifier:model.identifier tradeId:tradeId key:key];} failure:^(NSError *error) {HUDError(error.localizedDescription); }]; }
内购买管理类的实现
FCInAppPurchaseManager.m
首先要导入StoreKit框架:
#import <StoreKit/StoreKit.h>
单例类方法:
static FCInAppPurchaseManager *purchaseManager=nil; +(instancetype)sharedManager{static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{purchaseManager=[[FCInAppPurchaseManager alloc] init]; }); return purchaseManager; }
搞一个拓展,加上一些私有属性:
@interface FCInAppPurchaseManager()<SKProductsRequestDelegate,SKPaymentTransactionObserver> /** * 服务器生成的校验码 */ @property(nonatomic,copy)NSString *key; /** * iTunes Connect上商品的标识符 */ @property(nonatomic,copy)NSString *identifier; /** * 服务器的交易编号 */ @property(nonatomic,copy)NSString *tradeId;
@end
初始化方法中,添加监听:
-(instancetype)init{
if(self=[super init]){[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
实现购买商品的接口:保存交易id、key到本地的目的是,支付完成后,告诉服务器用户买了啥的这个过程是一个不确定的过程,有可能断网了,手机没电了、程序被终结了,程序bug崩溃了等不确定因素,一次要保留这一次的交易id、key,再次启动app时,我们的服务器没有拿着凭证去向苹果服务器验证交易信息苹果会自动调用监听购买结果的方法`paymentQueue : updatedTransactions :`,在这个方法中,完成上一次应该告诉我们服务器用户买了啥的任务。
/**
- 购买商品
- @param identifier 产品在iTunes connect中的标识符(HZSX.FriendCircle01 HZSX.FriendCircle02 HZSX.FriendCircle03 分别对应60圈币、180圈币 300圈币)
- @param tradeId 交易编号(服务端生成的)
@param key 验证用的key(服务端生成)
/
-(void)purchaseProductWithIdentifier:(NSString )identifier tradeId:(NSString )tradeId key:(NSString )key{if(![SKPaymentQueue canMakePayments]){
HUDError(@"您的手机没有打开程序内付费购买"); return;
}
if(![identifier hasPrefix:@"HZSX.FriendCircle"]){HUDError(@"商品类型错误"); return;
}
if(kFCEmptyCheck(tradeId)){
HUDError(@"交易编号不能为空");
return;
}
if(kFCEmptyCheck(key)){
HUDError(@"缺少必要参数");
return;
}
_identifier=identifier;
_tradeId=tradeId;
_key=key;
//存到本地
NSString token=[FCMainSettings getToken];
NSString tradeId_key=[NSString stringWithFormat:@"%@_tradeId",token];
NSString *tradeKey_key=[NSString stringWithFormat:@"%@_key",token];
[[NSUserDefaults standardUserDefaults] setObject:tradeId forKey:tradeId_key];
[[NSUserDefaults standardUserDefaults] setObject:key forKey:tradeKey_key];
[[NSUserDefaults standardUserDefaults] synchronize];NSSet set=[NSSet setWithObject:identifier];
//发起购买商品的请求
SKProductsRequest request=[[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate=self;
[request start];
}实现StoreKit的代理方法: \#pragma mark - SKProductsRequestDelegate
//收到的产品信息
- (void)productsRequest:(SKProductsRequest )request didReceiveResponse:(SKProductsResponse )response{
if(response.products.count==0){
HUDError(@"商品信息错误");
return;
}
SKProduct product=response.products.lastObject;
SKPayment payment=[SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}\#pragma mark - SKRequestDelegate
-(void)request:(SKRequest )request didFailWithError:(NSError )error{
HUDError(error.localizedDescription);
}
-(void)requestDidFinish:(SKRequest *)request{HUDDismiss;
}监听购买结果
pragma mark - 监听购买结果(若购买时,退出了app,苹果会在app启动时,自动调用这个方法)
-(void)paymentQueue:(SKPaymentQueue )queue updatedTransactions:(NSArray )transactions{
if(transactions.count==0!transactions){
HUDError(@"获取不到购买结果") return;
}
SKPaymentTransaction *transaction=transactions.lastObject;
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:{//交易完成[self recordTransaction:transaction];break; } case SKPaymentTransactionStateFailed:{//交易失败[self failedTransaction:transaction];break; } case SKPaymentTransactionStateRestored://已经购买过该商品[self restoreTransactions];break; case SKPaymentTransactionStatePurchasing: //商品添加进列表break; default:break;
}
那么问题来了? ###为什么不把交易完成后,告诉自己服务器,买了什么,这部分业务抽离出来呢? 考虑到从调起支付到支付结果(成功、失败等)需要`几秒甚至十几秒`,在这段时间内,用户可能离开了当前界面, 而苹果购买的结果回调时,用户不在当前界面就告诉不了我们的服务器,用户买了啥啥啥。 因此我把购买完成,告诉服务器的这部分逻辑也写进了这个管理类里面。这样,用户无论是否离开当前页面,只要支付成功了,就能调用告诉服务器用户买了啥的逻辑。
告诉服务器用户买了啥:同时将交易id、交易key、凭证
#pragma mark - 请求服务端,记录交易 告诉服务器我买了什么
-(void)recordTransaction:(SKPaymentTransaction *)transaction{//结束交易[[SKPaymentQueue defaultQueue] finishTransaction: transaction];NSString *token=[FCMainSettings getToken];NSString *tradeId_key=[NSString stringWithFormat:@"%@_tradeId",token];NSString *tradeKey_key=[NSString stringWithFormat:@"%@_key",token];if(kFCEmptyCheck(_tradeId)){_tradeId=[[NSUserDefaults standardUserDefaults] objectForKey:tradeId_key];}if(kFCEmptyCheck(_key)){_key=[[NSUserDefaults standardUserDefaults] objectForKey:tradeKey_key];}//获取苹果返回的支付凭证NSURL *receiptURL=[[NSBundle mainBundle] appStoreReceiptURL];NSData *receiptData=[NSData dataWithContentsOfURL:receiptURL];if(!receiptData){return;}NSString *base64_receipt=[[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]stringByReplacingOccurrencesOfString:@" " withString:@""];if(kFCEmptyCheck(base64_receipt)){return;}//sign=MD5(tradeid=tradeid&salt=mrpyp-2016&key=key)NSString *components=[[NSString stringWithFormat:@"tradeid=%@&salt=mrpyp-2016&key=%@",_tradeId,_key]stringByReplacingOccurrencesOfString:@" " withString:@""];//NSString *rsa_components=[[RSA encryptString:components publicKey:kInAppPurchaseRSAPublicKey]// stringByReplacingOccurrencesOfString:@" " withString:@""];NSString *md5_componets=[components MD5Hash];if(kFCEmptyCheck(token)kFCEmptyCheck(_tradeId)kFCEmptyCheck(md5_componets)){return;}NSDictionary *params=@{kAfterLoginToken:token,@"sign":md5_componets,@"tradeid":_tradeId,@"receipt":base64_receipt,@"key":_key};[self tellServerBuySuccessWithdTradeInfo:params];
}
告诉服务器,我们买了什么:
/*** 告诉服务器购买成功了** @param tradeInfo 交易信息* @param destinationPath 存放交易信息的文件路径*/
-(void)tellServerBuySuccessWithdTradeInfo:(NSDictionary *)tradeInfo/* destionationPath:(NSString *)destinationPath*/{[[FCWebServiceController sharedWebServiceController] sendRequestWithParameters:tradeInfo action:@"pay/ios_receipt_verify" success:^(id json) {if(![json isKindOfClass:[NSDictionary class]]){HUDError(kServerInnerError)return;}BOOL result=[[json objectForKey:@"result"] boolValue];if(!result){return;}NSLog(@"验证成功");//显示购买成功的视图//[self showBuySuccessViewWithCoin:60];} failure:^(NSError *error) {NSLog(@"%@",error.localizedDescription);}];
}
恢复已购买的商品
若你购买的商品类型是非消耗型
的,在用户切换帐号,换手机等情景下,恢复已购买的内容:
//恢复购买的装备
-(void)restoreTransactions{[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
并实现恢复购买的代理
//恢复购买的装备成功
-(void)paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{if(!transaction)return;[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:@[transaction]];
}
//恢复购买的装备失败
-(void)paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{HUDError(error.localizedDescription);
}
最后在dealloc
方法中注销监听:
-(void)dealloc{[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听
}
测试内购买
内购买的代码已经写好了,内购项目也新建了,沙盒测试帐号也有了,现在可以动手测试内购买了。
- 手机打开App Store,选择第一个tab
精品推荐
,滚动到最底部,点击自己的Apple ID,在弹出的框中,选择注销
。 - 打开我们自己的app,进入内购买
商品列表页面
,点一个商品,发起购买。 - 在登陆的弹框中选择
使用现有的Apple ID
,在登陆iTunes connect的弹框中,输入前面注册的沙盒测试帐号
- 在购买信息的弹框中选择
购买
- 购买完成,前往
我的帐户
,查看充值记录,验证服务器是否纪录此次交易。提交审核
若你的app时第一次加入内购买,则,app内购项目也要一并提交
至此,内购买集成结束。
作者: CGPointZero
链接:http://www.imooc.com/article/12792
来源:慕课网
2016年最新版App内购买详细指南相关推荐
- iOS开发之内购完全笔记(您已购买此 App 内购买项目。此项目将免费恢复。)
1.内购流程 1.在 AppStore 中创建相应的物品,创建内购沙盒测试账号 2.客户端从后台获取相应的物品 ID (当然也可以再客户端写死,但后期扩展性就受限制了) 3.依据相应的物品 ID 请求 ...
- iOS 上 App 内购买(in-app purchase)的误消费可以退款吗?
iOS 上 App 内购买(in-app purchase)的误消费可以退款吗?修改 周二晚按照@任轶 的答案做了,把购物清单的四个订单号列在内容里,当晚收到了苹果公司自动回复,周三受到苹果公司团队中 ...
- iOS 内购提示不允许App内购买项目,打开内购方式和检测不允许内购的方法。
这个问题是屏幕使用时间里面关闭了内购功能 打开方法如下 设置->屏幕使用时间->内容和隐私访问限制->iTunes Store与App Store购买项目->APP内购买项目- ...
- 您的首个 App 内购买项目必须以新的 App 版本提交
您的首个 App 内购买项目必须以新的 App 版本提交.请从 App 的"App 内购买项目"中选择然后点击"提交". 在上传二进制文件并提交首个 App 内 ...
- 2018版苹果开发者设置内购(App内购买项目)、税务、银行问题,开通苹果支付
项目中使用到了中间货币(金币)的形式来进行功能使用,模式是使用RMB换成-金币比如:(1RMB = 10金币),所以会集成第三方的支付平台,使用了微信和支付宝的第三方平台过后,发现审核失败,被苹果拒绝 ...
- App内购买退款流程2017
1.点击https://getsupport.apple.com/?caller=cups&PRKEYS=进入联系支持 2.点击应用软件与软件 3.点击iOS 应用软件 4.点击AppStor ...
- 如何对 iOS App 内购买项目进行测试?
1.App内如果有订阅项目,功能是不是可用的,如何进行测试呢?如果你的Apple ID 正好是开发者账号,那么用Xcode 运行的时候,到付费那一步,会有提醒现在是沙盒测试环境不会真实扣款,直接可以进 ...
- iOS开发创建App内购买项目发现元数据丢失
创建内购项目的时候,第一个内购项目由于没有提交截图,出现了元数据丢失问题,后来两个项目添加了截图还是出现元数据丢失问题,他们给我开的权限只是开发者权限,我能创建内购条目,但是创建好了之后不能修改内购条 ...
- IOS应用内购买App开发完整流程
2019独角兽企业重金招聘Python工程师标准>>> 看了一些网上教程,基本上是老版本的了.我针对自己遇到的一些问题,结合官方文档把IAP(In-App Purchase)过程梳理 ...
最新文章
- 区块链相关论文研读3- 关于超级账本Hyperledger Fabric的性能优化
- 人工智能的本质是最优化过程
- vm显示打不开 /dev/vmmon:Broken pipe
- haproxy的丰富特性简介
- python 多维数组轴_python – numpy通过任意轴重塑多维数组
- 教师资格证综合素质思维导图
- ICE专题:实战分布式的Hello Word 【原创】
- Java基础练习之流程控制(一)
- 【天梯选拔月赛】寻宝路线(dp)
- 数据结构 -- 景区旅游信息管理系统
- python qq自动接收文件_python学习之 实现QQ自动发送消息
- 自适应滤波器(Adaptive Filter)(1)--简介
- TypeError: from_buffer() cannot return the address of the raw string within a str or unicode or byte
- CM108AH和DP108/DP108T的区别
- 将OpenGL渲染的结果保存为图片
- outlook2013配置
- L1-058 6翻了 (15 分)
- 《30天自制操作系统》第1天
- 激烈竞争环境下,数字资产交易所们该如何突破?
- 百度网盘15G邀请码