Skip to content

feat: add Paywall onRestoreInitiated JS callback - #1923

Open
ninjz wants to merge 2 commits into
RevenueCat:mainfrom
ninjz:feat/on-restore-initiated
Open

feat: add Paywall onRestoreInitiated JS callback#1923
ninjz wants to merge 2 commits into
RevenueCat:mainfrom
ninjz:feat/on-restore-initiated

Conversation

@ninjz

@ninjz ninjz commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • A description about what and why you are contributing, even if it's trivial.
  • The issue number(s) or PR number(s) in the description if you are contributing in response to those.
  • If applicable, unit tests.

Depends on: RevenueCat/purchases-hybrid-common#1843 (must be released and this SDK bumped to that hybrid-common version before this compiles on CI).

Native RevenueCat paywalls already gate restore before it starts (purchases-ios#6392, Android PaywallListener.onRestoreInitiated). React Native never bridged it. Apps that must identify the user before StoreKit restore (logout leaves RC on the previous App User ID; restore would attach receipts to the wrong customer) have no hook. Purchase already has onPurchasePackageInitiated + resume(requestId, shouldProceed). This adds the restore equivalent.

JS API (same shape as purchase, minus package)

<RevenueCatUI.Paywall
  onRestoreInitiated={({ resume }) => {
    // Identify / logIn the correct App User ID first, then:
    resume(true);  // proceed with StoreKit restore
    // or resume(false) to cancel
  }}
/>

If onRestoreInitiated is omitted, JS auto-resume(true) (same as onPurchasePackageInitiated). If it is set, the host must call resume(true|false) or restore hangs.

Wired on full-screen RevenueCatUI.Paywall and the footer paywall (InternalPaywallFooterView / iOS footer view property), matching onPurchasePackageInitiated. Android BasePaywallViewManager emits the native event for both view types.

Android footer: if the host never calls resume, the SDK auto-resumes so restore cannot hang (same failure mode Bugbot flagged).

Native

  • iOS: RCT_EXPORT_VIEW_PROPERTY(onRestoreInitiated) on full-screen and footer, PaywallViewWrapper implements paywallViewController:didInitiateRestoreWithRequestId:, RNPaywalls.resumeRestoreInitiated
  • Android: OnRestoreInitiatedEvent, PaywallEventName.ON_RESTORE_INITIATED, RNPaywallsModule.resumeRestoreInitiatedPaywallListenerWrapper.resumeRestoreInitiated

Note for hybrid-common consumers

PaywallProxy is a Swift public class (objc_subclassing_restricted). Do not subclass it from ObjC/RN to add this hook — implement didInitiateRestoreWithResume: on PaywallProxy itself (hybrid-common #1843). A local RN 10.7.1 patch can use an ObjC category as a stopgap until this ships.

Bridge native restore-initiated gating to React Native, matching
onPurchasePackageInitiated so hosts can identify the user before restore.
Depends on a purchases-hybrid-common release that includes resumeRestoreInitiated.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ninjz
ninjz requested a review from a team as a code owner August 25, 2026 19:31

@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

Reviewed by Cursor Bugbot for commit c7a2e3d. Configure here.

BasePaywallViewManager emits onRestoreInitiated for RCPaywallFooterView
too. Full-screen JS always resumes; the footer view never listened, so a
Restore tap hung. Wire the same handler (auto-true if omitted) and export
the iOS footer view property.
@ninjz

ninjz commented Aug 26, 2026

Copy link
Copy Markdown
Author

Fixed the Android footer hang: InternalPaywallFooterView now bridges onRestoreInitiated the same way as full-screen (auto-resume(true) if omitted). iOS footer view property exported too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant