@jayfong/x-server 1.23.0 → 1.24.2

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
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.24.2](https://github.com/jfWorks/x-server/compare/v1.24.1...v1.24.2) (2022-05-02)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * returnUrl? ([fb46884](https://github.com/jfWorks/x-server/commit/fb4688405c16986727be2785695e9faa6a2db386))
11
+ * verifyNotifyData ([a4b76e3](https://github.com/jfWorks/x-server/commit/a4b76e3c63babc2f789646f932b1375a1c0a76e4))
12
+
13
+ ### [1.24.1](https://github.com/jfWorks/x-server/compare/v1.24.0...v1.24.1) (2022-05-02)
14
+
15
+ ## [1.24.0](https://github.com/jfWorks/x-server/compare/v1.23.0...v1.24.0) (2022-05-02)
16
+
17
+
18
+ ### Features
19
+
20
+ * add PayService ([c8d5ecc](https://github.com/jfWorks/x-server/commit/c8d5eccf2393c1a1bf98415564032b09ffb10497))
21
+
5
22
  ## [1.23.0](https://github.com/jfWorks/x-server/compare/v1.22.0...v1.23.0) (2022-05-01)
6
23
 
7
24
 
package/lib/_cjs/index.js CHANGED
@@ -178,6 +178,14 @@ Object.keys(_mail).forEach(function (key) {
178
178
  exports[key] = _mail[key];
179
179
  });
180
180
 
181
+ var _pay = require("./services/pay");
182
+
183
+ Object.keys(_pay).forEach(function (key) {
184
+ if (key === "default" || key === "__esModule") return;
185
+ if (key in exports && exports[key] === _pay[key]) return;
186
+ exports[key] = _pay[key];
187
+ });
188
+
181
189
  var _rate_limit = require("./services/rate_limit");
182
190
 
183
191
  Object.keys(_rate_limit).forEach(function (key) {
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+
5
+ exports.__esModule = true;
6
+ exports.PayService = void 0;
7
+
8
+ var _form = _interopRequireDefault(require("alipay-sdk/lib/form"));
9
+
10
+ var _alipaySdk = _interopRequireDefault(require("alipay-sdk"));
11
+
12
+ var _crypto = _interopRequireDefault(require("crypto"));
13
+
14
+ var _got = _interopRequireDefault(require("got"));
15
+
16
+ var _http_error = require("../core/http_error");
17
+
18
+ class PayService {
19
+ constructor(options) {
20
+ this.options = options;
21
+ this.serviceName = 'pay';
22
+ this.alipaySdk = void 0;
23
+ }
24
+
25
+ async prepareAlipay(options) {
26
+ if (!this.alipaySdk) {
27
+ this.alipaySdk = new _alipaySdk.default({
28
+ appId: this.options.alipay.appId,
29
+ privateKey: this.options.alipay.privateKey,
30
+ alipayPublicKey: this.options.alipay.publicKey
31
+ });
32
+ }
33
+
34
+ const formData = new _form.default();
35
+ formData.setMethod('get');
36
+ formData.addField('notifyUrl', options.notifyUrl);
37
+
38
+ if (options.returnUrl) {
39
+ formData.addField('returnUrl', options.returnUrl);
40
+ }
41
+
42
+ formData.addField('bizContent', {
43
+ outTradeNo: options.tradeNumber,
44
+ productCode: 'FAST_INSTANT_TRADE_PAY',
45
+ totalAmount: options.totalMoney,
46
+ subject: options.goodsName,
47
+ goodsType: '0',
48
+ passbackParams: 'alipay'
49
+ });
50
+ const payUrl = await this.alipaySdk.exec('alipay.trade.page.pay', {}, {
51
+ formData: formData
52
+ });
53
+ return {
54
+ payUrl: payUrl
55
+ };
56
+ }
57
+
58
+ async prepareWepay(options) {
59
+ const res = await this.wepayRequest('https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', {
60
+ appid: this.options.wepay.appId,
61
+ mchid: this.options.wepay.merchantId,
62
+ description: options.goodsName,
63
+ out_trade_no: options.tradeNumber,
64
+ attach: 'wepay',
65
+ notify_url: options.notifyUrl,
66
+ amount: {
67
+ total: options.totalMoney * 100,
68
+ currency: 'CNY'
69
+ },
70
+ payer: {
71
+ openid: options.openid
72
+ }
73
+ });
74
+
75
+ if (!res || !res.prepay_id) {
76
+ const error = res && res.message || '支付错误';
77
+ throw new Error(error);
78
+ }
79
+
80
+ const params = {
81
+ appId: this.options.wepay.appId,
82
+ timestamp: String(Math.round(Date.now() / 1000)),
83
+ nonceStr: Math.random().toString().substr(2, 12),
84
+ package: `prepay_id=${res.prepay_id}`,
85
+ signType: 'RSA',
86
+ paySign: ''
87
+ };
88
+ params.paySign = this.wepaySign([params.appId, params.timestamp, params.nonceStr, params.package]);
89
+ return {
90
+ payParams: params
91
+ };
92
+ }
93
+
94
+ verifyNotifyData(data) {
95
+ try {
96
+ var _this$alipaySdk;
97
+
98
+ return !!data && typeof data === 'object' && ( // 支付宝
99
+ data.passback_params === 'alipay' ? !!((_this$alipaySdk = this.alipaySdk) != null && _this$alipaySdk.checkNotifySign(data)) : // 微信支付
100
+ data.resource ? JSON.parse(this.wepayDecrypt(data.resource)).attach === 'wepay' : false);
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ verifyNotifyDataOrFail(data, message) {
107
+ if (!this.verifyNotifyData(data)) {
108
+ throw new _http_error.HttpError.BadRequest(message);
109
+ }
110
+ }
111
+
112
+ wepaySign(data) {
113
+ return _crypto.default.createSign('RSA-SHA256').update(data.map(v => `${v}\n`).join('')).sign(this.options.wepay.privateKey, 'base64');
114
+ }
115
+
116
+ wepayDecrypt(payload) {
117
+ const ciphertextBuffer = Buffer.from(payload.ciphertext, 'base64');
118
+ const authTag = ciphertextBuffer.slice(ciphertextBuffer.length - 16);
119
+ const data = ciphertextBuffer.slice(0, ciphertextBuffer.length - 16);
120
+
121
+ const decipherIv = _crypto.default.createDecipheriv('aes-256-gcm', this.options.wepay.secretKey, payload.nonce);
122
+
123
+ decipherIv.setAuthTag(Buffer.from(authTag));
124
+ decipherIv.setAAD(Buffer.from(payload.associated_data));
125
+ const decryptStr = decipherIv.update(data, undefined, 'utf8');
126
+ decipherIv.final();
127
+ return decryptStr;
128
+ }
129
+
130
+ async wepayRequest(url, data) {
131
+ const path = url.replace(/^https?:\/\/[^/]+/, '');
132
+ const body = JSON.stringify(data);
133
+ const params = {
134
+ method: 'POST',
135
+ url: path,
136
+ time: String(Math.round(Date.now() / 1000)),
137
+ rand: Math.random().toString().substr(2, 12),
138
+ body: body,
139
+ signature: ''
140
+ };
141
+ params.signature = this.wepaySign([params.method, params.url, params.time, params.rand, params.body]);
142
+ const Authorization = [`WECHATPAY2-SHA256-RSA2048 `, `mchid="${this.options.wepay.merchantId}",`, `nonce_str="${params.rand}",`, `signature="${params.signature}",`, `timestamp="${params.time}",`, `serial_no="${this.options.wepay.certificateSerialNumber}"`].join('');
143
+ const res = await _got.default.post(url, {
144
+ json: data,
145
+ headers: {
146
+ Authorization
147
+ },
148
+ responseType: 'json',
149
+ resolveBodyOnly: true
150
+ });
151
+ return res;
152
+ }
153
+
154
+ }
155
+
156
+ exports.PayService = PayService;
package/lib/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export * from './services/captcha';
20
20
  export * from './services/dispose';
21
21
  export * from './services/jwt';
22
22
  export * from './services/mail';
23
+ export * from './services/pay';
23
24
  export * from './services/rate_limit';
24
25
  export * from './services/redis';
25
26
  export * from './x';
package/lib/index.js CHANGED
@@ -21,6 +21,7 @@ export * from "./services/captcha";
21
21
  export * from "./services/dispose";
22
22
  export * from "./services/jwt";
23
23
  export * from "./services/mail";
24
+ export * from "./services/pay";
24
25
  export * from "./services/rate_limit";
25
26
  export * from "./services/redis";
26
27
  export * from "./x"; // @endindex
@@ -0,0 +1,82 @@
1
+ import { BaseService } from './base';
2
+ export interface PayOptions {
3
+ /** 支付宝 */
4
+ alipay?: {
5
+ /** 应用 ID */
6
+ appId: string;
7
+ /** 商户应用私钥 */
8
+ privateKey: string;
9
+ /** 支付宝公钥,用于对返回结果验签 */
10
+ publicKey: string;
11
+ };
12
+ /** 微信支付 */
13
+ wepay?: {
14
+ /** 公众号 APPID */
15
+ appId: string;
16
+ /** 商户 ID */
17
+ merchantId: string;
18
+ /** 商户应用私钥 */
19
+ privateKey: string;
20
+ /** 证书序列号 */
21
+ certificateSerialNumber: string;
22
+ /** 秘钥,用于解密请求平台证书、回调数据 */
23
+ secretKey: string;
24
+ };
25
+ }
26
+ export interface PayPrepareAlipayOptions {
27
+ /** 商品名称 */
28
+ goodsName: string;
29
+ /** 交易单号 */
30
+ tradeNumber: string;
31
+ /** 总金额 */
32
+ totalMoney: number;
33
+ /** 通知地址(POST) */
34
+ notifyUrl: string;
35
+ /** 返回地址 */
36
+ returnUrl?: string;
37
+ }
38
+ export interface PayPrepareAlipayResult {
39
+ /** 支付地址,需跳转过去 */
40
+ payUrl: string;
41
+ }
42
+ export interface PayPrepareWepayOptions {
43
+ /** 商品名称 */
44
+ goodsName: string;
45
+ /** 交易单号 */
46
+ tradeNumber: string;
47
+ /** 总金额 */
48
+ totalMoney: number;
49
+ /** 通知地址(POST) */
50
+ notifyUrl: string;
51
+ /** 对应公众号用户的 openid */
52
+ openid: string;
53
+ }
54
+ export interface PayPrepareWepayResult {
55
+ /** 支付参数,可直接传给 JSSDK 调用 */
56
+ payParams: {
57
+ appId: string;
58
+ timestamp: string;
59
+ nonceStr: string;
60
+ package: string;
61
+ signType: string;
62
+ paySign: string;
63
+ };
64
+ }
65
+ export declare class PayService implements BaseService {
66
+ private options;
67
+ serviceName: string;
68
+ private alipaySdk;
69
+ constructor(options: PayOptions);
70
+ prepareAlipay(options: PayPrepareAlipayOptions): Promise<PayPrepareAlipayResult>;
71
+ prepareWepay(options: PayPrepareWepayOptions): Promise<PayPrepareWepayResult>;
72
+ verifyNotifyData(data: any): boolean;
73
+ verifyNotifyDataOrFail(data: any, message?: string): void;
74
+ private wepaySign;
75
+ private wepayDecrypt;
76
+ private wepayRequest;
77
+ }
78
+ declare module '../x' {
79
+ interface X {
80
+ pay: PayService;
81
+ }
82
+ }
@@ -0,0 +1,140 @@
1
+ import AlipayFormData from 'alipay-sdk/lib/form';
2
+ import AlipaySdk from 'alipay-sdk';
3
+ import crypto from 'crypto';
4
+ import got from 'got';
5
+ import { HttpError } from "../core/http_error";
6
+ export class PayService {
7
+ constructor(options) {
8
+ this.options = options;
9
+ this.serviceName = 'pay';
10
+ this.alipaySdk = void 0;
11
+ }
12
+
13
+ async prepareAlipay(options) {
14
+ if (!this.alipaySdk) {
15
+ this.alipaySdk = new AlipaySdk({
16
+ appId: this.options.alipay.appId,
17
+ privateKey: this.options.alipay.privateKey,
18
+ alipayPublicKey: this.options.alipay.publicKey
19
+ });
20
+ }
21
+
22
+ const formData = new AlipayFormData();
23
+ formData.setMethod('get');
24
+ formData.addField('notifyUrl', options.notifyUrl);
25
+
26
+ if (options.returnUrl) {
27
+ formData.addField('returnUrl', options.returnUrl);
28
+ }
29
+
30
+ formData.addField('bizContent', {
31
+ outTradeNo: options.tradeNumber,
32
+ productCode: 'FAST_INSTANT_TRADE_PAY',
33
+ totalAmount: options.totalMoney,
34
+ subject: options.goodsName,
35
+ goodsType: '0',
36
+ passbackParams: 'alipay'
37
+ });
38
+ const payUrl = await this.alipaySdk.exec('alipay.trade.page.pay', {}, {
39
+ formData: formData
40
+ });
41
+ return {
42
+ payUrl: payUrl
43
+ };
44
+ }
45
+
46
+ async prepareWepay(options) {
47
+ const res = await this.wepayRequest('https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', {
48
+ appid: this.options.wepay.appId,
49
+ mchid: this.options.wepay.merchantId,
50
+ description: options.goodsName,
51
+ out_trade_no: options.tradeNumber,
52
+ attach: 'wepay',
53
+ notify_url: options.notifyUrl,
54
+ amount: {
55
+ total: options.totalMoney * 100,
56
+ currency: 'CNY'
57
+ },
58
+ payer: {
59
+ openid: options.openid
60
+ }
61
+ });
62
+
63
+ if (!res || !res.prepay_id) {
64
+ const error = res && res.message || '支付错误';
65
+ throw new Error(error);
66
+ }
67
+
68
+ const params = {
69
+ appId: this.options.wepay.appId,
70
+ timestamp: String(Math.round(Date.now() / 1000)),
71
+ nonceStr: Math.random().toString().substr(2, 12),
72
+ package: `prepay_id=${res.prepay_id}`,
73
+ signType: 'RSA',
74
+ paySign: ''
75
+ };
76
+ params.paySign = this.wepaySign([params.appId, params.timestamp, params.nonceStr, params.package]);
77
+ return {
78
+ payParams: params
79
+ };
80
+ }
81
+
82
+ verifyNotifyData(data) {
83
+ try {
84
+ var _this$alipaySdk;
85
+
86
+ return !!data && typeof data === 'object' && ( // 支付宝
87
+ data.passback_params === 'alipay' ? !!((_this$alipaySdk = this.alipaySdk) != null && _this$alipaySdk.checkNotifySign(data)) : // 微信支付
88
+ data.resource ? JSON.parse(this.wepayDecrypt(data.resource)).attach === 'wepay' : false);
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ verifyNotifyDataOrFail(data, message) {
95
+ if (!this.verifyNotifyData(data)) {
96
+ throw new HttpError.BadRequest(message);
97
+ }
98
+ }
99
+
100
+ wepaySign(data) {
101
+ return crypto.createSign('RSA-SHA256').update(data.map(v => `${v}\n`).join('')).sign(this.options.wepay.privateKey, 'base64');
102
+ }
103
+
104
+ wepayDecrypt(payload) {
105
+ const ciphertextBuffer = Buffer.from(payload.ciphertext, 'base64');
106
+ const authTag = ciphertextBuffer.slice(ciphertextBuffer.length - 16);
107
+ const data = ciphertextBuffer.slice(0, ciphertextBuffer.length - 16);
108
+ const decipherIv = crypto.createDecipheriv('aes-256-gcm', this.options.wepay.secretKey, payload.nonce);
109
+ decipherIv.setAuthTag(Buffer.from(authTag));
110
+ decipherIv.setAAD(Buffer.from(payload.associated_data));
111
+ const decryptStr = decipherIv.update(data, undefined, 'utf8');
112
+ decipherIv.final();
113
+ return decryptStr;
114
+ }
115
+
116
+ async wepayRequest(url, data) {
117
+ const path = url.replace(/^https?:\/\/[^/]+/, '');
118
+ const body = JSON.stringify(data);
119
+ const params = {
120
+ method: 'POST',
121
+ url: path,
122
+ time: String(Math.round(Date.now() / 1000)),
123
+ rand: Math.random().toString().substr(2, 12),
124
+ body: body,
125
+ signature: ''
126
+ };
127
+ params.signature = this.wepaySign([params.method, params.url, params.time, params.rand, params.body]);
128
+ const Authorization = [`WECHATPAY2-SHA256-RSA2048 `, `mchid="${this.options.wepay.merchantId}",`, `nonce_str="${params.rand}",`, `signature="${params.signature}",`, `timestamp="${params.time}",`, `serial_no="${this.options.wepay.certificateSerialNumber}"`].join('');
129
+ const res = await got.post(url, {
130
+ json: data,
131
+ headers: {
132
+ Authorization
133
+ },
134
+ responseType: 'json',
135
+ resolveBodyOnly: true
136
+ });
137
+ return res;
138
+ }
139
+
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayfong/x-server",
3
- "version": "1.23.0",
3
+ "version": "1.24.2",
4
4
  "license": "ISC",
5
5
  "sideEffects": false,
6
6
  "main": "lib/_cjs/index.js",
@@ -33,6 +33,7 @@
33
33
  "@types/jsonwebtoken": "^8.5.8",
34
34
  "@types/nodemailer": "^6.4.4",
35
35
  "@types/ws": "^8.5.3",
36
+ "alipay-sdk": "^3.2.0",
36
37
  "bufferutil": "^4.0.6",
37
38
  "bull": "^4.8.1",
38
39
  "chokidar": "^3.5.3",
@@ -50,6 +51,7 @@
50
51
  "fastify-multipart": "^5.3.1",
51
52
  "fastify-websocket": "^4.2.2",
52
53
  "fs-extra": "^10.0.1",
54
+ "got": "^11.8.2",
53
55
  "http-errors": "^1.8.1",
54
56
  "ioredis": "^4.28.5",
55
57
  "jsonwebtoken": "^8.5.1",