react-native-purchases-ui 9.9.0 → 9.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/RNPaywalls.podspec +1 -1
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/com/revenuecat/purchases/react/ui/BasePaywallViewManager.kt +32 -0
  4. package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallEventName.kt +3 -1
  5. package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallFooterViewManager.kt +5 -0
  6. package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallViewManager.kt +5 -0
  7. package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallsModule.kt +23 -1
  8. package/android/src/main/java/com/revenuecat/purchases/react/ui/events/OnPurchasePackageInitiatedEvent.kt +24 -0
  9. package/android/src/main/java/com/revenuecat/purchases/react/ui/views/WrappedPaywallComposeView.kt +5 -0
  10. package/ios/PaywallViewManager.m +1 -0
  11. package/ios/PaywallViewWrapper.h +1 -0
  12. package/ios/PaywallViewWrapper.m +23 -0
  13. package/ios/RNPaywalls.m +15 -0
  14. package/lib/commonjs/customVariables.js +91 -0
  15. package/lib/commonjs/customVariables.js.map +1 -0
  16. package/lib/commonjs/index.js +71 -9
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/module/customVariables.js +83 -0
  19. package/lib/module/customVariables.js.map +1 -0
  20. package/lib/module/index.js +54 -9
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/typescript/src/customVariables.d.ts +69 -0
  23. package/lib/typescript/src/customVariables.d.ts.map +1 -0
  24. package/lib/typescript/src/index.d.ts +39 -2
  25. package/lib/typescript/src/index.d.ts.map +1 -1
  26. package/package.json +13 -11
  27. package/src/customVariables.ts +86 -0
  28. package/src/index.tsx +94 -4
@@ -17,6 +17,6 @@ Pod::Spec.new do |spec|
17
17
  spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
18
18
 
19
19
  spec.dependency "React-Core"
20
- spec.dependency "PurchasesHybridCommonUI", '17.37.0'
20
+ spec.dependency "PurchasesHybridCommonUI", '17.40.0'
21
21
  spec.swift_version = '5.7'
22
22
  end
@@ -59,7 +59,7 @@ android {
59
59
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
60
60
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
61
61
  versionCode 1
62
- versionName '9.9.0'
62
+ versionName '9.10.1'
63
63
  }
64
64
 
65
65
  buildTypes {
@@ -91,7 +91,7 @@ dependencies {
91
91
  //noinspection GradleDynamicVersion
92
92
  implementation "com.facebook.react:react-native:+"
93
93
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
94
- implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.37.0'
94
+ implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.40.0'
95
95
  implementation 'androidx.compose.ui:ui-android:1.5.4'
96
96
  implementation "androidx.appcompat:appcompat:1.6.1"
97
97
  }
@@ -17,10 +17,12 @@ import com.revenuecat.purchases.react.ui.events.OnDismissEvent
17
17
  import com.revenuecat.purchases.react.ui.events.OnPurchaseCancelledEvent
18
18
  import com.revenuecat.purchases.react.ui.events.OnPurchaseCompletedEvent
19
19
  import com.revenuecat.purchases.react.ui.events.OnPurchaseErrorEvent
20
+ import com.revenuecat.purchases.react.ui.events.OnPurchasePackageInitiatedEvent
20
21
  import com.revenuecat.purchases.react.ui.events.OnPurchaseStartedEvent
21
22
  import com.revenuecat.purchases.react.ui.events.OnRestoreCompletedEvent
22
23
  import com.revenuecat.purchases.react.ui.events.OnRestoreErrorEvent
23
24
  import com.revenuecat.purchases.react.ui.events.OnRestoreStartedEvent
25
+ import com.revenuecat.purchases.ui.revenuecatui.CustomVariableValue
24
26
  import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
25
27
 
26
28
  internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>() {
@@ -31,6 +33,7 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
31
33
  private const val OFFERING_IDENTIFIER = "identifier"
32
34
  private const val OPTION_FONT_FAMILY = "fontFamily"
33
35
  private const val OPTION_DISPLAY_CLOSE_BUTTON = "displayCloseButton"
36
+ private const val OPTION_CUSTOM_VARIABLES = "customVariables"
34
37
 
35
38
  private const val OPTION_OFFERING_AVAILABLE_PACKAGES = "availablePackages"
36
39
 
@@ -41,6 +44,8 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
41
44
 
42
45
  abstract fun setDisplayDismissButton(view: T, display: Boolean)
43
46
 
47
+ abstract fun setCustomVariables(view: T, customVariables: Map<String, CustomVariableValue>)
48
+
44
49
  override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
45
50
  return MapBuilder.builder<String, Any>()
46
51
  .putEvent(PaywallEventName.ON_PURCHASE_STARTED)
@@ -52,6 +57,7 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
52
57
  .putEvent(PaywallEventName.ON_RESTORE_ERROR)
53
58
  .putEvent(PaywallEventName.ON_DISMISS)
54
59
  .putEvent(PaywallEventName.ON_MEASURE)
60
+ .putEvent(PaywallEventName.ON_PURCHASE_PACKAGE_INITIATED)
55
61
  .build()
56
62
  }
57
63
 
@@ -63,6 +69,7 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
63
69
  setOfferingIdProp(view, options)
64
70
  setFontFamilyProp(view, options)
65
71
  setDisplayCloseButton(view, options)
72
+ setCustomVariablesProp(view, options)
66
73
  }
67
74
  }
68
75
 
@@ -113,6 +120,21 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
113
120
  }
114
121
  }
115
122
 
123
+ private fun setCustomVariablesProp(view: T, options: ReadableMap?) {
124
+ val customVariablesMap = options?.getMap(OPTION_CUSTOM_VARIABLES) ?: return
125
+ val customVariables = mutableMapOf<String, CustomVariableValue>()
126
+ val iterator = customVariablesMap.keySetIterator()
127
+ while (iterator.hasNextKey()) {
128
+ val key = iterator.nextKey()
129
+ customVariablesMap.getString(key)?.let {
130
+ customVariables[key] = CustomVariableValue.String(it)
131
+ }
132
+ }
133
+ if (customVariables.isNotEmpty()) {
134
+ setCustomVariables(view, customVariables)
135
+ }
136
+ }
137
+
116
138
  internal fun createPaywallListenerWrapper(
117
139
  themedReactContext: ThemedReactContext,
118
140
  view: View
@@ -182,6 +204,16 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
182
204
  emitEvent(themedReactContext, view.id, event)
183
205
  }
184
206
 
207
+ override fun onPurchasePackageInitiated(rcPackage: Map<String, Any?>, requestId: String) {
208
+ val event = OnPurchasePackageInitiatedEvent(
209
+ surfaceId = view.surfaceId,
210
+ viewTag = view.id,
211
+ rcPackage,
212
+ requestId,
213
+ )
214
+ emitEvent(themedReactContext, view.id, event)
215
+ }
216
+
185
217
  }
186
218
 
187
219
  internal fun getDismissHandler(
@@ -9,11 +9,13 @@ internal enum class PaywallEventName(val eventName: String) {
9
9
  ON_RESTORE_COMPLETED("onRestoreCompleted"),
10
10
  ON_RESTORE_ERROR("onRestoreError"),
11
11
  ON_DISMISS("onDismiss"),
12
- ON_MEASURE("onMeasure");
12
+ ON_MEASURE("onMeasure"),
13
+ ON_PURCHASE_PACKAGE_INITIATED("onPurchasePackageInitiated");
13
14
  }
14
15
 
15
16
  internal enum class PaywallEventKey(val key: String) {
16
17
  PACKAGE("packageBeingPurchased"),
18
+ REQUEST_ID("requestId"),
17
19
  CUSTOMER_INFO("customerInfo"),
18
20
  STORE_TRANSACTION("storeTransaction"),
19
21
  ERROR("error"),
@@ -5,6 +5,7 @@ import com.facebook.react.uimanager.ThemedReactContext
5
5
  import com.revenuecat.purchases.PresentedOfferingContext
6
6
  import com.revenuecat.purchases.react.ui.events.OnMeasureEvent
7
7
  import com.revenuecat.purchases.react.ui.views.WrappedPaywallFooterComposeView
8
+ import com.revenuecat.purchases.ui.revenuecatui.CustomVariableValue
8
9
  import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
9
10
 
10
11
  internal class PaywallFooterViewManager : BasePaywallViewManager<WrappedPaywallFooterComposeView>() {
@@ -89,4 +90,8 @@ internal class PaywallFooterViewManager : BasePaywallViewManager<WrappedPaywallF
89
90
  // No-op since PaywallFooterView doesn't have a dismiss button
90
91
  }
91
92
 
93
+ override fun setCustomVariables(view: WrappedPaywallFooterComposeView, customVariables: Map<String, CustomVariableValue>) {
94
+ // No-op: Footer paywalls (legacy templates) don't support custom variables
95
+ }
96
+
92
97
  }
@@ -3,6 +3,7 @@ package com.revenuecat.purchases.react.ui
3
3
  import com.facebook.react.uimanager.ThemedReactContext
4
4
  import com.revenuecat.purchases.PresentedOfferingContext
5
5
  import com.revenuecat.purchases.react.ui.views.WrappedPaywallComposeView
6
+ import com.revenuecat.purchases.ui.revenuecatui.CustomVariableValue
6
7
  import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
7
8
 
8
9
 
@@ -43,4 +44,8 @@ internal class PaywallViewManager : BasePaywallViewManager<WrappedPaywallCompose
43
44
  view.setDisplayDismissButton(display)
44
45
  }
45
46
 
47
+ override fun setCustomVariables(view: WrappedPaywallComposeView, customVariables: Map<String, CustomVariableValue>) {
48
+ view.setCustomVariables(customVariables)
49
+ }
50
+
46
51
  }
@@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactApplicationContext
7
7
  import com.facebook.react.bridge.ReactContextBaseJavaModule
8
8
  import com.facebook.react.bridge.ReactMethod
9
9
  import com.facebook.react.bridge.ReadableMap
10
+ import com.revenuecat.purchases.hybridcommon.ui.PaywallListenerWrapper
10
11
  import com.revenuecat.purchases.hybridcommon.ui.PaywallResultListener
11
12
  import com.revenuecat.purchases.hybridcommon.ui.PaywallSource
12
13
  import com.revenuecat.purchases.hybridcommon.ui.PresentPaywallOptions
@@ -42,6 +43,7 @@ internal class RNPaywallsModule(
42
43
  presentedOfferingContext: ReadableMap?,
43
44
  displayCloseButton: Boolean?,
44
45
  fontFamily: String?,
46
+ customVariables: ReadableMap?,
45
47
  promise: Promise
46
48
  ) {
47
49
  presentPaywall(
@@ -50,6 +52,7 @@ internal class RNPaywallsModule(
50
52
  presentedOfferingContext,
51
53
  displayCloseButton,
52
54
  fontFamily,
55
+ customVariables,
53
56
  promise
54
57
  )
55
58
  }
@@ -61,6 +64,7 @@ internal class RNPaywallsModule(
61
64
  presentedOfferingContext: ReadableMap?,
62
65
  displayCloseButton: Boolean,
63
66
  fontFamily: String?,
67
+ customVariables: ReadableMap?,
64
68
  promise: Promise
65
69
  ) {
66
70
  presentPaywall(
@@ -69,10 +73,16 @@ internal class RNPaywallsModule(
69
73
  presentedOfferingContext,
70
74
  displayCloseButton,
71
75
  fontFamily,
76
+ customVariables,
72
77
  promise
73
78
  )
74
79
  }
75
80
 
81
+ @ReactMethod
82
+ fun resumePurchasePackageInitiated(requestId: String, shouldProceed: Boolean) {
83
+ PaywallListenerWrapper.resumePurchasePackageInitiated(requestId, shouldProceed)
84
+ }
85
+
76
86
  @ReactMethod
77
87
  fun addListener(eventName: String?) {
78
88
  // Keep: Required for RN built in Event Emitter Calls.
@@ -89,6 +99,7 @@ internal class RNPaywallsModule(
89
99
  presentedOfferingContext: ReadableMap?,
90
100
  displayCloseButton: Boolean?,
91
101
  fontFamilyName: String?,
102
+ customVariables: ReadableMap?,
92
103
  promise: Promise
93
104
  ) {
94
105
  val activity = currentFragmentActivity ?: return
@@ -101,6 +112,16 @@ internal class RNPaywallsModule(
101
112
  PaywallSource.OfferingIdentifierWithPresentedOfferingContext(offeringIdentifier, presentedOfferingContext=presentedOfferingContextMap)
102
113
  } ?: PaywallSource.DefaultOffering
103
114
 
115
+ val customVariablesMap = customVariables?.let { cv ->
116
+ val result = mutableMapOf<String, String>()
117
+ val iterator = cv.keySetIterator()
118
+ while (iterator.hasNextKey()) {
119
+ val key = iterator.nextKey()
120
+ cv.getString(key)?.let { result[key] = it }
121
+ }
122
+ result.takeIf { it.isNotEmpty() }
123
+ }
124
+
104
125
  // @ReactMethod is not guaranteed to run on the main thread
105
126
  activity.runOnUiThread {
106
127
  presentPaywallFromFragment(
@@ -114,7 +135,8 @@ internal class RNPaywallsModule(
114
135
  promise.resolve(paywallResult)
115
136
  }
116
137
  },
117
- fontFamily = fontFamily
138
+ fontFamily = fontFamily,
139
+ customVariables = customVariablesMap
118
140
  )
119
141
  )
120
142
  }
@@ -0,0 +1,24 @@
1
+ package com.revenuecat.purchases.react.ui.events
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+ import com.revenuecat.purchases.react.ui.PaywallEventKey
5
+ import com.revenuecat.purchases.react.ui.PaywallEventName
6
+
7
+ internal class OnPurchasePackageInitiatedEvent(
8
+ surfaceId: Int,
9
+ viewTag: Int,
10
+ private val packageMap: Map<String, Any?>,
11
+ private val requestId: String,
12
+ ) : PaywallEvent<OnPurchasePackageInitiatedEvent>(surfaceId, viewTag) {
13
+ override fun getPaywallEventName() = PaywallEventName.ON_PURCHASE_PACKAGE_INITIATED
14
+
15
+ override fun getPayload() = mapOf(
16
+ PaywallEventKey.PACKAGE to packageMap,
17
+ )
18
+
19
+ override fun getEventData(): WritableMap {
20
+ return super.getEventData().apply {
21
+ putString(PaywallEventKey.REQUEST_ID.key, requestId)
22
+ }
23
+ }
24
+ }
@@ -3,6 +3,7 @@ package com.revenuecat.purchases.react.ui.views
3
3
  import android.content.Context
4
4
  import android.util.AttributeSet
5
5
  import com.revenuecat.purchases.PresentedOfferingContext
6
+ import com.revenuecat.purchases.ui.revenuecatui.CustomVariableValue
6
7
  import com.revenuecat.purchases.ui.revenuecatui.PaywallListener
7
8
  import com.revenuecat.purchases.ui.revenuecatui.fonts.FontProvider
8
9
  import com.revenuecat.purchases.ui.revenuecatui.views.PaywallView
@@ -33,6 +34,10 @@ class WrappedPaywallComposeView(context: Context) : ComposeViewWrapper<PaywallVi
33
34
  wrappedView?.setDisplayDismissButton(shouldDisplayDismissButton)
34
35
  }
35
36
 
37
+ fun setCustomVariables(customVariables: Map<String, CustomVariableValue>) {
38
+ wrappedView?.setCustomVariables(customVariables)
39
+ }
40
+
36
41
  override fun requestLayout() {
37
42
  super.requestLayout()
38
43
  post(measureAndLayout)
@@ -30,6 +30,7 @@ RCT_EXPORT_VIEW_PROPERTY(onRestoreStarted, RCTDirectEventBlock)
30
30
  RCT_EXPORT_VIEW_PROPERTY(onRestoreCompleted, RCTDirectEventBlock)
31
31
  RCT_EXPORT_VIEW_PROPERTY(onRestoreError, RCTDirectEventBlock)
32
32
  RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
33
+ RCT_EXPORT_VIEW_PROPERTY(onPurchasePackageInitiated, RCTDirectEventBlock)
33
34
 
34
35
  RCT_EXPORT_MODULE(Paywall)
35
36
 
@@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
21
21
  @property (nonatomic, copy) RCTDirectEventBlock onRestoreCompleted;
22
22
  @property (nonatomic, copy) RCTDirectEventBlock onRestoreError;
23
23
  @property (nonatomic, copy) RCTDirectEventBlock onDismiss;
24
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onPurchasePackageInitiated;
24
25
 
25
26
  - (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
26
27
  - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
@@ -98,6 +98,16 @@ API_AVAILABLE(ios(15.0))
98
98
  if (displayCloseButton) {
99
99
  [self.paywallViewController updateWithDisplayCloseButton:displayCloseButton];
100
100
  }
101
+
102
+ NSDictionary *customVariables = options[@"customVariables"];
103
+ if (customVariables && [customVariables isKindOfClass:[NSDictionary class]]) {
104
+ for (NSString *key in customVariables) {
105
+ NSString *value = customVariables[key];
106
+ if ([value isKindOfClass:[NSString class]]) {
107
+ [self.paywallViewController setCustomVariable:value forKey:key];
108
+ }
109
+ }
110
+ }
101
111
  } else {
102
112
  NSLog(@"Error: attempted to present paywalls on unsupported iOS version.");
103
113
  }
@@ -197,4 +207,17 @@ didFailRestoringWithErrorDictionary:(NSDictionary *)errorDictionary API_AVAILABL
197
207
  NSLog(@"RNPaywalls - Paywall view wrapper did change size to: %@", NSStringFromCGSize(size));
198
208
  }
199
209
 
210
+ - (void)paywallViewController:(RCPaywallViewController *)controller
211
+ didInitiatePurchaseWithPackageDictionary:(NSDictionary *)packageDictionary
212
+ requestId:(NSString *)requestId API_AVAILABLE(ios(15.0)) {
213
+ if (self.onPurchasePackageInitiated) {
214
+ self.onPurchasePackageInitiated(@{
215
+ KeyPackage: packageDictionary,
216
+ @"requestId": requestId,
217
+ });
218
+ } else {
219
+ [PaywallProxy resumePurchasePackageInitiatedWithRequestId:requestId shouldProceed:YES];
220
+ }
221
+ }
222
+
200
223
  @end
package/ios/RNPaywalls.m CHANGED
@@ -65,6 +65,7 @@ RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
65
65
  presentedOfferingContext:(nullable NSDictionary *)presentedOfferingContext
66
66
  shouldDisplayCloseButton:(BOOL)displayCloseButton
67
67
  withFontFamily:(nullable NSString *)fontFamily
68
+ customVariables:(nullable NSDictionary *)customVariables
68
69
  withResolve:(RCTPromiseResolveBlock)resolve
69
70
  reject:(RCTPromiseRejectBlock)reject) {
70
71
  if (@available(iOS 15.0, *)) {
@@ -79,6 +80,9 @@ RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
79
80
  if (fontFamily) {
80
81
  options[PaywallOptionsKeys.fontName] = fontFamily;
81
82
  }
83
+ if (customVariables) {
84
+ options[PaywallOptionsKeys.customVariables] = customVariables;
85
+ }
82
86
 
83
87
  [self.paywalls presentPaywallWithOptions:options
84
88
  paywallResultHandler:^(NSString *result) {
@@ -94,6 +98,7 @@ RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifi
94
98
  presentedOfferingContext:(nullable NSDictionary *)presentedOfferingContext
95
99
  shouldDisplayCloseButton:(BOOL)displayCloseButton
96
100
  withFontFamily:(nullable NSString *)fontFamily
101
+ customVariables:(nullable NSDictionary *)customVariables
97
102
  withResolve:(RCTPromiseResolveBlock)resolve
98
103
  reject:(RCTPromiseRejectBlock)reject) {
99
104
  if (@available(iOS 15.0, *)) {
@@ -109,6 +114,9 @@ RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifi
109
114
  if (fontFamily) {
110
115
  options[PaywallOptionsKeys.fontName] = fontFamily;
111
116
  }
117
+ if (customVariables) {
118
+ options[PaywallOptionsKeys.customVariables] = customVariables;
119
+ }
112
120
 
113
121
  [self.paywalls presentPaywallIfNeededWithOptions:options
114
122
  paywallResultHandler:^(NSString *result) {
@@ -119,6 +127,13 @@ RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifi
119
127
  }
120
128
  }
121
129
 
130
+ RCT_EXPORT_METHOD(resumePurchasePackageInitiated:(NSString *)requestId
131
+ shouldProceed:(BOOL)shouldProceed) {
132
+ if (@available(iOS 15.0, *)) {
133
+ [PaywallProxy resumePurchasePackageInitiatedWithRequestId:requestId shouldProceed:shouldProceed];
134
+ }
135
+ }
136
+
122
137
  - (void)rejectPaywallsUnsupportedError:(RCTPromiseRejectBlock)reject {
123
138
  NSLog(@"Error: attempted to present paywalls on unsupported iOS version.");
124
139
  reject(@"PaywallsUnsupportedCode", @"Paywalls are not supported prior to iOS 15.", nil);
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CustomVariableValue = void 0;
7
+ exports.convertCustomVariablesToStringMap = convertCustomVariablesToStringMap;
8
+ exports.transformOptionsForNative = transformOptionsForNative;
9
+ /**
10
+ * A value type for custom paywall variables that can be passed to paywalls at runtime.
11
+ *
12
+ * Custom variables allow developers to personalize paywall text with dynamic values.
13
+ * Variables are defined in the RevenueCat dashboard and can be overridden at runtime.
14
+ *
15
+ * Currently only string values are supported. Additional types may be added in the future.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * RevenueCatUI.presentPaywall({
20
+ * customVariables: {
21
+ * 'player_name': CustomVariableValue.string('John'),
22
+ * 'level': CustomVariableValue.string('42'),
23
+ * },
24
+ * });
25
+ * ```
26
+ *
27
+ * In the paywall text (configured in the dashboard), use the `custom.` prefix:
28
+ * ```
29
+ * Hello {{ custom.player_name }}!
30
+ * ```
31
+ */
32
+
33
+ /**
34
+ * Factory methods for creating CustomVariableValue instances.
35
+ */
36
+ const CustomVariableValue = exports.CustomVariableValue = {
37
+ /**
38
+ * Creates a string custom variable value.
39
+ * @param value The string value for the custom variable.
40
+ * @returns A CustomVariableValue containing the string.
41
+ */
42
+ string: value => ({
43
+ type: 'string',
44
+ value
45
+ })
46
+ };
47
+
48
+ /**
49
+ * A map of custom variable names to their values.
50
+ */
51
+
52
+ /**
53
+ * Internal type for custom variables as sent to native bridge.
54
+ * Currently only string values are supported.
55
+ * @internal
56
+ */
57
+
58
+ /**
59
+ * Converts CustomVariables to a string map for native bridge.
60
+ * @internal
61
+ * @visibleForTesting
62
+ */
63
+ function convertCustomVariablesToStringMap(customVariables) {
64
+ if (!customVariables) return null;
65
+ const result = {};
66
+ for (const key of Object.keys(customVariables)) {
67
+ const variable = customVariables[key];
68
+ if (variable) {
69
+ result[key] = variable.value;
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+
75
+ /**
76
+ * Transforms options to native format, converting CustomVariables to string map.
77
+ * @internal
78
+ * @visibleForTesting
79
+ */
80
+ function transformOptionsForNative(options) {
81
+ if (!options) return undefined;
82
+ const {
83
+ customVariables,
84
+ ...rest
85
+ } = options;
86
+ return {
87
+ ...rest,
88
+ customVariables: convertCustomVariablesToStringMap(customVariables)
89
+ };
90
+ }
91
+ //# sourceMappingURL=customVariables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CustomVariableValue","exports","string","value","type","convertCustomVariablesToStringMap","customVariables","result","key","Object","keys","variable","transformOptionsForNative","options","undefined","rest"],"sourceRoot":"../../src","sources":["customVariables.ts"],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACO,MAAMA,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAAG;EACjC;AACF;AACA;AACA;AACA;EACEE,MAAM,EAAGC,KAAa,KAA2B;IAAEC,IAAI,EAAE,QAAQ;IAAED;EAAM,CAAC;AAC5E,CAAU;;AAEV;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACO,SAASE,iCAAiCA,CAC/CC,eAA4C,EACd;EAC9B,IAAI,CAACA,eAAe,EAAE,OAAO,IAAI;EACjC,MAAMC,MAA6B,GAAG,CAAC,CAAC;EACxC,KAAK,MAAMC,GAAG,IAAIC,MAAM,CAACC,IAAI,CAACJ,eAAe,CAAC,EAAE;IAC9C,MAAMK,QAAQ,GAAGL,eAAe,CAACE,GAAG,CAAC;IACrC,IAAIG,QAAQ,EAAE;MACZJ,MAAM,CAACC,GAAG,CAAC,GAAGG,QAAQ,CAACR,KAAK;IAC9B;EACF;EACA,OAAOI,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASK,yBAAyBA,CACvCC,OAAsB,EACyE;EAC/F,IAAI,CAACA,OAAO,EAAE,OAAOC,SAAS;EAC9B,MAAM;IAAER,eAAe;IAAE,GAAGS;EAAK,CAAC,GAAGF,OAAO;EAC5C,OAAO;IACL,GAAGE,IAAkC;IACrCT,eAAe,EAAED,iCAAiC,CAACC,eAAe;EACpE,CAAC;AACH","ignoreList":[]}
@@ -3,21 +3,42 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "CustomVariableValue", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _customVariables.CustomVariableValue;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "PAYWALL_RESULT", {
7
13
  enumerable: true,
8
14
  get: function () {
9
15
  return _purchasesTypescriptInternal.PAYWALL_RESULT;
10
16
  }
11
17
  });
18
+ Object.defineProperty(exports, "convertCustomVariablesToStringMap", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _customVariables.convertCustomVariablesToStringMap;
22
+ }
23
+ });
12
24
  exports.default = void 0;
25
+ Object.defineProperty(exports, "transformOptionsForNative", {
26
+ enumerable: true,
27
+ get: function () {
28
+ return _customVariables.transformOptionsForNative;
29
+ }
30
+ });
13
31
  var _reactNative = require("react-native");
14
32
  var _purchasesTypescriptInternal = require("@revenuecat/purchases-typescript-internal");
15
33
  var _react = _interopRequireWildcard(require("react"));
16
34
  var _environment = require("./utils/environment");
17
35
  var _nativeModules = require("./preview/nativeModules");
18
36
  var _previewComponents = require("./preview/previewComponents");
37
+ var _customVariables = require("./customVariables");
19
38
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
20
39
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
40
+ // Re-export for testing purposes (marked as @internal)
41
+
21
42
  const NATIVE_MODULE_NOT_FOUND_ERROR = `[RevenueCatUI] Native module not found. This can happen if:\n\n` + `- You are running in an unsupported environment (e.g., A browser or a container app that doesn't actually use the native modules)\n` + `- The native module failed to initialize\n` + `- react-native-purchases is not properly installed\n\n` + `To fix this:\n` + `- If using Expo, create a development build: https://docs.expo.dev/develop/development-builds/create-a-build/\n` + `- If using bare React Native, run 'pod install' and rebuild the app\n` + `- Make sure react-native-purchases is installed and you have rebuilt the app\n`;
22
43
 
23
44
  // Get the native module or use the preview implementation
@@ -48,7 +69,8 @@ const InternalPaywall = ({
48
69
  onRestoreStarted,
49
70
  onRestoreCompleted,
50
71
  onRestoreError,
51
- onDismiss
72
+ onDismiss,
73
+ onPurchasePackageInitiated
52
74
  }) => {
53
75
  if (usingPreviewAPIMode) {
54
76
  return /*#__PURE__*/_react.default.createElement(_previewComponents.PreviewPaywall, {
@@ -65,10 +87,12 @@ const InternalPaywall = ({
65
87
  onDismiss: onDismiss
66
88
  });
67
89
  } else if (!!NativePaywall) {
90
+ // Transform options to native format (CustomVariables -> string map)
91
+ const nativeOptions = (0, _customVariables.transformOptionsForNative)(options);
68
92
  return /*#__PURE__*/_react.default.createElement(NativePaywall, {
69
93
  style: style,
70
94
  children: children,
71
- options: options,
95
+ options: nativeOptions,
72
96
  onPurchaseStarted: event => onPurchaseStarted && onPurchaseStarted(event.nativeEvent),
73
97
  onPurchaseCompleted: event => onPurchaseCompleted && onPurchaseCompleted(event.nativeEvent),
74
98
  onPurchaseError: event => onPurchaseError && onPurchaseError(event.nativeEvent),
@@ -76,7 +100,24 @@ const InternalPaywall = ({
76
100
  onRestoreStarted: () => onRestoreStarted && onRestoreStarted(),
77
101
  onRestoreCompleted: event => onRestoreCompleted && onRestoreCompleted(event.nativeEvent),
78
102
  onRestoreError: event => onRestoreError && onRestoreError(event.nativeEvent),
79
- onDismiss: () => onDismiss && onDismiss()
103
+ onDismiss: () => onDismiss && onDismiss(),
104
+ onPurchasePackageInitiated: event => {
105
+ const {
106
+ packageBeingPurchased,
107
+ requestId
108
+ } = event.nativeEvent;
109
+ if (onPurchasePackageInitiated) {
110
+ const resume = shouldProceed => {
111
+ RNPaywalls.resumePurchasePackageInitiated(requestId, shouldProceed);
112
+ };
113
+ onPurchasePackageInitiated({
114
+ packageBeingPurchased,
115
+ resume
116
+ });
117
+ } else {
118
+ RNPaywalls.resumePurchasePackageInitiated(requestId, true);
119
+ }
120
+ }
80
121
  });
81
122
  }
82
123
  throw new Error(NATIVE_MODULE_NOT_FOUND_ERROR);
@@ -110,10 +151,12 @@ const InternalPaywallFooterView = ({
110
151
  onDismiss: onDismiss
111
152
  });
112
153
  } else if (!!NativePaywallFooter) {
154
+ // Transform options to native format (CustomVariables -> string map)
155
+ const nativeOptions = (0, _customVariables.transformOptionsForNative)(options);
113
156
  return /*#__PURE__*/_react.default.createElement(NativePaywallFooter, {
114
157
  style: style,
115
158
  children: children,
116
- options: options,
159
+ options: nativeOptions,
117
160
  onPurchaseStarted: event => onPurchaseStarted && onPurchaseStarted(event.nativeEvent),
118
161
  onPurchaseCompleted: event => onPurchaseCompleted && onPurchaseCompleted(event.nativeEvent),
119
162
  onPurchaseError: event => onPurchaseError && onPurchaseError(event.nativeEvent),
@@ -130,6 +173,21 @@ const InternalPaywallFooterView = ({
130
173
 
131
174
  // Currently the same as the base type, but can be extended later if needed
132
175
 
176
+ /**
177
+ * Helper type that replaces CustomVariables with NativeCustomVariables in an options type.
178
+ * @internal
179
+ */
180
+
181
+ /**
182
+ * Native props for FullScreenPaywall component.
183
+ * @internal
184
+ */
185
+
186
+ /**
187
+ * Native props for FooterPaywall component.
188
+ * @internal
189
+ */
190
+
133
191
  const InternalCustomerCenterView = !usingPreviewAPIMode && _reactNative.UIManager.getViewManagerConfig('CustomerCenterView') != null ? (0, _reactNative.requireNativeComponent)('CustomerCenterView') : null;
134
192
 
135
193
  // This is to prevent breaking changes when the native SDK adds new options
@@ -159,12 +217,13 @@ class RevenueCatUI {
159
217
  static presentPaywall({
160
218
  offering,
161
219
  displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
162
- fontFamily
220
+ fontFamily,
221
+ customVariables
163
222
  } = {}) {
164
223
  var _offering$availablePa;
165
224
  throwIfNativeModulesNotAvailable();
166
225
  RevenueCatUI.logWarningIfPreviewAPIMode("presentPaywall");
167
- return RNPaywalls.presentPaywall((offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa = offering.availablePackages) === null || _offering$availablePa === void 0 || (_offering$availablePa = _offering$availablePa[0]) === null || _offering$availablePa === void 0 ? void 0 : _offering$availablePa.presentedOfferingContext, displayCloseButton, fontFamily);
226
+ return RNPaywalls.presentPaywall((offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa = offering.availablePackages) === null || _offering$availablePa === void 0 || (_offering$availablePa = _offering$availablePa[0]) === null || _offering$availablePa === void 0 ? void 0 : _offering$availablePa.presentedOfferingContext, displayCloseButton, fontFamily, (0, _customVariables.convertCustomVariablesToStringMap)(customVariables));
168
227
  }
169
228
 
170
229
  /**
@@ -184,12 +243,13 @@ class RevenueCatUI {
184
243
  requiredEntitlementIdentifier,
185
244
  offering,
186
245
  displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
187
- fontFamily
246
+ fontFamily,
247
+ customVariables
188
248
  }) {
189
249
  var _offering$availablePa2;
190
250
  throwIfNativeModulesNotAvailable();
191
251
  RevenueCatUI.logWarningIfPreviewAPIMode("presentPaywallIfNeeded");
192
- return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier, (offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa2 = offering.availablePackages) === null || _offering$availablePa2 === void 0 || (_offering$availablePa2 = _offering$availablePa2[0]) === null || _offering$availablePa2 === void 0 ? void 0 : _offering$availablePa2.presentedOfferingContext, displayCloseButton, fontFamily);
252
+ return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier, (offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa2 = offering.availablePackages) === null || _offering$availablePa2 === void 0 || (_offering$availablePa2 = _offering$availablePa2[0]) === null || _offering$availablePa2 === void 0 ? void 0 : _offering$availablePa2.presentedOfferingContext, displayCloseButton, fontFamily, (0, _customVariables.convertCustomVariablesToStringMap)(customVariables));
193
253
  }
194
254
  static Paywall = ({
195
255
  style,
@@ -202,7 +262,8 @@ class RevenueCatUI {
202
262
  onRestoreStarted,
203
263
  onRestoreCompleted,
204
264
  onRestoreError,
205
- onDismiss
265
+ onDismiss,
266
+ onPurchasePackageInitiated
206
267
  }) => {
207
268
  return /*#__PURE__*/_react.default.createElement(InternalPaywall, {
208
269
  options: options,
@@ -215,6 +276,7 @@ class RevenueCatUI {
215
276
  onRestoreCompleted: onRestoreCompleted,
216
277
  onRestoreError: onRestoreError,
217
278
  onDismiss: onDismiss,
279
+ onPurchasePackageInitiated: onPurchasePackageInitiated,
218
280
  style: [{
219
281
  flex: 1
220
282
  }, style]