@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.
Files changed (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2140 -0
  3. package/dist/components/buttons/SubscryptsButton.d.ts +19 -0
  4. package/dist/components/buttons/SubscryptsButton.d.ts.map +1 -0
  5. package/dist/components/buttons/index.d.ts +5 -0
  6. package/dist/components/buttons/index.d.ts.map +1 -0
  7. package/dist/components/checkout/CheckoutWizard.d.ts +22 -0
  8. package/dist/components/checkout/CheckoutWizard.d.ts.map +1 -0
  9. package/dist/components/checkout/ConfigurationStep.d.ts +15 -0
  10. package/dist/components/checkout/ConfigurationStep.d.ts.map +1 -0
  11. package/dist/components/checkout/TransactionStep.d.ts +15 -0
  12. package/dist/components/checkout/TransactionStep.d.ts.map +1 -0
  13. package/dist/components/checkout/index.d.ts +7 -0
  14. package/dist/components/checkout/index.d.ts.map +1 -0
  15. package/dist/components/guards/SubscriptionGuard.d.ts +25 -0
  16. package/dist/components/guards/SubscriptionGuard.d.ts.map +1 -0
  17. package/dist/components/guards/index.d.ts +5 -0
  18. package/dist/components/guards/index.d.ts.map +1 -0
  19. package/dist/components/index.d.ts +20 -0
  20. package/dist/components/index.d.ts.map +1 -0
  21. package/dist/components/merchant/MerchantDashboard.d.ts +24 -0
  22. package/dist/components/merchant/MerchantDashboard.d.ts.map +1 -0
  23. package/dist/components/merchant/index.d.ts +6 -0
  24. package/dist/components/merchant/index.d.ts.map +1 -0
  25. package/dist/components/pricing/PlanCard.d.ts +45 -0
  26. package/dist/components/pricing/PlanCard.d.ts.map +1 -0
  27. package/dist/components/pricing/PricingTable.d.ts +68 -0
  28. package/dist/components/pricing/PricingTable.d.ts.map +1 -0
  29. package/dist/components/pricing/index.d.ts +10 -0
  30. package/dist/components/pricing/index.d.ts.map +1 -0
  31. package/dist/components/shared/ErrorDisplay.d.ts +34 -0
  32. package/dist/components/shared/ErrorDisplay.d.ts.map +1 -0
  33. package/dist/components/shared/LoadingSpinner.d.ts +5 -0
  34. package/dist/components/shared/LoadingSpinner.d.ts.map +1 -0
  35. package/dist/components/shared/Modal.d.ts +12 -0
  36. package/dist/components/shared/Modal.d.ts.map +1 -0
  37. package/dist/components/shared/NetworkSwitchPrompt.d.ts +33 -0
  38. package/dist/components/shared/NetworkSwitchPrompt.d.ts.map +1 -0
  39. package/dist/components/shared/SubscryptsErrorBoundary.d.ts +41 -0
  40. package/dist/components/shared/SubscryptsErrorBoundary.d.ts.map +1 -0
  41. package/dist/components/subscription/ConfirmDialog.d.ts +44 -0
  42. package/dist/components/subscription/ConfirmDialog.d.ts.map +1 -0
  43. package/dist/components/subscription/ManageSubscriptionModal.d.ts +35 -0
  44. package/dist/components/subscription/ManageSubscriptionModal.d.ts.map +1 -0
  45. package/dist/components/subscription/SubscriptionCard.d.ts +35 -0
  46. package/dist/components/subscription/SubscriptionCard.d.ts.map +1 -0
  47. package/dist/components/subscription/SubscriptionDashboard.d.ts +40 -0
  48. package/dist/components/subscription/SubscriptionDashboard.d.ts.map +1 -0
  49. package/dist/components/subscription/index.d.ts +12 -0
  50. package/dist/components/subscription/index.d.ts.map +1 -0
  51. package/dist/components/wallet/ConnectWalletModal.d.ts +29 -0
  52. package/dist/components/wallet/ConnectWalletModal.d.ts.map +1 -0
  53. package/dist/components/wallet/index.d.ts +6 -0
  54. package/dist/components/wallet/index.d.ts.map +1 -0
  55. package/dist/constants/index.d.ts +16 -0
  56. package/dist/constants/index.d.ts.map +1 -0
  57. package/dist/context/SubscryptsContext.d.ts +36 -0
  58. package/dist/context/SubscryptsContext.d.ts.map +1 -0
  59. package/dist/context/SubscryptsProvider.d.ts +28 -0
  60. package/dist/context/SubscryptsProvider.d.ts.map +1 -0
  61. package/dist/context/index.d.ts +7 -0
  62. package/dist/context/index.d.ts.map +1 -0
  63. package/dist/contract/abis/Subscrypts.d.ts +2917 -0
  64. package/dist/contract/abis/Subscrypts.d.ts.map +1 -0
  65. package/dist/contract/abis/dexFactoryABI.d.ts +184 -0
  66. package/dist/contract/abis/dexFactoryABI.d.ts.map +1 -0
  67. package/dist/contract/abis/dexPairABI.d.ts +775 -0
  68. package/dist/contract/abis/dexPairABI.d.ts.map +1 -0
  69. package/dist/contract/abis/dexPositionManagerABI.d.ts +948 -0
  70. package/dist/contract/abis/dexPositionManagerABI.d.ts.map +1 -0
  71. package/dist/contract/abis/dexQuoterABI.d.ts +206 -0
  72. package/dist/contract/abis/dexQuoterABI.d.ts.map +1 -0
  73. package/dist/contract/abis/dexRouterABI.d.ts +439 -0
  74. package/dist/contract/abis/dexRouterABI.d.ts.map +1 -0
  75. package/dist/contract/abis/dexUSDCABI.d.ts +132 -0
  76. package/dist/contract/abis/dexUSDCABI.d.ts.map +1 -0
  77. package/dist/contract/abis/index.d.ts +15 -0
  78. package/dist/contract/abis/index.d.ts.map +1 -0
  79. package/dist/contract/config.d.ts +40 -0
  80. package/dist/contract/config.d.ts.map +1 -0
  81. package/dist/contract/index.d.ts +12 -0
  82. package/dist/contract/index.d.ts.map +1 -0
  83. package/dist/contract/methods.d.ts +305 -0
  84. package/dist/contract/methods.d.ts.map +1 -0
  85. package/dist/hooks/events/index.d.ts +6 -0
  86. package/dist/hooks/events/index.d.ts.map +1 -0
  87. package/dist/hooks/events/useSubscryptsEvents.d.ts +63 -0
  88. package/dist/hooks/events/useSubscryptsEvents.d.ts.map +1 -0
  89. package/dist/hooks/index.d.ts +12 -0
  90. package/dist/hooks/index.d.ts.map +1 -0
  91. package/dist/hooks/merchant/index.d.ts +10 -0
  92. package/dist/hooks/merchant/index.d.ts.map +1 -0
  93. package/dist/hooks/merchant/useMerchantPlans.d.ts +30 -0
  94. package/dist/hooks/merchant/useMerchantPlans.d.ts.map +1 -0
  95. package/dist/hooks/merchant/useMerchantRevenue.d.ts +59 -0
  96. package/dist/hooks/merchant/useMerchantRevenue.d.ts.map +1 -0
  97. package/dist/hooks/merchant/useMerchantSubscribers.d.ts +54 -0
  98. package/dist/hooks/merchant/useMerchantSubscribers.d.ts.map +1 -0
  99. package/dist/hooks/plans/index.d.ts +12 -0
  100. package/dist/hooks/plans/index.d.ts.map +1 -0
  101. package/dist/hooks/plans/usePlan.d.ts +33 -0
  102. package/dist/hooks/plans/usePlan.d.ts.map +1 -0
  103. package/dist/hooks/plans/usePlans.d.ts +36 -0
  104. package/dist/hooks/plans/usePlans.d.ts.map +1 -0
  105. package/dist/hooks/plans/usePlansByMerchant.d.ts +35 -0
  106. package/dist/hooks/plans/usePlansByMerchant.d.ts.map +1 -0
  107. package/dist/hooks/pricing/index.d.ts +10 -0
  108. package/dist/hooks/pricing/index.d.ts.map +1 -0
  109. package/dist/hooks/pricing/usePlanPrice.d.ts +59 -0
  110. package/dist/hooks/pricing/usePlanPrice.d.ts.map +1 -0
  111. package/dist/hooks/pricing/useSUBSPrice.d.ts +39 -0
  112. package/dist/hooks/pricing/useSUBSPrice.d.ts.map +1 -0
  113. package/dist/hooks/subscriptions/index.d.ts +12 -0
  114. package/dist/hooks/subscriptions/index.d.ts.map +1 -0
  115. package/dist/hooks/subscriptions/useManageSubscription.d.ts +39 -0
  116. package/dist/hooks/subscriptions/useManageSubscription.d.ts.map +1 -0
  117. package/dist/hooks/subscriptions/useMySubscriptions.d.ts +53 -0
  118. package/dist/hooks/subscriptions/useMySubscriptions.d.ts.map +1 -0
  119. package/dist/hooks/subscriptions/useSubscribe.d.ts +58 -0
  120. package/dist/hooks/subscriptions/useSubscribe.d.ts.map +1 -0
  121. package/dist/hooks/subscriptions/useSubscriptionStatus.d.ts +34 -0
  122. package/dist/hooks/subscriptions/useSubscriptionStatus.d.ts.map +1 -0
  123. package/dist/hooks/tokens/index.d.ts +6 -0
  124. package/dist/hooks/tokens/index.d.ts.map +1 -0
  125. package/dist/hooks/tokens/useTokenBalance.d.ts +36 -0
  126. package/dist/hooks/tokens/useTokenBalance.d.ts.map +1 -0
  127. package/dist/hooks/wallet/index.d.ts +6 -0
  128. package/dist/hooks/wallet/index.d.ts.map +1 -0
  129. package/dist/hooks/wallet/useWallet.d.ts +40 -0
  130. package/dist/hooks/wallet/useWallet.d.ts.map +1 -0
  131. package/dist/index.cjs +2 -0
  132. package/dist/index.cjs.map +1 -0
  133. package/dist/index.d.ts +42 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +7850 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/services/cache.service.d.ts +89 -0
  138. package/dist/services/cache.service.d.ts.map +1 -0
  139. package/dist/services/index.d.ts +7 -0
  140. package/dist/services/index.d.ts.map +1 -0
  141. package/dist/services/token.service.d.ts +43 -0
  142. package/dist/services/token.service.d.ts.map +1 -0
  143. package/dist/services/wallet.service.d.ts +46 -0
  144. package/dist/services/wallet.service.d.ts.map +1 -0
  145. package/dist/style.css +3 -0
  146. package/dist/styles.d.ts +2 -0
  147. package/dist/styles.d.ts.map +1 -0
  148. package/dist/types/component.types.d.ts +192 -0
  149. package/dist/types/component.types.d.ts.map +1 -0
  150. package/dist/types/contract.types.d.ts +81 -0
  151. package/dist/types/contract.types.d.ts.map +1 -0
  152. package/dist/types/index.d.ts +8 -0
  153. package/dist/types/index.d.ts.map +1 -0
  154. package/dist/types/subscription.types.d.ts +65 -0
  155. package/dist/types/subscription.types.d.ts.map +1 -0
  156. package/dist/types/wallet.types.d.ts +53 -0
  157. package/dist/types/wallet.types.d.ts.map +1 -0
  158. package/dist/utils/errorMessages.d.ts +44 -0
  159. package/dist/utils/errorMessages.d.ts.map +1 -0
  160. package/dist/utils/errors.d.ts +60 -0
  161. package/dist/utils/errors.d.ts.map +1 -0
  162. package/dist/utils/formatters.d.ts +44 -0
  163. package/dist/utils/formatters.d.ts.map +1 -0
  164. package/dist/utils/index.d.ts +10 -0
  165. package/dist/utils/index.d.ts.map +1 -0
  166. package/dist/utils/logger.d.ts +80 -0
  167. package/dist/utils/logger.d.ts.map +1 -0
  168. package/dist/utils/permit.utils.d.ts +52 -0
  169. package/dist/utils/permit.utils.d.ts.map +1 -0
  170. package/dist/utils/subscriptionHelpers.d.ts +67 -0
  171. package/dist/utils/subscriptionHelpers.d.ts.map +1 -0
  172. package/dist/utils/subscriptionStatus.d.ts +48 -0
  173. package/dist/utils/subscriptionStatus.d.ts.map +1 -0
  174. package/dist/utils/validators.d.ts +76 -0
  175. package/dist/utils/validators.d.ts.map +1 -0
  176. package/dist/wallet/ExternalConnector.d.ts +32 -0
  177. package/dist/wallet/ExternalConnector.d.ts.map +1 -0
  178. package/dist/wallet/InjectedConnector.d.ts +55 -0
  179. package/dist/wallet/InjectedConnector.d.ts.map +1 -0
  180. package/dist/wallet/index.d.ts +9 -0
  181. package/dist/wallet/index.d.ts.map +1 -0
  182. package/dist/wallet/sessionStore.d.ts +28 -0
  183. package/dist/wallet/sessionStore.d.ts.map +1 -0
  184. package/dist/wallet/types.d.ts +66 -0
  185. package/dist/wallet/types.d.ts.map +1 -0
  186. 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
+ [![npm version](https://img.shields.io/npm/v/@subscrypts/subscrypts-sdk-react.svg)](https://www.npmjs.com/package/@subscrypts/subscrypts-sdk-react)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](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>