feat: 集成支付宝电脑网站支付(alipay direct)

- 新增 src/lib/alipay/ 模块:RSA2 签名、网关客户端、AlipayProvider
- 新增 /api/alipay/notify 异步通知回调路由
- config.ts 添加 ALIPAY_* 环境变量
- payment/index.ts 注册 alipaydirect 提供商
- 27 个单元测试全部通过
This commit is contained in:
erio
2026-03-05 01:48:10 +08:00
parent 9a90a7ebb9
commit 55756744a1
9 changed files with 749 additions and 0 deletions

42
src/lib/alipay/sign.ts Normal file
View File

@@ -0,0 +1,42 @@
import crypto from 'crypto';
/** 自动补全 PEM 格式PKCS8 */
function formatPrivateKey(key: string): string {
if (key.includes('-----BEGIN')) return key;
return `-----BEGIN PRIVATE KEY-----\n${key}\n-----END PRIVATE KEY-----`;
}
function formatPublicKey(key: string): string {
if (key.includes('-----BEGIN')) return key;
return `-----BEGIN PUBLIC KEY-----\n${key}\n-----END PUBLIC KEY-----`;
}
/** 生成 RSA2 签名 */
export function generateSign(params: Record<string, string>, privateKey: string): string {
const filtered = Object.entries(params)
.filter(
([key, value]) => key !== 'sign' && key !== 'sign_type' && value !== '' && value !== undefined && value !== null,
)
.sort(([a], [b]) => a.localeCompare(b));
const signStr = filtered.map(([key, value]) => `${key}=${value}`).join('&');
const signer = crypto.createSign('RSA-SHA256');
signer.update(signStr);
return signer.sign(formatPrivateKey(privateKey), 'base64');
}
/** 用支付宝公钥验证签名 */
export function verifySign(params: Record<string, string>, alipayPublicKey: string, sign: string): boolean {
const filtered = Object.entries(params)
.filter(
([key, value]) => key !== 'sign' && key !== 'sign_type' && value !== '' && value !== undefined && value !== null,
)
.sort(([a], [b]) => a.localeCompare(b));
const signStr = filtered.map(([key, value]) => `${key}=${value}`).join('&');
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(signStr);
return verifier.verify(formatPublicKey(alipayPublicKey), sign, 'base64');
}