_deep_diag.mjs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { config } from 'dotenv';
  2. import { resolve, dirname } from 'path';
  3. import { fileURLToPath } from 'url';
  4. import crypto from 'crypto';
  5. const __dirname = dirname(fileURLToPath(import.meta.url));
  6. config({ path: resolve(__dirname, '.env') });
  7. const { AlipaySdk } = await import('alipay-sdk');
  8. const appId = process.env.ALIPAY_APP_ID;
  9. const rawPrivateKey = process.env.ALIPAY_PRIVATE_KEY;
  10. const rawPublicKey = process.env.ALIPAY_PUBLIC_KEY;
  11. // 转 PKCS#1 PEM
  12. const pkcs8Buf = Buffer.from(rawPrivateKey, 'base64');
  13. const privObj = crypto.createPrivateKey({ key: pkcs8Buf, format: 'der', type: 'pkcs8' });
  14. const pkcs1Pem = privObj.export({ format: 'pem', type: 'pkcs1' });
  15. const alipayPubPem = rawPublicKey.includes('BEGIN')
  16. ? rawPublicKey
  17. : `-----BEGIN PUBLIC KEY-----\n${rawPublicKey}\n-----END PUBLIC KEY-----`;
  18. // 用户应用公钥
  19. const userAppPubKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu/fGftv/79Cj7MEonMxcv6Br+zPfRQoEOJ4eIvxfDrVg7N+/k7P+Rw9CaOMMYK5Shy7VJrBAx9oV59v9PMI9FdnAzYFHtiKzjq6ZmolZwXM44pZoXlGQ7ZxtuiN/FshN74me80OWXzU3FcUR9YG6UguddvdtlYvZRUScnCJabbys7KL9dUbRNvTQ6CnD8U6OoDGveISxFbA0aCa6BgZRo8Nuz28p8HYHI2+FEWnFDuE/GRU57mGXk8mR6siReNVlCMEX80Uq5RVG+6EXxeCwtt3lxCwE7aS/tvGOZOAsgOriDjyidNgrJgoPlY2Mb1fyRMPLJZvyFCYiwH46MvL39wIDAQAB';
  20. // 方式1: SDK 签名
  21. const sdk = new AlipaySdk({
  22. appId,
  23. privateKey: pkcs1Pem,
  24. keyType: 'PKCS1',
  25. alipayPublicKey: alipayPubPem,
  26. signType: 'RSA2',
  27. });
  28. const orderNo = 'DIAG' + Date.now();
  29. const form = sdk.pageExecute('alipay.trade.wap.pay', 'POST', {
  30. bizContent: {
  31. out_trade_no: orderNo,
  32. total_amount: '0.01',
  33. subject: '诊断测试',
  34. product_code: 'QUICK_WAP_WAY',
  35. },
  36. returnUrl: 'https://book.rrbrr.com/#/pages/payment-result/index',
  37. notifyUrl: 'https://book.rrbrr.com/api/payment/alipay/notify',
  38. });
  39. const formStr = String(form);
  40. const sdkSign = formStr.match(/sign=([^"&\s]+)/)?.[1] || '';
  41. console.log('SDK签名(前80):', sdkSign.substring(0, 80));
  42. console.log('SDK签名长度:', sdkSign.length);
  43. // 从URL中提取验签字符串
  44. const actionUrl = formStr.match(/action="([^"]+)"/)?.[1] || '';
  45. const urlParams = new URL(actionUrl);
  46. const paramMap = {};
  47. urlParams.searchParams.forEach((v, k) => {
  48. if (k !== 'sign') paramMap[k] = v;
  49. });
  50. // 构建验签字符串(按支付宝规则:参数排序后用&连接)
  51. const sortedParams = Object.keys(paramMap).sort().map(k => `${k}=${paramMap[k]}`).join('&');
  52. const signString = decodeURIComponent(sortedParams);
  53. console.log('\nSDK生成的验签字符串:', signString.substring(0, 200));
  54. // 方式2: 手动RSA2签名
  55. const decoder = new TextDecoder();
  56. const signStrBytes = new TextEncoder().encode(signString);
  57. const sign2 = crypto.createSign('RSA-SHA256');
  58. sign2.update(signString);
  59. const manualSign = sign2.sign(pkcs1Pem, 'base64');
  60. console.log('\n手动签名(前80):', manualSign.substring(0, 80));
  61. console.log('手动签名长度:', manualSign.length);
  62. console.log('SDK签名 vs 手动签名:', sdkSign === manualSign ? '✅ 一致' : '❌ 不一致');
  63. // 方案3: 转成URL-safe base64后比较
  64. function toUrlSafe(b64) {
  65. return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
  66. }
  67. console.log('\nURL-safe签名比较:');
  68. console.log('SDK:', toUrlSafe(sdkSign.substring(0, 50)));
  69. console.log('手动:', toUrlSafe(manualSign.substring(0, 50)));
  70. console.log('匹配:', toUrlSafe(sdkSign) === toUrlSafe(manualSign));
  71. // 方式4: 用应用公钥验SDK签名
  72. const appPubObj = crypto.createPublicKey({
  73. key: Buffer.from(userAppPubKey, 'base64'),
  74. format: 'der', type: 'spki'
  75. });
  76. const appPubPem = appPubObj.export({ format: 'pem', type: 'spki' });
  77. const verifySdk = crypto.createVerify('RSA-SHA256');
  78. verifySdk.update(signString);
  79. const sdkVerified = verifySdk.verify(appPubPem, Buffer.from(decodeURIComponent(sdkSign), 'base64'), 'base64');
  80. console.log('\n用应用公钥验SDK签名:', sdkVerified ? '✅ 通过' : '❌ 失败');
  81. const verifyManual = crypto.createVerify('RSA-SHA256');
  82. verifyManual.update(signString);
  83. const manualVerified = verifyManual.verify(appPubPem, Buffer.from(manualSign, 'base64'), 'base64');
  84. console.log('用应用公钥验手动签名:', manualVerified ? '✅ 通过' : '❌ 失败');
  85. // 方式5: 直接用curl(v3)试
  86. console.log('\n=== 用 curl(v3) 方式测试 ===');
  87. try {
  88. const result = await sdk.curl('POST', '/v3/alipay/trade/precreate', {
  89. body: {
  90. out_trade_no: orderNo + 'C',
  91. total_amount: '0.01',
  92. subject: 'curl测试',
  93. },
  94. timeout: 10000,
  95. });
  96. console.log('curl结果:', JSON.stringify(result).substring(0, 300));
  97. } catch (e) {
  98. console.log('curl错误:', e.message);
  99. }