@ridwan-retainer/paywall 0.1.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.
Files changed (116) hide show
  1. package/README.md +64 -0
  2. package/dist/__mocks__/async-storage.d.ts +12 -0
  3. package/dist/__mocks__/async-storage.d.ts.map +1 -0
  4. package/dist/__mocks__/async-storage.js +14 -0
  5. package/dist/__mocks__/react-native-purchases-ui.d.ts +17 -0
  6. package/dist/__mocks__/react-native-purchases-ui.d.ts.map +1 -0
  7. package/dist/__mocks__/react-native-purchases-ui.js +16 -0
  8. package/dist/__mocks__/react-native-purchases.d.ts +22 -0
  9. package/dist/__mocks__/react-native-purchases.d.ts.map +1 -0
  10. package/dist/__mocks__/react-native-purchases.js +25 -0
  11. package/dist/components/Paywall.d.ts +13 -0
  12. package/dist/components/Paywall.d.ts.map +1 -0
  13. package/dist/components/Paywall.js +18 -0
  14. package/dist/components/PaywallFooter.d.ts +13 -0
  15. package/dist/components/PaywallFooter.d.ts.map +1 -0
  16. package/dist/components/PaywallFooter.js +60 -0
  17. package/dist/components/PaywallGate.d.ts +12 -0
  18. package/dist/components/PaywallGate.d.ts.map +1 -0
  19. package/dist/components/PaywallGate.js +66 -0
  20. package/dist/components/PurchaseButton.d.ts +13 -0
  21. package/dist/components/PurchaseButton.d.ts.map +1 -0
  22. package/dist/components/PurchaseButton.js +30 -0
  23. package/dist/components/RestoreButton.d.ts +16 -0
  24. package/dist/components/RestoreButton.d.ts.map +1 -0
  25. package/dist/components/RestoreButton.js +35 -0
  26. package/dist/components/SubscriptionGate.d.ts +14 -0
  27. package/dist/components/SubscriptionGate.d.ts.map +1 -0
  28. package/dist/components/SubscriptionGate.js +22 -0
  29. package/dist/config/RevenueCatConfig.d.ts +4 -0
  30. package/dist/config/RevenueCatConfig.d.ts.map +1 -0
  31. package/dist/config/RevenueCatConfig.js +61 -0
  32. package/dist/config/environment.d.ts +3 -0
  33. package/dist/config/environment.d.ts.map +1 -0
  34. package/dist/config/environment.js +16 -0
  35. package/dist/config/types.d.ts +16 -0
  36. package/dist/config/types.d.ts.map +1 -0
  37. package/dist/config/types.js +2 -0
  38. package/dist/entitlements/CustomerInfoManager.d.ts +14 -0
  39. package/dist/entitlements/CustomerInfoManager.d.ts.map +1 -0
  40. package/dist/entitlements/CustomerInfoManager.js +59 -0
  41. package/dist/entitlements/EntitlementCache.d.ts +5 -0
  42. package/dist/entitlements/EntitlementCache.d.ts.map +1 -0
  43. package/dist/entitlements/EntitlementCache.js +39 -0
  44. package/dist/entitlements/subscriptionUtils.d.ts +10 -0
  45. package/dist/entitlements/subscriptionUtils.d.ts.map +1 -0
  46. package/dist/entitlements/subscriptionUtils.js +34 -0
  47. package/dist/entitlements/types.d.ts +13 -0
  48. package/dist/entitlements/types.d.ts.map +1 -0
  49. package/dist/entitlements/types.js +2 -0
  50. package/dist/hooks/useCurrentOffering.d.ts +11 -0
  51. package/dist/hooks/useCurrentOffering.d.ts.map +1 -0
  52. package/dist/hooks/useCurrentOffering.js +46 -0
  53. package/dist/hooks/useCustomerInfo.d.ts +8 -0
  54. package/dist/hooks/useCustomerInfo.d.ts.map +1 -0
  55. package/dist/hooks/useCustomerInfo.js +34 -0
  56. package/dist/hooks/useEntitlement.d.ts +10 -0
  57. package/dist/hooks/useEntitlement.d.ts.map +1 -0
  58. package/dist/hooks/useEntitlement.js +48 -0
  59. package/dist/hooks/useOfferings.d.ts +9 -0
  60. package/dist/hooks/useOfferings.d.ts.map +1 -0
  61. package/dist/hooks/useOfferings.js +58 -0
  62. package/dist/hooks/usePaywall.d.ts +6 -0
  63. package/dist/hooks/usePaywall.d.ts.map +1 -0
  64. package/dist/hooks/usePaywall.js +23 -0
  65. package/dist/hooks/usePurchase.d.ts +14 -0
  66. package/dist/hooks/usePurchase.d.ts.map +1 -0
  67. package/dist/hooks/usePurchase.js +48 -0
  68. package/dist/hooks/useRestorePurchases.d.ts +14 -0
  69. package/dist/hooks/useRestorePurchases.d.ts.map +1 -0
  70. package/dist/hooks/useRestorePurchases.js +48 -0
  71. package/dist/hooks/useRevenueCatInitialization.d.ts +5 -0
  72. package/dist/hooks/useRevenueCatInitialization.d.ts.map +1 -0
  73. package/dist/hooks/useRevenueCatInitialization.js +44 -0
  74. package/dist/index.d.ts +38 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +86 -0
  77. package/dist/offerings/OfferingsManager.d.ts +12 -0
  78. package/dist/offerings/OfferingsManager.d.ts.map +1 -0
  79. package/dist/offerings/OfferingsManager.js +44 -0
  80. package/dist/offerings/packageUtils.d.ts +11 -0
  81. package/dist/offerings/packageUtils.d.ts.map +1 -0
  82. package/dist/offerings/packageUtils.js +44 -0
  83. package/dist/offerings/types.d.ts +8 -0
  84. package/dist/offerings/types.d.ts.map +1 -0
  85. package/dist/offerings/types.js +2 -0
  86. package/dist/paywall/PaywallController.d.ts +21 -0
  87. package/dist/paywall/PaywallController.d.ts.map +1 -0
  88. package/dist/paywall/PaywallController.js +61 -0
  89. package/dist/paywall/types.d.ts +60 -0
  90. package/dist/paywall/types.d.ts.map +1 -0
  91. package/dist/paywall/types.js +10 -0
  92. package/dist/purchase/PurchaseManager.d.ts +10 -0
  93. package/dist/purchase/PurchaseManager.d.ts.map +1 -0
  94. package/dist/purchase/PurchaseManager.js +45 -0
  95. package/dist/purchase/errorHandling.d.ts +5 -0
  96. package/dist/purchase/errorHandling.d.ts.map +1 -0
  97. package/dist/purchase/errorHandling.js +36 -0
  98. package/dist/purchase/purchaseCache.d.ts +19 -0
  99. package/dist/purchase/purchaseCache.d.ts.map +1 -0
  100. package/dist/purchase/purchaseCache.js +57 -0
  101. package/dist/purchase/types.d.ts +23 -0
  102. package/dist/purchase/types.d.ts.map +1 -0
  103. package/dist/purchase/types.js +11 -0
  104. package/dist/restore/RestoreManager.d.ts +26 -0
  105. package/dist/restore/RestoreManager.d.ts.map +1 -0
  106. package/dist/restore/RestoreManager.js +72 -0
  107. package/dist/restore/autoRestore.d.ts +13 -0
  108. package/dist/restore/autoRestore.d.ts.map +1 -0
  109. package/dist/restore/autoRestore.js +37 -0
  110. package/dist/restore/errorHandling.d.ts +10 -0
  111. package/dist/restore/errorHandling.d.ts.map +1 -0
  112. package/dist/restore/errorHandling.js +30 -0
  113. package/dist/restore/types.d.ts +22 -0
  114. package/dist/restore/types.d.ts.map +1 -0
  115. package/dist/restore/types.js +11 -0
  116. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @factory/paywall
2
+
3
+ Revenue-first monetization layer using RevenueCat for in-app purchases and subscriptions in React Native applications.
4
+
5
+ ## Features
6
+
7
+ - RevenueCat integration for cross-platform subscription management
8
+ - Pre-built paywall UI components
9
+ - Subscription status tracking and management
10
+ - Purchase flow handling with proper error management
11
+ - Offering and package management
12
+ - TypeScript support with full type definitions
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @factory/paywall
18
+ ```
19
+
20
+ ### Peer Dependencies
21
+
22
+ This package requires the following peer dependencies:
23
+
24
+ ```bash
25
+ npm install react-native-purchases react-native-purchases-ui @react-native-async-storage/async-storage
26
+ ```
27
+
28
+ ## Setup
29
+
30
+ 1. Create a RevenueCat account at [revenuecat.com](https://www.revenuecat.com)
31
+ 2. Configure your app in the RevenueCat dashboard
32
+ 3. Set up your products in App Store Connect and Google Play Console
33
+ 4. Link your products to RevenueCat
34
+
35
+ ## Usage
36
+
37
+ ```typescript
38
+ import { PaywallProvider, usePaywall, PaywallView } from '@factory/paywall';
39
+
40
+ // Wrap your app with PaywallProvider
41
+ function App() {
42
+ return (
43
+ <PaywallProvider revenueCatApiKey="your-api-key">
44
+ <YourApp />
45
+ </PaywallProvider>
46
+ );
47
+ }
48
+
49
+ // Use the paywall hook in your components
50
+ function YourComponent() {
51
+ const { customerInfo, isSubscribed, offerings } = usePaywall();
52
+
53
+ return (
54
+ <View>
55
+ {!isSubscribed && <PaywallView />}
56
+ {isSubscribed && <Text>Thanks for subscribing!</Text>}
57
+ </View>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,12 @@
1
+ declare const AsyncStorage: {
2
+ setItem: jest.Mock<Promise<void>, [], any>;
3
+ getItem: jest.Mock<Promise<null>, [], any>;
4
+ removeItem: jest.Mock<Promise<void>, [], any>;
5
+ clear: jest.Mock<Promise<void>, [], any>;
6
+ getAllKeys: jest.Mock<Promise<never[]>, [], any>;
7
+ multiGet: jest.Mock<Promise<never[]>, [], any>;
8
+ multiSet: jest.Mock<Promise<void>, [], any>;
9
+ multiRemove: jest.Mock<Promise<void>, [], any>;
10
+ };
11
+ export default AsyncStorage;
12
+ //# sourceMappingURL=async-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-storage.d.ts","sourceRoot":"","sources":["../../src/__mocks__/async-storage.ts"],"names":[],"mappings":"AACA,QAAA,MAAM,YAAY;;;;;;;;;CASjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Mock for @react-native-async-storage/async-storage
4
+ const AsyncStorage = {
5
+ setItem: jest.fn(() => Promise.resolve()),
6
+ getItem: jest.fn(() => Promise.resolve(null)),
7
+ removeItem: jest.fn(() => Promise.resolve()),
8
+ clear: jest.fn(() => Promise.resolve()),
9
+ getAllKeys: jest.fn(() => Promise.resolve([])),
10
+ multiGet: jest.fn(() => Promise.resolve([])),
11
+ multiSet: jest.fn(() => Promise.resolve()),
12
+ multiRemove: jest.fn(() => Promise.resolve()),
13
+ };
14
+ exports.default = AsyncStorage;
@@ -0,0 +1,17 @@
1
+ export declare const PAYWALL_RESULT: {
2
+ PURCHASED: string;
3
+ RESTORED: string;
4
+ CANCELLED: string;
5
+ };
6
+ declare const RevenueCatUI: {
7
+ PAYWALL_RESULT: {
8
+ PURCHASED: string;
9
+ RESTORED: string;
10
+ CANCELLED: string;
11
+ };
12
+ presentPaywall: jest.Mock<any, any, any>;
13
+ presentPaywallIfNeeded: jest.Mock<any, any, any>;
14
+ Paywall: string;
15
+ };
16
+ export default RevenueCatUI;
17
+ //# sourceMappingURL=react-native-purchases-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-native-purchases-ui.d.ts","sourceRoot":"","sources":["../../src/__mocks__/react-native-purchases-ui.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc;;;;CAI1B,CAAC;AAEF,QAAA,MAAM,YAAY;;;;;;;;;CAKjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PAYWALL_RESULT = void 0;
4
+ // Mock for react-native-purchases-ui
5
+ exports.PAYWALL_RESULT = {
6
+ PURCHASED: 'PURCHASED',
7
+ RESTORED: 'RESTORED',
8
+ CANCELLED: 'CANCELLED',
9
+ };
10
+ const RevenueCatUI = {
11
+ PAYWALL_RESULT: exports.PAYWALL_RESULT,
12
+ presentPaywall: jest.fn(),
13
+ presentPaywallIfNeeded: jest.fn(),
14
+ Paywall: 'Paywall',
15
+ };
16
+ exports.default = RevenueCatUI;
@@ -0,0 +1,22 @@
1
+ export declare const LOG_LEVEL: {
2
+ VERBOSE: string;
3
+ DEBUG: string;
4
+ INFO: string;
5
+ WARN: string;
6
+ ERROR: string;
7
+ };
8
+ declare const Purchases: {
9
+ setLogLevel: jest.Mock<any, any, any>;
10
+ configure: jest.Mock<any, any, any>;
11
+ getOfferings: jest.Mock<any, any, any>;
12
+ getCustomerInfo: jest.Mock<any, any, any>;
13
+ purchasePackage: jest.Mock<any, any, any>;
14
+ restorePurchases: jest.Mock<any, any, any>;
15
+ syncPurchases: jest.Mock<any, any, any>;
16
+ logIn: jest.Mock<any, any, any>;
17
+ logOut: jest.Mock<any, any, any>;
18
+ addCustomerInfoUpdateListener: jest.Mock<any, any, any>;
19
+ removeCustomerInfoUpdateListener: jest.Mock<any, any, any>;
20
+ };
21
+ export default Purchases;
22
+ //# sourceMappingURL=react-native-purchases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-native-purchases.d.ts","sourceRoot":"","sources":["../../src/__mocks__/react-native-purchases.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,SAAS;;;;;;CAMrB,CAAC;AAEF,QAAA,MAAM,SAAS;;;;;;;;;;;;CAYd,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LOG_LEVEL = void 0;
4
+ // Mock for react-native-purchases
5
+ exports.LOG_LEVEL = {
6
+ VERBOSE: 'VERBOSE',
7
+ DEBUG: 'DEBUG',
8
+ INFO: 'INFO',
9
+ WARN: 'WARN',
10
+ ERROR: 'ERROR',
11
+ };
12
+ const Purchases = {
13
+ setLogLevel: jest.fn(),
14
+ configure: jest.fn(),
15
+ getOfferings: jest.fn(),
16
+ getCustomerInfo: jest.fn(),
17
+ purchasePackage: jest.fn(),
18
+ restorePurchases: jest.fn(),
19
+ syncPurchases: jest.fn(),
20
+ logIn: jest.fn(),
21
+ logOut: jest.fn(),
22
+ addCustomerInfoUpdateListener: jest.fn(),
23
+ removeCustomerInfoUpdateListener: jest.fn(),
24
+ };
25
+ exports.default = Purchases;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { PaywallCallbacks } from '../paywall/types';
3
+ type PaywallProps = PaywallCallbacks;
4
+ /**
5
+ * RevenueCat Paywall UI Component
6
+ * Official docs: https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls
7
+ *
8
+ * From docs: "Available listeners: onPurchaseStarted, onPurchaseCompleted,
9
+ * onPurchaseError, onPurchaseCancelled, onRestoreCompleted, onRestoreError, onDismiss"
10
+ */
11
+ export declare function Paywall({ onPurchaseStarted, onPurchaseCompleted, onPurchaseError, onPurchaseCancelled, onRestoreCompleted, onRestoreError, onDismiss, }: PaywallProps): React.JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=Paywall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Paywall.d.ts","sourceRoot":"","sources":["../../src/components/Paywall.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,KAAK,YAAY,GAAG,gBAAgB,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,SAAS,GACV,EAAE,YAAY,qBAYd"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Paywall = Paywall;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_purchases_ui_1 = __importDefault(require("react-native-purchases-ui"));
9
+ /**
10
+ * RevenueCat Paywall UI Component
11
+ * Official docs: https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls
12
+ *
13
+ * From docs: "Available listeners: onPurchaseStarted, onPurchaseCompleted,
14
+ * onPurchaseError, onPurchaseCancelled, onRestoreCompleted, onRestoreError, onDismiss"
15
+ */
16
+ function Paywall({ onPurchaseStarted, onPurchaseCompleted, onPurchaseError, onPurchaseCancelled, onRestoreCompleted, onRestoreError, onDismiss, }) {
17
+ return (<react_native_purchases_ui_1.default.Paywall onPurchaseStarted={onPurchaseStarted} onPurchaseCompleted={onPurchaseCompleted} onPurchaseError={onPurchaseError} onPurchaseCancelled={onPurchaseCancelled} onRestoreCompleted={onRestoreCompleted} onRestoreError={onRestoreError} onDismiss={onDismiss}/>);
18
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ interface PaywallFooterProps {
3
+ termsOfServiceUrl?: string;
4
+ privacyPolicyUrl?: string;
5
+ onRestorePress?: () => void;
6
+ }
7
+ /**
8
+ * Paywall footer with legal links and restore button
9
+ * Required for App Store compliance
10
+ */
11
+ export declare function PaywallFooter({ termsOfServiceUrl, privacyPolicyUrl, onRestorePress, }: PaywallFooterProps): React.JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=PaywallFooter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaywallFooter.d.ts","sourceRoot":"","sources":["../../src/components/PaywallFooter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,kBAAkB;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,EAAE,kBAAkB,qBAkCpB"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PaywallFooter = PaywallFooter;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ /**
10
+ * Paywall footer with legal links and restore button
11
+ * Required for App Store compliance
12
+ */
13
+ function PaywallFooter({ termsOfServiceUrl, privacyPolicyUrl, onRestorePress, }) {
14
+ const openUrl = (url) => {
15
+ react_native_1.Linking.openURL(url).catch(err => console.error('Failed to open URL:', err));
16
+ };
17
+ return (<react_native_1.View style={styles.container}>
18
+ {onRestorePress && (<react_native_1.Pressable onPress={onRestorePress} style={styles.restoreButton}>
19
+ <react_native_1.Text style={styles.restoreText}>Restore Purchases</react_native_1.Text>
20
+ </react_native_1.Pressable>)}
21
+
22
+ <react_native_1.View style={styles.linksContainer}>
23
+ {termsOfServiceUrl && (<react_native_1.Pressable onPress={() => openUrl(termsOfServiceUrl)}>
24
+ <react_native_1.Text style={styles.linkText}>Terms of Service</react_native_1.Text>
25
+ </react_native_1.Pressable>)}
26
+
27
+ {termsOfServiceUrl && privacyPolicyUrl && (<react_native_1.Text style={styles.separator}> • </react_native_1.Text>)}
28
+
29
+ {privacyPolicyUrl && (<react_native_1.Pressable onPress={() => openUrl(privacyPolicyUrl)}>
30
+ <react_native_1.Text style={styles.linkText}>Privacy Policy</react_native_1.Text>
31
+ </react_native_1.Pressable>)}
32
+ </react_native_1.View>
33
+ </react_native_1.View>);
34
+ }
35
+ const styles = react_native_1.StyleSheet.create({
36
+ container: {
37
+ padding: 16,
38
+ alignItems: 'center',
39
+ },
40
+ restoreButton: {
41
+ paddingVertical: 8,
42
+ marginBottom: 12,
43
+ },
44
+ restoreText: {
45
+ color: '#007AFF',
46
+ fontSize: 14,
47
+ },
48
+ linksContainer: {
49
+ flexDirection: 'row',
50
+ alignItems: 'center',
51
+ },
52
+ linkText: {
53
+ color: '#666',
54
+ fontSize: 12,
55
+ },
56
+ separator: {
57
+ color: '#666',
58
+ fontSize: 12,
59
+ },
60
+ });
@@ -0,0 +1,12 @@
1
+ import React, { type ReactNode } from 'react';
2
+ interface PaywallGateProps {
3
+ entitlementId: string;
4
+ children: ReactNode;
5
+ onAccessGranted?: () => void;
6
+ }
7
+ /**
8
+ * Feature gate that shows paywall modal when user lacks entitlement
9
+ */
10
+ export declare function PaywallGate({ entitlementId, children, onAccessGranted, }: PaywallGateProps): React.JSX.Element | null;
11
+ export {};
12
+ //# sourceMappingURL=PaywallGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaywallGate.d.ts","sourceRoot":"","sources":["../../src/components/PaywallGate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAKxD,UAAU,gBAAgB;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,aAAa,EACb,QAAQ,EACR,eAAe,GAChB,EAAE,gBAAgB,4BAmClB"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PaywallGate = PaywallGate;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const useEntitlement_1 = require("../hooks/useEntitlement");
40
+ const Paywall_1 = require("./Paywall");
41
+ /**
42
+ * Feature gate that shows paywall modal when user lacks entitlement
43
+ */
44
+ function PaywallGate({ entitlementId, children, onAccessGranted, }) {
45
+ const { hasEntitlement, isLoading } = (0, useEntitlement_1.useEntitlement)(entitlementId);
46
+ const [showPaywall, setShowPaywall] = (0, react_1.useState)(false);
47
+ if (isLoading) {
48
+ return null;
49
+ }
50
+ if (!hasEntitlement) {
51
+ return (<>
52
+ <react_native_1.Modal visible={showPaywall} animationType="slide" presentationStyle="pageSheet" onRequestClose={() => setShowPaywall(false)}>
53
+ <Paywall_1.Paywall onPurchaseCompleted={() => {
54
+ setShowPaywall(false);
55
+ onAccessGranted?.();
56
+ }} onDismiss={() => setShowPaywall(false)}/>
57
+ </react_native_1.Modal>
58
+
59
+ <react_native_1.View>
60
+ {children}
61
+ <react_native_1.Button title="Unlock Feature" onPress={() => setShowPaywall(true)}/>
62
+ </react_native_1.View>
63
+ </>);
64
+ }
65
+ return <>{children}</>;
66
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { type ViewStyle } from 'react-native';
3
+ import type { PurchasesPackage } from 'react-native-purchases';
4
+ interface PurchaseButtonProps {
5
+ package: PurchasesPackage;
6
+ onSuccess?: () => void;
7
+ onError?: (error: Error) => void;
8
+ title?: string;
9
+ style?: ViewStyle;
10
+ }
11
+ export declare function PurchaseButton({ package: pkg, onSuccess, onError, title, style, }: PurchaseButtonProps): React.JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=PurchaseButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PurchaseButton.d.ts","sourceRoot":"","sources":["../../src/components/PurchaseButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK/D,UAAU,mBAAmB;IAC3B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EAAE,GAAG,EACZ,SAAS,EACT,OAAO,EACP,KAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,qBA+BrB"}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PurchaseButton = PurchaseButton;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ const usePurchase_1 = require("../hooks/usePurchase");
10
+ const errorHandling_1 = require("../purchase/errorHandling");
11
+ function PurchaseButton({ package: pkg, onSuccess, onError, title = 'Subscribe', style, }) {
12
+ const { purchase, isPurchasing } = (0, usePurchase_1.usePurchase)();
13
+ const handlePurchase = async () => {
14
+ try {
15
+ await purchase(pkg);
16
+ onSuccess?.();
17
+ }
18
+ catch (err) {
19
+ const purchaseError = err instanceof Error
20
+ ? { message: err.message, userCancelled: false }
21
+ : { message: 'Purchase failed', userCancelled: false };
22
+ const errorMessage = (0, errorHandling_1.getUserFriendlyErrorMessage)(purchaseError);
23
+ react_native_1.Alert.alert('Purchase Failed', errorMessage);
24
+ onError?.(err instanceof Error ? err : new Error('Purchase failed'));
25
+ }
26
+ };
27
+ return (<react_native_1.Pressable onPress={handlePurchase} disabled={isPurchasing} style={style}>
28
+ {isPurchasing ? (<react_native_1.ActivityIndicator />) : (<react_native_1.Text>{title}</react_native_1.Text>)}
29
+ </react_native_1.Pressable>);
30
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { type ViewStyle } from 'react-native';
3
+ interface RestoreButtonProps {
4
+ onSuccess?: () => void;
5
+ onNoPurchases?: () => void;
6
+ onError?: (error: Error) => void;
7
+ title?: string;
8
+ style?: ViewStyle;
9
+ }
10
+ /**
11
+ * Restore Purchases button component
12
+ * Required by Apple App Store Review Guideline 3.1.1
13
+ */
14
+ export declare function RestoreButton({ onSuccess, onNoPurchases, onError, title, style, }: RestoreButtonProps): React.JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=RestoreButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestoreButton.d.ts","sourceRoot":"","sources":["../../src/components/RestoreButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzF,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,aAAa,EACb,OAAO,EACP,KAA2B,EAC3B,KAAK,GACN,EAAE,kBAAkB,qBA4CpB"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RestoreButton = RestoreButton;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ const useRestorePurchases_1 = require("../hooks/useRestorePurchases");
10
+ /**
11
+ * Restore Purchases button component
12
+ * Required by Apple App Store Review Guideline 3.1.1
13
+ */
14
+ function RestoreButton({ onSuccess, onNoPurchases, onError, title = 'Restore Purchases', style, }) {
15
+ const { restore, isRestoring, isSuccess, noPurchases } = (0, useRestorePurchases_1.useRestorePurchases)();
16
+ const handleRestore = async () => {
17
+ try {
18
+ await restore();
19
+ if (isSuccess) {
20
+ react_native_1.Alert.alert('Success', 'Your purchases have been restored!', [{ text: 'OK', onPress: onSuccess }]);
21
+ }
22
+ else if (noPurchases) {
23
+ react_native_1.Alert.alert('No Purchases Found', "We couldn't find any previous purchases associated with your account.", [{ text: 'OK', onPress: onNoPurchases }]);
24
+ }
25
+ }
26
+ catch (err) {
27
+ const error = err instanceof Error ? err : new Error('Restore failed');
28
+ react_native_1.Alert.alert('Restore Failed', error.message || 'Failed to restore purchases. Please try again.', [{ text: 'OK' }]);
29
+ onError?.(error);
30
+ }
31
+ };
32
+ return (<react_native_1.Pressable onPress={handleRestore} disabled={isRestoring} style={style}>
33
+ {isRestoring ? (<react_native_1.ActivityIndicator />) : (<react_native_1.Text>{title}</react_native_1.Text>)}
34
+ </react_native_1.Pressable>);
35
+ }
@@ -0,0 +1,14 @@
1
+ import React, { type ReactNode } from 'react';
2
+ interface SubscriptionGateProps {
3
+ entitlementId: string;
4
+ children: ReactNode;
5
+ fallback?: ReactNode;
6
+ loadingFallback?: ReactNode;
7
+ }
8
+ /**
9
+ * Render children only if user has the specified entitlement
10
+ * Official pattern: check entitlement before rendering premium content
11
+ */
12
+ export declare function SubscriptionGate({ entitlementId, children, fallback, loadingFallback, }: SubscriptionGateProps): React.JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=SubscriptionGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubscriptionGate.d.ts","sourceRoot":"","sources":["../../src/components/SubscriptionGate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9C,UAAU,qBAAqB;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,EACb,QAAQ,EACR,QAAe,EACf,eAAsB,GACvB,EAAE,qBAAqB,qBAYvB"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SubscriptionGate = SubscriptionGate;
7
+ const react_1 = __importDefault(require("react"));
8
+ const useEntitlement_1 = require("../hooks/useEntitlement");
9
+ /**
10
+ * Render children only if user has the specified entitlement
11
+ * Official pattern: check entitlement before rendering premium content
12
+ */
13
+ function SubscriptionGate({ entitlementId, children, fallback = null, loadingFallback = null, }) {
14
+ const { hasEntitlement, isLoading } = (0, useEntitlement_1.useEntitlement)(entitlementId);
15
+ if (isLoading) {
16
+ return <>{loadingFallback}</>;
17
+ }
18
+ if (!hasEntitlement) {
19
+ return <>{fallback}</>;
20
+ }
21
+ return <>{children}</>;
22
+ }
@@ -0,0 +1,4 @@
1
+ import type { PlatformConfig } from './types';
2
+ export declare function initializeRevenueCat(config: PlatformConfig, appUserID?: string): Promise<void>;
3
+ export declare function isRevenueCatConfigured(): boolean;
4
+ //# sourceMappingURL=RevenueCatConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RevenueCatConfig.d.ts","sourceRoot":"","sources":["../../src/config/RevenueCatConfig.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,cAAc,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initializeRevenueCat = initializeRevenueCat;
37
+ exports.isRevenueCatConfigured = isRevenueCatConfigured;
38
+ const react_native_1 = require("react-native");
39
+ const react_native_purchases_1 = __importStar(require("react-native-purchases"));
40
+ let isConfigured = false;
41
+ // Initialize RevenueCat SDK once per app lifecycle
42
+ async function initializeRevenueCat(config, appUserID) {
43
+ if (isConfigured) {
44
+ console.warn('[RevenueCat] Already initialized');
45
+ return;
46
+ }
47
+ // Enable debug logs in development
48
+ if (__DEV__) {
49
+ react_native_purchases_1.default.setLogLevel(react_native_purchases_1.LOG_LEVEL.DEBUG);
50
+ }
51
+ // Configure with platform-specific API key
52
+ const apiKey = react_native_1.Platform.OS === 'ios' ? config.iosApiKey : config.androidApiKey;
53
+ await react_native_purchases_1.default.configure({
54
+ apiKey,
55
+ appUserID, // Optional: custom user ID or RevenueCat generates anonymous ID
56
+ });
57
+ isConfigured = true;
58
+ }
59
+ function isRevenueCatConfigured() {
60
+ return isConfigured;
61
+ }
@@ -0,0 +1,3 @@
1
+ import type { PlatformConfig } from './types';
2
+ export declare function getRevenueCatConfig(): PlatformConfig;
3
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/config/environment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,SAAS,CAAC;AAMjE,wBAAgB,mBAAmB,IAAI,cAAc,CAepD"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRevenueCatConfig = getRevenueCatConfig;
4
+ // Load RevenueCat config from environment variables (Expo: EXPO_PUBLIC_ prefix)
5
+ function getRevenueCatConfig() {
6
+ const globalObj = globalThis;
7
+ const iosApiKey = globalObj.process?.env?.EXPO_PUBLIC_REVENUECAT_IOS || '';
8
+ const androidApiKey = globalObj.process?.env?.EXPO_PUBLIC_REVENUECAT_ANDROID || '';
9
+ if (!iosApiKey || !androidApiKey) {
10
+ throw new Error('[RevenueCat] Missing API keys. Set EXPO_PUBLIC_REVENUECAT_IOS and EXPO_PUBLIC_REVENUECAT_ANDROID in .env');
11
+ }
12
+ return {
13
+ iosApiKey,
14
+ androidApiKey,
15
+ };
16
+ }