payment-kit 1.13.216 → 1.13.217

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/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.13.216
17
+ version: 1.13.217
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.216",
3
+ "version": "1.13.217",
4
4
  "scripts": {
5
5
  "dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -51,7 +51,7 @@
51
51
  "@arcblock/ux": "^2.9.66",
52
52
  "@arcblock/validator": "^1.18.115",
53
53
  "@blocklet/logger": "1.16.25",
54
- "@blocklet/payment-react": "1.13.216",
54
+ "@blocklet/payment-react": "1.13.217",
55
55
  "@blocklet/sdk": "1.16.25",
56
56
  "@blocklet/ui-react": "^2.9.66",
57
57
  "@blocklet/uploader": "^0.0.76",
@@ -65,6 +65,7 @@
65
65
  "@ocap/mcrypto": "^1.18.115",
66
66
  "@ocap/util": "^1.18.115",
67
67
  "@ocap/wallet": "^1.18.115",
68
+ "@react-pdf/renderer": "^3.4.2",
68
69
  "@stripe/react-stripe-js": "^2.4.0",
69
70
  "@stripe/stripe-js": "^2.4.0",
70
71
  "ahooks": "^3.7.10",
@@ -114,7 +115,7 @@
114
115
  "devDependencies": {
115
116
  "@abtnode/types": "1.16.25",
116
117
  "@arcblock/eslint-config-ts": "^0.3.0",
117
- "@blocklet/payment-types": "1.13.216",
118
+ "@blocklet/payment-types": "1.13.217",
118
119
  "@types/cookie-parser": "^1.4.6",
119
120
  "@types/cors": "^2.8.17",
120
121
  "@types/dotenv-flow": "^3.3.3",
@@ -153,5 +154,5 @@
153
154
  "parser": "typescript"
154
155
  }
155
156
  },
156
- "gitHead": "bc5219c800ae2b18883ac598bf91d4c1c2e71523"
157
+ "gitHead": "3ce8954d7c26c881e0a7a46925447318954adca9"
157
158
  }
@@ -48,12 +48,6 @@ export default function InvoiceActions({ data, variant, onChange, mode }: Props)
48
48
  const isAdmin = mode === 'admin';
49
49
 
50
50
  const actions = [
51
- {
52
- label: t('admin.invoice.download'),
53
- handler: () => setState({ action: 'download' }),
54
- color: 'primary',
55
- disabled: data.status !== 'paid',
56
- },
57
51
  isAdmin && {
58
52
  label: t('admin.invoice.edit'),
59
53
  handler: () => setState({ action: 'edit' }),
@@ -0,0 +1,172 @@
1
+ const compose = (classes: string) => {
2
+ const css = {};
3
+
4
+ const classesArray: string[] = classes.replace(/\s+/g, ' ').split(' ');
5
+
6
+ classesArray.forEach((className) => {
7
+ if (typeof styles[className] !== undefined) {
8
+ Object.assign(css, styles[className]);
9
+ }
10
+ });
11
+
12
+ return css;
13
+ };
14
+
15
+ const colorDark = '#222';
16
+ const colorDark2 = '#888';
17
+ const colorGray = '#e3e3e3';
18
+ const colorWhite = '#fff';
19
+
20
+ const styles: any = {
21
+ dark: {
22
+ color: colorDark,
23
+ },
24
+
25
+ white: {
26
+ color: colorWhite,
27
+ },
28
+
29
+ gray: {
30
+ color: colorDark2,
31
+ },
32
+
33
+ 'bg-dark': {
34
+ backgroundColor: colorDark2,
35
+ },
36
+
37
+ 'bg-gray': {
38
+ backgroundColor: colorGray,
39
+ },
40
+
41
+ flex: {
42
+ display: 'flex',
43
+ flexDirection: 'row',
44
+ flexWrap: 'nowrap',
45
+ alignItems: 'start',
46
+ },
47
+
48
+ bold: {
49
+ fontWeight: 'bold',
50
+ },
51
+
52
+ 'w-auto': {
53
+ flex: 1,
54
+ paddingRight: '8px',
55
+ },
56
+
57
+ 'flex-1': {
58
+ flex: 1,
59
+ },
60
+
61
+ 'w-100': {
62
+ width: '100%',
63
+ },
64
+
65
+ 'w-50': {
66
+ width: '50%',
67
+ },
68
+
69
+ 'w-55': {
70
+ width: '55%',
71
+ },
72
+
73
+ 'w-45': {
74
+ width: '45%',
75
+ },
76
+
77
+ 'w-60': {
78
+ width: '60%',
79
+ },
80
+
81
+ 'w-40': {
82
+ width: '30%',
83
+ },
84
+
85
+ 'w-48': {
86
+ width: '48%',
87
+ },
88
+
89
+ 'w-17': {
90
+ width: '17%',
91
+ },
92
+
93
+ 'w-18': {
94
+ width: '18%',
95
+ },
96
+
97
+ row: {
98
+ borderBottom: `1px solid ${colorGray}`,
99
+ },
100
+
101
+ 'mt-40': {
102
+ marginTop: '40px',
103
+ },
104
+
105
+ 'mt-30': {
106
+ marginTop: '30px',
107
+ },
108
+
109
+ 'mt-20': {
110
+ marginTop: '20px',
111
+ },
112
+
113
+ 'mt-10': {
114
+ marginTop: '10px',
115
+ },
116
+
117
+ 'mb-5': {
118
+ marginBottom: '5px',
119
+ },
120
+
121
+ 'p-4-8': {
122
+ padding: '4px 8px',
123
+ },
124
+
125
+ 'p-5': {
126
+ padding: '5px',
127
+ },
128
+
129
+ 'pb-10': {
130
+ paddingBottom: '10px',
131
+ },
132
+
133
+ right: {
134
+ textAlign: 'right',
135
+ },
136
+ center: {
137
+ textAlign: 'right',
138
+ },
139
+
140
+ 'fs-20': {
141
+ fontSize: '20px',
142
+ },
143
+
144
+ 'fs-30': {
145
+ fontSize: '30px',
146
+ },
147
+
148
+ 'fs-12': {
149
+ fontSize: '11px',
150
+ },
151
+
152
+ page: {
153
+ fontFamily: 'Noto Sans SC',
154
+ fontSize: '11px',
155
+ color: '#555',
156
+ padding: '40px 35px',
157
+ fontWeight: 'bold',
158
+ },
159
+
160
+ span: {
161
+ padding: '4px 12px 4px 0',
162
+ },
163
+
164
+ logo: {
165
+ display: 'block',
166
+ borderRadius: '10px',
167
+ width: '60px',
168
+ height: '60px',
169
+ },
170
+ };
171
+
172
+ export default compose;
@@ -0,0 +1,222 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { formatAmount, formatTime, getPrefix, getPriceUintAmountByCurrency } from '@blocklet/payment-react';
3
+ import { Button } from '@mui/material';
4
+ import {
5
+ Font,
6
+ PDFDownloadLink,
7
+ Document as PdfDocument,
8
+ Image as PdfImage,
9
+ Page as PdfPage,
10
+ Text as PdfText,
11
+ View as PdfView,
12
+ } from '@react-pdf/renderer';
13
+ import { useEffect, useState } from 'react';
14
+ import { joinURL } from 'ufo';
15
+
16
+ import compose from './compose';
17
+
18
+ Font.register({
19
+ family: 'Noto Sans SC',
20
+ src: joinURL(getPrefix(), '/fonts/noto-sans-sc-chinese-simplified-500-normal.ttf'),
21
+ });
22
+
23
+ function Document({ children }: any) {
24
+ return <PdfDocument>{children}</PdfDocument>;
25
+ }
26
+
27
+ function Text2({ className, value }: any) {
28
+ return <PdfText style={compose(`span ${className || ''}`)}>{value}</PdfText>;
29
+ }
30
+
31
+ function Text({ className, children }: any) {
32
+ return <PdfText style={compose(`span ${className || ''}`)}>{children}</PdfText>;
33
+ }
34
+
35
+ function View({ className, children }: any) {
36
+ return <PdfView style={compose(`view ${className || ''}`)}>{children}</PdfView>;
37
+ }
38
+
39
+ function Page({ className, children }: any) {
40
+ return (
41
+ <PdfPage size="A4" style={compose(`page ${className || ''}`)}>
42
+ {children}
43
+ </PdfPage>
44
+ );
45
+ }
46
+
47
+ export function Download({ data }: any) {
48
+ const { t } = useLocaleContext();
49
+ const title = data.number;
50
+ return (
51
+ <div className="download-pdf ">
52
+ <PDFDownloadLink
53
+ key="pdf"
54
+ document={<InvoicePage data={data} t={t} />}
55
+ fileName={`${title}.pdf`}
56
+ aria-label="Save PDF"
57
+ title="Save PDF"
58
+ className="download-pdf__pdf">
59
+ <Button variant="contained" color="primary" size="small">
60
+ {t('payment.customer.invoice.download')}
61
+ </Button>
62
+ </PDFDownloadLink>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function InvoicePage({ data, t }: any) {
68
+ const [subTotal, setSubTotal] = useState(0);
69
+
70
+ const calculateAmount = (quantity: string, rate: string) => {
71
+ const quantityNumber = parseFloat(quantity);
72
+ const rateNumber = parseFloat(rate);
73
+ const amount = quantityNumber && rateNumber ? quantityNumber * rateNumber : 0;
74
+ return amount.toFixed(2);
75
+ };
76
+
77
+ useEffect(() => {
78
+ let temp = 0;
79
+
80
+ data.productLines.forEach((productLine: any) => {
81
+ const quantityNumber = parseFloat(productLine.quantity);
82
+ const rateNumber = parseFloat(productLine.rate);
83
+ const amount = quantityNumber && rateNumber ? quantityNumber * rateNumber : 0;
84
+
85
+ temp += amount;
86
+ });
87
+
88
+ setSubTotal(temp);
89
+ }, [data.productLines]);
90
+
91
+ return (
92
+ <Document>
93
+ <Page className="invoice-wrapper">
94
+ <View className="flex">
95
+ <View className="w-50 flex-1">
96
+ <Text2 className="fs-20 bold" value={t('admin.invoice.name')} />
97
+ </View>
98
+ <View className="right">
99
+ <PdfImage
100
+ style={{
101
+ ...compose('image logo center'),
102
+ }}
103
+ src={window.blocklet.appLogo}
104
+ />
105
+ <Text2 value={window.blocklet.appName} className="center p-10" />
106
+ </View>
107
+ </View>
108
+
109
+ <View className="flex mt-40">
110
+ <View className="w-60">
111
+ <Text2 className="bold dark mb-5" value={t('admin.invoice.billTo')} />
112
+ <Text2 value={data.customer.name} />
113
+ <Text2 className="blod fs-12" value={`DID:ABT: ${data.customer.did}`} />
114
+ </View>
115
+ <View className="w-45">
116
+ <View className="flex mb-5">
117
+ <View className="w-45">
118
+ <Text2 className="bold" value={t('admin.invoice.number')} />
119
+ </View>
120
+ <View className="w-60">
121
+ <Text2 value={data.number} className="gray" />
122
+ </View>
123
+ </View>
124
+ <View className="flex mb-5">
125
+ <View className="w-45">
126
+ <Text2 className="bold" value={t('admin.invoice.paidAt')} />
127
+ </View>
128
+ <View className="w-60">
129
+ <Text2 value={formatTime(data.period_start * 1000)} className="gray" />
130
+ </View>
131
+ </View>
132
+ <View className="flex mb-5">
133
+ <View className="w-45">
134
+ <Text2 className="bold" value={t('admin.invoice.dueDate')} />
135
+ </View>
136
+ <View className="w-60">
137
+ <Text2 value={formatTime(data.period_end * 1000)} className="gray" />
138
+ </View>
139
+ </View>
140
+ </View>
141
+ </View>
142
+
143
+ <View className="mt-30 row flex">
144
+ <View className="w-48 p-4-8">
145
+ <Text2 className="bold" value={t('admin.subscription.product')} />
146
+ </View>
147
+ <View className="w-17 p-4-8">
148
+ <Text2 className="bold right" value={t('common.quantity')} />
149
+ </View>
150
+ <View className="w-17 p-4-8">
151
+ <Text2 className="bold right" value={t('payment.customer.invoice.unitPrice')} />
152
+ </View>
153
+ <View className="w-18 p-4-8">
154
+ <Text2 className="bold right" value={t('common.amount')} />
155
+ </View>
156
+ </View>
157
+
158
+ {data.lines
159
+ .map((line: any) => {
160
+ return {
161
+ description:
162
+ line.description + (line.price.product.unit_label ? ` (per ${line.price.product.unit_label})` : ''),
163
+ quantity: line.quantity,
164
+ rate: !line.proration
165
+ ? formatAmount(getPriceUintAmountByCurrency(line.price, data.paymentCurrency) || line.amount, data.paymentCurrency.decimal) // prettier-ignore
166
+ : '',
167
+ };
168
+ })
169
+ .map((productLine: any) => {
170
+ return (
171
+ <View key={productLine.description} className="row flex">
172
+ <View className="w-48 p-4-8 pb-10">
173
+ <Text2 className="dark" value={productLine.description} />
174
+ </View>
175
+ <View className="w-17 p-4-8 pb-10">
176
+ <Text2 className="dark right" value={productLine.quantity} />
177
+ </View>
178
+ <View className="w-17 p-4-8 pb-10">
179
+ <Text className="dark right">
180
+ {productLine.rate} {data.paymentCurrency.symbol}
181
+ </Text>
182
+ </View>
183
+ <View className="w-18 p-4-8 pb-10">
184
+ <Text className="dark right">
185
+ {calculateAmount(productLine.quantity, productLine.rate)} {data.paymentCurrency.symbol}
186
+ </Text>
187
+ </View>
188
+ </View>
189
+ );
190
+ })}
191
+
192
+ <View className="flex">
193
+ <View className="w-50 mt-10" />
194
+ <View className="w-50 mt-20">
195
+ <View className="flex">
196
+ <View className="w-50 p-5">
197
+ <Text2 value={t('common.subtotal')} className="bold" />
198
+ </View>
199
+ <View className="w-50 p-5">
200
+ <Text className="right bold dark">
201
+ {subTotal?.toFixed(2)} {data.paymentCurrency.symbol}
202
+ </Text>
203
+ </View>
204
+ </View>
205
+ <View className="flex">
206
+ <View className="w-50 p-5">
207
+ <Text2 className="bold" value={t('common.total')} />
208
+ </View>
209
+ <View className="w-50 p-5">
210
+ <Text className="right bold dark">
211
+ {(typeof subTotal !== 'undefined' ? subTotal : 0).toFixed(2)} {data.paymentCurrency.symbol}
212
+ </Text>
213
+ </View>
214
+ </View>
215
+ </View>
216
+ </View>
217
+ </Page>
218
+ </Document>
219
+ );
220
+ }
221
+
222
+ export default InvoicePage;
@@ -44,7 +44,6 @@ const Transition = React.createContext({} as any);
44
44
 
45
45
  export function TransitionProvider({ children }: { children: React.ReactNode }) {
46
46
  const [isPending, startTransition] = React.useTransition();
47
-
48
47
  const memoObj = React.useMemo(() => ({ isPending, startTransition }), [isPending]);
49
48
  return <Transition.Provider value={memoObj}>{children}</Transition.Provider>;
50
49
  }
@@ -356,7 +356,7 @@ export default flat({
356
356
  finalizedAt: 'Finalized At',
357
357
  paidAt: 'Payment Date',
358
358
  summary: 'Summary',
359
- customer: 'Billed to',
359
+ billTo: 'Billed to',
360
360
  billing: 'Billing Method',
361
361
  download: 'Download PDF',
362
362
  edit: 'Edit Invoice',
@@ -347,7 +347,7 @@ export default flat({
347
347
  finalizedAt: '已完成时间',
348
348
  paidAt: '支付日期',
349
349
  summary: '摘要',
350
- customer: '开具给',
350
+ billTo: '开具给',
351
351
  billing: '计费方式',
352
352
  download: '下载PDF',
353
353
  edit: '编辑账单',
@@ -22,6 +22,7 @@ import Currency from '../../../../components/currency';
22
22
  import CustomerLink from '../../../../components/customer/link';
23
23
  import EventList from '../../../../components/event/list';
24
24
  import InfoRow from '../../../../components/info-row';
25
+ import { Download } from '../../../../components/invoice-pdf/pdf';
25
26
  import InvoiceActions from '../../../../components/invoice/action';
26
27
  import InvoiceTable from '../../../../components/invoice/table';
27
28
  import MetadataEditor from '../../../../components/metadata/editor';
@@ -103,7 +104,10 @@ export default function InvoiceDetail(props: { id: string }) {
103
104
  </Typography>
104
105
  <Status label={data.status} color={getInvoiceStatusColor(data.status)} sx={{ ml: 1 }} />
105
106
  </Stack>
106
- <InvoiceActions data={data} onChange={runAsync} variant="normal" />
107
+ <Box style={{ display: 'flex', gap: '10px' }}>
108
+ <Download data={data} />
109
+ <InvoiceActions data={data} onChange={runAsync} variant="normal" />
110
+ </Box>
107
111
  </Stack>
108
112
  </Box>
109
113
  </Box>
@@ -111,7 +115,7 @@ export default function InvoiceDetail(props: { id: string }) {
111
115
  <SectionHeader title={t('admin.details')} />
112
116
  <Stack>
113
117
  <InfoRow label={t('admin.invoice.number')} value={data.number} />
114
- <InfoRow label={t('admin.invoice.customer')} value={<CustomerLink customer={data.customer} />} />
118
+ <InfoRow label={t('admin.invoice.billTo')} value={<CustomerLink customer={data.customer} />} />
115
119
  <InfoRow
116
120
  label={t('admin.subscription.currentPeriod')}
117
121
  value={
@@ -13,6 +13,7 @@ import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom'
13
13
  import Currency from '../../../components/currency';
14
14
  import CustomerLink from '../../../components/customer/link';
15
15
  import InfoRow from '../../../components/info-row';
16
+ import { Download } from '../../../components/invoice-pdf/pdf';
16
17
  import InvoiceTable from '../../../components/invoice/table';
17
18
  import SectionHeader from '../../../components/section/header';
18
19
  import { useSessionContext } from '../../../contexts/session';
@@ -114,16 +115,7 @@ export default function CustomerInvoiceDetail() {
114
115
  </Typography>
115
116
  </Stack>
116
117
  <Stack direction="row" spacing={1} justifyContent="flex-end" alignItems="center">
117
- {['open', 'paid', 'uncollectible'].includes(data.status) && (
118
- <Button
119
- variant="contained"
120
- color="secondary"
121
- size="small"
122
- disabled={state.downloading}
123
- onClick={() => setState({ downloading: true })}>
124
- {t('payment.customer.invoice.download')}
125
- </Button>
126
- )}
118
+ {['open', 'paid', 'uncollectible'].includes(data.status) && <Download data={data} />}
127
119
  {['open', 'uncollectible'].includes(data.status) && (
128
120
  <Button variant="contained" color="primary" size="small" disabled={state.paying} onClick={onPay}>
129
121
  {t('payment.customer.invoice.pay')}
@@ -171,10 +163,7 @@ export default function CustomerInvoiceDetail() {
171
163
  }
172
164
  />
173
165
  )}
174
- <InfoRow
175
- label={t('admin.invoice.customer')}
176
- value={<CustomerLink customer={data.customer} linked={false} />}
177
- />
166
+ <InfoRow label={t('admin.invoice.billTo')} value={<CustomerLink customer={data.customer} linked={false} />} />
178
167
  </Stack>
179
168
  </Root>
180
169
  <Box>