@revenuecat/purchases-capacitor-ui 12.1.2 → 12.2.0

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.
package/Package.swift CHANGED
@@ -11,7 +11,7 @@ let package = Package(
11
11
  ],
12
12
  dependencies: [
13
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
14
- .package(url: "https://github.com/RevenueCat/purchases-hybrid-common.git", exact: "17.33.1")
14
+ .package(url: "https://github.com/RevenueCat/purchases-hybrid-common.git", exact: "17.41.1")
15
15
  ],
16
16
  targets: [
17
17
  .target(
@@ -13,6 +13,6 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '15.0'
15
15
  s.dependency 'Capacitor'
16
- s.dependency 'PurchasesHybridCommonUI', '17.33.1'
16
+ s.dependency 'PurchasesHybridCommonUI', '17.41.1'
17
17
  s.swift_version = '5.1'
18
18
  end
@@ -51,6 +51,6 @@ repositories {
51
51
  dependencies {
52
52
  implementation project(':capacitor-android')
53
53
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
54
- implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.33.1'
54
+ implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.41.1'
55
55
  implementation "org.jetbrains.kotlin:kotlin-stdlib:2.2.20"
56
56
  }
@@ -10,9 +10,11 @@ import com.getcapacitor.PluginCall
10
10
  import com.getcapacitor.PluginMethod
11
11
  import com.getcapacitor.annotation.CapacitorPlugin
12
12
  import com.revenuecat.purchases.PresentedOfferingContext
13
+ import com.revenuecat.purchases.hybridcommon.ui.PaywallListenerWrapper
13
14
  import com.revenuecat.purchases.hybridcommon.ui.PaywallResultListener
14
15
  import com.revenuecat.purchases.hybridcommon.ui.PaywallSource
15
16
  import com.revenuecat.purchases.hybridcommon.ui.PresentPaywallOptions
17
+ import com.revenuecat.purchases.hybridcommon.ui.HybridPurchaseLogicBridge
16
18
  import com.revenuecat.purchases.hybridcommon.ui.presentPaywallFromFragment
17
19
  import com.revenuecat.purchases.ui.revenuecatui.customercenter.ShowCustomerCenter
18
20
  import org.json.JSONObject
@@ -81,6 +83,50 @@ class RevenueCatUIPlugin : Plugin(), PaywallResultListener {
81
83
  )
82
84
  }
83
85
 
86
+ @PluginMethod
87
+ fun resumePurchaseInitiated(call: PluginCall) {
88
+ call.withRequiredRequestId { requestId ->
89
+ val shouldProceed = call.getBoolean("shouldProceed") ?: true
90
+ PaywallListenerWrapper.resumePurchasePackageInitiated(requestId, shouldProceed)
91
+ }
92
+ }
93
+
94
+ @PluginMethod
95
+ fun resumePurchaseLogicPurchase(call: PluginCall) {
96
+ resolvePurchaseLogicResult(call)
97
+ }
98
+
99
+ @PluginMethod
100
+ fun resumePurchaseLogicRestore(call: PluginCall) {
101
+ resolvePurchaseLogicResult(call)
102
+ }
103
+
104
+ private fun resolvePurchaseLogicResult(call: PluginCall) {
105
+ val requestId = call.getString("requestId")
106
+ if (requestId == null) {
107
+ call.reject("PAYWALL_ERROR", "Missing requestId")
108
+ return
109
+ }
110
+ val resultString = call.getString("result")
111
+ if (resultString == null) {
112
+ call.reject("PAYWALL_ERROR", "Missing result")
113
+ return
114
+ }
115
+ val errorMessage = call.getObject("error")?.optString("message")
116
+ HybridPurchaseLogicBridge.resolveResult(requestId, resultString, errorMessage)
117
+ call.resolve()
118
+ }
119
+
120
+ private fun PluginCall.withRequiredRequestId(block: (String) -> Unit) {
121
+ val requestId = getString("requestId")
122
+ if (requestId == null) {
123
+ reject("PAYWALL_ERROR", "Missing requestId")
124
+ return
125
+ }
126
+ block(requestId)
127
+ resolve()
128
+ }
129
+
84
130
  /**
85
131
  * Shared implementation for presenting a paywall
86
132
  */
@@ -111,6 +157,9 @@ class RevenueCatUIPlugin : Plugin(), PaywallResultListener {
111
157
  return
112
158
  }
113
159
 
160
+ val hasPaywallListener = call.getBoolean("hasPaywallListener") ?: false
161
+ val hasPurchaseLogic = call.getBoolean("hasPurchaseLogic") ?: false
162
+
114
163
  val presentedOfferingContext = presentedOfferingContext?.let { jsContext ->
115
164
  val offeringId = jsContext.optString("offeringIdentifier").takeUnless { it.isNullOrEmpty() }
116
165
  if (offeringId == null) { return@let null }
@@ -140,17 +189,86 @@ class RevenueCatUIPlugin : Plugin(), PaywallResultListener {
140
189
  PaywallSource.DefaultOffering
141
190
  }
142
191
 
192
+ val listener = if (hasPaywallListener) createPaywallListenerWrapper() else null
193
+ val purchaseLogic = if (hasPurchaseLogic) createPurchaseLogicBridge() else null
194
+
143
195
  val options = PresentPaywallOptions(
144
196
  paywallSource = paywallSource,
145
197
  requiredEntitlementIdentifier = requiredEntitlementIdentifier,
146
198
  shouldDisplayDismissButton = displayCloseButton,
147
- paywallResultListener = this
199
+ paywallResultListener = this,
200
+ paywallListener = listener,
201
+ purchaseLogic = purchaseLogic,
148
202
  )
149
203
 
150
204
  presentPaywallFromFragment(currentActivity, options)
151
205
  notifyListeners("paywallDisplayed", JSObject())
152
206
  }
153
207
 
208
+ private fun createPaywallListenerWrapper(): PaywallListenerWrapper {
209
+ return object : PaywallListenerWrapper() {
210
+ override fun onPurchaseStarted(rcPackage: Map<String, Any?>) {
211
+ notifyListeners("onPurchaseStarted", JSObject().apply {
212
+ put("packageBeingPurchased", JSONObject(rcPackage))
213
+ })
214
+ }
215
+
216
+ override fun onPurchaseCompleted(
217
+ customerInfo: Map<String, Any?>,
218
+ storeTransaction: Map<String, Any?>,
219
+ ) {
220
+ notifyListeners("onPurchaseCompleted", JSObject().apply {
221
+ put("customerInfo", JSONObject(customerInfo))
222
+ put("storeTransaction", JSONObject(storeTransaction))
223
+ })
224
+ }
225
+
226
+ override fun onPurchaseError(error: Map<String, Any?>) {
227
+ notifyListeners("onPurchaseError", JSObject().apply {
228
+ put("error", JSONObject(error))
229
+ })
230
+ }
231
+
232
+ override fun onPurchaseCancelled() {
233
+ notifyListeners("onPurchaseCancelled", JSObject())
234
+ }
235
+
236
+ override fun onRestoreStarted() {
237
+ notifyListeners("onRestoreStarted", JSObject())
238
+ }
239
+
240
+ override fun onRestoreCompleted(customerInfo: Map<String, Any?>) {
241
+ notifyListeners("onRestoreCompleted", JSObject().apply {
242
+ put("customerInfo", JSONObject(customerInfo))
243
+ })
244
+ }
245
+
246
+ override fun onRestoreError(error: Map<String, Any?>) {
247
+ notifyListeners("onRestoreError", JSObject().apply {
248
+ put("error", JSONObject(error))
249
+ })
250
+ }
251
+
252
+ override fun onPurchasePackageInitiated(rcPackage: Map<String, Any?>, requestId: String) {
253
+ notifyListeners("onPurchaseInitiated", JSObject().apply {
254
+ put("package", JSONObject(rcPackage))
255
+ put("requestId", requestId)
256
+ })
257
+ }
258
+ }
259
+ }
260
+
261
+ private fun createPurchaseLogicBridge(): HybridPurchaseLogicBridge {
262
+ return HybridPurchaseLogicBridge(
263
+ onPerformPurchase = { eventData ->
264
+ notifyListeners("onPerformPurchaseRequest", JSObject(JSONObject(eventData).toString()))
265
+ },
266
+ onPerformRestore = { eventData ->
267
+ notifyListeners("onPerformRestoreRequest", JSObject(JSONObject(eventData).toString()))
268
+ },
269
+ )
270
+ }
271
+
154
272
  @PluginMethod
155
273
  fun presentCustomerCenter(call: PluginCall) {
156
274
  val currentActivity = activity
@@ -1,5 +1,5 @@
1
1
  import type { PluginListenerHandle } from '@capacitor/core';
2
- import type { PurchasesOffering } from '@revenuecat/purchases-typescript-internal-esm';
2
+ import type { CustomerInfo, PurchasesError, PurchasesOffering, PurchasesPackage, PurchasesStoreTransaction } from '@revenuecat/purchases-typescript-internal-esm';
3
3
  import { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';
4
4
  export interface RevenueCatUIPlugin {
5
5
  /**
@@ -37,6 +37,108 @@ export interface RevenueCatUIPlugin {
37
37
  */
38
38
  removeAllListeners(): Promise<void>;
39
39
  }
40
+ /**
41
+ * Object passed to onPurchaseInitiated that allows the developer to
42
+ * control when (and whether) the purchase flow continues.
43
+ * The developer can store this and call resume() asynchronously
44
+ * (e.g., after an auth flow on a different screen).
45
+ */
46
+ export interface PurchaseResumable {
47
+ /** Call to proceed with or cancel the purchase. Defaults to true (proceed). */
48
+ resume(shouldProceed?: boolean): void;
49
+ }
50
+ /**
51
+ * Callbacks for observing paywall lifecycle events such as purchases,
52
+ * restores, and errors. All callbacks are optional.
53
+ *
54
+ * Pass as `listener` in {@link PresentPaywallOptions} to receive events
55
+ * while the paywall is displayed.
56
+ */
57
+ export interface PaywallListener {
58
+ /** Called when a purchase begins for a package. */
59
+ onPurchaseStarted?: (args: {
60
+ packageBeingPurchased: PurchasesPackage;
61
+ }) => void;
62
+ /** Called when a purchase completes successfully. */
63
+ onPurchaseCompleted?: (args: {
64
+ customerInfo: CustomerInfo;
65
+ storeTransaction: PurchasesStoreTransaction;
66
+ }) => void;
67
+ /** Called when a purchase fails with an error. */
68
+ onPurchaseError?: (args: {
69
+ error: PurchasesError;
70
+ }) => void;
71
+ /** Called when the user cancels a purchase. */
72
+ onPurchaseCancelled?: () => void;
73
+ /** Called when a restore operation begins. */
74
+ onRestoreStarted?: () => void;
75
+ /** Called when a restore operation completes successfully. */
76
+ onRestoreCompleted?: (args: {
77
+ customerInfo: CustomerInfo;
78
+ }) => void;
79
+ /** Called when a restore operation fails with an error. */
80
+ onRestoreError?: (args: {
81
+ error: PurchasesError;
82
+ }) => void;
83
+ /**
84
+ * Called before the payment sheet is displayed, allowing the app to gate
85
+ * the purchase flow (e.g., require authentication first).
86
+ *
87
+ * The developer receives a {@link PurchaseResumable} that can be stored
88
+ * and called asynchronously. Call `resumable.resume(true)` to proceed
89
+ * with the purchase, or `resumable.resume(false)` to cancel it.
90
+ *
91
+ * If this callback is not provided, the purchase proceeds automatically.
92
+ */
93
+ onPurchaseInitiated?: (args: {
94
+ packageBeingPurchased: PurchasesPackage;
95
+ resumable: PurchaseResumable;
96
+ }) => void;
97
+ }
98
+ /**
99
+ * Custom purchase and restore handlers for apps that manage their own
100
+ * purchase flow (`purchasesAreCompletedBy: MY_APP`).
101
+ *
102
+ * When provided in {@link PresentPaywallOptions}, the paywall delegates
103
+ * purchase and restore operations to these handlers instead of using
104
+ * RevenueCat's built-in purchase flow.
105
+ */
106
+ export interface PurchaseLogic {
107
+ /**
108
+ * Called when the user initiates a purchase from the paywall.
109
+ * Perform the purchase using your own payment system and return the result.
110
+ */
111
+ performPurchase: (args: {
112
+ packageToPurchase: PurchasesPackage;
113
+ }) => Promise<PurchaseLogicResult>;
114
+ /**
115
+ * Called when the user initiates a restore from the paywall.
116
+ * Perform the restore using your own system and return the result.
117
+ */
118
+ performRestore: () => Promise<PurchaseLogicResult>;
119
+ }
120
+ /**
121
+ * The result of a custom purchase or restore operation performed by {@link PurchaseLogic}.
122
+ */
123
+ export type PurchaseLogicResult = {
124
+ result: PURCHASE_LOGIC_RESULT.SUCCESS;
125
+ } | {
126
+ result: PURCHASE_LOGIC_RESULT.CANCELLATION;
127
+ } | {
128
+ result: PURCHASE_LOGIC_RESULT.ERROR;
129
+ error?: PurchasesError;
130
+ };
131
+ /**
132
+ * Possible outcomes from a custom {@link PurchaseLogic} operation.
133
+ */
134
+ export declare enum PURCHASE_LOGIC_RESULT {
135
+ /** The purchase or restore completed successfully. */
136
+ SUCCESS = "SUCCESS",
137
+ /** The user cancelled the purchase or restore. */
138
+ CANCELLATION = "CANCELLATION",
139
+ /** The purchase or restore failed with an error. */
140
+ ERROR = "ERROR"
141
+ }
40
142
  export interface PresentPaywallOptions {
41
143
  /**
42
144
  * The offering to present.
@@ -48,6 +150,16 @@ export interface PresentPaywallOptions {
48
150
  * Only applicable for original template paywalls, ignored for V2 Paywalls.
49
151
  */
50
152
  displayCloseButton?: boolean;
153
+ /**
154
+ * Optional listener for paywall lifecycle events such as purchase
155
+ * completion, restoration, and errors.
156
+ */
157
+ listener?: PaywallListener;
158
+ /**
159
+ * Optional custom purchase/restore logic for when
160
+ * `purchasesAreCompletedBy` is set to `MY_APP`.
161
+ */
162
+ purchaseLogic?: PurchaseLogic;
51
163
  }
52
164
  export interface PresentPaywallIfNeededOptions extends PresentPaywallOptions {
53
165
  /**
@@ -1,4 +1,16 @@
1
1
  import { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';
2
+ /**
3
+ * Possible outcomes from a custom {@link PurchaseLogic} operation.
4
+ */
5
+ export var PURCHASE_LOGIC_RESULT;
6
+ (function (PURCHASE_LOGIC_RESULT) {
7
+ /** The purchase or restore completed successfully. */
8
+ PURCHASE_LOGIC_RESULT["SUCCESS"] = "SUCCESS";
9
+ /** The user cancelled the purchase or restore. */
10
+ PURCHASE_LOGIC_RESULT["CANCELLATION"] = "CANCELLATION";
11
+ /** The purchase or restore failed with an error. */
12
+ PURCHASE_LOGIC_RESULT["ERROR"] = "ERROR";
13
+ })(PURCHASE_LOGIC_RESULT || (PURCHASE_LOGIC_RESULT = {}));
2
14
  // Using the enum from purchases-typescript-internal-esm instead of defining our own
3
15
  export { PAYWALL_RESULT as PaywallResultEnum };
4
16
  //# sourceMappingURL=definitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAgE/E,oFAAoF;AACpF,OAAO,EAAE,cAAc,IAAI,iBAAiB,EAAE,CAAC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\nimport type { PurchasesOffering } from '@revenuecat/purchases-typescript-internal-esm';\nimport { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';\n\nexport interface RevenueCatUIPlugin {\n /**\n * Presents a paywall configured in the RevenueCat dashboard.\n * @param options The options for presenting the paywall.\n * @returns A PaywallResult indicating what happened during the paywall presentation.\n */\n presentPaywall(options?: PresentPaywallOptions): Promise<PaywallResult>;\n\n /**\n * Presents a paywall only if the user does not have the specified entitlement.\n * @param options The options for presenting the paywall if needed.\n * @returns A PaywallResult indicating what happened during the paywall presentation.\n */\n presentPaywallIfNeeded(options: PresentPaywallIfNeededOptions): Promise<PaywallResult>;\n\n /**\n * Presents the customer center where users can manage their subscriptions.\n */\n presentCustomerCenter(): Promise<void>;\n\n /**\n * Used for web only. Enables or disables returning mock results instead of rejecting promises with \"not supported\".\n * For testing purposes only.\n * @param options Options for mock web results\n */\n setMockWebResults?(options: { shouldMockWebResults: boolean }): Promise<void>;\n\n /**\n * Listen for when a paywall is displayed or dismissed.\n * @param eventName The event to listen for\n * @param listener The listener to call when the event is triggered\n */\n addListener(eventName: 'paywallDisplayed' | 'paywallDismissed', listener: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Remove all listeners for this plugin.\n */\n removeAllListeners(): Promise<void>;\n}\n\nexport interface PresentPaywallOptions {\n /**\n * The offering to present.\n * If not provided, the current offering will be used.\n */\n offering?: PurchasesOffering;\n\n /**\n * Whether to display a close button on the paywall.\n * Only applicable for original template paywalls, ignored for V2 Paywalls.\n */\n displayCloseButton?: boolean;\n}\n\nexport interface PresentPaywallIfNeededOptions extends PresentPaywallOptions {\n /**\n * The identifier of the entitlement that is required.\n * The paywall will only be presented if the user doesn't have this entitlement.\n */\n requiredEntitlementIdentifier: string;\n}\n\n// Using the enum from purchases-typescript-internal-esm instead of defining our own\nexport { PAYWALL_RESULT as PaywallResultEnum };\n\nexport interface PaywallResult {\n /**\n * The result of the paywall presentation.\n */\n result: PAYWALL_RESULT;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAqH/E;;GAEG;AACH,MAAM,CAAN,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC/B,sDAAsD;IACtD,4CAAmB,CAAA;IACnB,kDAAkD;IAClD,sDAA6B,CAAA;IAC7B,oDAAoD;IACpD,wCAAe,CAAA;AACjB,CAAC,EAPW,qBAAqB,KAArB,qBAAqB,QAOhC;AAoCD,oFAAoF;AACpF,OAAO,EAAE,cAAc,IAAI,iBAAiB,EAAE,CAAC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\nimport type {\n CustomerInfo,\n PurchasesError,\n PurchasesOffering,\n PurchasesPackage,\n PurchasesStoreTransaction,\n} from '@revenuecat/purchases-typescript-internal-esm';\nimport { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';\n\nexport interface RevenueCatUIPlugin {\n /**\n * Presents a paywall configured in the RevenueCat dashboard.\n * @param options The options for presenting the paywall.\n * @returns A PaywallResult indicating what happened during the paywall presentation.\n */\n presentPaywall(options?: PresentPaywallOptions): Promise<PaywallResult>;\n\n /**\n * Presents a paywall only if the user does not have the specified entitlement.\n * @param options The options for presenting the paywall if needed.\n * @returns A PaywallResult indicating what happened during the paywall presentation.\n */\n presentPaywallIfNeeded(options: PresentPaywallIfNeededOptions): Promise<PaywallResult>;\n\n /**\n * Presents the customer center where users can manage their subscriptions.\n */\n presentCustomerCenter(): Promise<void>;\n\n /**\n * Used for web only. Enables or disables returning mock results instead of rejecting promises with \"not supported\".\n * For testing purposes only.\n * @param options Options for mock web results\n */\n setMockWebResults?(options: { shouldMockWebResults: boolean }): Promise<void>;\n\n /**\n * Listen for when a paywall is displayed or dismissed.\n * @param eventName The event to listen for\n * @param listener The listener to call when the event is triggered\n */\n addListener(eventName: 'paywallDisplayed' | 'paywallDismissed', listener: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Remove all listeners for this plugin.\n */\n removeAllListeners(): Promise<void>;\n}\n\n/**\n * Object passed to onPurchaseInitiated that allows the developer to\n * control when (and whether) the purchase flow continues.\n * The developer can store this and call resume() asynchronously\n * (e.g., after an auth flow on a different screen).\n */\nexport interface PurchaseResumable {\n /** Call to proceed with or cancel the purchase. Defaults to true (proceed). */\n resume(shouldProceed?: boolean): void;\n}\n\n/**\n * Callbacks for observing paywall lifecycle events such as purchases,\n * restores, and errors. All callbacks are optional.\n *\n * Pass as `listener` in {@link PresentPaywallOptions} to receive events\n * while the paywall is displayed.\n */\nexport interface PaywallListener {\n /** Called when a purchase begins for a package. */\n onPurchaseStarted?: (args: { packageBeingPurchased: PurchasesPackage }) => void;\n /** Called when a purchase completes successfully. */\n onPurchaseCompleted?: (args: { customerInfo: CustomerInfo; storeTransaction: PurchasesStoreTransaction }) => void;\n /** Called when a purchase fails with an error. */\n onPurchaseError?: (args: { error: PurchasesError }) => void;\n /** Called when the user cancels a purchase. */\n onPurchaseCancelled?: () => void;\n /** Called when a restore operation begins. */\n onRestoreStarted?: () => void;\n /** Called when a restore operation completes successfully. */\n onRestoreCompleted?: (args: { customerInfo: CustomerInfo }) => void;\n /** Called when a restore operation fails with an error. */\n onRestoreError?: (args: { error: PurchasesError }) => void;\n /**\n * Called before the payment sheet is displayed, allowing the app to gate\n * the purchase flow (e.g., require authentication first).\n *\n * The developer receives a {@link PurchaseResumable} that can be stored\n * and called asynchronously. Call `resumable.resume(true)` to proceed\n * with the purchase, or `resumable.resume(false)` to cancel it.\n *\n * If this callback is not provided, the purchase proceeds automatically.\n */\n onPurchaseInitiated?: (args: { packageBeingPurchased: PurchasesPackage; resumable: PurchaseResumable }) => void;\n}\n\n/**\n * Custom purchase and restore handlers for apps that manage their own\n * purchase flow (`purchasesAreCompletedBy: MY_APP`).\n *\n * When provided in {@link PresentPaywallOptions}, the paywall delegates\n * purchase and restore operations to these handlers instead of using\n * RevenueCat's built-in purchase flow.\n */\nexport interface PurchaseLogic {\n /**\n * Called when the user initiates a purchase from the paywall.\n * Perform the purchase using your own payment system and return the result.\n */\n performPurchase: (args: { packageToPurchase: PurchasesPackage }) => Promise<PurchaseLogicResult>;\n /**\n * Called when the user initiates a restore from the paywall.\n * Perform the restore using your own system and return the result.\n */\n performRestore: () => Promise<PurchaseLogicResult>;\n}\n\n/**\n * The result of a custom purchase or restore operation performed by {@link PurchaseLogic}.\n */\nexport type PurchaseLogicResult =\n | { result: PURCHASE_LOGIC_RESULT.SUCCESS }\n | { result: PURCHASE_LOGIC_RESULT.CANCELLATION }\n | { result: PURCHASE_LOGIC_RESULT.ERROR; error?: PurchasesError };\n\n/**\n * Possible outcomes from a custom {@link PurchaseLogic} operation.\n */\nexport enum PURCHASE_LOGIC_RESULT {\n /** The purchase or restore completed successfully. */\n SUCCESS = 'SUCCESS',\n /** The user cancelled the purchase or restore. */\n CANCELLATION = 'CANCELLATION',\n /** The purchase or restore failed with an error. */\n ERROR = 'ERROR',\n}\n\nexport interface PresentPaywallOptions {\n /**\n * The offering to present.\n * If not provided, the current offering will be used.\n */\n offering?: PurchasesOffering;\n\n /**\n * Whether to display a close button on the paywall.\n * Only applicable for original template paywalls, ignored for V2 Paywalls.\n */\n displayCloseButton?: boolean;\n\n /**\n * Optional listener for paywall lifecycle events such as purchase\n * completion, restoration, and errors.\n */\n listener?: PaywallListener;\n\n /**\n * Optional custom purchase/restore logic for when\n * `purchasesAreCompletedBy` is set to `MY_APP`.\n */\n purchaseLogic?: PurchaseLogic;\n}\n\nexport interface PresentPaywallIfNeededOptions extends PresentPaywallOptions {\n /**\n * The identifier of the entitlement that is required.\n * The paywall will only be presented if the user doesn't have this entitlement.\n */\n requiredEntitlementIdentifier: string;\n}\n\n// Using the enum from purchases-typescript-internal-esm instead of defining our own\nexport { PAYWALL_RESULT as PaywallResultEnum };\n\nexport interface PaywallResult {\n /**\n * The result of the paywall presentation.\n */\n result: PAYWALL_RESULT;\n}\n"]}
package/dist/esm/index.js CHANGED
@@ -1,7 +1,181 @@
1
1
  import { registerPlugin } from '@capacitor/core';
2
- const RevenueCatUI = registerPlugin('RevenueCatUI', {
2
+ import { PURCHASE_LOGIC_RESULT } from './definitions';
3
+ const nativePlugin = registerPlugin('RevenueCatUI', {
3
4
  web: () => import('./web').then((m) => new m.RevenueCatUIWeb()),
4
5
  });
6
+ function serializeResultForNative(result) {
7
+ if (result.result === PURCHASE_LOGIC_RESULT.ERROR && 'error' in result && result.error) {
8
+ return {
9
+ result: result.result,
10
+ error: {
11
+ code: result.error.code,
12
+ message: result.error.message,
13
+ },
14
+ };
15
+ }
16
+ return { result: result.result };
17
+ }
18
+ async function presentWithListenerSupport(nativeMethod, options, listener, purchaseLogic) {
19
+ const handles = [];
20
+ try {
21
+ // Register PaywallListener event handlers
22
+ if (listener) {
23
+ if (listener.onPurchaseStarted) {
24
+ const cb = listener.onPurchaseStarted;
25
+ handles.push(await nativePlugin.addListener('onPurchaseStarted', (data) => {
26
+ var _a;
27
+ cb({ packageBeingPurchased: (_a = data.packageBeingPurchased) !== null && _a !== void 0 ? _a : data });
28
+ }));
29
+ }
30
+ if (listener.onPurchaseCompleted) {
31
+ const cb = listener.onPurchaseCompleted;
32
+ handles.push(await nativePlugin.addListener('onPurchaseCompleted', (data) => {
33
+ cb({
34
+ customerInfo: data.customerInfo,
35
+ storeTransaction: data.storeTransaction,
36
+ });
37
+ }));
38
+ }
39
+ if (listener.onPurchaseError) {
40
+ const cb = listener.onPurchaseError;
41
+ handles.push(await nativePlugin.addListener('onPurchaseError', (data) => {
42
+ var _a;
43
+ cb({ error: (_a = data.error) !== null && _a !== void 0 ? _a : data });
44
+ }));
45
+ }
46
+ if (listener.onPurchaseCancelled) {
47
+ const cb = listener.onPurchaseCancelled;
48
+ handles.push(await nativePlugin.addListener('onPurchaseCancelled', () => {
49
+ cb();
50
+ }));
51
+ }
52
+ if (listener.onRestoreStarted) {
53
+ const cb = listener.onRestoreStarted;
54
+ handles.push(await nativePlugin.addListener('onRestoreStarted', () => {
55
+ cb();
56
+ }));
57
+ }
58
+ if (listener.onRestoreCompleted) {
59
+ const cb = listener.onRestoreCompleted;
60
+ handles.push(await nativePlugin.addListener('onRestoreCompleted', (data) => {
61
+ var _a;
62
+ cb({ customerInfo: (_a = data.customerInfo) !== null && _a !== void 0 ? _a : data });
63
+ }));
64
+ }
65
+ if (listener.onRestoreError) {
66
+ const cb = listener.onRestoreError;
67
+ handles.push(await nativePlugin.addListener('onRestoreError', (data) => {
68
+ var _a;
69
+ cb({ error: (_a = data.error) !== null && _a !== void 0 ? _a : data });
70
+ }));
71
+ }
72
+ }
73
+ // Always register onPurchaseInitiated so we auto-resume when the user
74
+ // doesn't provide an onPurchaseInitiated callback — otherwise the
75
+ // purchase flow hangs waiting for a resume that never comes.
76
+ // This must be outside the `if (listener)` block because the native
77
+ // delegate adapter always fires this event (even with purchaseLogic only).
78
+ const onPurchaseInitiatedCb = listener === null || listener === void 0 ? void 0 : listener.onPurchaseInitiated;
79
+ handles.push(await nativePlugin.addListener('onPurchaseInitiated', (data) => {
80
+ var _a;
81
+ const requestId = data.requestId;
82
+ const packageBeingPurchased = (_a = data.package) !== null && _a !== void 0 ? _a : data.packageBeingPurchased;
83
+ if (onPurchaseInitiatedCb) {
84
+ onPurchaseInitiatedCb({
85
+ packageBeingPurchased,
86
+ resumable: {
87
+ resume(shouldProceed = true) {
88
+ nativePlugin.resumePurchaseInitiated({ requestId, shouldProceed });
89
+ },
90
+ },
91
+ });
92
+ }
93
+ else {
94
+ // No callback provided — auto-proceed with the purchase.
95
+ nativePlugin.resumePurchaseInitiated({ requestId, shouldProceed: true });
96
+ }
97
+ }));
98
+ // Register PurchaseLogic event handlers
99
+ if (purchaseLogic) {
100
+ handles.push(await nativePlugin.addListener('onPerformPurchaseRequest', async (data) => {
101
+ var _a, _b, _c;
102
+ const requestId = data.requestId;
103
+ const packageToPurchase = (_b = (_a = data.package) !== null && _a !== void 0 ? _a : data.packageBeingPurchased) !== null && _b !== void 0 ? _b : data.packageToPurchase;
104
+ try {
105
+ const result = await purchaseLogic.performPurchase({ packageToPurchase });
106
+ await nativePlugin.resumePurchaseLogicPurchase(Object.assign({ requestId }, serializeResultForNative(result)));
107
+ }
108
+ catch (e) {
109
+ await nativePlugin.resumePurchaseLogicPurchase({
110
+ requestId,
111
+ result: PURCHASE_LOGIC_RESULT.ERROR,
112
+ error: { message: (_c = e === null || e === void 0 ? void 0 : e.message) !== null && _c !== void 0 ? _c : 'Unknown error' },
113
+ });
114
+ }
115
+ }));
116
+ handles.push(await nativePlugin.addListener('onPerformRestoreRequest', async (data) => {
117
+ var _a;
118
+ const requestId = data.requestId;
119
+ try {
120
+ const result = await purchaseLogic.performRestore();
121
+ await nativePlugin.resumePurchaseLogicRestore(Object.assign({ requestId }, serializeResultForNative(result)));
122
+ }
123
+ catch (e) {
124
+ await nativePlugin.resumePurchaseLogicRestore({
125
+ requestId,
126
+ result: PURCHASE_LOGIC_RESULT.ERROR,
127
+ error: { message: (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : 'Unknown error' },
128
+ });
129
+ }
130
+ }));
131
+ }
132
+ // Call native with serializable-only options
133
+ const nativeOptions = Object.assign(Object.assign({}, options), { hasPaywallListener: !!listener, hasPurchaseLogic: !!purchaseLogic });
134
+ // Remove non-serializable fields
135
+ delete nativeOptions.listener;
136
+ delete nativeOptions.purchaseLogic;
137
+ return await nativeMethod(nativeOptions);
138
+ }
139
+ finally {
140
+ // Clean up all registered listeners
141
+ for (const handle of handles) {
142
+ await handle.remove();
143
+ }
144
+ }
145
+ }
146
+ const RevenueCatUI = {
147
+ async presentPaywall(options) {
148
+ const listener = options === null || options === void 0 ? void 0 : options.listener;
149
+ const purchaseLogic = options === null || options === void 0 ? void 0 : options.purchaseLogic;
150
+ if (!listener && !purchaseLogic) {
151
+ return nativePlugin.presentPaywall(options !== null && options !== void 0 ? options : {});
152
+ }
153
+ return presentWithListenerSupport((opts) => nativePlugin.presentPaywall(opts), Object.assign({}, options), listener, purchaseLogic);
154
+ },
155
+ async presentPaywallIfNeeded(options) {
156
+ const listener = options === null || options === void 0 ? void 0 : options.listener;
157
+ const purchaseLogic = options === null || options === void 0 ? void 0 : options.purchaseLogic;
158
+ if (!listener && !purchaseLogic) {
159
+ return nativePlugin.presentPaywallIfNeeded(options);
160
+ }
161
+ return presentWithListenerSupport((opts) => nativePlugin.presentPaywallIfNeeded(opts), Object.assign({}, options), listener, purchaseLogic);
162
+ },
163
+ async presentCustomerCenter() {
164
+ return nativePlugin.presentCustomerCenter();
165
+ },
166
+ setMockWebResults: nativePlugin.setMockWebResults
167
+ ? async (options) => {
168
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
169
+ return nativePlugin.setMockWebResults(options);
170
+ }
171
+ : undefined,
172
+ addListener(eventName, listener) {
173
+ return nativePlugin.addListener(eventName, listener);
174
+ },
175
+ removeAllListeners() {
176
+ return nativePlugin.removeAllListeners();
177
+ },
178
+ };
5
179
  export * from './definitions';
6
180
  export { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';
7
181
  export { RevenueCatUI };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,cAAc,EAAE;IACtE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;CAChE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { RevenueCatUIPlugin } from './definitions';\n\nconst RevenueCatUI = registerPlugin<RevenueCatUIPlugin>('RevenueCatUI', {\n web: () => import('./web').then((m) => new m.RevenueCatUIWeb()),\n});\n\nexport * from './definitions';\nexport { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';\nexport { RevenueCatUI };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerPlugin } from '@capacitor/core';\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type {\n PaywallListener,\n PaywallResult,\n PresentPaywallIfNeededOptions,\n PresentPaywallOptions,\n PurchaseLogic,\n PurchaseLogicResult,\n RevenueCatUIPlugin,\n} from './definitions';\nimport { PURCHASE_LOGIC_RESULT } from './definitions';\n\n/**\n * Internal native plugin interface with native-only methods and events.\n * This is not exported to consumers.\n */\ninterface RevenueCatUINativePlugin {\n presentPaywall(options: {\n offering?: any;\n displayCloseButton?: boolean;\n hasPaywallListener?: boolean;\n hasPurchaseLogic?: boolean;\n }): Promise<PaywallResult>;\n presentPaywallIfNeeded(options: {\n offering?: any;\n displayCloseButton?: boolean;\n requiredEntitlementIdentifier: string;\n hasPaywallListener?: boolean;\n hasPurchaseLogic?: boolean;\n }): Promise<PaywallResult>;\n presentCustomerCenter(): Promise<void>;\n setMockWebResults?(options: { shouldMockWebResults: boolean }): Promise<void>;\n\n resumePurchaseInitiated(options: { requestId: string; shouldProceed: boolean }): Promise<void>;\n resumePurchaseLogicPurchase(options: {\n requestId: string;\n result: string;\n error?: { code?: string; message?: string };\n }): Promise<void>;\n resumePurchaseLogicRestore(options: {\n requestId: string;\n result: string;\n error?: { code?: string; message?: string };\n }): Promise<void>;\n\n addListener(eventName: string, listener: (data: any) => void): Promise<PluginListenerHandle>;\n removeAllListeners(): Promise<void>;\n}\n\nconst nativePlugin = registerPlugin<RevenueCatUINativePlugin>('RevenueCatUI', {\n web: () => import('./web').then((m) => new m.RevenueCatUIWeb()),\n});\n\nfunction serializeResultForNative(result: PurchaseLogicResult): {\n result: string;\n error?: { code?: string; message?: string };\n} {\n if (result.result === PURCHASE_LOGIC_RESULT.ERROR && 'error' in result && result.error) {\n return {\n result: result.result,\n error: {\n code: result.error.code,\n message: result.error.message,\n },\n };\n }\n return { result: result.result };\n}\n\nasync function presentWithListenerSupport(\n nativeMethod: (options: any) => Promise<PaywallResult>,\n options: any,\n listener?: PaywallListener,\n purchaseLogic?: PurchaseLogic,\n): Promise<PaywallResult> {\n const handles: PluginListenerHandle[] = [];\n\n try {\n // Register PaywallListener event handlers\n if (listener) {\n if (listener.onPurchaseStarted) {\n const cb = listener.onPurchaseStarted;\n handles.push(\n await nativePlugin.addListener('onPurchaseStarted', (data: any) => {\n cb({ packageBeingPurchased: data.packageBeingPurchased ?? data });\n }),\n );\n }\n if (listener.onPurchaseCompleted) {\n const cb = listener.onPurchaseCompleted;\n handles.push(\n await nativePlugin.addListener('onPurchaseCompleted', (data: any) => {\n cb({\n customerInfo: data.customerInfo,\n storeTransaction: data.storeTransaction,\n });\n }),\n );\n }\n if (listener.onPurchaseError) {\n const cb = listener.onPurchaseError;\n handles.push(\n await nativePlugin.addListener('onPurchaseError', (data: any) => {\n cb({ error: data.error ?? data });\n }),\n );\n }\n if (listener.onPurchaseCancelled) {\n const cb = listener.onPurchaseCancelled;\n handles.push(\n await nativePlugin.addListener('onPurchaseCancelled', () => {\n cb();\n }),\n );\n }\n if (listener.onRestoreStarted) {\n const cb = listener.onRestoreStarted;\n handles.push(\n await nativePlugin.addListener('onRestoreStarted', () => {\n cb();\n }),\n );\n }\n if (listener.onRestoreCompleted) {\n const cb = listener.onRestoreCompleted;\n handles.push(\n await nativePlugin.addListener('onRestoreCompleted', (data: any) => {\n cb({ customerInfo: data.customerInfo ?? data });\n }),\n );\n }\n if (listener.onRestoreError) {\n const cb = listener.onRestoreError;\n handles.push(\n await nativePlugin.addListener('onRestoreError', (data: any) => {\n cb({ error: data.error ?? data });\n }),\n );\n }\n }\n\n // Always register onPurchaseInitiated so we auto-resume when the user\n // doesn't provide an onPurchaseInitiated callback — otherwise the\n // purchase flow hangs waiting for a resume that never comes.\n // This must be outside the `if (listener)` block because the native\n // delegate adapter always fires this event (even with purchaseLogic only).\n const onPurchaseInitiatedCb = listener?.onPurchaseInitiated;\n handles.push(\n await nativePlugin.addListener('onPurchaseInitiated', (data: any) => {\n const requestId: string = data.requestId;\n const packageBeingPurchased = data.package ?? data.packageBeingPurchased;\n if (onPurchaseInitiatedCb) {\n onPurchaseInitiatedCb({\n packageBeingPurchased,\n resumable: {\n resume(shouldProceed = true) {\n nativePlugin.resumePurchaseInitiated({ requestId, shouldProceed });\n },\n },\n });\n } else {\n // No callback provided — auto-proceed with the purchase.\n nativePlugin.resumePurchaseInitiated({ requestId, shouldProceed: true });\n }\n }),\n );\n\n // Register PurchaseLogic event handlers\n if (purchaseLogic) {\n handles.push(\n await nativePlugin.addListener('onPerformPurchaseRequest', async (data: any) => {\n const requestId: string = data.requestId;\n const packageToPurchase = data.package ?? data.packageBeingPurchased ?? data.packageToPurchase;\n try {\n const result = await purchaseLogic.performPurchase({ packageToPurchase });\n await nativePlugin.resumePurchaseLogicPurchase({\n requestId,\n ...serializeResultForNative(result),\n });\n } catch (e: any) {\n await nativePlugin.resumePurchaseLogicPurchase({\n requestId,\n result: PURCHASE_LOGIC_RESULT.ERROR,\n error: { message: e?.message ?? 'Unknown error' },\n });\n }\n }),\n );\n handles.push(\n await nativePlugin.addListener('onPerformRestoreRequest', async (data: any) => {\n const requestId: string = data.requestId;\n try {\n const result = await purchaseLogic.performRestore();\n await nativePlugin.resumePurchaseLogicRestore({\n requestId,\n ...serializeResultForNative(result),\n });\n } catch (e: any) {\n await nativePlugin.resumePurchaseLogicRestore({\n requestId,\n result: PURCHASE_LOGIC_RESULT.ERROR,\n error: { message: e?.message ?? 'Unknown error' },\n });\n }\n }),\n );\n }\n\n // Call native with serializable-only options\n const nativeOptions = {\n ...options,\n hasPaywallListener: !!listener,\n hasPurchaseLogic: !!purchaseLogic,\n };\n // Remove non-serializable fields\n delete nativeOptions.listener;\n delete nativeOptions.purchaseLogic;\n\n return await nativeMethod(nativeOptions);\n } finally {\n // Clean up all registered listeners\n for (const handle of handles) {\n await handle.remove();\n }\n }\n}\n\nconst RevenueCatUI: RevenueCatUIPlugin = {\n async presentPaywall(options?: PresentPaywallOptions): Promise<PaywallResult> {\n const listener = options?.listener;\n const purchaseLogic = options?.purchaseLogic;\n\n if (!listener && !purchaseLogic) {\n return nativePlugin.presentPaywall(options ?? {});\n }\n\n return presentWithListenerSupport(\n (opts) => nativePlugin.presentPaywall(opts),\n { ...options },\n listener,\n purchaseLogic,\n );\n },\n\n async presentPaywallIfNeeded(options: PresentPaywallIfNeededOptions): Promise<PaywallResult> {\n const listener = options?.listener;\n const purchaseLogic = options?.purchaseLogic;\n\n if (!listener && !purchaseLogic) {\n return nativePlugin.presentPaywallIfNeeded(options);\n }\n\n return presentWithListenerSupport(\n (opts) => nativePlugin.presentPaywallIfNeeded(opts),\n { ...options },\n listener,\n purchaseLogic,\n );\n },\n\n async presentCustomerCenter(): Promise<void> {\n return nativePlugin.presentCustomerCenter();\n },\n\n setMockWebResults: nativePlugin.setMockWebResults\n ? async (options: { shouldMockWebResults: boolean }): Promise<void> => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return nativePlugin.setMockWebResults!(options);\n }\n : undefined,\n\n addListener(eventName: 'paywallDisplayed' | 'paywallDismissed', listener: () => void): Promise<PluginListenerHandle> {\n return nativePlugin.addListener(eventName, listener);\n },\n\n removeAllListeners(): Promise<void> {\n return nativePlugin.removeAllListeners();\n },\n};\n\nexport * from './definitions';\nexport { PAYWALL_RESULT } from '@revenuecat/purchases-typescript-internal-esm';\nexport { RevenueCatUI };\n"]}
package/dist/esm/web.d.ts CHANGED
@@ -11,6 +11,26 @@ export declare class RevenueCatUIWeb extends WebPlugin implements RevenueCatUIPl
11
11
  presentPaywall(options?: PresentPaywallOptions): Promise<PaywallResult>;
12
12
  presentPaywallIfNeeded(options: PresentPaywallIfNeededOptions): Promise<PaywallResult>;
13
13
  presentCustomerCenter(): Promise<void>;
14
+ resumePurchaseInitiated(_options: {
15
+ requestId: string;
16
+ shouldProceed: boolean;
17
+ }): Promise<void>;
18
+ resumePurchaseLogicPurchase(_options: {
19
+ requestId: string;
20
+ result: string;
21
+ error?: {
22
+ code?: string;
23
+ message?: string;
24
+ };
25
+ }): Promise<void>;
26
+ resumePurchaseLogicRestore(_options: {
27
+ requestId: string;
28
+ result: string;
29
+ error?: {
30
+ code?: string;
31
+ message?: string;
32
+ };
33
+ }): Promise<void>;
14
34
  addListener(eventName: string, listener: (...args: any[]) => void): Promise<PluginListenerHandle>;
15
35
  removeAllListeners(): Promise<void>;
16
36
  private mockNonReturningFunctionIfEnabled;
package/dist/esm/web.js CHANGED
@@ -23,6 +23,18 @@ export class RevenueCatUIWeb extends WebPlugin {
23
23
  async presentCustomerCenter() {
24
24
  return this.mockNonReturningFunctionIfEnabled('presentCustomerCenter');
25
25
  }
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ async resumePurchaseInitiated(_options) {
28
+ return this.mockNonReturningFunctionIfEnabled('resumePurchaseInitiated');
29
+ }
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ async resumePurchaseLogicPurchase(_options) {
32
+ return this.mockNonReturningFunctionIfEnabled('resumePurchaseLogicPurchase');
33
+ }
34
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ async resumePurchaseLogicRestore(_options) {
36
+ return this.mockNonReturningFunctionIfEnabled('resumePurchaseLogicRestore');
37
+ }
26
38
  addListener(eventName, listener) {
27
39
  if (eventName !== 'paywallDisplayed' && eventName !== 'paywallDismissed') {
28
40
  console.warn(`Unsupported event: ${eventName}`);