Skip to content

Fix exit offer crash in MY_APP mode with PurchaseLogic#6391

Merged
tonidero merged 5 commits into
mainfrom
fix/exit-offer-purchase-logic-handlers
Mar 6, 2026
Merged

Fix exit offer crash in MY_APP mode with PurchaseLogic#6391
tonidero merged 5 commits into
mainfrom
fix/exit-offer-purchase-logic-handlers

Conversation

@tonidero

@tonidero tonidero commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix fatal error when dismissing a paywall that has an exit offer configured while purchasesAreCompletedBy is set to .myApp
  • The exit offer PaywallViewController was created with performPurchase: nil and performRestore: nil, but in MY_APP mode these handlers are required otherwise, it fails to display with a crash.
  • Propagate the parent controller's performPurchase/performRestore handlers to the exit offer controller

Test plan

  • Configure a paywall with an exit offer in the dashboard
  • Set purchasesAreCompletedBy to .myApp
  • Present the paywall with performPurchase and performRestore handlers
  • Dismiss the paywall without purchasing — the exit offer should appear without crashing
  • Purchase from the exit offer — the purchase handlers should work correctly

🤖 Generated with Claude Code


Note

Medium Risk
Touches paywall dismissal/exit-offer presentation and purchase execution closures; a small change but it affects the purchase flow in .myApp mode and could impact exit-offer behavior if the wrong handlers are propagated.

Overview
Fixes an exit-offer presentation crash when Purchases is configured with .myApp purchase completion by passing through the existing performPurchase/performRestore closures to the exit-offer PaywallViewController instead of constructing it with nil handlers.

This ensures the exit-offer paywall can initiate purchases/restores using the same app-provided logic as the original paywall.

Written by Cursor Bugbot for commit f7e777b. This will update automatically on new commits. Configure here.

When `purchasesAreCompletedBy` is set to `.myApp`, the PaywallView
requires `performPurchase` and `performRestore` handlers to be provided.
The exit offer PaywallViewController was being created with `nil` for
both handlers, causing a fatal error when the user dismissed the paywall
and an exit offer was configured.

This change captures the parent controller's handlers from its
PurchaseHandler and passes them through to the exit offer controller,
following the same pattern already used for delegate, dismiss handler,
fonts, and touch blocking configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tonidero tonidero marked this pull request as ready for review March 3, 2026 19:25
@tonidero tonidero requested a review from a team as a code owner March 3, 2026 19:25
@tonidero tonidero requested a review from a team March 3, 2026 19:25

@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.

Fix itself looks good to me, but I don't really understand how this could cause a crash? Maybe an assertion that checked if the custom logic was provided when PACB: myApp?

@tonidero

tonidero commented Mar 4, 2026

Copy link
Copy Markdown
Contributor Author

Maybe an assertion that checked if the custom logic was provided when PACB: myApp?

@rickvdl That's pretty much it... Basically it threw an error that displayed a DebugErrorView, that actually crashes on release:

fatalError(self.description)

@tonidero tonidero requested review from a team and rickvdl March 4, 2026 14:01
@rickvdl

rickvdl commented Mar 4, 2026

Copy link
Copy Markdown
Member

Maybe an assertion that checked if the custom logic was provided when PACB: myApp?

@rickvdl That's pretty much it... Basically it threw an error that displayed a DebugErrorView, that actually crashes on release:

fatalError(self.description)

@tonidero Got it! Makes sense, thanks!

@tonidero tonidero enabled auto-merge (squash) March 4, 2026 14:22
@tonidero tonidero merged commit 192f3a6 into main Mar 6, 2026
13 of 15 checks passed
@tonidero tonidero deleted the fix/exit-offer-purchase-logic-handlers branch March 6, 2026 12:17
tonidero added a commit to RevenueCat/purchases-unity that referenced this pull request Mar 11, 2026
…CompletedBy.MyApp` (#842)

## Summary
- Add `PurchaseLogic` parameter to paywall presentation, allowing
customers in `PurchasesAreCompletedBy.MyApp` mode to handle purchases
and restores themselves through the paywall UI
- New public types: `PurchaseLogic` (with `PerformPurchaseHandler` and
`PerformRestoreHandler` delegates) and `PurchaseLogicResult` enum
- Optional `purchaseLogic` parameter added to all `PaywallOptions`
constructors
- Uses existing `HybridPurchaseLogicBridge` from purchases-hybrid-common
on both platforms
- Subtester: adds a "Paywall w/ PurchaseLogic" button that calls
`PurchasePackage` within the handler

### Android-specific
- Dialog uses `FLAG_NOT_FOCUSABLE` to prevent Unity's message processing
from breaking after `ProxyBillingActivity` (Google Play billing)
lifecycle transitions
- `AndroidJavaProxy.Invoke` override handles method dispatch as Unity's
proxy matching doesn't always resolve direct C# methods
- `SynchronizationContext` captured at setup time to ensure async
continuations run on the main thread

### iOS-specific
- Uses
`PaywallProxy.presentPaywall(options:purchaseLogicBridge:paywallResultHandler:)`
- Updated non-PurchaseLogic path to use the non-deprecated API with
`nil` bridge

### Related PRs
- RevenueCat/purchases-ios#6391 — Fix exit offer
crash in MY_APP mode (required for exit offers to work with
PurchaseLogic)

## Test plan
- [ ] Present paywall with `PurchaseLogic` on Android — purchase,
cancel, and error flows
- [ ] Present paywall with `PurchaseLogic` on iOS — purchase, cancel,
and error flows
- [ ] Present paywall WITHOUT `PurchaseLogic` — verify no regression on
both platforms
- [ ] Verify back button / dismiss works on both platforms
- [ ] Test with exit offers (requires purchases-ios#6391)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants