| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- 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);
- }
|