Iaphub 4.2.3

Iaphub 4.2.3

Iaphub 维护。



Iaphub 4.2.3

  • Iaphub
IAPHUB

实现和开发所有用于正确管理应用内购的工具可能非常复杂且耗时。你应该将这宝贵的时问花在构建您的应用程序上!

IAPHUB 拥有您需要的所有功能来增加您的销售🚀

功能
📜 收据验证 - 发送收据,我们会处理其余事情。
📨 网络钩子 - 直接将您的服务器接收网络钩子,以通知任何事件,如购买或订阅取消。
📊 实时分析 - 默认提供所有销售、订阅、客户以及改善您收入所需的所有洞察力。
🧪 A/B 测试 - 测试不同的定价,并获得最佳表现的价格的分析。
🌎 产品细分 - 根据定义的标准,如国家,向您的客户提供不同的产品或定价。
👤 客户管理 - 容易访问客户的详细信息,包括一页纸上的过去交易和活动订阅。

安装

在实际应用程序中实施应用内购应该易如反掌!

  1. IAPHUB 上创建账户。

  2. 将 Iaphub 添加到 Podfile pod 'Iaphub', '~> 4.0'

  3. 运行 pod install

  4. 确保您的 ios 项目的 应用内购买能力 在 XCode 上已启用

  5. 阅读我们关于 设置应用程序 的完整指南。


如果您从 v2.X.X 迁移到 v3.X.X,请参阅 这些说明
如果您从 v3.X.X 迁移到 v4.X.X,请参阅 这些说明

启动

导入 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 方法来验证用户。

您应该提供一个不可预测且非公开的 id。 (不允许使用电子邮件)

用户将被重置,只有当用户首次加载数据(使用 getProductsForSale/getActiveProducts)之后,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:' 预缀开头)。

用户将被重置,只有当用户首次加载数据(使用 getProductsForSale/getActiveProducts)之后,didReceiveUserUpdate 才会被调用。

Iaphub.logout();

设置用户标签

调用setUserTags方法来更新用户标签。
用户标签将显示在IAPHUB仪表板的用户页面上。
当使用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方法来获取用户的销售产品
当显示产品销售列表页面时,您应该使用此方法

如果因为网络问题请求失败,则方法返回缓存的最新请求(如果可用,否则引发错误)。

如果产品通过API返回,但sku无法加载,它将被从列表中过滤掉,并在didReceiveError方法中返回一个“意外”错误。

Iaphub.getProductsForSale({ (err: IHError?, products: [IHProduct]?) in
  // On a success err should be nil
})

获取有效产品

如果您在客户端依赖IAPHUB(而不是使用服务器与webhook)来检测用户是否有有效产品(自动续订订阅、不续订订阅或非消耗性产品),则应使用getActiveProducts方法。

如果由于网络问题请求失败,方法将返回缓存中的最新请求(如果有未过期订阅,否则抛出错误)。

如果API返回了有效产品但sku无法加载,则将返回产品,但只带来自API的属性(价格、标题、描述等属性将不会返回)。

订阅状态

描述
active 订阅处于活动状态
grace_period 订阅处于宽限期,用户还应访问您订阅提供的功能
retry_period 订阅处于重试期,您必须限制对您订阅提供的功能的访问,并显示一条信息,要求用户更新其支付信息
paused (仅限Android)订阅已暂停,将在稍后日期自动恢复(autoResumeDate属性),您必须限制对您订阅提供的功能的访问

默认情况下,只有具有activegrace_period状态的订阅才会通过getActiveProducts方法返回,因为在retry_periodpaused状态下,您必须限制对您订阅提供的功能的访问。

如果您想在用户处于retry_periodpaused状态时显示一条消息,您可以使用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 方法将返回有用的信息,如果您在使用 getProductsgetProductsForSale 方法时遇到问题(例如如果未返回任何销售产品)。

  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 管理订阅页面。

ℹ️用户将被重定向到 iOS < 15.0 的 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 错误代码

完整示例

您应该查看示例应用