expo-helium 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +5 -0
- package/README.md +139 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +43 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/paywallsdk/HeliumPaywallSdkModule.kt +50 -0
- package/android/src/main/java/expo/modules/paywallsdk/HeliumPaywallSdkView.kt +30 -0
- package/build/HeliumPaywallSdk.types.d.ts +77 -0
- package/build/HeliumPaywallSdk.types.d.ts.map +1 -0
- package/build/HeliumPaywallSdk.types.js +12 -0
- package/build/HeliumPaywallSdk.types.js.map +1 -0
- package/build/HeliumPaywallSdkModule.d.ts +15 -0
- package/build/HeliumPaywallSdkModule.d.ts.map +1 -0
- package/build/HeliumPaywallSdkModule.js +4 -0
- package/build/HeliumPaywallSdkModule.js.map +1 -0
- package/build/HeliumPaywallSdkView.d.ts +4 -0
- package/build/HeliumPaywallSdkView.d.ts.map +1 -0
- package/build/HeliumPaywallSdkView.js +7 -0
- package/build/HeliumPaywallSdkView.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +84 -0
- package/build/index.js.map +1 -0
- package/build/revenuecat/index.d.ts +2 -0
- package/build/revenuecat/index.d.ts.map +1 -0
- package/build/revenuecat/index.js +2 -0
- package/build/revenuecat/index.js.map +1 -0
- package/build/revenuecat/revenuecat.d.ts +16 -0
- package/build/revenuecat/revenuecat.d.ts.map +1 -0
- package/build/revenuecat/revenuecat.js +126 -0
- package/build/revenuecat/revenuecat.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/HeliumPaywallSdk.podspec +30 -0
- package/ios/HeliumPaywallSdkModule.swift +249 -0
- package/ios/HeliumPaywallSdkView.swift +38 -0
- package/package.json +55 -0
- package/src/HeliumPaywallSdk.types.ts +95 -0
- package/src/HeliumPaywallSdkModule.ts +36 -0
- package/src/HeliumPaywallSdkView.tsx +11 -0
- package/src/index.ts +113 -0
- package/src/revenuecat/index.ts +1 -0
- package/src/revenuecat/revenuecat.ts +136 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import Purchases, { PURCHASES_ERROR_CODE } from 'react-native-purchases';
|
|
2
|
+
// Rename the factory function
|
|
3
|
+
export function createRevenueCatPurchaseConfig(config) {
|
|
4
|
+
const rcHandler = new RevenueCatHeliumHandler(config?.apiKey);
|
|
5
|
+
return {
|
|
6
|
+
apiKey: config?.apiKey,
|
|
7
|
+
makePurchase: rcHandler.makePurchase.bind(rcHandler),
|
|
8
|
+
restorePurchases: rcHandler.restorePurchases.bind(rcHandler),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export class RevenueCatHeliumHandler {
|
|
12
|
+
productIdToPackageMapping = {};
|
|
13
|
+
isMappingInitialized = false;
|
|
14
|
+
initializationPromise = null;
|
|
15
|
+
constructor(apiKey) {
|
|
16
|
+
if (apiKey) {
|
|
17
|
+
Purchases.configure({ apiKey });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
}
|
|
21
|
+
this.initializePackageMapping();
|
|
22
|
+
}
|
|
23
|
+
async initializePackageMapping() {
|
|
24
|
+
if (this.initializationPromise) {
|
|
25
|
+
return this.initializationPromise;
|
|
26
|
+
}
|
|
27
|
+
this.initializationPromise = (async () => {
|
|
28
|
+
try {
|
|
29
|
+
const offerings = await Purchases.getOfferings();
|
|
30
|
+
if (offerings.current?.availablePackages) {
|
|
31
|
+
offerings.current.availablePackages.forEach((pkg) => {
|
|
32
|
+
if (pkg.product?.identifier) {
|
|
33
|
+
this.productIdToPackageMapping[pkg.product.identifier] = pkg;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
}
|
|
39
|
+
this.isMappingInitialized = true;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
this.isMappingInitialized = false;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
this.initializationPromise = null;
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
return this.initializationPromise;
|
|
49
|
+
}
|
|
50
|
+
async ensureMappingInitialized() {
|
|
51
|
+
if (!this.isMappingInitialized && !this.initializationPromise) {
|
|
52
|
+
await this.initializePackageMapping();
|
|
53
|
+
}
|
|
54
|
+
else if (this.initializationPromise) {
|
|
55
|
+
await this.initializationPromise;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async makePurchase(productId) {
|
|
59
|
+
await this.ensureMappingInitialized();
|
|
60
|
+
const pkg = this.productIdToPackageMapping[productId];
|
|
61
|
+
if (!pkg) {
|
|
62
|
+
return { status: 'failed', error: `RevenueCat Package not found for ID: ${productId}` };
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const { customerInfo } = await Purchases.purchasePackage(pkg);
|
|
66
|
+
const isActive = this.isProductActive(customerInfo, productId);
|
|
67
|
+
if (isActive) {
|
|
68
|
+
return { status: 'purchased' };
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// This case might occur if the purchase succeeded but the entitlement wasn't immediately active
|
|
72
|
+
// or if a different product became active.
|
|
73
|
+
// Consider if polling/listening might be needed here too, similar to pending.
|
|
74
|
+
// For now, returning failed as the specific product isn't confirmed active.
|
|
75
|
+
return { status: 'failed', error: 'Purchase possibly complete but entitlement/subscription not active for this product.' };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const purchasesError = error;
|
|
80
|
+
if (purchasesError?.code === PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR) {
|
|
81
|
+
// Wait for a terminal state for up to 5 seconds
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
// Define the listener function separately to remove it later
|
|
84
|
+
const updateListener = (updatedCustomerInfo) => {
|
|
85
|
+
const isActive = this.isProductActive(updatedCustomerInfo, productId);
|
|
86
|
+
if (isActive) {
|
|
87
|
+
clearTimeout(timeoutId);
|
|
88
|
+
// Remove listener using the function reference
|
|
89
|
+
Purchases.removeCustomerInfoUpdateListener(updateListener);
|
|
90
|
+
resolve({ status: 'purchased' });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const timeoutId = setTimeout(() => {
|
|
94
|
+
// Remove listener using the function reference on timeout
|
|
95
|
+
Purchases.removeCustomerInfoUpdateListener(updateListener);
|
|
96
|
+
resolve({ status: 'pending' });
|
|
97
|
+
}, 5000);
|
|
98
|
+
// Add the listener
|
|
99
|
+
Purchases.addCustomerInfoUpdateListener(updateListener);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (purchasesError?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
|
|
103
|
+
return { status: 'cancelled' };
|
|
104
|
+
}
|
|
105
|
+
// Handle other errors
|
|
106
|
+
return { status: 'failed', error: purchasesError?.message || 'RevenueCat purchase failed.' };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Helper function to check if a product is active in CustomerInfo
|
|
110
|
+
isProductActive(customerInfo, productId) {
|
|
111
|
+
return Object.values(customerInfo.entitlements.active).some((entitlement) => entitlement.productIdentifier === productId)
|
|
112
|
+
|| customerInfo.activeSubscriptions.includes(productId)
|
|
113
|
+
|| customerInfo.allPurchasedProductIdentifiers.includes(productId);
|
|
114
|
+
}
|
|
115
|
+
async restorePurchases() {
|
|
116
|
+
try {
|
|
117
|
+
const customerInfo = await Purchases.restorePurchases();
|
|
118
|
+
const isActive = Object.keys(customerInfo.entitlements.active).length > 0;
|
|
119
|
+
return isActive;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=revenuecat.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'HeliumPaywallSdk'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = {
|
|
14
|
+
:ios => '15.1',
|
|
15
|
+
:tvos => '15.1'
|
|
16
|
+
}
|
|
17
|
+
s.swift_version = '5.4'
|
|
18
|
+
s.source = { git: 'https://github.com/salami/expo-paywall-sdk' }
|
|
19
|
+
s.static_framework = true
|
|
20
|
+
|
|
21
|
+
s.dependency 'ExpoModulesCore'
|
|
22
|
+
s.dependency 'Helium', '2.0.11'
|
|
23
|
+
|
|
24
|
+
# Swift/Objective-C compatibility
|
|
25
|
+
s.pod_target_xcconfig = {
|
|
26
|
+
'DEFINES_MODULE' => 'YES',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
30
|
+
end
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import Helium
|
|
3
|
+
import SwiftUI
|
|
4
|
+
|
|
5
|
+
// Define purchase error enum
|
|
6
|
+
enum PurchaseError: LocalizedError {
|
|
7
|
+
case unknownStatus(status: String)
|
|
8
|
+
case purchaseFailed(errorMsg: String)
|
|
9
|
+
|
|
10
|
+
var errorDescription: String? {
|
|
11
|
+
switch self {
|
|
12
|
+
case let .unknownStatus(status):
|
|
13
|
+
return "Purchased not successful due to unknown status - \(status)."
|
|
14
|
+
case let .purchaseFailed(errorMsg):
|
|
15
|
+
return errorMsg
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public class HeliumPaywallSdkModule: Module {
|
|
21
|
+
// Single continuations for ongoing operations
|
|
22
|
+
private var currentProductId: String? = nil
|
|
23
|
+
private var purchaseContinuation: CheckedContinuation<HeliumPaywallTransactionStatus, Never>? = nil
|
|
24
|
+
private var restoreContinuation: CheckedContinuation<Bool, Never>? = nil
|
|
25
|
+
|
|
26
|
+
// Each module class must implement the definition function. The definition consists of components
|
|
27
|
+
// that describes the module's functionality and behavior.
|
|
28
|
+
// See https://docs.expo.dev/modules/module-api for more details about available components.
|
|
29
|
+
public func definition() -> ModuleDefinition {
|
|
30
|
+
// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
|
|
31
|
+
// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
|
|
32
|
+
// The module will be accessible from `requireNativeModule('HeliumPaywallSdk')` in JavaScript.
|
|
33
|
+
Name("HeliumPaywallSdk")
|
|
34
|
+
|
|
35
|
+
// Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary.
|
|
36
|
+
// Constants([
|
|
37
|
+
// "PI": Double.pi
|
|
38
|
+
// ])
|
|
39
|
+
|
|
40
|
+
// Defines event names that the module can send to JavaScript.
|
|
41
|
+
Events("onHeliumPaywallEvent")
|
|
42
|
+
|
|
43
|
+
Events("onDelegateActionEvent")
|
|
44
|
+
|
|
45
|
+
// todo use Record here? https://docs.expo.dev/modules/module-api/#records
|
|
46
|
+
Function("initialize") { (config: [String : Any]) in
|
|
47
|
+
let userTraitsMap = config["customUserTraits"] as? [String : Any]
|
|
48
|
+
|
|
49
|
+
// Create delegate with closures that send events to JavaScript
|
|
50
|
+
let delegate = InternalDelegate(
|
|
51
|
+
eventHandler: { [weak self] event in
|
|
52
|
+
self?.sendEvent("onHeliumPaywallEvent", event.toDictionary())
|
|
53
|
+
},
|
|
54
|
+
purchaseHandler: { [weak self] productId in
|
|
55
|
+
guard let self else { return .failed(PurchaseError.purchaseFailed(errorMsg: "Module not active!")) }
|
|
56
|
+
// Check if there's already a purchase in progress and cancel it
|
|
57
|
+
if let existingContinuation = self.purchaseContinuation {
|
|
58
|
+
existingContinuation.resume(returning: .cancelled)
|
|
59
|
+
self.purchaseContinuation = nil
|
|
60
|
+
self.currentProductId = nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return await withCheckedContinuation { continuation in
|
|
64
|
+
// Store the continuation and product ID
|
|
65
|
+
self.currentProductId = productId
|
|
66
|
+
self.purchaseContinuation = continuation
|
|
67
|
+
|
|
68
|
+
// Send event to JavaScript
|
|
69
|
+
self.sendEvent("onDelegateActionEvent", [
|
|
70
|
+
"type": "purchase",
|
|
71
|
+
"productId": productId
|
|
72
|
+
])
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
restoreHandler: { [weak self] in
|
|
76
|
+
guard let self else { return false }
|
|
77
|
+
// Check if there's already a restore in progress and cancel it
|
|
78
|
+
if let existingContinuation = self.restoreContinuation {
|
|
79
|
+
existingContinuation.resume(returning: false)
|
|
80
|
+
self.restoreContinuation = nil
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return await withCheckedContinuation { continuation in
|
|
84
|
+
// Store the continuation
|
|
85
|
+
self.restoreContinuation = continuation
|
|
86
|
+
|
|
87
|
+
// Send event to JavaScript
|
|
88
|
+
self.sendEvent("onDelegateActionEvent", [
|
|
89
|
+
"type": "restore"
|
|
90
|
+
])
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
Helium.shared.initialize(
|
|
96
|
+
apiKey: config["apiKey"] as? String ?? "",
|
|
97
|
+
heliumPaywallDelegate: delegate,
|
|
98
|
+
fallbackPaywall: FallbackView(),
|
|
99
|
+
customUserId: config["customUserId"] as? String,
|
|
100
|
+
customAPIEndpoint: config["customAPIEndpoint"] as? String,
|
|
101
|
+
customUserTraits: userTraitsMap != nil ? HeliumUserTraits(userTraitsMap!) : nil,
|
|
102
|
+
revenueCatAppUserId: config["revenueCatAppUserId"] as? String
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Function for JavaScript to provide purchase result
|
|
107
|
+
Function("handlePurchaseResult") { [weak self] (statusString: String, errorMsg: String?) in
|
|
108
|
+
guard let continuation = self?.purchaseContinuation else {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Parse status string
|
|
113
|
+
let lowercasedStatus = statusString.lowercased()
|
|
114
|
+
let status: HeliumPaywallTransactionStatus
|
|
115
|
+
|
|
116
|
+
switch lowercasedStatus {
|
|
117
|
+
case "purchased": status = .purchased
|
|
118
|
+
case "cancelled": status = .cancelled
|
|
119
|
+
case "restored": status = .restored
|
|
120
|
+
case "pending": status = .pending
|
|
121
|
+
case "failed": status = .failed(PurchaseError.purchaseFailed(errorMsg: errorMsg ?? "Unexpected error."))
|
|
122
|
+
default: status = .failed(PurchaseError.unknownStatus(status: lowercasedStatus))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Clear the references
|
|
126
|
+
self?.purchaseContinuation = nil
|
|
127
|
+
self?.currentProductId = nil
|
|
128
|
+
|
|
129
|
+
// Resume the continuation with the status
|
|
130
|
+
continuation.resume(returning: status)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Function for JavaScript to provide restore result
|
|
134
|
+
Function("handleRestoreResult") { [weak self] (success: Bool) in
|
|
135
|
+
guard let continuation = self?.restoreContinuation else {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
self?.restoreContinuation = nil
|
|
140
|
+
continuation.resume(returning: success)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Function("presentUpsell") { (trigger: String) in
|
|
144
|
+
Helium.shared.presentUpsell(trigger: trigger);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Function("hideUpsell") {
|
|
148
|
+
let _ = Helium.shared.hideUpsell();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Function("hideAllUpsells") {
|
|
152
|
+
Helium.shared.hideAllUpsells();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
Function("getDownloadStatus") {
|
|
156
|
+
return Helium.shared.getDownloadStatus().rawValue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Function("fallbackOpenOrCloseEvent") { (trigger: String?, isOpen: Bool, viewType: String?) in
|
|
160
|
+
HeliumPaywallDelegateWrapper.shared.onFallbackOpenCloseEvent(trigger: trigger, isOpen: isOpen, viewType: viewType)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Defines a JavaScript function that always returns a Promise and whose native code
|
|
164
|
+
// is by default dispatched on the different thread than the JavaScript runtime runs on.
|
|
165
|
+
// AsyncFunction("setValueAsync") { (value: String) in
|
|
166
|
+
// // Send an event to JavaScript.
|
|
167
|
+
// self.sendEvent("onHeliumPaywallEvent", [
|
|
168
|
+
// "value": value
|
|
169
|
+
// ])
|
|
170
|
+
// }
|
|
171
|
+
|
|
172
|
+
// Enables the module to be used as a native view. Definition components that are accepted as part of the
|
|
173
|
+
// view definition: Prop, Events.
|
|
174
|
+
View(HeliumPaywallSdkView.self) {
|
|
175
|
+
// Defines a setter for the `url` prop.
|
|
176
|
+
Prop("url") { (view: HeliumPaywallSdkView, url: URL) in
|
|
177
|
+
if view.webView.url != url {
|
|
178
|
+
view.webView.load(URLRequest(url: url))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
Events("onLoad")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fileprivate class InternalDelegate: HeliumPaywallDelegate {
|
|
188
|
+
private let eventHandler: (HeliumPaywallEvent) -> Void
|
|
189
|
+
private let purchaseHandler: (String) async -> HeliumPaywallTransactionStatus
|
|
190
|
+
private let restoreHandler: () async -> Bool
|
|
191
|
+
|
|
192
|
+
init(
|
|
193
|
+
eventHandler: @escaping (HeliumPaywallEvent) -> Void,
|
|
194
|
+
purchaseHandler: @escaping (String) async -> HeliumPaywallTransactionStatus,
|
|
195
|
+
restoreHandler: @escaping () async -> Bool
|
|
196
|
+
) {
|
|
197
|
+
self.eventHandler = eventHandler
|
|
198
|
+
self.purchaseHandler = purchaseHandler
|
|
199
|
+
self.restoreHandler = restoreHandler
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public func makePurchase(productId: String) async -> HeliumPaywallTransactionStatus {
|
|
203
|
+
return await purchaseHandler(productId)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public func restorePurchases() async -> Bool {
|
|
207
|
+
return await restoreHandler()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public func onHeliumPaywallEvent(event: HeliumPaywallEvent) {
|
|
211
|
+
eventHandler(event)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
fileprivate struct FallbackView: View {
|
|
216
|
+
@Environment(\.presentationMode) var presentationMode
|
|
217
|
+
|
|
218
|
+
var body: some View {
|
|
219
|
+
VStack(spacing: 20) {
|
|
220
|
+
Spacer()
|
|
221
|
+
|
|
222
|
+
Text("Fallback Paywall")
|
|
223
|
+
.font(.title)
|
|
224
|
+
.fontWeight(.bold)
|
|
225
|
+
|
|
226
|
+
Text("Something went wrong loading the paywall")
|
|
227
|
+
.font(.body)
|
|
228
|
+
.multilineTextAlignment(.center)
|
|
229
|
+
.foregroundColor(.secondary)
|
|
230
|
+
|
|
231
|
+
Spacer()
|
|
232
|
+
|
|
233
|
+
Button(action: {
|
|
234
|
+
presentationMode.wrappedValue.dismiss()
|
|
235
|
+
}) {
|
|
236
|
+
Text("Close")
|
|
237
|
+
.font(.headline)
|
|
238
|
+
.foregroundColor(.white)
|
|
239
|
+
.frame(maxWidth: .infinity)
|
|
240
|
+
.padding()
|
|
241
|
+
.background(Color.blue)
|
|
242
|
+
.cornerRadius(10)
|
|
243
|
+
}
|
|
244
|
+
.padding(.horizontal, 40)
|
|
245
|
+
.padding(.bottom, 40)
|
|
246
|
+
}
|
|
247
|
+
.padding()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import WebKit
|
|
3
|
+
|
|
4
|
+
// This view will be used as a native component. Make sure to inherit from `ExpoView`
|
|
5
|
+
// to apply the proper styling (e.g. border radius and shadows).
|
|
6
|
+
class HeliumPaywallSdkView: ExpoView {
|
|
7
|
+
let webView = WKWebView()
|
|
8
|
+
let onLoad = EventDispatcher()
|
|
9
|
+
var delegate: WebViewDelegate?
|
|
10
|
+
|
|
11
|
+
required init(appContext: AppContext? = nil) {
|
|
12
|
+
super.init(appContext: appContext)
|
|
13
|
+
clipsToBounds = true
|
|
14
|
+
delegate = WebViewDelegate { url in
|
|
15
|
+
self.onLoad(["url": url])
|
|
16
|
+
}
|
|
17
|
+
webView.navigationDelegate = delegate
|
|
18
|
+
addSubview(webView)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override func layoutSubviews() {
|
|
22
|
+
webView.frame = bounds
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class WebViewDelegate: NSObject, WKNavigationDelegate {
|
|
27
|
+
let onUrlChange: (String) -> Void
|
|
28
|
+
|
|
29
|
+
init(onUrlChange: @escaping (String) -> Void) {
|
|
30
|
+
self.onUrlChange = onUrlChange
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation) {
|
|
34
|
+
if let url = webView.url {
|
|
35
|
+
onUrlChange(url.absoluteString)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-helium",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Helium paywalls expo sdk",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "expo-module lint",
|
|
11
|
+
"test": "expo-module test",
|
|
12
|
+
"prepare": "expo-module prepare",
|
|
13
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:ios": "xed example/ios",
|
|
16
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"expo",
|
|
21
|
+
"expo-helium",
|
|
22
|
+
"helium-expo-sdk",
|
|
23
|
+
"expo-helium-sdk",
|
|
24
|
+
"HeliumPaywallSdk"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/cloudcaptainai/helium-expo-sdk.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/cloudcaptainai/helium-expo-sdk/issues"
|
|
32
|
+
},
|
|
33
|
+
"author": "Anish Doshi <anish@tryhelium.com> (https://tryhelium.com)",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"homepage": "https://github.com/cloudcaptainai/helium-expo-sdk/#readme",
|
|
36
|
+
"dependencies": {},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/react": "~19.0.0",
|
|
39
|
+
"expo-module-scripts": "^4.1.9",
|
|
40
|
+
"expo": "~53.0.0",
|
|
41
|
+
"react-native": "0.79.1",
|
|
42
|
+
"react-native-purchases": ">=5.1.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"expo": "*",
|
|
46
|
+
"react": "*",
|
|
47
|
+
"react-native": "*",
|
|
48
|
+
"react-native-purchases": ">=5.1.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"react-native-purchases": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type OnLoadEventPayload = {
|
|
4
|
+
url: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type HeliumPaywallSdkModuleEvents = {
|
|
8
|
+
onHeliumPaywallEvent: (params: HeliumPaywallEvent) => void;
|
|
9
|
+
onDelegateActionEvent: (params: DelegateActionEvent) => void;
|
|
10
|
+
};
|
|
11
|
+
export type HeliumPaywallEvent = {
|
|
12
|
+
type: string;
|
|
13
|
+
triggerName?: string;
|
|
14
|
+
paywallTemplateName?: string;
|
|
15
|
+
productKey?: string;
|
|
16
|
+
ctaName?: string;
|
|
17
|
+
configId?: string;
|
|
18
|
+
numAttempts?: number;
|
|
19
|
+
downloadTimeTakenMS?: number;
|
|
20
|
+
webviewRenderTimeTakenMS?: number;
|
|
21
|
+
imagesDownloadTimeTakenMS?: number;
|
|
22
|
+
fontsDownloadTimeTakenMS?: number;
|
|
23
|
+
bundleDownloadTimeMS?: number;
|
|
24
|
+
dismissAll?: boolean;
|
|
25
|
+
errorDescription?: string;
|
|
26
|
+
};
|
|
27
|
+
export type DelegateActionEvent = {
|
|
28
|
+
type: 'purchase' | 'restore';
|
|
29
|
+
productId?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type HeliumPaywallSdkViewProps = {
|
|
33
|
+
url: string;
|
|
34
|
+
onLoad: (event: { nativeEvent: OnLoadEventPayload }) => void;
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type HeliumTransactionStatus = 'purchased' | 'failed' | 'cancelled' | 'pending' | 'restored';
|
|
39
|
+
export type HeliumPurchaseResult = {
|
|
40
|
+
status: HeliumTransactionStatus;
|
|
41
|
+
error?: string; // Optional error message
|
|
42
|
+
};
|
|
43
|
+
export type HeliumDownloadStatus = 'downloadSuccess' | 'downloadFailure' | 'inProgress' | 'notDownloadedYet';
|
|
44
|
+
|
|
45
|
+
// --- Purchase Configuration Types ---
|
|
46
|
+
|
|
47
|
+
/** Interface for providing custom purchase handling logic. */
|
|
48
|
+
|
|
49
|
+
export interface HeliumPurchaseConfig {
|
|
50
|
+
makePurchase: (productId: string) => Promise<HeliumPurchaseResult>;
|
|
51
|
+
restorePurchases: () => Promise<boolean>;
|
|
52
|
+
|
|
53
|
+
/** Optional RevenueCat API Key. If not provided, RevenueCat must be configured elsewhere. */
|
|
54
|
+
apiKey?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Helper function for creating Custom Purchase Config
|
|
58
|
+
export function createCustomPurchaseConfig(callbacks: {
|
|
59
|
+
makePurchase: (productId: string) => Promise<HeliumPurchaseResult>;
|
|
60
|
+
restorePurchases: () => Promise<boolean>;
|
|
61
|
+
}): HeliumPurchaseConfig {
|
|
62
|
+
return {
|
|
63
|
+
makePurchase: callbacks.makePurchase,
|
|
64
|
+
restorePurchases: callbacks.restorePurchases,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface HeliumConfig {
|
|
69
|
+
/** Your Helium API Key */
|
|
70
|
+
apiKey: string;
|
|
71
|
+
/** Configuration for handling purchases. Can be custom functions or a pre-built handler config. */
|
|
72
|
+
purchaseConfig: HeliumPurchaseConfig;
|
|
73
|
+
/** Callback for receiving all Helium paywall events. */
|
|
74
|
+
onHeliumPaywallEvent: (event: HeliumPaywallEvent) => void; // Still mandatory
|
|
75
|
+
|
|
76
|
+
// Optional configurations
|
|
77
|
+
triggers?: string[];
|
|
78
|
+
customUserId?: string;
|
|
79
|
+
customAPIEndpoint?: string;
|
|
80
|
+
customUserTraits?: Record<string, any>;
|
|
81
|
+
revenueCatAppUserId?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface NativeHeliumConfig {
|
|
85
|
+
apiKey: string;
|
|
86
|
+
customUserId?: string;
|
|
87
|
+
customAPIEndpoint?: string;
|
|
88
|
+
customUserTraits?: Record<string, any>;
|
|
89
|
+
revenueCatAppUserId?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const HELIUM_CTA_NAMES = {
|
|
93
|
+
SCHEDULE_CALL: 'schedule_call',
|
|
94
|
+
SUBSCRIBE_BUTTON: 'subscribe_button',
|
|
95
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NativeModule, requireNativeModule } from "expo";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
HeliumDownloadStatus,
|
|
5
|
+
HeliumPaywallSdkModuleEvents,
|
|
6
|
+
HeliumTransactionStatus,
|
|
7
|
+
NativeHeliumConfig,
|
|
8
|
+
} from "./HeliumPaywallSdk.types";
|
|
9
|
+
|
|
10
|
+
declare class HeliumPaywallSdkModule extends NativeModule<HeliumPaywallSdkModuleEvents> {
|
|
11
|
+
initialize(config: NativeHeliumConfig): void;
|
|
12
|
+
|
|
13
|
+
presentUpsell(triggerName: string): void;
|
|
14
|
+
|
|
15
|
+
hideUpsell(): void;
|
|
16
|
+
|
|
17
|
+
hideAllUpsells(): void;
|
|
18
|
+
|
|
19
|
+
getDownloadStatus(): HeliumDownloadStatus;
|
|
20
|
+
|
|
21
|
+
fallbackOpenOrCloseEvent(
|
|
22
|
+
trigger: string,
|
|
23
|
+
isOpen: boolean,
|
|
24
|
+
viewType: string,
|
|
25
|
+
): void;
|
|
26
|
+
|
|
27
|
+
handlePurchaseResult(
|
|
28
|
+
statusString: HeliumTransactionStatus,
|
|
29
|
+
errorMsg?: string,
|
|
30
|
+
): void;
|
|
31
|
+
|
|
32
|
+
handleRestoreResult(success: boolean): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// This call loads the native module object from the JSI.
|
|
36
|
+
export default requireNativeModule<HeliumPaywallSdkModule>("HeliumPaywallSdk");
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { HeliumPaywallSdkViewProps } from './HeliumPaywallSdk.types';
|
|
5
|
+
|
|
6
|
+
const NativeView: React.ComponentType<HeliumPaywallSdkViewProps> =
|
|
7
|
+
requireNativeView('HeliumPaywallSdk');
|
|
8
|
+
|
|
9
|
+
export default function HeliumPaywallSdkView(props: HeliumPaywallSdkViewProps) {
|
|
10
|
+
return <NativeView {...props} />;
|
|
11
|
+
}
|