@blocklet/payment-react 1.19.22 → 1.20.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 (43) hide show
  1. package/.aigne/doc-smith/config.yaml +114 -0
  2. package/.aigne/doc-smith/output/structure-plan.json +361 -0
  3. package/.aigne/doc-smith/preferences.yml +55 -0
  4. package/.aigne/doc-smith/upload-cache.yaml +264 -0
  5. package/README.md +2 -3
  6. package/docs/_sidebar.md +33 -0
  7. package/docs/components-business-auto-topup.md +238 -0
  8. package/docs/components-business-overdue-invoice-payment.md +231 -0
  9. package/docs/components-business-resume-subscription.md +177 -0
  10. package/docs/components-business.md +45 -0
  11. package/docs/components-checkout-checkout-donate.md +199 -0
  12. package/docs/components-checkout-checkout-form.md +185 -0
  13. package/docs/components-checkout-checkout-table.md +228 -0
  14. package/docs/components-checkout.md +131 -0
  15. package/docs/components-history-credit-grants-list.md +98 -0
  16. package/docs/components-history-credit-transactions-list.md +116 -0
  17. package/docs/components-history-invoice-list.md +104 -0
  18. package/docs/components-history-payment-list.md +65 -0
  19. package/docs/components-history.md +92 -0
  20. package/docs/components-ui-form-elements-address-form.md +150 -0
  21. package/docs/components-ui-form-elements-country-select.md +105 -0
  22. package/docs/components-ui-form-elements-currency-selector.md +124 -0
  23. package/docs/components-ui-form-elements-phone-input.md +160 -0
  24. package/docs/components-ui-form-elements.md +125 -0
  25. package/docs/components-ui-payment-summary.md +157 -0
  26. package/docs/components-ui-pricing-table.md +227 -0
  27. package/docs/components-ui.md +44 -0
  28. package/docs/components.md +95 -0
  29. package/docs/getting-started.md +111 -0
  30. package/docs/guides-theming.md +175 -0
  31. package/docs/guides-utilities.md +235 -0
  32. package/docs/guides.md +95 -0
  33. package/docs/hooks-use-mobile.md +70 -0
  34. package/docs/hooks-use-subscription.md +129 -0
  35. package/docs/hooks.md +84 -0
  36. package/docs/overview.md +87 -0
  37. package/docs/providers-donate-provider.md +175 -0
  38. package/docs/providers-payment-provider.md +245 -0
  39. package/docs/providers.md +101 -0
  40. package/es/payment/form/index.js +15 -1
  41. package/lib/payment/form/index.js +14 -1
  42. package/package.json +5 -5
  43. package/src/payment/form/index.tsx +16 -1
@@ -0,0 +1,235 @@
1
+ # Utilities
2
+
3
+ The `@blocklet/payment-react` library exports several utility functions and classes to simplify common tasks in your application, such as client-side caching, date formatting, and internationalization. These tools are designed to work seamlessly with the library's components and the underlying payment infrastructure.
4
+
5
+ ## API Client
6
+
7
+ The library exports a pre-configured Axios instance for making API requests to your payment backend. It automatically handles base URLs and authentication when used within the `PaymentProvider` context. For more details on setting up the provider, see the [PaymentProvider documentation](./providers-payment-provider.md).
8
+
9
+ ```tsx
10
+ import { api } from '@blocklet/payment-react';
11
+
12
+ // Basic GET request
13
+ const response = await api.get('/api/payments');
14
+
15
+ // POST request with a body
16
+ const data = await api.post('/api/checkout', { amount: 100 });
17
+
18
+ // Request with query parameters
19
+ const results = await api.get('/api/invoices', {
20
+ params: { status: 'paid' }
21
+ });
22
+
23
+ // Request with custom configuration
24
+ const config = {
25
+ headers: { 'Custom-Header': 'value' }
26
+ };
27
+ const response = await api.put('/api/subscription', data, config);
28
+ ```
29
+
30
+ ## CachedRequest
31
+
32
+ The `CachedRequest` class offers a straightforward way to implement client-side caching, reducing redundant network calls and improving your application's responsiveness. It supports multiple caching strategies and time-to-live (TTL) configurations.
33
+
34
+ ```d2
35
+ direction: down
36
+
37
+ fetch: "Call priceRequest.fetch()"
38
+ cache_check: "Is data in cache and not expired?"
39
+
40
+ fetch -> cache_check
41
+
42
+ subgraph "Cache Hit" {
43
+ direction: down
44
+ style.stroke: "#4CAF50"
45
+ return_cached: "Return cached data"
46
+ }
47
+
48
+ subgraph "Cache Miss" {
49
+ direction: down
50
+ style.stroke: "#F44336"
51
+ api_call: "Execute api.get('/api/prices')"
52
+ store_cache: "Store response in cache with timestamp"
53
+ return_fresh: "Return fresh data"
54
+ api_call -> store_cache -> return_fresh
55
+ }
56
+
57
+ cache_check -> return_cached: Yes
58
+ cache_check -> api_call: No
59
+ ```
60
+
61
+ ### Usage
62
+
63
+ To use it, create an instance of `CachedRequest` with a unique key, a data-fetching function, and optional configuration.
64
+
65
+ ```tsx
66
+ import { CachedRequest, api } from '@blocklet/payment-react';
67
+
68
+ // Create a cached request instance
69
+ const priceRequest = new CachedRequest(
70
+ 'product-prices',
71
+ () => api.get('/api/prices'),
72
+ {
73
+ strategy: 'session', // 'session' | 'local' | 'memory'
74
+ ttl: 5 * 60 * 1000 // Cache for 5 minutes
75
+ }
76
+ );
77
+
78
+ // Use the cached request
79
+ async function fetchPrices() {
80
+ // This will use the cache if available and not expired
81
+ const prices = await priceRequest.fetch();
82
+
83
+ // To bypass the cache and force a network request
84
+ const freshPrices = await priceRequest.fetch(true);
85
+
86
+ return prices;
87
+ }
88
+ ```
89
+
90
+ ### Configuration Options
91
+
92
+ | Option | Type | Description |
93
+ |---|---|---|
94
+ | `strategy` | `'session'` \| `'local'` \| `'memory'` | The caching strategy to use. `session` uses `sessionStorage`, `local` uses `localStorage`, and `memory` uses a global in-memory cache. Defaults to `'session'`. |
95
+ | `ttl` | `number` | Time-to-live in milliseconds. After this duration, the cached data is considered expired. Defaults to `0` (no expiration). |
96
+
97
+ ## Date Handling with dayjs
98
+
99
+ The library exports a pre-configured `dayjs` instance with useful plugins already enabled, including `relativeTime`, `localizedFormat`, `duration`, `utc`, and `timezone`. This ensures consistent date and time handling across your application.
100
+
101
+ ```tsx
102
+ import { dayjs } from '@blocklet/payment-react';
103
+
104
+ // Format the current date
105
+ const formattedDate = dayjs().format('YYYY-MM-DD');
106
+
107
+ // Parse a timestamp and get its Unix value
108
+ const timestamp = 1672531200000;
109
+ const dateObject = dayjs(timestamp);
110
+ const unixValue = dateObject.unix();
111
+
112
+ // Display relative time
113
+ const fiveMinutesAgo = dayjs().subtract(5, 'minute');
114
+ const relativeString = dayjs().from(fiveMinutesAgo);
115
+ ```
116
+
117
+ ## Formatting Utilities
118
+
119
+ A collection of functions to format data for display, from prices and statuses to plain text.
120
+
121
+ ### Price and Amount Formatting
122
+
123
+ These functions help display monetary values and quantities correctly according to currency decimals and locale conventions.
124
+
125
+ - `formatPrice(price, currency, unit_label, quantity)`: Formats a full price object into a human-readable string, including recurring intervals (e.g., "$10.00 / month").
126
+ - `formatAmount(amount, decimals)`: Formats a raw amount string based on the currency's decimal places.
127
+ - `formatBNStr(str, decimals, precision)`: Formats a BigNumber string into a readable number, handling large values and token conversions.
128
+
129
+ ```tsx
130
+ import { formatPrice, formatAmount } from '@blocklet/payment-react';
131
+
132
+ // Example price and currency objects (simplified)
133
+ const price = { type: 'recurring', recurring: { interval: 'month', interval_count: 1 }, unit_amount: '1000' };
134
+ const currency = { symbol: '$', decimal: 2 };
135
+
136
+ const displayPrice = formatPrice(price, currency);
137
+ // Result: "10.00 $ per month"
138
+
139
+ const displayAmount = formatAmount('12345', 2);
140
+ // Result: "123.45"
141
+ ```
142
+
143
+ ### Status Colors
144
+
145
+ Several utility functions map resource statuses to semantic colors (`success`, `warning`, `error`, `primary`, `default`) for consistent UI indicators in components like `<Status />`.
146
+
147
+ | Function | Status Examples | Color |
148
+ |---|---|---|
149
+ | `getSubscriptionStatusColor` | `active`, `trialing` | `success`, `primary` |
150
+ | `getInvoiceStatusColor` | `paid`, `open`, `uncollectible` | `success`, `secondary`, `warning` |
151
+ | `getPaymentIntentStatusColor` | `succeeded`, `requires_action` | `success`, `warning` |
152
+ | `getRefundStatusColor` | `succeeded`, `pending` | `success`, `default` |
153
+
154
+ ### Text Utilities
155
+
156
+ - `truncateText(text, maxLength, useWidth)`: Truncates a string to a specified length, adding an ellipsis. The `useWidth` option calculates length based on character width for better CJK character handling.
157
+
158
+ ## Internationalization (i18n)
159
+
160
+ For applications targeting a global audience, the library includes utilities to manage translations.
161
+
162
+ ### `createTranslator`
163
+
164
+ You can create your own translator instance with custom language packs.
165
+
166
+ ```tsx
167
+ import { createTranslator } from '@blocklet/payment-react';
168
+
169
+ const myTranslations = {
170
+ en: {
171
+ checkout: { title: 'Complete Payment' }
172
+ },
173
+ zh: {
174
+ checkout: { title: '完成支付' }
175
+ }
176
+ };
177
+
178
+ const translator = createTranslator({ fallbackLocale: 'en' }, myTranslations);
179
+
180
+ console.log(translator('checkout.title', 'zh')); // Outputs: '完成支付'
181
+ ```
182
+
183
+ ### Merging with Library Translations
184
+
185
+ To leverage the built-in translations for payment components, you can merge them with your application's translation files.
186
+
187
+ ```tsx
188
+ import { translations as paymentTranslations } from '@blocklet/payment-react';
189
+ import merge from 'lodash/merge';
190
+
191
+ import en from './en'; // Your app's English translations
192
+ import zh from './zh'; // Your app's Chinese translations
193
+
194
+ export const translations = merge(
195
+ {
196
+ zh,
197
+ en,
198
+ },
199
+ paymentTranslations
200
+ );
201
+ ```
202
+
203
+ ## Lazy Loading Components
204
+
205
+ To optimize your application's bundle size, you can use the `createLazyComponent` utility to dynamically load components only when they are needed.
206
+
207
+ ```tsx
208
+ import { createLazyComponent } from '@blocklet/payment-react';
209
+
210
+ const LazyLoadedComponent = createLazyComponent(async () => {
211
+ // Dynamically import the component and its dependencies
212
+ const [{ Component }, { useHook }] = await Promise.all([
213
+ import('./Component'),
214
+ import('./hooks')
215
+ ]);
216
+
217
+ // Make dependencies available if needed
218
+ globalThis.__DEPENDENCIES__ = { useHook };
219
+
220
+ return Component;
221
+ });
222
+
223
+ function App() {
224
+ return (
225
+ <div>
226
+ {/* This component will be loaded on demand */}
227
+ <LazyLoadedComponent />
228
+ </div>
229
+ );
230
+ }
231
+ ```
232
+
233
+ ---
234
+
235
+ These utilities provide robust solutions for common development challenges. To learn about the custom React hooks for handling side effects and state, proceed to the [Hooks](./hooks.md) guide.
package/docs/guides.md ADDED
@@ -0,0 +1,95 @@
1
+ # Guides
2
+
3
+ Welcome to the guides section. While the Components documentation covers individual UI pieces, these guides focus on broader topics that span the entire library. Here, you'll learn about advanced subjects like customizing the appearance of your payment flows, using powerful utility functions, and implementing patterns to create a robust and polished user experience.
4
+
5
+ These guides are designed for developers looking to move beyond basic integration and fully leverage the capabilities of `@blocklet/payment-react`.
6
+
7
+ <x-cards>
8
+ <x-card data-title="Theming" data-icon="lucide:palette" data-href="/guides/theming">
9
+ Learn how to customize the look and feel of payment components to match your application's branding using Material-UI themes.
10
+ </x-card>
11
+ <x-card data-title="Utilities" data-icon="lucide:wrench" data-href="/guides/utilities">
12
+ Explore a suite of helper functions for common tasks like cached data fetching, date formatting, and internationalization.
13
+ </x-card>
14
+ </x-cards>
15
+
16
+ ---
17
+
18
+ ## Customizing Appearance
19
+
20
+ The library is built on Material-UI and exposes a flexible theming system. You can provide a custom theme configuration to adjust everything from colors and typography to the shape of buttons and inputs. For a comprehensive overview, see the full [Theming Guide](./guides-theming.md).
21
+
22
+ For example, you can change the primary button color by passing a `theme` object to a component. Note that components requiring session context must be wrapped in `PaymentProvider`.
23
+
24
+ ```tsx
25
+ import { PaymentProvider, CheckoutForm } from '@blocklet/payment-react';
26
+ // useSessionContext is a hook from your app's session management setup.
27
+ import { useSessionContext } from '../hooks/session';
28
+
29
+ function MyCheckoutPage() {
30
+ const { session, connectApi } = useSessionContext();
31
+
32
+ // For a complete guide on setting up PaymentProvider and session context,
33
+ // please refer to the PaymentProvider documentation.
34
+ if (!session) {
35
+ return <div>Loading session...</div>;
36
+ }
37
+
38
+ return (
39
+ <PaymentProvider session={session} connect={connectApi}>
40
+ <CheckoutForm
41
+ id="plink_xxx"
42
+ theme={{
43
+ components: {
44
+ MuiButton: {
45
+ styleOverrides: {
46
+ containedPrimary: {
47
+ backgroundColor: '#1DC1C7',
48
+ color: '#fff',
49
+ '&:hover': {
50
+ backgroundColor: 'rgb(20, 135, 139)',
51
+ },
52
+ },
53
+ },
54
+ },
55
+ },
56
+ }}
57
+ />
58
+ </PaymentProvider>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ## Simplifying Logic with Utilities
64
+
65
+ Beyond components, `@blocklet/payment-react` exports a suite of utility functions and classes to handle common tasks. The `CachedRequest` class, for instance, provides an easy way to cache API responses, reducing network load and improving performance. Discover more tools in our [Utilities Guide](./guides-utilities.md).
66
+
67
+ ```tsx
68
+ import { CachedRequest, api } from '@blocklet/payment-react';
69
+
70
+ // Create a cached request that fetches prices and caches them for 5 minutes
71
+ const priceRequest = new CachedRequest(
72
+ 'product-prices',
73
+ () => api.get('/api/prices'),
74
+ {
75
+ strategy: 'session', // 'session' | 'local' | 'memory'
76
+ ttl: 5 * 60 * 1000 // 5 minutes
77
+ }
78
+ );
79
+
80
+ async function fetchPrices() {
81
+ // Will use cache if available and not expired
82
+ const prices = await priceRequest.fetch();
83
+
84
+ // Force a refresh from the network
85
+ const freshPrices = await priceRequest.fetch(true);
86
+
87
+ return prices;
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Next Steps
94
+
95
+ After mastering these advanced topics, proceed to the [Hooks](./hooks.md) documentation to learn about handling real-time events and responsive design.
@@ -0,0 +1,70 @@
1
+ # useMobile
2
+
3
+ The `useMobile` hook is a convenient utility for creating responsive layouts in your application. It helps determine if the current viewport width is considered 'mobile' based on standard Material-UI breakpoints.
4
+
5
+ This hook simplifies responsive logic by abstracting away the underlying `useTheme` and `useMediaQuery` calls from Material-UI.
6
+
7
+ ## Basic Usage
8
+
9
+ Import the hook and use it within your component to get a boolean value indicating if the screen is mobile-sized, along with the corresponding pixel width for that breakpoint.
10
+
11
+ ```tsx
12
+ import { useMobile } from '@blocklet/payment-react';
13
+ import Typography from '@mui/material/Typography';
14
+
15
+ function ResponsiveComponent() {
16
+ const { isMobile } = useMobile();
17
+
18
+ return (
19
+ <Typography variant="h5">
20
+ {isMobile ? 'This is the mobile view.' : 'This is the desktop view.'}
21
+ </Typography>
22
+ );
23
+ }
24
+ ```
25
+
26
+ In this example, the component will render different text depending on whether the screen width is below the default 'md' (medium) breakpoint.
27
+
28
+ ## Parameters
29
+
30
+ The `useMobile` hook accepts an optional parameter to customize the breakpoint used for mobile detection.
31
+
32
+ | Name | Type | Default | Description |
33
+ |---------------|--------------------------------------------|---------|---------------------------------------------------------------------------------------------------------------|
34
+ | `mobilePoint` | `'xs'` \| `'sm'` \| `'md'` \| `'lg'` \| `'xl'` | `'md'` | The Material-UI breakpoint to use as the threshold. The hook will return `true` for screen sizes at or below this point. |
35
+
36
+ ## Return Value
37
+
38
+ The hook returns an object with two properties:
39
+
40
+ | Key | Type | Description |
41
+ |--------------|-----------|---------------------------------------------------------------------------------------------------------|
42
+ | `isMobile` | `boolean` | `true` if the screen width is at or below the specified `mobilePoint`, otherwise `false`. |
43
+ | `mobileSize` | `string` | A string representing the pixel width of the `mobilePoint` breakpoint (e.g., `'900px'` for the `'md'` breakpoint). |
44
+
45
+ ## Example with Custom Breakpoint
46
+
47
+ You can specify a different breakpoint if your application's design requires a different threshold for mobile view.
48
+
49
+ ```tsx
50
+ import { useMobile } from '@blocklet/payment-react';
51
+ import Box from '@mui/material/Box';
52
+
53
+ function CustomBreakpointComponent() {
54
+ // Consider 'sm' and below as mobile
55
+ const { isMobile, mobileSize } = useMobile('sm');
56
+
57
+ return (
58
+ <Box sx={{ p: 2, border: '1px solid grey' }}>
59
+ <p>The mobile breakpoint is set to {mobileSize}.</p>
60
+ {isMobile ? (
61
+ <p>This layout is optimized for smaller screens.</p>
62
+ ) : (
63
+ <p>This layout is for tablets and desktops.</p>
64
+ )}
65
+ </Box>
66
+ );
67
+ }
68
+ ```
69
+
70
+ This component adjusts its responsive behavior based on the 'sm' (small) breakpoint, providing more granular control over your UI.
@@ -0,0 +1,129 @@
1
+ # useSubscription
2
+
3
+ The `useSubscription` hook provides a straightforward way to subscribe to real-time events from the payment service. It manages the WebSocket connection lifecycle, allowing your components to react instantly to backend events like `invoice.paid` or `subscription.updated`.
4
+
5
+ This is essential for creating dynamic user experiences where the UI needs to reflect changes without requiring manual refreshes.
6
+
7
+ ## How It Works
8
+
9
+ The hook abstracts the complexity of managing a WebSocket connection. When your component mounts, `useSubscription` establishes a connection to the relay service, subscribes to the specified channel, and returns a subscription object. This object can then be used to listen for incoming events. The hook also handles cleanup, disconnecting and unsubscribing when the component unmounts.
10
+
11
+ ```d2
12
+ shape: sequence_diagram
13
+
14
+ Component: "Your React Component"
15
+ Hook: "useSubscription Hook"
16
+ WsClient: "WebSocket Client"
17
+ Relay: "Relay Service"
18
+
19
+ Component -> Hook: "Renders and calls useSubscription(\"invoice_xyz\")"
20
+ Hook -> WsClient: "Establishes WebSocket connection if not connected"
21
+ WsClient -> Relay: "Connects to service endpoint"
22
+ Hook -> WsClient: "Subscribes to formatted channel (e.g., relay:appId:invoice_xyz)"
23
+ WsClient -> Relay: "Sends subscription request"
24
+ Relay -> WsClient: "Confirms subscription"
25
+ Hook -> Component: "Returns subscription object"
26
+
27
+ Component -> Component: "Attaches event listener (e.g., subscription.on('update', ...))"
28
+
29
+ Relay -> WsClient: "Pushes 'update' event with data"
30
+ WsClient -> Hook: "Forwards event to the subscription"
31
+ Hook -> Component: "Triggers the attached event listener"
32
+ Component -> Component: "Updates state (e.g., setStatus('Paid'))"
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ To use the hook, simply pass the channel you wish to subscribe to. The underlying implementation requires that the channel name does not contain special characters like `/`, `.`, or `:`, as this will prevent the client from receiving events.
38
+
39
+ ### Parameters
40
+
41
+ | Name | Type | Description |
42
+ | :-------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
43
+ | `channel` | `string` | **Required**. A unique identifier for the subscription channel. It must not contain special characters like `/`, `.`, or `:`. The hook prefixes this with the application ID to create the final channel name (e.g., `relay:appId:channel`). |
44
+
45
+ ### Return Value
46
+
47
+ The hook returns a `subscription` object, which is an instance of the WebSocket client's subscription handler. You can attach event listeners to this object using its `.on(eventName, callback)` method.
48
+
49
+ ## Example
50
+
51
+ Here is an example of a component that displays the status of an invoice. It uses `useSubscription` to listen for real-time updates and changes the status from "Pending" to "Paid" when an `invoice.paid` event is received.
52
+
53
+ ```tsx
54
+ import React, { useState, useEffect } from 'react';
55
+ import { PaymentProvider, useSubscription } from '@blocklet/payment-react';
56
+
57
+ // Assume this hook is defined in your application to provide session context.
58
+ // See the PaymentProvider documentation for an example implementation.
59
+ import { useSessionContext } from './session-context';
60
+
61
+ interface InvoiceStatusProps {
62
+ invoiceId: string;
63
+ initialStatus: 'pending' | 'paid' | 'failed';
64
+ }
65
+
66
+ function InvoiceStatus({ invoiceId, initialStatus }: InvoiceStatusProps) {
67
+ const [status, setStatus] = useState(initialStatus);
68
+
69
+ // The channel should be a simple string.
70
+ const subscription = useSubscription(invoiceId);
71
+
72
+ useEffect(() => {
73
+ // Ensure the subscription object is available before attaching listeners.
74
+ if (subscription) {
75
+ const handleInvoiceUpdate = (event: { status: string }) => {
76
+ console.log('Received event:', event);
77
+ if (event.status === 'paid') {
78
+ setStatus('paid');
79
+ }
80
+ };
81
+
82
+ // Listen for a specific event name.
83
+ // The actual event name depends on your backend implementation.
84
+ subscription.on('invoice.paid', handleInvoiceUpdate);
85
+
86
+ // Cleanup function to remove the listener when the component
87
+ // unmounts or the subscription object changes.
88
+ return () => {
89
+ subscription.off('invoice.paid', handleInvoiceUpdate);
90
+ };
91
+ }
92
+ }, [subscription]); // Re-run effect if the subscription object changes.
93
+
94
+ return (
95
+ <div>
96
+ <h2>Invoice Status</h2>
97
+ <p>Invoice ID: {invoiceId}</p>
98
+ <p>Status: <strong>{status.toUpperCase()}</strong></p>
99
+ </div>
100
+ );
101
+ }
102
+
103
+ // Example of how to use the InvoiceStatus component within an application.
104
+ function App() {
105
+ const { session, connectApi } = useSessionContext();
106
+
107
+ // Render the component only after the session is loaded.
108
+ if (!session) {
109
+ return <div>Loading session...</div>;
110
+ }
111
+
112
+ return (
113
+ <PaymentProvider session={session} connect={connectApi}>
114
+ <InvoiceStatus invoiceId="invoice_12345" initialStatus="pending" />
115
+ </PaymentProvider>
116
+ );
117
+ }
118
+
119
+ export default App;
120
+ ```
121
+
122
+ In this example:
123
+ 1. The `App` component calls `useSessionContext()` to retrieve the user `session` and `connectApi` client.
124
+ 2. It renders the `PaymentProvider`, which provides the necessary context to its children. For details on setting up your session context, please refer to the [PaymentProvider](./providers-payment-provider.md) documentation.
125
+ 3. The `InvoiceStatus` component subscribes to a channel named after the `invoiceId`.
126
+ 4. An effect is set up to listen for changes to the `subscription` object.
127
+ 5. Once the `subscription` object is available, an event handler `handleInvoiceUpdate` is attached to the `invoice.paid` event.
128
+ 6. When a matching event is received from the server, the handler updates the component's state, and the UI re-renders to show the "PAID" status.
129
+ 7. The `useEffect` cleanup function ensures the event listener is removed to prevent memory leaks.
package/docs/hooks.md ADDED
@@ -0,0 +1,84 @@
1
+ # Hooks
2
+
3
+ The `@blocklet/payment-react` library includes custom React hooks to manage specific functionalities and side effects within your application. These hooks abstract away complex logic, making it easier to handle real-time events and create responsive user interfaces.
4
+
5
+ This section provides an overview of the available hooks. For detailed usage and examples, refer to the specific hook's documentation.
6
+
7
+ ---
8
+
9
+ ## useSubscription
10
+
11
+ The `useSubscription` hook establishes a WebSocket connection to listen for real-time events from the payment service. It is essential for scenarios where you need to react to asynchronous updates, such as a payment being successfully processed or an invoice status changing.
12
+
13
+ **When to Use:**
14
+ - When you need to update the UI in real-time based on backend payment events.
15
+ - To listen for `invoice.paid`, `subscription.updated`, or other critical events without constant polling.
16
+
17
+ **Basic Usage:**
18
+
19
+ ```tsx
20
+ import { useSubscription } from '@blocklet/payment-react';
21
+ import { useEffect } from 'react';
22
+
23
+ function RealTimeInvoiceStatus({ invoiceId }) {
24
+ // The channel's value cannot contain separators like /, ., :
25
+ const channel = `invoice_${invoiceId}`;
26
+ const subscription = useSubscription(channel);
27
+
28
+ useEffect(() => {
29
+ if (subscription) {
30
+ subscription.on('invoice.paid', (data) => {
31
+ console.log('Invoice paid!', data);
32
+ // Update UI to show paid status
33
+ });
34
+ }
35
+ }, [subscription]);
36
+
37
+ return <div>Listening for updates on invoice {invoiceId}...</div>;
38
+ }
39
+ ```
40
+
41
+ ➡️ **[Learn more about `useSubscription`](./hooks-use-subscription.md)**
42
+
43
+ ---
44
+
45
+ ## useMobile
46
+
47
+ The `useMobile` hook is a simple utility for detecting the viewport size. It helps determine if the application is being viewed on a mobile device based on Material-UI's breakpoint system, which is useful for creating responsive payment flows.
48
+
49
+ **When to Use:**
50
+ - To conditionally render different layouts for mobile and desktop views.
51
+ - To adjust component props (e.g., `mode='inline'` vs `mode='popup'`) based on screen size.
52
+
53
+ **Basic Usage:**
54
+
55
+ ```tsx
56
+ import { PaymentProvider, CheckoutForm, useMobile } from '@blocklet/payment-react';
57
+ // Import your own session context hook
58
+ import { useSessionContext } from '../hooks/session';
59
+
60
+ function ResponsiveCheckout() {
61
+ const { session, connectApi } = useSessionContext();
62
+ const { isMobile } = useMobile();
63
+
64
+ return (
65
+ <PaymentProvider session={session} connect={connectApi}>
66
+ <CheckoutForm
67
+ id="plink_xxx"
68
+ // Use a different mode on mobile devices
69
+ mode={isMobile ? 'popup' : 'inline'}
70
+ />
71
+ </PaymentProvider>
72
+ );
73
+ }
74
+ ```
75
+
76
+ ➡️ **[Learn more about `useMobile`](./hooks-use-mobile.md)**
77
+
78
+ ---
79
+
80
+ ## Next Steps
81
+
82
+ Now that you have an overview of the available hooks, you can explore the rich set of UI and business logic components to build your payment flows.
83
+
84
+ ➡️ **[Explore Components](./components.md)**