Skip to content

Fix repeated paywall purchase cancellations#6826

Merged
JZDesign merged 7 commits into
mainfrom
cursor/fix-paywall-cancel-loading-858a
May 22, 2026
Merged

Fix repeated paywall purchase cancellations#6826
JZDesign merged 7 commits into
mainfrom
cursor/fix-paywall-cancel-loading-858a

Conversation

@JZDesign

@JZDesign JZDesign commented May 20, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-android and hybrids

Motivation

If you cancel a paywall purchase by dismissing the payment sheet—you get the callback from onPurchaseCancelled if you do it again… you don't.

Description

Adds published ID that is set everytime the purchase session result is set, this way, if there is a duplicate value set, the view has what it needs to trigger the on canceled event

Open in Web Open in Cursor 

cursoragent and others added 2 commits May 20, 2026 20:57
Co-authored-by: jacob.rakidzich <jacob.rakidzich@revenuecat.com>
Co-authored-by: jacob.rakidzich <jacob.rakidzich@revenuecat.com>
@JZDesign JZDesign marked this pull request as ready for review May 20, 2026 21:04
@JZDesign JZDesign requested review from a team as code owners May 20, 2026 21:04
Comment thread RevenueCatUI/Purchasing/PurchaseHandler.swift Outdated
Comment on lines 12 to 892
@@ -79,6 +80,11 @@ final class PurchaseHandler: ObservableObject {
@Published
fileprivate(set) var sessionPurchaseResult: PurchaseResultData?

/// Unique identifier for the latest `sessionPurchaseResult`.
/// This allows SwiftUI preference listeners to receive consecutive identical results.
@Published
fileprivate(set) var sessionPurchaseResultID: UUID?

/// Whether a purchase was successfully completed in the current session.
/// Convenience property for checking if we should skip exit offers.
var hasPurchasedInSession: Bool {
@@ -220,6 +226,7 @@ final class PurchaseHandler: ObservableObject {
self.paywallEventTracker.discardSession(sessionID: sessionID)
}
self.sessionPurchaseResult = nil
self.sessionPurchaseResultID = nil
self.purchaseResult = nil
self.activePaywallSessionID = nil
}
@@ -492,6 +499,7 @@ extension PurchaseHandler {
// onPurchaseCompleted and onPurchaseCancelled modifiers both work correctly.
withAnimation(Constants.defaultAnimation) {
self.sessionPurchaseResult = result
self.sessionPurchaseResultID = UUID()
}

self.setResult(result)
@@ -555,6 +563,7 @@ extension PurchaseHandler {
// onPurchaseCompleted and onPurchaseCancelled modifiers both work correctly.
withAnimation(Constants.defaultAnimation) {
self.sessionPurchaseResult = resultInfo
self.sessionPurchaseResultID = UUID()
}

self.setResult(resultInfo)
@@ -863,19 +872,21 @@ struct RestoreInProgressPreferenceKey: PreferenceKey {
struct PurchasedResultPreferenceKey: PreferenceKey {

struct PurchaseResult: Equatable {
var id: UUID
var transaction: StoreTransaction?
var customerInfo: CustomerInfo
var userCancelled: Bool

init(data: PurchaseResultData) {
init(data: PurchaseResultData, id: UUID) {
self.id = id
self.transaction = data.transaction
self.customerInfo = data.customerInfo
self.userCancelled = data.userCancelled
}

init?(data: PurchaseResultData?) {
guard let data else { return nil }
self.init(data: data)
init?(data: PurchaseResultData?, id: UUID?) {
guard let data, let id else { return nil }
self.init(data: data, id: id)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of scope, but should we just make all these let instead?

@JZDesign JZDesign enabled auto-merge (squash) May 21, 2026 21:41
Comment thread RevenueCatUI/Purchasing/PurchaseHandler.swift

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 515dd70. Configure here.

Comment thread RevenueCatUI/Purchasing/PurchaseHandler.swift

@rickvdl rickvdl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a well scoped fix to me, good catch! One nit, LGTM!

value: self.purchaseHandler.packageBeingPurchased)
.preference(key: PurchasedResultPreferenceKey.self,
value: .init(data: self.purchaseHandler.sessionPurchaseResult))
value: .init(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could create a helper init here to only take the purchaseHandler, in order to prevent drift between the two identical calls (this one and PaywallView.swift)

@JZDesign JZDesign merged commit 60a3c37 into main May 22, 2026
17 of 19 checks passed
@JZDesign JZDesign deleted the cursor/fix-paywall-cancel-loading-858a branch May 22, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants