expo-helium 0.8.3 → 0.8.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.
@@ -6,6 +6,7 @@ export declare class RevenueCatHeliumHandler {
6
6
  private productIdToPackageMapping;
7
7
  private isMappingInitialized;
8
8
  private initializationPromise;
9
+ private rcProductToPackageMapping;
9
10
  constructor(apiKey?: string);
10
11
  private initializePackageMapping;
11
12
  private ensureMappingInitialized;
@@ -1 +1 @@
1
- {"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAGrF,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,oBAAoB,CAOvB;AAED,qBAAa,uBAAuB;IAChC,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,qBAAqB,CAA8B;gBAE/C,MAAM,CAAC,EAAE,MAAM;YAQb,wBAAwB;YAyBxB,wBAAwB;IAQhC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA0DpE,OAAO,CAAC,eAAe;IAMjB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CAS7C"}
1
+ {"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAGrF,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,oBAAoB,CAOvB;AAED,qBAAa,uBAAuB;IAChC,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,yBAAyB,CAA6C;gBAElE,MAAM,CAAC,EAAE,MAAM;YAQb,wBAAwB;YAyBxB,wBAAwB;IAQhC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA+EpE,OAAO,CAAC,eAAe;IAMjB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CAS7C"}
@@ -12,6 +12,7 @@ export class RevenueCatHeliumHandler {
12
12
  productIdToPackageMapping = {};
13
13
  isMappingInitialized = false;
14
14
  initializationPromise = null;
15
+ rcProductToPackageMapping = {};
15
16
  constructor(apiKey) {
16
17
  if (apiKey) {
17
18
  Purchases.configure({ apiKey });
@@ -27,15 +28,14 @@ export class RevenueCatHeliumHandler {
27
28
  this.initializationPromise = (async () => {
28
29
  try {
29
30
  const offerings = await Purchases.getOfferings();
30
- if (offerings.current?.availablePackages) {
31
- offerings.current.availablePackages.forEach((pkg) => {
31
+ const allOfferings = offerings.all;
32
+ for (const offering of Object.values(allOfferings)) {
33
+ offering.availablePackages.forEach((pkg) => {
32
34
  if (pkg.product?.identifier) {
33
35
  this.productIdToPackageMapping[pkg.product.identifier] = pkg;
34
36
  }
35
37
  });
36
38
  }
37
- else {
38
- }
39
39
  this.isMappingInitialized = true;
40
40
  }
41
41
  catch (error) {
@@ -58,11 +58,35 @@ export class RevenueCatHeliumHandler {
58
58
  async makePurchase(productId) {
59
59
  await this.ensureMappingInitialized();
60
60
  const pkg = this.productIdToPackageMapping[productId];
61
+ let rcProduct;
61
62
  if (!pkg) {
62
- return { status: 'failed', error: `RevenueCat Package not found for ID: ${productId}` };
63
+ // Use cached if available
64
+ rcProduct = this.rcProductToPackageMapping[productId];
65
+ if (!rcProduct) {
66
+ // Try to retrieve now
67
+ try {
68
+ const rcProducts = await Purchases.getProducts([productId]);
69
+ rcProduct = rcProducts.length > 0 ? rcProducts[0] : undefined;
70
+ }
71
+ catch {
72
+ // 'failed' status will be returned
73
+ }
74
+ if (rcProduct) {
75
+ this.rcProductToPackageMapping[productId] = rcProduct;
76
+ }
77
+ }
63
78
  }
64
79
  try {
65
- const { customerInfo } = await Purchases.purchasePackage(pkg);
80
+ let customerInfo;
81
+ if (pkg) {
82
+ customerInfo = (await Purchases.purchasePackage(pkg)).customerInfo;
83
+ }
84
+ else if (rcProduct) {
85
+ customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;
86
+ }
87
+ else {
88
+ return { status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}` };
89
+ }
66
90
  const isActive = this.isProductActive(customerInfo, productId);
67
91
  if (isActive) {
68
92
  return { status: 'purchased' };
@@ -1 +1 @@
1
- {"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"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","sourcesContent":["import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases';\nimport type { PurchasesError, PurchasesPackage, CustomerInfoUpdateListener, CustomerInfo, PurchasesEntitlementInfo } from 'react-native-purchases';\nimport {HeliumPurchaseConfig, HeliumPurchaseResult} from \"../HeliumPaywallSdk.types\";\n\n// Rename the factory function\nexport function createRevenueCatPurchaseConfig(config?: {\n apiKey?: string;\n}): HeliumPurchaseConfig {\n const rcHandler = new RevenueCatHeliumHandler(config?.apiKey);\n return {\n apiKey: config?.apiKey,\n makePurchase: rcHandler.makePurchase.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 constructor(apiKey?: string) {\n if (apiKey) {\n Purchases.configure({ apiKey });\n } else {\n }\n 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 const offerings = await Purchases.getOfferings();\n if (offerings.current?.availablePackages) {\n offerings.current.availablePackages.forEach((pkg: PurchasesPackage) => {\n if (pkg.product?.identifier) {\n this.productIdToPackageMapping[pkg.product.identifier] = pkg;\n }\n });\n } else {\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 makePurchase(productId: string): Promise<HeliumPurchaseResult> {\n await this.ensureMappingInitialized();\n\n const pkg: PurchasesPackage | undefined = this.productIdToPackageMapping[productId];\n if (!pkg) {\n return { status: 'failed', error: `RevenueCat Package not found for ID: ${productId}` };\n }\n\n try {\n const { customerInfo } = await Purchases.purchasePackage(pkg);\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 { status: 'failed', error: 'Purchase possibly complete but entitlement/subscription not active for this product.' };\n }\n } catch (error) {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n // Wait for a terminal state for up to 5 seconds\n return new Promise((resolve) => {\n // Define the listener function separately to remove it later\n const updateListener: CustomerInfoUpdateListener = (updatedCustomerInfo: CustomerInfo) => {\n const isActive = this.isProductActive(updatedCustomerInfo, productId);\n if (isActive) {\n clearTimeout(timeoutId);\n // Remove listener using the function reference\n Purchases.removeCustomerInfoUpdateListener(updateListener);\n resolve({ status: 'purchased' });\n }\n };\n\n const timeoutId = setTimeout(() => {\n // Remove listener using the function reference on timeout\n Purchases.removeCustomerInfoUpdateListener(updateListener);\n resolve({ status: 'pending' });\n }, 5000);\n\n // Add the listener\n Purchases.addCustomerInfoUpdateListener(updateListener);\n });\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return { status: 'cancelled' };\n }\n\n // Handle other errors\n return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };\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 const isActive = Object.keys(customerInfo.entitlements.active).length > 0;\n return isActive;\n } catch (error) {\n return false;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/revenuecat/revenuecat.ts"],"names":[],"mappings":"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","sourcesContent":["import Purchases, {PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';\nimport type { PurchasesError, PurchasesPackage, CustomerInfoUpdateListener, CustomerInfo, PurchasesEntitlementInfo } from 'react-native-purchases';\nimport {HeliumPurchaseConfig, HeliumPurchaseResult} from \"../HeliumPaywallSdk.types\";\n\n// Rename the factory function\nexport function createRevenueCatPurchaseConfig(config?: {\n apiKey?: string;\n}): HeliumPurchaseConfig {\n const rcHandler = new RevenueCatHeliumHandler(config?.apiKey);\n return {\n apiKey: config?.apiKey,\n makePurchase: rcHandler.makePurchase.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(apiKey?: string) {\n if (apiKey) {\n Purchases.configure({ apiKey });\n } else {\n }\n 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 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 makePurchase(productId: string): Promise<HeliumPurchaseResult> {\n await this.ensureMappingInitialized();\n\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 { status: 'failed', error: 'Purchase possibly complete but entitlement/subscription not active for this product.' };\n }\n } catch (error) {\n const purchasesError = error as PurchasesError;\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {\n // Wait for a terminal state for up to 5 seconds\n return new Promise((resolve) => {\n // Define the listener function separately to remove it later\n const updateListener: CustomerInfoUpdateListener = (updatedCustomerInfo: CustomerInfo) => {\n const isActive = this.isProductActive(updatedCustomerInfo, productId);\n if (isActive) {\n clearTimeout(timeoutId);\n // Remove listener using the function reference\n Purchases.removeCustomerInfoUpdateListener(updateListener);\n resolve({ status: 'purchased' });\n }\n };\n\n const timeoutId = setTimeout(() => {\n // Remove listener using the function reference on timeout\n Purchases.removeCustomerInfoUpdateListener(updateListener);\n resolve({ status: 'pending' });\n }, 5000);\n\n // Add the listener\n Purchases.addCustomerInfoUpdateListener(updateListener);\n });\n }\n\n if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return { status: 'cancelled' };\n }\n\n // Handle other errors\n return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };\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 const isActive = Object.keys(customerInfo.entitlements.active).length > 0;\n return isActive;\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": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Helium paywalls expo sdk",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,4 +1,4 @@
1
- import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases';
1
+ import Purchases, {PURCHASES_ERROR_CODE, PurchasesStoreProduct} from 'react-native-purchases';
2
2
  import type { PurchasesError, PurchasesPackage, CustomerInfoUpdateListener, CustomerInfo, PurchasesEntitlementInfo } from 'react-native-purchases';
3
3
  import {HeliumPurchaseConfig, HeliumPurchaseResult} from "../HeliumPaywallSdk.types";
4
4
 
@@ -19,6 +19,8 @@ export class RevenueCatHeliumHandler {
19
19
  private isMappingInitialized: boolean = false;
20
20
  private initializationPromise: Promise<void> | null = null;
21
21
 
22
+ private rcProductToPackageMapping: Record<string, PurchasesStoreProduct> = {};
23
+
22
24
  constructor(apiKey?: string) {
23
25
  if (apiKey) {
24
26
  Purchases.configure({ apiKey });
@@ -34,13 +36,13 @@ export class RevenueCatHeliumHandler {
34
36
  this.initializationPromise = (async () => {
35
37
  try {
36
38
  const offerings = await Purchases.getOfferings();
37
- if (offerings.current?.availablePackages) {
38
- offerings.current.availablePackages.forEach((pkg: PurchasesPackage) => {
39
+ const allOfferings = offerings.all;
40
+ for (const offering of Object.values(allOfferings)) {
41
+ offering.availablePackages.forEach((pkg: PurchasesPackage) => {
39
42
  if (pkg.product?.identifier) {
40
43
  this.productIdToPackageMapping[pkg.product.identifier] = pkg;
41
44
  }
42
45
  });
43
- } else {
44
46
  }
45
47
  this.isMappingInitialized = true;
46
48
  } catch (error) {
@@ -64,12 +66,33 @@ export class RevenueCatHeliumHandler {
64
66
  await this.ensureMappingInitialized();
65
67
 
66
68
  const pkg: PurchasesPackage | undefined = this.productIdToPackageMapping[productId];
69
+ let rcProduct: PurchasesStoreProduct | undefined;
67
70
  if (!pkg) {
68
- return { status: 'failed', error: `RevenueCat Package not found for ID: ${productId}` };
71
+ // Use cached if available
72
+ rcProduct = this.rcProductToPackageMapping[productId];
73
+ if (!rcProduct) {
74
+ // Try to retrieve now
75
+ try {
76
+ const rcProducts = await Purchases.getProducts([productId]);
77
+ rcProduct = rcProducts.length > 0 ? rcProducts[0] : undefined;
78
+ } catch {
79
+ // 'failed' status will be returned
80
+ }
81
+ if (rcProduct) {
82
+ this.rcProductToPackageMapping[productId] = rcProduct;
83
+ }
84
+ }
69
85
  }
70
86
 
71
87
  try {
72
- const { customerInfo } = await Purchases.purchasePackage(pkg);
88
+ let customerInfo: CustomerInfo;
89
+ if (pkg) {
90
+ customerInfo = (await Purchases.purchasePackage(pkg)).customerInfo;
91
+ } else if (rcProduct) {
92
+ customerInfo = (await Purchases.purchaseStoreProduct(rcProduct)).customerInfo;
93
+ } else {
94
+ return { status: 'failed', error: `RevenueCat Product/Package not found for ID: ${productId}` };
95
+ }
73
96
  const isActive = this.isProductActive(customerInfo, productId);
74
97
  if (isActive) {
75
98
  return { status: 'purchased' };