实现和开发所有用于正确管理应用内购的工具可能非常复杂且耗时。你应该将这宝贵的时问花在构建您的应用程序上!
IAPHUB 拥有您需要的所有功能来增加您的销售
功能 | |
---|---|
收据验证 - 发送收据,我们会处理其余事情。 | |
网络钩子 - 直接将您的服务器接收网络钩子,以通知任何事件,如购买或订阅取消。 | |
实时分析 - 默认提供所有销售、订阅、客户以及改善您收入所需的所有洞察力。 | |
A/B 测试 - 测试不同的定价,并获得最佳表现的价格的分析。 | |
产品细分 - 根据定义的标准,如国家,向您的客户提供不同的产品或定价。 | |
客户管理 - 容易访问客户的详细信息,包括一页纸上的过去交易和活动订阅。 |
安装
在实际应用程序中实施应用内购应该易如反掌!
-
在 IAPHUB 上创建账户。
-
将 Iaphub 添加到 Podfile
pod 'Iaphub', '~> 4.0'
。 -
运行
pod install
。 -
确保您的 ios 项目的 应用内购买能力 在 XCode 上已启用
-
阅读我们关于 设置应用程序 的完整指南。
启动
导入 Iaphub
并在 application(_:didFinishLaunchingWithOptions:)
中执行 start 方法。
import Iaphub
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
Iaphub.delegate = self
Iaphub.start(
// The app id is available on the settings page of your app
appId: "5e4890f6c61fc971cf46db4d",
// The (client) api key is available on the settings page of your app
apiKey: "SDp7aY220RtzZrsvRpp4BGFm6qZqNkNf",
// The user id, if you do not specify one an anonymous id will be generated (id prefixed with 'a:')
// You can provide it if the user is already logged in on app start
userId: "42",
// If you want to allow purchases when the user has an anonymous user id
// If you're listenning to IAPHUB webhooks your implementation must support users with anonymous user ids
// This option is disabled by default, when disabled the buy method will return an error when the user isn't logged in
allowAnonymousPurchase: true
)
return true
}
}
代理
IAPHUB 暴露了不同的事件,您可以通过使用 IaphubDelegate
来监听它们。
它们都是可选的,但建议使用 didReceiveUserUpdate
以了解何时刷新产品的状态。
extension AppDelegate: IaphubDelegate {
func didReceiveUserUpdate() {
// Called when the user has already been fetch and is updated
// It means the products for sale or active products are different from the one you previously loaded using getProductsForSale/getActiveProducts
// You should refresh your view with the new state of your products
// When using the login/logout method, the user is reset, meaning this event won't be called until the user has been loaded first using the getProductsForSale/getActiveProducts methods
}
func didReceiveDeferredPurchase(transaction: IHReceiptTransaction) {
// Called when a deferred purchase has been processed
// A deferred purchase is a purchase processed 'outside' of the buy method
}
func didReceiveError(err: IHError) {
// Called when IAPHUB has detected an error
// It can be interesting to log unexpected errors
if (err.code == "unexpected") {
print("Unexpected error: \(err.localizedDescription)")
}
}
func didReceiveBuyRequest(sku: String) {
// Called when a purchase intent is made from outside the app (from a promoted In-App purchase for example), IAPHUB is allowing all of them by default
// If you want to allow/disallow a purchase intent (to wait until the user is logged in for example) you can implement this method
// You'll have to call the buy method whenever you're ready
// Also note you'll have a callback to know when the transaction is done (you woudn't otherwise)
Iaphub.buy(sku: sku, { (err, transaction) in
})
}
func didProcessReceipt(_ err: IHError?, _ receipt: IHReceipt?) {
// Called after a receipt has been processed
}
}
登录
调用 login
方法来验证用户。
didReceiveUserUpdate
才会被调用。
Iaphub.login(userId: "3e4890f6c72fc971cf46db5d", { (err: IHError?) in
// On a success the err should be nil
});
获取用户 ID
调用 getUserId
方法获取登录用户的用户 ID。
如果没有用户登录,将返回匿名用户 ID(以 'a:' 预缀开头)。
let userId = Iaphub.getUserId()
登出
调用 logout
方法来注销用户。
用户将返回到匿名用户 ID(以 'a:' 预缀开头)。
didReceiveUserUpdate
才会被调用。
Iaphub.logout();
设置用户标签
调用setUserTags
方法来更新用户标签。
用户标签将显示在IAPHUB仪表板的用户页面上。
当使用IAPHUB的智能列表时,您将能够根据用户标签返回不同的产品。
// To set a tag
Iaphub.setUserTags(tags: ["gender": "male"], { (err: IHError?) in
// On a success err should be nil
});
// To remove a tag pass a empty string
Iaphub.setUserTags(tags: ["gender": ""], { (err: IHError?) in
// On a success err should be nil
});
一些细节
- 必须在意APHUB仪表板中创建标签(否则方法将引发错误)
- 在意APHUB仪表板中创建标签时,您必须检查允许从客户端编辑标签的选项(否则您只能通过从您的服务器使用IAPHUB API来编辑标签)
- 标签键长度限制为32个字符
- 标签值长度限制为64个字符
设置设备参数
调用setDeviceParams
方法来设置设备的参数
当使用IAPHUB的智能列表时,您将能够根据设备参数返回不同的产品。
// For instance you can provide the app version on app launch
// Useful to return a product only supported in a new version
Iaphub.setDeviceParams(params: ["appVersion": "2.0.0"])
// To clear the device params
Iaphub.setDeviceParams(params: [:])
一些细节
- 参数不会在设备上保存,如果应用程序重新启动,则不会持续保存
- 参数不会在IAPHUB上保存,只是在获取销售产品时提供给API
- 参数键限制为32个字符,必须是有效的键(《`^[a-zA-Z_]*$`》)
- 参数值限制为32个字符
- 您可以提供最多5个参数
获取销售产品
调用getProductsForSale
方法来获取用户的销售产品
当显示产品销售列表页面时,您应该使用此方法
didReceiveError
方法中返回一个“意外”错误。
Iaphub.getProductsForSale({ (err: IHError?, products: [IHProduct]?) in
// On a success err should be nil
})
获取有效产品
如果您在客户端依赖IAPHUB(而不是使用服务器与webhook)来检测用户是否有有效产品(自动续订订阅、不续订订阅或非消耗性产品),则应使用getActiveProducts
方法。
订阅状态
值 | 描述 |
---|---|
active | 订阅处于活动状态 |
grace_period | 订阅处于宽限期,用户还应访问您订阅提供的功能 |
retry_period | 订阅处于重试期,您必须限制对您订阅提供的功能的访问,并显示一条信息,要求用户更新其支付信息 |
paused | (仅限Android)订阅已暂停,将在稍后日期自动恢复(autoResumeDate 属性),您必须限制对您订阅提供的功能的访问 |
默认情况下,只有具有active
或grace_period
状态的订阅才会通过getActiveProducts
方法返回,因为在retry_period
或paused
状态下,您必须限制对您订阅提供的功能的访问。
如果您想在用户处于retry_period
或paused
状态时显示一条消息,您可以使用includeSubscriptionStates
选项。
Iaphub.getActiveProducts(includeSubscriptionStates: ["retry_period", "paused"], { (err: IHError?, products: [IHActiveProduct]?) in
// On a success err should be nil
})
获取所有产品
您还可以通过一个方法 getProducts()
来获取销售产品和活动产品。
Iaphub.getProducts({ (err: IHError?, products: IHProducts?) in
// On a success err should be nil
})
获取账单状态
getBillingStatus
方法将返回有用的信息,如果您在使用 getProducts
或 getProductsForSale
方法时遇到问题(例如如果未返回任何销售产品)。
let status = Iaphub.getBillingStatus()
// You should display an appropriate message if the billing is unavailable
if (status.error?.code == "billing_unavailable") {
// Display a message saying that the in-app billing isn't available on the device
}
// Check the products that were filtered from the products for sale
if (!status.filteredProductIds.isEmpty) {
// The product ids in the array were not returned by Google Play
}
购买产品
调用 buy
方法来购买产品
getProductsForSale
得到的产品的sku。
Iaphub.buy(sku: sku, { (err: IHError?, transaction: IHReceiptTransaction?) in
// Check error
if let err = err {
// Do not do anything if purchase cancelled or product already purchased
if (err.code == "user_cancelled" || err.code == "product_already_purchased") {
return
}
// The billing is unavailable (An iPhone can be restricted from accessing the Apple App Store)
else if (err.code == "billing_unavailable") {
return self.openAlert("In-app purchase not allowed")
}
// The product has already been bought but it's owned by a different user, restore needed to transfer it to this user
else if (err.code == "product_owned_different_user") {
return self.openAlert("You already purchased this product but it is currently used by a different account, restore your purchases to transfer it to this account")
}
// The user tried to purchase a product that is already going to be active on next renewal
else if (err.code == "product_change_next_renewal") {
return self.openAlert("The product will be changed on the next renewal date")
}
// The payment has been deferred (transaction pending, its final status is pending external action)
else if (err.code == "deferred_payment") {
return self.openAlert("Purchase awaiting approval, your purchase has been processed but is awaiting approval")
}
/*
* The remote server couldn't be reached properly
* The user will have to restore its purchases in order to validate the transaction
* An automatic restore should be triggered on every relaunch of your app since the transaction hasn't been 'finished'
*/
else if (err.code == "network_error") {
return self.openAlert("Please try to restore your purchases later (Button in the settings) or contact the support ([email protected])")
}
/*
* The receipt has been processed on IAPHUB but something went wrong
* It is probably because of an issue with the configuration of your app or a call to the Itunes/GooglePlay API that failed
* IAPHUB will automatically retry to process the receipt if possible (depends on the error)
*/
else if (err.code == "receipt_failed") {
return self.openAlert("We're having trouble validating your transaction, give us some time we'll retry to validate your transaction ASAP!")
}
/*
* The user has already an active subscription on a different platform (android or ios)
* This security has been implemented to prevent a user from ending up with two subscriptions of different platforms
* You can disable the security by providing the 'crossPlatformConflict' parameter to the buy method (Iaphub.buy(sku: sku, crossPlatformConflict: false))
*/
else if (err.code == "cross_platform_conflict") {
Alert.alert(
`Seems like you already have a subscription on a different platform`,
`You have to use the same platform to change your subscription or wait for your current subscription to expire`
);
}
// Any other error
return self.openAlert("We were not able to process your purchase, please try again later or contact the support ([email protected])")
}
/*
* The purchase has been successful but we need to check that the webhook to our server was successful as well (if you implemented webhooks)
* If the webhook request failed, IAPHUB will send you an alert and retry again in 1 minute, 10 minutes, 1 hour and 24 hours.
* You can retry the webhook directly from the dashboard as well
*/
if (transaction?.webhookStatus == "failed") {
self.openAlert("Your purchase was successful but we need some more time to validate it, should arrive soon! Otherwise contact the support ([email protected])")
}
// Everything was successful! Yay!
else {
self.openAlert("Your purchase has been processed successfully!")
}
})
恢复用户购买
调用 restore
方法来恢复用户购买
Iaphub.restore({ (err: IHError?, response: IHRestoreResponse?) in
if (err != nil) {
self.openAlert("Restore failed")
}
else {
self.openAlert("Restore successful")
}
})
显示管理订阅
调用 showManageSubscriptions
以显示 App Store 管理订阅页面。
Iaphub.showManageSubscriptions({ (err: IHError?) in
if (err != nil) {
self.openAlert("Couldn't redirect to the app store, please check your subscriptions directly from the App Store App")
}
})
属性
Prop | Type | 描述 |
---|---|---|
id | String |
产品 ID(从 IAPHUB 获得) |
type | String |
产品类型(可能值:'consumable', 'non_consumable', 'subscription', 'renewable_subscription') |
sku | String |
产品 SKU(例如: "membership_tier1") |
price | Double = 0 |
价格金额(例如:12.99) |
currency | String? |
价格货币代码(例如:"USD") |
localizedPrice | String? |
本地化价格(例如:"$12.99") |
localizedTitle | String? |
产品名称(例如:"Membership") |
localizedDescription | String? |
产品描述(例如:"加入会员社区") |
group | String? |
Group id(从 IAPHUB 获得) |
groupName | String? |
IAPHUB 上创建的产品分组名称(例如:"premium") |
subscriptionDuration | String? |
订阅周期持续时间,指定为 ISO 8601 格式(可能值:"P1W","P1M","P3M","P6M","P1Y") |
subscriptionIntroPhases | [IHSubscriptionIntroPhase]? |
订阅介绍阶段的顺序列表(介绍价格,免费试用) |
IHSubscriptionIntroPhase
Prop | Type | 描述 |
---|---|---|
type | String |
引导类型(可能的值:'试用', '引导') |
price | 双倍 |
引导价格金额(例如:2.99) |
currency | String |
引导价格货币代码(例如:英文”USD”) |
localizedPrice | String |
本地化引导价格(例如:"$2.99") |
cycleCount | String |
引导期中的周期数 |
cycleDuration | String |
使用ISO 8601格式指定的引导周期持续时间(可能的值:'P1W', 'P1M', 'P3M', 'P6M', 'P1Y') |
IHActiveProduct(从IHProduct继承)
Prop | Type | 描述 |
---|---|---|
购买 | String? |
购买id(来自IAPHUB) |
购买日期 | 日期? |
购买日期 |
平台 | String? |
购买的平台(可能的值:'ios', 'android') |
过期日期 | 日期? |
订阅过期日期 |
isSubscriptionRenewable | 布尔值 = false |
如果启用自动续订则为真 |
isFamilyShare | 布尔值 = false |
如果订阅是由家庭成员共享的(仅限iOS订阅)则为真 |
isPromo | 布尔值 = false |
如果使用优惠码购买则为真 |
promoCode | String? |
优惠码(仅在Android订阅中作为花名代码提供,一次性代码不可用)(iOS:值为优惠参考名称) |
originalPurchase | String? |
可续订订阅的原始购买id(来自IAPHUB) |
subscriptionRenewalProduct | String? |
下一次续订的订阅产品id(仅在当前产品不同时定义) |
subscriptionRenewalProductSku | String? |
下一次续订的订阅产品SKU |
subscriptionState | String? |
订阅状态 (可能的值:'active', 'grace_period', 'retry_period', 'paused') |
subscriptionPeriodType | String? |
订阅的当前阶段类型 (可能的值:'normal', 'trial', 'intro') |
IHReceiptTransaction(从IHActiveProduct继承)
Prop | Type | 描述 |
---|---|---|
webhookStatus | String? |
Webhook状态(可能的值:'成功', '失败', '禁用') |
IHProducts
Prop | Type | 描述 |
---|---|---|
productsForSale | [Product] |
销售产品 |
activeProducts | [ActiveProduct] |
活动产品 |
IHRestoreResponse
Prop | Type | 描述 |
---|---|---|
newPurchases | [ReceiptTransaction] |
在恢复过程中处理的新采购 |
transferredActiveProducts | [ActiveProduct] |
在恢复过程中(从另一用户处)转出的活动产品 |
IHBillingStatus
Prop | Type | 描述 |
---|---|---|
error | IHError? |
错误 |
filteredProductIds | [String] |
从销售产品中筛选的产品 |
IHError (继承自 LocalizedError)
Prop | Type | 描述 |
---|---|---|
message | String |
错误消息 |
code | String |
错误代码 |
完整示例
您应该查看示例应用。