@xiashe/sdk 0.1.17 → 0.1.18

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/alipayWallet.mjs CHANGED
@@ -12,6 +12,13 @@ const SENSITIVE_USER_OUTPUT = /payment[-_ ]?proof|payment[-_ ]?needed|seller_sig
12
12
  const DEFAULT_STATE_DIR = path.join(os.homedir(), '.xiashe', 'alipay-402');
13
13
  const OFFICIAL_MEDIA_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp']);
14
14
  const MAX_OFFICIAL_MEDIA_BYTES = 8 * 1024 * 1024;
15
+ const SAFE_OFFICIAL_FAILURE_CODES = new Set([
16
+ 'TRADE_PROGRESS_FAILED',
17
+ 'PAYMENT_NEEDED_REJECTED',
18
+ 'SYSTEM_ERROR',
19
+ 'BUYER_IDENTITY_REJECTED',
20
+ 'WALLET_NOT_READY'
21
+ ]);
15
22
 
16
23
  export class AlipayWalletError extends Error {
17
24
  constructor(code, message, options = {}) {
@@ -230,6 +237,81 @@ function walletFailureCode(stdout, stderr, exitCode) {
230
237
  return Number.isInteger(exitCode) && exitCode !== 0 ? 'official_cli_nonzero_exit' : 'official_cli_unclassified';
231
238
  }
232
239
 
240
+ /**
241
+ * The official wallet can return useful machine error codes alongside protocol
242
+ * material. Expose only a small allow-list: arbitrary vendor output can carry
243
+ * credentials, URLs, trade identifiers, or PayGuard internals.
244
+ */
245
+ function safeOfficialFailureCode(stdout, stderr) {
246
+ const output = `${stdout}\n${stderr}`;
247
+ const candidate = output.match(/(?:"(?:errorCode|code)"|\b(?:errorCode|code)\b)\s*[:=]\s*["']?([A-Z][A-Z0-9_]{2,80})/i)?.[1]?.toUpperCase() || '';
248
+ return SAFE_OFFICIAL_FAILURE_CODES.has(candidate) ? candidate : null;
249
+ }
250
+
251
+ function paymentFailureDiagnosis(failureCode, stdout, stderr) {
252
+ const officialCode = safeOfficialFailureCode(stdout, stderr);
253
+ const base = {
254
+ code: failureCode,
255
+ stage: 'official_wallet_trade_creation',
256
+ nextStep: '不要重复付款;保留诊断代码和本次 attemptId 以便定位。'
257
+ };
258
+ if (failureCode === 'payment_needed_rejected') {
259
+ return {
260
+ ...base,
261
+ stage: 'alipay_bill_validation',
262
+ nextStep: '核对支付宝平台中该 APPID 的应用公钥、service_id、收款方 2088 seller_id 和线上接入状态;修复后必须创建新订单再测试。',
263
+ ...(officialCode ? { officialCode } : {})
264
+ };
265
+ }
266
+ if (failureCode === 'wallet_runtime_not_supported') {
267
+ return {
268
+ ...base,
269
+ stage: 'agent_runtime_identity',
270
+ nextStep: '当前运行时没有通过支付宝 Agent 身份校验;请换用已被官方钱包识别并已授权的本地 Agent 运行时。',
271
+ ...(officialCode ? { officialCode } : {})
272
+ };
273
+ }
274
+ if (failureCode === 'buyer_identity_rejected') {
275
+ return {
276
+ ...base,
277
+ stage: 'buyer_identity_validation',
278
+ nextStep: '支付宝钱包的买家身份未通过校验;完成钱包授权后,用同一受支持运行时创建新订单。',
279
+ ...(officialCode ? { officialCode } : {})
280
+ };
281
+ }
282
+ if (failureCode === 'wallet_not_ready') {
283
+ return {
284
+ ...base,
285
+ stage: 'wallet_authorization',
286
+ nextStep: '支付宝钱包未完成授权或未启用;先完成官方钱包授权,再创建新订单。',
287
+ ...(officialCode ? { officialCode } : {})
288
+ };
289
+ }
290
+ if (failureCode === 'wallet_network_error') {
291
+ return {
292
+ ...base,
293
+ stage: 'wallet_network',
294
+ nextStep: '钱包或网络请求没有完成;先检查网络,再创建新订单。',
295
+ ...(officialCode ? { officialCode } : {})
296
+ };
297
+ }
298
+ return {
299
+ ...base,
300
+ ...(officialCode ? { officialCode } : {})
301
+ };
302
+ }
303
+
304
+ function paymentFailureUserVisible(diagnosis) {
305
+ const lines = [
306
+ '支付宝付款未创建可确认交易。',
307
+ `诊断代码:${diagnosis.code}`,
308
+ `失败阶段:${diagnosis.stage}`,
309
+ ...(diagnosis.officialCode ? [`官方返回代码:${diagnosis.officialCode}`] : []),
310
+ `下一步:${diagnosis.nextStep}`
311
+ ];
312
+ return lines.join('\n');
313
+ }
314
+
233
315
  function paymentMessage(status) {
234
316
  if (status === 'completed') return '支付宝钱包显示本次付款已完成;正在由服务端确认权益。';
235
317
  if (status === 'pending_confirmation') return '支付宝已发起本次付款确认;请打开支付宝 App 完成确认,然后运行 status。';
@@ -285,6 +367,7 @@ function publicPaymentResult(state, status, diagnostic, options = {}) {
285
367
  const commandFailed = options.commandFailed === true;
286
368
  const failureCode = options.failureCode || 'official_cli_nonzero_exit';
287
369
  const failed = status === 'failed' || status === 'wallet_command_failed';
370
+ const diagnosis = failed ? options.diagnosis : null;
288
371
  return {
289
372
  ok: !failed,
290
373
  status,
@@ -293,7 +376,8 @@ function publicPaymentResult(state, status, diagnostic, options = {}) {
293
376
  walletInvoked: true,
294
377
  message: failed ? paymentFailureMessage(failureCode) : paymentMessage(status),
295
378
  diagnostic,
296
- ...(options.userVisible ? { userVisible: options.userVisible } : {}),
379
+ ...(failed && diagnosis ? { userVisible: paymentFailureUserVisible(diagnosis), diagnosis } : {}),
380
+ ...(!failed && options.userVisible ? { userVisible: options.userVisible } : {}),
297
381
  ...(options.officialMedia ? { officialMedia: options.officialMedia } : {}),
298
382
  ...(status === 'pending_confirmation'
299
383
  ? { confirmation: { source: 'official_alipay_wallet', surface: 'app_or_official_media', required: true } }
@@ -392,9 +476,11 @@ export async function payAlipay402Resource(input, options = {}) {
392
476
  await writePaymentState(state, options);
393
477
  if (status === 'completed') await rm(paths.paymentNeededPath, { force: true });
394
478
  const failed = commandFailed || status === 'failed';
479
+ const failureCode = failed ? walletFailureCode(walletInvocation.stdout, walletInvocation.stderr, walletInvocation.exitCode) : undefined;
395
480
  return publicPaymentResult(state, status, result.diagnostic, {
396
481
  commandFailed: failed,
397
- failureCode: failed ? walletFailureCode(walletInvocation.stdout, walletInvocation.stderr, walletInvocation.exitCode) : undefined,
482
+ failureCode,
483
+ diagnosis: failed ? paymentFailureDiagnosis(failureCode, walletInvocation.stdout, walletInvocation.stderr) : undefined,
398
484
  userVisible: ['pending_confirmation', 'completed'].includes(status)
399
485
  ? userVisibleWalletOutput(walletInvocation.stdout, walletInvocation.stderr)
400
486
  : null,
@@ -440,9 +526,11 @@ export async function queryAlipay402Resource(input, options = {}) {
440
526
  await rm(paymentNeededPath, { force: true });
441
527
  }
442
528
  const failed = commandFailed || status === 'failed';
529
+ const failureCode = failed ? walletFailureCode(walletInvocation.stdout, walletInvocation.stderr, walletInvocation.exitCode) : undefined;
443
530
  return publicPaymentResult(state, status, result.diagnostic, {
444
531
  commandFailed: failed,
445
- failureCode: failed ? walletFailureCode(walletInvocation.stdout, walletInvocation.stderr, walletInvocation.exitCode) : undefined,
532
+ failureCode,
533
+ diagnosis: failed ? paymentFailureDiagnosis(failureCode, walletInvocation.stdout, walletInvocation.stderr) : undefined,
446
534
  userVisible: ['pending_confirmation', 'completed'].includes(status)
447
535
  ? userVisibleWalletOutput(walletInvocation.stdout, walletInvocation.stderr)
448
536
  : null,
package/index.mjs CHANGED
@@ -6,7 +6,7 @@ import path from 'node:path';
6
6
  import { ConvexHttpClient } from 'convex/browser';
7
7
  import { anyApi as api } from 'convex/server';
8
8
 
9
- export const VERSION = '0.1.17';
9
+ export const VERSION = '0.1.18';
10
10
 
11
11
  export const DEFAULTS = {
12
12
  cn: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiashe/sdk",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "main": "index.mjs",
6
6
  "exports": {