8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

RevenueCat-customerInfo.activeSubscriptions 始终返回值

Vardana Bhanot 2月前

71 0

这是一个生产错误,由于 customerInfo.activeSubscriptions 返回了之前订阅的所有产品标识符,因此之前订阅过的用户仍然可以访问所有 Pro 功能

返回了所有 customerInfo.activeSubscriptions ,之前订阅过的用户仍然可以访问所有 Pro 功能 productIdentifiers

customerInfo.entitlements 总是返回值。

我已经添加了

print("ENTITLEMENTS \(customerInfo.entitlements)")
                        
print("ACTIVE SUBS \(customerInfo.activeSubscriptions)")

这将返回

权利 [\'pro\':

我曾尝试使用

customerInfo.entitlements.active

但我得到了相同的结果。

我也尝试过删除缓存信息

    Purchases.shared.invalidateCustomerInfoCache()

另外,打开 Debug > StoreKit 时,不会显示交易。

这是验证订阅状态的代码。

func verifyIAPReceipt() {
    
    Purchases.shared.invalidateCustomerInfoCache()
   
    Purchases.shared.getCustomerInfo { (customerInfo, error) in
        if error == nil {
            if let customerInfo = customerInfo {
                
                if customerInfo.entitlements.active.isEmpty {
                    
                    print("ENTITLEMENTS IS EMPTY") <-- This is not executed

                    self.unsubscribe()

                } else {
                        
                        print("ENTITLEMENTS IS NOT EMPTY") <-- Executed

                    print("ENTITLEMENTS \(customerInfo.entitlements)")
                    print("ACTIVE SUBS \(customerInfo.activeSubscriptions)")

                    if customerInfo.activeSubscriptions.isEmpty == false {
                        print("Active Subscriptions is NOT empty") <-- Executed
                        
                        for s in customerInfo.activeSubscriptions  {
                            if s == "com.app.come.promonthly" || s == "com.app.com.proyearly"  {
                                subscribe() <-- Executed Unlocks features
                            } 
                        }
                    } else if customerInfo.activeSubscriptions.isEmpty == true {
                        print("Active Subscriptions are empty")
                        self.unsubscribe()
                    } else {
                        print("Active Subscription and profile is subscribed")
                    }
                }
                
            }
            
            
        } else {
            print("ERROR GETTING CUSTOMER INFO TO VERIFY RECEIPTS")
        }
    }
}

使用时 fetchPolicy: .fetchCurrent 返回有效订阅

    Purchases.shared.getCustomerInfo(fetchPolicy: .fetchCurrent, completion: { (customerInfo, error) in
        if error != nil {
            print("FETCH POLICY ERROR:\(error)")
        }
        
        if customerInfo != nil {
            print("FETCH POLICY ACTIVE SUBSCRIPTIONS:\(customerInfo!.activeSubscriptions)")
            print("FETCH POLICY ACTIVE ENTITLEMENTS:\(customerInfo!.entitlements)")
            print("FETCH POLICY ACTIVE ALL PURCHASED PRODUCT ID'S:\(customerInfo!.allPurchasedProductIdentifiers)")
        }
    })

输出

获取策略有效权利:[\'pro\':

获取政策有效权利:

自我.主动=[:],自我.验证=验证结果.未请求>

获取策略激活所有已购买的产品 ID:[\'com.appname.com.promonthly\', \'com.appname.com.proyearly\']

这是订阅代码

func subscribe() {

print("Subscribe")


//Revenue Cat

if let packages = offering?.availablePackages {
    for p in packages {
        if p.storeProduct.productIdentifier == selectedproductbundle {
                            
            Purchases.shared.purchase(package: p) { (transaction, customerInfo, error, userCancelled) in
                
                print("PURCHASE")
                
                if userCancelled {
                    print("User cancelled purchase")
                    return
                }
                
                if let err = error {
                    
                    if let error = error as? RevenueCat.ErrorCode {
                        print(error.errorCode)
                        print("ERROR: \(error.errorUserInfo)")
                        
                        switch error {
                        case .purchaseNotAllowedError:
                            errorDescription = "Purchases not allowed on this device."
                            showError.toggle()
                            
                        case .purchaseInvalidError:
                            errorDescription = "Purchase invalid, check payment source."
                            
                        default: break
                            
                        }
                        
                    }
                } else if customerInfo?.activeSubscriptions.isEmpty == false {
                    print("Unlocked Pro ")
                    
                    // Update profile
                    print("Customer INFO: \(customerInfo!)")
                    
                    print("Entitlements: \(customerInfo!.entitlements.all)") 
                    
                    if customerInfo != nil {
                        for s in customerInfo!.activeSubscriptions {
                            if s == "com.appname.com.promonthly" || s == "com.appname.com.proyearly"  {
                                subscribeToPro()
                            }
                        }
                    }
                } else {
                    print("PURCHASE WITH: \(String(describing: transaction?.productIdentifier)) && \(String(describing: customerInfo?.activeSubscriptions.count))")
                }
            }
        }
    }
}
}

我已经通过模拟器和设备进行了测试。

帖子版权声明 1、本帖标题:RevenueCat-customerInfo.activeSubscriptions 始终返回值
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Vardana Bhanot在本站《swift》版块原创发布, 转载请注明出处!
最新回复 (0)
  • @Toru 感谢您的回复,它正在恢复有效订阅。这也适用于生产用户,他们在取消订阅后仍然可以访问所有专业功能。

  • @Toru 我在 RevenueCat 中确实拥有权利,权利 > 产品(\'pro\')> 产品标识符(\'com.appname.com.promonthly\')。

  • PMF 2月前 0 只看Ta
    引用 4

    假设您在 RevenueCat 仪表板中正确配置了您的权利,它们应该始终返回正确的数据。

    您的代码中引起我注意的一件事是您正在尝试调用 .active 对象 entitlements 上的属性

    当您仔细查看该属性的实现时,您将看到以下内容:

    @objc var active: [String: EntitlementInfo] {
        return self.activeInAnyEnvironment
    }
    

    这意味着之前属于 TestFlight 组的任何用户,只要能够在沙盒中进行任何购买,仍然可以被授予该权利。

    如果您确实只想支持实际购买了订阅的客户,那么这种方法并不安全。

    这里的解决方案是专门使用 activeInCurrentEnvironment - 而不是 active aka activeInAnyEnvironment 。特别是在生产中。

    在我们的应用程序中,仅仅是为了我们自己开发应用程序时的方便,我们引入了以下扩展。

    extension EntitlementInfos {
        /// Returns activeInAnyEnvironment for TestFlight and DEBUG build and activeInCurrentEnvironment for AppStore version.
        var activeForConfig: [String: EntitlementInfo] {
            switch Distribution.current {
            case .testFlight, .debug: activeInAnyEnvironment
            case .appStore: activeInCurrentEnvironment
            }
        }
    }
    

    Distribution.current 值根据编译器标志和 AppStore 收据数据确定。

  • 如果用户已订阅该应用程序但随后取消订阅,以下代码仍会执行“subscribe()”

    虽然不清楚代码中的内容 unsubscribe() subscribe() 功能,但假设您希望 unsubscribe() 在用户不再有权访问订阅时执行该操作,那么您的条件检查似乎是错误的。

    if !customerInfo.entitlements.active.isEmpty {
        print("ENTITLEMENTS IS EMPTY") <-- This is not executed
        self.unsubscribe()
    } else {
        print("ENTITLEMENTS IS NOT EMPTY") <-- Executed
        //...
        subscribe() <-- Executed Unlocks features
    }
    

    上述代码应如下所示:

    if customerInfo.entitlements.active.isEmpty {
        // User no longer has any active subscription
        unsubscribe()
    } else {
        subscribe()
    }
    

    我不确定为什么 customerInfo.activeSubscriptions 即使订阅已过期仍会返回值......

  • 我正在使用 Swift 开发 iOS 音频播放器。目前,该播放器可以播放受 DRM 保护的视频,支持字幕,并允许您更改音轨。所有这些功能也

    我正在使用 Swift 开发适用于 iOS 的音频播放器。目前,该播放器可以播放受 DRM 保护的视频,支持字幕,并允许您更改音轨。使用 ChromeCast 时也可以使用所有这些功能。

    我想添加通过 AirPlay 播放受 DRM 保护的视频的支持。普通视频在 AirPlay 上播放毫无问题。

    尽管进行了广泛的研究,我仍然无法找到有关该主题的相关或最新信息,并且 iOS 文档也无济于事。

    有人能提供有关如何通过 AirPlay 启用受 DRM 保护的视频播放的指导或资源吗?

    func launchAirPlay() {
        let rect = CGRect(x: -100, y: 0, width: 0, height: 0)
        let airplayVolume = MPVolumeView(frame: rect)
        airplayVolume.showsVolumeSlider = false
        self.view.addSubview(airplayVolume)
        for view: UIView in airplayVolume.subviews {
            if let button = view as? UIButton {
                button.sendActions(for: .touchUpInside)
                break
            }
        }
        airplayVolume.removeFromSuperview()
        pause()
    }
    
  • 看来 RevenueCat 中的授权产品标识符有误。现在代码返回授权,但问题仍然存在,即未订阅的用户获得了授权和有效订阅。

  • \'此外,打开 Debug > StoreKit 时,不会显示交易。\' 您是否在 Xcode 中进行 StoreKit 测试?我想知道 RevenueCat 是否可以与 StoreKit 测试配合使用,因为他们可能只关心在真实环境或至少沙盒中发生的购买。您最好查看 RevenueCat 仪表板,看看用户是否真的取消了订阅(不仅仅是取消,因为取消和过期之间存在差距)

  • 我有以下 SwiftUI 视图:import SwiftUIstruct ContentView: View { var items = [1, 2] var body: some View { NavigationStack { List { ForEach(s...

    我有以下 SwiftUI 视图:

    import SwiftUI
    
    struct ContentView: View {
        var items = [1, 2]
        var body: some View {
            NavigationStack {
                List {
                    ForEach(self.items, id: \.self) { item in
                        DisclosureGroup(content: {
                            Text("Extra content for item \(item)")
                        }, label: {
                            HStack {
                                NavigationLink {
                                    DetailView()
                                } label: {
                                    Text("View Details")
                                }
                                .buttonStyle(.borderless)
                            }
                        })
                    }
                }
            }
        }
    }
    

    当我按下 中的任意位置时 DisclosureGroup ,我希望显示“额外内容”。当我特别按下“查看详细信息”按钮时,我希望导航到 DetailView .

    然而,目前, NavigationLink 似乎以某种方式覆盖了 的 ontap 手势 DisclosureGroup 。如果我单击 上的任意位置 DisclosureGroup ,它会导航到 DetailView - 仅当我明确单击 的 DisclosureGroup 箭头时才显示 \'额外内容\'。

    这个问题 的许多答案 :

    虽然更改 List ScrollView 确实有效,但它也会弄乱填充,而且我不确定是否有一种干净的方法将其应用于 ScrollView (如果 Apple List 将来更改样式,我希望我的视图能够自动更新而无需更改代码)。

    改变 but为并nStyle to .borderless 不能阻止 NavigationLink 覆盖 ontap 手势。

    带有 NavigationLink 或 的 selected 隐藏 isActive ,因为它们在 iOS 16 中已被弃用。

    同时,嵌入 NavigationLink 按钮内部并不会导航到 DetailView - 它只是执行按钮的操作。

  • StoreKit Testing 似乎可以与 RevenueCat 配合使用,但还有很多事情要做。你都做了这些吗?revenuecat.com/docs/test-and-launch/sandbox/…

  • 您可以使用 iOS 16 的编程导航方式,以及 Button 触发导航的方式。请使用以下方式之一:

    如果目标取决于 item 您在 中“循环”的 , ForEach ,这在您的情况下很可能就是这种情况。

    按顺序显示这三种方式:

    let items = [1, 2]
    @State private var presented = false
    var body: some View {
        NavigationStack {
            List {
                ForEach(self.items, id: \.self) { item in
                    DisclosureGroup {
                        Text("Extra content for item \(item)")
                    } label: {
                        HStack {
                            Button("View Details") {
                                presented = true
                            }
                            .tint(.primary) // remove the default tint of the button
                            .buttonStyle(.borderless)
                        }
                    }
                }
            }
            .navigationDestination(isPresented: $presented) {
                DetailView()
            }
        }
    }
    

    或者

    let items = [1, 2]
    // in reality the items are probably not Ints - replace this with whatever type of thing you are showing details of
    @State private var selectedItem: Int?
    var body: some View {
        NavigationStack {
            List {
                ForEach(self.items, id: \.self) { item in
                    DisclosureGroup {
                        Text("Extra content for item \(item)")
                    } label: {
                        HStack {
                            Button("View Details") {
                                selectedItem = item
                            }
                            .tint(.primary)
                            .buttonStyle(.borderless)
                        }
                    }
                }
            }
            .navigationDestination(item: $selectedItem) { item in
                DetailView(for: item)
            }
    
        }
    }
    

    或者

    let items = [1, 2]
    @State private var navigationPath: NavigationPath
    var body: some View {
        NavigationStack(path: $navigationPath) {
            List {
                ForEach(self.items, id: \.self) { item in
                    DisclosureGroup {
                        Text("Extra content for item \(item)")
                    } label: {
                        HStack {
                            Button("View Details") {
                                navigationPath.append(item)
                            }
                            .tint(.primary)
                            .buttonStyle(.borderless)
                        }
                    }
                }
            }
            // in reality the items are probably not Ints - replace this with whatever type of thing you are showing details of
            .navigationDestination(for: Int.self) { item in
                DetailView(for: item)
            }
    
        }
    }
    
返回
作者最近主题: