@blocklet/payment-react 1.18.6 → 1.18.8

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 (70) hide show
  1. package/README.md +394 -341
  2. package/es/checkout/donate.d.ts +29 -4
  3. package/es/checkout/donate.js +193 -95
  4. package/es/components/livemode.js +1 -1
  5. package/es/components/loading-button.d.ts +10 -0
  6. package/es/components/loading-button.js +75 -0
  7. package/es/components/pricing-table.js +2 -3
  8. package/es/components/table.js +1 -1
  9. package/es/index.d.ts +2 -1
  10. package/es/index.js +3 -1
  11. package/es/libs/util.d.ts +1 -0
  12. package/es/libs/util.js +10 -1
  13. package/es/payment/amount.js +1 -1
  14. package/es/payment/form/index.js +14 -12
  15. package/es/payment/form/stripe/form.js +20 -5
  16. package/es/payment/index.js +0 -1
  17. package/es/payment/product-card.js +2 -2
  18. package/es/payment/product-item.js +1 -1
  19. package/es/payment/product-skeleton.js +2 -2
  20. package/es/payment/skeleton/donation.js +1 -1
  21. package/es/payment/skeleton/overview.js +1 -1
  22. package/es/payment/skeleton/payment.js +1 -1
  23. package/es/payment/summary.js +2 -2
  24. package/es/theme/index.js +5 -3
  25. package/es/theme/typography.js +8 -8
  26. package/lib/checkout/donate.d.ts +29 -4
  27. package/lib/checkout/donate.js +197 -136
  28. package/lib/components/livemode.js +1 -1
  29. package/lib/components/loading-button.d.ts +10 -0
  30. package/lib/components/loading-button.js +86 -0
  31. package/lib/components/pricing-table.js +3 -4
  32. package/lib/components/table.js +1 -1
  33. package/lib/index.d.ts +2 -1
  34. package/lib/index.js +8 -0
  35. package/lib/libs/util.d.ts +1 -0
  36. package/lib/libs/util.js +7 -0
  37. package/lib/payment/amount.js +1 -1
  38. package/lib/payment/form/index.js +14 -15
  39. package/lib/payment/form/stripe/form.js +25 -6
  40. package/lib/payment/index.js +0 -1
  41. package/lib/payment/product-card.js +2 -2
  42. package/lib/payment/product-item.js +1 -1
  43. package/lib/payment/product-skeleton.js +2 -2
  44. package/lib/payment/skeleton/donation.js +1 -1
  45. package/lib/payment/skeleton/overview.js +1 -1
  46. package/lib/payment/skeleton/payment.js +1 -1
  47. package/lib/payment/summary.js +4 -4
  48. package/lib/theme/index.js +5 -3
  49. package/lib/theme/typography.js +8 -8
  50. package/package.json +8 -8
  51. package/src/checkout/donate.tsx +209 -128
  52. package/src/components/livemode.tsx +1 -1
  53. package/src/components/loading-button.tsx +100 -0
  54. package/src/components/pricing-table.tsx +3 -3
  55. package/src/components/table.tsx +1 -1
  56. package/src/index.ts +2 -0
  57. package/src/libs/util.ts +11 -1
  58. package/src/payment/amount.tsx +1 -1
  59. package/src/payment/form/index.tsx +65 -60
  60. package/src/payment/form/stripe/form.tsx +21 -6
  61. package/src/payment/index.tsx +0 -1
  62. package/src/payment/product-card.tsx +2 -2
  63. package/src/payment/product-item.tsx +1 -1
  64. package/src/payment/product-skeleton.tsx +2 -2
  65. package/src/payment/skeleton/donation.tsx +1 -1
  66. package/src/payment/skeleton/overview.tsx +1 -1
  67. package/src/payment/skeleton/payment.tsx +1 -1
  68. package/src/payment/summary.tsx +2 -2
  69. package/src/theme/index.tsx +3 -1
  70. package/src/theme/typography.ts +8 -8
package/README.md CHANGED
@@ -1,13 +1,32 @@
1
- # Payment Kit Components
1
+ # @blocklet/payment-react
2
2
 
3
- ## Quick Start
3
+ [![npm version](https://img.shields.io/npm/v/@blocklet/payment-react)](https://www.npmjs.com/package/@blocklet/payment-react)
4
+
5
+ A React component library for building payment flows, subscriptions, and donation systems in Blocklet applications. Seamlessly integrated with Blocklet's payment infrastructure.
6
+
7
+ ## Features
8
+
9
+ - 🛠️ **Pre-built UI Components**: Includes checkout forms, pricing tables, donation widgets, and more
10
+ - 🎨 **Customizable Themes**: Full control over styling via Material-UI themes
11
+ - 🌍 **i18n Support**: Built-in localization for global audiences
12
+ - 🧩 **Lazy Loading**: Optimize bundle size with dynamic imports
13
+ - 💳 **Payment Operations**: Handle subscriptions, refunds, invoices, and metered billing
14
+
15
+ ## Related Links
16
+
17
+ - [Payment Kit Documentation](https://www.arcblock.io/docs/arcblock-payment-kit/en/start-payment-react) - Official documentation with detailed guides and API references
18
+ - [Example Implementation](https://github.com/blocklet/payment-kit/blob/master/blocklets/example/src/pages/checkout.jsx) - Complete example showing how to integrate the payment components
19
+
20
+ ## Installation
4
21
 
5
- ### 1. Installation
6
22
  ```bash
7
- npm install @blocklet/payment-react @mui/material @emotion/react @emotion/styled
23
+ npm install @blocklet/payment-react
8
24
  ```
9
25
 
10
- ### 2. Basic Setup
26
+ ## Quick Start
27
+
28
+ ### Basic Integration
29
+
11
30
  ```tsx
12
31
  import { PaymentProvider, CheckoutForm } from '@blocklet/payment-react';
13
32
 
@@ -15,314 +34,428 @@ function App() {
15
34
  return (
16
35
  <PaymentProvider session={session} connect={connectApi}>
17
36
  <CheckoutForm
18
- id="plink_xxx"
19
- mode="inline"
37
+ id="plink_xxx" // Payment Link ID
38
+ mode="inline" // Embed directly in your UI
20
39
  showCheckoutSummary={true}
21
- onChange={(state) => console.log(state)}
40
+ onChange={(state) => console.log('Checkout State:', state)}
22
41
  />
23
42
  </PaymentProvider>
24
43
  );
25
44
  }
26
45
  ```
27
46
 
28
- ## Component Usage
47
+ ## Available Components & Utilities
29
48
 
30
49
  ### Core Components
50
+ - `CheckoutForm` - Payment form for checkout sessions and payment links
51
+ - `CheckoutTable` - Pricing table display
52
+ - `CheckoutDonate` - Donation widget
53
+ - `OverdueInvoicePayment` - Handle overdue invoice payments
31
54
 
32
- ```tsx
33
- import { CheckoutForm, CheckoutTable, CheckoutDonate, PaymentProvider } from '@blocklet/payment-react';
34
-
35
- // Checkout with session
36
- <CheckoutForm
37
- id="cs_xxx"
38
- mode="inline"
39
- showCheckoutSummary={true}
40
- onChange={(state) => console.log(state)}
41
- />
55
+ ### Form Components
56
+ - `FormInput` - Base form input component
57
+ - `PhoneInput` - Phone number input with validation
58
+ - `AddressForm` - Complete address form
59
+ - `StripeForm` - Stripe payment form
60
+ - `CurrencySelector` - Currency selection dropdown
61
+ - `CountrySelect` - Country selection dropdown
42
62
 
43
- // Checkout with payment link
44
- <CheckoutForm
45
- id="plink_xxx"
46
- mode="inline"
47
- />
63
+ ### Display Components
64
+ - `Status` - Status indicator
65
+ - `Livemode` - Test mode indicator
66
+ - `Switch` - Toggle switch
67
+ - `ConfirmDialog` - Confirmation dialog
68
+ - `Amount` - Amount display with formatting
69
+ - `TruncatedText` - Text truncation
70
+ - `Link` - Safe navigation link
48
71
 
49
- // Pricing table
50
- <CheckoutTable
51
- id="prctbl_xxx"
52
- mode="inline"
53
- />
72
+ ### UI Components
54
73
 
55
- // Donation form
56
- <CheckoutDonate
57
- mode="inline"
58
- settings={{
59
- target: 'donation-target',
60
- title: 'Support Us',
61
- description: 'Help us continue our work',
62
- beneficiaries: [{
63
- address: 'wallet_address',
64
- share: '100'
65
- }],
66
- amount: {
67
- minimum: '0.01',
68
- maximum: '1',
69
- custom: true
74
+ ```tsx
75
+ // Loading Button with state management
76
+ import { LoadingButton } from '@blocklet/payment-react';
77
+
78
+ function PaymentButton() {
79
+ const [loading, setLoading] = useState(false);
80
+
81
+ const handlePayment = async () => {
82
+ setLoading(true);
83
+ try {
84
+ await processPayment();
85
+ } finally {
86
+ setLoading(false);
70
87
  }
71
- }}
72
- />
73
- ```
88
+ };
74
89
 
75
- ### Form Components
90
+ return (
91
+ <LoadingButton
92
+ loading={loading}
93
+ onClick={handlePayment}
94
+ variant="contained"
95
+ color="primary"
96
+ >
97
+ Pay Now
98
+ </LoadingButton>
99
+ );
100
+ }
101
+ ```
76
102
 
77
- ```tsx
78
- import {
79
- FormInput,
80
- PhoneInput,
81
- AddressForm,
82
- CurrencySelector,
83
- CountrySelect
84
- } from '@blocklet/payment-react';
103
+ ### Transaction Components
104
+ - `TxLink` - Transaction link
105
+ - `TxGas` - Gas fee display
106
+ - `PaymentBeneficiaries` - Payment beneficiaries list
85
107
 
86
- // Phone input with validation
87
- <PhoneInput
88
- name="phone"
89
- countryFieldName="billing_address.country"
90
- />
108
+ ### History Components
109
+ - `CustomerInvoiceList` - Invoice history list
110
+ - `CustomerPaymentList` - Payment history list
91
111
 
92
- // Address form
93
- <AddressForm
94
- mode="required"
95
- stripe={false}
96
- sx={{}}
97
- />
112
+ ### Context Providers
113
+ - `PaymentProvider` - Payment context provider
114
+ - `DonateProvider` - Donation context provider
115
+ - `PaymentThemeProvider` - Theme provider
98
116
 
99
- // Currency selector
100
- <CurrencySelector
101
- value={0}
102
- onChange={(index) => {}}
103
- currencies={[
104
- {
105
- id: 'currency_id',
106
- symbol: '$',
107
- logo: 'currency_logo_url',
108
- name: 'USD',
109
- method: { name: 'Payment Method' }
110
- }
111
- ]}
112
- />
117
+ ### Hooks
118
+ - `useSubscription` - event socket callback
119
+ - `useMobile` - Mobile detection
113
120
 
114
- // Country select
115
- <CountrySelect
116
- value="us"
117
- onChange={(value: CountryIso2) => {}}
118
- name="country"
119
- sx={{}}
120
- />
121
- ```
122
-
123
- ### Display Components
121
+ ### Utilities
124
122
 
123
+ #### API Client
125
124
  ```tsx
126
- import {
127
- PricingItem,
128
- TruncatedText,
129
- TxGas,
130
- TxLink,
131
- ProductSkeleton,
132
- } from '@blocklet/payment-react';
125
+ import { api } from '@blocklet/payment-react';
133
126
 
134
- // Display pricing information
135
- <PricingItem
136
- productId="prod_xxx"
137
- quantity={1}
138
- priceId="price_xxx"
139
- >
140
- {(pricing, product) => (
141
- <div>
142
- <div>Total: {pricing.totalPrice}</div>
143
- <div>Product: {product?.name}</div>
144
- </div>
145
- )}
146
- </PricingItem>
147
-
148
- // Display transaction gas fee
149
- <TxGas
150
- details={paymentDetails}
151
- method={paymentMethod}
152
- />
153
-
154
- // Display transaction link
155
- <TxLink
156
- details={paymentDetails}
157
- method={paymentMethod}
158
- mode="dashboard"
159
- align="left"
160
- />
127
+ // Basic usage
128
+ const response = await api.get('/api/payments');
129
+ const data = await api.post('/api/checkout', { amount: 100 });
161
130
 
162
- // Loading skeleton
163
- <ProductSkeleton />
131
+ // With query parameters
132
+ const results = await api.get('/api/invoices', {
133
+ params: { status: 'paid' }
134
+ });
164
135
 
165
- // Truncate long text
166
- <TruncatedText
167
- text="Very long text..."
168
- maxLength={20}
169
- />
136
+ // With request config
137
+ const config = {
138
+ headers: { 'Custom-Header': 'value' }
139
+ };
140
+ const response = await api.put('/api/subscription', data, config);
170
141
  ```
171
142
 
172
- ### Donation Components
143
+ #### Cached Request
144
+ ```tsx
145
+ import { CachedRequest } from '@blocklet/payment-react';
146
+
147
+ // Create a cached request
148
+ const priceRequest = new CachedRequest(
149
+ 'product-prices',
150
+ () => api.get('/api/prices'),
151
+ {
152
+ strategy: 'session', // 'session' | 'local' | 'memory'
153
+ ttl: 5 * 60 * 1000 // Cache for 5 minutes
154
+ }
155
+ );
156
+
157
+ // Use the cached request
158
+ async function fetchPrices() {
159
+ // Will use cache if available and not expired
160
+ const prices = await priceRequest.fetch();
161
+
162
+ // Force refresh cache
163
+ const freshPrices = await priceRequest.fetch(true);
164
+
165
+ return prices;
166
+ }
167
+ ```
173
168
 
169
+ #### Date Handling
174
170
  ```tsx
175
- import { CheckoutDonate } from '@blocklet/payment-react';
171
+ import { dayjs } from '@blocklet/payment-react';
176
172
 
177
- // Basic usage
178
- <CheckoutDonate
179
- mode="default" // 'default' | 'inline' | 'custom'
180
- settings={{
181
- target: 'donation-target',
182
- title: 'Support Us',
183
- description: 'Help us continue our work',
184
- beneficiaries: [{
185
- address: 'wallet_address',
186
- share: '100'
187
- }],
188
- amount: {
189
- minimum: '0.01',
190
- maximum: '100',
191
- custom: true
192
- },
193
- appearance: {
194
- button: {
195
- text: 'Donate Now',
196
- icon: <FavoriteIcon />,
197
- size: 'large',
198
- color: 'primary',
199
- variant: 'contained'
200
- },
201
- history: {
202
- variant: 'avatar' // 'avatar' | 'table'
203
- }
204
- }
205
- }}
206
- />
173
+ // Format dates
174
+ const formatted = dayjs().format('YYYY-MM-DD');
207
175
 
208
- // Custom render mode
209
- <CheckoutDonate
210
- mode="custom"
211
- settings={{
212
- target: 'custom-donation',
213
- // ... other settings
214
- }}
215
- >
216
- {(openDonate, totalAmount, supporters, loading) => (
217
- <div>
218
- {loading && <div>Loading...</div>}
219
- <button onClick={openDonate}>
220
- Support Us
221
- </button>
222
- <div>Total Donations: {totalAmount}</div>
223
- <div>Supporters: {supporters.supporters.length}</div>
224
- {supporters.supporters.map(supporter => (
225
- <div key={supporter.id}>
226
- <img src={supporter.customer?.avatar} alt={supporter.customer?.name} />
227
- <span>{supporter.customer?.name}</span>
228
- <span>{formatAmount(supporter.amount_total, supporters.currency.decimal)} {supporters.currency.symbol}</span>
229
- </div>
230
- ))}
231
- </div>
232
- )}
233
- </CheckoutDonate>
176
+ // Parse timestamps
177
+ const date = dayjs(timestamp);
178
+ const unix = date.unix();
179
+
180
+ // Relative time
181
+ const relative = dayjs().from(date);
234
182
  ```
235
183
 
236
- ### Lazy Loading Components
184
+ #### i18n Setup
185
+ ```tsx
186
+ // use your own translator
187
+ import { createTranslator } from '@blocklet/payment-react';
188
+
189
+ const translator = createTranslator({
190
+ en: {
191
+ checkout: { title: 'Complete Payment' }
192
+ },
193
+ zh: {
194
+ checkout: { title: '完成支付' }
195
+ }
196
+ });
237
197
 
238
- The SDK provides `createLazyComponent` for optimizing bundle size by loading components and their dependencies on demand:
198
+ // use payment-react locales
199
+ import { translations as extraTranslations } from '@blocklet/payment-react';
200
+ import merge from 'lodash/merge';
239
201
 
202
+ import en from './en';
203
+ import zh from './zh';
204
+
205
+ export const translations = merge(
206
+ {
207
+ zh,
208
+ en,
209
+ },
210
+ extraTranslations
211
+ );
212
+ ```
213
+
214
+ #### Lazy Loading
240
215
  ```tsx
241
216
  import { createLazyComponent } from '@blocklet/payment-react';
242
217
 
243
- // 1. Create lazy component with dependencies
244
218
  const LazyComponent = createLazyComponent(async () => {
245
- // Load dependencies in parallel
246
- const [dep1, dep2] = await Promise.all([
247
- import('heavy-dependency-1'),
248
- import('heavy-dependency-2')
219
+ const [{ Component }, { useHook }] = await Promise.all([
220
+ import('./Component'),
221
+ import('./hooks')
249
222
  ]);
223
+
224
+ globalThis.__DEPENDENCIES__ = { useHook };
225
+ return Component;
226
+ });
227
+ ```
250
228
 
251
- // Store dependencies for reuse
252
- const dependencies = {
253
- Component1: dep1.Component1,
254
- Component2: dep1.Component2,
255
- useHook1: dep2.useHook1,
256
- useHook2: dep2.useHook2,
257
- };
229
+ ## Complete Examples
258
230
 
259
- // Make dependencies available to child components
260
- globalThis.__DEPENDENCIES__ = dependencies;
231
+ ### Donation Page Example
261
232
 
262
- // Load and return the actual component that uses these dependencies
263
- const { default: Component } = await import('./MyComponent');
264
- return Component;
265
- });
233
+ ```tsx
234
+ import {
235
+ DonateProvider,
236
+ CheckoutDonate,
237
+ PaymentProvider
238
+ } from '@blocklet/payment-react';
239
+ import { useEffect, useState } from 'react';
240
+
241
+ function DonationPage() {
242
+ const [session, setSession] = useState(null);
266
243
 
267
- // 2. Use dependencies in your component
268
- // Access loaded dependencies
269
- const {
270
- Component1,
271
- Component2,
272
- useHook1,
273
- useHook2
274
- } = globalThis.__DEPENDENCIES__;
275
- function MyComponent(props) {
276
- // Use hooks from dependencies
277
- const hook1Result = useHook1();
278
- const hook2Result = useHook2();
244
+ useEffect(() => {
245
+ // Get session from your auth system
246
+ const getSession = async () => {
247
+ const userSession = await fetchSession();
248
+ setSession(userSession);
249
+ };
250
+ getSession();
251
+ }, []);
279
252
 
280
253
  return (
281
- <div>
282
- <Component1 {...hook1Result} />
283
- <Component2 {...hook2Result} />
284
- </div>
254
+ <PaymentProvider session={session} connect={connectApi}>
255
+ <DonateProvider
256
+ mountLocation="your-unique-donate-instance"
257
+ description="Help locate this donation instance"
258
+ defaultSettings={{
259
+ btnText: 'Like',
260
+ }}
261
+ >
262
+ <CheckoutDonate
263
+ settings={{
264
+ target: "post-123", // required, unique identifier for the donation instance
265
+ title: "Support Author", // required, title of the donation modal
266
+ description: "If you find this article helpful, feel free to buy me a coffee", // required, description of the donation
267
+ reference: "https://your-site.com/posts/123", // required, reference link of the donation
268
+ beneficiaries: [
269
+ {
270
+ address: "tip user did", // required, address of the beneficiary
271
+ share: "100", // required, percentage share
272
+ },
273
+ ],
274
+ }}
275
+ />
276
+
277
+ {/* Custom donation history display */}
278
+ <CheckoutDonate
279
+ mode="custom"
280
+ settings={{
281
+ target: "post-123", // required, unique identifier for the donation instance
282
+ title: "Support Author", // required, title of the donation modal
283
+ description: "If you find this article helpful, feel free to buy me a coffee", // required, description of the donation
284
+ reference: "https://your-site.com/posts/123", // required, reference link of the donation
285
+ beneficiaries: [
286
+ {
287
+ address: "tip user did", // required, address of the beneficiary
288
+ share: "100", // required, percentage share
289
+ },
290
+ ],
291
+ }}
292
+ >
293
+ {(openDonate, totalAmount, supporters, loading, settings) => (
294
+ <div>
295
+ <h2>Our Supporters</h2>
296
+ {loading ? (
297
+ <CircularProgress />
298
+ ) : (
299
+ <div>
300
+ <div>
301
+ Total Donations: {totalAmount} {supporters.currency?.symbol}
302
+ </div>
303
+ <div>
304
+ {supporters.supporters.map(supporter => (
305
+ <div key={supporter.id}>
306
+ <span>{supporter.customer?.name}</span>
307
+ <span>{supporter.amount_total} {supporters.currency?.symbol}</span>
308
+ </div>
309
+ ))}
310
+ </div>
311
+ </div>
312
+ )}
313
+ </div>
314
+ )}
315
+ </CheckoutDonate>
316
+ </DonateProvider>
317
+ </PaymentProvider>
285
318
  );
286
319
  }
320
+ ```
287
321
 
288
- // 3. Use the lazy component
289
- function App() {
322
+ ### Subscription Management Example
323
+
324
+ - `OverdueInvoicePayment` component
325
+ - Display overdue invoices for a subscription, and support batch payment
326
+ - props:
327
+ - `subscriptionId`: [required] The subscription ID
328
+ - `onPaid`: [optional] Callback function called after successful payment
329
+ - `mode`: [optional] Component mode, `default` or `custom` (default is `default`)
330
+ - `dialogProps`: [optional] Dialog properties, default is `{ open: true }`
331
+ - `children`: [optional] Custom rendering with payment data when `mode` is `custom`
332
+ - Custom mode:
333
+ - `children` will receive two parameters:
334
+ - `handlePay`: Function to start the
335
+
336
+ payment process
337
+ - `data`: Payment data (includes `subscription`, `summary`, `invoices`, `subscriptionUrl`)
338
+ ```tsx
339
+ import {
340
+ PaymentProvider,
341
+ OverdueInvoicePayment,
342
+ CustomerInvoiceList,
343
+ Amount
344
+ } from '@blocklet/payment-react';
345
+
346
+ function SubscriptionPage({ subscriptionId }) {
290
347
  return (
291
- <LazyComponent
292
- prop1="value1"
293
- prop2="value2"
294
- onEvent={() => {}}
295
- />
348
+ <PaymentProvider session={session}>
349
+ {/* Handle overdue payments */}
350
+ <OverdueInvoicePayment
351
+ subscriptionId={subscriptionId}
352
+ onPaid={() => {
353
+ // Refresh subscription status
354
+ refetchSubscription();
355
+ }}
356
+ />
357
+
358
+ {/* Custom Overdue Invoice Payment */}
359
+ <OverdueInvoicePayment
360
+ subscriptionId={subscriptionId}
361
+ onPaid={() => {
362
+ refetchSubscription();
363
+ }}
364
+ mode="custom"
365
+ >
366
+ {(handlePay, { subscription, summary, invoices }) => (
367
+ <Card>
368
+ <CardHeader title="Overdue Payments" />
369
+ <CardContent>
370
+ <Stack spacing={2}>
371
+ {Object.entries(summary).map(([currencyId, info]) => (
372
+ <div key={currencyId}>
373
+ <Typography>
374
+ Due Amount:
375
+ <Amount
376
+ amount={info.amount}
377
+ />
378
+ {info.currency?.symbol}
379
+ </Typography>
380
+ <Button
381
+ onClick={() => handlePay(info)}
382
+ variant="contained"
383
+ >
384
+ Pay Now
385
+ </Button>
386
+ </div>
387
+ ))}
388
+ </Stack>
389
+ </CardContent>
390
+ </Card>
391
+ )}
392
+ </OverdueInvoicePayment>
393
+
394
+ {/* Display invoice history */}
395
+ <CustomerInvoiceList
396
+ subscription_id={subscriptionId}
397
+ type="table"
398
+ include_staking
399
+ status="open,paid,uncollectible,void"
400
+ />
401
+ </PaymentProvider>
296
402
  );
297
403
  }
298
404
  ```
299
405
 
300
- Key features:
301
- - 🚀 Loads dependencies on demand
302
- - 📦 Reduces initial bundle size
303
- - 🌐 Provides global access to loaded dependencies
304
- - ⚡️ Supports parallel loading
305
- - 🔄 Handles async initialization
306
- - ❌ Built-in error handling
406
+ ## Best Practices
307
407
 
308
- ## Theme Customization
408
+ ### Cache Management
409
+ ```tsx
410
+ // 1. Choose appropriate cache strategy
411
+ const shortLivedCache = new CachedRequest('key', fetchData, {
412
+ strategy: 'memory',
413
+ ttl: 60 * 1000 // 1 minute
414
+ });
309
415
 
310
- Since version 1.14.22, the component includes a built-in theme provider. If you need to modify the styles of internal components, you need to pass the `theme` property to override or inherit the external theme.
416
+ const persistentCache = new CachedRequest('key', fetchData, {
417
+ strategy: 'local',
418
+ ttl: 24 * 60 * 60 * 1000 // 1 day
419
+ });
311
420
 
312
- | option | description |
313
- | --- | --- |
314
- | default [default value] | wrapped with built-in PaymentThemeProvider |
315
- | inherit | use the parent component's themeProvider |
316
- | PaymentThemeOptions | override some styles of PaymentThemeProvider |
421
+ // 2. Clear cache when data changes
422
+ async function updateData() {
423
+ await api.post('/api/data', newData);
424
+ await cache.fetch(true); // Force refresh
425
+ }
317
426
 
318
- ```ts
319
- export type PaymentThemeOptions = ThemeOptions & {
320
- sx?: SxProps;
321
- };
427
+ // 3. Handle cache errors
428
+ try {
429
+ const data = await cache.fetch();
430
+ } catch (err) {
431
+ console.error('Cache error:', err);
432
+ // Fallback to fresh data
433
+ const fresh = await cache.fetch(true);
434
+ }
322
435
  ```
323
436
 
437
+ ### Bundle Optimization
438
+ - Use lazy loading for non-critical components
439
+ - Import only required components
440
+ - Leverage code splitting with dynamic imports
441
+
442
+ ### Theme Consistency
443
+ - Maintain consistent styling across components
444
+ - Use theme provider for global style changes
445
+ - Override styles at component level when needed
446
+
447
+ ### Theme Customization
448
+
449
+ Since version 1.14.22, the component includes a built-in theme provider. If you need to modify the styles of internal components, pass the `theme` property to override or inherit the external theme.
450
+
451
+ | Option | Description |
452
+ | --- | --- |
453
+ | default | Wrapped with built-in `PaymentThemeProvider` |
454
+ | inherit | Use the parent component's themeProvider |
455
+ | PaymentThemeOptions | Override some styles of `PaymentThemeProvider` |
456
+
324
457
  ```tsx
325
- // 1. use themeOptions
458
+ // 1. Use themeOptions
326
459
  <CheckoutForm
327
460
  id="plink_xxx"
328
461
  onChange={console.info}
@@ -343,7 +476,7 @@ export type PaymentThemeOptions = ThemeOptions & {
343
476
  }}
344
477
  />
345
478
 
346
- // 2. use theme sx
479
+ // 2. Use theme sx
347
480
  <CheckoutForm
348
481
  id="plink_xxx"
349
482
  showCheckoutSummary={false}
@@ -362,73 +495,6 @@ export type PaymentThemeOptions = ThemeOptions & {
362
495
  />
363
496
  ```
364
497
 
365
- ## I18n Support
366
-
367
- ```tsx
368
- import { createTranslator, translations as extraTranslations } from '@blocklet/payment-react';
369
- import merge from 'lodash/merge';
370
-
371
- import en from './en';
372
- import zh from './zh';
373
-
374
- export const translations = merge({ zh, en }, extraTranslations);
375
- ```
376
-
377
- ## Recent Updates
378
-
379
- ### v1.14.23
380
- We recommend using `showCheckoutSummary={false}` instead of `mode=inline-minimal` for better semantics:
381
-
382
- ```tsx
383
- // Recommended
384
- <CheckoutForm
385
- id="plink_xxx"
386
- showCheckoutSummary={false}
387
- onChange={console.info}
388
- />
389
-
390
- // Not recommended
391
- <CheckoutForm
392
- id="plink_xxx"
393
- mode="inline-minimal"
394
- onChange={console.info}
395
- />
396
- ```
397
-
398
- ### v1.16.4
399
-
400
- - Add `OverdueInvoicePayment` component
401
- - display all overdue invoices of a subscription, and support batch payment
402
- - props:
403
- - `subscriptionId`: [required] the id of the subscription
404
- - `onPaid`: [optional] a callback function that will be called when the payment is successful
405
- - `mode`: [optional] the mode of the component, `default` or `custom`, default is `default`
406
- - `dialogProps`: [optional] the props of the dialog, default is `{ open: true }`
407
- - `children`: [optional] a function that will be called with the payment data, if `mode` is `custom`, otherwise, the default dialog will be displayed
408
- - custom mode:
409
- - the `children` will receive two parameters:
410
- - `handlePay`: a function that starts the payment process
411
- - `data`: the payment data
412
- - `subscription`: the subscription data
413
- - `summary`: the summary of the payment, the key is the `currencyId`, the value includes `amount`, `currency`, `method`
414
- - `invoices`: the list of invoices
415
- - `subscriptionUrl`: the url of the subscription
416
- ```tsx
417
- <OverdueInvoicePayment subscriptionId={data.id} onPaid={() => { console.log('paid') }} />
418
-
419
- // custom mode
420
- <OverdueInvoicePayment subscriptionId={data.id} onPaid={() => { console.log('paid') }} mode="custom">
421
- {(handlePay, { subscription, summary, invoices, subscriptionUrl }) => (
422
- <div>custom content</div>
423
- <p>Subscription: {subscription.name}</p>
424
- <p>Current currency Total: {summary[currencyId].amount}</p>
425
- <p>Invoices: {invoices.length}</p>
426
- <p>Subscription URL: {subscriptionUrl}</p>
427
- <button onClick={() => handlePay(summary[currencyId])}>Pay</button>
428
- )}
429
- </OverdueInvoicePayment>
430
- ```
431
-
432
498
  ### Status & Utility Components
433
499
  ```tsx
434
500
  import {
@@ -447,33 +513,20 @@ import {
447
513
  sx={{ margin: 1 }}
448
514
  />
449
515
 
450
- // Live/Test mode indicator
451
- <Livemode livemode={false} />
516
+ // Test mode indicator
517
+ <Livemode />
452
518
 
453
519
  // Custom switch button
454
520
  <Switch
455
521
  checked={true}
456
522
  onChange={(checked) => console.log('Switched:', checked)}
457
- size="small"
458
- color="primary"
459
523
  />
460
524
 
461
525
  // Safe navigation link
462
- <Link
463
- href="https://example.com"
464
- confirmBeforeNavigate={true}
465
- confirmMessage="Are you sure to leave?"
466
- target="_blank"
467
- />
526
+ <Link to="/demo" />
527
+
528
+
529
+ ## License
530
+
531
+ Apache-2.0
468
532
 
469
- // Amount display with formatting
470
- <Amount
471
- value={1000}
472
- currency={{
473
- symbol: '$',
474
- decimal: 2
475
- }}
476
- variant="body1"
477
- color="primary"
478
- />
479
- ```