wxpay-nodejs-sdk 0.2.2 → 0.2.3
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/dist/index.d.mts +153 -6
- package/dist/index.d.ts +153 -6
- package/dist/index.js +190 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +181 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -181,13 +181,8 @@ var CertificateManager = class {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const platform = os.platform();
|
|
187
|
-
const arch = os.arch();
|
|
188
|
-
const nodeVersion = process.version;
|
|
189
|
-
return `wxpay-nodejs-sdk/${SDK_VERSION} (${platform} ${arch}) Node.js/${nodeVersion}`;
|
|
190
|
-
}
|
|
184
|
+
|
|
185
|
+
// src/utils/exceptions.ts
|
|
191
186
|
var WxPayError = class extends Error {
|
|
192
187
|
/** HTTP 状态码 */
|
|
193
188
|
status;
|
|
@@ -210,7 +205,64 @@ var WxPayError = class extends Error {
|
|
|
210
205
|
get isServerError() {
|
|
211
206
|
return this.status >= 500 && this.status < 600;
|
|
212
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* 判断是否为特定错误码
|
|
210
|
+
*
|
|
211
|
+
* @param code - 错误码
|
|
212
|
+
* @returns 是否匹配
|
|
213
|
+
*/
|
|
214
|
+
isApiError(code) {
|
|
215
|
+
return this.detail.code === code;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var ServiceException = class extends WxPayError {
|
|
219
|
+
/** 微信支付业务错误码 */
|
|
220
|
+
errorCode;
|
|
221
|
+
/** 微信支付业务错误信息 */
|
|
222
|
+
errorMessage;
|
|
223
|
+
constructor(status, headers, detail) {
|
|
224
|
+
super(status, headers, detail);
|
|
225
|
+
this.name = "ServiceException";
|
|
226
|
+
this.errorCode = detail.code;
|
|
227
|
+
this.errorMessage = detail.message;
|
|
228
|
+
}
|
|
213
229
|
};
|
|
230
|
+
var HttpException = class extends WxPayError {
|
|
231
|
+
constructor(message, cause) {
|
|
232
|
+
super(0, {}, { code: "NETWORK_ERROR", message });
|
|
233
|
+
this.name = "HttpException";
|
|
234
|
+
if (cause) {
|
|
235
|
+
this.cause = cause;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
var ValidationException = class extends WxPayError {
|
|
240
|
+
constructor(message) {
|
|
241
|
+
super(0, {}, { code: "SIGN_ERROR", message });
|
|
242
|
+
this.name = "ValidationException";
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
var DecryptionException = class extends WxPayError {
|
|
246
|
+
constructor(message) {
|
|
247
|
+
super(0, {}, { code: "DECRYPT_ERROR", message });
|
|
248
|
+
this.name = "DecryptionException";
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
var MalformedMessageException = class extends WxPayError {
|
|
252
|
+
constructor(message) {
|
|
253
|
+
super(0, {}, { code: "PARSE_ERROR", message });
|
|
254
|
+
this.name = "MalformedMessageException";
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/utils/http.ts
|
|
259
|
+
var SDK_VERSION = "0.2.1";
|
|
260
|
+
function getUserAgent() {
|
|
261
|
+
const platform = os.platform();
|
|
262
|
+
const arch = os.arch();
|
|
263
|
+
const nodeVersion = process.version;
|
|
264
|
+
return `wxpay-nodejs-sdk/${SDK_VERSION} (${platform} ${arch}) Node.js/${nodeVersion}`;
|
|
265
|
+
}
|
|
214
266
|
function buildUrl(base, path, params) {
|
|
215
267
|
const url = new URL(path, base);
|
|
216
268
|
if (params) {
|
|
@@ -272,7 +324,7 @@ async function parseResponse(response, verify) {
|
|
|
272
324
|
message: `HTTP ${response.status}: ${response.statusText}`
|
|
273
325
|
};
|
|
274
326
|
}
|
|
275
|
-
throw new
|
|
327
|
+
throw new ServiceException(response.status, headers, errorDetail);
|
|
276
328
|
}
|
|
277
329
|
if (verify) {
|
|
278
330
|
const signature = headers["wechatpay-signature"];
|
|
@@ -282,10 +334,7 @@ async function parseResponse(response, verify) {
|
|
|
282
334
|
if (signature && timestamp && nonce && serial) {
|
|
283
335
|
const valid = verify(rawBody, signature, timestamp, nonce, serial);
|
|
284
336
|
if (!valid) {
|
|
285
|
-
throw new
|
|
286
|
-
code: "SIGN_ERROR",
|
|
287
|
-
message: "\u5E94\u7B54\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25"
|
|
288
|
-
});
|
|
337
|
+
throw new ValidationException("\u5E94\u7B54\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25");
|
|
289
338
|
}
|
|
290
339
|
}
|
|
291
340
|
}
|
|
@@ -297,10 +346,9 @@ async function parseResponse(response, verify) {
|
|
|
297
346
|
}
|
|
298
347
|
data = parsed;
|
|
299
348
|
} catch (error) {
|
|
300
|
-
throw new
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
});
|
|
349
|
+
throw new MalformedMessageException(
|
|
350
|
+
error instanceof Error ? error.message : "\u54CD\u5E94\u6570\u636E\u89E3\u6790\u5931\u8D25"
|
|
351
|
+
);
|
|
304
352
|
}
|
|
305
353
|
return {
|
|
306
354
|
status: response.status,
|
|
@@ -318,6 +366,12 @@ ${body}
|
|
|
318
366
|
`;
|
|
319
367
|
}
|
|
320
368
|
function sign(signString, privateKey) {
|
|
369
|
+
if (!signString) {
|
|
370
|
+
throw new Error("\u7B7E\u540D\u4E32\u4E0D\u80FD\u4E3A\u7A7A");
|
|
371
|
+
}
|
|
372
|
+
if (!privateKey) {
|
|
373
|
+
throw new Error("\u5546\u6237\u79C1\u94A5\u4E0D\u80FD\u4E3A\u7A7A");
|
|
374
|
+
}
|
|
321
375
|
const signer = crypto2.createSign("RSA-SHA256");
|
|
322
376
|
signer.update(signString);
|
|
323
377
|
signer.end();
|
|
@@ -337,6 +391,18 @@ function isTimestampValid(timestamp) {
|
|
|
337
391
|
return Math.abs(now - responseTime) < RESPONSE_EXPIRED_SECONDS;
|
|
338
392
|
}
|
|
339
393
|
function verifySignature(body, signature, timestamp, nonce, publicKey) {
|
|
394
|
+
if (!signature) {
|
|
395
|
+
throw new Error("\u7B7E\u540D\u503C(signature)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
396
|
+
}
|
|
397
|
+
if (!timestamp) {
|
|
398
|
+
throw new Error("\u65F6\u95F4\u6233(timestamp)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
399
|
+
}
|
|
400
|
+
if (!nonce) {
|
|
401
|
+
throw new Error("\u968F\u673A\u4E32(nonce)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
402
|
+
}
|
|
403
|
+
if (!publicKey) {
|
|
404
|
+
throw new Error("\u516C\u94A5(publicKey)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
405
|
+
}
|
|
340
406
|
if (!isTimestampValid(timestamp)) {
|
|
341
407
|
return false;
|
|
342
408
|
}
|
|
@@ -350,6 +416,12 @@ ${body}
|
|
|
350
416
|
return verifier.verify(publicKey, signature, "base64");
|
|
351
417
|
}
|
|
352
418
|
function oaepEncrypt(plaintext, publicKey) {
|
|
419
|
+
if (!publicKey) {
|
|
420
|
+
throw new Error("\u52A0\u5BC6\u516C\u94A5\u4E0D\u80FD\u4E3A\u7A7A");
|
|
421
|
+
}
|
|
422
|
+
if (!plaintext) {
|
|
423
|
+
return "";
|
|
424
|
+
}
|
|
353
425
|
const encrypted = crypto2.publicEncrypt(
|
|
354
426
|
{
|
|
355
427
|
key: publicKey,
|
|
@@ -361,6 +433,12 @@ function oaepEncrypt(plaintext, publicKey) {
|
|
|
361
433
|
return encrypted.toString("base64");
|
|
362
434
|
}
|
|
363
435
|
function oaepDecrypt(ciphertext, privateKey) {
|
|
436
|
+
if (!privateKey) {
|
|
437
|
+
throw new Error("\u89E3\u5BC6\u79C1\u94A5\u4E0D\u80FD\u4E3A\u7A7A");
|
|
438
|
+
}
|
|
439
|
+
if (!ciphertext) {
|
|
440
|
+
return "";
|
|
441
|
+
}
|
|
364
442
|
const decrypted = crypto2.privateDecrypt(
|
|
365
443
|
{
|
|
366
444
|
key: privateKey,
|
|
@@ -775,6 +853,48 @@ var WxPayClient = class _WxPayClient {
|
|
|
775
853
|
};
|
|
776
854
|
}
|
|
777
855
|
};
|
|
856
|
+
|
|
857
|
+
// src/utils/sensitive.ts
|
|
858
|
+
var sensitiveFields = /* @__PURE__ */ new Map();
|
|
859
|
+
function registerSensitiveFields(typeName, fields) {
|
|
860
|
+
sensitiveFields.set(typeName, new Set(fields));
|
|
861
|
+
}
|
|
862
|
+
function getSensitiveFields(typeName) {
|
|
863
|
+
return sensitiveFields.get(typeName) ?? /* @__PURE__ */ new Set();
|
|
864
|
+
}
|
|
865
|
+
function encryptSensitiveFields(obj, typeName, publicKey) {
|
|
866
|
+
const fields = getSensitiveFields(typeName);
|
|
867
|
+
if (fields.size === 0) return obj;
|
|
868
|
+
const result = { ...obj };
|
|
869
|
+
for (const field of fields) {
|
|
870
|
+
const value = result[field];
|
|
871
|
+
if (typeof value === "string" && value.length > 0) {
|
|
872
|
+
result[field] = oaepEncrypt(value, publicKey);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return result;
|
|
876
|
+
}
|
|
877
|
+
function decryptSensitiveFields(obj, typeName, privateKey) {
|
|
878
|
+
const fields = getSensitiveFields(typeName);
|
|
879
|
+
if (fields.size === 0) return obj;
|
|
880
|
+
const result = { ...obj };
|
|
881
|
+
for (const field of fields) {
|
|
882
|
+
const value = result[field];
|
|
883
|
+
if (typeof value === "string" && value.length > 0) {
|
|
884
|
+
try {
|
|
885
|
+
result[field] = oaepDecrypt(value, privateKey);
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return result;
|
|
891
|
+
}
|
|
892
|
+
function encryptSensitiveFieldsInArray(arr, typeName, publicKey) {
|
|
893
|
+
return arr.map((item) => encryptSensitiveFields(item, typeName, publicKey));
|
|
894
|
+
}
|
|
895
|
+
function decryptSensitiveFieldsInArray(arr, typeName, privateKey) {
|
|
896
|
+
return arr.map((item) => decryptSensitiveFields(item, typeName, privateKey));
|
|
897
|
+
}
|
|
778
898
|
var CertificateService = class {
|
|
779
899
|
client;
|
|
780
900
|
apiV3Key;
|
|
@@ -2636,6 +2756,8 @@ var BillService = class {
|
|
|
2636
2756
|
}
|
|
2637
2757
|
};
|
|
2638
2758
|
var SUPPORTED_SIGNATURE_TYPES = /* @__PURE__ */ new Set(["WECHATPAY2-SHA256-RSA2048"]);
|
|
2759
|
+
var DEFAULT_SIGNATURE_TYPE = "WECHATPAY2-SHA256-RSA2048";
|
|
2760
|
+
var SUPPORTED_ALGORITHMS = /* @__PURE__ */ new Set(["AEAD_AES_256_GCM"]);
|
|
2639
2761
|
var CallbackHandler = class {
|
|
2640
2762
|
apiV3Key;
|
|
2641
2763
|
certificates;
|
|
@@ -2652,14 +2774,26 @@ var CallbackHandler = class {
|
|
|
2652
2774
|
* @param headers - 回调请求头
|
|
2653
2775
|
* @param body - 回调请求体(原始 JSON 字符串)
|
|
2654
2776
|
* @returns 签名验证是否通过
|
|
2655
|
-
* @throws
|
|
2777
|
+
* @throws 如果必填参数缺失、签名类型不支持或找不到对应的证书
|
|
2656
2778
|
*/
|
|
2657
2779
|
verifySignature(headers, body) {
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2780
|
+
if (!headers["wechatpay-serial"]) {
|
|
2781
|
+
throw new Error("\u56DE\u8C03\u5934 wechatpay-serial \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2782
|
+
}
|
|
2783
|
+
if (!headers["wechatpay-signature"]) {
|
|
2784
|
+
throw new Error("\u56DE\u8C03\u5934 wechatpay-signature \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2785
|
+
}
|
|
2786
|
+
if (!headers["wechatpay-timestamp"]) {
|
|
2787
|
+
throw new Error("\u56DE\u8C03\u5934 wechatpay-timestamp \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2788
|
+
}
|
|
2789
|
+
if (!headers["wechatpay-nonce"]) {
|
|
2790
|
+
throw new Error("\u56DE\u8C03\u5934 wechatpay-nonce \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2791
|
+
}
|
|
2792
|
+
const signatureType = headers["wechatpay-signature-type"] ?? DEFAULT_SIGNATURE_TYPE;
|
|
2793
|
+
if (!SUPPORTED_SIGNATURE_TYPES.has(signatureType)) {
|
|
2661
2794
|
throw new Error(`\u4E0D\u652F\u6301\u7684\u7B7E\u540D\u7C7B\u578B: ${signatureType}`);
|
|
2662
2795
|
}
|
|
2796
|
+
const serialNo = headers["wechatpay-serial"];
|
|
2663
2797
|
const publicKey = this.certificates.getPublicKey(serialNo);
|
|
2664
2798
|
if (!publicKey) {
|
|
2665
2799
|
throw new Error(`\u672A\u627E\u5230\u5E8F\u5217\u53F7\u4E3A ${serialNo} \u7684\u5E73\u53F0\u8BC1\u4E66\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u914D\u7F6E\u5E73\u53F0\u8BC1\u4E66`);
|
|
@@ -2679,10 +2813,22 @@ var CallbackHandler = class {
|
|
|
2679
2813
|
*
|
|
2680
2814
|
* @param notification - 回调通知 JSON 对象
|
|
2681
2815
|
* @returns 解密后的业务数据
|
|
2682
|
-
* @throws
|
|
2816
|
+
* @throws 如果通知结构无效、算法不匹配或解密失败
|
|
2683
2817
|
*/
|
|
2684
2818
|
decryptNotification(notification) {
|
|
2685
2819
|
const { resource } = notification;
|
|
2820
|
+
if (!resource.algorithm) {
|
|
2821
|
+
throw new Error("\u56DE\u8C03\u901A\u77E5 resource \u7F3A\u5C11 algorithm \u5B57\u6BB5");
|
|
2822
|
+
}
|
|
2823
|
+
if (!resource.ciphertext) {
|
|
2824
|
+
throw new Error("\u56DE\u8C03\u901A\u77E5 resource \u7F3A\u5C11 ciphertext \u5B57\u6BB5");
|
|
2825
|
+
}
|
|
2826
|
+
if (!resource.nonce) {
|
|
2827
|
+
throw new Error("\u56DE\u8C03\u901A\u77E5 resource \u7F3A\u5C11 nonce \u5B57\u6BB5");
|
|
2828
|
+
}
|
|
2829
|
+
if (!SUPPORTED_ALGORITHMS.has(resource.algorithm)) {
|
|
2830
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684\u52A0\u5BC6\u7B97\u6CD5: ${resource.algorithm}`);
|
|
2831
|
+
}
|
|
2686
2832
|
const plaintext = this.aesGcmDecrypt(
|
|
2687
2833
|
resource.ciphertext,
|
|
2688
2834
|
resource.associated_data,
|
|
@@ -2930,10 +3076,23 @@ var CallbackHandler = class {
|
|
|
2930
3076
|
* @param associatedData - 附加数据(用于 AEAD 认证)
|
|
2931
3077
|
* @param nonce - 随机串
|
|
2932
3078
|
* @returns 解密后的明文字符串
|
|
3079
|
+
* @throws 如果密文格式无效或解密失败
|
|
2933
3080
|
*/
|
|
2934
3081
|
aesGcmDecrypt(ciphertext, associatedData, nonce) {
|
|
3082
|
+
if (!ciphertext) {
|
|
3083
|
+
throw new Error("\u5BC6\u6587(ciphertext)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
3084
|
+
}
|
|
3085
|
+
if (!nonce) {
|
|
3086
|
+
throw new Error("\u968F\u673A\u4E32(nonce)\u4E0D\u80FD\u4E3A\u7A7A");
|
|
3087
|
+
}
|
|
3088
|
+
if (this.apiV3Key.length !== 32) {
|
|
3089
|
+
throw new Error("APIv3 \u5BC6\u94A5\u65E0\u6548\uFF0C\u957F\u5EA6\u5FC5\u987B\u4E3A 32 \u4E2A\u5B57\u8282");
|
|
3090
|
+
}
|
|
2935
3091
|
const key = Buffer.from(this.apiV3Key, "utf-8");
|
|
2936
3092
|
const ciphertextBuffer = Buffer.from(ciphertext, "base64");
|
|
3093
|
+
if (ciphertextBuffer.length <= 16) {
|
|
3094
|
+
throw new Error("\u5BC6\u6587\u957F\u5EA6\u65E0\u6548\uFF0C\u5FC5\u987B\u5927\u4E8E 16 \u5B57\u8282\uFF08\u542B AuthTag\uFF09");
|
|
3095
|
+
}
|
|
2937
3096
|
const authTag = ciphertextBuffer.subarray(-16);
|
|
2938
3097
|
const encryptedData = ciphertextBuffer.subarray(0, -16);
|
|
2939
3098
|
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, Buffer.from(nonce, "utf-8"));
|
|
@@ -4716,6 +4875,6 @@ ${packageStr}
|
|
|
4716
4875
|
};
|
|
4717
4876
|
}
|
|
4718
4877
|
|
|
4719
|
-
export { AppService, BillService, BusinessCircleService, CallbackHandler, CertificateManager, CertificateService, CombineAppService, CombineH5Service, CombineMiniProgramService, CombineNativeService, CombineService, ComplaintService, CouponService, GoldPlanService, H5Service, JsapiService, LoveFeastService, MedInsService, MediaService, MerchantExclusiveCouponService, MerchantTransferService, NativeService, ParkingService, PartnershipService, PayGiftActivityService, PayScoreService, PayrollCardService, ProfitSharingService, RetailStoreService, ScanAndRideService, SecurityService, SmartGuideService, WxPayClient, WxPayError, buildAppBridgeConfig, buildAuthorization, buildH5CouponUrl, buildJsapiBridgeConfig, buildMedInsJsapiBridgeConfig, buildMedInsMiniProgramBridgeConfig, buildMerchantTransferAuthorizationJsapiBridgeConfig, buildMerchantTransferJsapiBridgeConfig, buildMerchantTransferMiniProgramBridgeConfig, buildMiniProgramBridgeConfig, buildParkingAppBridgePath, buildParkingH5BridgeUrl, buildParkingMiniProgramBridgeConfig, buildParkingRepayBridgeConfig, buildPayScoreAppBridgeConfig, buildPayScoreDetailAppBridgeConfig, buildPayScoreDetailJsapiBridgeConfig, buildPayScoreDetailMiniProgramBridgeConfig, buildPayScoreJsapiBridgeConfig, buildPayScoreMiniProgramBridgeConfig, buildSignString, generateAppPaySign, generateNonce, generateNonceStr, generatePayScorePaySign, generatePaySign, isTimestampValid, oaepDecrypt, oaepEncrypt, sign, verifySignature };
|
|
4878
|
+
export { AppService, BillService, BusinessCircleService, CallbackHandler, CertificateManager, CertificateService, CombineAppService, CombineH5Service, CombineMiniProgramService, CombineNativeService, CombineService, ComplaintService, CouponService, DecryptionException, GoldPlanService, H5Service, HttpException, JsapiService, LoveFeastService, MalformedMessageException, MedInsService, MediaService, MerchantExclusiveCouponService, MerchantTransferService, NativeService, ParkingService, PartnershipService, PayGiftActivityService, PayScoreService, PayrollCardService, ProfitSharingService, RetailStoreService, ScanAndRideService, SecurityService, ServiceException, SmartGuideService, ValidationException, WxPayClient, WxPayError, buildAppBridgeConfig, buildAuthorization, buildH5CouponUrl, buildJsapiBridgeConfig, buildMedInsJsapiBridgeConfig, buildMedInsMiniProgramBridgeConfig, buildMerchantTransferAuthorizationJsapiBridgeConfig, buildMerchantTransferJsapiBridgeConfig, buildMerchantTransferMiniProgramBridgeConfig, buildMiniProgramBridgeConfig, buildParkingAppBridgePath, buildParkingH5BridgeUrl, buildParkingMiniProgramBridgeConfig, buildParkingRepayBridgeConfig, buildPayScoreAppBridgeConfig, buildPayScoreDetailAppBridgeConfig, buildPayScoreDetailJsapiBridgeConfig, buildPayScoreDetailMiniProgramBridgeConfig, buildPayScoreJsapiBridgeConfig, buildPayScoreMiniProgramBridgeConfig, buildSignString, decryptSensitiveFields, decryptSensitiveFieldsInArray, encryptSensitiveFields, encryptSensitiveFieldsInArray, generateAppPaySign, generateNonce, generateNonceStr, generatePayScorePaySign, generatePaySign, isTimestampValid, oaepDecrypt, oaepEncrypt, registerSensitiveFields, sign, verifySignature };
|
|
4720
4879
|
//# sourceMappingURL=index.mjs.map
|
|
4721
4880
|
//# sourceMappingURL=index.mjs.map
|