import { config } from 'dotenv'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import crypto from 'crypto'; const __dirname = dirname(fileURLToPath(import.meta.url)); config({ path: resolve(__dirname, '.env') }); const { AlipaySdk } = await import('alipay-sdk'); const appId = process.env.ALIPAY_APP_ID; const rawPrivateKey = process.env.ALIPAY_PRIVATE_KEY; const rawPublicKey = process.env.ALIPAY_PUBLIC_KEY; // 转 PKCS#1 PEM const pkcs8Buf = Buffer.from(rawPrivateKey, 'base64'); const privObj = crypto.createPrivateKey({ key: pkcs8Buf, format: 'der', type: 'pkcs8' }); const pkcs1Pem = privObj.export({ format: 'pem', type: 'pkcs1' }); const alipayPubPem = rawPublicKey.includes('BEGIN') ? rawPublicKey : `-----BEGIN PUBLIC KEY-----\n${rawPublicKey}\n-----END PUBLIC KEY-----`; // 用户应用公钥 const userAppPubKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu/fGftv/79Cj7MEonMxcv6Br+zPfRQoEOJ4eIvxfDrVg7N+/k7P+Rw9CaOMMYK5Shy7VJrBAx9oV59v9PMI9FdnAzYFHtiKzjq6ZmolZwXM44pZoXlGQ7ZxtuiN/FshN74me80OWXzU3FcUR9YG6UguddvdtlYvZRUScnCJabbys7KL9dUbRNvTQ6CnD8U6OoDGveISxFbA0aCa6BgZRo8Nuz28p8HYHI2+FEWnFDuE/GRU57mGXk8mR6siReNVlCMEX80Uq5RVG+6EXxeCwtt3lxCwE7aS/tvGOZOAsgOriDjyidNgrJgoPlY2Mb1fyRMPLJZvyFCYiwH46MvL39wIDAQAB'; // 方式1: SDK 签名 const sdk = new AlipaySdk({ appId, privateKey: pkcs1Pem, keyType: 'PKCS1', alipayPublicKey: alipayPubPem, signType: 'RSA2', }); const orderNo = 'DIAG' + Date.now(); const form = sdk.pageExecute('alipay.trade.wap.pay', 'POST', { bizContent: { out_trade_no: orderNo, total_amount: '0.01', subject: '诊断测试', product_code: 'QUICK_WAP_WAY', }, returnUrl: 'https://book.rrbrr.com/#/pages/payment-result/index', notifyUrl: 'https://book.rrbrr.com/api/payment/alipay/notify', }); const formStr = String(form); const sdkSign = formStr.match(/sign=([^"&\s]+)/)?.[1] || ''; console.log('SDK签名(前80):', sdkSign.substring(0, 80)); console.log('SDK签名长度:', sdkSign.length); // 从URL中提取验签字符串 const actionUrl = formStr.match(/action="([^"]+)"/)?.[1] || ''; const urlParams = new URL(actionUrl); const paramMap = {}; urlParams.searchParams.forEach((v, k) => { if (k !== 'sign') paramMap[k] = v; }); // 构建验签字符串(按支付宝规则:参数排序后用&连接) const sortedParams = Object.keys(paramMap).sort().map(k => `${k}=${paramMap[k]}`).join('&'); const signString = decodeURIComponent(sortedParams); console.log('\nSDK生成的验签字符串:', signString.substring(0, 200)); // 方式2: 手动RSA2签名 const decoder = new TextDecoder(); const signStrBytes = new TextEncoder().encode(signString); const sign2 = crypto.createSign('RSA-SHA256'); sign2.update(signString); const manualSign = sign2.sign(pkcs1Pem, 'base64'); console.log('\n手动签名(前80):', manualSign.substring(0, 80)); console.log('手动签名长度:', manualSign.length); console.log('SDK签名 vs 手动签名:', sdkSign === manualSign ? '✅ 一致' : '❌ 不一致'); // 方案3: 转成URL-safe base64后比较 function toUrlSafe(b64) { return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } console.log('\nURL-safe签名比较:'); console.log('SDK:', toUrlSafe(sdkSign.substring(0, 50))); console.log('手动:', toUrlSafe(manualSign.substring(0, 50))); console.log('匹配:', toUrlSafe(sdkSign) === toUrlSafe(manualSign)); // 方式4: 用应用公钥验SDK签名 const appPubObj = crypto.createPublicKey({ key: Buffer.from(userAppPubKey, 'base64'), format: 'der', type: 'spki' }); const appPubPem = appPubObj.export({ format: 'pem', type: 'spki' }); const verifySdk = crypto.createVerify('RSA-SHA256'); verifySdk.update(signString); const sdkVerified = verifySdk.verify(appPubPem, Buffer.from(decodeURIComponent(sdkSign), 'base64'), 'base64'); console.log('\n用应用公钥验SDK签名:', sdkVerified ? '✅ 通过' : '❌ 失败'); const verifyManual = crypto.createVerify('RSA-SHA256'); verifyManual.update(signString); const manualVerified = verifyManual.verify(appPubPem, Buffer.from(manualSign, 'base64'), 'base64'); console.log('用应用公钥验手动签名:', manualVerified ? '✅ 通过' : '❌ 失败'); // 方式5: 直接用curl(v3)试 console.log('\n=== 用 curl(v3) 方式测试 ==='); try { const result = await sdk.curl('POST', '/v3/alipay/trade/precreate', { body: { out_trade_no: orderNo + 'C', total_amount: '0.01', subject: 'curl测试', }, timeout: 10000, }); console.log('curl结果:', JSON.stringify(result).substring(0, 300)); } catch (e) { console.log('curl错误:', e.message); }