expo-helium 3.1.3 → 3.1.4

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.
@@ -43,7 +43,7 @@ android {
43
43
  }
44
44
 
45
45
  dependencies {
46
- implementation("com.tryhelium.paywall:core:0.1.15")
46
+ implementation("com.tryhelium.paywall:core:0.1.16")
47
47
  implementation("com.google.code.gson:gson:2.10.1")
48
48
  implementation("com.android.billingclient:billing:8.0.0")
49
49
  implementation("org.jetbrains.kotlin:kotlin-reflect")
@@ -102,6 +102,7 @@ class HeliumPaywallSdkModule : Module() {
102
102
  val apiKey = config["apiKey"] as? String ?: ""
103
103
  val customUserId = config["customUserId"] as? String
104
104
  val customAPIEndpoint = config["customAPIEndpoint"] as? String
105
+ val revenueCatAppUserId = config["revenueCatAppUserId"] as? String
105
106
  val useDefaultDelegate = config["useDefaultDelegate"] as? Boolean ?: false
106
107
 
107
108
  @Suppress("UNCHECKED_CAST")
@@ -163,6 +164,7 @@ class HeliumPaywallSdkModule : Module() {
163
164
  customUserId = customUserId,
164
165
  customApiEndpoint = customAPIEndpoint,
165
166
  customUserTraits = customUserTraits,
167
+ revenueCatAppUserId = revenueCatAppUserId,
166
168
  fallbackConfig = fallbackConfig,
167
169
  environment = environment
168
170
  )
@@ -445,7 +447,7 @@ class HeliumPaywallSdkModule : Module() {
445
447
  is String -> HeliumUserTraitsArgument.StringParam(value)
446
448
  is Int -> HeliumUserTraitsArgument.IntParam(value)
447
449
  is Long -> HeliumUserTraitsArgument.LongParam(value)
448
- is Double -> HeliumUserTraitsArgument.DoubleParam(value.toString())
450
+ is Double -> HeliumUserTraitsArgument.DoubleParam(value)
449
451
  is Boolean -> HeliumUserTraitsArgument.BooleanParam(value)
450
452
  is List<*> -> {
451
453
  val items = value.mapNotNull { convertToHeliumUserTraitsArgument(it) }
@@ -20,6 +20,8 @@ export declare class RevenueCatHeliumHandler {
20
20
  makePurchaseAndroid(productId: string, basePlanId?: string, offerId?: string): Promise<HeliumPurchaseResult>;
21
21
  private findAndroidSubscriptionOption;
22
22
  private isProductActive;
23
+ private evaluatePurchaseResult;
24
+ private handlePurchasesError;
23
25
  restorePurchases(): Promise<boolean>;
24
26
  }
25
27
  //# sourceMappingURL=revenuecat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AASA,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAIrF,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,oBAAoB,CAOvB;AAED,qBAAa,uBAAuB;IAClC,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,yBAAyB,CAA6C;gBAElE,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;YAiBtE,wBAAwB;YA4BxB,wBAAwB;IAQhC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA8DjE,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAiFpG,6BAA6B;IAwC3C,OAAO,CAAC,eAAe;IAMjB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CAQ3C"}
1
+ {"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AASA,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAIrF,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,oBAAoB,CAOvB;AAED,qBAAa,uBAAuB;IAClC,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,yBAAyB,CAA6C;gBAElE,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;YAiBtE,wBAAwB;YA4BxB,wBAAwB;IAQhC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAwCjE,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAsDpG,6BAA6B;IAwC3C,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,oBAAoB;IAetB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CAQ3C"}
@@ -1,4 +1,4 @@
1
- import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases';
1
+ import Purchases, { PRODUCT_CATEGORY, PURCHASES_ERROR_CODE } from 'react-native-purchases';
2
2
  import { Platform } from 'react-native';
3
3
  import { setRevenueCatAppUserId } from "../index";
4
4
  // Rename the factory function
@@ -102,30 +102,10 @@ export class RevenueCatHeliumHandler {
102
102
  else {
103
103
  return { status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}` };
104
104
  }
105
- const isActive = this.isProductActive(customerInfo, productId);
106
- if (isActive) {
107
- return { status: 'purchased' };
108
- }
109
- else {
110
- // This case might occur if the purchase succeeded but the entitlement wasn't immediately active
111
- // or if a different product became active.
112
- // Consider if polling/listening might be needed here too, similar to pending.
113
- // For now, returning failed as the specific product isn't confirmed active.
114
- return {
115
- status: 'failed',
116
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
117
- };
118
- }
105
+ return this.evaluatePurchaseResult(customerInfo, productId);
119
106
  }
120
107
  catch (error) {
121
- const purchasesError = error;
122
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
123
- return { status: 'pending' };
124
- }
125
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
126
- return { status: 'cancelled' };
127
- }
128
- return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };
108
+ return this.handlePurchasesError(error);
129
109
  }
130
110
  }
131
111
  // Android-specific purchase logic (completely separated from iOS)
@@ -138,63 +118,41 @@ export class RevenueCatHeliumHandler {
138
118
  if (subscriptionOption) {
139
119
  try {
140
120
  const customerInfo = (await Purchases.purchaseSubscriptionOption(subscriptionOption)).customerInfo;
141
- const isActive = this.isProductActive(customerInfo, productId);
142
- if (isActive) {
143
- return { status: 'purchased' };
144
- }
145
- else {
146
- return {
147
- status: 'failed',
148
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
149
- };
150
- }
121
+ return this.evaluatePurchaseResult(customerInfo, productId);
151
122
  }
152
123
  catch (error) {
153
- const purchasesError = error;
154
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
155
- return { status: 'pending' };
156
- }
157
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
158
- return { status: 'cancelled' };
159
- }
160
- return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };
124
+ return this.handlePurchasesError(error);
161
125
  }
162
126
  }
163
127
  }
164
128
  // Handle one-time purchase or subscription that didn't have matching base plan / offer
165
129
  let rcProduct;
166
130
  try {
167
- const products = await Purchases.getProducts([productId]);
168
- if (products.length === 0) {
169
- return { status: 'failed', error: `Android product not found: ${productId}` };
131
+ // Try non-subscription (NON_SUBSCRIPTION) product first; most likely not a sub at this point
132
+ let products = await Purchases.getProducts([productId], PRODUCT_CATEGORY.NON_SUBSCRIPTION);
133
+ if (products.length > 0) {
134
+ rcProduct = products[0];
135
+ }
136
+ else {
137
+ // Then try subscription product (let RC pick option since we couldn't find a match)
138
+ products = await Purchases.getProducts([productId]);
139
+ if (products.length > 0) {
140
+ rcProduct = products[0];
141
+ }
142
+ }
143
+ if (!rcProduct) {
144
+ return { status: 'failed', error: `[RC] Android product not found: ${productId}` };
170
145
  }
171
- rcProduct = products[0];
172
146
  }
173
147
  catch {
174
- return { status: 'failed', error: `Failed to retrieve Android product: ${productId}` };
148
+ return { status: 'failed', error: `[RC] Failed to retrieve Android product: ${productId}` };
175
149
  }
176
150
  try {
177
151
  const customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;
178
- const isActive = this.isProductActive(customerInfo, productId);
179
- if (isActive) {
180
- return { status: 'purchased' };
181
- }
182
- else {
183
- return {
184
- status: 'failed',
185
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
186
- };
187
- }
152
+ return this.evaluatePurchaseResult(customerInfo, productId);
188
153
  }
189
154
  catch (error) {
190
- const purchasesError = error;
191
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
192
- return { status: 'pending' };
193
- }
194
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
195
- return { status: 'cancelled' };
196
- }
197
- return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };
155
+ return this.handlePurchasesError(error);
198
156
  }
199
157
  }
200
158
  // Android helper: Find subscription option
@@ -232,6 +190,25 @@ export class RevenueCatHeliumHandler {
232
190
  || customerInfo.activeSubscriptions.includes(productId)
233
191
  || customerInfo.allPurchasedProductIdentifiers.includes(productId);
234
192
  }
193
+ // Helper function to process purchase result
194
+ evaluatePurchaseResult(customerInfo, productId) {
195
+ if (!this.isProductActive(customerInfo, productId)) {
196
+ console.log('Purchase succeeded but product not immediately active in customerInfo:', productId);
197
+ }
198
+ return { status: 'purchased' };
199
+ }
200
+ // Helper function to handle RevenueCat purchase errors
201
+ handlePurchasesError(error) {
202
+ const purchasesError = error;
203
+ if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
204
+ return { status: 'pending' };
205
+ }
206
+ if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
207
+ return { status: 'cancelled' };
208
+ }
209
+ const errorDesc = purchasesError?.message || 'purchase failed.';
210
+ return { status: 'failed', error: `[RC] ${errorDesc} code: ${purchasesError?.code}` };
211
+ }
235
212
  async restorePurchases() {
236
213
  try {
237
214
  const customerInfo = await Purchases.restorePurchases();
@@ -1 +1 @@
1
- {"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AAOA,OAAO,SAAS,EAAE,EAAC,oBAAoB,EAAwB,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAC,sBAAsB,EAAC,MAAM,UAAU,CAAC;AAEhD,8BAA8B;AAC9B,MAAM,UAAU,8BAA8B,CAAC,MAI9C;IACC,MAAM,SAAS,GAAG,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO;QACL,eAAe,EAAE,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1D,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;QAClE,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,uBAAuB;IAC1B,yBAAyB,GAAqC,EAAE,CAAC;IACjE,oBAAoB,GAAY,KAAK,CAAC;IACtC,qBAAqB,GAAyB,IAAI,CAAC;IAEnD,yBAAyB,GAA0C,EAAE,CAAC;IAE9E,YAAY,MAAwE;QAClF,mDAAmD;QACnD,IAAI,eAAmC,CAAC;QACxC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;YAC/C,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC;QACrC,CAAC;aAAM,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,IAAI,MAAM,EAAE,aAAa,EAAE,CAAC;YAC9D,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,eAAe,GAAG,MAAM,EAAE,MAAM,CAAC;QACnC,CAAC;QAED,IAAI,eAAe,EAAE,CAAC;YACpB,SAAS,CAAC,SAAS,CAAC,EAAC,MAAM,EAAE,eAAe,EAAC,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,IAAI,CAAC,wBAAwB,EAAE,CAAC;IACvC,CAAC;IAEO,KAAK,CAAC,wBAAwB;QACpC,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,qBAAqB,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,qBAAqB,GAAG,CAAC,KAAK,IAAI,EAAE;YACvC,IAAI,CAAC;gBACH,4CAA4C;gBAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;gBAEvD,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC;gBACjD,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC;gBACnC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;oBACnD,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAqB,EAAE,EAAE;wBAC3D,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;4BAC5B,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC;wBAC/D,CAAC;oBACH,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YACpC,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACpC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,IAAI,CAAC,qBAAqB,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,wBAAwB;QACpC,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC9D,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACtC,MAAM,IAAI,CAAC,qBAAqB,CAAC;QACnC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,4CAA4C;QAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;QAEvD,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACtC,MAAM,GAAG,GAAiC,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;QACpF,IAAI,SAA4C,CAAC;QACjD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,0BAA0B;YAC1B,SAAS,GAAG,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;YACtD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,sBAAsB;gBACtB,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;oBAC5D,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAChE,CAAC;gBAAC,MAAM,CAAC;oBACP,mCAAmC;gBACrC,CAAC;gBACD,IAAI,SAAS,EAAE,CAAC;oBACd,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,IAAI,YAA0B,CAAC;YAC/B,IAAI,GAAG,EAAE,CAAC;gBACR,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;YACrE,CAAC;iBAAM,IAAI,SAAS,EAAE,CAAC;gBACrB,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YAChF,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,gDAAgD,SAAS,EAAE,EAAC,CAAC;YAChG,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,gGAAgG;gBAChG,2CAA2C;gBAC3C,8EAA8E;gBAC9E,4EAA4E;gBAC5E,OAAO;oBACL,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,sFAAsF;iBAC9F,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,cAAc,GAAG,KAAuB,CAAC;YAE/C,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,qBAAqB,EAAE,CAAC;gBACxE,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC;YAC7B,CAAC;YAED,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,wBAAwB,EAAE,CAAC;gBAC3E,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;YAC/B,CAAC;YAED,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,IAAI,6BAA6B,EAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,mBAAmB,CAAC,SAAiB,EAAE,UAAmB,EAAE,OAAgB;QAChF,4CAA4C;QAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;QAEvD,8CAA8C;QAC9C,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,6BAA6B,CACjE,SAAS,EACT,UAAU,EACV,OAAO,CACR,CAAC;YAEF,IAAI,kBAAkB,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,MAAM,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,0BAA0B,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC;oBAEnG,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;oBAC/D,IAAI,QAAQ,EAAE,CAAC;wBACb,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;oBAC/B,CAAC;yBAAM,CAAC;wBACN,OAAO;4BACL,MAAM,EAAE,QAAQ;4BAChB,KAAK,EAAE,sFAAsF;yBAC9F,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,cAAc,GAAG,KAAuB,CAAC;oBAE/C,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,qBAAqB,EAAE,CAAC;wBACxE,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC;oBAC7B,CAAC;oBAED,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,wBAAwB,EAAE,CAAC;wBAC3E,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;oBAC/B,CAAC;oBAED,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,IAAI,6BAA6B,EAAC,CAAC;gBAC7F,CAAC;YACH,CAAC;QACH,CAAC;QAED,uFAAuF;QACvF,IAAI,SAAgC,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,8BAA8B,SAAS,EAAE,EAAC,CAAC;YAC9E,CAAC;YACD,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,uCAAuC,SAAS,EAAE,EAAC,CAAC;QACvF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YAEpF,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,OAAO;oBACL,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,sFAAsF;iBAC9F,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,cAAc,GAAG,KAAuB,CAAC;YAE/C,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,qBAAqB,EAAE,CAAC;gBACxE,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC;YAC7B,CAAC;YAED,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,wBAAwB,EAAE,CAAC;gBAC3E,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;YAC/B,CAAC;YAED,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,IAAI,6BAA6B,EAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,2CAA2C;IACnC,KAAK,CAAC,6BAA6B,CACzC,SAAiB,EACjB,UAAmB,EACnB,OAAgB;QAEhB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,2EAA2E;YAC3E,sEAAsE;YACtE,MAAM,sBAAsB,GAAG,QAAQ,CAAC,OAAO,CAC7C,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,mBAAmB,IAAI,EAAE,CAC7C,CAAC;YAEF,IAAI,sBAAsB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxC,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,kBAAkD,CAAC;YAEvD,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC1B,gDAAgD;gBAChD,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;gBAC5C,kBAAkB,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;YAC/E,CAAC;YACD,IAAI,CAAC,kBAAkB,IAAI,UAAU,EAAE,CAAC;gBACtC,yDAAyD;gBACzD,kBAAkB,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;YACjF,CAAC;YAED,OAAO,kBAAkB,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,kEAAkE;IAC1D,eAAe,CAAC,YAA0B,EAAE,SAAiB;QACnE,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,WAAqC,EAAE,EAAE,CAAC,WAAW,CAAC,iBAAiB,KAAK,SAAS,CAAC;eAC9I,YAAY,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,CAAC;eACpD,YAAY,CAAC,8BAA8B,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,gBAAgB,EAAE,CAAC;YACxD,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF","sourcesContent":["import type {\n CustomerInfo,\n PurchasesEntitlementInfo,\n PurchasesError,\n PurchasesPackage,\n SubscriptionOption\n} from 'react-native-purchases';\nimport Purchases, {PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';\nimport {Platform} from 'react-native';\nimport {HeliumPurchaseConfig, HeliumPurchaseResult} from \"../HeliumPaywallSdk.types\";\nimport {setRevenueCatAppUserId} from \"../index\";\n\n// Rename the factory function\nexport function createRevenueCatPurchaseConfig(config?: {\n apiKey?: string;\n apiKeyIOS?: string;\n apiKeyAndroid?: string;\n}): HeliumPurchaseConfig {\n const rcHandler = new RevenueCatHeliumHandler(config);\n return {\n makePurchaseIOS: rcHandler.makePurchaseIOS.bind(rcHandler),\n makePurchaseAndroid: rcHandler.makePurchaseAndroid.bind(rcHandler),\n restorePurchases: rcHandler.restorePurchases.bind(rcHandler),\n };\n}\n\nexport class RevenueCatHeliumHandler {\n private productIdToPackageMapping: Record<string, PurchasesPackage> = {};\n private isMappingInitialized: boolean = false;\n private initializationPromise: Promise<void> | null = null;\n\n private rcProductToPackageMapping: Record<string, PurchasesStoreProduct> = {};\n\n constructor(config?: { apiKey?: string; apiKeyIOS?: string; apiKeyAndroid?: string }) {\n // Determine which API key to use based on platform\n let effectiveApiKey: string | undefined;\n if (Platform.OS === 'ios' && config?.apiKeyIOS) {\n effectiveApiKey = config.apiKeyIOS;\n } else if (Platform.OS === 'android' && config?.apiKeyAndroid) {\n effectiveApiKey = config.apiKeyAndroid;\n } else {\n effectiveApiKey = config?.apiKey;\n }\n\n if (effectiveApiKey) {\n Purchases.configure({apiKey: effectiveApiKey});\n }\n void this.initializePackageMapping();\n }\n\n private async initializePackageMapping(): Promise<void> {\n if (this.initializationPromise) {\n return this.initializationPromise;\n }\n this.initializationPromise = (async () => {\n try {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n const offerings = await Purchases.getOfferings();\n const allOfferings = offerings.all;\n for (const offering of Object.values(allOfferings)) {\n offering.availablePackages.forEach((pkg: PurchasesPackage) => {\n if (pkg.product?.identifier) {\n this.productIdToPackageMapping[pkg.product.identifier] = pkg;\n }\n });\n }\n this.isMappingInitialized = true;\n } catch (error) {\n this.isMappingInitialized = false;\n } finally {\n this.initializationPromise = null;\n }\n })();\n return this.initializationPromise;\n }\n\n private async ensureMappingInitialized(): Promise<void> {\n if (!this.isMappingInitialized && !this.initializationPromise) {\n await this.initializePackageMapping();\n } else if (this.initializationPromise) {\n await this.initializationPromise;\n }\n }\n\n async makePurchaseIOS(productId: string): Promise<HeliumPurchaseResult> {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n await this.ensureMappingInitialized();\n const pkg: PurchasesPackage | undefined = this.productIdToPackageMapping[productId];\n let rcProduct: PurchasesStoreProduct | undefined;\n if (!pkg) {\n // Use cached if available\n rcProduct = this.rcProductToPackageMapping[productId];\n if (!rcProduct) {\n // Try to retrieve now\n try {\n const rcProducts = await Purchases.getProducts([productId]);\n rcProduct = rcProducts.length > 0 ? rcProducts[0] : undefined;\n } catch {\n // 'failed' status will be returned\n }\n if (rcProduct) {\n this.rcProductToPackageMapping[productId] = rcProduct;\n }\n }\n }\n\n try {\n let customerInfo: CustomerInfo;\n if (pkg) {\n customerInfo = (await Purchases.purchasePackage(pkg)).customerInfo;\n } else if (rcProduct) {\n customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;\n } else {\n return {status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}`};\n }\n const isActive = this.isProductActive(customerInfo, productId);\n if (isActive) {\n return {status: 'purchased'};\n } else {\n // This case might occur if the purchase succeeded but the entitlement wasn't immediately active\n // or if a different product became active.\n // Consider if polling/listening might be needed here too, similar to pending.\n // For now, returning failed as the specific product isn't confirmed active.\n return {\n status: 'failed',\n error: 'Purchase possibly complete but entitlement/subscription not active for this product.'\n };\n }\n } catch (error) {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n return {status: 'pending'};\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return {status: 'cancelled'};\n }\n\n return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};\n }\n }\n\n // Android-specific purchase logic (completely separated from iOS)\n async makePurchaseAndroid(productId: string, basePlanId?: string, offerId?: string): Promise<HeliumPurchaseResult> {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n // Handle subscription with base plan or offer\n if (basePlanId || offerId) {\n const subscriptionOption = await this.findAndroidSubscriptionOption(\n productId,\n basePlanId,\n offerId\n );\n\n if (subscriptionOption) {\n try {\n const customerInfo = (await Purchases.purchaseSubscriptionOption(subscriptionOption)).customerInfo;\n\n const isActive = this.isProductActive(customerInfo, productId);\n if (isActive) {\n return {status: 'purchased'};\n } else {\n return {\n status: 'failed',\n error: 'Purchase possibly complete but entitlement/subscription not active for this product.'\n };\n }\n } catch (error) {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n return {status: 'pending'};\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return {status: 'cancelled'};\n }\n\n return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};\n }\n }\n }\n\n // Handle one-time purchase or subscription that didn't have matching base plan / offer\n let rcProduct: PurchasesStoreProduct;\n try {\n const products = await Purchases.getProducts([productId]);\n if (products.length === 0) {\n return {status: 'failed', error: `Android product not found: ${productId}`};\n }\n rcProduct = products[0];\n } catch {\n return {status: 'failed', error: `Failed to retrieve Android product: ${productId}`};\n }\n\n try {\n const customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;\n\n const isActive = this.isProductActive(customerInfo, productId);\n if (isActive) {\n return {status: 'purchased'};\n } else {\n return {\n status: 'failed',\n error: 'Purchase possibly complete but entitlement/subscription not active for this product.'\n };\n }\n } catch (error) {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n return {status: 'pending'};\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return {status: 'cancelled'};\n }\n\n return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};\n }\n }\n\n // Android helper: Find subscription option\n private async findAndroidSubscriptionOption(\n productId: string,\n basePlanId?: string,\n offerId?: string\n ): Promise<SubscriptionOption | undefined> {\n try {\n const products = await Purchases.getProducts([productId]);\n if (products.length === 0) {\n return undefined;\n }\n\n // RC will return multiple products if multiple base plans per subscription\n // Collect all subscription options from all products into a flat list\n const allSubscriptionOptions = products.flatMap(\n product => product.subscriptionOptions ?? []\n );\n\n if (allSubscriptionOptions.length === 0) {\n return undefined;\n }\n\n let subscriptionOption: SubscriptionOption | undefined;\n\n if (offerId && basePlanId) {\n // Look for specific offer: \"basePlanId:offerId\"\n const targetId = `${basePlanId}:${offerId}`;\n subscriptionOption = allSubscriptionOptions.find(opt => opt.id === targetId);\n }\n if (!subscriptionOption && basePlanId) {\n // Otherwise the RC option id will simply be base plan id\n subscriptionOption = allSubscriptionOptions.find(opt => opt.id === basePlanId);\n }\n\n return subscriptionOption;\n } catch (error) {\n return undefined;\n }\n }\n\n // Helper function to check if a product is active in CustomerInfo\n private isProductActive(customerInfo: CustomerInfo, productId: string): boolean {\n return Object.values(customerInfo.entitlements.active).some((entitlement: PurchasesEntitlementInfo) => entitlement.productIdentifier === productId)\n || customerInfo.activeSubscriptions.includes(productId)\n || customerInfo.allPurchasedProductIdentifiers.includes(productId);\n }\n\n async restorePurchases(): Promise<boolean> {\n try {\n const customerInfo = await Purchases.restorePurchases();\n return Object.keys(customerInfo.entitlements.active).length > 0;\n } catch (error) {\n return false;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AAOA,OAAO,SAAS,EAAE,EAAC,gBAAgB,EAAE,oBAAoB,EAAwB,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAC,sBAAsB,EAAC,MAAM,UAAU,CAAC;AAEhD,8BAA8B;AAC9B,MAAM,UAAU,8BAA8B,CAAC,MAI9C;IACC,MAAM,SAAS,GAAG,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO;QACL,eAAe,EAAE,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1D,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;QAClE,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,uBAAuB;IAC1B,yBAAyB,GAAqC,EAAE,CAAC;IACjE,oBAAoB,GAAY,KAAK,CAAC;IACtC,qBAAqB,GAAyB,IAAI,CAAC;IAEnD,yBAAyB,GAA0C,EAAE,CAAC;IAE9E,YAAY,MAAwE;QAClF,mDAAmD;QACnD,IAAI,eAAmC,CAAC;QACxC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;YAC/C,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC;QACrC,CAAC;aAAM,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,IAAI,MAAM,EAAE,aAAa,EAAE,CAAC;YAC9D,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,eAAe,GAAG,MAAM,EAAE,MAAM,CAAC;QACnC,CAAC;QAED,IAAI,eAAe,EAAE,CAAC;YACpB,SAAS,CAAC,SAAS,CAAC,EAAC,MAAM,EAAE,eAAe,EAAC,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,IAAI,CAAC,wBAAwB,EAAE,CAAC;IACvC,CAAC;IAEO,KAAK,CAAC,wBAAwB;QACpC,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,qBAAqB,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,qBAAqB,GAAG,CAAC,KAAK,IAAI,EAAE;YACvC,IAAI,CAAC;gBACH,4CAA4C;gBAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;gBAEvD,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC;gBACjD,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC;gBACnC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;oBACnD,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAqB,EAAE,EAAE;wBAC3D,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;4BAC5B,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC;wBAC/D,CAAC;oBACH,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YACpC,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACpC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,IAAI,CAAC,qBAAqB,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,wBAAwB;QACpC,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC9D,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACtC,MAAM,IAAI,CAAC,qBAAqB,CAAC;QACnC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,4CAA4C;QAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;QAEvD,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACtC,MAAM,GAAG,GAAiC,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;QACpF,IAAI,SAA4C,CAAC;QACjD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,0BAA0B;YAC1B,SAAS,GAAG,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;YACtD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,sBAAsB;gBACtB,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;oBAC5D,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAChE,CAAC;gBAAC,MAAM,CAAC;oBACP,mCAAmC;gBACrC,CAAC;gBACD,IAAI,SAAS,EAAE,CAAC;oBACd,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,IAAI,YAA0B,CAAC;YAC/B,IAAI,GAAG,EAAE,CAAC;gBACR,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;YACrE,CAAC;iBAAM,IAAI,SAAS,EAAE,CAAC;gBACrB,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YAChF,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,gDAAgD,SAAS,EAAE,EAAC,CAAC;YAChG,CAAC;YACD,OAAO,IAAI,CAAC,sBAAsB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,mBAAmB,CAAC,SAAiB,EAAE,UAAmB,EAAE,OAAgB;QAChF,4CAA4C;QAC5C,sBAAsB,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;QAEvD,8CAA8C;QAC9C,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,6BAA6B,CACjE,SAAS,EACT,UAAU,EACV,OAAO,CACR,CAAC;YAEF,IAAI,kBAAkB,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,MAAM,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,0BAA0B,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC;oBAEnG,OAAO,IAAI,CAAC,sBAAsB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;gBAC9D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;QAED,uFAAuF;QACvF,IAAI,SAA4C,CAAC;QACjD,IAAI,CAAC;YACH,6FAA6F;YAC7F,IAAI,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;YAC3F,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,oFAAoF;gBACpF,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;gBACpD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxB,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,mCAAmC,SAAS,EAAE,EAAC,CAAC;YACnF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,4CAA4C,SAAS,EAAE,EAAC,CAAC;QAC5F,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,CAAC,MAAM,SAAS,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;YAEpF,OAAO,IAAI,CAAC,sBAAsB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,2CAA2C;IACnC,KAAK,CAAC,6BAA6B,CACzC,SAAiB,EACjB,UAAmB,EACnB,OAAgB;QAEhB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,2EAA2E;YAC3E,sEAAsE;YACtE,MAAM,sBAAsB,GAAG,QAAQ,CAAC,OAAO,CAC7C,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,mBAAmB,IAAI,EAAE,CAC7C,CAAC;YAEF,IAAI,sBAAsB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxC,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,kBAAkD,CAAC;YAEvD,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC1B,gDAAgD;gBAChD,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;gBAC5C,kBAAkB,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;YAC/E,CAAC;YACD,IAAI,CAAC,kBAAkB,IAAI,UAAU,EAAE,CAAC;gBACtC,yDAAyD;gBACzD,kBAAkB,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;YACjF,CAAC;YAED,OAAO,kBAAkB,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,kEAAkE;IAC1D,eAAe,CAAC,YAA0B,EAAE,SAAiB;QACnE,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,WAAqC,EAAE,EAAE,CAAC,WAAW,CAAC,iBAAiB,KAAK,SAAS,CAAC;eAC9I,YAAY,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,CAAC;eACpD,YAAY,CAAC,8BAA8B,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;IAED,6CAA6C;IACrC,sBAAsB,CAAC,YAA0B,EAAE,SAAiB;QAC1E,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,wEAAwE,EAAE,SAAS,CAAC,CAAC;QACnG,CAAC;QAED,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;IAC/B,CAAC;IAED,uDAAuD;IAC/C,oBAAoB,CAAC,KAAc;QACzC,MAAM,cAAc,GAAG,KAAuB,CAAC;QAE/C,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,qBAAqB,EAAE,CAAC;YACxE,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC;QAC7B,CAAC;QAED,IAAI,cAAc,EAAE,IAAI,KAAK,oBAAoB,CAAC,wBAAwB,EAAE,CAAC;YAC3E,OAAO,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC;QAC/B,CAAC;QAED,MAAM,SAAS,GAAG,cAAc,EAAE,OAAO,IAAI,kBAAkB,CAAC;QAChE,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,SAAS,UAAU,cAAc,EAAE,IAAI,EAAE,EAAC,CAAC;IACtF,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,gBAAgB,EAAE,CAAC;YACxD,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF","sourcesContent":["import type {\n CustomerInfo,\n PurchasesEntitlementInfo,\n PurchasesError,\n PurchasesPackage,\n SubscriptionOption\n} from 'react-native-purchases';\nimport Purchases, {PRODUCT_CATEGORY, PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';\nimport {Platform} from 'react-native';\nimport {HeliumPurchaseConfig, HeliumPurchaseResult} from \"../HeliumPaywallSdk.types\";\nimport {setRevenueCatAppUserId} from \"../index\";\n\n// Rename the factory function\nexport function createRevenueCatPurchaseConfig(config?: {\n apiKey?: string;\n apiKeyIOS?: string;\n apiKeyAndroid?: string;\n}): HeliumPurchaseConfig {\n const rcHandler = new RevenueCatHeliumHandler(config);\n return {\n makePurchaseIOS: rcHandler.makePurchaseIOS.bind(rcHandler),\n makePurchaseAndroid: rcHandler.makePurchaseAndroid.bind(rcHandler),\n restorePurchases: rcHandler.restorePurchases.bind(rcHandler),\n };\n}\n\nexport class RevenueCatHeliumHandler {\n private productIdToPackageMapping: Record<string, PurchasesPackage> = {};\n private isMappingInitialized: boolean = false;\n private initializationPromise: Promise<void> | null = null;\n\n private rcProductToPackageMapping: Record<string, PurchasesStoreProduct> = {};\n\n constructor(config?: { apiKey?: string; apiKeyIOS?: string; apiKeyAndroid?: string }) {\n // Determine which API key to use based on platform\n let effectiveApiKey: string | undefined;\n if (Platform.OS === 'ios' && config?.apiKeyIOS) {\n effectiveApiKey = config.apiKeyIOS;\n } else if (Platform.OS === 'android' && config?.apiKeyAndroid) {\n effectiveApiKey = config.apiKeyAndroid;\n } else {\n effectiveApiKey = config?.apiKey;\n }\n\n if (effectiveApiKey) {\n Purchases.configure({apiKey: effectiveApiKey});\n }\n void this.initializePackageMapping();\n }\n\n private async initializePackageMapping(): Promise<void> {\n if (this.initializationPromise) {\n return this.initializationPromise;\n }\n this.initializationPromise = (async () => {\n try {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n const offerings = await Purchases.getOfferings();\n const allOfferings = offerings.all;\n for (const offering of Object.values(allOfferings)) {\n offering.availablePackages.forEach((pkg: PurchasesPackage) => {\n if (pkg.product?.identifier) {\n this.productIdToPackageMapping[pkg.product.identifier] = pkg;\n }\n });\n }\n this.isMappingInitialized = true;\n } catch (error) {\n this.isMappingInitialized = false;\n } finally {\n this.initializationPromise = null;\n }\n })();\n return this.initializationPromise;\n }\n\n private async ensureMappingInitialized(): Promise<void> {\n if (!this.isMappingInitialized && !this.initializationPromise) {\n await this.initializePackageMapping();\n } else if (this.initializationPromise) {\n await this.initializationPromise;\n }\n }\n\n async makePurchaseIOS(productId: string): Promise<HeliumPurchaseResult> {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n await this.ensureMappingInitialized();\n const pkg: PurchasesPackage | undefined = this.productIdToPackageMapping[productId];\n let rcProduct: PurchasesStoreProduct | undefined;\n if (!pkg) {\n // Use cached if available\n rcProduct = this.rcProductToPackageMapping[productId];\n if (!rcProduct) {\n // Try to retrieve now\n try {\n const rcProducts = await Purchases.getProducts([productId]);\n rcProduct = rcProducts.length > 0 ? rcProducts[0] : undefined;\n } catch {\n // 'failed' status will be returned\n }\n if (rcProduct) {\n this.rcProductToPackageMapping[productId] = rcProduct;\n }\n }\n }\n\n try {\n let customerInfo: CustomerInfo;\n if (pkg) {\n customerInfo = (await Purchases.purchasePackage(pkg)).customerInfo;\n } else if (rcProduct) {\n customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;\n } else {\n return {status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}`};\n }\n return this.evaluatePurchaseResult(customerInfo, productId);\n } catch (error) {\n return this.handlePurchasesError(error);\n }\n }\n\n // Android-specific purchase logic (completely separated from iOS)\n async makePurchaseAndroid(productId: string, basePlanId?: string, offerId?: string): Promise<HeliumPurchaseResult> {\n // Keep this value as up-to-date as possible\n setRevenueCatAppUserId(await Purchases.getAppUserID());\n\n // Handle subscription with base plan or offer\n if (basePlanId || offerId) {\n const subscriptionOption = await this.findAndroidSubscriptionOption(\n productId,\n basePlanId,\n offerId\n );\n\n if (subscriptionOption) {\n try {\n const customerInfo = (await Purchases.purchaseSubscriptionOption(subscriptionOption)).customerInfo;\n\n return this.evaluatePurchaseResult(customerInfo, productId);\n } catch (error) {\n return this.handlePurchasesError(error);\n }\n }\n }\n\n // Handle one-time purchase or subscription that didn't have matching base plan / offer\n let rcProduct: PurchasesStoreProduct | undefined;\n try {\n // Try non-subscription (NON_SUBSCRIPTION) product first; most likely not a sub at this point\n let products = await Purchases.getProducts([productId], PRODUCT_CATEGORY.NON_SUBSCRIPTION);\n if (products.length > 0) {\n rcProduct = products[0];\n } else {\n // Then try subscription product (let RC pick option since we couldn't find a match)\n products = await Purchases.getProducts([productId]);\n if (products.length > 0) {\n rcProduct = products[0];\n }\n }\n if (!rcProduct) {\n return {status: 'failed', error: `[RC] Android product not found: ${productId}`};\n }\n } catch {\n return {status: 'failed', error: `[RC] Failed to retrieve Android product: ${productId}`};\n }\n\n try {\n const customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;\n\n return this.evaluatePurchaseResult(customerInfo, productId);\n } catch (error) {\n return this.handlePurchasesError(error);\n }\n }\n\n // Android helper: Find subscription option\n private async findAndroidSubscriptionOption(\n productId: string,\n basePlanId?: string,\n offerId?: string\n ): Promise<SubscriptionOption | undefined> {\n try {\n const products = await Purchases.getProducts([productId]);\n if (products.length === 0) {\n return undefined;\n }\n\n // RC will return multiple products if multiple base plans per subscription\n // Collect all subscription options from all products into a flat list\n const allSubscriptionOptions = products.flatMap(\n product => product.subscriptionOptions ?? []\n );\n\n if (allSubscriptionOptions.length === 0) {\n return undefined;\n }\n\n let subscriptionOption: SubscriptionOption | undefined;\n\n if (offerId && basePlanId) {\n // Look for specific offer: \"basePlanId:offerId\"\n const targetId = `${basePlanId}:${offerId}`;\n subscriptionOption = allSubscriptionOptions.find(opt => opt.id === targetId);\n }\n if (!subscriptionOption && basePlanId) {\n // Otherwise the RC option id will simply be base plan id\n subscriptionOption = allSubscriptionOptions.find(opt => opt.id === basePlanId);\n }\n\n return subscriptionOption;\n } catch (error) {\n return undefined;\n }\n }\n\n // Helper function to check if a product is active in CustomerInfo\n private isProductActive(customerInfo: CustomerInfo, productId: string): boolean {\n return Object.values(customerInfo.entitlements.active).some((entitlement: PurchasesEntitlementInfo) => entitlement.productIdentifier === productId)\n || customerInfo.activeSubscriptions.includes(productId)\n || customerInfo.allPurchasedProductIdentifiers.includes(productId);\n }\n\n // Helper function to process purchase result\n private evaluatePurchaseResult(customerInfo: CustomerInfo, productId: string): HeliumPurchaseResult {\n if (!this.isProductActive(customerInfo, productId)) {\n console.log('Purchase succeeded but product not immediately active in customerInfo:', productId);\n }\n\n return {status: 'purchased'};\n }\n\n // Helper function to handle RevenueCat purchase errors\n private handlePurchasesError(error: unknown): HeliumPurchaseResult {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n return {status: 'pending'};\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return {status: 'cancelled'};\n }\n\n const errorDesc = purchasesError?.message || 'purchase failed.';\n return {status: 'failed', error: `[RC] ${errorDesc} code: ${purchasesError?.code}`};\n }\n\n async restorePurchases(): Promise<boolean> {\n try {\n const customerInfo = await Purchases.restorePurchases();\n return Object.keys(customerInfo.entitlements.active).length > 0;\n } catch (error) {\n return false;\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-helium",
3
- "version": "3.1.3",
3
+ "version": "3.1.4",
4
4
  "description": "Helium paywalls expo sdk",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -5,7 +5,7 @@ import type {
5
5
  PurchasesPackage,
6
6
  SubscriptionOption
7
7
  } from 'react-native-purchases';
8
- import Purchases, {PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';
8
+ import Purchases, {PRODUCT_CATEGORY, PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';
9
9
  import {Platform} from 'react-native';
10
10
  import {HeliumPurchaseConfig, HeliumPurchaseResult} from "../HeliumPaywallSdk.types";
11
11
  import {setRevenueCatAppUserId} from "../index";
@@ -117,31 +117,9 @@ export class RevenueCatHeliumHandler {
117
117
  } else {
118
118
  return {status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}`};
119
119
  }
120
- const isActive = this.isProductActive(customerInfo, productId);
121
- if (isActive) {
122
- return {status: 'purchased'};
123
- } else {
124
- // This case might occur if the purchase succeeded but the entitlement wasn't immediately active
125
- // or if a different product became active.
126
- // Consider if polling/listening might be needed here too, similar to pending.
127
- // For now, returning failed as the specific product isn't confirmed active.
128
- return {
129
- status: 'failed',
130
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
131
- };
132
- }
120
+ return this.evaluatePurchaseResult(customerInfo, productId);
133
121
  } catch (error) {
134
- const purchasesError = error as PurchasesError;
135
-
136
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
137
- return {status: 'pending'};
138
- }
139
-
140
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
141
- return {status: 'cancelled'};
142
- }
143
-
144
- return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};
122
+ return this.handlePurchasesError(error);
145
123
  }
146
124
  }
147
125
 
@@ -162,67 +140,40 @@ export class RevenueCatHeliumHandler {
162
140
  try {
163
141
  const customerInfo = (await Purchases.purchaseSubscriptionOption(subscriptionOption)).customerInfo;
164
142
 
165
- const isActive = this.isProductActive(customerInfo, productId);
166
- if (isActive) {
167
- return {status: 'purchased'};
168
- } else {
169
- return {
170
- status: 'failed',
171
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
172
- };
173
- }
143
+ return this.evaluatePurchaseResult(customerInfo, productId);
174
144
  } catch (error) {
175
- const purchasesError = error as PurchasesError;
176
-
177
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
178
- return {status: 'pending'};
179
- }
180
-
181
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
182
- return {status: 'cancelled'};
183
- }
184
-
185
- return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};
145
+ return this.handlePurchasesError(error);
186
146
  }
187
147
  }
188
148
  }
189
149
 
190
150
  // Handle one-time purchase or subscription that didn't have matching base plan / offer
191
- let rcProduct: PurchasesStoreProduct;
151
+ let rcProduct: PurchasesStoreProduct | undefined;
192
152
  try {
193
- const products = await Purchases.getProducts([productId]);
194
- if (products.length === 0) {
195
- return {status: 'failed', error: `Android product not found: ${productId}`};
153
+ // Try non-subscription (NON_SUBSCRIPTION) product first; most likely not a sub at this point
154
+ let products = await Purchases.getProducts([productId], PRODUCT_CATEGORY.NON_SUBSCRIPTION);
155
+ if (products.length > 0) {
156
+ rcProduct = products[0];
157
+ } else {
158
+ // Then try subscription product (let RC pick option since we couldn't find a match)
159
+ products = await Purchases.getProducts([productId]);
160
+ if (products.length > 0) {
161
+ rcProduct = products[0];
162
+ }
163
+ }
164
+ if (!rcProduct) {
165
+ return {status: 'failed', error: `[RC] Android product not found: ${productId}`};
196
166
  }
197
- rcProduct = products[0];
198
167
  } catch {
199
- return {status: 'failed', error: `Failed to retrieve Android product: ${productId}`};
168
+ return {status: 'failed', error: `[RC] Failed to retrieve Android product: ${productId}`};
200
169
  }
201
170
 
202
171
  try {
203
172
  const customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;
204
173
 
205
- const isActive = this.isProductActive(customerInfo, productId);
206
- if (isActive) {
207
- return {status: 'purchased'};
208
- } else {
209
- return {
210
- status: 'failed',
211
- error: 'Purchase possibly complete but entitlement/subscription not active for this product.'
212
- };
213
- }
174
+ return this.evaluatePurchaseResult(customerInfo, productId);
214
175
  } catch (error) {
215
- const purchasesError = error as PurchasesError;
216
-
217
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
218
- return {status: 'pending'};
219
- }
220
-
221
- if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
222
- return {status: 'cancelled'};
223
- }
224
-
225
- return {status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.'};
176
+ return this.handlePurchasesError(error);
226
177
  }
227
178
  }
228
179
 
@@ -273,6 +224,31 @@ export class RevenueCatHeliumHandler {
273
224
  || customerInfo.allPurchasedProductIdentifiers.includes(productId);
274
225
  }
275
226
 
227
+ // Helper function to process purchase result
228
+ private evaluatePurchaseResult(customerInfo: CustomerInfo, productId: string): HeliumPurchaseResult {
229
+ if (!this.isProductActive(customerInfo, productId)) {
230
+ console.log('Purchase succeeded but product not immediately active in customerInfo:', productId);
231
+ }
232
+
233
+ return {status: 'purchased'};
234
+ }
235
+
236
+ // Helper function to handle RevenueCat purchase errors
237
+ private handlePurchasesError(error: unknown): HeliumPurchaseResult {
238
+ const purchasesError = error as PurchasesError;
239
+
240
+ if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
241
+ return {status: 'pending'};
242
+ }
243
+
244
+ if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
245
+ return {status: 'cancelled'};
246
+ }
247
+
248
+ const errorDesc = purchasesError?.message || 'purchase failed.';
249
+ return {status: 'failed', error: `[RC] ${errorDesc} code: ${purchasesError?.code}`};
250
+ }
251
+
276
252
  async restorePurchases(): Promise<boolean> {
277
253
  try {
278
254
  const customerInfo = await Purchases.restorePurchases();