@byarcadia-app/plutus 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dominik Woźniak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ <h1 align="center">@byarcadia-app/plutus</h1>
2
+
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/@byarcadia-app/plutus"><img src="https://img.shields.io/npm/v/@byarcadia-app/plutus?style=flat" alt="npm version" /></a>&nbsp;<a href="https://github.com/byarcadia-app/plutus/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@byarcadia-app/plutus?style=flat" alt="License" /></a>&nbsp;<img src="https://img.shields.io/badge/platform-iOS-black?style=flat" alt="iOS only" />&nbsp;<img src="https://img.shields.io/badge/status-alpha-orange?style=flat" alt="Alpha" />
5
+ </p>
6
+
7
+ <p align="center">
8
+ Reusable <a href="https://www.revenuecat.com/">RevenueCat</a> wrapper for <a href="https://reactnative.dev/">React Native</a> in-app purchases — configurable, callback-driven, zero app-specific dependencies.
9
+ </p>
10
+
11
+ Named after the Greek god of wealth — Plutus wraps RevenueCat so you can set up in-app purchases once and reuse across projects.
12
+
13
+ ## What it does
14
+
15
+ - **[PlutusProvider](docs/provider.md)** — RevenueCat SDK initialization, customer info listener, entitlement state management
16
+ - **[usePlutus](docs/hooks.md#useplutus)** — Access `isPro`, `isInTrial`, `isReady`, `managementURL`, `purchasePackage`, `restorePurchases`
17
+ - **[useOfferings](docs/hooks.md#useofferings)** — Load offerings with configurable identifiers, per-package trial detection, computed discount percentages
18
+ - **[usePaywall](docs/hooks.md#usepaywall)** — Purchase flow orchestration with subscription type selection, configurable callbacks
19
+ - **[useRescuePaywall](docs/hooks.md#userescuepaywall)** — Rescue/discount offer purchase flow
20
+ - **[Translations](docs/translations.md)** — English fallback strings, override with `Partial<PlutusTranslations>`
21
+ - **[Error handling](docs/errors.md)** — Typed error objects with error codes and factory functions
22
+ - **Callback-driven** — All app-specific concerns (analytics, alerts, navigation, haptics) via callbacks, not baked in
23
+
24
+ ## Documentation
25
+
26
+ - [Installation & Quick Start](docs/installation.md)
27
+ - [PlutusProvider](docs/provider.md)
28
+ - [Hooks](docs/hooks.md)
29
+ - [Errors](docs/errors.md)
30
+ - [Translations](docs/translations.md)
31
+
32
+ ## Example App
33
+
34
+ A complete working integration lives in `example/`. It demonstrates provider setup and all hook usage patterns.
35
+
36
+ ```bash
37
+ pnpm example:start # Start Expo dev server
38
+ pnpm example:ios # Run on iOS simulator
39
+ ```
40
+
41
+ ## Requirements
42
+
43
+ | Dependency | Version |
44
+ | ---------------------- | ------- |
45
+ | React | >= 18 |
46
+ | React Native | >= 0.72 |
47
+ | react-native-purchases | >= 9 |
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,126 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { PurchasesPackage, LOG_LEVEL, CustomerInfo } from 'react-native-purchases';
3
+
4
+ type PlutusErrorCode = "INIT_FAILED" | "PURCHASE_FAILED" | "OFFERINGS_FAILED" | "RESTORE_FAILED";
5
+ interface PlutusError {
6
+ readonly code: PlutusErrorCode;
7
+ readonly message: string;
8
+ readonly cause?: unknown;
9
+ readonly package?: PurchasesPackage;
10
+ }
11
+ declare const errors: {
12
+ INIT_FAILED: (cause: unknown) => PlutusError;
13
+ PURCHASE_FAILED: (cause: unknown, pack?: PurchasesPackage) => PlutusError;
14
+ OFFERINGS_FAILED: (cause: unknown) => PlutusError;
15
+ RESTORE_FAILED: (cause: unknown) => PlutusError;
16
+ };
17
+
18
+ declare const defaultTranslations: {
19
+ purchaseError: {
20
+ title: string;
21
+ message: string;
22
+ };
23
+ restoreError: {
24
+ title: string;
25
+ message: string;
26
+ };
27
+ };
28
+ type PlutusTranslations = typeof defaultTranslations;
29
+
30
+ interface PlutusConfig {
31
+ apiKey: string;
32
+ entitlementName: string;
33
+ logLevel?: LOG_LEVEL;
34
+ offerings?: {
35
+ default?: string;
36
+ rescue?: string;
37
+ };
38
+ callbacks?: {
39
+ onError?: (error: PlutusError) => void;
40
+ onCustomerInfoUpdated?: (customerInfo: CustomerInfo, state: {
41
+ isPro: boolean;
42
+ isInTrial: boolean;
43
+ }) => void;
44
+ onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;
45
+ };
46
+ translations?: Partial<PlutusTranslations>;
47
+ }
48
+
49
+ interface PlutusContextValue {
50
+ isPro: boolean;
51
+ isInTrial: boolean;
52
+ isReady: boolean;
53
+ managementURL: string | null;
54
+ purchasePackage: (pack: PurchasesPackage) => Promise<boolean | undefined>;
55
+ restorePurchases: () => Promise<boolean>;
56
+ translations: typeof defaultTranslations;
57
+ onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;
58
+ onError?: (error: PlutusError) => void;
59
+ offeringsConfig: {
60
+ default: string;
61
+ rescue: string;
62
+ };
63
+ }
64
+ interface PlutusProviderProps extends PlutusConfig {
65
+ children: React.ReactNode;
66
+ }
67
+ declare const PlutusProvider: ({ children, apiKey, entitlementName, logLevel, offerings, callbacks, translations: translationOverrides, }: PlutusProviderProps) => react_jsx_runtime.JSX.Element;
68
+
69
+ declare const usePlutus: () => PlutusContextValue;
70
+
71
+ interface UseOfferingsOptions {
72
+ refetchKey?: string | number;
73
+ }
74
+ declare const useOfferings: (options?: UseOfferingsOptions) => {
75
+ isLoading: boolean;
76
+ monthlyOffer: PurchasesPackage | undefined;
77
+ annualOffer: PurchasesPackage | undefined;
78
+ rescueOffer: PurchasesPackage | undefined;
79
+ monthlyHasTrial: boolean;
80
+ annualHasTrial: boolean;
81
+ annualDiscountPercentage: number | undefined;
82
+ rescueOffsetDiscountPercentage: number | undefined;
83
+ };
84
+
85
+ interface UsePaywallOptions {
86
+ monthlyOffer?: PurchasesPackage;
87
+ annualOffer?: PurchasesPackage;
88
+ onClose?: () => void;
89
+ onPurchaseSuccess?: (subscriptionType: "monthly" | "annual") => void;
90
+ onPurchaseFailed?: () => void;
91
+ onRestoreSuccess?: () => void;
92
+ onRestoreFailed?: () => void;
93
+ termsUrl?: string;
94
+ privacyUrl?: string;
95
+ }
96
+ declare const usePaywall: ({ monthlyOffer, annualOffer, onClose, onPurchaseSuccess, onPurchaseFailed, onRestoreSuccess, onRestoreFailed, termsUrl, privacyUrl, }: UsePaywallOptions) => {
97
+ subscriptionType: "monthly" | "annual";
98
+ isPurchasing: boolean;
99
+ handleSubscriptionTypeChange: (type: "monthly" | "annual") => void;
100
+ handleTermsPress: () => void;
101
+ handlePrivacyPress: () => void;
102
+ handleClosePress: () => void;
103
+ handleRestorePurchases: () => Promise<void>;
104
+ handlePurchasePackage: () => Promise<void>;
105
+ };
106
+
107
+ interface UseRescuePaywallOptions {
108
+ rescueOffer?: PurchasesPackage;
109
+ onClose?: () => void;
110
+ onPurchaseSuccess?: () => void;
111
+ onPurchaseFailed?: () => void;
112
+ onRestoreSuccess?: () => void;
113
+ onRestoreFailed?: () => void;
114
+ termsUrl?: string;
115
+ privacyUrl?: string;
116
+ }
117
+ declare const useRescuePaywall: ({ rescueOffer, onClose, onPurchaseSuccess, onPurchaseFailed, onRestoreSuccess, onRestoreFailed, termsUrl, privacyUrl, }: UseRescuePaywallOptions) => {
118
+ isPurchasing: boolean;
119
+ handleTermsPress: () => void;
120
+ handlePrivacyPress: () => void;
121
+ handleClosePress: () => void;
122
+ handleRestorePurchases: () => Promise<void>;
123
+ handlePurchasePackage: () => Promise<void>;
124
+ };
125
+
126
+ export { type PlutusConfig, type PlutusError, type PlutusErrorCode, PlutusProvider, type PlutusTranslations, errors, useOfferings, usePaywall, usePlutus, useRescuePaywall };
@@ -0,0 +1,126 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { PurchasesPackage, LOG_LEVEL, CustomerInfo } from 'react-native-purchases';
3
+
4
+ type PlutusErrorCode = "INIT_FAILED" | "PURCHASE_FAILED" | "OFFERINGS_FAILED" | "RESTORE_FAILED";
5
+ interface PlutusError {
6
+ readonly code: PlutusErrorCode;
7
+ readonly message: string;
8
+ readonly cause?: unknown;
9
+ readonly package?: PurchasesPackage;
10
+ }
11
+ declare const errors: {
12
+ INIT_FAILED: (cause: unknown) => PlutusError;
13
+ PURCHASE_FAILED: (cause: unknown, pack?: PurchasesPackage) => PlutusError;
14
+ OFFERINGS_FAILED: (cause: unknown) => PlutusError;
15
+ RESTORE_FAILED: (cause: unknown) => PlutusError;
16
+ };
17
+
18
+ declare const defaultTranslations: {
19
+ purchaseError: {
20
+ title: string;
21
+ message: string;
22
+ };
23
+ restoreError: {
24
+ title: string;
25
+ message: string;
26
+ };
27
+ };
28
+ type PlutusTranslations = typeof defaultTranslations;
29
+
30
+ interface PlutusConfig {
31
+ apiKey: string;
32
+ entitlementName: string;
33
+ logLevel?: LOG_LEVEL;
34
+ offerings?: {
35
+ default?: string;
36
+ rescue?: string;
37
+ };
38
+ callbacks?: {
39
+ onError?: (error: PlutusError) => void;
40
+ onCustomerInfoUpdated?: (customerInfo: CustomerInfo, state: {
41
+ isPro: boolean;
42
+ isInTrial: boolean;
43
+ }) => void;
44
+ onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;
45
+ };
46
+ translations?: Partial<PlutusTranslations>;
47
+ }
48
+
49
+ interface PlutusContextValue {
50
+ isPro: boolean;
51
+ isInTrial: boolean;
52
+ isReady: boolean;
53
+ managementURL: string | null;
54
+ purchasePackage: (pack: PurchasesPackage) => Promise<boolean | undefined>;
55
+ restorePurchases: () => Promise<boolean>;
56
+ translations: typeof defaultTranslations;
57
+ onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;
58
+ onError?: (error: PlutusError) => void;
59
+ offeringsConfig: {
60
+ default: string;
61
+ rescue: string;
62
+ };
63
+ }
64
+ interface PlutusProviderProps extends PlutusConfig {
65
+ children: React.ReactNode;
66
+ }
67
+ declare const PlutusProvider: ({ children, apiKey, entitlementName, logLevel, offerings, callbacks, translations: translationOverrides, }: PlutusProviderProps) => react_jsx_runtime.JSX.Element;
68
+
69
+ declare const usePlutus: () => PlutusContextValue;
70
+
71
+ interface UseOfferingsOptions {
72
+ refetchKey?: string | number;
73
+ }
74
+ declare const useOfferings: (options?: UseOfferingsOptions) => {
75
+ isLoading: boolean;
76
+ monthlyOffer: PurchasesPackage | undefined;
77
+ annualOffer: PurchasesPackage | undefined;
78
+ rescueOffer: PurchasesPackage | undefined;
79
+ monthlyHasTrial: boolean;
80
+ annualHasTrial: boolean;
81
+ annualDiscountPercentage: number | undefined;
82
+ rescueOffsetDiscountPercentage: number | undefined;
83
+ };
84
+
85
+ interface UsePaywallOptions {
86
+ monthlyOffer?: PurchasesPackage;
87
+ annualOffer?: PurchasesPackage;
88
+ onClose?: () => void;
89
+ onPurchaseSuccess?: (subscriptionType: "monthly" | "annual") => void;
90
+ onPurchaseFailed?: () => void;
91
+ onRestoreSuccess?: () => void;
92
+ onRestoreFailed?: () => void;
93
+ termsUrl?: string;
94
+ privacyUrl?: string;
95
+ }
96
+ declare const usePaywall: ({ monthlyOffer, annualOffer, onClose, onPurchaseSuccess, onPurchaseFailed, onRestoreSuccess, onRestoreFailed, termsUrl, privacyUrl, }: UsePaywallOptions) => {
97
+ subscriptionType: "monthly" | "annual";
98
+ isPurchasing: boolean;
99
+ handleSubscriptionTypeChange: (type: "monthly" | "annual") => void;
100
+ handleTermsPress: () => void;
101
+ handlePrivacyPress: () => void;
102
+ handleClosePress: () => void;
103
+ handleRestorePurchases: () => Promise<void>;
104
+ handlePurchasePackage: () => Promise<void>;
105
+ };
106
+
107
+ interface UseRescuePaywallOptions {
108
+ rescueOffer?: PurchasesPackage;
109
+ onClose?: () => void;
110
+ onPurchaseSuccess?: () => void;
111
+ onPurchaseFailed?: () => void;
112
+ onRestoreSuccess?: () => void;
113
+ onRestoreFailed?: () => void;
114
+ termsUrl?: string;
115
+ privacyUrl?: string;
116
+ }
117
+ declare const useRescuePaywall: ({ rescueOffer, onClose, onPurchaseSuccess, onPurchaseFailed, onRestoreSuccess, onRestoreFailed, termsUrl, privacyUrl, }: UseRescuePaywallOptions) => {
118
+ isPurchasing: boolean;
119
+ handleTermsPress: () => void;
120
+ handlePrivacyPress: () => void;
121
+ handleClosePress: () => void;
122
+ handleRestorePurchases: () => Promise<void>;
123
+ handlePurchasePackage: () => Promise<void>;
124
+ };
125
+
126
+ export { type PlutusConfig, type PlutusError, type PlutusErrorCode, PlutusProvider, type PlutusTranslations, errors, useOfferings, usePaywall, usePlutus, useRescuePaywall };
package/dist/index.js ADDED
@@ -0,0 +1,424 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var Purchases = require('react-native-purchases');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var reactNative = require('react-native');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var Purchases__default = /*#__PURE__*/_interopDefault(Purchases);
11
+
12
+ // src/provider/plutus-provider.tsx
13
+
14
+ // src/errors.ts
15
+ var errors = {
16
+ INIT_FAILED: (cause) => ({
17
+ code: "INIT_FAILED",
18
+ message: "Initialization failed",
19
+ cause
20
+ }),
21
+ PURCHASE_FAILED: (cause, pack) => ({
22
+ code: "PURCHASE_FAILED",
23
+ message: "Purchase failed",
24
+ cause,
25
+ package: pack
26
+ }),
27
+ OFFERINGS_FAILED: (cause) => ({
28
+ code: "OFFERINGS_FAILED",
29
+ message: "Failed to load offerings",
30
+ cause
31
+ }),
32
+ RESTORE_FAILED: (cause) => ({
33
+ code: "RESTORE_FAILED",
34
+ message: "Restore purchases failed",
35
+ cause
36
+ })
37
+ };
38
+
39
+ // src/translations.ts
40
+ var defaultTranslations = {
41
+ purchaseError: {
42
+ title: "Purchase Error",
43
+ message: "There was a problem processing your purchase. Please try again."
44
+ },
45
+ restoreError: {
46
+ title: "Restore Error",
47
+ message: "There was a problem restoring your purchases. Please try again."
48
+ }
49
+ };
50
+ var PlutusContext = react.createContext(null);
51
+ var PlutusProvider = ({
52
+ children,
53
+ apiKey,
54
+ entitlementName,
55
+ logLevel,
56
+ offerings,
57
+ callbacks,
58
+ translations: translationOverrides
59
+ }) => {
60
+ const [isReady, setIsReady] = react.useState(false);
61
+ const [isPro, setIsPro] = react.useState(false);
62
+ const [isInTrial, setIsInTrial] = react.useState(false);
63
+ const [managementURL, setManagementURL] = react.useState(null);
64
+ const translations = react.useMemo(
65
+ () => ({
66
+ ...defaultTranslations,
67
+ ...translationOverrides,
68
+ purchaseError: {
69
+ ...defaultTranslations.purchaseError,
70
+ ...translationOverrides?.purchaseError
71
+ },
72
+ restoreError: {
73
+ ...defaultTranslations.restoreError,
74
+ ...translationOverrides?.restoreError
75
+ }
76
+ }),
77
+ [translationOverrides]
78
+ );
79
+ const offeringsConfig = react.useMemo(
80
+ () => ({
81
+ default: offerings?.default ?? "default",
82
+ rescue: offerings?.rescue ?? "rescue"
83
+ }),
84
+ [offerings?.default, offerings?.rescue]
85
+ );
86
+ const updateCustomerInformation = react.useCallback(
87
+ (customerInfo) => {
88
+ const entitlement = customerInfo?.entitlements.active?.[entitlementName];
89
+ const newIsPro = entitlement !== void 0;
90
+ const newIsInTrial = entitlement?.periodType === "TRIAL";
91
+ setIsPro(newIsPro);
92
+ setIsInTrial(newIsInTrial);
93
+ setManagementURL(customerInfo.managementURL);
94
+ callbacks?.onCustomerInfoUpdated?.(customerInfo, {
95
+ isPro: newIsPro,
96
+ isInTrial: newIsInTrial
97
+ });
98
+ },
99
+ [entitlementName, callbacks]
100
+ );
101
+ react.useEffect(() => {
102
+ const customerInfoUpdateListener = (info) => {
103
+ updateCustomerInformation(info);
104
+ };
105
+ const init = async () => {
106
+ try {
107
+ await Purchases__default.default.setLogLevel(logLevel ?? Purchases.LOG_LEVEL.ERROR);
108
+ Purchases__default.default.configure({ apiKey });
109
+ Purchases__default.default.addCustomerInfoUpdateListener(customerInfoUpdateListener);
110
+ setIsReady(true);
111
+ } catch (error) {
112
+ callbacks?.onError?.(errors.INIT_FAILED(error));
113
+ }
114
+ };
115
+ init();
116
+ return () => {
117
+ Purchases__default.default.removeCustomerInfoUpdateListener(customerInfoUpdateListener);
118
+ };
119
+ }, [apiKey, logLevel, updateCustomerInformation, callbacks]);
120
+ const purchasePackage = react.useCallback(
121
+ async (pack) => {
122
+ try {
123
+ const result = await Purchases__default.default.purchasePackage(pack);
124
+ updateCustomerInformation(result.customerInfo);
125
+ return result.customerInfo.entitlements.active?.[entitlementName] !== void 0;
126
+ } catch (error) {
127
+ const errorCode = error?.code;
128
+ if (errorCode === Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
129
+ return void 0;
130
+ }
131
+ callbacks?.onError?.(errors.PURCHASE_FAILED(error, pack));
132
+ return void 0;
133
+ }
134
+ },
135
+ [entitlementName, updateCustomerInformation, callbacks]
136
+ );
137
+ const restorePurchases = react.useCallback(async () => {
138
+ try {
139
+ const customerInfo = await Purchases__default.default.restorePurchases();
140
+ updateCustomerInformation(customerInfo);
141
+ return customerInfo.entitlements.active?.[entitlementName] !== void 0;
142
+ } catch (error) {
143
+ callbacks?.onError?.(errors.RESTORE_FAILED(error));
144
+ return false;
145
+ }
146
+ }, [entitlementName, updateCustomerInformation, callbacks]);
147
+ const value = react.useMemo(
148
+ () => ({
149
+ isPro,
150
+ isInTrial,
151
+ isReady,
152
+ managementURL,
153
+ purchasePackage,
154
+ restorePurchases,
155
+ translations,
156
+ onTrackEvent: callbacks?.onTrackEvent,
157
+ onError: callbacks?.onError,
158
+ offeringsConfig
159
+ }),
160
+ [
161
+ isPro,
162
+ isInTrial,
163
+ isReady,
164
+ managementURL,
165
+ purchasePackage,
166
+ restorePurchases,
167
+ translations,
168
+ callbacks?.onTrackEvent,
169
+ callbacks?.onError,
170
+ offeringsConfig
171
+ ]
172
+ );
173
+ return /* @__PURE__ */ jsxRuntime.jsx(PlutusContext.Provider, { value, children });
174
+ };
175
+ var usePlutus = () => {
176
+ const context = react.use(PlutusContext);
177
+ if (!context) {
178
+ throw new Error("usePlutus must be used within a PlutusProvider");
179
+ }
180
+ return context;
181
+ };
182
+ var hasFreeTrial = (offer) => {
183
+ if (!offer?.product?.introPrice) return false;
184
+ return offer.product.introPrice.price === 0;
185
+ };
186
+ var calculateAnnualDiscount = (monthlyOffer, annualOffer) => {
187
+ if (!monthlyOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {
188
+ return void 0;
189
+ }
190
+ const monthlyPricePerYear = monthlyOffer.product.pricePerYear;
191
+ const annualPricePerYear = annualOffer.product.pricePerYear;
192
+ if (monthlyPricePerYear <= annualPricePerYear) {
193
+ return void 0;
194
+ }
195
+ const savingsAmount = monthlyPricePerYear - annualPricePerYear;
196
+ const discountPercentage = savingsAmount / monthlyPricePerYear * 100;
197
+ return Math.floor(discountPercentage);
198
+ };
199
+ var calculateRescueOffsetDiscount = (rescueOffer, annualOffer) => {
200
+ if (!rescueOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {
201
+ return void 0;
202
+ }
203
+ const rescuePricePerYear = rescueOffer.product.pricePerYear;
204
+ const annualPricePerYear = annualOffer.product.pricePerYear;
205
+ if (rescuePricePerYear >= annualPricePerYear) {
206
+ return void 0;
207
+ }
208
+ const savingsAmount = annualPricePerYear - rescuePricePerYear;
209
+ const discountPercentage = savingsAmount / annualPricePerYear * 100;
210
+ return Math.floor(discountPercentage);
211
+ };
212
+ var useOfferings = (options) => {
213
+ const { isReady, offeringsConfig, onError } = usePlutus();
214
+ const [isLoading, setIsLoading] = react.useState(false);
215
+ const [monthlyOffer, setMonthlyOffer] = react.useState();
216
+ const [annualOffer, setAnnualOffer] = react.useState();
217
+ const [rescueOffer, setRescueOffer] = react.useState();
218
+ const loadOfferings = async () => {
219
+ try {
220
+ const offerings = await Purchases__default.default.getOfferings();
221
+ const defaultPackages = offerings?.all?.[offeringsConfig.default]?.availablePackages;
222
+ const rescuePackages = offerings?.all?.[offeringsConfig.rescue]?.availablePackages;
223
+ setMonthlyOffer(
224
+ defaultPackages?.find((pkg) => pkg.packageType === "MONTHLY")
225
+ );
226
+ setAnnualOffer(
227
+ defaultPackages?.find((pkg) => pkg.packageType === "ANNUAL")
228
+ );
229
+ setRescueOffer(rescuePackages?.find((pkg) => pkg.packageType === "ANNUAL"));
230
+ } catch (error) {
231
+ onError?.(errors.OFFERINGS_FAILED(error));
232
+ }
233
+ };
234
+ react.useEffect(() => {
235
+ if (isReady) {
236
+ setIsLoading(true);
237
+ loadOfferings().finally(() => setIsLoading(false));
238
+ }
239
+ }, [isReady, options?.refetchKey]);
240
+ const monthlyHasTrial = react.useMemo(() => hasFreeTrial(monthlyOffer), [monthlyOffer]);
241
+ const annualHasTrial = react.useMemo(() => hasFreeTrial(annualOffer), [annualOffer]);
242
+ const annualDiscountPercentage = react.useMemo(
243
+ () => calculateAnnualDiscount(monthlyOffer, annualOffer),
244
+ [monthlyOffer, annualOffer]
245
+ );
246
+ const rescueOffsetDiscountPercentage = react.useMemo(
247
+ () => calculateRescueOffsetDiscount(rescueOffer, annualOffer),
248
+ [rescueOffer, annualOffer]
249
+ );
250
+ return {
251
+ isLoading,
252
+ monthlyOffer,
253
+ annualOffer,
254
+ rescueOffer,
255
+ monthlyHasTrial,
256
+ annualHasTrial,
257
+ annualDiscountPercentage,
258
+ rescueOffsetDiscountPercentage
259
+ };
260
+ };
261
+ var usePaywall = ({
262
+ monthlyOffer,
263
+ annualOffer,
264
+ onClose,
265
+ onPurchaseSuccess,
266
+ onPurchaseFailed,
267
+ onRestoreSuccess,
268
+ onRestoreFailed,
269
+ termsUrl,
270
+ privacyUrl
271
+ }) => {
272
+ const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();
273
+ const [subscriptionType, setSubscriptionType] = react.useState("annual");
274
+ const [isPurchasing, setIsPurchasing] = react.useState(false);
275
+ const handleSubscriptionTypeChange = (type) => {
276
+ onTrackEvent?.("paywall_subscription_type_changed", { type });
277
+ setSubscriptionType(type);
278
+ };
279
+ const handleRestorePurchases = async () => {
280
+ onTrackEvent?.("paywall_restore_purchases");
281
+ setIsPurchasing(true);
282
+ try {
283
+ const restored = await restorePurchases();
284
+ if (restored) {
285
+ onTrackEvent?.("paywall_restore_purchases_success");
286
+ onRestoreSuccess?.();
287
+ } else {
288
+ onTrackEvent?.("paywall_restore_failed");
289
+ onRestoreFailed?.();
290
+ }
291
+ } finally {
292
+ setIsPurchasing(false);
293
+ }
294
+ };
295
+ const handlePurchasePackage = async () => {
296
+ if (!monthlyOffer || !annualOffer) return;
297
+ try {
298
+ setIsPurchasing(true);
299
+ onTrackEvent?.("paywall_purchase_package", {
300
+ type: subscriptionType,
301
+ is_rescue_offer: false
302
+ });
303
+ const selectedOffer = subscriptionType === "monthly" ? monthlyOffer : annualOffer;
304
+ const purchased = await purchasePackage(selectedOffer);
305
+ if (purchased) {
306
+ onTrackEvent?.("paywall_purchase_success", {
307
+ type: subscriptionType,
308
+ is_rescue_offer: false
309
+ });
310
+ onPurchaseSuccess?.(subscriptionType);
311
+ } else {
312
+ onTrackEvent?.("paywall_purchase_failed");
313
+ onPurchaseFailed?.();
314
+ }
315
+ } finally {
316
+ setIsPurchasing(false);
317
+ }
318
+ };
319
+ const handleClosePress = () => {
320
+ onTrackEvent?.("paywall_close_button_pressed", {
321
+ is_rescue_offer: false
322
+ });
323
+ onClose?.();
324
+ };
325
+ const handleTermsPress = () => {
326
+ onTrackEvent?.("paywall_terms_pressed");
327
+ if (termsUrl) reactNative.Linking.openURL(termsUrl);
328
+ };
329
+ const handlePrivacyPress = () => {
330
+ onTrackEvent?.("paywall_privacy_pressed");
331
+ if (privacyUrl) reactNative.Linking.openURL(privacyUrl);
332
+ };
333
+ return {
334
+ subscriptionType,
335
+ isPurchasing,
336
+ handleSubscriptionTypeChange,
337
+ handleTermsPress,
338
+ handlePrivacyPress,
339
+ handleClosePress,
340
+ handleRestorePurchases,
341
+ handlePurchasePackage
342
+ };
343
+ };
344
+ var useRescuePaywall = ({
345
+ rescueOffer,
346
+ onClose,
347
+ onPurchaseSuccess,
348
+ onPurchaseFailed,
349
+ onRestoreSuccess,
350
+ onRestoreFailed,
351
+ termsUrl,
352
+ privacyUrl
353
+ }) => {
354
+ const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();
355
+ const [isPurchasing, setIsPurchasing] = react.useState(false);
356
+ const handleRestorePurchases = async () => {
357
+ onTrackEvent?.("paywall_restore_purchases");
358
+ setIsPurchasing(true);
359
+ try {
360
+ const restored = await restorePurchases();
361
+ if (restored) {
362
+ onTrackEvent?.("paywall_restore_purchases_success");
363
+ onRestoreSuccess?.();
364
+ } else {
365
+ onTrackEvent?.("paywall_restore_failed");
366
+ onRestoreFailed?.();
367
+ }
368
+ } finally {
369
+ setIsPurchasing(false);
370
+ }
371
+ };
372
+ const handlePurchasePackage = async () => {
373
+ if (!rescueOffer) return;
374
+ try {
375
+ setIsPurchasing(true);
376
+ onTrackEvent?.("paywall_purchase_package", {
377
+ is_rescue_offer: true
378
+ });
379
+ const purchased = await purchasePackage(rescueOffer);
380
+ if (purchased) {
381
+ onTrackEvent?.("paywall_purchase_success", {
382
+ is_rescue_offer: true
383
+ });
384
+ onPurchaseSuccess?.();
385
+ } else {
386
+ onTrackEvent?.("paywall_purchase_failed");
387
+ onPurchaseFailed?.();
388
+ }
389
+ } finally {
390
+ setIsPurchasing(false);
391
+ }
392
+ };
393
+ const handleClosePress = () => {
394
+ onTrackEvent?.("paywall_close_button_pressed", {
395
+ is_rescue_offer: true
396
+ });
397
+ onClose?.();
398
+ };
399
+ const handleTermsPress = () => {
400
+ onTrackEvent?.("paywall_terms_pressed");
401
+ if (termsUrl) reactNative.Linking.openURL(termsUrl);
402
+ };
403
+ const handlePrivacyPress = () => {
404
+ onTrackEvent?.("paywall_privacy_pressed");
405
+ if (privacyUrl) reactNative.Linking.openURL(privacyUrl);
406
+ };
407
+ return {
408
+ isPurchasing,
409
+ handleTermsPress,
410
+ handlePrivacyPress,
411
+ handleClosePress,
412
+ handleRestorePurchases,
413
+ handlePurchasePackage
414
+ };
415
+ };
416
+
417
+ exports.PlutusProvider = PlutusProvider;
418
+ exports.errors = errors;
419
+ exports.useOfferings = useOfferings;
420
+ exports.usePaywall = usePaywall;
421
+ exports.usePlutus = usePlutus;
422
+ exports.useRescuePaywall = useRescuePaywall;
423
+ //# sourceMappingURL=index.js.map
424
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/translations.ts","../src/provider/plutus-provider.tsx","../src/provider/use-plutus.ts","../src/hooks/use-offerings.ts","../src/hooks/use-paywall.ts","../src/hooks/use-rescue-paywall.ts"],"names":["createContext","useState","useMemo","useCallback","useEffect","Purchases","LOG_LEVEL","PURCHASES_ERROR_CODE","jsx","use","Linking"],"mappings":"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","file":"index.js","sourcesContent":["import type { PurchasesPackage } from \"react-native-purchases\";\n\nexport type PlutusErrorCode =\n | \"INIT_FAILED\"\n | \"PURCHASE_FAILED\"\n | \"OFFERINGS_FAILED\"\n | \"RESTORE_FAILED\";\n\nexport interface PlutusError {\n readonly code: PlutusErrorCode;\n readonly message: string;\n readonly cause?: unknown;\n readonly package?: PurchasesPackage;\n}\n\nexport const errors = {\n INIT_FAILED: (cause: unknown): PlutusError => ({\n code: \"INIT_FAILED\",\n message: \"Initialization failed\",\n cause,\n }),\n PURCHASE_FAILED: (cause: unknown, pack?: PurchasesPackage): PlutusError => ({\n code: \"PURCHASE_FAILED\",\n message: \"Purchase failed\",\n cause,\n package: pack,\n }),\n OFFERINGS_FAILED: (cause: unknown): PlutusError => ({\n code: \"OFFERINGS_FAILED\",\n message: \"Failed to load offerings\",\n cause,\n }),\n RESTORE_FAILED: (cause: unknown): PlutusError => ({\n code: \"RESTORE_FAILED\",\n message: \"Restore purchases failed\",\n cause,\n }),\n};\n","export const defaultTranslations = {\n purchaseError: {\n title: \"Purchase Error\",\n message: \"There was a problem processing your purchase. Please try again.\",\n },\n restoreError: {\n title: \"Restore Error\",\n message: \"There was a problem restoring your purchases. Please try again.\",\n },\n};\n\nexport type PlutusTranslations = typeof defaultTranslations;\n","import { createContext, useCallback, useEffect, useMemo, useState } from \"react\";\nimport Purchases, {\n type CustomerInfo,\n LOG_LEVEL,\n PURCHASES_ERROR_CODE,\n type PurchasesError,\n type PurchasesPackage,\n} from \"react-native-purchases\";\n\nimport { errors, type PlutusError } from \"../errors\";\nimport { defaultTranslations } from \"../translations\";\nimport type { PlutusConfig } from \"../types\";\n\nexport interface PlutusContextValue {\n isPro: boolean;\n isInTrial: boolean;\n isReady: boolean;\n managementURL: string | null;\n purchasePackage: (pack: PurchasesPackage) => Promise<boolean | undefined>;\n restorePurchases: () => Promise<boolean>;\n translations: typeof defaultTranslations;\n onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;\n onError?: (error: PlutusError) => void;\n offeringsConfig: { default: string; rescue: string };\n}\n\nexport const PlutusContext = createContext<PlutusContextValue | null>(null);\n\ninterface PlutusProviderProps extends PlutusConfig {\n children: React.ReactNode;\n}\n\nexport const PlutusProvider = ({\n children,\n apiKey,\n entitlementName,\n logLevel,\n offerings,\n callbacks,\n translations: translationOverrides,\n}: PlutusProviderProps) => {\n const [isReady, setIsReady] = useState(false);\n const [isPro, setIsPro] = useState(false);\n const [isInTrial, setIsInTrial] = useState(false);\n const [managementURL, setManagementURL] = useState<string | null>(null);\n\n const translations = useMemo(\n () => ({\n ...defaultTranslations,\n ...translationOverrides,\n purchaseError: {\n ...defaultTranslations.purchaseError,\n ...translationOverrides?.purchaseError,\n },\n restoreError: {\n ...defaultTranslations.restoreError,\n ...translationOverrides?.restoreError,\n },\n }),\n [translationOverrides],\n );\n\n const offeringsConfig = useMemo(\n () => ({\n default: offerings?.default ?? \"default\",\n rescue: offerings?.rescue ?? \"rescue\",\n }),\n [offerings?.default, offerings?.rescue],\n );\n\n const updateCustomerInformation = useCallback(\n (customerInfo: CustomerInfo) => {\n const entitlement = customerInfo?.entitlements.active?.[entitlementName];\n\n const newIsPro = entitlement !== undefined;\n const newIsInTrial = entitlement?.periodType === \"TRIAL\";\n\n setIsPro(newIsPro);\n setIsInTrial(newIsInTrial);\n setManagementURL(customerInfo.managementURL);\n\n callbacks?.onCustomerInfoUpdated?.(customerInfo, {\n isPro: newIsPro,\n isInTrial: newIsInTrial,\n });\n },\n [entitlementName, callbacks],\n );\n\n useEffect(() => {\n const customerInfoUpdateListener = (info: CustomerInfo) => {\n updateCustomerInformation(info);\n };\n\n const init = async () => {\n try {\n await Purchases.setLogLevel(logLevel ?? LOG_LEVEL.ERROR);\n\n Purchases.configure({ apiKey });\n\n Purchases.addCustomerInfoUpdateListener(customerInfoUpdateListener);\n\n setIsReady(true);\n } catch (error) {\n callbacks?.onError?.(errors.INIT_FAILED(error));\n }\n };\n\n init();\n\n return () => {\n Purchases.removeCustomerInfoUpdateListener(customerInfoUpdateListener);\n };\n }, [apiKey, logLevel, updateCustomerInformation, callbacks]);\n\n const purchasePackage = useCallback(\n async (pack: PurchasesPackage): Promise<boolean | undefined> => {\n try {\n const result = await Purchases.purchasePackage(pack);\n updateCustomerInformation(result.customerInfo);\n\n return result.customerInfo.entitlements.active?.[entitlementName] !== undefined;\n } catch (error: unknown) {\n const errorCode = (error as PurchasesError)?.code;\n\n if (errorCode === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return undefined;\n }\n\n callbacks?.onError?.(errors.PURCHASE_FAILED(error, pack));\n\n return undefined;\n }\n },\n [entitlementName, updateCustomerInformation, callbacks],\n );\n\n const restorePurchases = useCallback(async (): Promise<boolean> => {\n try {\n const customerInfo = await Purchases.restorePurchases();\n updateCustomerInformation(customerInfo);\n\n return customerInfo.entitlements.active?.[entitlementName] !== undefined;\n } catch (error) {\n callbacks?.onError?.(errors.RESTORE_FAILED(error));\n\n return false;\n }\n }, [entitlementName, updateCustomerInformation, callbacks]);\n\n const value = useMemo<PlutusContextValue>(\n () => ({\n isPro,\n isInTrial,\n isReady,\n managementURL,\n purchasePackage,\n restorePurchases,\n translations,\n onTrackEvent: callbacks?.onTrackEvent,\n onError: callbacks?.onError,\n offeringsConfig,\n }),\n [\n isPro,\n isInTrial,\n isReady,\n managementURL,\n purchasePackage,\n restorePurchases,\n translations,\n callbacks?.onTrackEvent,\n callbacks?.onError,\n offeringsConfig,\n ],\n );\n\n return <PlutusContext.Provider value={value}>{children}</PlutusContext.Provider>;\n};\n","import { use } from \"react\";\n\nimport { PlutusContext } from \"./plutus-provider\";\n\nexport const usePlutus = () => {\n const context = use(PlutusContext);\n\n if (!context) {\n throw new Error(\"usePlutus must be used within a PlutusProvider\");\n }\n\n return context;\n};\n","import { useEffect, useMemo, useState } from \"react\";\nimport Purchases, { type PurchasesPackage } from \"react-native-purchases\";\n\nimport { errors } from \"../errors\";\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UseOfferingsOptions {\n refetchKey?: string | number;\n}\n\nconst hasFreeTrial = (offer?: PurchasesPackage): boolean => {\n if (!offer?.product?.introPrice) return false;\n return offer.product.introPrice.price === 0;\n};\n\nconst calculateAnnualDiscount = (\n monthlyOffer?: PurchasesPackage,\n annualOffer?: PurchasesPackage,\n): number | undefined => {\n if (!monthlyOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {\n return undefined;\n }\n\n const monthlyPricePerYear = monthlyOffer.product.pricePerYear;\n const annualPricePerYear = annualOffer.product.pricePerYear;\n\n if (monthlyPricePerYear <= annualPricePerYear) {\n return undefined;\n }\n\n const savingsAmount = monthlyPricePerYear - annualPricePerYear;\n const discountPercentage = (savingsAmount / monthlyPricePerYear) * 100;\n\n return Math.floor(discountPercentage);\n};\n\nconst calculateRescueOffsetDiscount = (\n rescueOffer?: PurchasesPackage,\n annualOffer?: PurchasesPackage,\n): number | undefined => {\n if (!rescueOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {\n return undefined;\n }\n\n const rescuePricePerYear = rescueOffer.product.pricePerYear;\n const annualPricePerYear = annualOffer.product.pricePerYear;\n\n if (rescuePricePerYear >= annualPricePerYear) {\n return undefined;\n }\n\n const savingsAmount = annualPricePerYear - rescuePricePerYear;\n const discountPercentage = (savingsAmount / annualPricePerYear) * 100;\n\n return Math.floor(discountPercentage);\n};\n\nexport const useOfferings = (options?: UseOfferingsOptions) => {\n const { isReady, offeringsConfig, onError } = usePlutus();\n\n const [isLoading, setIsLoading] = useState(false);\n const [monthlyOffer, setMonthlyOffer] = useState<PurchasesPackage | undefined>();\n const [annualOffer, setAnnualOffer] = useState<PurchasesPackage | undefined>();\n const [rescueOffer, setRescueOffer] = useState<PurchasesPackage | undefined>();\n\n const loadOfferings = async () => {\n try {\n const offerings = await Purchases.getOfferings();\n\n const defaultPackages = offerings?.all?.[offeringsConfig.default]?.availablePackages;\n const rescuePackages = offerings?.all?.[offeringsConfig.rescue]?.availablePackages;\n\n setMonthlyOffer(\n defaultPackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"MONTHLY\"),\n );\n setAnnualOffer(\n defaultPackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"ANNUAL\"),\n );\n setRescueOffer(rescuePackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"ANNUAL\"));\n } catch (error) {\n onError?.(errors.OFFERINGS_FAILED(error));\n }\n };\n\n useEffect(() => {\n if (isReady) {\n setIsLoading(true);\n loadOfferings().finally(() => setIsLoading(false));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isReady, options?.refetchKey]);\n\n const monthlyHasTrial = useMemo(() => hasFreeTrial(monthlyOffer), [monthlyOffer]);\n\n const annualHasTrial = useMemo(() => hasFreeTrial(annualOffer), [annualOffer]);\n\n const annualDiscountPercentage = useMemo(\n () => calculateAnnualDiscount(monthlyOffer, annualOffer),\n [monthlyOffer, annualOffer],\n );\n\n const rescueOffsetDiscountPercentage = useMemo(\n () => calculateRescueOffsetDiscount(rescueOffer, annualOffer),\n [rescueOffer, annualOffer],\n );\n\n return {\n isLoading,\n monthlyOffer,\n annualOffer,\n rescueOffer,\n monthlyHasTrial,\n annualHasTrial,\n annualDiscountPercentage,\n rescueOffsetDiscountPercentage,\n };\n};\n","import { Linking } from \"react-native\";\nimport { useState } from \"react\";\nimport type { PurchasesPackage } from \"react-native-purchases\";\n\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UsePaywallOptions {\n monthlyOffer?: PurchasesPackage;\n annualOffer?: PurchasesPackage;\n onClose?: () => void;\n onPurchaseSuccess?: (subscriptionType: \"monthly\" | \"annual\") => void;\n onPurchaseFailed?: () => void;\n onRestoreSuccess?: () => void;\n onRestoreFailed?: () => void;\n termsUrl?: string;\n privacyUrl?: string;\n}\n\nexport const usePaywall = ({\n monthlyOffer,\n annualOffer,\n onClose,\n onPurchaseSuccess,\n onPurchaseFailed,\n onRestoreSuccess,\n onRestoreFailed,\n termsUrl,\n privacyUrl,\n}: UsePaywallOptions) => {\n const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();\n\n const [subscriptionType, setSubscriptionType] = useState<\"monthly\" | \"annual\">(\"annual\");\n const [isPurchasing, setIsPurchasing] = useState(false);\n\n const handleSubscriptionTypeChange = (type: \"monthly\" | \"annual\") => {\n onTrackEvent?.(\"paywall_subscription_type_changed\", { type });\n setSubscriptionType(type);\n };\n\n const handleRestorePurchases = async () => {\n onTrackEvent?.(\"paywall_restore_purchases\");\n setIsPurchasing(true);\n\n try {\n const restored = await restorePurchases();\n\n if (restored) {\n onTrackEvent?.(\"paywall_restore_purchases_success\");\n onRestoreSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_restore_failed\");\n onRestoreFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handlePurchasePackage = async () => {\n if (!monthlyOffer || !annualOffer) return;\n\n try {\n setIsPurchasing(true);\n onTrackEvent?.(\"paywall_purchase_package\", {\n type: subscriptionType,\n is_rescue_offer: false,\n });\n\n const selectedOffer = subscriptionType === \"monthly\" ? monthlyOffer : annualOffer;\n const purchased = await purchasePackage(selectedOffer);\n\n if (purchased) {\n onTrackEvent?.(\"paywall_purchase_success\", {\n type: subscriptionType,\n is_rescue_offer: false,\n });\n onPurchaseSuccess?.(subscriptionType);\n } else {\n onTrackEvent?.(\"paywall_purchase_failed\");\n onPurchaseFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handleClosePress = () => {\n onTrackEvent?.(\"paywall_close_button_pressed\", {\n is_rescue_offer: false,\n });\n onClose?.();\n };\n\n const handleTermsPress = () => {\n onTrackEvent?.(\"paywall_terms_pressed\");\n if (termsUrl) Linking.openURL(termsUrl);\n };\n\n const handlePrivacyPress = () => {\n onTrackEvent?.(\"paywall_privacy_pressed\");\n if (privacyUrl) Linking.openURL(privacyUrl);\n };\n\n return {\n subscriptionType,\n isPurchasing,\n handleSubscriptionTypeChange,\n handleTermsPress,\n handlePrivacyPress,\n handleClosePress,\n handleRestorePurchases,\n handlePurchasePackage,\n };\n};\n","import { Linking } from \"react-native\";\nimport { useState } from \"react\";\nimport type { PurchasesPackage } from \"react-native-purchases\";\n\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UseRescuePaywallOptions {\n rescueOffer?: PurchasesPackage;\n onClose?: () => void;\n onPurchaseSuccess?: () => void;\n onPurchaseFailed?: () => void;\n onRestoreSuccess?: () => void;\n onRestoreFailed?: () => void;\n termsUrl?: string;\n privacyUrl?: string;\n}\n\nexport const useRescuePaywall = ({\n rescueOffer,\n onClose,\n onPurchaseSuccess,\n onPurchaseFailed,\n onRestoreSuccess,\n onRestoreFailed,\n termsUrl,\n privacyUrl,\n}: UseRescuePaywallOptions) => {\n const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();\n\n const [isPurchasing, setIsPurchasing] = useState(false);\n\n const handleRestorePurchases = async () => {\n onTrackEvent?.(\"paywall_restore_purchases\");\n setIsPurchasing(true);\n\n try {\n const restored = await restorePurchases();\n\n if (restored) {\n onTrackEvent?.(\"paywall_restore_purchases_success\");\n onRestoreSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_restore_failed\");\n onRestoreFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handlePurchasePackage = async () => {\n if (!rescueOffer) return;\n\n try {\n setIsPurchasing(true);\n onTrackEvent?.(\"paywall_purchase_package\", {\n is_rescue_offer: true,\n });\n\n const purchased = await purchasePackage(rescueOffer);\n\n if (purchased) {\n onTrackEvent?.(\"paywall_purchase_success\", {\n is_rescue_offer: true,\n });\n onPurchaseSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_purchase_failed\");\n onPurchaseFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handleClosePress = () => {\n onTrackEvent?.(\"paywall_close_button_pressed\", {\n is_rescue_offer: true,\n });\n onClose?.();\n };\n\n const handleTermsPress = () => {\n onTrackEvent?.(\"paywall_terms_pressed\");\n if (termsUrl) Linking.openURL(termsUrl);\n };\n\n const handlePrivacyPress = () => {\n onTrackEvent?.(\"paywall_privacy_pressed\");\n if (privacyUrl) Linking.openURL(privacyUrl);\n };\n\n return {\n isPurchasing,\n handleTermsPress,\n handlePrivacyPress,\n handleClosePress,\n handleRestorePurchases,\n handlePurchasePackage,\n };\n};\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,413 @@
1
+ import { createContext, useState, useMemo, useCallback, useEffect, use } from 'react';
2
+ import Purchases, { PURCHASES_ERROR_CODE, LOG_LEVEL } from 'react-native-purchases';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { Linking } from 'react-native';
5
+
6
+ // src/provider/plutus-provider.tsx
7
+
8
+ // src/errors.ts
9
+ var errors = {
10
+ INIT_FAILED: (cause) => ({
11
+ code: "INIT_FAILED",
12
+ message: "Initialization failed",
13
+ cause
14
+ }),
15
+ PURCHASE_FAILED: (cause, pack) => ({
16
+ code: "PURCHASE_FAILED",
17
+ message: "Purchase failed",
18
+ cause,
19
+ package: pack
20
+ }),
21
+ OFFERINGS_FAILED: (cause) => ({
22
+ code: "OFFERINGS_FAILED",
23
+ message: "Failed to load offerings",
24
+ cause
25
+ }),
26
+ RESTORE_FAILED: (cause) => ({
27
+ code: "RESTORE_FAILED",
28
+ message: "Restore purchases failed",
29
+ cause
30
+ })
31
+ };
32
+
33
+ // src/translations.ts
34
+ var defaultTranslations = {
35
+ purchaseError: {
36
+ title: "Purchase Error",
37
+ message: "There was a problem processing your purchase. Please try again."
38
+ },
39
+ restoreError: {
40
+ title: "Restore Error",
41
+ message: "There was a problem restoring your purchases. Please try again."
42
+ }
43
+ };
44
+ var PlutusContext = createContext(null);
45
+ var PlutusProvider = ({
46
+ children,
47
+ apiKey,
48
+ entitlementName,
49
+ logLevel,
50
+ offerings,
51
+ callbacks,
52
+ translations: translationOverrides
53
+ }) => {
54
+ const [isReady, setIsReady] = useState(false);
55
+ const [isPro, setIsPro] = useState(false);
56
+ const [isInTrial, setIsInTrial] = useState(false);
57
+ const [managementURL, setManagementURL] = useState(null);
58
+ const translations = useMemo(
59
+ () => ({
60
+ ...defaultTranslations,
61
+ ...translationOverrides,
62
+ purchaseError: {
63
+ ...defaultTranslations.purchaseError,
64
+ ...translationOverrides?.purchaseError
65
+ },
66
+ restoreError: {
67
+ ...defaultTranslations.restoreError,
68
+ ...translationOverrides?.restoreError
69
+ }
70
+ }),
71
+ [translationOverrides]
72
+ );
73
+ const offeringsConfig = useMemo(
74
+ () => ({
75
+ default: offerings?.default ?? "default",
76
+ rescue: offerings?.rescue ?? "rescue"
77
+ }),
78
+ [offerings?.default, offerings?.rescue]
79
+ );
80
+ const updateCustomerInformation = useCallback(
81
+ (customerInfo) => {
82
+ const entitlement = customerInfo?.entitlements.active?.[entitlementName];
83
+ const newIsPro = entitlement !== void 0;
84
+ const newIsInTrial = entitlement?.periodType === "TRIAL";
85
+ setIsPro(newIsPro);
86
+ setIsInTrial(newIsInTrial);
87
+ setManagementURL(customerInfo.managementURL);
88
+ callbacks?.onCustomerInfoUpdated?.(customerInfo, {
89
+ isPro: newIsPro,
90
+ isInTrial: newIsInTrial
91
+ });
92
+ },
93
+ [entitlementName, callbacks]
94
+ );
95
+ useEffect(() => {
96
+ const customerInfoUpdateListener = (info) => {
97
+ updateCustomerInformation(info);
98
+ };
99
+ const init = async () => {
100
+ try {
101
+ await Purchases.setLogLevel(logLevel ?? LOG_LEVEL.ERROR);
102
+ Purchases.configure({ apiKey });
103
+ Purchases.addCustomerInfoUpdateListener(customerInfoUpdateListener);
104
+ setIsReady(true);
105
+ } catch (error) {
106
+ callbacks?.onError?.(errors.INIT_FAILED(error));
107
+ }
108
+ };
109
+ init();
110
+ return () => {
111
+ Purchases.removeCustomerInfoUpdateListener(customerInfoUpdateListener);
112
+ };
113
+ }, [apiKey, logLevel, updateCustomerInformation, callbacks]);
114
+ const purchasePackage = useCallback(
115
+ async (pack) => {
116
+ try {
117
+ const result = await Purchases.purchasePackage(pack);
118
+ updateCustomerInformation(result.customerInfo);
119
+ return result.customerInfo.entitlements.active?.[entitlementName] !== void 0;
120
+ } catch (error) {
121
+ const errorCode = error?.code;
122
+ if (errorCode === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
123
+ return void 0;
124
+ }
125
+ callbacks?.onError?.(errors.PURCHASE_FAILED(error, pack));
126
+ return void 0;
127
+ }
128
+ },
129
+ [entitlementName, updateCustomerInformation, callbacks]
130
+ );
131
+ const restorePurchases = useCallback(async () => {
132
+ try {
133
+ const customerInfo = await Purchases.restorePurchases();
134
+ updateCustomerInformation(customerInfo);
135
+ return customerInfo.entitlements.active?.[entitlementName] !== void 0;
136
+ } catch (error) {
137
+ callbacks?.onError?.(errors.RESTORE_FAILED(error));
138
+ return false;
139
+ }
140
+ }, [entitlementName, updateCustomerInformation, callbacks]);
141
+ const value = useMemo(
142
+ () => ({
143
+ isPro,
144
+ isInTrial,
145
+ isReady,
146
+ managementURL,
147
+ purchasePackage,
148
+ restorePurchases,
149
+ translations,
150
+ onTrackEvent: callbacks?.onTrackEvent,
151
+ onError: callbacks?.onError,
152
+ offeringsConfig
153
+ }),
154
+ [
155
+ isPro,
156
+ isInTrial,
157
+ isReady,
158
+ managementURL,
159
+ purchasePackage,
160
+ restorePurchases,
161
+ translations,
162
+ callbacks?.onTrackEvent,
163
+ callbacks?.onError,
164
+ offeringsConfig
165
+ ]
166
+ );
167
+ return /* @__PURE__ */ jsx(PlutusContext.Provider, { value, children });
168
+ };
169
+ var usePlutus = () => {
170
+ const context = use(PlutusContext);
171
+ if (!context) {
172
+ throw new Error("usePlutus must be used within a PlutusProvider");
173
+ }
174
+ return context;
175
+ };
176
+ var hasFreeTrial = (offer) => {
177
+ if (!offer?.product?.introPrice) return false;
178
+ return offer.product.introPrice.price === 0;
179
+ };
180
+ var calculateAnnualDiscount = (monthlyOffer, annualOffer) => {
181
+ if (!monthlyOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {
182
+ return void 0;
183
+ }
184
+ const monthlyPricePerYear = monthlyOffer.product.pricePerYear;
185
+ const annualPricePerYear = annualOffer.product.pricePerYear;
186
+ if (monthlyPricePerYear <= annualPricePerYear) {
187
+ return void 0;
188
+ }
189
+ const savingsAmount = monthlyPricePerYear - annualPricePerYear;
190
+ const discountPercentage = savingsAmount / monthlyPricePerYear * 100;
191
+ return Math.floor(discountPercentage);
192
+ };
193
+ var calculateRescueOffsetDiscount = (rescueOffer, annualOffer) => {
194
+ if (!rescueOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {
195
+ return void 0;
196
+ }
197
+ const rescuePricePerYear = rescueOffer.product.pricePerYear;
198
+ const annualPricePerYear = annualOffer.product.pricePerYear;
199
+ if (rescuePricePerYear >= annualPricePerYear) {
200
+ return void 0;
201
+ }
202
+ const savingsAmount = annualPricePerYear - rescuePricePerYear;
203
+ const discountPercentage = savingsAmount / annualPricePerYear * 100;
204
+ return Math.floor(discountPercentage);
205
+ };
206
+ var useOfferings = (options) => {
207
+ const { isReady, offeringsConfig, onError } = usePlutus();
208
+ const [isLoading, setIsLoading] = useState(false);
209
+ const [monthlyOffer, setMonthlyOffer] = useState();
210
+ const [annualOffer, setAnnualOffer] = useState();
211
+ const [rescueOffer, setRescueOffer] = useState();
212
+ const loadOfferings = async () => {
213
+ try {
214
+ const offerings = await Purchases.getOfferings();
215
+ const defaultPackages = offerings?.all?.[offeringsConfig.default]?.availablePackages;
216
+ const rescuePackages = offerings?.all?.[offeringsConfig.rescue]?.availablePackages;
217
+ setMonthlyOffer(
218
+ defaultPackages?.find((pkg) => pkg.packageType === "MONTHLY")
219
+ );
220
+ setAnnualOffer(
221
+ defaultPackages?.find((pkg) => pkg.packageType === "ANNUAL")
222
+ );
223
+ setRescueOffer(rescuePackages?.find((pkg) => pkg.packageType === "ANNUAL"));
224
+ } catch (error) {
225
+ onError?.(errors.OFFERINGS_FAILED(error));
226
+ }
227
+ };
228
+ useEffect(() => {
229
+ if (isReady) {
230
+ setIsLoading(true);
231
+ loadOfferings().finally(() => setIsLoading(false));
232
+ }
233
+ }, [isReady, options?.refetchKey]);
234
+ const monthlyHasTrial = useMemo(() => hasFreeTrial(monthlyOffer), [monthlyOffer]);
235
+ const annualHasTrial = useMemo(() => hasFreeTrial(annualOffer), [annualOffer]);
236
+ const annualDiscountPercentage = useMemo(
237
+ () => calculateAnnualDiscount(monthlyOffer, annualOffer),
238
+ [monthlyOffer, annualOffer]
239
+ );
240
+ const rescueOffsetDiscountPercentage = useMemo(
241
+ () => calculateRescueOffsetDiscount(rescueOffer, annualOffer),
242
+ [rescueOffer, annualOffer]
243
+ );
244
+ return {
245
+ isLoading,
246
+ monthlyOffer,
247
+ annualOffer,
248
+ rescueOffer,
249
+ monthlyHasTrial,
250
+ annualHasTrial,
251
+ annualDiscountPercentage,
252
+ rescueOffsetDiscountPercentage
253
+ };
254
+ };
255
+ var usePaywall = ({
256
+ monthlyOffer,
257
+ annualOffer,
258
+ onClose,
259
+ onPurchaseSuccess,
260
+ onPurchaseFailed,
261
+ onRestoreSuccess,
262
+ onRestoreFailed,
263
+ termsUrl,
264
+ privacyUrl
265
+ }) => {
266
+ const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();
267
+ const [subscriptionType, setSubscriptionType] = useState("annual");
268
+ const [isPurchasing, setIsPurchasing] = useState(false);
269
+ const handleSubscriptionTypeChange = (type) => {
270
+ onTrackEvent?.("paywall_subscription_type_changed", { type });
271
+ setSubscriptionType(type);
272
+ };
273
+ const handleRestorePurchases = async () => {
274
+ onTrackEvent?.("paywall_restore_purchases");
275
+ setIsPurchasing(true);
276
+ try {
277
+ const restored = await restorePurchases();
278
+ if (restored) {
279
+ onTrackEvent?.("paywall_restore_purchases_success");
280
+ onRestoreSuccess?.();
281
+ } else {
282
+ onTrackEvent?.("paywall_restore_failed");
283
+ onRestoreFailed?.();
284
+ }
285
+ } finally {
286
+ setIsPurchasing(false);
287
+ }
288
+ };
289
+ const handlePurchasePackage = async () => {
290
+ if (!monthlyOffer || !annualOffer) return;
291
+ try {
292
+ setIsPurchasing(true);
293
+ onTrackEvent?.("paywall_purchase_package", {
294
+ type: subscriptionType,
295
+ is_rescue_offer: false
296
+ });
297
+ const selectedOffer = subscriptionType === "monthly" ? monthlyOffer : annualOffer;
298
+ const purchased = await purchasePackage(selectedOffer);
299
+ if (purchased) {
300
+ onTrackEvent?.("paywall_purchase_success", {
301
+ type: subscriptionType,
302
+ is_rescue_offer: false
303
+ });
304
+ onPurchaseSuccess?.(subscriptionType);
305
+ } else {
306
+ onTrackEvent?.("paywall_purchase_failed");
307
+ onPurchaseFailed?.();
308
+ }
309
+ } finally {
310
+ setIsPurchasing(false);
311
+ }
312
+ };
313
+ const handleClosePress = () => {
314
+ onTrackEvent?.("paywall_close_button_pressed", {
315
+ is_rescue_offer: false
316
+ });
317
+ onClose?.();
318
+ };
319
+ const handleTermsPress = () => {
320
+ onTrackEvent?.("paywall_terms_pressed");
321
+ if (termsUrl) Linking.openURL(termsUrl);
322
+ };
323
+ const handlePrivacyPress = () => {
324
+ onTrackEvent?.("paywall_privacy_pressed");
325
+ if (privacyUrl) Linking.openURL(privacyUrl);
326
+ };
327
+ return {
328
+ subscriptionType,
329
+ isPurchasing,
330
+ handleSubscriptionTypeChange,
331
+ handleTermsPress,
332
+ handlePrivacyPress,
333
+ handleClosePress,
334
+ handleRestorePurchases,
335
+ handlePurchasePackage
336
+ };
337
+ };
338
+ var useRescuePaywall = ({
339
+ rescueOffer,
340
+ onClose,
341
+ onPurchaseSuccess,
342
+ onPurchaseFailed,
343
+ onRestoreSuccess,
344
+ onRestoreFailed,
345
+ termsUrl,
346
+ privacyUrl
347
+ }) => {
348
+ const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();
349
+ const [isPurchasing, setIsPurchasing] = useState(false);
350
+ const handleRestorePurchases = async () => {
351
+ onTrackEvent?.("paywall_restore_purchases");
352
+ setIsPurchasing(true);
353
+ try {
354
+ const restored = await restorePurchases();
355
+ if (restored) {
356
+ onTrackEvent?.("paywall_restore_purchases_success");
357
+ onRestoreSuccess?.();
358
+ } else {
359
+ onTrackEvent?.("paywall_restore_failed");
360
+ onRestoreFailed?.();
361
+ }
362
+ } finally {
363
+ setIsPurchasing(false);
364
+ }
365
+ };
366
+ const handlePurchasePackage = async () => {
367
+ if (!rescueOffer) return;
368
+ try {
369
+ setIsPurchasing(true);
370
+ onTrackEvent?.("paywall_purchase_package", {
371
+ is_rescue_offer: true
372
+ });
373
+ const purchased = await purchasePackage(rescueOffer);
374
+ if (purchased) {
375
+ onTrackEvent?.("paywall_purchase_success", {
376
+ is_rescue_offer: true
377
+ });
378
+ onPurchaseSuccess?.();
379
+ } else {
380
+ onTrackEvent?.("paywall_purchase_failed");
381
+ onPurchaseFailed?.();
382
+ }
383
+ } finally {
384
+ setIsPurchasing(false);
385
+ }
386
+ };
387
+ const handleClosePress = () => {
388
+ onTrackEvent?.("paywall_close_button_pressed", {
389
+ is_rescue_offer: true
390
+ });
391
+ onClose?.();
392
+ };
393
+ const handleTermsPress = () => {
394
+ onTrackEvent?.("paywall_terms_pressed");
395
+ if (termsUrl) Linking.openURL(termsUrl);
396
+ };
397
+ const handlePrivacyPress = () => {
398
+ onTrackEvent?.("paywall_privacy_pressed");
399
+ if (privacyUrl) Linking.openURL(privacyUrl);
400
+ };
401
+ return {
402
+ isPurchasing,
403
+ handleTermsPress,
404
+ handlePrivacyPress,
405
+ handleClosePress,
406
+ handleRestorePurchases,
407
+ handlePurchasePackage
408
+ };
409
+ };
410
+
411
+ export { PlutusProvider, errors, useOfferings, usePaywall, usePlutus, useRescuePaywall };
412
+ //# sourceMappingURL=index.mjs.map
413
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/translations.ts","../src/provider/plutus-provider.tsx","../src/provider/use-plutus.ts","../src/hooks/use-offerings.ts","../src/hooks/use-paywall.ts","../src/hooks/use-rescue-paywall.ts"],"names":["useState","Purchases","useEffect","useMemo","Linking"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { PurchasesPackage } from \"react-native-purchases\";\n\nexport type PlutusErrorCode =\n | \"INIT_FAILED\"\n | \"PURCHASE_FAILED\"\n | \"OFFERINGS_FAILED\"\n | \"RESTORE_FAILED\";\n\nexport interface PlutusError {\n readonly code: PlutusErrorCode;\n readonly message: string;\n readonly cause?: unknown;\n readonly package?: PurchasesPackage;\n}\n\nexport const errors = {\n INIT_FAILED: (cause: unknown): PlutusError => ({\n code: \"INIT_FAILED\",\n message: \"Initialization failed\",\n cause,\n }),\n PURCHASE_FAILED: (cause: unknown, pack?: PurchasesPackage): PlutusError => ({\n code: \"PURCHASE_FAILED\",\n message: \"Purchase failed\",\n cause,\n package: pack,\n }),\n OFFERINGS_FAILED: (cause: unknown): PlutusError => ({\n code: \"OFFERINGS_FAILED\",\n message: \"Failed to load offerings\",\n cause,\n }),\n RESTORE_FAILED: (cause: unknown): PlutusError => ({\n code: \"RESTORE_FAILED\",\n message: \"Restore purchases failed\",\n cause,\n }),\n};\n","export const defaultTranslations = {\n purchaseError: {\n title: \"Purchase Error\",\n message: \"There was a problem processing your purchase. Please try again.\",\n },\n restoreError: {\n title: \"Restore Error\",\n message: \"There was a problem restoring your purchases. Please try again.\",\n },\n};\n\nexport type PlutusTranslations = typeof defaultTranslations;\n","import { createContext, useCallback, useEffect, useMemo, useState } from \"react\";\nimport Purchases, {\n type CustomerInfo,\n LOG_LEVEL,\n PURCHASES_ERROR_CODE,\n type PurchasesError,\n type PurchasesPackage,\n} from \"react-native-purchases\";\n\nimport { errors, type PlutusError } from \"../errors\";\nimport { defaultTranslations } from \"../translations\";\nimport type { PlutusConfig } from \"../types\";\n\nexport interface PlutusContextValue {\n isPro: boolean;\n isInTrial: boolean;\n isReady: boolean;\n managementURL: string | null;\n purchasePackage: (pack: PurchasesPackage) => Promise<boolean | undefined>;\n restorePurchases: () => Promise<boolean>;\n translations: typeof defaultTranslations;\n onTrackEvent?: (name: string, params?: Record<string, unknown>) => void;\n onError?: (error: PlutusError) => void;\n offeringsConfig: { default: string; rescue: string };\n}\n\nexport const PlutusContext = createContext<PlutusContextValue | null>(null);\n\ninterface PlutusProviderProps extends PlutusConfig {\n children: React.ReactNode;\n}\n\nexport const PlutusProvider = ({\n children,\n apiKey,\n entitlementName,\n logLevel,\n offerings,\n callbacks,\n translations: translationOverrides,\n}: PlutusProviderProps) => {\n const [isReady, setIsReady] = useState(false);\n const [isPro, setIsPro] = useState(false);\n const [isInTrial, setIsInTrial] = useState(false);\n const [managementURL, setManagementURL] = useState<string | null>(null);\n\n const translations = useMemo(\n () => ({\n ...defaultTranslations,\n ...translationOverrides,\n purchaseError: {\n ...defaultTranslations.purchaseError,\n ...translationOverrides?.purchaseError,\n },\n restoreError: {\n ...defaultTranslations.restoreError,\n ...translationOverrides?.restoreError,\n },\n }),\n [translationOverrides],\n );\n\n const offeringsConfig = useMemo(\n () => ({\n default: offerings?.default ?? \"default\",\n rescue: offerings?.rescue ?? \"rescue\",\n }),\n [offerings?.default, offerings?.rescue],\n );\n\n const updateCustomerInformation = useCallback(\n (customerInfo: CustomerInfo) => {\n const entitlement = customerInfo?.entitlements.active?.[entitlementName];\n\n const newIsPro = entitlement !== undefined;\n const newIsInTrial = entitlement?.periodType === \"TRIAL\";\n\n setIsPro(newIsPro);\n setIsInTrial(newIsInTrial);\n setManagementURL(customerInfo.managementURL);\n\n callbacks?.onCustomerInfoUpdated?.(customerInfo, {\n isPro: newIsPro,\n isInTrial: newIsInTrial,\n });\n },\n [entitlementName, callbacks],\n );\n\n useEffect(() => {\n const customerInfoUpdateListener = (info: CustomerInfo) => {\n updateCustomerInformation(info);\n };\n\n const init = async () => {\n try {\n await Purchases.setLogLevel(logLevel ?? LOG_LEVEL.ERROR);\n\n Purchases.configure({ apiKey });\n\n Purchases.addCustomerInfoUpdateListener(customerInfoUpdateListener);\n\n setIsReady(true);\n } catch (error) {\n callbacks?.onError?.(errors.INIT_FAILED(error));\n }\n };\n\n init();\n\n return () => {\n Purchases.removeCustomerInfoUpdateListener(customerInfoUpdateListener);\n };\n }, [apiKey, logLevel, updateCustomerInformation, callbacks]);\n\n const purchasePackage = useCallback(\n async (pack: PurchasesPackage): Promise<boolean | undefined> => {\n try {\n const result = await Purchases.purchasePackage(pack);\n updateCustomerInformation(result.customerInfo);\n\n return result.customerInfo.entitlements.active?.[entitlementName] !== undefined;\n } catch (error: unknown) {\n const errorCode = (error as PurchasesError)?.code;\n\n if (errorCode === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {\n return undefined;\n }\n\n callbacks?.onError?.(errors.PURCHASE_FAILED(error, pack));\n\n return undefined;\n }\n },\n [entitlementName, updateCustomerInformation, callbacks],\n );\n\n const restorePurchases = useCallback(async (): Promise<boolean> => {\n try {\n const customerInfo = await Purchases.restorePurchases();\n updateCustomerInformation(customerInfo);\n\n return customerInfo.entitlements.active?.[entitlementName] !== undefined;\n } catch (error) {\n callbacks?.onError?.(errors.RESTORE_FAILED(error));\n\n return false;\n }\n }, [entitlementName, updateCustomerInformation, callbacks]);\n\n const value = useMemo<PlutusContextValue>(\n () => ({\n isPro,\n isInTrial,\n isReady,\n managementURL,\n purchasePackage,\n restorePurchases,\n translations,\n onTrackEvent: callbacks?.onTrackEvent,\n onError: callbacks?.onError,\n offeringsConfig,\n }),\n [\n isPro,\n isInTrial,\n isReady,\n managementURL,\n purchasePackage,\n restorePurchases,\n translations,\n callbacks?.onTrackEvent,\n callbacks?.onError,\n offeringsConfig,\n ],\n );\n\n return <PlutusContext.Provider value={value}>{children}</PlutusContext.Provider>;\n};\n","import { use } from \"react\";\n\nimport { PlutusContext } from \"./plutus-provider\";\n\nexport const usePlutus = () => {\n const context = use(PlutusContext);\n\n if (!context) {\n throw new Error(\"usePlutus must be used within a PlutusProvider\");\n }\n\n return context;\n};\n","import { useEffect, useMemo, useState } from \"react\";\nimport Purchases, { type PurchasesPackage } from \"react-native-purchases\";\n\nimport { errors } from \"../errors\";\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UseOfferingsOptions {\n refetchKey?: string | number;\n}\n\nconst hasFreeTrial = (offer?: PurchasesPackage): boolean => {\n if (!offer?.product?.introPrice) return false;\n return offer.product.introPrice.price === 0;\n};\n\nconst calculateAnnualDiscount = (\n monthlyOffer?: PurchasesPackage,\n annualOffer?: PurchasesPackage,\n): number | undefined => {\n if (!monthlyOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {\n return undefined;\n }\n\n const monthlyPricePerYear = monthlyOffer.product.pricePerYear;\n const annualPricePerYear = annualOffer.product.pricePerYear;\n\n if (monthlyPricePerYear <= annualPricePerYear) {\n return undefined;\n }\n\n const savingsAmount = monthlyPricePerYear - annualPricePerYear;\n const discountPercentage = (savingsAmount / monthlyPricePerYear) * 100;\n\n return Math.floor(discountPercentage);\n};\n\nconst calculateRescueOffsetDiscount = (\n rescueOffer?: PurchasesPackage,\n annualOffer?: PurchasesPackage,\n): number | undefined => {\n if (!rescueOffer?.product?.pricePerYear || !annualOffer?.product?.pricePerYear) {\n return undefined;\n }\n\n const rescuePricePerYear = rescueOffer.product.pricePerYear;\n const annualPricePerYear = annualOffer.product.pricePerYear;\n\n if (rescuePricePerYear >= annualPricePerYear) {\n return undefined;\n }\n\n const savingsAmount = annualPricePerYear - rescuePricePerYear;\n const discountPercentage = (savingsAmount / annualPricePerYear) * 100;\n\n return Math.floor(discountPercentage);\n};\n\nexport const useOfferings = (options?: UseOfferingsOptions) => {\n const { isReady, offeringsConfig, onError } = usePlutus();\n\n const [isLoading, setIsLoading] = useState(false);\n const [monthlyOffer, setMonthlyOffer] = useState<PurchasesPackage | undefined>();\n const [annualOffer, setAnnualOffer] = useState<PurchasesPackage | undefined>();\n const [rescueOffer, setRescueOffer] = useState<PurchasesPackage | undefined>();\n\n const loadOfferings = async () => {\n try {\n const offerings = await Purchases.getOfferings();\n\n const defaultPackages = offerings?.all?.[offeringsConfig.default]?.availablePackages;\n const rescuePackages = offerings?.all?.[offeringsConfig.rescue]?.availablePackages;\n\n setMonthlyOffer(\n defaultPackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"MONTHLY\"),\n );\n setAnnualOffer(\n defaultPackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"ANNUAL\"),\n );\n setRescueOffer(rescuePackages?.find((pkg: PurchasesPackage) => pkg.packageType === \"ANNUAL\"));\n } catch (error) {\n onError?.(errors.OFFERINGS_FAILED(error));\n }\n };\n\n useEffect(() => {\n if (isReady) {\n setIsLoading(true);\n loadOfferings().finally(() => setIsLoading(false));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isReady, options?.refetchKey]);\n\n const monthlyHasTrial = useMemo(() => hasFreeTrial(monthlyOffer), [monthlyOffer]);\n\n const annualHasTrial = useMemo(() => hasFreeTrial(annualOffer), [annualOffer]);\n\n const annualDiscountPercentage = useMemo(\n () => calculateAnnualDiscount(monthlyOffer, annualOffer),\n [monthlyOffer, annualOffer],\n );\n\n const rescueOffsetDiscountPercentage = useMemo(\n () => calculateRescueOffsetDiscount(rescueOffer, annualOffer),\n [rescueOffer, annualOffer],\n );\n\n return {\n isLoading,\n monthlyOffer,\n annualOffer,\n rescueOffer,\n monthlyHasTrial,\n annualHasTrial,\n annualDiscountPercentage,\n rescueOffsetDiscountPercentage,\n };\n};\n","import { Linking } from \"react-native\";\nimport { useState } from \"react\";\nimport type { PurchasesPackage } from \"react-native-purchases\";\n\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UsePaywallOptions {\n monthlyOffer?: PurchasesPackage;\n annualOffer?: PurchasesPackage;\n onClose?: () => void;\n onPurchaseSuccess?: (subscriptionType: \"monthly\" | \"annual\") => void;\n onPurchaseFailed?: () => void;\n onRestoreSuccess?: () => void;\n onRestoreFailed?: () => void;\n termsUrl?: string;\n privacyUrl?: string;\n}\n\nexport const usePaywall = ({\n monthlyOffer,\n annualOffer,\n onClose,\n onPurchaseSuccess,\n onPurchaseFailed,\n onRestoreSuccess,\n onRestoreFailed,\n termsUrl,\n privacyUrl,\n}: UsePaywallOptions) => {\n const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();\n\n const [subscriptionType, setSubscriptionType] = useState<\"monthly\" | \"annual\">(\"annual\");\n const [isPurchasing, setIsPurchasing] = useState(false);\n\n const handleSubscriptionTypeChange = (type: \"monthly\" | \"annual\") => {\n onTrackEvent?.(\"paywall_subscription_type_changed\", { type });\n setSubscriptionType(type);\n };\n\n const handleRestorePurchases = async () => {\n onTrackEvent?.(\"paywall_restore_purchases\");\n setIsPurchasing(true);\n\n try {\n const restored = await restorePurchases();\n\n if (restored) {\n onTrackEvent?.(\"paywall_restore_purchases_success\");\n onRestoreSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_restore_failed\");\n onRestoreFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handlePurchasePackage = async () => {\n if (!monthlyOffer || !annualOffer) return;\n\n try {\n setIsPurchasing(true);\n onTrackEvent?.(\"paywall_purchase_package\", {\n type: subscriptionType,\n is_rescue_offer: false,\n });\n\n const selectedOffer = subscriptionType === \"monthly\" ? monthlyOffer : annualOffer;\n const purchased = await purchasePackage(selectedOffer);\n\n if (purchased) {\n onTrackEvent?.(\"paywall_purchase_success\", {\n type: subscriptionType,\n is_rescue_offer: false,\n });\n onPurchaseSuccess?.(subscriptionType);\n } else {\n onTrackEvent?.(\"paywall_purchase_failed\");\n onPurchaseFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handleClosePress = () => {\n onTrackEvent?.(\"paywall_close_button_pressed\", {\n is_rescue_offer: false,\n });\n onClose?.();\n };\n\n const handleTermsPress = () => {\n onTrackEvent?.(\"paywall_terms_pressed\");\n if (termsUrl) Linking.openURL(termsUrl);\n };\n\n const handlePrivacyPress = () => {\n onTrackEvent?.(\"paywall_privacy_pressed\");\n if (privacyUrl) Linking.openURL(privacyUrl);\n };\n\n return {\n subscriptionType,\n isPurchasing,\n handleSubscriptionTypeChange,\n handleTermsPress,\n handlePrivacyPress,\n handleClosePress,\n handleRestorePurchases,\n handlePurchasePackage,\n };\n};\n","import { Linking } from \"react-native\";\nimport { useState } from \"react\";\nimport type { PurchasesPackage } from \"react-native-purchases\";\n\nimport { usePlutus } from \"../provider/use-plutus\";\n\ninterface UseRescuePaywallOptions {\n rescueOffer?: PurchasesPackage;\n onClose?: () => void;\n onPurchaseSuccess?: () => void;\n onPurchaseFailed?: () => void;\n onRestoreSuccess?: () => void;\n onRestoreFailed?: () => void;\n termsUrl?: string;\n privacyUrl?: string;\n}\n\nexport const useRescuePaywall = ({\n rescueOffer,\n onClose,\n onPurchaseSuccess,\n onPurchaseFailed,\n onRestoreSuccess,\n onRestoreFailed,\n termsUrl,\n privacyUrl,\n}: UseRescuePaywallOptions) => {\n const { restorePurchases, purchasePackage, onTrackEvent } = usePlutus();\n\n const [isPurchasing, setIsPurchasing] = useState(false);\n\n const handleRestorePurchases = async () => {\n onTrackEvent?.(\"paywall_restore_purchases\");\n setIsPurchasing(true);\n\n try {\n const restored = await restorePurchases();\n\n if (restored) {\n onTrackEvent?.(\"paywall_restore_purchases_success\");\n onRestoreSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_restore_failed\");\n onRestoreFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handlePurchasePackage = async () => {\n if (!rescueOffer) return;\n\n try {\n setIsPurchasing(true);\n onTrackEvent?.(\"paywall_purchase_package\", {\n is_rescue_offer: true,\n });\n\n const purchased = await purchasePackage(rescueOffer);\n\n if (purchased) {\n onTrackEvent?.(\"paywall_purchase_success\", {\n is_rescue_offer: true,\n });\n onPurchaseSuccess?.();\n } else {\n onTrackEvent?.(\"paywall_purchase_failed\");\n onPurchaseFailed?.();\n }\n } finally {\n setIsPurchasing(false);\n }\n };\n\n const handleClosePress = () => {\n onTrackEvent?.(\"paywall_close_button_pressed\", {\n is_rescue_offer: true,\n });\n onClose?.();\n };\n\n const handleTermsPress = () => {\n onTrackEvent?.(\"paywall_terms_pressed\");\n if (termsUrl) Linking.openURL(termsUrl);\n };\n\n const handlePrivacyPress = () => {\n onTrackEvent?.(\"paywall_privacy_pressed\");\n if (privacyUrl) Linking.openURL(privacyUrl);\n };\n\n return {\n isPurchasing,\n handleTermsPress,\n handlePrivacyPress,\n handleClosePress,\n handleRestorePurchases,\n handlePurchasePackage,\n };\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@byarcadia-app/plutus",
3
+ "version": "0.1.0",
4
+ "description": "Reusable RevenueCat wrapper for React Native in-app purchases",
5
+ "keywords": [
6
+ "in-app-purchases",
7
+ "react-native",
8
+ "revenuecat",
9
+ "subscriptions"
10
+ ],
11
+ "homepage": "https://github.com/byarcadia-app/plutus#readme",
12
+ "license": "MIT",
13
+ "author": "Dominik Woźniak <dominik@byarcadia.app> (github.com/dominikwozniak)",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/byarcadia-app/plutus"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "sideEffects": false,
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.mjs",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.mjs",
30
+ "require": "./dist/index.js"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "release": "pnpm build && changeset publish",
37
+ "version": "changeset version",
38
+ "check": "tsc --noEmit",
39
+ "lint": "oxlint .",
40
+ "fmt": "oxfmt .",
41
+ "fmt:check": "oxfmt --check .",
42
+ "example:start": "pnpm --filter @byarcadia-app/plutus-example start",
43
+ "example:ios": "pnpm --filter @byarcadia-app/plutus-example ios"
44
+ },
45
+ "devDependencies": {
46
+ "@changesets/changelog-github": "^0.5.2",
47
+ "@changesets/cli": "^2.29.8",
48
+ "@types/react": "^19.2.14",
49
+ "oxfmt": "^0.34.0",
50
+ "oxlint": "^1.49.0",
51
+ "react": "^19.2.4",
52
+ "react-native": "^0.84.0",
53
+ "react-native-purchases": "^9.0.0",
54
+ "tsup": "^8.5.1",
55
+ "typescript": "^5.9.3"
56
+ },
57
+ "peerDependencies": {
58
+ "react": ">=18",
59
+ "react-native": ">=0.72",
60
+ "react-native-purchases": ">=9"
61
+ },
62
+ "packageManager": "pnpm@10.14.0"
63
+ }