订单配置
This commit is contained in:
1 parent
e81481623c
commit
c7f43e30a9
11 files changed
+492
-19
No files matched your search
@@ -0,0 +1,42 @@
|
||||
import { createDecipheriv, createHash, timingSafeEqual } from 'node:crypto';
|
||||
|
||||
/**
|
||||
* 微信「消息推送」服务器地址相关的两个协议细节。
|
||||
*
|
||||
* 小程序后台填了 URL 之后,微信会先发一个 GET 来验签,验不过就一直显示
|
||||
* 「服务器地址验证失败」,也就配不上发货推送。虚拟支付文档说的「发货推送 URL」
|
||||
* 指的就是这个地址。
|
||||
*/
|
||||
|
||||
/** 签名 = sha1(Token 与 URL 参数按字典序排序后拼接)。安全模式下把 Encrypt 也算进去。 */
|
||||
export function wechatSignature(token: string, ...parts: string[]): string {
|
||||
return createHash('sha1').update([token, ...parts].sort().join('')).digest('hex');
|
||||
}
|
||||
|
||||
export function sameHex(a: string, b: string): boolean {
|
||||
const x = Buffer.from(a);
|
||||
const y = Buffer.from(b);
|
||||
return x.length > 0 && x.length === y.length && timingSafeEqual(x, y);
|
||||
}
|
||||
|
||||
/** 取 XML 标签文本(兼容 CDATA)。 */
|
||||
export function xmlField(xml: string, field: string): string {
|
||||
const match = new RegExp(`<${field}>(?:<!\\[CDATA\\[)?([\\s\\S]*?)(?:\\]\\]>)?</${field}>`).exec(xml);
|
||||
return match ? match[1]!.trim() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全模式:AES-256-CBC。
|
||||
* 密钥 = Base64(EncodingAESKey + '='),IV 取密钥前 16 字节。
|
||||
* 明文 = 16 字节随机数 + 4 字节长度 + msg + appId,填充是 PKCS#7(块长 32)。
|
||||
* 填充长度可能是 17~32,OpenSSL 的自动去填充只认 ≤16,所以关掉自己剥。
|
||||
*/
|
||||
export function decryptWechatMessage(encodingAesKey: string, base64: string): string {
|
||||
const key = Buffer.from(encodingAesKey + '=', 'base64');
|
||||
const decipher = createDecipheriv('aes-256-cbc', key, key.subarray(0, 16));
|
||||
decipher.setAutoPadding(false);
|
||||
const raw = Buffer.concat([decipher.update(Buffer.from(base64, 'base64')), decipher.final()]);
|
||||
const pad = raw[raw.length - 1]!;
|
||||
const body = pad >= 1 && pad <= 32 ? raw.subarray(0, raw.length - pad) : raw;
|
||||
return body.subarray(20, 20 + body.readUInt32BE(16)).toString('utf8');
|
||||
}
|
||||
Reference in new issue
Block a user