@subscrypts/subscrypts-sdk-react 1.6.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 +2140 -0
- package/dist/components/buttons/SubscryptsButton.d.ts +19 -0
- package/dist/components/buttons/SubscryptsButton.d.ts.map +1 -0
- package/dist/components/buttons/index.d.ts +5 -0
- package/dist/components/buttons/index.d.ts.map +1 -0
- package/dist/components/checkout/CheckoutWizard.d.ts +22 -0
- package/dist/components/checkout/CheckoutWizard.d.ts.map +1 -0
- package/dist/components/checkout/ConfigurationStep.d.ts +15 -0
- package/dist/components/checkout/ConfigurationStep.d.ts.map +1 -0
- package/dist/components/checkout/TransactionStep.d.ts +15 -0
- package/dist/components/checkout/TransactionStep.d.ts.map +1 -0
- package/dist/components/checkout/index.d.ts +7 -0
- package/dist/components/checkout/index.d.ts.map +1 -0
- package/dist/components/guards/SubscriptionGuard.d.ts +25 -0
- package/dist/components/guards/SubscriptionGuard.d.ts.map +1 -0
- package/dist/components/guards/index.d.ts +5 -0
- package/dist/components/guards/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +20 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/merchant/MerchantDashboard.d.ts +24 -0
- package/dist/components/merchant/MerchantDashboard.d.ts.map +1 -0
- package/dist/components/merchant/index.d.ts +6 -0
- package/dist/components/merchant/index.d.ts.map +1 -0
- package/dist/components/pricing/PlanCard.d.ts +45 -0
- package/dist/components/pricing/PlanCard.d.ts.map +1 -0
- package/dist/components/pricing/PricingTable.d.ts +68 -0
- package/dist/components/pricing/PricingTable.d.ts.map +1 -0
- package/dist/components/pricing/index.d.ts +10 -0
- package/dist/components/pricing/index.d.ts.map +1 -0
- package/dist/components/shared/ErrorDisplay.d.ts +34 -0
- package/dist/components/shared/ErrorDisplay.d.ts.map +1 -0
- package/dist/components/shared/LoadingSpinner.d.ts +5 -0
- package/dist/components/shared/LoadingSpinner.d.ts.map +1 -0
- package/dist/components/shared/Modal.d.ts +12 -0
- package/dist/components/shared/Modal.d.ts.map +1 -0
- package/dist/components/shared/NetworkSwitchPrompt.d.ts +33 -0
- package/dist/components/shared/NetworkSwitchPrompt.d.ts.map +1 -0
- package/dist/components/shared/SubscryptsErrorBoundary.d.ts +41 -0
- package/dist/components/shared/SubscryptsErrorBoundary.d.ts.map +1 -0
- package/dist/components/subscription/ConfirmDialog.d.ts +44 -0
- package/dist/components/subscription/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/subscription/ManageSubscriptionModal.d.ts +35 -0
- package/dist/components/subscription/ManageSubscriptionModal.d.ts.map +1 -0
- package/dist/components/subscription/SubscriptionCard.d.ts +35 -0
- package/dist/components/subscription/SubscriptionCard.d.ts.map +1 -0
- package/dist/components/subscription/SubscriptionDashboard.d.ts +40 -0
- package/dist/components/subscription/SubscriptionDashboard.d.ts.map +1 -0
- package/dist/components/subscription/index.d.ts +12 -0
- package/dist/components/subscription/index.d.ts.map +1 -0
- package/dist/components/wallet/ConnectWalletModal.d.ts +29 -0
- package/dist/components/wallet/ConnectWalletModal.d.ts.map +1 -0
- package/dist/components/wallet/index.d.ts +6 -0
- package/dist/components/wallet/index.d.ts.map +1 -0
- package/dist/constants/index.d.ts +16 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/context/SubscryptsContext.d.ts +36 -0
- package/dist/context/SubscryptsContext.d.ts.map +1 -0
- package/dist/context/SubscryptsProvider.d.ts +28 -0
- package/dist/context/SubscryptsProvider.d.ts.map +1 -0
- package/dist/context/index.d.ts +7 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/contract/abis/Subscrypts.d.ts +2917 -0
- package/dist/contract/abis/Subscrypts.d.ts.map +1 -0
- package/dist/contract/abis/dexFactoryABI.d.ts +184 -0
- package/dist/contract/abis/dexFactoryABI.d.ts.map +1 -0
- package/dist/contract/abis/dexPairABI.d.ts +775 -0
- package/dist/contract/abis/dexPairABI.d.ts.map +1 -0
- package/dist/contract/abis/dexPositionManagerABI.d.ts +948 -0
- package/dist/contract/abis/dexPositionManagerABI.d.ts.map +1 -0
- package/dist/contract/abis/dexQuoterABI.d.ts +206 -0
- package/dist/contract/abis/dexQuoterABI.d.ts.map +1 -0
- package/dist/contract/abis/dexRouterABI.d.ts +439 -0
- package/dist/contract/abis/dexRouterABI.d.ts.map +1 -0
- package/dist/contract/abis/dexUSDCABI.d.ts +132 -0
- package/dist/contract/abis/dexUSDCABI.d.ts.map +1 -0
- package/dist/contract/abis/index.d.ts +15 -0
- package/dist/contract/abis/index.d.ts.map +1 -0
- package/dist/contract/config.d.ts +40 -0
- package/dist/contract/config.d.ts.map +1 -0
- package/dist/contract/index.d.ts +12 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/methods.d.ts +305 -0
- package/dist/contract/methods.d.ts.map +1 -0
- package/dist/hooks/events/index.d.ts +6 -0
- package/dist/hooks/events/index.d.ts.map +1 -0
- package/dist/hooks/events/useSubscryptsEvents.d.ts +63 -0
- package/dist/hooks/events/useSubscryptsEvents.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +12 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/merchant/index.d.ts +10 -0
- package/dist/hooks/merchant/index.d.ts.map +1 -0
- package/dist/hooks/merchant/useMerchantPlans.d.ts +30 -0
- package/dist/hooks/merchant/useMerchantPlans.d.ts.map +1 -0
- package/dist/hooks/merchant/useMerchantRevenue.d.ts +59 -0
- package/dist/hooks/merchant/useMerchantRevenue.d.ts.map +1 -0
- package/dist/hooks/merchant/useMerchantSubscribers.d.ts +54 -0
- package/dist/hooks/merchant/useMerchantSubscribers.d.ts.map +1 -0
- package/dist/hooks/plans/index.d.ts +12 -0
- package/dist/hooks/plans/index.d.ts.map +1 -0
- package/dist/hooks/plans/usePlan.d.ts +33 -0
- package/dist/hooks/plans/usePlan.d.ts.map +1 -0
- package/dist/hooks/plans/usePlans.d.ts +36 -0
- package/dist/hooks/plans/usePlans.d.ts.map +1 -0
- package/dist/hooks/plans/usePlansByMerchant.d.ts +35 -0
- package/dist/hooks/plans/usePlansByMerchant.d.ts.map +1 -0
- package/dist/hooks/pricing/index.d.ts +10 -0
- package/dist/hooks/pricing/index.d.ts.map +1 -0
- package/dist/hooks/pricing/usePlanPrice.d.ts +59 -0
- package/dist/hooks/pricing/usePlanPrice.d.ts.map +1 -0
- package/dist/hooks/pricing/useSUBSPrice.d.ts +39 -0
- package/dist/hooks/pricing/useSUBSPrice.d.ts.map +1 -0
- package/dist/hooks/subscriptions/index.d.ts +12 -0
- package/dist/hooks/subscriptions/index.d.ts.map +1 -0
- package/dist/hooks/subscriptions/useManageSubscription.d.ts +39 -0
- package/dist/hooks/subscriptions/useManageSubscription.d.ts.map +1 -0
- package/dist/hooks/subscriptions/useMySubscriptions.d.ts +53 -0
- package/dist/hooks/subscriptions/useMySubscriptions.d.ts.map +1 -0
- package/dist/hooks/subscriptions/useSubscribe.d.ts +58 -0
- package/dist/hooks/subscriptions/useSubscribe.d.ts.map +1 -0
- package/dist/hooks/subscriptions/useSubscriptionStatus.d.ts +34 -0
- package/dist/hooks/subscriptions/useSubscriptionStatus.d.ts.map +1 -0
- package/dist/hooks/tokens/index.d.ts +6 -0
- package/dist/hooks/tokens/index.d.ts.map +1 -0
- package/dist/hooks/tokens/useTokenBalance.d.ts +36 -0
- package/dist/hooks/tokens/useTokenBalance.d.ts.map +1 -0
- package/dist/hooks/wallet/index.d.ts +6 -0
- package/dist/hooks/wallet/index.d.ts.map +1 -0
- package/dist/hooks/wallet/useWallet.d.ts +40 -0
- package/dist/hooks/wallet/useWallet.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7850 -0
- package/dist/index.js.map +1 -0
- package/dist/services/cache.service.d.ts +89 -0
- package/dist/services/cache.service.d.ts.map +1 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/token.service.d.ts +43 -0
- package/dist/services/token.service.d.ts.map +1 -0
- package/dist/services/wallet.service.d.ts +46 -0
- package/dist/services/wallet.service.d.ts.map +1 -0
- package/dist/style.css +3 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/types/component.types.d.ts +192 -0
- package/dist/types/component.types.d.ts.map +1 -0
- package/dist/types/contract.types.d.ts +81 -0
- package/dist/types/contract.types.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/subscription.types.d.ts +65 -0
- package/dist/types/subscription.types.d.ts.map +1 -0
- package/dist/types/wallet.types.d.ts +53 -0
- package/dist/types/wallet.types.d.ts.map +1 -0
- package/dist/utils/errorMessages.d.ts +44 -0
- package/dist/utils/errorMessages.d.ts.map +1 -0
- package/dist/utils/errors.d.ts +60 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/formatters.d.ts +44 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +80 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/permit.utils.d.ts +52 -0
- package/dist/utils/permit.utils.d.ts.map +1 -0
- package/dist/utils/subscriptionHelpers.d.ts +67 -0
- package/dist/utils/subscriptionHelpers.d.ts.map +1 -0
- package/dist/utils/subscriptionStatus.d.ts +48 -0
- package/dist/utils/subscriptionStatus.d.ts.map +1 -0
- package/dist/utils/validators.d.ts +76 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/wallet/ExternalConnector.d.ts +32 -0
- package/dist/wallet/ExternalConnector.d.ts.map +1 -0
- package/dist/wallet/InjectedConnector.d.ts +55 -0
- package/dist/wallet/InjectedConnector.d.ts.map +1 -0
- package/dist/wallet/index.d.ts +9 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/sessionStore.d.ts +28 -0
- package/dist/wallet/sessionStore.d.ts.map +1 -0
- package/dist/wallet/types.d.ts +66 -0
- package/dist/wallet/types.d.ts.map +1 -0
- package/package.json +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,2140 @@
|
|
|
1
|
+
# @subscrypts/subscrypts-sdk-react
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**Official React SDK for Subscrypts**
|
|
6
|
+
|
|
7
|
+
Decentralized subscription payments on Arbitrum - Built for developers of all skill levels
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@subscrypts/subscrypts-sdk-react)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
|
|
13
|
+
[Documentation](#documentation) • [Quick Start](#quick-start-guide) • [Examples](#complete-examples) • [API Reference](#api-reference)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📖 Table of Contents
|
|
20
|
+
|
|
21
|
+
- [What is Subscrypts?](#-what-is-subscrypts)
|
|
22
|
+
- [Features](#-features)
|
|
23
|
+
- [Prerequisites](#-prerequisites)
|
|
24
|
+
- [Installation](#-installation)
|
|
25
|
+
- [Quick Start Guide](#-quick-start-guide)
|
|
26
|
+
- [1. Basic Setup (5 minutes)](#1-basic-setup-5-minutes)
|
|
27
|
+
- [2. Protect Content with Subscriptions](#2-protect-content-with-subscriptions)
|
|
28
|
+
- [3. Add a Subscribe Button](#3-add-a-subscribe-button)
|
|
29
|
+
- [Core Concepts](#-core-concepts)
|
|
30
|
+
- [Complete Examples](#-complete-examples)
|
|
31
|
+
- [API Reference](#-api-reference)
|
|
32
|
+
- [Components](#components)
|
|
33
|
+
- [Hooks](#hooks)
|
|
34
|
+
- [Types](#types)
|
|
35
|
+
- [Advanced Usage](#-advanced-usage)
|
|
36
|
+
- [Styling & Customization](#-styling--customization)
|
|
37
|
+
- [Troubleshooting](#-troubleshooting)
|
|
38
|
+
- [FAQ](#-faq)
|
|
39
|
+
- [Support](#-support)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🚀 What is Subscrypts?
|
|
44
|
+
|
|
45
|
+
Subscrypts is a **decentralized subscription protocol** built on Arbitrum that enables Web3 applications to accept recurring payments in cryptocurrency. Think "Stripe for Web3" - but fully decentralized, with no intermediaries.
|
|
46
|
+
|
|
47
|
+
This React SDK makes it incredibly easy to:
|
|
48
|
+
- ✅ Add subscription-based access control to your app
|
|
49
|
+
- ✅ Accept payments in SUBS tokens or USDC
|
|
50
|
+
- ✅ Manage user subscriptions on the blockchain
|
|
51
|
+
- ✅ Build custom subscription UIs with headless hooks
|
|
52
|
+
|
|
53
|
+
**Perfect for:** Premium content sites, SaaS platforms, membership communities, paywalled APIs, and any application requiring recurring payments.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## ✨ Features
|
|
58
|
+
|
|
59
|
+
### 🎨 **Pre-built UI Components**
|
|
60
|
+
Drop-in React components that work out of the box - no blockchain expertise required.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<SubscriptionGuard planId="123">
|
|
64
|
+
<YourPremiumContent />
|
|
65
|
+
</SubscriptionGuard>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 🎯 **Headless Hooks API**
|
|
69
|
+
Complete control over UI with powerful, composable hooks.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
const { status, isActive } = useSubscriptionStatus('1');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 🔐 **Built-in Access Control**
|
|
76
|
+
Automatically protect routes and components based on subscription status.
|
|
77
|
+
|
|
78
|
+
### 💳 **Dual Payment Support**
|
|
79
|
+
Accept payments in both SUBS tokens (native) and USDC (with auto-swap).
|
|
80
|
+
|
|
81
|
+
### 🎭 **Three Wallet Modes**
|
|
82
|
+
- **Internal Mode**: Built-in wallet connection (MetaMask, browser wallets)
|
|
83
|
+
- **External Mode**: Integrate with existing Wagmi/RainbowKit setups
|
|
84
|
+
- **Connector Mode**: Pluggable wallet architecture for custom providers (Privy, Web3Auth, etc.)
|
|
85
|
+
|
|
86
|
+
### 🔄 **Session Persistence**
|
|
87
|
+
Wallet connections are remembered across page reloads - no popup on return visits.
|
|
88
|
+
|
|
89
|
+
### 🛡️ **Error Recovery**
|
|
90
|
+
Human-readable error messages with retry actions for blockchain errors.
|
|
91
|
+
|
|
92
|
+
### 📱 **Mobile Responsive**
|
|
93
|
+
All components work perfectly on desktop and mobile devices.
|
|
94
|
+
|
|
95
|
+
### 🌳 **Tree-Shakeable**
|
|
96
|
+
Import only what you need - optimized bundle sizes.
|
|
97
|
+
|
|
98
|
+
### 📘 **TypeScript First**
|
|
99
|
+
Full type safety with comprehensive TypeScript definitions.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 📋 Prerequisites
|
|
104
|
+
|
|
105
|
+
Before you start, make sure you have:
|
|
106
|
+
|
|
107
|
+
1. **Node.js 16+** installed ([Download here](https://nodejs.org/))
|
|
108
|
+
2. **A React app** (React 18+ or React 19+)
|
|
109
|
+
3. **Basic understanding of React** (hooks, components, props)
|
|
110
|
+
4. **A MetaMask wallet** (for testing) ([Install here](https://metamask.io/))
|
|
111
|
+
|
|
112
|
+
**Don't have a React app yet?** Create one:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Using Create React App
|
|
116
|
+
npx create-react-app my-subscription-app
|
|
117
|
+
cd my-subscription-app
|
|
118
|
+
|
|
119
|
+
# Or using Vite (recommended)
|
|
120
|
+
npm create vite@latest my-subscription-app -- --template react-ts
|
|
121
|
+
cd my-subscription-app
|
|
122
|
+
npm install
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 📦 Installation
|
|
128
|
+
|
|
129
|
+
### Step 1: Install the SDK
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm install @subscrypts/subscrypts-sdk-react ethers
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Why ethers?** It's the library we use to interact with the blockchain. Don't worry - you won't need to use it directly!
|
|
136
|
+
|
|
137
|
+
### Step 2: That's it!
|
|
138
|
+
|
|
139
|
+
You're ready to add subscriptions to your app. Let's go! 🎉
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🎓 Quick Start Guide
|
|
144
|
+
|
|
145
|
+
### 1. Basic Setup (5 minutes)
|
|
146
|
+
|
|
147
|
+
First, wrap your app with the `SubscryptsProvider`. This gives all components access to wallet and subscription data.
|
|
148
|
+
|
|
149
|
+
**src/App.tsx** or **src/App.jsx**
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
import { SubscryptsProvider } from '@subscrypts/subscrypts-sdk-react';
|
|
153
|
+
import '@subscrypts/subscrypts-sdk-react/styles'; // Import default styles
|
|
154
|
+
|
|
155
|
+
function App() {
|
|
156
|
+
return (
|
|
157
|
+
<SubscryptsProvider
|
|
158
|
+
enableWalletManagement={true}
|
|
159
|
+
defaultNetwork={42161} // Arbitrum One mainnet
|
|
160
|
+
>
|
|
161
|
+
<YourAppContent />
|
|
162
|
+
</SubscryptsProvider>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export default App;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**What did we just do?**
|
|
170
|
+
- `SubscryptsProvider`: Makes subscription features available throughout your app
|
|
171
|
+
- `enableWalletManagement={true}`: Enables built-in wallet connection (MetaMask, etc.)
|
|
172
|
+
- Import styles: Loads the pre-built CSS for all components
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### 2. Protect Content with Subscriptions
|
|
177
|
+
|
|
178
|
+
Use `SubscriptionGuard` to protect any content. Only users with active subscriptions can see what's inside.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { SubscriptionGuard } from '@subscrypts/subscrypts-sdk-react';
|
|
182
|
+
|
|
183
|
+
function MyApp() {
|
|
184
|
+
return (
|
|
185
|
+
<div>
|
|
186
|
+
<h1>Welcome to My Premium Site</h1>
|
|
187
|
+
|
|
188
|
+
{/* Anyone can see this */}
|
|
189
|
+
<p>This is free content everyone can see!</p>
|
|
190
|
+
|
|
191
|
+
{/* Only subscribers can see this */}
|
|
192
|
+
<SubscriptionGuard
|
|
193
|
+
planId="1"
|
|
194
|
+
fallbackUrl="/subscribe"
|
|
195
|
+
>
|
|
196
|
+
<div className="premium-content">
|
|
197
|
+
<h2>Premium Content 🔒</h2>
|
|
198
|
+
<p>This exclusive content is only visible to subscribers!</p>
|
|
199
|
+
<video src="/premium-video.mp4" controls />
|
|
200
|
+
</div>
|
|
201
|
+
</SubscriptionGuard>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**What's happening here?**
|
|
208
|
+
- Users **without** a subscription see nothing (or get redirected to `/subscribe`)
|
|
209
|
+
- Users **with** an active subscription see the premium content
|
|
210
|
+
- The SDK automatically checks the blockchain for subscription status
|
|
211
|
+
|
|
212
|
+
**Where do I get `planId`?**
|
|
213
|
+
You'll receive the plan ID when you create a subscription plan on the Subscrypts contract. This is the unique identifier for your subscription plan on the blockchain.
|
|
214
|
+
|
|
215
|
+
**Important:** Plan IDs must be numeric strings representing the on-chain plan ID. The smart contract auto-increments plan IDs starting from 1. Use numeric strings like `"1"`, `"2"`, `"42"` - NOT descriptive names like `"premium-plan"`.
|
|
216
|
+
|
|
217
|
+
Examples:
|
|
218
|
+
- ✅ Correct: `planId="1"`, `planId="42"`
|
|
219
|
+
- ❌ Wrong: `planId="premium-plan"`, `planId="basic"`
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### 3. Add a Subscribe Button
|
|
224
|
+
|
|
225
|
+
Let users subscribe with a single button click! The checkout flow is handled automatically.
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { SubscryptsButton } from '@subscrypts/subscrypts-sdk-react';
|
|
229
|
+
|
|
230
|
+
function SubscribePage() {
|
|
231
|
+
const handleSuccess = (subscriptionId) => {
|
|
232
|
+
console.log('Subscribed! ID:', subscriptionId);
|
|
233
|
+
// Redirect to premium content or show success message
|
|
234
|
+
window.location.href = '/dashboard';
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div className="subscribe-page">
|
|
239
|
+
<h1>Subscribe to Premium</h1>
|
|
240
|
+
<p>Get access to exclusive content for only 10 SUBS/month!</p>
|
|
241
|
+
|
|
242
|
+
<SubscryptsButton
|
|
243
|
+
planId="2"
|
|
244
|
+
variant="primary"
|
|
245
|
+
size="lg"
|
|
246
|
+
onSuccess={handleSuccess}
|
|
247
|
+
>
|
|
248
|
+
Subscribe Now - 10 SUBS/month
|
|
249
|
+
</SubscryptsButton>
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**What happens when users click the button?**
|
|
256
|
+
1. If wallet isn't connected → Connect wallet prompt appears
|
|
257
|
+
2. If wallet is connected → Checkout modal opens
|
|
258
|
+
3. User selects payment method (SUBS or USDC)
|
|
259
|
+
4. User chooses subscription duration (12, 24, 36 months, or custom)
|
|
260
|
+
5. User approves transactions in their wallet
|
|
261
|
+
6. `onSuccess` callback fires with the subscription ID
|
|
262
|
+
7. User now has access to premium content! 🎉
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## ⚡ Gas Optimization & Passive Collection
|
|
267
|
+
|
|
268
|
+
The Subscrypts contract includes an automatic subscription renewal feature called **Passive Collection**.
|
|
269
|
+
|
|
270
|
+
### What is Passive Collection?
|
|
271
|
+
|
|
272
|
+
On every SUBS token transfer, the contract automatically processes up to `subscriptionCollectPassiveMax` expired subscriptions from the network. This ensures subscriptions are renewed regularly without requiring manual collection.
|
|
273
|
+
|
|
274
|
+
### Impact on Your Transactions
|
|
275
|
+
|
|
276
|
+
**Gas Costs**: SUBS transfers may cost 20-30% more gas than standard ERC20 transfers due to passive collection processing.
|
|
277
|
+
|
|
278
|
+
**Transaction Logs**: You may see subscription renewal events unrelated to your transfer.
|
|
279
|
+
|
|
280
|
+
**Best Practice**: Always add gas buffer to SUBS transfer operations:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const estimatedGas = await subsContract.estimateGas.transfer(to, amount);
|
|
284
|
+
const gasLimit = (estimatedGas * 130n) / 100n; // 30% buffer for passive collection
|
|
285
|
+
|
|
286
|
+
await subsContract.transfer(to, amount, { gasLimit });
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Why It Exists
|
|
290
|
+
|
|
291
|
+
Passive collection is a form of "gas socialism" where all users help maintain the subscription network. Instead of requiring merchants or subscribers to manually trigger renewals, the network collectively processes them during normal token activity.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 🧠 Core Concepts
|
|
296
|
+
|
|
297
|
+
### Merchants and Plans
|
|
298
|
+
|
|
299
|
+
- **Merchant**: Your business/project on Subscrypts (you!)
|
|
300
|
+
- **Plan**: A subscription tier you offer (e.g., "Basic", "Premium", "Enterprise")
|
|
301
|
+
- Each plan has a unique `planId` and cost in SUBS tokens
|
|
302
|
+
|
|
303
|
+
### Subscription Status
|
|
304
|
+
|
|
305
|
+
Subscriptions can be:
|
|
306
|
+
- **Active**: User has paid and can access content
|
|
307
|
+
- **Expired**: Subscription period ended
|
|
308
|
+
- **Auto-renewing**: Subscription automatically renews each cycle
|
|
309
|
+
- **Manual**: User must manually renew
|
|
310
|
+
|
|
311
|
+
### Payment Methods
|
|
312
|
+
|
|
313
|
+
1. **SUBS Tokens** (native):
|
|
314
|
+
- Direct payment with SUBS tokens
|
|
315
|
+
- Lower gas fees
|
|
316
|
+
- Preferred method
|
|
317
|
+
|
|
318
|
+
2. **USDC** (with auto-swap):
|
|
319
|
+
- Pay with USDC stablecoin
|
|
320
|
+
- Automatically swapped to SUBS via Uniswap
|
|
321
|
+
- Great for users who prefer stablecoins
|
|
322
|
+
|
|
323
|
+
### Wallet Modes
|
|
324
|
+
|
|
325
|
+
**Internal Mode** (Easiest):
|
|
326
|
+
```tsx
|
|
327
|
+
<SubscryptsProvider enableWalletManagement={true}>
|
|
328
|
+
```
|
|
329
|
+
SDK handles wallet connection for you using browser extensions (MetaMask, etc.)
|
|
330
|
+
|
|
331
|
+
**External Mode** (Wagmi/RainbowKit):
|
|
332
|
+
```tsx
|
|
333
|
+
<SubscryptsProvider
|
|
334
|
+
enableWalletManagement={false}
|
|
335
|
+
externalProvider={yourWagmiSigner}
|
|
336
|
+
>
|
|
337
|
+
```
|
|
338
|
+
Use when you already have wallet management (Wagmi, RainbowKit, etc.)
|
|
339
|
+
|
|
340
|
+
**Connector Mode** (Custom Providers):
|
|
341
|
+
```tsx
|
|
342
|
+
import { InjectedConnector } from '@subscrypts/subscrypts-sdk-react';
|
|
343
|
+
|
|
344
|
+
<SubscryptsProvider connectors={[new InjectedConnector(), myPrivyConnector]}>
|
|
345
|
+
```
|
|
346
|
+
Pluggable architecture - implement the `WalletConnector` interface for any provider.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## 📚 Complete Examples
|
|
351
|
+
|
|
352
|
+
### Example 1: Simple Paywall
|
|
353
|
+
|
|
354
|
+
Protect a single page or component:
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
import { SubscriptionGuard, SubscryptsButton } from '@subscrypts/subscrypts-sdk-react';
|
|
358
|
+
|
|
359
|
+
function PremiumArticle() {
|
|
360
|
+
return (
|
|
361
|
+
<article>
|
|
362
|
+
<h1>The Future of Web3 Subscriptions</h1>
|
|
363
|
+
|
|
364
|
+
{/* Free preview */}
|
|
365
|
+
<p>
|
|
366
|
+
Decentralized subscriptions are revolutionizing how we think about
|
|
367
|
+
recurring payments in Web3...
|
|
368
|
+
</p>
|
|
369
|
+
|
|
370
|
+
{/* Premium content */}
|
|
371
|
+
<SubscriptionGuard
|
|
372
|
+
planId="1"
|
|
373
|
+
fallbackUrl="/subscribe"
|
|
374
|
+
>
|
|
375
|
+
<div className="premium-section">
|
|
376
|
+
<h2>Deep Dive Analysis</h2>
|
|
377
|
+
<p>
|
|
378
|
+
[Full article content only visible to subscribers...]
|
|
379
|
+
</p>
|
|
380
|
+
</div>
|
|
381
|
+
</SubscriptionGuard>
|
|
382
|
+
|
|
383
|
+
{/* Subscribe CTA */}
|
|
384
|
+
<div className="subscribe-cta">
|
|
385
|
+
<h3>Want to read more?</h3>
|
|
386
|
+
<SubscryptsButton planId="1">
|
|
387
|
+
Subscribe for $5/month
|
|
388
|
+
</SubscryptsButton>
|
|
389
|
+
</div>
|
|
390
|
+
</article>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
### Example 2: Subscription Status Badge
|
|
398
|
+
|
|
399
|
+
Show users their current subscription status:
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
import { useSubscriptionStatus } from '@subscrypts/subscrypts-sdk-react';
|
|
403
|
+
|
|
404
|
+
function SubscriptionBadge() {
|
|
405
|
+
const { status, isLoading } = useSubscriptionStatus('1');
|
|
406
|
+
|
|
407
|
+
if (isLoading) {
|
|
408
|
+
return <div>Checking subscription...</div>;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!status || !status.isActive) {
|
|
412
|
+
return (
|
|
413
|
+
<div className="badge free">
|
|
414
|
+
<span>Free Tier</span>
|
|
415
|
+
<a href="/subscribe">Upgrade to Premium</a>
|
|
416
|
+
</div>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return (
|
|
421
|
+
<div className="badge premium">
|
|
422
|
+
<span>✓ Premium Member</span>
|
|
423
|
+
<p>
|
|
424
|
+
{status.isAutoRenewing
|
|
425
|
+
? `Renews ${status.expirationDate.toLocaleDateString()}`
|
|
426
|
+
: `Expires ${status.expirationDate.toLocaleDateString()}`
|
|
427
|
+
}
|
|
428
|
+
</p>
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
### Example 3: Custom Checkout Flow
|
|
437
|
+
|
|
438
|
+
Build your own checkout UI using hooks:
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
import { useState } from 'react';
|
|
442
|
+
import { useSubscribe, useWallet, useTokenBalance } from '@subscrypts/subscrypts-sdk-react';
|
|
443
|
+
|
|
444
|
+
function CustomCheckout({ planId }) {
|
|
445
|
+
const [cycles, setCycles] = useState(12);
|
|
446
|
+
const [autoRenew, setAutoRenew] = useState(true);
|
|
447
|
+
const [paymentMethod, setPaymentMethod] = useState('SUBS');
|
|
448
|
+
|
|
449
|
+
const { isConnected, connect } = useWallet();
|
|
450
|
+
const { subscribe, txState, error } = useSubscribe();
|
|
451
|
+
const { formatted: subsBalance } = useTokenBalance('SUBS');
|
|
452
|
+
const { formatted: usdcBalance } = useTokenBalance('USDC');
|
|
453
|
+
|
|
454
|
+
const handleSubscribe = async () => {
|
|
455
|
+
try {
|
|
456
|
+
const subscriptionId = await subscribe({
|
|
457
|
+
planId,
|
|
458
|
+
cycleLimit: cycles,
|
|
459
|
+
autoRenew,
|
|
460
|
+
paymentMethod
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
alert(`Success! Subscription ID: ${subscriptionId}`);
|
|
464
|
+
} catch (err) {
|
|
465
|
+
console.error('Subscription failed:', err);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
if (!isConnected) {
|
|
470
|
+
return <button onClick={connect}>Connect Wallet</button>;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return (
|
|
474
|
+
<div className="custom-checkout">
|
|
475
|
+
<h2>Subscribe to Premium</h2>
|
|
476
|
+
|
|
477
|
+
{/* Duration selector */}
|
|
478
|
+
<div>
|
|
479
|
+
<label>Subscription Length:</label>
|
|
480
|
+
<select value={cycles} onChange={(e) => setCycles(Number(e.target.value))}>
|
|
481
|
+
<option value={12}>12 months</option>
|
|
482
|
+
<option value={24}>24 months</option>
|
|
483
|
+
<option value={36}>36 months</option>
|
|
484
|
+
</select>
|
|
485
|
+
</div>
|
|
486
|
+
|
|
487
|
+
{/* Auto-renewal toggle */}
|
|
488
|
+
<div>
|
|
489
|
+
<label>
|
|
490
|
+
<input
|
|
491
|
+
type="checkbox"
|
|
492
|
+
checked={autoRenew}
|
|
493
|
+
onChange={(e) => setAutoRenew(e.target.checked)}
|
|
494
|
+
/>
|
|
495
|
+
Auto-renew subscription
|
|
496
|
+
</label>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
{/* Payment method */}
|
|
500
|
+
<div>
|
|
501
|
+
<label>Payment Method:</label>
|
|
502
|
+
<div>
|
|
503
|
+
<button
|
|
504
|
+
onClick={() => setPaymentMethod('SUBS')}
|
|
505
|
+
className={paymentMethod === 'SUBS' ? 'active' : ''}
|
|
506
|
+
>
|
|
507
|
+
SUBS (Balance: {subsBalance})
|
|
508
|
+
</button>
|
|
509
|
+
<button
|
|
510
|
+
onClick={() => setPaymentMethod('USDC')}
|
|
511
|
+
className={paymentMethod === 'USDC' ? 'active' : ''}
|
|
512
|
+
>
|
|
513
|
+
USDC (Balance: {usdcBalance})
|
|
514
|
+
</button>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
{/* Subscribe button */}
|
|
519
|
+
<button
|
|
520
|
+
onClick={handleSubscribe}
|
|
521
|
+
disabled={txState !== 'idle'}
|
|
522
|
+
>
|
|
523
|
+
{txState === 'idle' && 'Subscribe Now'}
|
|
524
|
+
{txState === 'approving' && 'Approving...'}
|
|
525
|
+
{txState === 'subscribing' && 'Processing...'}
|
|
526
|
+
{txState === 'success' && 'Success!'}
|
|
527
|
+
</button>
|
|
528
|
+
|
|
529
|
+
{error && <div className="error">{error.message}</div>}
|
|
530
|
+
</div>
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
### Example 4: Multi-Tier Pricing
|
|
538
|
+
|
|
539
|
+
Offer multiple subscription tiers:
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
import { SubscryptsButton } from '@subscrypts/subscrypts-sdk-react';
|
|
543
|
+
|
|
544
|
+
function PricingPage() {
|
|
545
|
+
const plans = [
|
|
546
|
+
{
|
|
547
|
+
id: '1',
|
|
548
|
+
name: 'Basic',
|
|
549
|
+
price: '5 SUBS',
|
|
550
|
+
features: ['Access to articles', 'Email support']
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: '2',
|
|
554
|
+
name: 'Premium',
|
|
555
|
+
price: '10 SUBS',
|
|
556
|
+
features: ['Everything in Basic', 'Video content', 'Priority support']
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: '3',
|
|
560
|
+
name: 'Enterprise',
|
|
561
|
+
price: '20 SUBS',
|
|
562
|
+
features: ['Everything in Premium', 'API access', 'Custom integrations']
|
|
563
|
+
}
|
|
564
|
+
];
|
|
565
|
+
|
|
566
|
+
return (
|
|
567
|
+
<div className="pricing-grid">
|
|
568
|
+
{plans.map((plan) => (
|
|
569
|
+
<div key={plan.id} className="pricing-card">
|
|
570
|
+
<h3>{plan.name}</h3>
|
|
571
|
+
<div className="price">{plan.price}/month</div>
|
|
572
|
+
<ul>
|
|
573
|
+
{plan.features.map((feature) => (
|
|
574
|
+
<li key={feature}>✓ {feature}</li>
|
|
575
|
+
))}
|
|
576
|
+
</ul>
|
|
577
|
+
<SubscryptsButton
|
|
578
|
+
planId={plan.id}
|
|
579
|
+
variant="primary"
|
|
580
|
+
onSuccess={(id) => {
|
|
581
|
+
console.log(`Subscribed to ${plan.name}:`, id);
|
|
582
|
+
window.location.href = '/dashboard';
|
|
583
|
+
}}
|
|
584
|
+
>
|
|
585
|
+
Choose {plan.name}
|
|
586
|
+
</SubscryptsButton>
|
|
587
|
+
</div>
|
|
588
|
+
))}
|
|
589
|
+
</div>
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## 📖 API Reference
|
|
597
|
+
|
|
598
|
+
### Components
|
|
599
|
+
|
|
600
|
+
#### `<SubscryptsProvider>`
|
|
601
|
+
|
|
602
|
+
**Required wrapper** for all Subscrypts components and hooks.
|
|
603
|
+
|
|
604
|
+
```tsx
|
|
605
|
+
<SubscryptsProvider
|
|
606
|
+
enableWalletManagement={true}
|
|
607
|
+
defaultNetwork={42161}
|
|
608
|
+
externalProvider={yourProvider} // Optional
|
|
609
|
+
>
|
|
610
|
+
<YourApp />
|
|
611
|
+
</SubscryptsProvider>
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Props:**
|
|
615
|
+
|
|
616
|
+
| Prop | Type | Required | Default | Description |
|
|
617
|
+
|------|------|----------|---------|-------------|
|
|
618
|
+
| `enableWalletManagement` | `boolean` | No | `true` | Enable built-in wallet connection |
|
|
619
|
+
| `externalProvider` | `ExternalWalletConfig` | No | - | Use external wallet (Wagmi/RainbowKit) |
|
|
620
|
+
| `connectors` | `WalletConnector[]` | No | - | Custom wallet connectors (overrides enableWalletManagement) |
|
|
621
|
+
| `persistSession` | `boolean` | No | `true` | Remember wallet across page reloads |
|
|
622
|
+
| `onAccountChange` | `(newAddr, oldAddr) => void` | No | - | Callback when wallet account changes |
|
|
623
|
+
| `onChainChange` | `(newChainId, oldChainId) => void` | No | - | Callback when network changes |
|
|
624
|
+
| `debug` | `'silent' \| 'info' \| 'debug'` | No | `'info'` | Logging level |
|
|
625
|
+
| `children` | `ReactNode` | Yes | - | Your app components |
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
#### `<SubscriptionGuard>`
|
|
630
|
+
|
|
631
|
+
**Protect content** based on subscription status.
|
|
632
|
+
|
|
633
|
+
```tsx
|
|
634
|
+
// Single plan
|
|
635
|
+
<SubscriptionGuard planId="1" fallbackUrl="/subscribe">
|
|
636
|
+
<PremiumContent />
|
|
637
|
+
</SubscriptionGuard>
|
|
638
|
+
|
|
639
|
+
// Multi-plan: any of these grants access
|
|
640
|
+
<SubscriptionGuard planIds={['1', '2', '3']}>
|
|
641
|
+
<PremiumContent />
|
|
642
|
+
</SubscriptionGuard>
|
|
643
|
+
|
|
644
|
+
// Multi-plan: require ALL plans
|
|
645
|
+
<SubscriptionGuard planIds={['1', '2']} requireAll>
|
|
646
|
+
<BundleContent />
|
|
647
|
+
</SubscriptionGuard>
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Props:**
|
|
651
|
+
|
|
652
|
+
| Prop | Type | Required | Default | Description |
|
|
653
|
+
|------|------|----------|---------|-------------|
|
|
654
|
+
| `planId` | `string` | No | - | Single plan ID to check |
|
|
655
|
+
| `planIds` | `string[]` | No | - | Multiple plan IDs to check |
|
|
656
|
+
| `requireAll` | `boolean` | No | `false` | Require ALL plans (true) or ANY plan (false) |
|
|
657
|
+
| `fallbackUrl` | `string` | No | - | Redirect URL when subscription is inactive |
|
|
658
|
+
| `loadingComponent` | `ReactNode` | No | - | Custom loading indicator |
|
|
659
|
+
| `onAccessDenied` | `() => void` | No | - | Callback when access is denied |
|
|
660
|
+
| `children` | `ReactNode` | Yes | - | Content to protect |
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
#### `<SubscryptsButton>`
|
|
665
|
+
|
|
666
|
+
**One-click subscribe button** with built-in checkout flow.
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
<SubscryptsButton
|
|
670
|
+
planId="2"
|
|
671
|
+
variant="primary"
|
|
672
|
+
size="lg"
|
|
673
|
+
referralAddress="0x..."
|
|
674
|
+
onSuccess={(id) => console.log('Subscribed:', id)}
|
|
675
|
+
onError={(err) => console.error(err)}
|
|
676
|
+
>
|
|
677
|
+
Subscribe Now
|
|
678
|
+
</SubscryptsButton>
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
**Props:**
|
|
682
|
+
|
|
683
|
+
| Prop | Type | Required | Default | Description |
|
|
684
|
+
|------|------|----------|---------|-------------|
|
|
685
|
+
| `planId` | `string` | Yes | - | The plan ID to subscribe to |
|
|
686
|
+
| `variant` | `'primary'` \| `'secondary'` \| `'outline'` | No | `'primary'` | Button style variant |
|
|
687
|
+
| `size` | `'sm'` \| `'md'` \| `'lg'` | No | `'md'` | Button size |
|
|
688
|
+
| `referralAddress` | `string` | No | - | Ethereum address for referral rewards |
|
|
689
|
+
| `onSuccess` | `(id: string) => void` | No | - | Callback when subscription succeeds |
|
|
690
|
+
| `onError` | `(error: Error) => void` | No | - | Callback when subscription fails |
|
|
691
|
+
| `children` | `ReactNode` | No | `'Subscribe'` | Button text |
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
#### `<CheckoutWizard>`
|
|
696
|
+
|
|
697
|
+
**Full checkout modal** with multi-step flow. Usually used via `SubscryptsButton`, but can be used standalone.
|
|
698
|
+
|
|
699
|
+
```tsx
|
|
700
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
701
|
+
|
|
702
|
+
<CheckoutWizard
|
|
703
|
+
planId="2"
|
|
704
|
+
isOpen={isOpen}
|
|
705
|
+
onClose={() => setIsOpen(false)}
|
|
706
|
+
onSuccess={(id) => alert('Success!')}
|
|
707
|
+
/>
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Props:**
|
|
711
|
+
|
|
712
|
+
| Prop | Type | Required | Description |
|
|
713
|
+
|------|------|----------|-------------|
|
|
714
|
+
| `planId` | `string` | Yes | The plan ID to subscribe to |
|
|
715
|
+
| `isOpen` | `boolean` | Yes | Modal open state |
|
|
716
|
+
| `onClose` | `() => void` | Yes | Close modal callback |
|
|
717
|
+
| `referralAddress` | `string` | No | Referral address |
|
|
718
|
+
| `onSuccess` | `(id: string) => void` | No | Success callback |
|
|
719
|
+
| `onError` | `(error: Error) => void` | No | Error callback |
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
#### `<PricingTable>`
|
|
724
|
+
|
|
725
|
+
**Display multiple subscription plans** in a responsive grid with built-in checkout.
|
|
726
|
+
|
|
727
|
+
```tsx
|
|
728
|
+
<PricingTable
|
|
729
|
+
plans={[
|
|
730
|
+
{ planId: '1', title: 'Basic', subscribeLabel: 'Start Free' },
|
|
731
|
+
{ planId: '2', title: 'Pro', featured: true, subscribeLabel: 'Go Pro' },
|
|
732
|
+
{ planId: '3', title: 'Enterprise', subscribeLabel: 'Contact Us' }
|
|
733
|
+
]}
|
|
734
|
+
currency="SUBS"
|
|
735
|
+
showFields={['description', 'amount', 'frequency', 'subscribers']}
|
|
736
|
+
columns={3}
|
|
737
|
+
/>
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
**Props:**
|
|
741
|
+
|
|
742
|
+
| Prop | Type | Required | Default | Description |
|
|
743
|
+
|------|------|----------|---------|-------------|
|
|
744
|
+
| `plans` | `(string \| PlanConfig)[]` | Yes | - | Array of plan IDs or configurations |
|
|
745
|
+
| `currency` | `'SUBS' \| 'USDC'` | No | `'SUBS'` | Currency for prices |
|
|
746
|
+
| `showFields` | `PlanField[]` | No | `['description', 'amount', 'frequency']` | Fields to display |
|
|
747
|
+
| `columns` | `1 \| 2 \| 3 \| 4` | No | auto | Grid columns |
|
|
748
|
+
| `onSubscribe` | `(planId: string) => void` | No | - | Custom subscribe handler |
|
|
749
|
+
| `referralAddress` | `string` | No | - | Referral for all subscriptions |
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
#### `<PlanCard>`
|
|
754
|
+
|
|
755
|
+
**Single plan display card** with configurable fields.
|
|
756
|
+
|
|
757
|
+
```tsx
|
|
758
|
+
<PlanCard
|
|
759
|
+
plan={plan}
|
|
760
|
+
currency="SUBS"
|
|
761
|
+
showFields={['description', 'amount', 'frequency']}
|
|
762
|
+
onSubscribe={(id) => openCheckout(id)}
|
|
763
|
+
featured={true}
|
|
764
|
+
title="Premium Plan"
|
|
765
|
+
/>
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Props:**
|
|
769
|
+
|
|
770
|
+
| Prop | Type | Required | Default | Description |
|
|
771
|
+
|------|------|----------|---------|-------------|
|
|
772
|
+
| `plan` | `Plan` | Yes | - | Plan data from smart contract |
|
|
773
|
+
| `currency` | `'SUBS' \| 'USDC'` | No | `'SUBS'` | Currency for price display |
|
|
774
|
+
| `showFields` | `PlanField[]` | No | `['description', 'amount', 'frequency']` | Fields to show |
|
|
775
|
+
| `onSubscribe` | `(planId: string) => void` | No | - | Subscribe click handler |
|
|
776
|
+
| `featured` | `boolean` | No | `false` | Highlight this plan |
|
|
777
|
+
| `title` | `string` | No | - | Custom title (overrides description) |
|
|
778
|
+
|
|
779
|
+
**Available `PlanField` values:** `'description'`, `'amount'`, `'frequency'`, `'subscribers'`, `'merchant'`, `'referralBonus'`, `'attributes'`
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
#### `<ErrorDisplay>`
|
|
784
|
+
|
|
785
|
+
**Human-readable error messages** for blockchain errors with retry support.
|
|
786
|
+
|
|
787
|
+
```tsx
|
|
788
|
+
import { ErrorDisplay } from '@subscrypts/subscrypts-sdk-react';
|
|
789
|
+
|
|
790
|
+
<ErrorDisplay
|
|
791
|
+
error={transactionError}
|
|
792
|
+
onRetry={() => retryTransaction()}
|
|
793
|
+
onDismiss={() => clearError()}
|
|
794
|
+
/>
|
|
795
|
+
|
|
796
|
+
// Compact variant for inline use
|
|
797
|
+
<ErrorDisplay error={error} compact />
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
**Props:**
|
|
801
|
+
|
|
802
|
+
| Prop | Type | Required | Default | Description |
|
|
803
|
+
|------|------|----------|---------|-------------|
|
|
804
|
+
| `error` | `Error \| null` | Yes | - | Error to display (renders nothing if null) |
|
|
805
|
+
| `onRetry` | `() => void` | No | - | Retry callback (shows retry button if provided) |
|
|
806
|
+
| `onDismiss` | `() => void` | No | - | Dismiss callback (shows dismiss button if provided) |
|
|
807
|
+
| `compact` | `boolean` | No | `false` | Compact inline display |
|
|
808
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
#### `<NetworkSwitchPrompt>`
|
|
813
|
+
|
|
814
|
+
**Prompt users to switch** to Arbitrum One when on the wrong network.
|
|
815
|
+
|
|
816
|
+
```tsx
|
|
817
|
+
import { NetworkSwitchPrompt } from '@subscrypts/subscrypts-sdk-react';
|
|
818
|
+
|
|
819
|
+
<NetworkSwitchPrompt
|
|
820
|
+
currentChainId={chainId}
|
|
821
|
+
onSwitch={() => switchToArbitrum()}
|
|
822
|
+
/>
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
**Props:**
|
|
826
|
+
|
|
827
|
+
| Prop | Type | Required | Default | Description |
|
|
828
|
+
|------|------|----------|---------|-------------|
|
|
829
|
+
| `currentChainId` | `number \| null` | Yes | - | User's current chain ID |
|
|
830
|
+
| `onSwitch` | `() => void` | Yes | - | Callback to trigger network switch |
|
|
831
|
+
| `onDismiss` | `() => void` | No | - | Dismiss callback |
|
|
832
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
#### `<SubscryptsErrorBoundary>`
|
|
837
|
+
|
|
838
|
+
**Catch and display React errors** with reset capability.
|
|
839
|
+
|
|
840
|
+
```tsx
|
|
841
|
+
import { SubscryptsErrorBoundary } from '@subscrypts/subscrypts-sdk-react';
|
|
842
|
+
|
|
843
|
+
<SubscryptsErrorBoundary
|
|
844
|
+
onError={(error) => logToService(error)}
|
|
845
|
+
fallback={(error, reset) => (
|
|
846
|
+
<div>
|
|
847
|
+
<p>Something went wrong: {error.message}</p>
|
|
848
|
+
<button onClick={reset}>Try Again</button>
|
|
849
|
+
</div>
|
|
850
|
+
)}
|
|
851
|
+
>
|
|
852
|
+
<YourComponents />
|
|
853
|
+
</SubscryptsErrorBoundary>
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
**Props:**
|
|
857
|
+
|
|
858
|
+
| Prop | Type | Required | Default | Description |
|
|
859
|
+
|------|------|----------|---------|-------------|
|
|
860
|
+
| `children` | `ReactNode` | Yes | - | Components to protect |
|
|
861
|
+
| `fallback` | `ReactNode \| (error, reset) => ReactNode` | No | Default error UI | Custom error display |
|
|
862
|
+
| `onError` | `(error: Error) => void` | No | - | Error logging callback |
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
#### `<ConnectWalletModal>`
|
|
867
|
+
|
|
868
|
+
**Wallet selection modal** that lists available connectors.
|
|
869
|
+
|
|
870
|
+
```tsx
|
|
871
|
+
import { ConnectWalletModal } from '@subscrypts/subscrypts-sdk-react';
|
|
872
|
+
|
|
873
|
+
<ConnectWalletModal
|
|
874
|
+
isOpen={showWalletModal}
|
|
875
|
+
onClose={() => setShowWalletModal(false)}
|
|
876
|
+
connectors={connectors}
|
|
877
|
+
onConnect={connectWith}
|
|
878
|
+
/>
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
**Props:**
|
|
882
|
+
|
|
883
|
+
| Prop | Type | Required | Default | Description |
|
|
884
|
+
|------|------|----------|---------|-------------|
|
|
885
|
+
| `isOpen` | `boolean` | Yes | - | Modal visibility |
|
|
886
|
+
| `onClose` | `() => void` | Yes | - | Close callback |
|
|
887
|
+
| `connectors` | `WalletConnector[]` | Yes | - | Available wallet connectors |
|
|
888
|
+
| `onConnect` | `(connectorId: string) => Promise<void>` | Yes | - | Connect handler |
|
|
889
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
#### `<ManageSubscriptionModal>`
|
|
894
|
+
|
|
895
|
+
**Manage an existing subscription** with cancel, auto-renewal toggle, and cycle updates.
|
|
896
|
+
|
|
897
|
+
```tsx
|
|
898
|
+
import { ManageSubscriptionModal } from '@subscrypts/subscrypts-sdk-react';
|
|
899
|
+
|
|
900
|
+
<ManageSubscriptionModal
|
|
901
|
+
isOpen={showManage}
|
|
902
|
+
onClose={() => setShowManage(false)}
|
|
903
|
+
subscriptionId="42"
|
|
904
|
+
subscription={subscriptionData}
|
|
905
|
+
onCancelled={() => refetchSubscriptions()}
|
|
906
|
+
onUpdated={() => refetchSubscriptions()}
|
|
907
|
+
/>
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
**Props:**
|
|
911
|
+
|
|
912
|
+
| Prop | Type | Required | Default | Description |
|
|
913
|
+
|------|------|----------|---------|-------------|
|
|
914
|
+
| `isOpen` | `boolean` | Yes | - | Modal visibility |
|
|
915
|
+
| `onClose` | `() => void` | Yes | - | Close callback |
|
|
916
|
+
| `subscriptionId` | `string` | Yes | - | Subscription ID to manage |
|
|
917
|
+
| `subscription` | `Subscription` | No | - | Pre-loaded subscription data |
|
|
918
|
+
| `onCancelled` | `() => void` | No | - | Called after successful cancellation |
|
|
919
|
+
| `onUpdated` | `() => void` | No | - | Called after successful update |
|
|
920
|
+
|
|
921
|
+
---
|
|
922
|
+
|
|
923
|
+
#### `<ConfirmDialog>`
|
|
924
|
+
|
|
925
|
+
**Reusable confirmation dialog** for destructive or important actions.
|
|
926
|
+
|
|
927
|
+
```tsx
|
|
928
|
+
import { ConfirmDialog } from '@subscrypts/subscrypts-sdk-react';
|
|
929
|
+
|
|
930
|
+
<ConfirmDialog
|
|
931
|
+
isOpen={showConfirm}
|
|
932
|
+
title="Cancel Subscription?"
|
|
933
|
+
message="Your subscription will remain active until the end of the current period."
|
|
934
|
+
variant="danger"
|
|
935
|
+
confirmLabel="Cancel Subscription"
|
|
936
|
+
onConfirm={handleCancel}
|
|
937
|
+
onCancel={() => setShowConfirm(false)}
|
|
938
|
+
/>
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Props:**
|
|
942
|
+
|
|
943
|
+
| Prop | Type | Required | Default | Description |
|
|
944
|
+
|------|------|----------|---------|-------------|
|
|
945
|
+
| `isOpen` | `boolean` | Yes | - | Dialog visibility |
|
|
946
|
+
| `title` | `string` | Yes | - | Dialog title |
|
|
947
|
+
| `message` | `string` | Yes | - | Dialog message |
|
|
948
|
+
| `confirmLabel` | `string` | No | `'Confirm'` | Confirm button text |
|
|
949
|
+
| `cancelLabel` | `string` | No | `'Cancel'` | Cancel button text |
|
|
950
|
+
| `variant` | `'danger' \| 'default'` | No | `'default'` | Visual variant (danger = red button) |
|
|
951
|
+
| `onConfirm` | `() => void` | Yes | - | Confirm callback |
|
|
952
|
+
| `onCancel` | `() => void` | Yes | - | Cancel callback |
|
|
953
|
+
|
|
954
|
+
---
|
|
955
|
+
|
|
956
|
+
#### `<SubscriptionCard>`
|
|
957
|
+
|
|
958
|
+
**Display subscription details** with status badge and manage button.
|
|
959
|
+
|
|
960
|
+
```tsx
|
|
961
|
+
import { SubscriptionCard } from '@subscrypts/subscrypts-sdk-react';
|
|
962
|
+
|
|
963
|
+
<SubscriptionCard
|
|
964
|
+
subscription={subscription}
|
|
965
|
+
showManageButton={true}
|
|
966
|
+
onCancelled={() => refetchSubscriptions()}
|
|
967
|
+
/>
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Props:**
|
|
971
|
+
|
|
972
|
+
| Prop | Type | Required | Default | Description |
|
|
973
|
+
|------|------|----------|---------|-------------|
|
|
974
|
+
| `subscription` | `Subscription` | Yes | - | Subscription to display |
|
|
975
|
+
| `showManageButton` | `boolean` | No | `true` | Show manage button |
|
|
976
|
+
| `showFiatPrice` | `boolean` | No | `false` | Show fiat price |
|
|
977
|
+
| `onManage` | `(id: string) => void` | No | - | Custom manage handler |
|
|
978
|
+
| `onCancelled` | `() => void` | No | - | Called after cancellation |
|
|
979
|
+
| `onUpdated` | `() => void` | No | - | Called after update |
|
|
980
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
#### `<SubscriptionDashboard>`
|
|
985
|
+
|
|
986
|
+
**Complete subscription management dashboard** with pagination.
|
|
987
|
+
|
|
988
|
+
```tsx
|
|
989
|
+
import { SubscriptionDashboard } from '@subscrypts/subscrypts-sdk-react';
|
|
990
|
+
|
|
991
|
+
<SubscriptionDashboard
|
|
992
|
+
pageSize={10}
|
|
993
|
+
showFiatPrices={true}
|
|
994
|
+
onSubscriptionCancelled={(id) => console.log('Cancelled:', id)}
|
|
995
|
+
/>
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
**Props:**
|
|
999
|
+
|
|
1000
|
+
| Prop | Type | Required | Default | Description |
|
|
1001
|
+
|------|------|----------|---------|-------------|
|
|
1002
|
+
| `address` | `string` | No | Connected wallet | Address to fetch subscriptions for |
|
|
1003
|
+
| `pageSize` | `number` | No | `10` | Subscriptions per page |
|
|
1004
|
+
| `showFiatPrices` | `boolean` | No | `false` | Show fiat prices on cards |
|
|
1005
|
+
| `emptyComponent` | `React.ReactNode` | No | Default message | Custom empty state |
|
|
1006
|
+
| `loadingComponent` | `React.ReactNode` | No | Spinner | Custom loading state |
|
|
1007
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
1008
|
+
| `onSubscriptionCancelled` | `(id: string) => void` | No | - | Called after cancellation |
|
|
1009
|
+
| `onSubscriptionUpdated` | `(id: string) => void` | No | - | Called after update |
|
|
1010
|
+
|
|
1011
|
+
---
|
|
1012
|
+
|
|
1013
|
+
#### `<MerchantDashboard>`
|
|
1014
|
+
|
|
1015
|
+
**Complete merchant dashboard** with revenue, plans, and subscribers.
|
|
1016
|
+
|
|
1017
|
+
```tsx
|
|
1018
|
+
import { MerchantDashboard } from '@subscrypts/subscrypts-sdk-react';
|
|
1019
|
+
|
|
1020
|
+
<MerchantDashboard />
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**Props:**
|
|
1024
|
+
|
|
1025
|
+
| Prop | Type | Required | Default | Description |
|
|
1026
|
+
|------|------|----------|---------|-------------|
|
|
1027
|
+
| `merchantAddress` | `string` | No | Connected wallet | Merchant address |
|
|
1028
|
+
| `className` | `string` | No | - | Additional CSS class |
|
|
1029
|
+
|
|
1030
|
+
**Features:**
|
|
1031
|
+
- Revenue overview card (MRR in SUBS/USD, active vs total subscribers)
|
|
1032
|
+
- Grid of merchant's plans with subscriber counts
|
|
1033
|
+
- Click plan to view subscriber details
|
|
1034
|
+
- Empty state for new merchants
|
|
1035
|
+
|
|
1036
|
+
---
|
|
1037
|
+
|
|
1038
|
+
### Hooks
|
|
1039
|
+
|
|
1040
|
+
#### `useSubscriptionStatus`
|
|
1041
|
+
|
|
1042
|
+
**Check if user has an active subscription.** This is your primary access control hook.
|
|
1043
|
+
|
|
1044
|
+
```tsx
|
|
1045
|
+
const { status, isLoading, error, refetch } = useSubscriptionStatus(
|
|
1046
|
+
'1', // Plan ID
|
|
1047
|
+
'0x...' // Optional: check different address
|
|
1048
|
+
);
|
|
1049
|
+
|
|
1050
|
+
if (status?.isActive) {
|
|
1051
|
+
// User has active subscription
|
|
1052
|
+
}
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
**Returns:**
|
|
1056
|
+
|
|
1057
|
+
```typescript
|
|
1058
|
+
{
|
|
1059
|
+
status: {
|
|
1060
|
+
isActive: boolean; // Whether subscription is active
|
|
1061
|
+
expirationDate: Date | null; // When subscription expires
|
|
1062
|
+
isAutoRenewing: boolean; // Auto-renewal enabled
|
|
1063
|
+
remainingCycles: number; // Cycles remaining
|
|
1064
|
+
subscriptionId: string | null; // Blockchain subscription ID
|
|
1065
|
+
} | null;
|
|
1066
|
+
isLoading: boolean; // Loading state
|
|
1067
|
+
error: Error | null; // Error if any
|
|
1068
|
+
refetch: () => Promise<void>; // Manually refresh status
|
|
1069
|
+
}
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
#### `useSubscribe`
|
|
1075
|
+
|
|
1076
|
+
**Execute subscription transactions.** Creates new subscriptions with SUBS or USDC.
|
|
1077
|
+
|
|
1078
|
+
```tsx
|
|
1079
|
+
const { subscribe, isSubscribing, txState, error, subscriptionId } = useSubscribe();
|
|
1080
|
+
|
|
1081
|
+
const handleSubscribe = async () => {
|
|
1082
|
+
const subId = await subscribe({
|
|
1083
|
+
planId: '2',
|
|
1084
|
+
cycleLimit: 12,
|
|
1085
|
+
autoRenew: true,
|
|
1086
|
+
paymentMethod: 'SUBS'
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
console.log('Subscription ID:', subId);
|
|
1090
|
+
};
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
**Returns:**
|
|
1094
|
+
|
|
1095
|
+
```typescript
|
|
1096
|
+
{
|
|
1097
|
+
subscribe: (params: SubscribeParams) => Promise<string>;
|
|
1098
|
+
isSubscribing: boolean; // Transaction in progress
|
|
1099
|
+
txState: 'idle' | 'approving' | 'subscribing' | 'success' | 'error';
|
|
1100
|
+
error: Error | null; // Error if any
|
|
1101
|
+
txHash: string | null; // Transaction hash
|
|
1102
|
+
subscriptionId: string | null; // Created subscription ID
|
|
1103
|
+
}
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
**Subscribe Params:**
|
|
1107
|
+
|
|
1108
|
+
```typescript
|
|
1109
|
+
{
|
|
1110
|
+
planId: string; // Plan to subscribe to
|
|
1111
|
+
cycleLimit: number; // Number of payment cycles
|
|
1112
|
+
autoRenew: boolean; // Enable auto-renewal
|
|
1113
|
+
paymentMethod: 'SUBS' | 'USDC'; // Payment token
|
|
1114
|
+
referralAddress?: string; // Optional referral
|
|
1115
|
+
}
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
#### `useWallet`
|
|
1121
|
+
|
|
1122
|
+
**Access wallet connection state, actions, and connector information.**
|
|
1123
|
+
|
|
1124
|
+
```tsx
|
|
1125
|
+
const {
|
|
1126
|
+
isConnected,
|
|
1127
|
+
address,
|
|
1128
|
+
chainId,
|
|
1129
|
+
connect,
|
|
1130
|
+
disconnect,
|
|
1131
|
+
connectors,
|
|
1132
|
+
activeConnector,
|
|
1133
|
+
connectWith
|
|
1134
|
+
} = useWallet();
|
|
1135
|
+
|
|
1136
|
+
if (!isConnected) {
|
|
1137
|
+
return <button onClick={connect}>Connect Wallet</button>;
|
|
1138
|
+
}
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
**Returns:**
|
|
1142
|
+
|
|
1143
|
+
```typescript
|
|
1144
|
+
{
|
|
1145
|
+
isConnected: boolean; // Wallet connected
|
|
1146
|
+
address: string | null; // User's wallet address
|
|
1147
|
+
chainId: number | null; // Connected network ID
|
|
1148
|
+
connect?: () => Promise<void>; // Connect wallet
|
|
1149
|
+
disconnect?: () => Promise<void>; // Disconnect wallet
|
|
1150
|
+
switchNetwork: (chainId: number) => Promise<void>; // Switch network
|
|
1151
|
+
connectors: WalletConnector[]; // Available wallet connectors
|
|
1152
|
+
activeConnector: WalletConnector | null; // Currently active connector
|
|
1153
|
+
connectWith: (connectorId: string) => Promise<void>; // Connect with specific connector
|
|
1154
|
+
}
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
---
|
|
1158
|
+
|
|
1159
|
+
#### `useTokenBalance`
|
|
1160
|
+
|
|
1161
|
+
**Get SUBS or USDC balance** for connected wallet.
|
|
1162
|
+
|
|
1163
|
+
```tsx
|
|
1164
|
+
const { balance, formatted, isLoading, refetch } = useTokenBalance('SUBS');
|
|
1165
|
+
|
|
1166
|
+
console.log(`Balance: ${formatted} SUBS`); // "Balance: 100.50 SUBS"
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
**Returns:**
|
|
1170
|
+
|
|
1171
|
+
```typescript
|
|
1172
|
+
{
|
|
1173
|
+
balance: bigint | null; // Raw balance (wei)
|
|
1174
|
+
formatted: string; // Human-readable balance
|
|
1175
|
+
isLoading: boolean; // Loading state
|
|
1176
|
+
refetch: () => Promise<void>; // Refresh balance
|
|
1177
|
+
}
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
---
|
|
1181
|
+
|
|
1182
|
+
#### `usePlan`
|
|
1183
|
+
|
|
1184
|
+
**Fetch a single plan** from the smart contract.
|
|
1185
|
+
|
|
1186
|
+
```tsx
|
|
1187
|
+
const { plan, isLoading, error, refetch } = usePlan('1');
|
|
1188
|
+
|
|
1189
|
+
if (plan) {
|
|
1190
|
+
console.log(`Plan: ${plan.description}, Amount: ${plan.subscriptionAmount}`);
|
|
1191
|
+
}
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
**Returns:**
|
|
1195
|
+
|
|
1196
|
+
```typescript
|
|
1197
|
+
{
|
|
1198
|
+
plan: Plan | null; // Plan data
|
|
1199
|
+
isLoading: boolean; // Loading state
|
|
1200
|
+
error: Error | null; // Error if any
|
|
1201
|
+
refetch: () => Promise<void>; // Refresh plan
|
|
1202
|
+
}
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
---
|
|
1206
|
+
|
|
1207
|
+
#### `usePlans`
|
|
1208
|
+
|
|
1209
|
+
**Fetch multiple plans** in parallel from the smart contract.
|
|
1210
|
+
|
|
1211
|
+
```tsx
|
|
1212
|
+
const { plans, isLoading, error, refetch } = usePlans(['1', '2', '3']);
|
|
1213
|
+
|
|
1214
|
+
plans.forEach(plan => {
|
|
1215
|
+
console.log(plan.description);
|
|
1216
|
+
});
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
**Returns:**
|
|
1220
|
+
|
|
1221
|
+
```typescript
|
|
1222
|
+
{
|
|
1223
|
+
plans: Plan[]; // Array of plans
|
|
1224
|
+
isLoading: boolean; // Loading state
|
|
1225
|
+
error: Error | null; // Error if any
|
|
1226
|
+
refetch: () => Promise<void>; // Refresh all plans
|
|
1227
|
+
}
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
#### `useSUBSPrice`
|
|
1233
|
+
|
|
1234
|
+
**Fetch the current SUBS/USD price** from the on-chain oracle.
|
|
1235
|
+
|
|
1236
|
+
```tsx
|
|
1237
|
+
const { priceUsd, isLoading, refetch } = useSUBSPrice();
|
|
1238
|
+
|
|
1239
|
+
if (priceUsd) {
|
|
1240
|
+
console.log(`1 SUBS = $${priceUsd.toFixed(4)}`);
|
|
1241
|
+
}
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
**Returns:**
|
|
1245
|
+
|
|
1246
|
+
```typescript
|
|
1247
|
+
{
|
|
1248
|
+
priceUsd: number | null; // 1 SUBS = X USD
|
|
1249
|
+
rawPrice: bigint | null; // Raw 18-decimal value
|
|
1250
|
+
isLoading: boolean;
|
|
1251
|
+
error: Error | null;
|
|
1252
|
+
refetch: () => Promise<void>;
|
|
1253
|
+
}
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
---
|
|
1257
|
+
|
|
1258
|
+
#### `usePlanPrice`
|
|
1259
|
+
|
|
1260
|
+
**Get comprehensive price info** for a plan (SUBS, USDC, USD).
|
|
1261
|
+
|
|
1262
|
+
```tsx
|
|
1263
|
+
const { price, isLoading } = usePlanPrice('1');
|
|
1264
|
+
|
|
1265
|
+
if (price) {
|
|
1266
|
+
console.log(`${price.subsFormatted} SUBS / ${price.frequency}`);
|
|
1267
|
+
if (price.usdValue) console.log(`≈ ${formatFiatPrice(price.usdValue)}`);
|
|
1268
|
+
}
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
**Returns:**
|
|
1272
|
+
|
|
1273
|
+
```typescript
|
|
1274
|
+
{
|
|
1275
|
+
price: {
|
|
1276
|
+
subsAmount: bigint; // Price in SUBS (18 decimals)
|
|
1277
|
+
subsFormatted: string; // e.g. "10.5000"
|
|
1278
|
+
usdcAmount: bigint | null; // USDC equivalent (6 decimals)
|
|
1279
|
+
usdcFormatted: string | null; // e.g. "5.25"
|
|
1280
|
+
usdValue: number | null; // USD display value
|
|
1281
|
+
frequency: string; // "Monthly", "Weekly", etc.
|
|
1282
|
+
isUsdDenominated: boolean;
|
|
1283
|
+
} | null;
|
|
1284
|
+
isLoading: boolean;
|
|
1285
|
+
error: Error | null;
|
|
1286
|
+
refetch: () => Promise<void>;
|
|
1287
|
+
}
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
---
|
|
1291
|
+
|
|
1292
|
+
#### `useManageSubscription`
|
|
1293
|
+
|
|
1294
|
+
**Manage an existing subscription**: cancel, toggle auto-renewal, update cycles.
|
|
1295
|
+
|
|
1296
|
+
```tsx
|
|
1297
|
+
const {
|
|
1298
|
+
cancelSubscription,
|
|
1299
|
+
toggleAutoRenew,
|
|
1300
|
+
updateCycles,
|
|
1301
|
+
isProcessing,
|
|
1302
|
+
txState
|
|
1303
|
+
} = useManageSubscription('42');
|
|
1304
|
+
|
|
1305
|
+
// Cancel
|
|
1306
|
+
await cancelSubscription();
|
|
1307
|
+
|
|
1308
|
+
// Toggle auto-renewal
|
|
1309
|
+
await toggleAutoRenew(false);
|
|
1310
|
+
|
|
1311
|
+
// Set remaining cycles
|
|
1312
|
+
await updateCycles(12);
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
**Returns:**
|
|
1316
|
+
|
|
1317
|
+
```typescript
|
|
1318
|
+
{
|
|
1319
|
+
cancelSubscription: () => Promise<void>;
|
|
1320
|
+
toggleAutoRenew: (enabled: boolean) => Promise<void>;
|
|
1321
|
+
updateCycles: (cycles: number) => Promise<void>;
|
|
1322
|
+
updateAttributes: (attributes: string) => Promise<void>;
|
|
1323
|
+
txState: TransactionState;
|
|
1324
|
+
error: Error | null;
|
|
1325
|
+
isProcessing: boolean;
|
|
1326
|
+
}
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
---
|
|
1330
|
+
|
|
1331
|
+
#### `usePlansByMerchant`
|
|
1332
|
+
|
|
1333
|
+
**Fetch all plans** created by a specific merchant address.
|
|
1334
|
+
|
|
1335
|
+
```tsx
|
|
1336
|
+
const { plans, total, isLoading } = usePlansByMerchant('0x1234...');
|
|
1337
|
+
|
|
1338
|
+
plans.forEach(plan => {
|
|
1339
|
+
console.log(plan.description, plan.subscriberCount.toString());
|
|
1340
|
+
});
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
**Returns:**
|
|
1344
|
+
|
|
1345
|
+
```typescript
|
|
1346
|
+
{
|
|
1347
|
+
plans: Plan[];
|
|
1348
|
+
total: number;
|
|
1349
|
+
isLoading: boolean;
|
|
1350
|
+
error: Error | null;
|
|
1351
|
+
refetch: () => Promise<void>;
|
|
1352
|
+
}
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
---
|
|
1356
|
+
|
|
1357
|
+
#### `useMySubscriptions`
|
|
1358
|
+
|
|
1359
|
+
**Fetch paginated subscriptions** for the connected wallet.
|
|
1360
|
+
|
|
1361
|
+
```tsx
|
|
1362
|
+
// Get all subscriptions (up to 100)
|
|
1363
|
+
const {
|
|
1364
|
+
subscriptions,
|
|
1365
|
+
page,
|
|
1366
|
+
hasMore,
|
|
1367
|
+
nextPage,
|
|
1368
|
+
prevPage,
|
|
1369
|
+
isLoading
|
|
1370
|
+
} = useMySubscriptions();
|
|
1371
|
+
|
|
1372
|
+
// Filter to specific plans (recommended for reliability)
|
|
1373
|
+
const {
|
|
1374
|
+
subscriptions,
|
|
1375
|
+
isLoading
|
|
1376
|
+
} = useMySubscriptions(undefined, 10, ['1', '2', '3']);
|
|
1377
|
+
|
|
1378
|
+
return (
|
|
1379
|
+
<div>
|
|
1380
|
+
{subscriptions.map(sub => (
|
|
1381
|
+
<SubscriptionCard key={sub.id} subscription={sub} />
|
|
1382
|
+
))}
|
|
1383
|
+
<button onClick={prevPage} disabled={page === 1}>Previous</button>
|
|
1384
|
+
<button onClick={nextPage} disabled={!hasMore}>Next</button>
|
|
1385
|
+
</div>
|
|
1386
|
+
);
|
|
1387
|
+
```
|
|
1388
|
+
|
|
1389
|
+
**Parameters:**
|
|
1390
|
+
|
|
1391
|
+
- `address?: string` - Optional wallet address (defaults to connected wallet)
|
|
1392
|
+
- `pageSize?: number` - Subscriptions per page (default: 10)
|
|
1393
|
+
- `planIds?: string[]` - Optional plan IDs to filter (v1.5.2+, enables automatic fallback if contract returns empty)
|
|
1394
|
+
|
|
1395
|
+
**Returns:**
|
|
1396
|
+
|
|
1397
|
+
```typescript
|
|
1398
|
+
{
|
|
1399
|
+
subscriptions: Subscription[];
|
|
1400
|
+
total: number;
|
|
1401
|
+
page: number;
|
|
1402
|
+
pageSize: number;
|
|
1403
|
+
hasMore: boolean;
|
|
1404
|
+
isLoading: boolean;
|
|
1405
|
+
error: Error | null;
|
|
1406
|
+
nextPage: () => void;
|
|
1407
|
+
prevPage: () => void;
|
|
1408
|
+
refetch: () => Promise<void>;
|
|
1409
|
+
}
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
---
|
|
1413
|
+
|
|
1414
|
+
#### `useSubscryptsEvents`
|
|
1415
|
+
|
|
1416
|
+
**Subscribe to real-time protocol events** for live updates.
|
|
1417
|
+
|
|
1418
|
+
```tsx
|
|
1419
|
+
useSubscryptsEvents({
|
|
1420
|
+
onSubscriptionCreated: (event) => {
|
|
1421
|
+
console.log('New subscription:', event.subscriptionId);
|
|
1422
|
+
refetchDashboard();
|
|
1423
|
+
},
|
|
1424
|
+
onSubscriptionPaid: (event) => {
|
|
1425
|
+
console.log('Payment made:', event.amount);
|
|
1426
|
+
},
|
|
1427
|
+
onSubscriptionStopped: (event) => {
|
|
1428
|
+
console.log('Subscription stopped:', event.subscriptionId);
|
|
1429
|
+
}
|
|
1430
|
+
});
|
|
1431
|
+
```
|
|
1432
|
+
|
|
1433
|
+
**Returns:**
|
|
1434
|
+
|
|
1435
|
+
```typescript
|
|
1436
|
+
{
|
|
1437
|
+
isListening: boolean;
|
|
1438
|
+
error: Error | null;
|
|
1439
|
+
}
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
---
|
|
1443
|
+
|
|
1444
|
+
#### `useMerchantPlans`
|
|
1445
|
+
|
|
1446
|
+
**Fetch all plans** owned by the connected wallet (merchant).
|
|
1447
|
+
|
|
1448
|
+
```tsx
|
|
1449
|
+
const { plans, total, isLoading } = useMerchantPlans();
|
|
1450
|
+
|
|
1451
|
+
plans.forEach(plan => {
|
|
1452
|
+
console.log(`${plan.description}: ${plan.subscriberCount.toString()} subscribers`);
|
|
1453
|
+
});
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
**Returns:** Same as `usePlansByMerchant` - wrapper using connected wallet address.
|
|
1457
|
+
|
|
1458
|
+
---
|
|
1459
|
+
|
|
1460
|
+
#### `useMerchantSubscribers`
|
|
1461
|
+
|
|
1462
|
+
**Fetch paginated subscribers** for a specific plan.
|
|
1463
|
+
|
|
1464
|
+
```tsx
|
|
1465
|
+
const {
|
|
1466
|
+
subscribers,
|
|
1467
|
+
total,
|
|
1468
|
+
activeCount,
|
|
1469
|
+
page,
|
|
1470
|
+
hasMore,
|
|
1471
|
+
nextPage,
|
|
1472
|
+
prevPage,
|
|
1473
|
+
isLoading
|
|
1474
|
+
} = useMerchantSubscribers('1');
|
|
1475
|
+
|
|
1476
|
+
console.log(`${activeCount} active out of ${total} subscribers`);
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
**Returns:**
|
|
1480
|
+
|
|
1481
|
+
```typescript
|
|
1482
|
+
{
|
|
1483
|
+
subscribers: Subscription[];
|
|
1484
|
+
total: number;
|
|
1485
|
+
activeCount: number;
|
|
1486
|
+
page: number;
|
|
1487
|
+
pageSize: number;
|
|
1488
|
+
hasMore: boolean;
|
|
1489
|
+
isLoading: boolean;
|
|
1490
|
+
error: Error | null;
|
|
1491
|
+
nextPage: () => void;
|
|
1492
|
+
prevPage: () => void;
|
|
1493
|
+
refetch: () => Promise<void>;
|
|
1494
|
+
}
|
|
1495
|
+
```
|
|
1496
|
+
|
|
1497
|
+
---
|
|
1498
|
+
|
|
1499
|
+
#### `useMerchantRevenue`
|
|
1500
|
+
|
|
1501
|
+
**Calculate Monthly Recurring Revenue (MRR)** from active subscriptions.
|
|
1502
|
+
|
|
1503
|
+
```tsx
|
|
1504
|
+
const { revenue, isLoading } = useMerchantRevenue();
|
|
1505
|
+
|
|
1506
|
+
if (revenue) {
|
|
1507
|
+
console.log(`MRR: ${revenue.mrrFormatted} SUBS`);
|
|
1508
|
+
console.log(`≈ $${revenue.mrrUsdEstimate?.toFixed(2)}`);
|
|
1509
|
+
console.log(`${revenue.activeSubscribers} / ${revenue.totalSubscribers} active`);
|
|
1510
|
+
}
|
|
1511
|
+
```
|
|
1512
|
+
|
|
1513
|
+
**Returns:**
|
|
1514
|
+
|
|
1515
|
+
```typescript
|
|
1516
|
+
{
|
|
1517
|
+
revenue: {
|
|
1518
|
+
totalSubscribers: number;
|
|
1519
|
+
activeSubscribers: number;
|
|
1520
|
+
monthlyRecurringRevenue: bigint;
|
|
1521
|
+
mrrFormatted: string;
|
|
1522
|
+
mrrUsdEstimate: number | null;
|
|
1523
|
+
} | null;
|
|
1524
|
+
isLoading: boolean;
|
|
1525
|
+
error: Error | null;
|
|
1526
|
+
refetch: () => Promise<void>;
|
|
1527
|
+
}
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1530
|
+
---
|
|
1531
|
+
|
|
1532
|
+
#### `useSubscrypts`
|
|
1533
|
+
|
|
1534
|
+
**Access full SDK context.** Advanced use cases only.
|
|
1535
|
+
|
|
1536
|
+
```tsx
|
|
1537
|
+
const {
|
|
1538
|
+
wallet,
|
|
1539
|
+
signer,
|
|
1540
|
+
provider,
|
|
1541
|
+
subscryptsContract,
|
|
1542
|
+
subsTokenContract,
|
|
1543
|
+
usdcTokenContract,
|
|
1544
|
+
subsBalance,
|
|
1545
|
+
usdcBalance,
|
|
1546
|
+
refreshBalances,
|
|
1547
|
+
network
|
|
1548
|
+
} = useSubscrypts();
|
|
1549
|
+
```
|
|
1550
|
+
|
|
1551
|
+
---
|
|
1552
|
+
|
|
1553
|
+
### Types
|
|
1554
|
+
|
|
1555
|
+
#### `Plan`
|
|
1556
|
+
|
|
1557
|
+
The Plan type represents a subscription plan created by a merchant on the Subscrypts smart contract.
|
|
1558
|
+
|
|
1559
|
+
```typescript
|
|
1560
|
+
interface Plan {
|
|
1561
|
+
id: bigint; // Plan ID (auto-incremented by contract)
|
|
1562
|
+
merchantAddress: string; // Creator's wallet address
|
|
1563
|
+
currencyCode: bigint; // 0 = SUBS, 1 = USD
|
|
1564
|
+
subscriptionAmount: bigint; // Price (18 decimals for SUBS)
|
|
1565
|
+
paymentFrequency: bigint; // Seconds between payments
|
|
1566
|
+
referralBonus: bigint; // Referral reward amount
|
|
1567
|
+
commission: bigint; // Protocol commission
|
|
1568
|
+
description: string; // Plan name/description (bytes32)
|
|
1569
|
+
defaultAttributes: string; // Default subscription attributes
|
|
1570
|
+
verificationExpiryDate: bigint; // Expiry timestamp
|
|
1571
|
+
subscriberCount: bigint; // Total subscriber count
|
|
1572
|
+
isActive: boolean; // Whether plan accepts subscriptions
|
|
1573
|
+
}
|
|
1574
|
+
```
|
|
1575
|
+
|
|
1576
|
+
**Note:** When passing planId to components and hooks, use numeric strings (e.g., `"1"`, `"42"`). These are automatically converted to bigint for blockchain calls.
|
|
1577
|
+
|
|
1578
|
+
---
|
|
1579
|
+
|
|
1580
|
+
#### `Subscription`
|
|
1581
|
+
|
|
1582
|
+
The Subscription type represents an active or past subscription from a wallet to a specific plan. This is the **full subscription data** returned by `useMySubscriptions()`, `useMerchantSubscribers()`, and used by `SubscriptionCard`.
|
|
1583
|
+
|
|
1584
|
+
```typescript
|
|
1585
|
+
interface Subscription {
|
|
1586
|
+
id: bigint; // Subscription ID (auto-incremented by contract)
|
|
1587
|
+
merchantAddress: string; // Plan owner's wallet address
|
|
1588
|
+
planId: bigint; // Associated plan ID
|
|
1589
|
+
subscriberAddress: string; // Subscriber's wallet address
|
|
1590
|
+
currencyCode: bigint; // 0 = SUBS, 1 = USD
|
|
1591
|
+
subscriptionAmount: bigint; // Subscription price (copied from plan)
|
|
1592
|
+
paymentFrequency: bigint; // Payment interval in seconds
|
|
1593
|
+
isRecurring: boolean; // Auto-renewal enabled
|
|
1594
|
+
remainingCycles: number; // Cycles left before expiration
|
|
1595
|
+
customAttributes: string; // Custom metadata (bytes32)
|
|
1596
|
+
lastPaymentDate: bigint; // Timestamp of last payment
|
|
1597
|
+
nextPaymentDate: bigint; // Timestamp when next payment is due
|
|
1598
|
+
}
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
**Important:** `useSubscriptionStatus()` returns a simplified `SubscriptionStatus` object (see below) rather than the full `Subscription` struct. The `SubscriptionStatus` is derived from the full subscription data for easier access control decisions.
|
|
1602
|
+
|
|
1603
|
+
---
|
|
1604
|
+
|
|
1605
|
+
#### `SubscriptionStatus`
|
|
1606
|
+
|
|
1607
|
+
```typescript
|
|
1608
|
+
interface SubscriptionStatus {
|
|
1609
|
+
isActive: boolean;
|
|
1610
|
+
expirationDate: Date | null;
|
|
1611
|
+
isAutoRenewing: boolean;
|
|
1612
|
+
remainingCycles: number;
|
|
1613
|
+
subscriptionId: string | null;
|
|
1614
|
+
}
|
|
1615
|
+
```
|
|
1616
|
+
|
|
1617
|
+
#### `PaymentMethod`
|
|
1618
|
+
|
|
1619
|
+
```typescript
|
|
1620
|
+
type PaymentMethod = 'SUBS' | 'USDC';
|
|
1621
|
+
```
|
|
1622
|
+
|
|
1623
|
+
#### `TransactionState`
|
|
1624
|
+
|
|
1625
|
+
```typescript
|
|
1626
|
+
type TransactionState =
|
|
1627
|
+
| 'idle'
|
|
1628
|
+
| 'approving'
|
|
1629
|
+
| 'waiting_approval'
|
|
1630
|
+
| 'subscribing'
|
|
1631
|
+
| 'waiting_subscribe'
|
|
1632
|
+
| 'success'
|
|
1633
|
+
| 'error';
|
|
1634
|
+
```
|
|
1635
|
+
|
|
1636
|
+
#### `PlanPriceInfo`
|
|
1637
|
+
|
|
1638
|
+
```typescript
|
|
1639
|
+
interface PlanPriceInfo {
|
|
1640
|
+
subsAmount: bigint; // Price in SUBS (18 decimals)
|
|
1641
|
+
subsFormatted: string; // e.g. "10.5000"
|
|
1642
|
+
usdcAmount: bigint | null; // USDC equivalent (6 decimals)
|
|
1643
|
+
usdcFormatted: string | null; // e.g. "5.25"
|
|
1644
|
+
usdValue: number | null; // USD display value
|
|
1645
|
+
frequency: string; // "Monthly", "Weekly", etc.
|
|
1646
|
+
isUsdDenominated: boolean; // Whether plan is USD-denominated
|
|
1647
|
+
}
|
|
1648
|
+
```
|
|
1649
|
+
|
|
1650
|
+
#### `SubscriptionHealth`
|
|
1651
|
+
|
|
1652
|
+
```typescript
|
|
1653
|
+
interface SubscriptionHealth {
|
|
1654
|
+
state: SubscriptionState; // 'active' | 'expired' | 'expiring-soon' | ...
|
|
1655
|
+
isPaymentDue: boolean;
|
|
1656
|
+
shouldRenew: boolean;
|
|
1657
|
+
daysUntilExpiry: number | null;
|
|
1658
|
+
cyclesRemaining: number;
|
|
1659
|
+
}
|
|
1660
|
+
```
|
|
1661
|
+
|
|
1662
|
+
---
|
|
1663
|
+
|
|
1664
|
+
## 🔧 Advanced Usage
|
|
1665
|
+
|
|
1666
|
+
### Using with Wagmi / RainbowKit
|
|
1667
|
+
|
|
1668
|
+
If you already have wallet management with Wagmi:
|
|
1669
|
+
|
|
1670
|
+
```tsx
|
|
1671
|
+
import { SubscryptsProvider } from '@subscrypts/subscrypts-sdk-react';
|
|
1672
|
+
import { useWalletClient } from 'wagmi';
|
|
1673
|
+
|
|
1674
|
+
function App() {
|
|
1675
|
+
const { data: walletClient } = useWalletClient();
|
|
1676
|
+
|
|
1677
|
+
return (
|
|
1678
|
+
<SubscryptsProvider
|
|
1679
|
+
enableWalletManagement={false}
|
|
1680
|
+
externalProvider={walletClient}
|
|
1681
|
+
>
|
|
1682
|
+
<YourApp />
|
|
1683
|
+
</SubscryptsProvider>
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
```
|
|
1687
|
+
|
|
1688
|
+
---
|
|
1689
|
+
|
|
1690
|
+
### Referral Program Integration
|
|
1691
|
+
|
|
1692
|
+
Earn rewards by referring users:
|
|
1693
|
+
|
|
1694
|
+
```tsx
|
|
1695
|
+
<SubscryptsButton
|
|
1696
|
+
planId="premium"
|
|
1697
|
+
referralAddress="0x1234..." // Your referral address
|
|
1698
|
+
onSuccess={(id) => console.log('Referral subscription:', id)}
|
|
1699
|
+
>
|
|
1700
|
+
Subscribe with My Referral
|
|
1701
|
+
</SubscryptsButton>
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
---
|
|
1705
|
+
|
|
1706
|
+
### Custom Transaction Handling
|
|
1707
|
+
|
|
1708
|
+
Monitor transaction states:
|
|
1709
|
+
|
|
1710
|
+
```tsx
|
|
1711
|
+
const { subscribe, txState, txHash } = useSubscribe();
|
|
1712
|
+
|
|
1713
|
+
useEffect(() => {
|
|
1714
|
+
if (txState === 'approving') {
|
|
1715
|
+
console.log('User is approving token spend...');
|
|
1716
|
+
} else if (txState === 'subscribing') {
|
|
1717
|
+
console.log('Creating subscription...');
|
|
1718
|
+
} else if (txState === 'success') {
|
|
1719
|
+
console.log('Success! Transaction:', txHash);
|
|
1720
|
+
}
|
|
1721
|
+
}, [txState, txHash]);
|
|
1722
|
+
```
|
|
1723
|
+
|
|
1724
|
+
---
|
|
1725
|
+
|
|
1726
|
+
### Error Handling
|
|
1727
|
+
|
|
1728
|
+
**Option 1: Use `getErrorMessage` for user-friendly error strings:**
|
|
1729
|
+
|
|
1730
|
+
```tsx
|
|
1731
|
+
import { getErrorMessage } from '@subscrypts/subscrypts-sdk-react';
|
|
1732
|
+
|
|
1733
|
+
try {
|
|
1734
|
+
await subscribe({ /* ... */ });
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
const errorInfo = getErrorMessage(error);
|
|
1737
|
+
// errorInfo.title: "Transaction Rejected"
|
|
1738
|
+
// errorInfo.message: "You rejected the transaction in your wallet."
|
|
1739
|
+
// errorInfo.suggestion: "Please try again and confirm the transaction."
|
|
1740
|
+
// errorInfo.isRetryable: true
|
|
1741
|
+
}
|
|
1742
|
+
```
|
|
1743
|
+
|
|
1744
|
+
**Option 2: Use `ErrorDisplay` component for automatic error rendering:**
|
|
1745
|
+
|
|
1746
|
+
```tsx
|
|
1747
|
+
import { ErrorDisplay } from '@subscrypts/subscrypts-sdk-react';
|
|
1748
|
+
|
|
1749
|
+
<ErrorDisplay error={txError} onRetry={retryTransaction} />
|
|
1750
|
+
```
|
|
1751
|
+
|
|
1752
|
+
**Option 3: Catch specific error classes:**
|
|
1753
|
+
|
|
1754
|
+
```tsx
|
|
1755
|
+
import {
|
|
1756
|
+
InsufficientBalanceError,
|
|
1757
|
+
NetworkError,
|
|
1758
|
+
TransactionError
|
|
1759
|
+
} from '@subscrypts/subscrypts-sdk-react';
|
|
1760
|
+
|
|
1761
|
+
try {
|
|
1762
|
+
await subscribe({ /* ... */ });
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
if (error instanceof InsufficientBalanceError) {
|
|
1765
|
+
alert('Insufficient balance! Please add funds.');
|
|
1766
|
+
} else if (error instanceof NetworkError) {
|
|
1767
|
+
alert('Wrong network! Please switch to Arbitrum.');
|
|
1768
|
+
} else if (error instanceof TransactionError) {
|
|
1769
|
+
alert('Transaction failed: ' + error.message);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
```
|
|
1773
|
+
|
|
1774
|
+
### Subscription Status Resolver
|
|
1775
|
+
|
|
1776
|
+
A pure function for normalizing subscription states. Works in React components, Node.js scripts, AI agents, and anywhere else:
|
|
1777
|
+
|
|
1778
|
+
```tsx
|
|
1779
|
+
import { resolveSubscriptionStatus } from '@subscrypts/subscrypts-sdk-react';
|
|
1780
|
+
|
|
1781
|
+
const status = resolveSubscriptionStatus({ subscription });
|
|
1782
|
+
|
|
1783
|
+
console.log(status.state); // 'active' | 'expired' | 'expiring-soon' | 'cancelled' | 'not-found'
|
|
1784
|
+
console.log(status.isActive); // true/false
|
|
1785
|
+
console.log(status.daysUntilExpiry); // number | null
|
|
1786
|
+
|
|
1787
|
+
if (status.state === 'expiring-soon') {
|
|
1788
|
+
showRenewalReminder();
|
|
1789
|
+
}
|
|
1790
|
+
```
|
|
1791
|
+
|
|
1792
|
+
---
|
|
1793
|
+
|
|
1794
|
+
### Decision Helpers
|
|
1795
|
+
|
|
1796
|
+
Pure utility functions for subscription decisions. No blockchain calls - operate on existing data. Usable in React components, Node.js scripts, AI agents, cron jobs, and automation:
|
|
1797
|
+
|
|
1798
|
+
```tsx
|
|
1799
|
+
import {
|
|
1800
|
+
canAccess,
|
|
1801
|
+
isPaymentDue,
|
|
1802
|
+
shouldRenew,
|
|
1803
|
+
getSubscriptionHealth
|
|
1804
|
+
} from '@subscrypts/subscrypts-sdk-react';
|
|
1805
|
+
|
|
1806
|
+
// Check if subscription grants access
|
|
1807
|
+
if (canAccess(subscription)) {
|
|
1808
|
+
showPremiumContent();
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// Check if payment is past due
|
|
1812
|
+
if (isPaymentDue(subscription)) {
|
|
1813
|
+
triggerPaymentCollection();
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// Check if subscription should auto-renew (due + auto-renewing + cycles remaining)
|
|
1817
|
+
if (shouldRenew(subscription)) {
|
|
1818
|
+
processRenewalPayment();
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// Get comprehensive health summary
|
|
1822
|
+
const health = getSubscriptionHealth(subscription);
|
|
1823
|
+
console.log(health.state); // 'active' | 'expired' | 'expiring-soon' | ...
|
|
1824
|
+
console.log(health.isPaymentDue); // false
|
|
1825
|
+
console.log(health.shouldRenew); // false
|
|
1826
|
+
console.log(health.daysUntilExpiry); // 25
|
|
1827
|
+
console.log(health.cyclesRemaining); // 11
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
---
|
|
1831
|
+
|
|
1832
|
+
## 🎨 Styling & Customization
|
|
1833
|
+
|
|
1834
|
+
### Using Default Styles
|
|
1835
|
+
|
|
1836
|
+
Import the pre-built stylesheet:
|
|
1837
|
+
|
|
1838
|
+
```tsx
|
|
1839
|
+
import '@subscrypts/subscrypts-sdk-react/styles';
|
|
1840
|
+
```
|
|
1841
|
+
|
|
1842
|
+
This includes styles for all components with the `subscrypts-` prefix to avoid conflicts.
|
|
1843
|
+
|
|
1844
|
+
---
|
|
1845
|
+
|
|
1846
|
+
### Customizing Colors
|
|
1847
|
+
|
|
1848
|
+
Override CSS variables in your own stylesheet:
|
|
1849
|
+
|
|
1850
|
+
```css
|
|
1851
|
+
:root {
|
|
1852
|
+
/* Primary colors */
|
|
1853
|
+
--subscrypts-color-primary: #3B82F6; /* Blue */
|
|
1854
|
+
--subscrypts-color-secondary: #10B981; /* Green */
|
|
1855
|
+
--subscrypts-color-error: #EF4444; /* Red */
|
|
1856
|
+
--subscrypts-color-success: #10B981; /* Green */
|
|
1857
|
+
|
|
1858
|
+
/* Background colors */
|
|
1859
|
+
--subscrypts-bg-primary: #FFFFFF;
|
|
1860
|
+
--subscrypts-bg-secondary: #F3F4F6;
|
|
1861
|
+
--subscrypts-bg-overlay: rgba(0, 0, 0, 0.5);
|
|
1862
|
+
|
|
1863
|
+
/* Text colors */
|
|
1864
|
+
--subscrypts-text-primary: #1F2937;
|
|
1865
|
+
--subscrypts-text-secondary: #6B7280;
|
|
1866
|
+
--subscrypts-text-inverse: #FFFFFF;
|
|
1867
|
+
|
|
1868
|
+
/* Borders and spacing */
|
|
1869
|
+
--subscrypts-border-radius: 0.5rem;
|
|
1870
|
+
--subscrypts-spacing-unit: 0.25rem;
|
|
1871
|
+
}
|
|
1872
|
+
```
|
|
1873
|
+
|
|
1874
|
+
---
|
|
1875
|
+
|
|
1876
|
+
### Custom Button Styles
|
|
1877
|
+
|
|
1878
|
+
Target specific component classes:
|
|
1879
|
+
|
|
1880
|
+
```css
|
|
1881
|
+
/* Customize primary button */
|
|
1882
|
+
.subscrypts-btn-primary {
|
|
1883
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
1884
|
+
border-radius: 12px;
|
|
1885
|
+
font-weight: 600;
|
|
1886
|
+
text-transform: uppercase;
|
|
1887
|
+
letter-spacing: 0.5px;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
.subscrypts-btn-primary:hover {
|
|
1891
|
+
transform: translateY(-2px);
|
|
1892
|
+
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
1893
|
+
}
|
|
1894
|
+
```
|
|
1895
|
+
|
|
1896
|
+
---
|
|
1897
|
+
|
|
1898
|
+
### Building Custom Components
|
|
1899
|
+
|
|
1900
|
+
Use hooks to build completely custom UIs:
|
|
1901
|
+
|
|
1902
|
+
```tsx
|
|
1903
|
+
import { useSubscriptionStatus, useSubscribe } from '@subscrypts/subscrypts-sdk-react';
|
|
1904
|
+
|
|
1905
|
+
function MyCustomSubscriptionUI() {
|
|
1906
|
+
const { status } = useSubscriptionStatus('merchant', 'plan');
|
|
1907
|
+
const { subscribe } = useSubscribe();
|
|
1908
|
+
|
|
1909
|
+
// Your custom UI with your own styles
|
|
1910
|
+
return (
|
|
1911
|
+
<div className="my-custom-design">
|
|
1912
|
+
{/* ... */}
|
|
1913
|
+
</div>
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
---
|
|
1919
|
+
|
|
1920
|
+
## 🐛 Troubleshooting
|
|
1921
|
+
|
|
1922
|
+
### "Wallet not connected" error
|
|
1923
|
+
|
|
1924
|
+
**Problem:** Hooks return null or components don't work.
|
|
1925
|
+
|
|
1926
|
+
**Solution:** Make sure you've wrapped your app with `<SubscryptsProvider>`:
|
|
1927
|
+
|
|
1928
|
+
```tsx
|
|
1929
|
+
// ✅ Correct
|
|
1930
|
+
<SubscryptsProvider enableWalletManagement={true}>
|
|
1931
|
+
<App />
|
|
1932
|
+
</SubscryptsProvider>
|
|
1933
|
+
|
|
1934
|
+
// ❌ Wrong - missing provider
|
|
1935
|
+
<App />
|
|
1936
|
+
```
|
|
1937
|
+
|
|
1938
|
+
---
|
|
1939
|
+
|
|
1940
|
+
### "Cannot read property 'isActive' of null"
|
|
1941
|
+
|
|
1942
|
+
**Problem:** Subscription status is null.
|
|
1943
|
+
|
|
1944
|
+
**Solution:** Always check loading state and handle null:
|
|
1945
|
+
|
|
1946
|
+
```tsx
|
|
1947
|
+
const { status, isLoading } = useSubscriptionStatus('merchant', 'plan');
|
|
1948
|
+
|
|
1949
|
+
if (isLoading) return <div>Loading...</div>;
|
|
1950
|
+
if (!status) return <div>No subscription found</div>;
|
|
1951
|
+
if (status.isActive) return <div>Active!</div>;
|
|
1952
|
+
```
|
|
1953
|
+
|
|
1954
|
+
---
|
|
1955
|
+
|
|
1956
|
+
### Styles not appearing
|
|
1957
|
+
|
|
1958
|
+
**Problem:** Components have no styling.
|
|
1959
|
+
|
|
1960
|
+
**Solution:** Import the stylesheet:
|
|
1961
|
+
|
|
1962
|
+
```tsx
|
|
1963
|
+
import '@subscrypts/subscrypts-sdk-react/styles';
|
|
1964
|
+
```
|
|
1965
|
+
|
|
1966
|
+
---
|
|
1967
|
+
|
|
1968
|
+
### "Wrong network" error
|
|
1969
|
+
|
|
1970
|
+
**Problem:** User is on wrong blockchain network.
|
|
1971
|
+
|
|
1972
|
+
**Solution:** SDK will auto-prompt to switch networks. Or manually check:
|
|
1973
|
+
|
|
1974
|
+
```tsx
|
|
1975
|
+
const { chainId } = useWallet();
|
|
1976
|
+
|
|
1977
|
+
if (chainId !== 42161) {
|
|
1978
|
+
return <div>Please switch to Arbitrum One</div>;
|
|
1979
|
+
}
|
|
1980
|
+
```
|
|
1981
|
+
|
|
1982
|
+
---
|
|
1983
|
+
|
|
1984
|
+
### TypeScript errors
|
|
1985
|
+
|
|
1986
|
+
**Problem:** Type errors in your IDE.
|
|
1987
|
+
|
|
1988
|
+
**Solution:** Make sure you have `@types` installed:
|
|
1989
|
+
|
|
1990
|
+
```bash
|
|
1991
|
+
npm install --save-dev @types/react @types/react-dom
|
|
1992
|
+
```
|
|
1993
|
+
|
|
1994
|
+
And enable `esModuleInterop` in your `tsconfig.json`:
|
|
1995
|
+
|
|
1996
|
+
```json
|
|
1997
|
+
{
|
|
1998
|
+
"compilerOptions": {
|
|
1999
|
+
"esModuleInterop": true
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
```
|
|
2003
|
+
|
|
2004
|
+
---
|
|
2005
|
+
|
|
2006
|
+
## ❓ FAQ
|
|
2007
|
+
|
|
2008
|
+
### Q: Do I need to know blockchain development?
|
|
2009
|
+
|
|
2010
|
+
**A:** No! The SDK handles all blockchain interactions for you. If you know React, you can build with Subscrypts.
|
|
2011
|
+
|
|
2012
|
+
---
|
|
2013
|
+
|
|
2014
|
+
### Q: What tokens can users pay with?
|
|
2015
|
+
|
|
2016
|
+
**A:** Users can pay with:
|
|
2017
|
+
- **SUBS tokens** (native Subscrypts token)
|
|
2018
|
+
- **USDC** (automatically swapped to SUBS)
|
|
2019
|
+
|
|
2020
|
+
---
|
|
2021
|
+
|
|
2022
|
+
### Q: How much does it cost to use Subscrypts?
|
|
2023
|
+
|
|
2024
|
+
**A:** The SDK is free to use. You pay only blockchain gas fees (very low on Arbitrum - typically under $0.01) and Subscrypts protocol fees (small percentage of subscription revenue).
|
|
2025
|
+
|
|
2026
|
+
---
|
|
2027
|
+
|
|
2028
|
+
### Q: Can I use this with Next.js?
|
|
2029
|
+
|
|
2030
|
+
**A:** Yes! Subscrypts works with:
|
|
2031
|
+
- Create React App
|
|
2032
|
+
- Next.js (App Router and Pages Router)
|
|
2033
|
+
- Vite
|
|
2034
|
+
- Remix
|
|
2035
|
+
- Any React framework
|
|
2036
|
+
|
|
2037
|
+
For Next.js, make sure to mark components as client-side:
|
|
2038
|
+
|
|
2039
|
+
```tsx
|
|
2040
|
+
'use client'; // Add this at the top
|
|
2041
|
+
|
|
2042
|
+
import { SubscryptsButton } from '@subscrypts/subscrypts-sdk-react';
|
|
2043
|
+
```
|
|
2044
|
+
|
|
2045
|
+
---
|
|
2046
|
+
|
|
2047
|
+
### Q: Can users pay with credit cards?
|
|
2048
|
+
|
|
2049
|
+
**A:** Not directly. Subscrypts is a blockchain-native protocol requiring cryptocurrency payments. However, users can buy crypto with credit cards through exchanges and then subscribe.
|
|
2050
|
+
|
|
2051
|
+
---
|
|
2052
|
+
|
|
2053
|
+
### Q: How do I get SUBS tokens?
|
|
2054
|
+
|
|
2055
|
+
**A:**
|
|
2056
|
+
1. SUBS tokens are available on Arbitrum One
|
|
2057
|
+
2. You can acquire SUBS through decentralized exchanges
|
|
2058
|
+
3. Contact Subscrypts support for more information about acquiring SUBS tokens
|
|
2059
|
+
|
|
2060
|
+
---
|
|
2061
|
+
|
|
2062
|
+
### Q: Is this production-ready?
|
|
2063
|
+
|
|
2064
|
+
**A:** Yes! The SDK is fully tested and used in production applications on Arbitrum One. Make sure to:
|
|
2065
|
+
- Test thoroughly before deploying
|
|
2066
|
+
- Audit your smart contract integrations
|
|
2067
|
+
- Have error handling in place
|
|
2068
|
+
- Monitor your subscriptions
|
|
2069
|
+
|
|
2070
|
+
---
|
|
2071
|
+
|
|
2072
|
+
### Q: Can I customize the checkout flow?
|
|
2073
|
+
|
|
2074
|
+
**A:** Absolutely! You have three options:
|
|
2075
|
+
1. Use `<SubscryptsButton>` with default checkout (easiest)
|
|
2076
|
+
2. Use `<CheckoutWizard>` directly for more control
|
|
2077
|
+
3. Use hooks (`useSubscribe`) to build completely custom UI
|
|
2078
|
+
|
|
2079
|
+
---
|
|
2080
|
+
|
|
2081
|
+
### Q: What happens if a subscription expires?
|
|
2082
|
+
|
|
2083
|
+
**A:**
|
|
2084
|
+
- `useSubscriptionStatus` returns `isActive: false`
|
|
2085
|
+
- `<SubscriptionGuard>` hides protected content
|
|
2086
|
+
- User is redirected to `fallbackUrl` (if provided)
|
|
2087
|
+
- No automatic charges (unless auto-renewal is enabled)
|
|
2088
|
+
|
|
2089
|
+
---
|
|
2090
|
+
|
|
2091
|
+
### Q: How do refunds work?
|
|
2092
|
+
|
|
2093
|
+
**A:** Subscrypts is blockchain-based, so refunds must be handled manually by sending tokens back to users. We recommend having clear refund policies and managing them through customer support.
|
|
2094
|
+
|
|
2095
|
+
---
|
|
2096
|
+
|
|
2097
|
+
## 🆘 Support
|
|
2098
|
+
|
|
2099
|
+
### Documentation
|
|
2100
|
+
|
|
2101
|
+
- **Full Docs**: [docs.subscrypts.com](https://docs.subscrypts.com)
|
|
2102
|
+
- **Video Tutorials**: [youtube.com/@subscrypts](https://youtube.com/@subscrypts)
|
|
2103
|
+
|
|
2104
|
+
### Community
|
|
2105
|
+
|
|
2106
|
+
- **Discord**: [discord.gg/subscrypts](https://discord.gg/subscrypts)
|
|
2107
|
+
- **Twitter**: [@Subscrypts](https://twitter.com/subscrypts)
|
|
2108
|
+
- **GitHub**: [github.com/subscrypts](https://github.com/subscrypts)
|
|
2109
|
+
|
|
2110
|
+
### Issues & Bug Reports
|
|
2111
|
+
|
|
2112
|
+
Found a bug? [Open an issue on GitHub](https://github.com/Subscrypts/subscrypts-sdk-react/issues)
|
|
2113
|
+
|
|
2114
|
+
---
|
|
2115
|
+
|
|
2116
|
+
## 📄 License
|
|
2117
|
+
|
|
2118
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
2119
|
+
|
|
2120
|
+
---
|
|
2121
|
+
|
|
2122
|
+
## 🙏 Acknowledgments
|
|
2123
|
+
|
|
2124
|
+
Built with:
|
|
2125
|
+
- [React](https://react.dev/)
|
|
2126
|
+
- [Ethers.js](https://docs.ethers.org/)
|
|
2127
|
+
- [Vite](https://vitejs.dev/)
|
|
2128
|
+
- [TypeScript](https://www.typescriptlang.org/)
|
|
2129
|
+
|
|
2130
|
+
Powered by [Arbitrum](https://arbitrum.io/)
|
|
2131
|
+
|
|
2132
|
+
---
|
|
2133
|
+
|
|
2134
|
+
<div align="center">
|
|
2135
|
+
|
|
2136
|
+
**Made with ❤️ by the Subscrypts team**
|
|
2137
|
+
|
|
2138
|
+
[Website](https://subscrypts.com) • [Documentation](https://docs.subscrypts.com) • [Discord](https://discord.gg/subscrypts)
|
|
2139
|
+
|
|
2140
|
+
</div>
|