@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 +21 -0
- package/README.md +51 -0
- package/dist/index.d.mts +126 -0
- package/dist/index.d.ts +126 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +413 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
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> <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> <img src="https://img.shields.io/badge/platform-iOS-black?style=flat" alt="iOS only" /> <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
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|