payment-kit 1.25.2 → 1.25.5

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.
@@ -253,6 +253,15 @@ export async function isDelegationSufficientForPayment(args: {
253
253
  }
254
254
  }
255
255
 
256
+ // Log delegation check context for debugging
257
+ logger.info('isDelegationSufficientForPayment: checking delegation', {
258
+ originalUserDid: userDid,
259
+ resolvedDelegator: delegator,
260
+ delegatorChanged: userDid !== delegator,
261
+ walletAddress: wallet.address,
262
+ paymentMethodApiHost: paymentMethod.settings?.arcblock?.api_host,
263
+ });
264
+
256
265
  const client = paymentMethod.getOcapClient();
257
266
 
258
267
  // have delegated before? Use migration-aware fallback query
@@ -263,6 +272,12 @@ export async function isDelegationSufficientForPayment(args: {
263
272
  client,
264
273
  });
265
274
  if (!delegationResult) {
275
+ logger.error('isDelegationSufficientForPayment: no delegation address found', {
276
+ delegator,
277
+ storedDelegationAddress,
278
+ subscriptionDelegationAddress: subscription?.payment_details?.arcblock?.delegation_address,
279
+ subscriptionId: subscription?.id,
280
+ });
266
281
  return { sufficient: false, reason: 'NO_DELEGATION' };
267
282
  }
268
283
 
@@ -15,17 +15,30 @@ import { wallet } from './auth';
15
15
  import logger from './logger';
16
16
  import { Subscription } from '../store/models';
17
17
 
18
- // Cache for migratedFrom list (refreshed when wallet.address changes)
18
+ // Cache for migratedFrom list (keyed by wallet.address + chain host)
19
19
  let cachedMigratedFrom: string[] | null = null;
20
20
  let cachedWalletAddress: string | null = null;
21
+ let cachedChainHost: string | null = null;
21
22
 
22
23
  /**
23
24
  * Get the migratedFrom list for the current app wallet (with caching)
24
- * The cache is invalidated when wallet.address changes
25
+ * The cache is invalidated when wallet.address or chain host changes
25
26
  */
26
27
  export async function getMigratedFromList(client: OcapClient): Promise<string[]> {
27
- // If wallet address hasn't changed, use cached value
28
- if (wallet.address === cachedWalletAddress && cachedMigratedFrom !== null) {
28
+ // @ts-ignore - OcapClient has host property
29
+ const chainHost = client?.config?.httpEndpoint || 'unknown';
30
+ // If wallet address and chain host haven't changed, use cached value
31
+ if (
32
+ wallet.address === cachedWalletAddress &&
33
+ chainHost === cachedChainHost &&
34
+ cachedMigratedFrom &&
35
+ cachedMigratedFrom.length > 0
36
+ ) {
37
+ logger.info('wallet-migration: using cached migratedFrom list', {
38
+ walletAddress: wallet.address,
39
+ chainHost,
40
+ migratedFromCount: cachedMigratedFrom.length,
41
+ });
29
42
  return cachedMigratedFrom;
30
43
  }
31
44
 
@@ -34,17 +47,23 @@ export async function getMigratedFromList(client: OcapClient): Promise<string[]>
34
47
  const { state } = await client.getAccountState({ address: wallet.address });
35
48
  cachedMigratedFrom = state?.migratedFrom || [];
36
49
  cachedWalletAddress = wallet.address;
50
+ cachedChainHost = chainHost;
37
51
 
38
- if (cachedMigratedFrom.length > 0) {
39
- logger.info('wallet-migration: loaded migratedFrom list', {
40
- walletAddress: wallet.address,
41
- migratedFrom: cachedMigratedFrom,
42
- });
43
- }
52
+ logger.info('wallet-migration: loaded migratedFrom list', {
53
+ walletAddress: wallet.address,
54
+ chainHost,
55
+ migratedFromCount: cachedMigratedFrom.length,
56
+ migratedFrom: cachedMigratedFrom,
57
+ migratedTo: state?.migratedTo,
58
+ });
44
59
 
45
60
  return cachedMigratedFrom;
46
61
  } catch (err) {
47
- logger.error('wallet-migration: failed to get migratedFrom list', { error: err });
62
+ logger.error('wallet-migration: failed to get migratedFrom list', {
63
+ walletAddress: wallet.address,
64
+ chainHost,
65
+ error: err,
66
+ });
48
67
  return [];
49
68
  }
50
69
  }
@@ -54,6 +73,7 @@ export async function getMigratedFromList(client: OcapClient): Promise<string[]>
54
73
  */
55
74
  export function clearMigratedFromCache(): void {
56
75
  cachedMigratedFrom = null;
76
+ cachedChainHost = null;
57
77
  cachedWalletAddress = null;
58
78
  }
59
79
 
@@ -85,19 +105,67 @@ export async function getDelegationAddressWithFallback({
85
105
  delegator,
86
106
  client,
87
107
  }: GetDelegationAddressParams): Promise<AddressWithFallbackResult | null> {
88
- // 1. Check stored delegation_address first
108
+ // @ts-ignore - OcapClient has host property
109
+ const chainHost = client?.config?.httpEndpoint || 'unknown';
110
+
111
+ logger.info('wallet-migration: getDelegationAddressWithFallback called', {
112
+ delegator,
113
+ storedAddress: storedAddress || null,
114
+ walletAddress: wallet.address,
115
+ chainHost,
116
+ });
117
+
118
+ // 1. Check stored delegation_address first - but verify it has valid state
89
119
  if (storedAddress) {
90
- return { address: storedAddress, needsBackfill: false, source: 'stored' };
120
+ try {
121
+ const { state: storedState } = await client.getDelegateState({ address: storedAddress });
122
+ if (storedState?.ops?.length > 0) {
123
+ logger.info('wallet-migration: found valid delegation at stored address', {
124
+ storedAddress,
125
+ delegator,
126
+ opsCount: storedState.ops.length,
127
+ });
128
+ return { address: storedAddress, needsBackfill: false, source: 'stored' };
129
+ }
130
+ logger.warn('wallet-migration: stored delegation address has no valid state, falling back', {
131
+ storedAddress,
132
+ delegator,
133
+ hasState: !!storedState,
134
+ opsCount: storedState?.ops?.length || 0,
135
+ });
136
+ } catch (err) {
137
+ logger.warn('wallet-migration: failed to query stored delegation address, falling back', {
138
+ storedAddress,
139
+ delegator,
140
+ error: err,
141
+ });
142
+ }
143
+ // Continue to fallback instead of returning early
91
144
  }
92
145
 
93
146
  // 2. Try current wallet.address
94
147
  const currentAddress = toDelegateAddress(delegator, wallet.address);
148
+ let currentStateResult: { hasState: boolean; opsCount: number; error?: string } = {
149
+ hasState: false,
150
+ opsCount: 0,
151
+ };
95
152
  try {
96
153
  const { state: currentState } = await client.getDelegateState({ address: currentAddress });
154
+ currentStateResult = {
155
+ hasState: !!currentState,
156
+ opsCount: currentState?.ops?.length || 0,
157
+ };
97
158
  if (currentState?.ops?.length > 0) {
159
+ logger.info('wallet-migration: found valid delegation at current address', {
160
+ currentAddress,
161
+ delegator,
162
+ walletAddress: wallet.address,
163
+ opsCount: currentState.ops.length,
164
+ });
98
165
  return { address: currentAddress, needsBackfill: true, source: 'current' };
99
166
  }
100
167
  } catch (err) {
168
+ currentStateResult.error = String(err);
101
169
  logger.warn('wallet-migration: failed to query current delegation state', {
102
170
  address: currentAddress,
103
171
  error: err,
@@ -106,11 +174,24 @@ export async function getDelegationAddressWithFallback({
106
174
 
107
175
  // 3. Fallback to migratedFrom addresses
108
176
  const migratedFrom = await getMigratedFromList(client);
177
+ const migratedResults: Array<{
178
+ appDid: string;
179
+ address: string;
180
+ hasState: boolean;
181
+ opsCount: number;
182
+ error?: string;
183
+ }> = [];
109
184
  for (const oldAppDid of migratedFrom) {
110
185
  const oldAddress = toDelegateAddress(delegator, oldAppDid);
111
186
  try {
112
187
  // eslint-disable-next-line no-await-in-loop
113
188
  const { state: oldState } = await client.getDelegateState({ address: oldAddress });
189
+ migratedResults.push({
190
+ appDid: oldAppDid,
191
+ address: oldAddress,
192
+ hasState: !!oldState,
193
+ opsCount: oldState?.ops?.length || 0,
194
+ });
114
195
  if (oldState?.ops?.length > 0) {
115
196
  logger.info('wallet-migration: found delegation in migratedFrom', {
116
197
  delegator,
@@ -120,6 +201,13 @@ export async function getDelegationAddressWithFallback({
120
201
  return { address: oldAddress, needsBackfill: true, source: 'migrated' };
121
202
  }
122
203
  } catch (err) {
204
+ migratedResults.push({
205
+ appDid: oldAppDid,
206
+ address: oldAddress,
207
+ hasState: false,
208
+ opsCount: 0,
209
+ error: String(err),
210
+ });
123
211
  logger.warn('wallet-migration: failed to query migrated delegation state', {
124
212
  address: oldAddress,
125
213
  oldAppDid,
@@ -128,7 +216,16 @@ export async function getDelegationAddressWithFallback({
128
216
  }
129
217
  }
130
218
 
131
- // Not found
219
+ // Not found - log detailed search results
220
+ logger.error('wallet-migration: no delegation found after full search', {
221
+ delegator,
222
+ chainHost,
223
+ currentWalletAddress: wallet.address,
224
+ currentDelegationAddress: currentAddress,
225
+ currentStateResult,
226
+ migratedFromCount: migratedFrom.length,
227
+ migratedResults,
228
+ });
132
229
  return null;
133
230
  }
134
231
 
@@ -25,6 +25,7 @@ jest.mock('../../src/libs/logger', () => ({
25
25
  describe('wallet-migration', () => {
26
26
  // Mock OcapClient
27
27
  const createMockClient = (overrides: any = {}) => ({
28
+ host: 'https://main.abtnetwork.io/api',
28
29
  getAccountState: jest.fn().mockResolvedValue({
29
30
  state: {
30
31
  migratedFrom: ['zNKq13Dr2TBHELpLDUJFGxepiGP7YHbAVxPn', 'zNKq6yG8AVbwdRBDJSNCDVJJUQD1AXdkK7Wp'],
@@ -76,8 +77,15 @@ describe('wallet-migration', () => {
76
77
  const currentWalletAddress = 'zNKcF7wyrvAvn4YaADjr4p9gpFUSHnW2aQJa';
77
78
  const oldAppDid = 'zNKq13Dr2TBHELpLDUJFGxepiGP7YHbAVxPn';
78
79
 
79
- it('should return stored address if available', async () => {
80
- const mockClient = createMockClient();
80
+ it('should return stored address if available and valid', async () => {
81
+ const mockClient = createMockClient({
82
+ getDelegateState: jest.fn().mockImplementation(({ address }) => {
83
+ if (address === 'stored_delegation_address') {
84
+ return { state: { ops: [{ key: 'fg:x:transfer' }] } };
85
+ }
86
+ return { state: null };
87
+ }),
88
+ });
81
89
 
82
90
  const result = await getDelegationAddressWithFallback({
83
91
  storedAddress: 'stored_delegation_address',
@@ -90,8 +98,34 @@ describe('wallet-migration', () => {
90
98
  needsBackfill: false,
91
99
  source: 'stored',
92
100
  });
93
- // Should not query chain if stored
94
- expect(mockClient.getDelegateState).not.toHaveBeenCalled();
101
+ // Should verify stored address
102
+ expect(mockClient.getDelegateState).toHaveBeenCalledWith({ address: 'stored_delegation_address' });
103
+ });
104
+
105
+ it('should fallback if stored address has no valid state', async () => {
106
+ const oldAddress = toDelegateAddress(delegator, oldAppDid);
107
+ const mockClient = createMockClient({
108
+ getDelegateState: jest.fn().mockImplementation(({ address }) => {
109
+ // stored address has no state, but migratedFrom address has
110
+ if (address === oldAddress) {
111
+ return { state: { ops: [{ key: 'fg:x:transfer' }] } };
112
+ }
113
+ return { state: null };
114
+ }),
115
+ });
116
+
117
+ const result = await getDelegationAddressWithFallback({
118
+ storedAddress: 'invalid_stored_address',
119
+ delegator,
120
+ client: mockClient as any,
121
+ });
122
+
123
+ // Should fallback to migratedFrom
124
+ expect(result).toEqual({
125
+ address: oldAddress,
126
+ needsBackfill: true,
127
+ source: 'migrated',
128
+ });
95
129
  });
96
130
 
97
131
  it('should return current address if delegation exists on current wallet', async () => {
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.25.2
17
+ version: 1.25.5
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.25.2",
3
+ "version": "1.25.5",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "prelint": "npm run types",
@@ -59,9 +59,9 @@
59
59
  "@blocklet/error": "^0.3.5",
60
60
  "@blocklet/js-sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
61
61
  "@blocklet/logger": "^1.17.8-beta-20260104-120132-cb5b1914",
62
- "@blocklet/payment-broker-client": "1.25.2",
63
- "@blocklet/payment-react": "1.25.2",
64
- "@blocklet/payment-vendor": "1.25.2",
62
+ "@blocklet/payment-broker-client": "1.25.5",
63
+ "@blocklet/payment-react": "1.25.5",
64
+ "@blocklet/payment-vendor": "1.25.5",
65
65
  "@blocklet/sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
66
66
  "@blocklet/ui-react": "^3.4.7",
67
67
  "@blocklet/uploader": "^0.3.19",
@@ -132,7 +132,7 @@
132
132
  "devDependencies": {
133
133
  "@abtnode/types": "^1.17.8-beta-20260104-120132-cb5b1914",
134
134
  "@arcblock/eslint-config-ts": "^0.3.3",
135
- "@blocklet/payment-types": "1.25.2",
135
+ "@blocklet/payment-types": "1.25.5",
136
136
  "@types/cookie-parser": "^1.4.9",
137
137
  "@types/cors": "^2.8.19",
138
138
  "@types/debug": "^4.1.12",
@@ -179,5 +179,5 @@
179
179
  "parser": "typescript"
180
180
  }
181
181
  },
182
- "gitHead": "11621a1c925f94bf4f73644de70088e5f0580f74"
182
+ "gitHead": "fc70dce73c8d46dd6cd7936d188d551316cd02db"
183
183
  }