@blocklet/payment-react 1.18.6 → 1.18.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +394 -341
- package/es/checkout/donate.d.ts +28 -4
- package/es/checkout/donate.js +5 -6
- package/es/components/loading-button.d.ts +10 -0
- package/es/components/loading-button.js +75 -0
- package/es/components/pricing-table.js +2 -3
- package/es/index.d.ts +2 -1
- package/es/index.js +3 -1
- package/es/payment/form/index.js +13 -11
- package/es/payment/form/stripe/form.js +19 -4
- package/es/payment/index.js +0 -1
- package/es/payment/summary.js +1 -1
- package/es/theme/index.js +2 -1
- package/lib/checkout/donate.d.ts +28 -4
- package/lib/checkout/donate.js +4 -7
- package/lib/components/loading-button.d.ts +10 -0
- package/lib/components/loading-button.js +86 -0
- package/lib/components/pricing-table.js +3 -4
- package/lib/index.d.ts +2 -1
- package/lib/index.js +8 -0
- package/lib/payment/form/index.js +12 -13
- package/lib/payment/form/stripe/form.js +24 -5
- package/lib/payment/index.js +0 -1
- package/lib/payment/summary.js +3 -3
- package/lib/theme/index.js +2 -1
- package/package.json +6 -6
- package/src/checkout/donate.tsx +33 -16
- package/src/components/loading-button.tsx +100 -0
- package/src/components/pricing-table.tsx +3 -3
- package/src/index.ts +2 -0
- package/src/payment/form/index.tsx +64 -59
- package/src/payment/form/stripe/form.tsx +20 -5
- package/src/payment/index.tsx +0 -1
- package/src/payment/summary.tsx +1 -1
- package/src/theme/index.tsx +1 -0
package/README.md
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @blocklet/payment-react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](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
|
|
23
|
+
npm install @blocklet/payment-react
|
|
8
24
|
```
|
|
9
25
|
|
|
10
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
50
|
-
<CheckoutTable
|
|
51
|
-
id="prctbl_xxx"
|
|
52
|
-
mode="inline"
|
|
53
|
-
/>
|
|
72
|
+
### UI Components
|
|
54
73
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
countryFieldName="billing_address.country"
|
|
90
|
-
/>
|
|
108
|
+
### History Components
|
|
109
|
+
- `CustomerInvoiceList` - Invoice history list
|
|
110
|
+
- `CustomerPaymentList` - Payment history list
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
sx={{}}
|
|
97
|
-
/>
|
|
112
|
+
### Context Providers
|
|
113
|
+
- `PaymentProvider` - Payment context provider
|
|
114
|
+
- `DonateProvider` - Donation context provider
|
|
115
|
+
- `PaymentThemeProvider` - Theme provider
|
|
98
116
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
//
|
|
163
|
-
|
|
131
|
+
// With query parameters
|
|
132
|
+
const results = await api.get('/api/invoices', {
|
|
133
|
+
params: { status: 'paid' }
|
|
134
|
+
});
|
|
164
135
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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 {
|
|
171
|
+
import { dayjs } from '@blocklet/payment-react';
|
|
176
172
|
|
|
177
|
-
//
|
|
178
|
-
|
|
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
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
import('
|
|
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
|
-
|
|
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
|
-
|
|
260
|
-
globalThis.__DEPENDENCIES__ = dependencies;
|
|
231
|
+
### Donation Page Example
|
|
261
232
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
268
|
-
//
|
|
269
|
-
const {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
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
|
-
<
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
289
|
-
|
|
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
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
416
|
+
const persistentCache = new CachedRequest('key', fetchData, {
|
|
417
|
+
strategy: 'local',
|
|
418
|
+
ttl: 24 * 60 * 60 * 1000 // 1 day
|
|
419
|
+
});
|
|
311
420
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
451
|
-
<Livemode
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
```
|