RevenueCat / purchases-ios

In-app purchases and subscriptions made easy. Support for iOS, watchOS, tvOS, macOS, and visionOS.
https://www.revenuecat.com/
MIT License
2.26k stars 303 forks source link

Clicking the Restore Purchase button in Paywalls doesn't get the user information #3511

Closed emperinter closed 7 months ago

emperinter commented 8 months ago

Describe the bug A clear and concise description of what the bug is. The more detail you can provide the faster our team will be able to triage and resolve the issue. Do not remove any of the steps from the template below. If a step is not applicable to your issue, please leave that step empty.

  1. Environment

    1. Platform: ios/ipados
    2. SDK version: 4.31.2
    3. StoreKit version:
      • [x] StoreKit 1
      • [x] StoreKit 2 (enabled with usesStoreKit2IfAvailable(true))
    4. OS version:
    5. Xcode version:
    6. Device and/or simulator:
      • [x] Device: ipad2018
      • [x] Simulator: iphone
    7. Environment:
      • [x] Sandbox
      • [ ] TestFight
      • [ ] Production
    8. How widespread is the issue. Percentage of devices affected.
      • ipad2018 - Device
      • iphone 14 Plus - Simulator
      • iphone 15 - Simulator
      • other: unkonw
  2. Debug logs that reproduce the issue. Complete logs with Purchases.logLevel = .verbose will help us debug this issue.

Purchases.shared.customerInfo():
Optional(<CustomerInfo:
originalApplicationVersion=1.0,
latestExpirationDate=nil,
activeEntitlements=[:],
activeSubscriptions=[:],
nonSubscriptions=[],
requestDate=2023-12-10 18:36:09 +0000,
firstSeen=2023-12-10 18:34:32 +0000,
originalAppUserId=$RCAnonymousID:xxxxxxxxxxxx,
entitlements=[:]
verification=VerificationResult.notRequested
>)
DEBUG: ℹ️ Vending CustomerInfo from cache.
VERBOSE: PurchasesOrchestrator: caching presented paywall
VERBOSE: Storing event: impression(RevenueCat.PaywallEvent.CreationData(id: D6AC7AF6-2DB4-4214-8C8B-2E0EA68C4EF8, date: 2023-12-10 18:39:54 +0000), RevenueCat.PaywallEvent.Data(offeringIdentifier: "default", paywallRevision: 7, sessionIdentifier: 8CA8F7B6-CA3E-498B-9048-433305C9667F, displayMode: RevenueCat.PaywallViewMode.fullScreen, localeIdentifier: "zh-Hans_US", darkMode: true))
DEBUG: ℹ️ Found cached trial or intro eligibility for products: ["FinanceMonthly", "FinanceAnnual", "FinanceQuarterly"]
DEBUG: ℹ️ Found 0 unsynced attributes for App User ID: $RCAnonymousID:xxxxxxxxxxxxxxxx
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ SKReceiptRefreshRequest started
DEBUG: ℹ️ SKReceiptRefreshRequest finished
DEBUG: ℹ️ Loaded receipt from url file:///private/var/mobile/Containers/Data/Application/EAFBB078-124F-461A-81FF-FA4EE2CD0BC3/StoreKit/sandboxReceipt
INFO: ℹ️ Parsing receipt
INFO: ℹ️ Receipt parsed successfully
VERBOSE: PurchasesOrchestrator: clearing presented paywall
VERBOSE: Storing event: close(RevenueCat.PaywallEvent.CreationData(id: 185189A5-0DCF-40CD-A744-B38EA9822DE6, date: 2023-12-10 18:39:59 +0000), RevenueCat.PaywallEvent.Data(offeringIdentifier: "default", paywallRevision: 7, sessionIdentifier: 8CA8F7B6-CA3E-498B-9048-433305C9667F, displayMode: RevenueCat.PaywallViewMode.fullScreen, localeIdentifier: "zh-Hans_US", darkMode: true))
Purchases.shared.customerInfo():
Optional(<CustomerInfo:
originalApplicationVersion=1.0,
latestExpirationDate=nil,
activeEntitlements=[:],
activeSubscriptions=[:],
nonSubscriptions=[],
requestDate=2023-12-10 18:36:09 +0000,
firstSeen=2023-12-10 18:34:32 +0000,
originalAppUserId=$RCAnonymousID:xxxxxxxxxxxxxxxxx,
entitlements=[:]
verification=VerificationResult.notRequested
>)
DEBUG: ℹ️ Vending CustomerInfo from cache.
tcp_input [C1.1.1.1:3] flags=4 seq=334,856,892, ack=0, win=0 state=8 rcv_nxt=334,856,892, snd_una=4,097,417,438
tcp_input [C1.1.1.1:3] flags=4 seq=334,856,892, ack=0, win=0 state=0 rcv_nxt=334,856,892, snd_una=4,097,417,438
tcp_input [C1.1.1.1:3] flags=4 seq=334,856,892, ack=0, win=0 state=0 rcv_nxt=334,856,892, snd_una=4,097,417,438
  1. Steps to reproduce, with a description of expected vs. actual behavior
       {
        /* other code */    
        }
        .sheet(isPresented: self.$displayPaywall) {
            PaywallView()
                .toolbar {
                    ToolbarItem(placement: .topBarLeading) {
                        Button(action: {
                            self.displayPaywall = false
                        }) {
                            Image(systemName: "xmark")
                                .foregroundColor(.black) // Set the image color
                        }
                    }
                }
                .onDisappear(){
                    fetchCustomerInfoAndUpdateUI()
                }
            Button(action: {
                self.displayPaywall = false
            }) {
                Image(systemName: "xmark")
                    .foregroundColor(.red) // Set the image color
                    .padding()
            }
            .background(
                Color.yellow
                    .opacity(0.5) // Adjust opacity to control brightness
                    .blur(radius: 8) // Adjust radius for the blur effect
            )
            .cornerRadius(10)
            .shadow(radius: 5)
        }
        .onAppear {
            fetchCustomerInfoAndUpdateUI()
        }
    }

    private func fetchCustomerInfoAndUpdateUI() {
       Task {
           do {
               self.customerInfo = try await Purchases.shared.customerInfo()
               print("Purchases.shared.customerInfo():\n\(String(describing: self.customerInfo))")
               if let description = customerInfo?.activeSubscriptions, !description.isEmpty {
                    self.subscriptionType = String(description.first!)
                    let order_month = productDurationMap[String(description.first!)] ?? 0
                    if order_month > 0 {
                        PersistenceController.shared.saveSubscription(month: order_month, type: self.subscriptionType)
                        updateVip(month: order_month)
                        self.isSubscribed = true
                    }
                   self.subscriptionType = String(description.first!)
                   self.isSubscribed = true
               } else {
                   self.subscriptionType = "No Subscription"
                   self.isSubscribed = false
               }
               viewModel.loadImage(imageName: self.customerInfo!.originalAppUserId)
           } catch {
               // Handle error
           }
       }
   }
  1. Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)

  2. Additional context Add any other context about the problem here.

RCGitBot commented 8 months ago

👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!

NachoSoto commented 8 months ago

Thanks for the report! Could you please attach the complete logs? We can't see any calls to restore in those.

In the mean time, have you considered using the .onPurchaseCompleted and .onRestoreCompleted modifiers?

PaywallView()
  .onPurchaseCompleted { customerInfo in updateUI(with: customerInfo) }
  .onRestoreCompleted { customerInfo in updateUI(with: customerInfo) }
emperinter commented 8 months ago

The process I tested:

Results

Recovery did not get the user's subscription information

Other

I also tried the onPurchaseCompleted and onRestoreCompleted usage you mentioned here, and it prints the following result when restoring the subscription

PaywallView()
    .onPurchaseCompleted{customerInfo in
        print("Purchases onPurchaseCompleted:\n\(customerInfo)")
        fetchCustomerInfoAndUpdateUI()
    }
    .onRestoreCompleted { customerInfo in
        print("Purchases onRestoreCompleted:\n\(customerInfo)")
        fetchCustomerInfoAndUpdateUI()
    }
    .toolbar {
        ToolbarItem(placement: .topBarLeading) {
            Button(action: {
                self.displayPaywall = false
            }) {
                Image(systemName: "xmark")
                    .foregroundColor(.black) // Set the image color
            }
        }
    }
    .onDisappear(){
        fetchCustomerInfoAndUpdateUI()
    }

The real ID has been removed from the log

Purchases onRestoreCompleted:
<CustomerInfo:
originalApplicationVersion=1.0,
latestExpirationDate=nil,
activeEntitlements=[:],
activeSubscriptions=[:],
nonSubscriptions=[],
requestDate=2023-12-14 18:37:42 +0000,
firstSeen=2023-12-14 18:36:38 +0000,
originalAppUserId=$RCAnonymousID:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
entitlements=[:]
verification=VerificationResult.notRequested
>

Full Log: revenue_debubg_log.txt

mshmoustafa commented 8 months ago

@emperinter There's a bug in StoreKit that doesn't allow you to restore purchases when you load a sandbox app on your device until you make a new purchase, which I think you are experiencing. Unfortunately this is an issue in sandbox. I recommend trying this in TestFlight instead. More info at this blog post.

github-actions[bot] commented 7 months ago

This issue has been automatically locked due to no recent activity after it was closed. Please open a new issue for related reports.