payment-kit 1.18.5 → 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.
@@ -26,7 +26,7 @@ export const handlers = new WalletHandler({
26
26
  export const blocklet = new AuthService();
27
27
 
28
28
  export type CallbackArgs = {
29
- request: Request;
29
+ request: Request & { context: Record<string, any> };
30
30
  userDid: string;
31
31
  userPk: string;
32
32
  didwallet: {
@@ -12,6 +12,7 @@ import {
12
12
  } from './shared';
13
13
  import { ensureStakeInvoice } from '../../libs/invoice';
14
14
  import { EVM_CHAIN_TYPES } from '../../libs/constants';
15
+ import logger from '../../libs/logger';
15
16
 
16
17
  export default {
17
18
  action: 'change-payment',
@@ -26,17 +27,15 @@ export default {
26
27
  const { subscriptionId } = extraParams;
27
28
  const { subscription, paymentMethod, paymentCurrency } = await ensureChangePaymentContext(subscriptionId);
28
29
 
29
- const claims: { [type: string]: [string, object] } = {};
30
-
30
+ const claimsList: any[] = [];
31
31
  // @ts-ignore
32
32
  const items = subscription!.items as TLineItemExpanded[];
33
33
  const trialing = true;
34
34
  const billingThreshold = Number(subscription.billing_thresholds?.amount_gte || 0);
35
35
 
36
36
  if (paymentMethod.type === 'arcblock') {
37
- claims.delegation = [
38
- 'signature',
39
- await getDelegationTxClaim({
37
+ claimsList.push({
38
+ signature: await getDelegationTxClaim({
40
39
  mode: 'setup',
41
40
  userDid,
42
41
  userPk,
@@ -48,12 +47,10 @@ export default {
48
47
  billingThreshold,
49
48
  items,
50
49
  }),
51
- ];
50
+ });
52
51
 
53
- // we always need to stake for the subscription
54
- claims.staking = [
55
- 'prepareTx',
56
- await getStakeTxClaim({
52
+ claimsList.push({
53
+ prepareTx: await getStakeTxClaim({
57
54
  userDid,
58
55
  userPk,
59
56
  paymentCurrency,
@@ -61,9 +58,9 @@ export default {
61
58
  items,
62
59
  subscription,
63
60
  }),
64
- ];
61
+ });
65
62
 
66
- return claims;
63
+ return claimsList;
67
64
  }
68
65
 
69
66
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
@@ -71,9 +68,8 @@ export default {
71
68
  throw new Error(`Payment currency ${paymentMethod.type}:${paymentCurrency.id} does not support subscription`);
72
69
  }
73
70
 
74
- claims.delegation = [
75
- 'signature',
76
- await getDelegationTxClaim({
71
+ claimsList.push({
72
+ signature: await getDelegationTxClaim({
77
73
  mode: 'subscription',
78
74
  userDid,
79
75
  userPk,
@@ -85,19 +81,48 @@ export default {
85
81
  billingThreshold,
86
82
  items,
87
83
  }),
88
- ];
84
+ });
89
85
 
90
- return claims;
86
+ return claimsList;
91
87
  }
92
88
 
93
- throw new Error(`ChangeMethod: Payment method ${paymentMethod.type} not supported`);
89
+ throw new Error(`ChangePayment: Payment method ${paymentMethod.type} not supported`);
94
90
  },
95
91
 
96
- onAuth: async ({ request, userDid, userPk, claims, extraParams }: CallbackArgs) => {
92
+ onAuth: async ({ request, userDid, userPk, claims, extraParams, updateSession, step }: CallbackArgs) => {
97
93
  const { subscriptionId } = extraParams;
98
- const { subscription, setupIntent, paymentCurrency, paymentMethod, customer } =
94
+ const { setupIntent, subscription, paymentMethod, paymentCurrency, customer } =
99
95
  await ensureChangePaymentContext(subscriptionId);
100
96
 
97
+ const result = request?.context?.store?.result || [];
98
+ result.push({
99
+ step,
100
+ claim: claims?.[0],
101
+ });
102
+
103
+ // 判断是否为最后一步
104
+ const staking = result.find((x: any) => x.claim?.type === 'prepareTx' && x.claim?.meta?.purpose === 'staking');
105
+ const isFinalStep = (paymentMethod.type === 'arcblock' && staking) || paymentMethod.type !== 'arcblock';
106
+
107
+ if (!isFinalStep) {
108
+ await updateSession({
109
+ result,
110
+ });
111
+ return { result };
112
+ }
113
+
114
+ // 清理 session
115
+ try {
116
+ await updateSession({
117
+ result: [],
118
+ });
119
+ } catch (error) {
120
+ logger.error('updateSession', {
121
+ error,
122
+ });
123
+ }
124
+ const claimsList = result.map((x: any) => x.claim);
125
+
101
126
  const prepareTxExecution = async () => {
102
127
  await subscription?.update({
103
128
  payment_settings: {
@@ -141,7 +166,7 @@ export default {
141
166
  const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
142
167
  userDid,
143
168
  userPk,
144
- claims,
169
+ claimsList,
145
170
  paymentMethod,
146
171
  request,
147
172
  subscription?.id,
@@ -172,7 +197,7 @@ export default {
172
197
 
173
198
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
174
199
  await prepareTxExecution();
175
- const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
200
+ const paymentDetails = await executeEvmTransaction('approve', userDid, claimsList, paymentMethod);
176
201
  waitForEvmTxConfirm(paymentMethod.getEvmClient(), +paymentDetails.block_height, paymentMethod.confirmation.block)
177
202
  .then(async () => {
178
203
  await afterTxExecution(paymentDetails);
@@ -16,6 +16,7 @@ import {
16
16
  } from './shared';
17
17
  import { ensureStakeInvoice } from '../../libs/invoice';
18
18
  import { EVM_CHAIN_TYPES } from '../../libs/constants';
19
+ import logger from '../../libs/logger';
19
20
 
20
21
  export default {
21
22
  action: 'change-plan',
@@ -30,7 +31,7 @@ export default {
30
31
  const { subscriptionId } = extraParams;
31
32
  const { paymentMethod, paymentCurrency, subscription, customer } = await ensureSubscription(subscriptionId);
32
33
 
33
- const claims: { [type: string]: [string, object] } = {};
34
+ const claimsList: any[] = [];
34
35
  // @ts-ignore
35
36
  const items = subscription!.items as TLineItemExpanded[];
36
37
  const trialing = false;
@@ -45,11 +46,9 @@ export default {
45
46
  amount: fastCheckoutAmount,
46
47
  });
47
48
 
48
- // if we can complete purchase without any wallet interaction
49
49
  if (delegation.sufficient === false) {
50
- claims.delegation = [
51
- 'signature',
52
- await getDelegationTxClaim({
50
+ claimsList.push({
51
+ signature: await getDelegationTxClaim({
53
52
  mode: 'subscription',
54
53
  userDid,
55
54
  userPk,
@@ -61,13 +60,11 @@ export default {
61
60
  billingThreshold,
62
61
  items,
63
62
  }),
64
- ];
63
+ });
65
64
  }
66
65
 
67
- // we always need to stake for the subscription
68
- claims.staking = [
69
- 'prepareTx',
70
- await getStakeTxClaim({
66
+ claimsList.push({
67
+ prepareTx: await getStakeTxClaim({
71
68
  userDid,
72
69
  userPk,
73
70
  paymentCurrency,
@@ -75,9 +72,9 @@ export default {
75
72
  items,
76
73
  subscription: subscription!,
77
74
  }),
78
- ];
75
+ });
79
76
 
80
- return claims;
77
+ return claimsList;
81
78
  }
82
79
 
83
80
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
@@ -85,9 +82,8 @@ export default {
85
82
  throw new Error(`Payment currency ${paymentMethod.type}:${paymentCurrency.id} does not support subscription`);
86
83
  }
87
84
 
88
- claims.delegation = [
89
- 'signature',
90
- await getDelegationTxClaim({
85
+ claimsList.push({
86
+ signature: await getDelegationTxClaim({
91
87
  mode: 'subscription',
92
88
  userDid,
93
89
  userPk,
@@ -99,19 +95,48 @@ export default {
99
95
  billingThreshold,
100
96
  items,
101
97
  }),
102
- ];
98
+ });
103
99
 
104
- return claims;
100
+ return claimsList;
105
101
  }
106
102
 
107
103
  throw new Error(`ChangePlan: Payment method ${paymentMethod.type} not supported`);
108
104
  },
109
105
 
110
- onAuth: async ({ request, userDid, userPk, claims, extraParams }: CallbackArgs) => {
106
+ onAuth: async ({ request, userDid, userPk, claims, extraParams, updateSession, step }: CallbackArgs) => {
111
107
  const { subscriptionId } = extraParams;
112
108
  const { invoice, paymentMethod, subscription, paymentCurrency, customer } =
113
109
  await ensureSubscription(subscriptionId);
114
110
 
111
+ const result = request?.context?.store?.result || [];
112
+ result.push({
113
+ step,
114
+ claim: claims?.[0],
115
+ });
116
+
117
+ // 判断是否为最后一步
118
+ const staking = result.find((x: any) => x.claim?.type === 'prepareTx' && x.claim?.meta?.purpose === 'staking');
119
+ const isFinalStep = (paymentMethod.type === 'arcblock' && staking) || paymentMethod.type !== 'arcblock';
120
+
121
+ if (!isFinalStep) {
122
+ await updateSession({
123
+ result,
124
+ });
125
+ return { result };
126
+ }
127
+
128
+ // 清理 session
129
+ try {
130
+ await updateSession({
131
+ result: [],
132
+ });
133
+ } catch (error) {
134
+ logger.error('updateSession', {
135
+ error,
136
+ });
137
+ }
138
+ const claimsList = result.map((x: any) => x.claim);
139
+
115
140
  const prepareTxExecution = async () => {
116
141
  await subscription?.update({
117
142
  payment_settings: {
@@ -148,7 +173,7 @@ export default {
148
173
  const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
149
174
  userDid,
150
175
  userPk,
151
- claims,
176
+ claimsList,
152
177
  paymentMethod,
153
178
  request,
154
179
  subscription?.id,
@@ -181,7 +206,7 @@ export default {
181
206
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
182
207
  await prepareTxExecution();
183
208
 
184
- const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
209
+ const paymentDetails = await executeEvmTransaction('approve', userDid, claimsList, paymentMethod);
185
210
  waitForEvmTxConfirm(paymentMethod.getEvmClient(), +paymentDetails.block_height, paymentMethod.confirmation.block)
186
211
  .then(async () => {
187
212
  await afterTxExecution(paymentDetails);
@@ -27,10 +27,10 @@ export default {
27
27
 
28
28
  onConnect: async (args: CallbackArgs) => {
29
29
  const { userDid, userPk, extraParams } = args;
30
- const { checkoutSessionId, connectedDid } = extraParams;
30
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
31
31
  const { paymentIntent, paymentCurrency, paymentMethod } = await ensurePaymentIntent(
32
32
  checkoutSessionId,
33
- connectedDid || userDid
33
+ connectedDid || sessionUserDid || userDid
34
34
  );
35
35
  if (!paymentIntent) {
36
36
  throw new Error('Payment intent not found');
@@ -87,10 +87,10 @@ export default {
87
87
 
88
88
  onAuth: async (args: CallbackArgs) => {
89
89
  const { request, userDid, claims, extraParams } = args;
90
- const { checkoutSessionId, connectedDid } = extraParams;
90
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
91
91
  const { checkoutSession, customer, paymentIntent, paymentMethod } = await ensurePaymentIntent(
92
92
  checkoutSessionId,
93
- connectedDid || userDid
93
+ connectedDid || sessionUserDid || userDid
94
94
  );
95
95
  if (!paymentIntent) {
96
96
  throw new Error('Payment intent not found');
@@ -30,19 +30,18 @@ export default {
30
30
  },
31
31
  onConnect: async (args: CallbackArgs) => {
32
32
  const { userDid, userPk, extraParams } = args;
33
- const { checkoutSessionId, connectedDid } = extraParams;
33
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
34
34
  const { paymentMethod, paymentCurrency, checkoutSession, subscription, customer } = await ensureSetupIntent(
35
35
  checkoutSessionId,
36
- connectedDid || userDid
36
+ connectedDid || sessionUserDid || userDid
37
37
  );
38
38
  if (!subscription) {
39
39
  throw new Error('Subscription for checkoutSession not found');
40
40
  }
41
41
 
42
- const claims: { [type: string]: [string, object] } = {};
42
+ const claimsList: any[] = [];
43
43
  const now = dayjs().unix();
44
44
  const items = checkoutSession.line_items as TLineItemExpanded[];
45
-
46
45
  const { trialEnd, trialInDays } = getSubscriptionTrialSetup(
47
46
  checkoutSession.subscription_data as any,
48
47
  paymentCurrency.id
@@ -62,9 +61,8 @@ export default {
62
61
  });
63
62
  // if we can complete purchase without any wallet interaction
64
63
  if (delegation.sufficient === false) {
65
- claims.delegation = [
66
- 'signature',
67
- await getDelegationTxClaim({
64
+ claimsList.push({
65
+ signature: await getDelegationTxClaim({
68
66
  mode: checkoutSession.mode,
69
67
  userDid,
70
68
  userPk,
@@ -76,13 +74,12 @@ export default {
76
74
  billingThreshold: Math.max(minStakeAmount, billingThreshold),
77
75
  items,
78
76
  }),
79
- ];
77
+ });
80
78
  }
81
79
 
82
80
  // we always need to stake for the subscription
83
- claims.staking = [
84
- 'prepareTx',
85
- await getStakeTxClaim({
81
+ claimsList.push({
82
+ prepareTx: await getStakeTxClaim({
86
83
  userDid,
87
84
  userPk,
88
85
  paymentCurrency,
@@ -90,9 +87,9 @@ export default {
90
87
  items,
91
88
  subscription,
92
89
  }),
93
- ];
90
+ });
94
91
 
95
- return claims;
92
+ return claimsList;
96
93
  }
97
94
 
98
95
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
@@ -100,9 +97,8 @@ export default {
100
97
  throw new Error(`Payment currency ${paymentMethod.type}:${paymentCurrency.id} does not support setup`);
101
98
  }
102
99
 
103
- claims.delegation = [
104
- 'signature',
105
- await getDelegationTxClaim({
100
+ claimsList.push({
101
+ signature: await getDelegationTxClaim({
106
102
  mode: checkoutSession.mode,
107
103
  userDid,
108
104
  userPk,
@@ -114,23 +110,52 @@ export default {
114
110
  billingThreshold,
115
111
  items,
116
112
  }),
117
- ];
113
+ });
118
114
 
119
- return claims;
115
+ return claimsList;
120
116
  }
121
117
 
122
118
  throw new Error(`Payment method ${paymentMethod.type} not supported`);
123
119
  },
124
120
  onAuth: async (args: CallbackArgs) => {
125
- const { request, userDid, userPk, claims, extraParams } = args;
126
- const { checkoutSessionId, connectedDid } = extraParams;
121
+ const { request, userDid, userPk, claims, extraParams, updateSession, step } = args;
122
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
127
123
  const { setupIntent, checkoutSession, paymentMethod, subscription, invoice, paymentCurrency, customer } =
128
- await ensureSetupIntent(checkoutSessionId, connectedDid || userDid);
124
+ await ensureSetupIntent(checkoutSessionId, connectedDid || sessionUserDid || userDid);
129
125
 
130
126
  if (!subscription) {
131
127
  throw new Error('Subscription for checkoutSession not found');
132
128
  }
133
129
 
130
+ const result = request?.context?.store?.result || [];
131
+ result.push({
132
+ step,
133
+ claim: claims?.[0],
134
+ });
135
+
136
+ // 判断是否为最后一步
137
+ const staking = result.find((x: any) => x.claim?.type === 'prepareTx' && x.claim?.meta?.purpose === 'staking');
138
+ const isFinalStep = (paymentMethod.type === 'arcblock' && staking) || paymentMethod.type !== 'arcblock';
139
+
140
+ if (!isFinalStep) {
141
+ await updateSession({
142
+ result,
143
+ });
144
+ return { result };
145
+ }
146
+
147
+ // 清理 session
148
+ try {
149
+ await updateSession({
150
+ result: [],
151
+ });
152
+ } catch (error) {
153
+ logger.error('updateSession', {
154
+ error,
155
+ });
156
+ }
157
+ const claimsList = result.map((x: any) => x.claim);
158
+
134
159
  const paymentSettings = {
135
160
  payment_method_types: [paymentMethod.type],
136
161
  payment_method_options: {
@@ -177,7 +202,7 @@ export default {
177
202
  const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
178
203
  userDid,
179
204
  userPk,
180
- claims,
205
+ claimsList,
181
206
  paymentMethod,
182
207
  request,
183
208
  subscription?.id,
@@ -216,12 +241,12 @@ export default {
216
241
 
217
242
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
218
243
  await prepareTxExecution();
219
- broadcastEvmTransaction(checkoutSessionId, 'pending', claims);
220
- const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
244
+ broadcastEvmTransaction(checkoutSessionId, 'pending', claimsList);
245
+ const paymentDetails = await executeEvmTransaction('approve', userDid, claimsList, paymentMethod);
221
246
  waitForEvmTxConfirm(paymentMethod.getEvmClient(), +paymentDetails.block_height, paymentMethod.confirmation.block)
222
247
  .then(async () => {
223
248
  await afterTxExecution(paymentDetails);
224
- broadcastEvmTransaction(checkoutSessionId, 'confirmed', claims);
249
+ broadcastEvmTransaction(checkoutSessionId, 'confirmed', claimsList);
225
250
  })
226
251
  .catch(console.error);
227
252
 
@@ -31,16 +31,15 @@ export default {
31
31
  },
32
32
  onConnect: async (args: CallbackArgs) => {
33
33
  const { userDid, userPk, extraParams } = args;
34
- const { checkoutSessionId, connectedDid } = extraParams;
34
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
35
35
  const { checkoutSession, paymentMethod, paymentCurrency, subscription, customer } = await ensurePaymentIntent(
36
36
  checkoutSessionId,
37
- connectedDid || userDid
37
+ connectedDid || sessionUserDid || userDid
38
38
  );
39
39
  if (!subscription) {
40
40
  throw new Error('Subscription for checkoutSession not found');
41
41
  }
42
42
 
43
- const claims: { [type: string]: [string, object] } = {};
44
43
  const now = dayjs().unix();
45
44
  const items = checkoutSession.line_items as TLineItemExpanded[];
46
45
  const { trialEnd, trialInDays } = getSubscriptionTrialSetup(
@@ -51,7 +50,7 @@ export default {
51
50
  const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
52
51
  const minStakeAmount = Number(checkoutSession.subscription_data?.min_stake_amount || 0);
53
52
  const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, trialing);
54
-
53
+ const claimsList: any[] = [];
55
54
  if (paymentMethod.type === 'arcblock') {
56
55
  const delegation = await isDelegationSufficientForPayment({
57
56
  paymentMethod,
@@ -62,9 +61,8 @@ export default {
62
61
 
63
62
  // if we can complete purchase without any wallet interaction
64
63
  if (delegation.sufficient === false) {
65
- claims.delegation = [
66
- 'signature',
67
- await getDelegationTxClaim({
64
+ claimsList.push({
65
+ signature: await getDelegationTxClaim({
68
66
  mode: checkoutSession.mode,
69
67
  userDid,
70
68
  userPk,
@@ -76,13 +74,12 @@ export default {
76
74
  billingThreshold: Math.max(minStakeAmount, billingThreshold),
77
75
  items,
78
76
  }),
79
- ];
77
+ });
80
78
  }
81
79
 
82
80
  // we always need to stake for the subscription
83
- claims.staking = [
84
- 'prepareTx',
85
- await getStakeTxClaim({
81
+ claimsList.push({
82
+ prepareTx: await getStakeTxClaim({
86
83
  userDid,
87
84
  userPk,
88
85
  paymentCurrency,
@@ -90,9 +87,9 @@ export default {
90
87
  items,
91
88
  subscription,
92
89
  }),
93
- ];
90
+ });
94
91
 
95
- return claims;
92
+ return claimsList;
96
93
  }
97
94
 
98
95
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
@@ -100,9 +97,8 @@ export default {
100
97
  throw new Error(`Payment currency ${paymentMethod.type}:${paymentCurrency.id} does not support subscription`);
101
98
  }
102
99
 
103
- claims.delegation = [
104
- 'signature',
105
- await getDelegationTxClaim({
100
+ claimsList.push({
101
+ signature: await getDelegationTxClaim({
106
102
  mode: checkoutSession.mode,
107
103
  userDid,
108
104
  userPk,
@@ -114,20 +110,43 @@ export default {
114
110
  billingThreshold,
115
111
  items,
116
112
  }),
117
- ];
113
+ });
118
114
 
119
- return claims;
115
+ return claimsList;
120
116
  }
121
117
 
122
118
  throw new Error(`subscription: Payment method ${paymentMethod.type} not supported`);
123
119
  },
124
120
  onAuth: async (args: CallbackArgs) => {
125
- const { request, userDid, userPk, claims, extraParams } = args;
126
- const { checkoutSessionId, connectedDid } = extraParams;
121
+ const { request, userDid, userPk, claims, extraParams, updateSession, step } = args;
122
+ const { checkoutSessionId, connectedDid, sessionUserDid } = extraParams;
127
123
  const { checkoutSession, customer, paymentMethod, subscription, paymentCurrency } = await ensurePaymentIntent(
128
124
  checkoutSessionId,
129
- connectedDid || userDid
125
+ connectedDid || sessionUserDid || userDid
130
126
  );
127
+ const result = request?.context?.store?.result || [];
128
+ result.push({
129
+ step,
130
+ claim: claims?.[0],
131
+ });
132
+ const staking = result.find((x: any) => x.claim?.type === 'prepareTx' && x.claim?.meta?.purpose === 'staking');
133
+ const isFinalStep = (paymentMethod.type === 'arcblock' && staking) || paymentMethod.type !== 'arcblock';
134
+ if (!isFinalStep) {
135
+ await updateSession({
136
+ result,
137
+ });
138
+ return { result };
139
+ }
140
+ try {
141
+ await updateSession({
142
+ result: [],
143
+ });
144
+ } catch (error) {
145
+ logger.error('updateSession', {
146
+ error,
147
+ });
148
+ }
149
+ const claimsList = result.map((x: any) => x.claim);
131
150
 
132
151
  if (!subscription) {
133
152
  throw new Error('Subscription for checkoutSession not found');
@@ -164,7 +183,7 @@ export default {
164
183
  const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
165
184
  userDid,
166
185
  userPk,
167
- claims,
186
+ claimsList,
168
187
  paymentMethod,
169
188
  request,
170
189
  subscription?.id,
@@ -198,9 +217,9 @@ export default {
198
217
  if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
199
218
  await prepareTxExecution();
200
219
  const { invoice } = await ensureInvoiceForCheckout({ checkoutSession, customer, subscription });
201
- broadcastEvmTransaction(checkoutSessionId, 'pending', claims);
220
+ broadcastEvmTransaction(checkoutSessionId, 'pending', claimsList);
202
221
 
203
- const paymentDetails = await executeEvmTransaction('approve', userDid, claims, paymentMethod);
222
+ const paymentDetails = await executeEvmTransaction('approve', userDid, claimsList, paymentMethod);
204
223
  waitForEvmTxConfirm(
205
224
  paymentMethod.getEvmClient(),
206
225
  Number(paymentDetails.block_height),
@@ -208,7 +227,7 @@ export default {
208
227
  )
209
228
  .then(async () => {
210
229
  await afterTxExecution(invoice!, paymentDetails);
211
- broadcastEvmTransaction(checkoutSessionId, 'confirmed', claims);
230
+ broadcastEvmTransaction(checkoutSessionId, 'confirmed', claimsList);
212
231
  })
213
232
  .catch(console.error);
214
233
 
@@ -146,7 +146,7 @@ export async function createPaymentLink(payload: any) {
146
146
  }
147
147
 
148
148
  const PaymentLinkCreateSchema = Joi.object({
149
- name: Joi.string().required(),
149
+ name: Joi.string().optional(),
150
150
  line_items: Joi.array()
151
151
  .items(
152
152
  Joi.object({
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.18.5
17
+ version: 1.18.7
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.18.5",
3
+ "version": "1.18.7",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -46,18 +46,18 @@
46
46
  "@abtnode/cron": "^1.16.38",
47
47
  "@arcblock/did": "^1.19.9",
48
48
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
49
- "@arcblock/did-connect": "^2.11.34",
49
+ "@arcblock/did-connect": "^2.11.40",
50
50
  "@arcblock/did-util": "^1.19.9",
51
51
  "@arcblock/jwt": "^1.19.9",
52
- "@arcblock/ux": "^2.11.34",
52
+ "@arcblock/ux": "^2.11.40",
53
53
  "@arcblock/validator": "^1.19.9",
54
54
  "@blocklet/js-sdk": "^1.16.38",
55
55
  "@blocklet/logger": "^1.16.38",
56
- "@blocklet/payment-react": "1.18.5",
56
+ "@blocklet/payment-react": "1.18.7",
57
57
  "@blocklet/sdk": "^1.16.38",
58
- "@blocklet/ui-react": "^2.11.34",
59
- "@blocklet/uploader": "^0.1.66",
60
- "@blocklet/xss": "^0.1.22",
58
+ "@blocklet/ui-react": "^2.11.40",
59
+ "@blocklet/uploader": "^0.1.70",
60
+ "@blocklet/xss": "^0.1.25",
61
61
  "@mui/icons-material": "^5.16.6",
62
62
  "@mui/lab": "^5.0.0-alpha.173",
63
63
  "@mui/material": "^5.16.6",
@@ -121,7 +121,7 @@
121
121
  "devDependencies": {
122
122
  "@abtnode/types": "^1.16.38",
123
123
  "@arcblock/eslint-config-ts": "^0.3.3",
124
- "@blocklet/payment-types": "1.18.5",
124
+ "@blocklet/payment-types": "1.18.7",
125
125
  "@types/cookie-parser": "^1.4.7",
126
126
  "@types/cors": "^2.8.17",
127
127
  "@types/debug": "^4.1.12",
@@ -167,5 +167,5 @@
167
167
  "parser": "typescript"
168
168
  }
169
169
  },
170
- "gitHead": "83cca0f1ac5814964c8bedd04f34a3a2a986f554"
170
+ "gitHead": "b75168ab5e2cd939571e6e26543374c8d0dab963"
171
171
  }
@@ -219,6 +219,7 @@ export default function SubscriptionList({ customer_id, features, status }: List
219
219
  q: {
220
220
  'like-metadata': text,
221
221
  'like-description': text,
222
+ 'like-id': text,
222
223
  },
223
224
  pageSize: 100,
224
225
  page: 1,
@@ -140,7 +140,7 @@ export function SubscriptionActionsInner({
140
140
  const { t, locale } = useLocaleContext();
141
141
  const { reset, getValues } = useFormContext();
142
142
  const navigate = useNavigate();
143
- const { connect } = usePaymentContext();
143
+ const { connect, session } = usePaymentContext();
144
144
  const [subs, setSubscription] = useState(subscription);
145
145
  const { checkUnpaidInvoices } = useUnpaidInvoicesCheckForSubscription(subscription.id, true);
146
146
  const action = getSubscriptionAction(subs, actionProps ?? {});
@@ -239,7 +239,7 @@ export function SubscriptionActionsInner({
239
239
  saveConnect: false,
240
240
  action: 'delegation',
241
241
  prefix: joinURL(getPrefix(), '/api/did'),
242
- extraParams: { subscriptionId: subscription.id },
242
+ extraParams: { subscriptionId: subscription.id, sessionUserDid: session.user.did },
243
243
  onSuccess: () => {
244
244
  connect.close();
245
245
  Toast.success(t('customer.delegation.success'));
@@ -286,7 +286,7 @@ export function SubscriptionActionsInner({
286
286
  saveConnect: false,
287
287
  action: 'overdraft-protection',
288
288
  prefix: joinURL(getPrefix(), '/api/did'),
289
- extraParams: { subscriptionId: subscription.id, amount },
289
+ extraParams: { subscriptionId: subscription.id, amount, sessionUserDid: session.user.did },
290
290
  onSuccess: () => {
291
291
  connect.close();
292
292
  Toast.success(t('customer.overdraftProtection.settingSuccess'));
@@ -62,6 +62,7 @@ export default function Uploader({
62
62
  }}
63
63
  coreProps={{
64
64
  restrictions: {
65
+ // @ts-ignore
65
66
  allowedFileExts,
66
67
  maxFileSize,
67
68
  maxNumberOfFiles,
package/src/libs/util.ts CHANGED
@@ -3,12 +3,14 @@
3
3
  /* eslint-disable @typescript-eslint/indent */
4
4
  import { formatCheckoutHeadlines, formatPrice, getPrefix, getPriceCurrencyOptions } from '@blocklet/payment-react';
5
5
  import type {
6
+ ChainType,
6
7
  LineItem,
7
8
  PriceRecurring,
8
9
  TInvoiceExpanded,
9
10
  TLineItemExpanded,
10
11
  TPaymentCurrency,
11
12
  TPaymentLinkExpanded,
13
+ TPaymentMethod,
12
14
  TPaymentMethodExpanded,
13
15
  TPrice,
14
16
  TProductExpanded,
@@ -364,3 +366,14 @@ export function getAppInfo(address: string): { name: string; avatar: string; typ
364
366
  }
365
367
  return null;
366
368
  }
369
+
370
+ export function getTokenBalanceLink(method: TPaymentMethod, address: string) {
371
+ const explorerHost = (method?.settings?.[method?.type as ChainType] as any)?.explorer_host || '';
372
+ if (method.type === 'arcblock' && address) {
373
+ return joinURL(explorerHost, 'accounts', address, 'tokens');
374
+ }
375
+ if (['ethereum', 'base'].includes(method.type) && address) {
376
+ return joinURL(explorerHost, 'address', address);
377
+ }
378
+ return '';
379
+ }
@@ -28,14 +28,14 @@ import {
28
28
  } from '@blocklet/payment-react';
29
29
  import { joinURL } from 'ufo';
30
30
  import type { TSubscriptionExpanded } from '@blocklet/payment-types';
31
- import { ArrowBackOutlined } from '@mui/icons-material';
31
+ import { ArrowBackOutlined, ArrowForwardOutlined } from '@mui/icons-material';
32
32
  import { BN, fromUnitToToken } from '@ocap/util';
33
33
  import RechargeList from '../../components/invoice/recharge';
34
34
  import SubscriptionDescription from '../../components/subscription/description';
35
35
  import InfoRow from '../../components/info-row';
36
36
  import Currency from '../../components/currency';
37
37
  import SubscriptionMetrics from '../../components/subscription/metrics';
38
- import { goBackOrFallback } from '../../libs/util';
38
+ import { getTokenBalanceLink, goBackOrFallback } from '../../libs/util';
39
39
  import CustomerLink from '../../components/customer/link';
40
40
  import { useSessionContext } from '../../contexts/session';
41
41
  import { formatSmartDuration, TimeUnit } from '../../libs/dayjs';
@@ -237,6 +237,8 @@ export default function RechargePage() {
237
237
  });
238
238
  };
239
239
 
240
+ const balanceLink = getTokenBalanceLink(paymentMethod!, payerValue?.paymentAddress || receiveAddress);
241
+
240
242
  return (
241
243
  <Root>
242
244
  <Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ position: 'relative' }}>
@@ -336,16 +338,43 @@ export default function RechargePage() {
336
338
  </BalanceCard>
337
339
 
338
340
  <BalanceCard
341
+ onClick={() => balanceLink && window.open(balanceLink, '_blank')}
339
342
  sx={{
340
343
  background: 'var(--tags-tag-orange-bg, #B7FEE3)',
341
344
  color: 'var(--tags-tag-orange-text, #007C52)',
342
345
  borderRadius: 'var(--radius-m, 8px)',
343
346
  border: 'none',
347
+
348
+ transition: 'all 0.2s ease-in-out',
349
+ position: 'relative',
350
+ cursor: balanceLink ? 'pointer' : 'default',
351
+ '&:hover': balanceLink
352
+ ? {
353
+ '& .MuiSvgIcon-root': {
354
+ opacity: 1,
355
+ transform: 'translateX(0)',
356
+ },
357
+ }
358
+ : undefined,
344
359
  }}>
345
- <Typography variant="subtitle1">{t('admin.customer.summary.balance')}</Typography>
346
- <Typography variant="h4">
347
- {currentBalance} {subscription.paymentCurrency.symbol}
348
- </Typography>
360
+ <Stack direction="row" alignItems="center" spacing={1}>
361
+ <Stack flex={1}>
362
+ <Typography variant="subtitle1">{t('admin.customer.summary.balance')}</Typography>
363
+ <Typography variant="h4">
364
+ {currentBalance} {subscription.paymentCurrency.symbol}
365
+ </Typography>
366
+ </Stack>
367
+ {balanceLink && (
368
+ <ArrowForwardOutlined
369
+ sx={{
370
+ opacity: 0,
371
+ transform: 'translateX(-10px)',
372
+ transition: 'all 0.2s ease-in-out',
373
+ color: 'inherit',
374
+ }}
375
+ />
376
+ )}
377
+ </Stack>
349
378
  </BalanceCard>
350
379
 
351
380
  <Paper elevation={0} sx={{ mb: 2, mt: 2, backgroundColor: 'background.default' }}>
@@ -6,6 +6,7 @@ import Toast from '@arcblock/ux/lib/Toast';
6
6
  import {
7
7
  AddressForm,
8
8
  CurrencySelector,
9
+ LoadingButton,
9
10
  PaymentSummary,
10
11
  StripeForm,
11
12
  api,
@@ -20,7 +21,6 @@ import {
20
21
  } from '@blocklet/payment-react';
21
22
  import type { TCustomer, TPaymentCurrency, TPaymentMethod, TSubscriptionExpanded } from '@blocklet/payment-types';
22
23
  import { ArrowBackOutlined } from '@mui/icons-material';
23
- import { LoadingButton } from '@mui/lab';
24
24
  import { Alert, CircularProgress, Stack, Typography } from '@mui/material';
25
25
  import { styled } from '@mui/system';
26
26
  import { useRequest, useSetState } from 'ahooks';
@@ -2,6 +2,7 @@
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
4
  import {
5
+ LoadingButton,
5
6
  PricingTable,
6
7
  api,
7
8
  formatBNStr,
@@ -12,7 +13,6 @@ import {
12
13
  } from '@blocklet/payment-react';
13
14
  import type { TLineItemExpanded, TPricingTableExpanded, TSubscriptionExpanded } from '@blocklet/payment-types';
14
15
  import { ArrowBackOutlined } from '@mui/icons-material';
15
- import { LoadingButton } from '@mui/lab';
16
16
  import { Alert, CircularProgress, Divider, Stack, Typography } from '@mui/material';
17
17
  import { useRequest, useSetState } from 'ahooks';
18
18
  import { useRef } from 'react';
@@ -53,7 +53,7 @@ export default function CustomerSubscriptionChangePlan() {
53
53
  const navigate = useNavigate();
54
54
  const { id } = useParams() as { id: string };
55
55
  const { t, locale } = useLocaleContext();
56
- const { connect } = usePaymentContext();
56
+ const { connect, session } = usePaymentContext();
57
57
  const [searchParams] = useSearchParams();
58
58
  const redirectUrl = searchParams.get('redirectUrl');
59
59
  const confirmRef = useRef<HTMLButtonElement>(null);
@@ -164,6 +164,7 @@ export default function CustomerSubscriptionChangePlan() {
164
164
  extraParams: {
165
165
  invoiceId: result.data.pending_update?.updates?.latest_invoice_id,
166
166
  subscriptionId: result.data.id,
167
+ sessionUserDid: session?.user?.did,
167
168
  },
168
169
  onSuccess: () => {
169
170
  setState({ paid: true, paying: false });
@@ -1,25 +1,32 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
- import { Box, Card, CardActionArea, CardContent, Grid, Typography, Divider } from '@mui/material';
2
+ import { Box, Grid, Typography, Divider } from '@mui/material';
3
3
  import { useNavigate } from 'react-router-dom';
4
- import { Favorite, Inventory, Link as LinkIcon, TableChart, Payment, Code } from '@mui/icons-material';
4
+ import {
5
+ Link as LinkIcon,
6
+ Payment,
7
+ Code,
8
+ Inventory2Outlined,
9
+ TableChartOutlined,
10
+ FavoriteBorderOutlined,
11
+ } from '@mui/icons-material';
5
12
 
6
13
  const basicFeatures = [
7
14
  {
8
15
  title: 'integrations.features.products.title',
9
16
  description: 'integrations.features.products.intro',
10
- icon: <Inventory color="primary" sx={{ fontSize: 40 }} />,
17
+ icon: <Inventory2Outlined sx={{ fontSize: 32, color: 'text.lighter' }} />,
11
18
  path: '/admin/products',
12
19
  },
13
20
  {
14
21
  title: 'integrations.features.paymentLinks.title',
15
22
  description: 'integrations.features.paymentLinks.intro',
16
- icon: <LinkIcon color="primary" sx={{ fontSize: 40 }} />,
23
+ icon: <LinkIcon sx={{ fontSize: 32, color: 'text.lighter' }} />,
17
24
  path: '/admin/products/links',
18
25
  },
19
26
  {
20
27
  title: 'integrations.features.pricingTables.title',
21
28
  description: 'integrations.features.pricingTables.intro',
22
- icon: <TableChart color="primary" sx={{ fontSize: 40 }} />,
29
+ icon: <TableChartOutlined sx={{ fontSize: 32, color: 'text.lighter' }} />,
23
30
  path: '/admin/products/pricing-tables',
24
31
  },
25
32
  ];
@@ -28,20 +35,20 @@ const advancedFeatures = [
28
35
  {
29
36
  title: 'integrations.features.donate.title',
30
37
  description: 'integrations.features.donate.intro',
31
- icon: <Favorite color="primary" sx={{ fontSize: 40 }} />,
38
+ icon: <FavoriteBorderOutlined sx={{ fontSize: 32, color: 'text.lighter' }} />,
32
39
  path: '/integrations/donations',
33
40
  },
34
41
  {
35
42
  title: 'integrations.features.paymentMethods.title',
36
43
  description: 'integrations.features.paymentMethods.intro',
37
- icon: <Payment color="primary" sx={{ fontSize: 40 }} />,
44
+ icon: <Payment sx={{ fontSize: 32, color: 'text.lighter' }} />,
38
45
  path: '/admin/settings/payment-methods',
39
46
  },
40
47
  {
41
48
  title: 'integrations.features.api.title',
42
49
  description: 'integrations.features.api.intro',
43
- icon: <Code color="primary" sx={{ fontSize: 40 }} />,
44
- path: 'https://www.npmjs.com/package/@blocklet/payment-js',
50
+ icon: <Code sx={{ fontSize: 32, color: 'text.lighter' }} />,
51
+ path: 'https://www.arcblock.io/docs/arcblock-payment-kit/en/start-payment-js',
45
52
  external: true,
46
53
  },
47
54
  ];
@@ -61,7 +68,7 @@ export default function Overview() {
61
68
  return (
62
69
  <Box>
63
70
  <Box mb={4}>
64
- <Typography variant="h2" gutterBottom fontWeight="bold" mb={2}>
71
+ <Typography variant="h2" gutterBottom fontWeight="bold" mb={1}>
65
72
  {t('common.welcome')}
66
73
  </Typography>
67
74
  <Typography variant="body1" color="text.secondary" mb={2}>
@@ -69,50 +76,66 @@ export default function Overview() {
69
76
  </Typography>
70
77
  </Box>
71
78
 
72
- <Typography variant="h5" gutterBottom>
79
+ <Typography variant="h3" gutterBottom mb={2}>
73
80
  {t('integrations.basicFeatures')} 🚀
74
81
  </Typography>
75
- <Grid container spacing={3} mb={4}>
82
+ <Grid container spacing={2} mb={4} sx={{ maxWidth: 1200 }}>
76
83
  {basicFeatures.map((item) => (
77
84
  <Grid item xs={12} sm={6} md={4} key={item.path}>
78
- <Card sx={{ height: '100%' }}>
79
- <CardActionArea onClick={() => handleClick(item)} sx={{ height: '100%' }}>
80
- <CardContent>
81
- <Box display="flex" alignItems="center" gap={2} mb={2}>
82
- {item.icon}
83
- <Typography variant="h6">{t(item.title)}</Typography>
84
- </Box>
85
- <Typography variant="body2" color="text.secondary">
86
- {t(item.description)}
87
- </Typography>
88
- </CardContent>
89
- </CardActionArea>
90
- </Card>
85
+ <Box
86
+ className="base-card"
87
+ onClick={() => handleClick(item)}
88
+ sx={{
89
+ height: '100%',
90
+ cursor: 'pointer',
91
+ '&:hover': {
92
+ boxShadow:
93
+ '0px 0px 0px 1px var(--shadows-card-hover-1, rgba(2, 7, 19, 0.08)), 0px 1px 2px -1px var(--shadows-card-hover-2, rgba(2, 7, 19, 0.08)), 0px 2px 8px 0px var(--shadows-card-hover-3, rgba(2, 7, 19, 0.10))',
94
+ },
95
+ }}>
96
+ <Box mb={1}>
97
+ {item.icon}
98
+ <Typography variant="h4" mt={1.5}>
99
+ {t(item.title)}
100
+ </Typography>
101
+ </Box>
102
+ <Typography variant="body2" color="text.secondary">
103
+ {t(item.description)}
104
+ </Typography>
105
+ </Box>
91
106
  </Grid>
92
107
  ))}
93
108
  </Grid>
94
109
 
95
110
  <Divider sx={{ my: 4 }} />
96
111
 
97
- <Typography variant="h5" gutterBottom>
112
+ <Typography variant="h3" gutterBottom mb={2}>
98
113
  {t('integrations.advancedFeatures')} 🔥
99
114
  </Typography>
100
- <Grid container spacing={3}>
115
+ <Grid container spacing={2} sx={{ maxWidth: 1200 }}>
101
116
  {advancedFeatures.map((item) => (
102
117
  <Grid item xs={12} sm={6} md={4} key={item.path}>
103
- <Card sx={{ height: '100%' }}>
104
- <CardActionArea onClick={() => handleClick(item)} sx={{ height: '100%' }}>
105
- <CardContent>
106
- <Box display="flex" alignItems="center" gap={2} mb={2}>
107
- {item.icon}
108
- <Typography variant="h6">{t(item.title)}</Typography>
109
- </Box>
110
- <Typography variant="body2" color="text.secondary">
111
- {t(item.description)}
112
- </Typography>
113
- </CardContent>
114
- </CardActionArea>
115
- </Card>
118
+ <Box
119
+ className="base-card"
120
+ onClick={() => handleClick(item)}
121
+ sx={{
122
+ height: '100%',
123
+ cursor: 'pointer',
124
+ '&:hover': {
125
+ boxShadow:
126
+ '0px 0px 0px 1px var(--shadows-card-hover-1, rgba(2, 7, 19, 0.08)), 0px 1px 2px -1px var(--shadows-card-hover-2, rgba(2, 7, 19, 0.08)), 0px 2px 8px 0px var(--shadows-card-hover-3, rgba(2, 7, 19, 0.10))',
127
+ },
128
+ }}>
129
+ <Box mb={1}>
130
+ {item.icon}
131
+ <Typography variant="h6" mt={1.5}>
132
+ {t(item.title)}
133
+ </Typography>
134
+ </Box>
135
+ <Typography variant="body2" color="text.secondary">
136
+ {t(item.description)}
137
+ </Typography>
138
+ </Box>
116
139
  </Grid>
117
140
  ))}
118
141
  </Grid>