IAP资料合集

138-0432-1111
中文完整介绍:

https://blog.csdn.net/jjjjjj123321/article/details/78481248

开发文档:

https://developer.apple.com/in-app-purchase/

配置文档:

https://help.apple.com/itunes-connect/developer/#/dev1f52cd4dd?sub=dev456315af2

注意事项:

https://developer.apple.com/library/content/technotes/tn2259/_index.html

一 四种应用内支付类型:

1 Consumable

消耗型的,越用越少,可反复购买,比如游戏中的水晶。

2 Non-Consumable

一次性购买,不会过期,比如P图app的滤镜。

3 Auto-Renewable Subscriptions

用户可以购买服务的访问权限或者周期性更新的内容。比如按月访问云存储,或者订阅周刊。用户会定期付费,直到他们决定取消订阅。看意思是自动续费,类似于腾讯视频会员的自动续费。

4 Non-Renewing Subscriptions

Users can purchase access to services or content for a limited duration, such as a season pass to streaming content. This type of subscription does not renew automatically, so users need to renew each time.

用户可以购买服务或者内容的访问权限,这个是有时间限制的。比如腾讯视频的一个月的会员,这个订阅不会自动更新(自动续费),用户需要主动更新(主动付费)

二 准备工作:

1 签署Paid Applications Agreement协议,设置银行账户和税费信息

地址:https://help.apple.com/itunes-connect/developer/#/devb6df5ee51

2 配置Xcode

地址:http://help.apple.com/xcode/mac/current/#/dev88ff319e7

3 在iTunes Connect上创建In-App Purchases

include details such as name, pricing, and description that highlights the features and functionality of your in-app purchase.

地址:https://help.apple.com/itunes-connect/developer/#/devae49fb316

三 设计&开发:

1 设计你的IAP商店

设计指导:https://developer.apple.com/videos/play/wwdc2014/218/

优化指导:https://developer.apple.com/videos/play/wwdc2014/303/

2 实现IAP

通过StoreKit Framework将IAP商店嵌入你的app,保证内容和服务的购买过程安全可靠。

编程指导:

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267

该框架会连接App Store,安全地处理用户的支付,提醒他们授权支付。之后该框架会通知你的app,提供对应的产品给用户。使用IAP来收集 支付及支付以外的特性和内容。

image
image

使用IAP你可以实现以下场景:

  • 一个基本的app版本,提供了额外的高级特性

  • 一个杂志app,允许用户购买和下载新的期刊

  • 一个游戏提供新的难度供玩家探索

  • 一个在线游戏,允许用户购买虚拟物品

image

用户浏览的你app的商店,你的app展示出示的产品。

用户选择想要购买的产品,app从App Store请求支付。

App Store处理支付请求,你的app提供用户购买的产品。

[1] 获取产品id

在你app上出售的每一个产品都有一个唯一的id。你的app使用这些产品id来从app store获取产品信息,比如价格,当用户购买这些产品时,也是使用这个id来提交支付请求的。app可以通过app bundle中存储一份id列表,也可以从服务器获取id列表(要考虑版本兼容性问题,最好带上支持的最低版本号)。【简单来说,就是id要从自己服务器获取,要注意版本兼容性问题】

[2] 获取产品信息

为了确保你的用户看到的都是实际可购买的产品,在展示你的商店之前,先去app store上查一下产品信息。

Use a products request object to query the App Store

First, create an instance of SKProductsRequest and initialize it with a list of product identifiers.

Be sure to keep a strong reference to the request object; otherwise, the system might deallocate the request before it can complete.

(1) 创建并初始化一个SKProductsRequest,确保对请求对象的强引用,否则,系统可能在请求完成前销毁它。

(2) 这个请求获取产品的正确信息,还有无效产品的id列表。并调用delegate来处理这个结果。

(3) delegate必须实现SKProductsRequestDelegate协议,来处理 App Store返回的响应。

代码:

  • (void)validateProductIdentifiers:(NSArray *)productIdentifiers

{

SKProductsRequest *productsRequest = [[SKProductsRequest alloc]

initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];

// Keep a strong reference to the request.

self.request = productsRequest;

productsRequest.delegate = self;

[productsRequest start];

}

// SKProductsRequestDelegate protocol method

  • (void)productsRequest:(SKProductsRequest *)request

didReceiveResponse:(SKProductsResponse *)response

{

self.products = response.products; // 产品对象列表

for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {

// Handle any invalid product identifiers.

}

[self displayStoreUI]; // Custom method

}

当用户购买了产品,你需要根据相应的产品对象创建一个支付请求,所以要把返回给delegate的产品对象列表保存下来。如果你app上卖的产品会发生改变,你可能需要创建一个类,来封装对产品对象的引用,以及其他信息(比如:你从服务器获取的图片和描述文本)。收到无效的产品id,通常说明你app的产品id列表存在错误,很有可能是iTunes Connect上配置不正确导致的。

[3] 展示app的商店UI

只有用户可以支付的时候才显示iap商店,价格要明确,以下是格式化显示价格的方法:

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];

[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];

[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

[numberFormatter setLocale:product.priceLocale];

NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];

[4] 请求支付

(1) 创建一个支付请求

当用户选择购买一个产品,利用产品对象创建一个支付请求,可以设置购买数量。

SKProduct *product = <# Product returned by a products request #>;

SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];

payment.quantity = 2;

(2) 防欺诈

app store使用irregular activity detection engine来检测欺诈行为。一些app可以提供额外的信息来提高引擎对不正常交易的探测能力。If your users have an account with you, in addition to their App Store accounts, provide this additional piece of information when requesting payment.

例如,考虑以下两种情况。正常情况下,服务器上的不同用户,使用各自不同的app store账号来购买游戏金币。相反,同一个用户使用不同的app store账号来多次购买金币,app store是无法探测到这种不正常的情况的,app store需要你提供该用户在你服务器上的账号。通过以下代码获得用户账号的哈希值,然后赋值给applicationUsername属性。

import <CommonCrypto/CommonCrypto.h>

// Custom method to calculate the SHA-256 hash using Common Crypto

  • (NSString )hashedValueForAccountName:(NSString)userAccountName

{

const int HASH_SIZE = 32;

unsigned char hashedChars[HASH_SIZE];

const char *accountName = [userAccountName UTF8String];

size_t accountNameLen = strlen(accountName);

// Confirm that the length of the user name is small enough

// to be recast when calling the hash function.

if (accountNameLen > UINT32_MAX) {

NSLog(@"Account name too long to hash: %@", userAccountName);

return nil;

}

CC_SHA256(accountName, (CC_LONG)accountNameLen, hashedChars);

// Convert the array of bytes into a string showing its hex representation.

NSMutableString *userAccountHash = [[NSMutableString alloc] init];

for (int i = 0; i < HASH_SIZE; i++) {

// Add a dash every four bytes, for readability.

if (i != 0 && i%4 == 0) {

[userAccountHash appendString:@"-"];

}

[userAccountHash appendFormat:@"%02x", hashedChars[i]];

}

return userAccountHash;

}

(3) 提交支付请求

Adding a payment request to the transaction queue submits it to the App Store. If you add a payment object to the queue multiple times, it’s submitted multiple times—the user is charged multiple times and your app is expected to deliver the product multiple times.

通过将支付请求添加到事务队列来将其提交到app store。如果你多次增加一个支付对象到队列,就会提交多次,用户就会消费多次,你的产品,讲道理,就应该给用户提供多次产品。所以要锁UI,或者锁住按钮。

[[SKPaymentQueue defaultQueue] addPayment:payment];

[5] 等待app store,处理事务

你的app提交的每一个支付请求,都有一个必须处理的事务。

在你的app与app store之间通信的场景中,事务队列扮演了一个核心角色。当事务的状态发生变化时,比如,支付成功,StoreKit会调用事务队列的观察者。哪个类做为观察者,由你来决定。观察者必须遵循 SKPaymentTransactionObserver协议。你的app不必轮询事务的状态。除了支付以外,你还可以通过事务队列下载Apple-hosted(托管?) content and to find out that subscriptions have been renewed。

当你的app启动时,注册一个事务队列观察者,确保观察者可以在任何时候处理事务。比如,考虑这种情况:用户在进入隧道之前,正在你的app中购买某些东西。你的app没有为用户提供购买的内容,因为没有网络连接。下一次你的app启动,StoreKit会再次调用你的事务队列观察者,你需要在此时为用户提供购买的内容。类似的,如果你的app没有成功的标记一个事务已经处理完成,StoreKit会在每次你的app启动时,调用观察者,直到事务正确的处理完成

// app启动时,添加观察者

  • (BOOL)application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

/* ... */

[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];

}

在观察者类中实现 paymentQueue:updatedTransactions: 方法,StoreKit会在事务状态发生变化时,调用该方法,比如支付完成。事务的状态告诉你,你的app需要如何处理。

image

SKErrorDomain

  • (void)paymentQueue:(SKPaymentQueue *)queue

updatedTransactions:(NSArray *)transactions

{

for (SKPaymentTransaction *transaction in transactions) {

switch (transaction.transactionState) {

// Call the appropriate custom method for the transaction state.

case SKPaymentTransactionStatePurchasing: // 处理中

[self showTransactionAsInProgress:transaction deferred:NO];

break;

case SKPaymentTransactionStateDeferred: // 延期

[self showTransactionAsInProgress:transaction deferred:YES];

break;

case SKPaymentTransactionStateFailed: // 失败

[self failedTransaction:transaction];

break;

case SKPaymentTransactionStatePurchased: // 购买成功

[self completeTransaction:transaction];

break;

case SKPaymentTransactionStateRestored: // 已经购买过了

[self restoreTransaction:transaction];

break;

default:

// For debugging

NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));

break;

}

}

}

[6] 存储购买记录

购买完成后,你的app需要保存一个持久化的购买记录。你的app用户使用该购买记录,在app启动后继续使用购买到的产品。这个记录还要用来恢复购买。具体见Restoring Purchased Products。你的app的持久化策略依赖于产品的类型和iOS版本。

For non-consumable products and auto-renewable subscriptions in iOS 7 and later, use the app receipt as your persistent record.

For non-renewing subscriptions, use iCloud or your own server to keep a persistent record.

For consumable products, your app updates its internal state to reflect the purchase, but there’s no need to keep a persistent record because consumable products aren’t restored or synced across devices. Ensure that the updated state is part of an object that supports state preservation (in iOS) or that you manually preserve the state across app launches (in iOS or macOS). For information about state preservation, see State Preservation and Restoration.

使用app收据来保存。

app收据包含了用户的购买记录,该记录由苹果加密。 Receipt Validation Programming Guide

消费型产品的信息在他们被支付后,会被加入到收据中,并且会一直保存在该收据中,直到你完成事务。在你完成事务后,这一信息会在下一次更新收据时,被删除,比如,下次用户有购买行为时。其他类型的产品会在支付时生成收据,并且会一直保存在收据中。

Persisting a Value in User Defaults or iCloud

if USE_ICLOUD_STORAGE

NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];

else

NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];

endif

[storage setBool:YES forKey:@"enable_rocket_car"];

[storage setObject:@15 forKey:@"highest_unlocked_level"];

[storage synchronize];

Persisting a Receipt in User Defaults or iCloud

if USE_ICLOUD_STORAGE

NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];

else

NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];

endif

NSData *newReceipt = transaction.transactionReceipt;

NSArray *savedReceipts = [storage arrayForKey:@"receipts"];

if (!savedReceipts) {

// Storing the first receipt

[storage setObject:@[newReceipt] forKey:@"receipts"];

} else {

// Adding another receipt

NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt];

[storage setObject:updatedReceipts forKey:@"receipts"];

}

[storage synchronize];

Persisting Using Your Own Server

Send a copy of the receipt to your server along with some kind of credentials or identifier so you can keep track of which receipts belong to a particular user. For example, let users identify themselves to your server with an email or user name, plus a password. Don’t use the identifierForVendor property of UIDevice—you can’t use it to identify and restore purchases made by the same user on a different device, because different devices have different values for this property.

[7] 解锁app功能

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

NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];

// Custom method to work with receipts

BOOL rocketCarEnabled = [self receipt:receiptData

includesProductID:@"com.example.rocketCar"];

[8] 完成事务

Finishing a transaction tells StoreKit that you’ve completed everything needed for the purchase

Unfinished transactions remain in the queue until they’re finished, and the transaction queue observer is called every time your app is launched so your app can finish the transactions.

Your app needs to finish every transaction, regardless of whether the transaction succeeded or failed.

Complete all of the following actions before you finish the transaction:

  • Persist the purchase.
  • Download associated content.
  • Update your app’s UI to let the user access the product.

To finish a transaction, call the finishTransaction: method on the payment queue.

SKPaymentTransaction *transaction = <# The current payment #>;

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

After you finish a transaction, don’t take any actions on that transaction or do any work to deliver the product. If any work remains, your app isn’t ready to finish the transaction yet.

api文档:

https://developer.apple.com/documentation/storekit

3 验证收据:

Receipts provide a valuable record of the sale. Consider using receipt validation code to protect your content and prevent unauthorized purchases.

收据提供了一个有价值的销售记录。考虑使用收据验证代码来保护您的内容,防止未授权的购买。

编程指导:

https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Introduction.html#//apple_ref/doc/uid/TP40010573-CH105-SW1

四 测试

1 交易测试

使用沙箱测试环境来测试IAP,不会发生真实的消费。

创建一个沙箱测试账号:http://help.apple.com/itunes-connect/developer/#/dev8b997bee1

测试步骤:

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/ShowUI.html#//apple_ref/doc/uid/TP40008267-CH3-SW11

2 完整用户体验测试

在App Store上发布您的应用程序之前,使用TestFlight可以从更广泛的用户那里获取有关您的应用和应用内购买的宝贵反馈。在iTunes Connect中邀请您的团队中的用户,可以通过电子邮件,增加10000名测试人员,所有应用程序内购买在beta测试期间是免费的,并且在测试期结束时不会继续。

https://developer.apple.com/testflight/

3 推荐的测试步骤

(1) 在iTunes Connect上创建一个测试账号

(2) 在一台调试手机上,登出app store。然后编译运行你的app

(3) 使用你的app来产生一个iap。当提示要登录app store的时候,使用你的测试账号。注意文案“[Environment: Sandbox]”是提示的一部分,表明你连的是测试环境

(4) 如果没有[Environment: Sandbox],那么你连的是生产环境。

注意,不要用测试账号登录生产环境,如果你这样做,测试账号就废了,再也不能用了。

http://blog.csdn.net/xiaominghimi/article/details/6937097

坑:

http://news.ipadown.com/16391

五 发布

1 提交审核

2 在App Store上推广您的IAP

在iOS11上,您可以在您的产品页面,推广20个IAP产品。

https://developer.apple.com/app-store/promoting-in-app-purchases/

3 发布促销代码

这个就是试用代码,每个app可以分发1000个code

疑问:

放到商店里的产品是怎么定义的?是否可以在线动态调整?还是只能通过iTunes Connect配置?

整不明白,就到这里提问:

https://developer.apple.com//contact/app-store/

image

中文资料:

http://blog.csdn.net/darling_shadow/article/details/51538267

itunes connect填写银行等信息:

http://www.jianshu.com/p/c7cf65911bc1

这个一定要看,最完整阐述:

http://blog.csdn.net/teng_ontheway/article/details/47011813

http://openfibers.github.io/blog/2015/02/28/in-app-purchase-walk-through/

http://blog.csdn.net/teng_ontheway/article/details/47016373

http://blog.csdn.net/teng_ontheway/article/details/47023119

第三方库:

https://github.com/robotmedia/RMStore

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