payment-kit 1.25.2 → 1.25.4

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,21 @@ 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.host || client.endpoint || 'unknown';
30
+
31
+ // If wallet address and chain host haven't changed, use cached value
32
+ if (wallet.address === cachedWalletAddress && chainHost === cachedChainHost && cachedMigratedFrom !== null) {
29
33
  return cachedMigratedFrom;
30
34
  }
31
35
 
@@ -34,17 +38,23 @@ export async function getMigratedFromList(client: OcapClient): Promise<string[]>
34
38
  const { state } = await client.getAccountState({ address: wallet.address });
35
39
  cachedMigratedFrom = state?.migratedFrom || [];
36
40
  cachedWalletAddress = wallet.address;
41
+ cachedChainHost = chainHost;
37
42
 
38
43
  if (cachedMigratedFrom.length > 0) {
39
44
  logger.info('wallet-migration: loaded migratedFrom list', {
40
45
  walletAddress: wallet.address,
46
+ chainHost,
41
47
  migratedFrom: cachedMigratedFrom,
42
48
  });
43
49
  }
44
50
 
45
51
  return cachedMigratedFrom;
46
52
  } catch (err) {
47
- logger.error('wallet-migration: failed to get migratedFrom list', { error: err });
53
+ logger.error('wallet-migration: failed to get migratedFrom list', {
54
+ walletAddress: wallet.address,
55
+ chainHost,
56
+ error: err,
57
+ });
48
58
  return [];
49
59
  }
50
60
  }
@@ -54,6 +64,7 @@ export async function getMigratedFromList(client: OcapClient): Promise<string[]>
54
64
  */
55
65
  export function clearMigratedFromCache(): void {
56
66
  cachedMigratedFrom = null;
67
+ cachedChainHost = null;
57
68
  cachedWalletAddress = null;
58
69
  }
59
70
 
@@ -85,19 +96,46 @@ export async function getDelegationAddressWithFallback({
85
96
  delegator,
86
97
  client,
87
98
  }: GetDelegationAddressParams): Promise<AddressWithFallbackResult | null> {
88
- // 1. Check stored delegation_address first
99
+ // 1. Check stored delegation_address first - but verify it has valid state
89
100
  if (storedAddress) {
90
- return { address: storedAddress, needsBackfill: false, source: 'stored' };
101
+ try {
102
+ const { state: storedState } = await client.getDelegateState({ address: storedAddress });
103
+ if (storedState?.ops?.length > 0) {
104
+ return { address: storedAddress, needsBackfill: false, source: 'stored' };
105
+ }
106
+ logger.warn('wallet-migration: stored delegation address has no valid state, falling back', {
107
+ storedAddress,
108
+ delegator,
109
+ hasState: !!storedState,
110
+ opsCount: storedState?.ops?.length || 0,
111
+ });
112
+ } catch (err) {
113
+ logger.warn('wallet-migration: failed to query stored delegation address, falling back', {
114
+ storedAddress,
115
+ delegator,
116
+ error: err,
117
+ });
118
+ }
119
+ // Continue to fallback instead of returning early
91
120
  }
92
121
 
93
122
  // 2. Try current wallet.address
94
123
  const currentAddress = toDelegateAddress(delegator, wallet.address);
124
+ let currentStateResult: { hasState: boolean; opsCount: number; error?: string } = {
125
+ hasState: false,
126
+ opsCount: 0,
127
+ };
95
128
  try {
96
129
  const { state: currentState } = await client.getDelegateState({ address: currentAddress });
130
+ currentStateResult = {
131
+ hasState: !!currentState,
132
+ opsCount: currentState?.ops?.length || 0,
133
+ };
97
134
  if (currentState?.ops?.length > 0) {
98
135
  return { address: currentAddress, needsBackfill: true, source: 'current' };
99
136
  }
100
137
  } catch (err) {
138
+ currentStateResult.error = String(err);
101
139
  logger.warn('wallet-migration: failed to query current delegation state', {
102
140
  address: currentAddress,
103
141
  error: err,
@@ -106,11 +144,24 @@ export async function getDelegationAddressWithFallback({
106
144
 
107
145
  // 3. Fallback to migratedFrom addresses
108
146
  const migratedFrom = await getMigratedFromList(client);
147
+ const migratedResults: Array<{
148
+ appDid: string;
149
+ address: string;
150
+ hasState: boolean;
151
+ opsCount: number;
152
+ error?: string;
153
+ }> = [];
109
154
  for (const oldAppDid of migratedFrom) {
110
155
  const oldAddress = toDelegateAddress(delegator, oldAppDid);
111
156
  try {
112
157
  // eslint-disable-next-line no-await-in-loop
113
158
  const { state: oldState } = await client.getDelegateState({ address: oldAddress });
159
+ migratedResults.push({
160
+ appDid: oldAppDid,
161
+ address: oldAddress,
162
+ hasState: !!oldState,
163
+ opsCount: oldState?.ops?.length || 0,
164
+ });
114
165
  if (oldState?.ops?.length > 0) {
115
166
  logger.info('wallet-migration: found delegation in migratedFrom', {
116
167
  delegator,
@@ -120,6 +171,13 @@ export async function getDelegationAddressWithFallback({
120
171
  return { address: oldAddress, needsBackfill: true, source: 'migrated' };
121
172
  }
122
173
  } catch (err) {
174
+ migratedResults.push({
175
+ appDid: oldAppDid,
176
+ address: oldAddress,
177
+ hasState: false,
178
+ opsCount: 0,
179
+ error: String(err),
180
+ });
123
181
  logger.warn('wallet-migration: failed to query migrated delegation state', {
124
182
  address: oldAddress,
125
183
  oldAppDid,
@@ -128,7 +186,15 @@ export async function getDelegationAddressWithFallback({
128
186
  }
129
187
  }
130
188
 
131
- // Not found
189
+ // Not found - log detailed search results
190
+ logger.error('wallet-migration: no delegation found after full search', {
191
+ delegator,
192
+ currentWalletAddress: wallet.address,
193
+ currentDelegationAddress: currentAddress,
194
+ currentStateResult,
195
+ migratedFromCount: migratedFrom.length,
196
+ migratedResults,
197
+ });
132
198
  return null;
133
199
  }
134
200
 
@@ -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.4
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.4",
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.4",
63
+ "@blocklet/payment-react": "1.25.4",
64
+ "@blocklet/payment-vendor": "1.25.4",
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.4",
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": "e551d0dd9b08432ff880a33d859fa9a00c8ff649"
183
183
  }