相关文章推荐
道上混的紫菜汤  ·  Visual ...·  1 年前    · 
大力的足球  ·  java.io.FileNotFoundEx ...·  1 年前    · 
痴情的凉面  ·  pillow ...·  2 年前    · 

随着CSDN APP用户 iOS 15以上系统占比覆盖度到98%,我们与支付中台决定接入StoreKit 2,彻底告别掉单,恶意退款等iOS内购遇到的问题。本文不阐述,StoreKit2 的详细说明,如果有兴趣的可以查看 官方文档 。本文只演示项目接入代码。

因为StoreKit2 只支持 Swift语言,下面案例均用Swift来实现,如有OC项目需要混编,可以暴漏了方法给OC调用就行。

获取商品列表

     /// 通过 productIds 请求 Product 列表
    /// - Parameter productIds: product ids
    /// - Returns: Product 列表
    public func requestProducts(productIds: [String]) async -> [Product]? {
        products = try? await Product.products(for: Set.init(productIds))
        return products

拉起应用内购买(唤起支付)

StoreKit 2 支持传入一个appAccountToken 这个值必须是UUID格式,这个UUID可以关联我们的订单和苹果的交易。我们可以将我们的订单编号,补充转换成UUID格式。这样我们就可以保证我们的订单与苹果订单唯一绑定,如果有发生掉单,用户可以拿着交易凭证上面的订单号,通过苹果给的查询交易的接口,先查询到苹果的交易id,然后再找到我们的订单号,完成补单操作。

/// 发起支付
    /// - Parameter product: Product对象
    public func purchase(product: Product, uid: String) async throws -> Transaction? {
        guard purchaseState != .inProgress else {
            throw PurchaseException.purchaseInProgressException
        purchaseState = .inProgress
        //App account token
        //用于将用户订单ID 绑定到交易(Transcation)中,即可建立苹果的交易订单数据与用户信息的映射关系,方便数据整合与追溯
        let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: uid)!)
        //发起支付流程
        guard let res = try? await product.purchase(options: [uuid]) else {
            purchaseState = .failed
            throw PurchaseException.transactionVerificationFailed
        var validateTransaction: Transaction? = nil
        switch res {
        case .success(let verificationResult):
            //购买状态:成功
            print("用户购买成功")
            purchaseState = .complete
          //可以将交易ID回传给服务端,服务端通过调用Appstore API来验证交易的可信,然后下发对应权益
          let checkResult = checkTransactionVerificationResult(verificationResult)
            if !checkResult.verified {
                purchaseState = .failedVerification
                throw PurchaseException.transactionVerificationFailed
            validateTransaction = checkResult.transaction
            //结束交易
            await validateTransaction!.finish()
        case .userCancelled:
            //购买状态:用户取消
            print("用户取消购买")
            purchaseState = .cancelled
            throw PurchaseException.purchaseUserCancelled
        case .pending:
            //购买状态:进行中
            print("用户购买中")
            purchaseState = .pending
        default:
            //购买状态:未知
            print("用户购买状态:未知")
            purchaseState = .unknown
        return validateTransaction

交易信息校验

     /// 校验
    /// - Parameter result: 支付返回结果
    /// - Returns: 是否验证成功
    private func checkTransactionVerificationResult(_ result: VerificationResult<Transaction>) -> (transaction: Transaction, verified: Bool) {
        //Check whether the JWS parses StoreKit verification.
        switch result {
        case .unverified(let transaction, _):
            //StoreKit parses the JWS, but it fails verification.
            return (transaction: transaction, verified: false)
        case .verified(let transaction):
            //The reult is verified. Return the unwrapped value.
            return (transaction: transaction, verified: true)

交易状态监听

我们测试发现,沙盒环境会将已经完成的订单也会通过该方法通知过来,所以服务端要对已经验证过,下发权益的交易做处理。

/// 支付监听事件
    public func listenForTransaction(completion:@escaping (Transaction) -> Void) -> Void {
         Task.detached {
            for await verificationResult in Transaction.updates {
                let checkResult = self.checkTransactionVerificationResult(verificationResult)
                if checkResult.verified {
                    let validatedTransaction = checkResult.transaction
                    await validatedTransaction.finish()
                    //有未完成的订单,需要重新发送给服务端验证,是否下发权益
                    completion(validatedTransaction)
                } else {
                    print("Transaction failed verification.")
/// 恢复购买
    public func reStorePurchase(){
        Task {
            try? await AppStore.sync()

StoreKit 2 也提供了退款方法,使得退款流程更加简洁。沙盒测试退款,也更加容易。

/// 发起退款
    /// - Parameters:
    ///   - transactionId: transaction.originalID
    ///   - scene: Window scene
    public func refunRequest(for transactionId: UInt64, scene: UIWindowScene!) async {
        do {
            let res = try await Transaction.beginRefundRequest(for: transactionId, in: scene)
            switch res {
            case .userCancelled:
                // Customer cancelled refund request.
                print("用户取消退款。")
            case .success:
                print("退款提交成功。")
                // Refund request was successfully submitted.
            @unknown default:
                print("退款返回错误:未知")
        catch StoreKit.Transaction.RefundRequestError.duplicateRequest {
            print("退款请求错误:重复请求")
        catch StoreKit.Transaction.RefundRequestError.failed {
            print("退款请求错误:失败")
        catch {
            print("退款请求错误:其他")

StoreKit 1 兼容

因为StoreKit2 只支持iOS 15以上系统设备,那对于低于iOS 15的设备还需要使用StoreKit1,并且StoreKit2 不支持 AppStore 活动与AppStore 订阅SKU的推广,对于有这个需求的也是需要使用StoreKit1来实现

- (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
    // product里存放的有我们配置在App Store的产品ID,以及价格等等
    return NO;

StoreKit1 中,如果给SKPayment 设置了applicationUsername 并且这个值是UUID类型的话,服务端在调用苹果API的时候,也是可以拿到这个值的。这块可以在官网看到具体介绍。所以我们可以和StoreKit2 一样,给applicationUsername 增加UUID类型的订单号绑定。这样不管是StoreKit1 还是StoreKit2,都不会有掉单找不到订单的风险。

SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
if ([payment respondsToSelector:@selector(setApplicationUsername:)])
   payment.applicationUsername = uuid;
[[SKPaymentQueue defaultQueue] addPayment:payment];

服务端不再采用原来的上传沙盒票据校验的方式,统一使用App Store Server API JWS方式来验证。这块具体查看官方文档就行。对于自动续订,及退款,使用 App Store Server Notifications 来实现,续订权益下发,及退款的权益回收。

文章目录引言I 、StoreKit 2 只支持 iOS 15+1.1 Choosing a StoreKit API for In-App Purchase I 、StoreKit 2 只支持 iOS 15+ StoreKit 2 for Swift only! https://developer.apple.com/videos/play/wwdc2021/10114 Manage in-app purchases on your server https://developer.apple
它能做什么 DEStoreKitManager是一个 MIT 许可的库,可以简化 iOS 应用内买。 它会自动为您处理 IAP 样板,因此您可以专注于运输和制作更好的产品。 DEStoreKitManager旨在尽可能灵活和轻量级地使用。 您可以使用区块或委托从 App Store 获取产品并进行买交易。 使用块,管理 StoreKit 交互从未如此简单:您可以将所有逻辑封装在一段代码中。 如果您想改用委托, DEStoreKitManager的架构也使其更容易。 对于每个产品提取或买,您可以拥有不同的委托,并且每个委托只会收到有关其发起的提取/买的通知。 您可以让多个委托同时进行任意数量的获取和/或买,而无需担心找出哪个委托负责什么。 默认情况下, DEStoreKitManager还会自动缓存您在内存中收到的产品,因此您无需跟踪   本文讲解iOS系统框架StoreKit中的SKStoreProductViewControllerSKStoreReviewController这两个Controller。 SKStoreProductViewController   这个是系统自带的控制器类,用于在我们的应用内展示AppStore中App的界面,是iOS6.0之后系统对我们开放的。   内容不... public class MXLiveIAPStoreV2 { static let shared: MXLiveIAPStoreV2 = MXLiveIAPStoreV2() typealias Transaction = ...
原文地址:https://zou8944.com/2022/05/13/IAP-StoreKit2%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/ TL;DR; 202110月,苹果发布了StoreKit2。新API和流程看起来更加简化,但是苹果官方文档并没有让开发者接入变得简单,调试起来也是各种问题,这方便和StoreKit1一样做的不好。梳理是必要的。 官方文档虽然有组织结构,但并不清晰,按照如下结构去看,能够.
StoreKit2 示例应用教程 storekit2-demo-appA demo app for the StoreKit2 blog post项目地址:https://gitcode.com/gh_mirrors/st/storekit2-demo-app 项目介绍 StoreKit2 示例应用是一个由 RevenueCat 提供的开源项目,旨在帮助开发者理解和实现 StoreKit2 AP...