payment-kit 1.13.250 → 1.13.251

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.
@@ -1,11 +1,8 @@
1
1
  import { JsonRpcProvider, TransactionReceipt, ethers } from 'ethers';
2
2
 
3
3
  import { ethWallet } from '../../libs/auth';
4
- import logger from '../../libs/logger';
5
- import type { PaymentMethod } from '../../store/models/payment-method';
6
4
  import { getApproveFunction } from './contract';
7
5
  import erc20Abi from './erc20-abi.json';
8
- import { waitForEvmTxReceipt } from './tx';
9
6
 
10
7
  export async function fetchErc20Meta(provider: JsonRpcProvider, contractAddress: string) {
11
8
  const contract = new ethers.Contract(contractAddress, erc20Abi, provider);
@@ -125,27 +122,3 @@ export async function sendErc20ToUser(
125
122
  const receipt = await res.wait();
126
123
  return receipt;
127
124
  }
128
-
129
- export async function executeEvmTransaction(
130
- type: string,
131
- userDid: string,
132
- claims: any[],
133
- paymentMethod: PaymentMethod
134
- ) {
135
- const client = paymentMethod.getEvmClient();
136
- const claim = claims.find((x) => x.type === 'signature');
137
- logger.info('executeEvmTransaction', { type, userDid, claim });
138
- const receipt = await waitForEvmTxReceipt(client, claim.hash);
139
- if (!receipt.status) {
140
- throw new Error(`EVM Transaction failed: ${claim.hash}`);
141
- }
142
-
143
- return {
144
- type,
145
- tx_hash: claim.hash,
146
- payer: userDid,
147
- block_height: receipt.blockNumber.toString(),
148
- gas_used: receipt.gasUsed.toString(),
149
- gas_price: receipt.gasPrice.toString(),
150
- };
151
- }
@@ -2,6 +2,8 @@ import type { JsonRpcProvider, TransactionReceipt, TransactionResponse } from 'e
2
2
  import waitFor from 'p-wait-for';
3
3
 
4
4
  import logger from '../../libs/logger';
5
+ import { broadcast } from '../../libs/ws';
6
+ import type { PaymentMethod } from '../../store/models/payment-method';
5
7
 
6
8
  export async function waitForEvmTxReceipt(provider: JsonRpcProvider, txHash: string) {
7
9
  let mined: TransactionResponse;
@@ -41,3 +43,38 @@ export async function waitForEvmTxConfirm(provider: JsonRpcProvider, height: num
41
43
  { interval: 3000, timeout: 30 * 60 * 1000 }
42
44
  );
43
45
  }
46
+
47
+ export async function executeEvmTransaction(
48
+ type: string,
49
+ userDid: string,
50
+ claims: any[],
51
+ paymentMethod: PaymentMethod
52
+ ) {
53
+ const client = paymentMethod.getEvmClient();
54
+ const claim = claims.find((x) => x.type === 'signature');
55
+ logger.info('executeEvmTransaction', { type, userDid, claim });
56
+ const receipt = await waitForEvmTxReceipt(client, claim.hash);
57
+ if (!receipt.status) {
58
+ throw new Error(`EVM Transaction failed: ${claim.hash}`);
59
+ }
60
+
61
+ return {
62
+ type,
63
+ tx_hash: claim.hash,
64
+ payer: userDid,
65
+ block_height: receipt.blockNumber.toString(),
66
+ gas_used: receipt.gasUsed.toString(),
67
+ gas_price: receipt.gasPrice.toString(),
68
+ };
69
+ }
70
+
71
+ export function broadcastEvmTransaction(checkoutSessionId: string, status: string, claims: any[]) {
72
+ const claim = claims.find((x) => x.type === 'signature');
73
+ if (claim?.hash) {
74
+ broadcast('checkout.session.evm_transaction', {
75
+ id: checkoutSessionId,
76
+ txHash: claim.hash,
77
+ status,
78
+ });
79
+ }
80
+ }
@@ -3,23 +3,23 @@ import { sendToRelay } from '@blocklet/sdk/service/notification';
3
3
  import type { CheckoutSession, Invoice, PaymentIntent } from '../store/models';
4
4
  import { events } from './event';
5
5
 
6
- export function broadcast(channel: string, eventName: string, data: any) {
7
- sendToRelay(channel, eventName, data).catch((err: any) => {
8
- console.error(`Failed to broadcast info: ${channel}.${eventName}`, err);
6
+ export function broadcast(eventName: string, data: any) {
7
+ sendToRelay('events', eventName, data).catch((err: any) => {
8
+ console.error(`Failed to broadcast event: ${eventName}`, err);
9
9
  });
10
10
  }
11
11
 
12
12
  export function initEventBroadcast() {
13
13
  events.on('payment_intent.succeeded', (data: PaymentIntent) => {
14
- broadcast('events', 'payment_intent.succeeded', data);
14
+ broadcast('payment_intent.succeeded', data);
15
15
  });
16
16
  events.on('checkout.session.completed', (data: CheckoutSession) => {
17
- broadcast('events', 'checkout.session.completed', data);
17
+ broadcast('checkout.session.completed', data);
18
18
  });
19
19
  events.on('checkout.session.nft_minted', (data: CheckoutSession) => {
20
- broadcast('events', 'checkout.session.nft_minted', data);
20
+ broadcast('checkout.session.nft_minted', data);
21
21
  });
22
22
  events.on('invoice.paid', (data: Invoice) => {
23
- broadcast('events', 'invoice.paid', data);
23
+ broadcast('invoice.paid', data);
24
24
  });
25
25
  }
@@ -1107,6 +1107,7 @@ const schema = Joi.object<{
1107
1107
  customer_id?: string;
1108
1108
  customer_did?: string;
1109
1109
  payment_intent_id?: string;
1110
+ payment_link_id?: string;
1110
1111
  subscription_id?: string;
1111
1112
  livemode?: boolean;
1112
1113
  }>({
@@ -1118,6 +1119,7 @@ const schema = Joi.object<{
1118
1119
  customer_id: Joi.string().empty(''),
1119
1120
  customer_did: Joi.string().empty(''),
1120
1121
  payment_intent_id: Joi.string().empty(''),
1122
+ payment_link_id: Joi.string().empty(''),
1121
1123
  subscription_id: Joi.string().empty(''),
1122
1124
  livemode: Joi.boolean().empty(''),
1123
1125
  });
@@ -1141,6 +1143,9 @@ router.get('/', auth, async (req, res) => {
1141
1143
  if (query.payment_intent_id) {
1142
1144
  where.payment_intent_id = query.payment_intent_id;
1143
1145
  }
1146
+ if (query.payment_link_id) {
1147
+ where.payment_link_id = query.payment_link_id;
1148
+ }
1144
1149
  if (query.subscription_id) {
1145
1150
  where.subscription_id = query.subscription_id;
1146
1151
  }
@@ -1191,4 +1196,20 @@ router.get('/', auth, async (req, res) => {
1191
1196
  }
1192
1197
  });
1193
1198
 
1199
+ router.put('/:id', auth, async (req, res) => {
1200
+ const doc = await CheckoutSession.findByPk(req.params.id);
1201
+
1202
+ if (!doc) {
1203
+ return res.status(404).json({ error: 'CheckoutSession not found' });
1204
+ }
1205
+
1206
+ const raw = pick(req.body, ['metadata']);
1207
+ if (raw.metadata) {
1208
+ raw.metadata = formatMetadata(raw.metadata);
1209
+ }
1210
+
1211
+ await doc.update(raw);
1212
+ res.json(doc);
1213
+ });
1214
+
1194
1215
  export default router;
@@ -1,5 +1,4 @@
1
- import { executeEvmTransaction } from '../../integrations/ethereum/token';
2
- import { waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
1
+ import { executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
3
2
  import type { CallbackArgs } from '../../libs/auth';
4
3
  import { isDelegationSufficientForPayment } from '../../libs/payment';
5
4
  import { getFastCheckoutAmount } from '../../libs/session';
@@ -1,5 +1,4 @@
1
- import { executeEvmTransaction } from '../../integrations/ethereum/token';
2
- import { waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
1
+ import { executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
3
2
  import type { CallbackArgs } from '../../libs/auth';
4
3
  import { isDelegationSufficientForPayment } from '../../libs/payment';
5
4
  import { getFastCheckoutAmount } from '../../libs/session';
@@ -2,8 +2,8 @@ import type { Transaction, TransferV3Tx } from '@ocap/client';
2
2
  import { toBase58 } from '@ocap/util';
3
3
  import { fromAddress } from '@ocap/wallet';
4
4
 
5
- import { encodeTransferItx, executeEvmTransaction } from '../../integrations/ethereum/token';
6
- import { waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
5
+ import { encodeTransferItx } from '../../integrations/ethereum/token';
6
+ import { executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
7
7
  import { CallbackArgs, ethWallet } from '../../libs/auth';
8
8
  import logger from '../../libs/logger';
9
9
  import { getGasPayerExtra } from '../../libs/payment';
@@ -1,5 +1,4 @@
1
- import { executeEvmTransaction } from '../../integrations/ethereum/token';
2
- import { waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
1
+ import { broadcastEvmTransaction, executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
3
2
  import type { CallbackArgs } from '../../libs/auth';
4
3
  import dayjs from '../../libs/dayjs';
5
4
  import logger from '../../libs/logger';
@@ -182,10 +181,12 @@ export default {
182
181
 
183
182
  if (paymentMethod.type === 'ethereum') {
184
183
  await prepareTxExecution();
184
+ broadcastEvmTransaction(checkoutSessionId, 'pending', claims);
185
185
  const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
186
186
  waitForEvmTxConfirm(paymentMethod.getEvmClient(), +paymentDetails.block_height, paymentMethod.confirmation.block)
187
187
  .then(async () => {
188
188
  await afterTxExecution(paymentDetails);
189
+ broadcastEvmTransaction(checkoutSessionId, 'confirmed', claims);
189
190
  })
190
191
  .catch(console.error);
191
192
 
@@ -1,5 +1,4 @@
1
- import { executeEvmTransaction } from '../../integrations/ethereum/token';
2
- import { waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
1
+ import { broadcastEvmTransaction, executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
3
2
  import type { CallbackArgs } from '../../libs/auth';
4
3
  import dayjs from '../../libs/dayjs';
5
4
  import logger from '../../libs/logger';
@@ -165,6 +164,7 @@ export default {
165
164
  if (paymentMethod.type === 'ethereum') {
166
165
  await prepareTxExecution();
167
166
  const { invoice } = await ensureInvoiceForCheckout({ checkoutSession, customer, subscription });
167
+ broadcastEvmTransaction(checkoutSessionId, 'pending', claims);
168
168
 
169
169
  const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
170
170
  waitForEvmTxConfirm(
@@ -174,6 +174,7 @@ export default {
174
174
  )
175
175
  .then(async () => {
176
176
  await afterTxExecution(invoice!, paymentDetails);
177
+ broadcastEvmTransaction(checkoutSessionId, 'confirmed', claims);
177
178
  })
178
179
  .catch(console.error);
179
180
 
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.250
17
+ version: 1.13.251
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.250",
3
+ "version": "1.13.251",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -51,7 +51,7 @@
51
51
  "@arcblock/ux": "^2.9.77",
52
52
  "@arcblock/validator": "^1.18.116",
53
53
  "@blocklet/logger": "1.16.26",
54
- "@blocklet/payment-react": "1.13.250",
54
+ "@blocklet/payment-react": "1.13.251",
55
55
  "@blocklet/sdk": "1.16.26",
56
56
  "@blocklet/ui-react": "^2.9.77",
57
57
  "@blocklet/uploader": "^0.1.6",
@@ -116,7 +116,7 @@
116
116
  "devDependencies": {
117
117
  "@abtnode/types": "1.16.26",
118
118
  "@arcblock/eslint-config-ts": "^0.3.0",
119
- "@blocklet/payment-types": "1.13.250",
119
+ "@blocklet/payment-types": "1.13.251",
120
120
  "@types/cookie-parser": "^1.4.7",
121
121
  "@types/cors": "^2.8.17",
122
122
  "@types/dotenv-flow": "^3.3.3",
@@ -155,5 +155,5 @@
155
155
  "parser": "typescript"
156
156
  }
157
157
  },
158
- "gitHead": "a75495162255f5182866532b3b7f2373340774a5"
158
+ "gitHead": "46caad7baca15f2ba3876018589f78ba11179f59"
159
159
  }
@@ -25,8 +25,13 @@ export default function ProductForm(props: Props) {
25
25
  const images = useWatch({ control, name: 'images' });
26
26
 
27
27
  const onUploaded = (result: any) => {
28
- const tmp = new URL(result.url);
29
- setValue('images', [tmp.pathname]);
28
+ console.warn('onUploaded', result);
29
+ if (result.url) {
30
+ const tmp = new URL(result.url);
31
+ setValue('images', [tmp.pathname]);
32
+ } else {
33
+ setValue('images', []);
34
+ }
30
35
  };
31
36
 
32
37
  return (
@@ -38,7 +38,7 @@ export default function SubscriptionMetrics({ subscription }: Props) {
38
38
  divider
39
39
  />
40
40
  )}
41
- {upcoming && upcoming.amount !== '0' && (
41
+ {upcoming?.amount && upcoming.amount !== '0' && (
42
42
  <InfoMetric
43
43
  label={t('admin.subscription.nextInvoiceAmount')}
44
44
  value={`${formatBNStr(upcoming.amount, subscription.paymentCurrency.decimal)} ${
@@ -1,4 +1,4 @@
1
- import { UploadFileOutlined } from '@mui/icons-material';
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import { Box, Button, Typography } from '@mui/material';
3
3
  import { styled } from '@mui/system';
4
4
  import { lazy, useCallback, useEffect, useRef } from 'react';
@@ -14,12 +14,17 @@ type Props = {
14
14
  };
15
15
 
16
16
  export default function Uploader({ onUploaded, preview, maxFileSize, maxNumberOfFiles, allowedFileExts }: Props) {
17
+ const { t } = useLocaleContext();
17
18
  const uploaderRef = useRef<any>(null);
18
19
  const handleOpen = useCallback(() => {
19
20
  if (!uploaderRef.current) return;
20
21
  uploaderRef.current.open();
21
22
  }, []);
22
23
 
24
+ const handleRemove = () => {
25
+ onUploaded({ url: '' });
26
+ };
27
+
23
28
  useEffect(() => {
24
29
  if (uploaderRef.current) {
25
30
  const uploader = uploaderRef.current.getUploader();
@@ -33,17 +38,20 @@ export default function Uploader({ onUploaded, preview, maxFileSize, maxNumberOf
33
38
  display="flex"
34
39
  alignItems={preview ? 'flex-end' : 'center'}
35
40
  justifyContent="center"
36
- onClick={handleOpen}
37
41
  style={{
38
42
  backgroundImage: preview ? `url(${preview})` : 'none',
39
43
  backgroundRepeat: 'no-repeat',
40
44
  backgroundSize: 'contain',
41
45
  backgroundPosition: 'center',
42
46
  }}>
43
- <Button fullWidth variant={preview ? 'contained' : 'text'} color="inherit" size="small">
44
- <UploadFileOutlined sx={{ mr: 1 }} fontSize="small" />
45
- <Typography>{preview ? 'Change' : 'Upload'}</Typography>
47
+ <Button variant={preview ? 'contained' : 'text'} color="inherit" size="small" onClick={handleOpen}>
48
+ <Typography>{t(`common.${preview ? 'change' : 'upload'}`)}</Typography>
46
49
  </Button>
50
+ {preview && (
51
+ <Button variant="contained" color="error" size="small" onClick={handleRemove}>
52
+ <Typography>{t('common.remove')}</Typography>
53
+ </Button>
54
+ )}
47
55
  </Div>
48
56
  <UploaderComponent
49
57
  // @ts-ignore
@@ -1,7 +1,16 @@
1
1
  /* eslint-disable jsx-a11y/anchor-is-valid */
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
- import { Status, TxGas, TxLink, api, formatError, formatTime, getInvoiceStatusColor } from '@blocklet/payment-react';
4
+ import {
5
+ Status,
6
+ TxGas,
7
+ TxLink,
8
+ api,
9
+ formatError,
10
+ formatTime,
11
+ getInvoiceStatusColor,
12
+ usePaymentContext,
13
+ } from '@blocklet/payment-react';
5
14
  import type { TInvoiceExpanded } from '@blocklet/payment-types';
6
15
  import { ArrowBackOutlined } from '@mui/icons-material';
7
16
  import { Alert, Box, Button, CircularProgress, Stack, Typography } from '@mui/material';
@@ -16,7 +25,6 @@ import InfoRow from '../../../components/info-row';
16
25
  import { Download } from '../../../components/invoice-pdf/pdf';
17
26
  import InvoiceTable from '../../../components/invoice/table';
18
27
  import SectionHeader from '../../../components/section/header';
19
- import { useSessionContext } from '../../../contexts/session';
20
28
  import { goBackOrFallback } from '../../../libs/util';
21
29
  import CustomerRefundList from '../refund/list';
22
30
 
@@ -27,7 +35,7 @@ const fetchData = (id: string): Promise<TInvoiceExpanded> => {
27
35
  export default function CustomerInvoiceDetail() {
28
36
  const { t } = useLocaleContext();
29
37
  const [searchParams] = useSearchParams();
30
- const { connectApi } = useSessionContext();
38
+ const { connect } = usePaymentContext();
31
39
  const params = useParams<{ id: string }>();
32
40
  const [state, setState] = useSetState({
33
41
  downloading: false,
@@ -39,8 +47,9 @@ export default function CustomerInvoiceDetail() {
39
47
 
40
48
  const onPay = () => {
41
49
  setState({ paying: true });
42
- connectApi.open({
50
+ connect.open({
43
51
  action: 'collect',
52
+ saveConnect: false,
44
53
  messages: {
45
54
  scan: '',
46
55
  title: t(`payment.customer.invoice.${action || 'pay'}`),
@@ -50,11 +59,11 @@ export default function CustomerInvoiceDetail() {
50
59
  } as any,
51
60
  extraParams: { invoiceId: params.id, action },
52
61
  onSuccess: async () => {
53
- connectApi.close();
62
+ connect.close();
54
63
  await runAsync();
55
64
  },
56
65
  onClose: () => {
57
- connectApi.close();
66
+ connect.close();
58
67
  setState({ paying: false });
59
68
  },
60
69
  onError: (err: any) => {
@@ -66,7 +75,7 @@ export default function CustomerInvoiceDetail() {
66
75
 
67
76
  const closePay = () => {
68
77
  setState({ paying: true });
69
- connectApi.close();
78
+ connect.close();
70
79
  };
71
80
 
72
81
  useEffect(() => {
@@ -1,7 +1,15 @@
1
1
  /* eslint-disable react/no-unstable-nested-components */
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
- import { PricingTable, api, formatBNStr, formatError, formatPrice, formatTime } from '@blocklet/payment-react';
4
+ import {
5
+ PricingTable,
6
+ api,
7
+ formatBNStr,
8
+ formatError,
9
+ formatPrice,
10
+ formatTime,
11
+ usePaymentContext,
12
+ } from '@blocklet/payment-react';
5
13
  import type { TLineItemExpanded, TPricingTableExpanded, TSubscriptionExpanded } from '@blocklet/payment-types';
6
14
  import { ArrowBackOutlined } from '@mui/icons-material';
7
15
  import { LoadingButton } from '@mui/lab';
@@ -12,7 +20,6 @@ import { useNavigate, useParams } from 'react-router-dom';
12
20
  import InfoCard from '../../../components/info-card';
13
21
  import SectionHeader from '../../../components/section/header';
14
22
  import SubscriptionDescription from '../../../components/subscription/description';
15
- import { useSessionContext } from '../../../contexts/session';
16
23
  import { goBackOrFallback } from '../../../libs/util';
17
24
 
18
25
  const fetchData = async (
@@ -45,7 +52,7 @@ export default function CustomerSubscriptionChangePlan() {
45
52
  const navigate = useNavigate();
46
53
  const { id } = useParams() as { id: string };
47
54
  const { t, locale } = useLocaleContext();
48
- const { connectApi } = useSessionContext();
55
+ const { connect } = usePaymentContext();
49
56
 
50
57
  const { loading, error, data } = useRequest(() => fetchData(id));
51
58
  const [state, setState] = useSetState({
@@ -142,7 +149,7 @@ export default function CustomerSubscriptionChangePlan() {
142
149
  setState({ paying: true });
143
150
  try {
144
151
  setState({ paying: true });
145
- connectApi.open({
152
+ connect.open({
146
153
  action: result.data.connectAction,
147
154
  saveConnect: false,
148
155
  messages: {
@@ -156,12 +163,12 @@ export default function CustomerSubscriptionChangePlan() {
156
163
  onSuccess: () => {
157
164
  setState({ paid: true, paying: false });
158
165
  setTimeout(() => {
159
- connectApi.close();
166
+ connect.close();
160
167
  handleBack();
161
168
  }, 2000);
162
169
  },
163
170
  onClose: () => {
164
- connectApi.close();
171
+ connect.close();
165
172
  setState({ paying: false });
166
173
  },
167
174
  onError: (err: any) => {