node-easywechat 3.5.8 → 3.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v3.5.9 (2023-12-18)
5
+
6
+ - Feat: 微信支付增加读取平台证书的方法loadPlatformCerts,并自动写入商户配置 (#57)
7
+ - Feat: 微信支付增加v2版本的消息验证方法validateV2 (#56)
8
+
4
9
  ## v3.5.8 (2023-12-18)
5
10
 
6
11
  - Feat: thirdpartyCode2Session方法重命名为code2Session (#54)
@@ -82,8 +82,8 @@ class AES_GCM {
82
82
  else {
83
83
  buf = Buffer.from(ciphertext);
84
84
  }
85
- let tag = buf.slice(-16);
86
- let payload = buf.slice(0, -16);
85
+ let tag = buf.subarray(-16);
86
+ let payload = buf.subarray(0, -16);
87
87
  let decipher = (0, crypto_1.createDecipheriv)(method, key, iv).setAuthTag(tag).setAAD(Buffer.from(aad));
88
88
  return Buffer.concat([
89
89
  decipher.update(payload),
@@ -1,7 +1,8 @@
1
1
  /// <reference types="node" />
2
2
  export declare class PublicKey {
3
3
  protected certificate: Buffer;
4
- constructor(certificate: string);
4
+ protected serialNo: string;
5
+ constructor(certificate: string, serialNo?: string);
5
6
  /**
6
7
  * 获取公钥的序列号
7
8
  * @returns
@@ -17,4 +18,10 @@ export declare class PublicKey {
17
18
  * @returns
18
19
  */
19
20
  toString(): string;
21
+ /**
22
+ * 通过内容创建实例
23
+ * @param content 证书内容
24
+ * @param serialNo 证书序列号
25
+ */
26
+ static createByCertificateContent(content: string, serialNo: string): PublicKey;
20
27
  }
@@ -7,8 +7,15 @@ exports.PublicKey = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const crypto_1 = require("crypto");
9
9
  class PublicKey {
10
- constructor(certificate) {
11
- if (fs_1.default.existsSync(certificate)) {
10
+ constructor(certificate, serialNo = '') {
11
+ if (serialNo) {
12
+ if (!certificate) {
13
+ throw new Error('Invalid PublicKey content');
14
+ }
15
+ this.certificate = Buffer.from(certificate);
16
+ this.serialNo = serialNo;
17
+ }
18
+ else if (fs_1.default.existsSync(certificate)) {
12
19
  this.certificate = fs_1.default.readFileSync(certificate) || Buffer.from('');
13
20
  }
14
21
  else {
@@ -20,12 +27,15 @@ class PublicKey {
20
27
  * @returns
21
28
  */
22
29
  getSerialNo() {
23
- try {
24
- return new crypto_1.X509Certificate(this.certificate).serialNumber;
25
- }
26
- catch (e) {
27
- throw new Error('Read the $certificate failed, please check it whether or nor correct');
30
+ if (!this.serialNo) {
31
+ try {
32
+ this.serialNo = new crypto_1.X509Certificate(this.certificate).serialNumber;
33
+ }
34
+ catch (e) {
35
+ throw new Error('Read the $certificate failed, please check it whether or nor correct');
36
+ }
28
37
  }
38
+ return this.serialNo;
29
39
  }
30
40
  /**
31
41
  * 获取证书内容
@@ -41,5 +51,13 @@ class PublicKey {
41
51
  toString() {
42
52
  return this.certificate.toString();
43
53
  }
54
+ /**
55
+ * 通过内容创建实例
56
+ * @param content 证书内容
57
+ * @param serialNo 证书序列号
58
+ */
59
+ static createByCertificateContent(content, serialNo) {
60
+ return new PublicKey(content, serialNo);
61
+ }
44
62
  }
45
63
  exports.PublicKey = PublicKey;
@@ -60,6 +60,11 @@ declare class Application implements ApplicationInterface {
60
60
  * @returns
61
61
  */
62
62
  protected getHttpClientDefaultOptions(): Record<string, any>;
63
+ /**
64
+ * 如果未配置平台证书,则尝试从接口读取
65
+ * @param force 是否强制读取,默认:false
66
+ */
67
+ loadPlatformCerts(force?: boolean): Promise<void>;
63
68
  }
64
69
  interface Application extends ConfigMixin, CacheMixin, ServerRequestMixin, HttpClientMixin {
65
70
  }
@@ -1,4 +1,13 @@
1
1
  'use strict';
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
@@ -15,6 +24,8 @@ const Utils_2 = __importDefault(require("./Utils"));
15
24
  const Config_1 = __importDefault(require("../OfficialAccount/Config"));
16
25
  const Client_1 = __importDefault(require("./Client"));
17
26
  const Validator_1 = __importDefault(require("./Validator"));
27
+ const PublicKey_1 = require("../Core/Support/PublicKey");
28
+ const AES_1 = require("../Core/Support/AES");
18
29
  /**
19
30
  * 微信支付应用
20
31
  */
@@ -117,6 +128,28 @@ class Application {
117
128
  baseURL: 'https://api.mch.weixin.qq.com',
118
129
  }, this.getConfig().get('http'));
119
130
  }
131
+ /**
132
+ * 如果未配置平台证书,则尝试从接口读取
133
+ * @param force 是否强制读取,默认:false
134
+ */
135
+ loadPlatformCerts(force = false) {
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ let exists_certs = this.config.get('platform_certs', []);
138
+ if (force || !exists_certs || exists_certs.length === 0) {
139
+ let response = yield this.getClient().get('/v3/certificates');
140
+ let data = response.toObject();
141
+ if (data && data.length > 0) {
142
+ let certs = {};
143
+ let key = this.config.get('secret_key');
144
+ data.forEach((item) => {
145
+ let content = AES_1.AES_GCM.decrypt(item.encrypt_certificate.ciphertext, key, item.encrypt_certificate.nonce, item.encrypt_certificate.associated_data).toString();
146
+ certs[item.serial_no] = PublicKey_1.PublicKey.createByCertificateContent(content, item.serial_no);
147
+ });
148
+ this.getMerchant().setPlatformCerts(certs);
149
+ }
150
+ }
151
+ });
152
+ }
120
153
  }
121
154
  ;
122
155
  ;
@@ -31,5 +31,10 @@ declare abstract class MerchantInterface {
31
31
  * @returns
32
32
  */
33
33
  getPlatformCert(serial: string): PublicKey;
34
+ /**
35
+ * 设置平台证书
36
+ * @param certs 键名:序列号,键值:PublicKey实例
37
+ */
38
+ setPlatformCerts(certs: Record<string, PublicKey>): void;
34
39
  }
35
40
  export = MerchantInterface;
@@ -30,6 +30,11 @@ class MerchantInterface {
30
30
  * @returns
31
31
  */
32
32
  getPlatformCert(serial) { return null; }
33
+ /**
34
+ * 设置平台证书
35
+ * @param certs 键名:序列号,键值:PublicKey实例
36
+ */
37
+ setPlatformCerts(certs) { }
33
38
  }
34
39
  ;
35
40
  module.exports = MerchantInterface;
@@ -1,8 +1,13 @@
1
1
  import Request from "../../Core/Http/Request";
2
+ import Message from "../Message";
2
3
  declare abstract class ValidatorInterface {
3
4
  /**
4
5
  * 验证请求是否正确
5
6
  */
6
7
  validate(request: Request): boolean;
8
+ /**
9
+ * 验证请求是否正确(v2)
10
+ */
11
+ validateV2(message: Message): boolean;
7
12
  }
8
13
  export = ValidatorInterface;
@@ -4,6 +4,10 @@ class ValidatorInterface {
4
4
  * 验证请求是否正确
5
5
  */
6
6
  validate(request) { return true; }
7
+ /**
8
+ * 验证请求是否正确(v2)
9
+ */
10
+ validateV2(message) { return true; }
7
11
  }
8
12
  ;
9
13
  module.exports = ValidatorInterface;
@@ -21,5 +21,6 @@ declare class Merchant implements MerchantInterface {
21
21
  getV2SecretKey(): string;
22
22
  getCertificate(): PublicKey;
23
23
  getPlatformCert(serial: string): PublicKey;
24
+ setPlatformCerts(certs: Record<string, PublicKey>): void;
24
25
  }
25
26
  export = Merchant;
@@ -60,5 +60,8 @@ class Merchant {
60
60
  var _a;
61
61
  return (_a = this.platformCerts[serial]) !== null && _a !== void 0 ? _a : null;
62
62
  }
63
+ setPlatformCerts(certs) {
64
+ this.platformCerts = certs;
65
+ }
63
66
  }
64
67
  module.exports = Merchant;
@@ -1,6 +1,7 @@
1
1
  import ValidatorInterface from "./Contracts/ValidatorInterface";
2
2
  import MessageInterface from "../Core/Http/Contracts/MessageInterface";
3
3
  import MerchantInterface from "./Contracts/MerchantInterface";
4
+ import Message from "./Message";
4
5
  declare class Validator implements ValidatorInterface {
5
6
  protected merchant: MerchantInterface;
6
7
  static MAX_ALLOWED_CLOCK_OFFSET: number;
@@ -10,5 +11,6 @@ declare class Validator implements ValidatorInterface {
10
11
  static HEADER_SIGNATURE: string;
11
12
  constructor(merchant: MerchantInterface);
12
13
  validate(request: MessageInterface): boolean;
14
+ validateV2(message: Message): boolean;
13
15
  }
14
16
  export = Validator;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  const Utils_1 = require("../Core/Support/Utils");
6
6
  const RSA_1 = __importDefault(require("../Core/Support/RSA"));
7
+ const Utils_2 = __importDefault(require("./Utils"));
7
8
  class Validator {
8
9
  constructor(merchant) {
9
10
  this.merchant = merchant;
@@ -39,6 +40,18 @@ class Validator {
39
40
  }
40
41
  return true;
41
42
  }
43
+ validateV2(message) {
44
+ let messageSign = (message.get('sign') + '' || '').toUpperCase();
45
+ if (!messageSign) {
46
+ throw new Error('Missing Signature');
47
+ }
48
+ const utils = new Utils_2.default(this.merchant);
49
+ let calculateSign = utils.createV2Signature(message.toObject());
50
+ if (messageSign !== calculateSign) {
51
+ throw new Error('Invalid Signature');
52
+ }
53
+ return true;
54
+ }
42
55
  }
43
56
  Validator.MAX_ALLOWED_CLOCK_OFFSET = 300;
44
57
  Validator.HEADER_TIMESTAMP = 'Wechatpay-Timestamp';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-easywechat",
3
- "version": "3.5.8",
3
+ "version": "3.5.9",
4
4
  "description": "EasyWechat SDK for Node.js (NOT OFFICIAL)",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {