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.
- package/android/build.gradle +1 -1
- package/android/src/main/java/expo/modules/paywallsdk/HeliumPaywallSdkModule.kt +3 -1
- package/build/revenuecat/revenuecat.d.ts +2 -0
- package/build/revenuecat/revenuecat.d.ts.map +1 -1
- package/build/revenuecat/revenuecat.js +41 -64
- package/build/revenuecat/revenuecat.js.map +1 -1
- package/package.json +1 -1
- package/src/revenuecat/revenuecat.ts +47 -71
package/android/build.gradle
CHANGED
|
@@ -43,7 +43,7 @@ android {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
dependencies {
|
|
46
|
-
implementation("com.tryhelium.paywall:core:0.1.
|
|
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
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"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","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":"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","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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|