@yoooloo42/beat 1.0.22 → 1.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoooloo42/beat",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -19,9 +19,11 @@
19
19
  "@alicloud/ocr20191230": "^4.0.1",
20
20
  "@alicloud/openapi-client": "^0.4.15",
21
21
  "@alicloud/pop-core": "^1.8.0",
22
- "@yoooloo42/bean": "^1.0.13",
22
+ "@yoooloo42/bean": "^1.0.14",
23
23
  "aliyun-api-gateway": "^1.1.6",
24
- "nodemailer": "^7.0.10"
24
+ "axios": "^1.13.2",
25
+ "nodemailer": "^7.0.10",
26
+ "xml2js": "^0.6.2"
25
27
  },
26
28
  "exports": {
27
29
  "./libs/*": "./src/libs/*.js",
@@ -0,0 +1,87 @@
1
+ const axios = require('axios');
2
+
3
+ /**
4
+ * @function getMiniProgramCode
5
+ * @description 获取微信小程序的不限制数量二维码(A 或 C 码)。
6
+ * @param {object} para
7
+ * @param {string} para.appid - 小程序 AppID
8
+ * @param {string} para.secret - 小程序 AppSecret
9
+ * @param {string} para.scene - 小程序码中可以附加的场景值(最大 32 个可见字符)
10
+ * @param {string} [para.page] - 必须是已经发布的小程序页面路径,例如 pages/index/index,非必填。
11
+ * @returns {Promise<object>} {code, message, data: {base64, scene}}
12
+ */
13
+ async function getMiniProgramCode(para) {
14
+ const { appid, secret, scene, page } = para;
15
+
16
+ // 1. 获取全局接口调用凭证 access_token (这里假设 getGlobalAccessToken 已定义)
17
+ // 注意:小程序和公众号共用这一个全局 access_token 接口
18
+ const tokenResult = await getGlobalAccessToken({ appid, secret });
19
+
20
+ if (tokenResult.code !== 0) {
21
+ return {
22
+ code: 1,
23
+ message: `获取接口令牌access_token失败: ${tokenResult.message}`
24
+ };
25
+ }
26
+ const access_token = tokenResult.data.access_token;
27
+
28
+ // 2. 构造请求参数和 URL
29
+ const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`;
30
+
31
+ // 微信接口要求 POST 请求体是 JSON 格式
32
+ const requestBody = {
33
+ scene: scene,
34
+ page: page || '', // 默认值,如果未提供
35
+ // width: 430, // 默认宽度,可以根据需要添加
36
+ // auto_color: false, // 默认不自动配置线条颜色,可以根据需要添加
37
+ };
38
+
39
+ try {
40
+ // 使用 axios 发送 POST 请求
41
+ const response = await axios.post(url, requestBody, {
42
+ // 关键点:指定 responseType 为 arraybuffer,以正确处理二进制图片流
43
+ responseType: 'arraybuffer',
44
+ // 指定请求头为 JSON
45
+ headers: {
46
+ 'Content-Type': 'application/json'
47
+ }
48
+ });
49
+
50
+ // 3. 检查微信返回的数据是否为错误信息(错误信息是 JSON 格式)
51
+ const contentType = response.headers['content-type'];
52
+
53
+ // 微信接口返回图片成功时,contentType 是 image/jpeg;返回错误时,是 application/json
54
+ if (contentType && contentType.includes('application/json')) {
55
+ // 将 ArrayBuffer 转换为字符串,然后解析 JSON 错误信息
56
+ const errorBuffer = Buffer.from(response.data);
57
+ const errorData = JSON.parse(errorBuffer.toString('utf8'));
58
+
59
+ return {
60
+ code: 1,
61
+ message: `获取微信小程序码失败: ${errorData.errmsg || '未知错误'}`,
62
+ error_data: errorData
63
+ };
64
+ }
65
+
66
+ // 4. 处理成功返回的图片流
67
+ // response.data 是一个 Buffer 对象 (Node.js 环境下)
68
+ const imageBuffer = Buffer.from(response.data);
69
+
70
+ return {
71
+ code: 0,
72
+ message: '获取微信小程序码成功',
73
+ data: {
74
+ // 将二进制 Buffer 转换为 Base64 字符串,并去除 Base64 编码中可能存在的换行符
75
+ base64: imageBuffer.toString('base64').replace(/[\r\n]/g, ''),
76
+ scene: scene
77
+ }
78
+ };
79
+
80
+ } catch (error) {
81
+ // 处理网络请求等底层错误
82
+ return {
83
+ code: 2,
84
+ message: `网络请求失败: ${error.message}`
85
+ };
86
+ }
87
+ }
@@ -0,0 +1,265 @@
1
+ import axios from 'axios'
2
+
3
+ /**
4
+ * @function getGlobalAccessToken
5
+ * @description 获取微信公众号/开放平台全局接口调用凭证 access_token。
6
+ * 此 token 需缓存,用于调用绝大多数基础 API。
7
+ * @param {object} para
8
+ * @param {string} para.appid - 微信 AppID
9
+ * @param {string} para.secret - 微信 AppSecret
10
+ * @returns {Promise<object>} {code, message, data: {access_token, expires_in}}
11
+ */
12
+
13
+ /*使用环境与说明
14
+ 使用环境: Node.js 服务器后端
15
+ 适用场景:
16
+ 1. 需要调用微信公众号/开放平台基础 API 时(如发送模板消息、自定义菜单、上传素材等)。
17
+ 2. 此 access_token 与用户无关,代表应用本身的权限。
18
+ 重要提示:
19
+ 微信限制了获取频率,请务必将其缓存起来(例如,在 Redis 或数据库中),并在 2 小时有效期内复用。
20
+ */
21
+
22
+ async function getGlobalAccessToken(para) {
23
+ const { appid, secret } = para;
24
+ const url = 'https://api.weixin.qq.com/cgi-bin/token';
25
+
26
+ try {
27
+ const response = await axios.get(url, {
28
+ params: {
29
+ grant_type: 'client_credential',
30
+ appid: appid,
31
+ secret: secret
32
+ }
33
+ });
34
+
35
+ const data = response.data;
36
+
37
+ // 检查微信接口返回的错误码(通常是 errcode 字段)
38
+ if (data.errcode) {
39
+ return {
40
+ code: 1,
41
+ message: `获取 access_token 失败: ${data.errmsg || '未知错误'}`,
42
+ error_data: data
43
+ };
44
+ }
45
+
46
+ return {
47
+ code: 0,
48
+ message: '获取 access_token 成功',
49
+ data: {
50
+ access_token: data.access_token,
51
+ expires_in: data.expires_in // 建议一并返回有效期,方便缓存
52
+ }
53
+ };
54
+
55
+ } catch (error) {
56
+ // 处理网络请求等底层错误
57
+ return {
58
+ code: 2,
59
+ message: `网络请求失败: ${error.message}`
60
+ };
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @function getMiniProgramSession
66
+ * @description 获取小程序用户的 openid 和 session_key。
67
+ * @param {object} para
68
+ * @param {string} para.appid - 小程序 AppID
69
+ * @param {string} para.secret - 小程序 AppSecret
70
+ * @param {string} para.js_code - 小程序端 wx.login() 获得的登录凭证 code
71
+ * @returns {Promise<object>} {code, message, data: {openid, session_key, unionid}}
72
+ */
73
+
74
+ /*使用环境与说明
75
+ 使用环境: Node.js 服务器后端(接收小程序前端传来的 js_code)
76
+ 适用场景:
77
+ 1. 小程序登录/注册:通过 openid 识别并建立或关联您的系统用户。
78
+ 2. 解密数据:使用 session_key 解密小程序加密的用户敏感数据(如手机号、完整用户信息)。
79
+ 重要提示:
80
+ 程序中将您的参数 grant_type=programmerization_code 修正为微信官方文档中的 grant_type=authorization_code。
81
+ */
82
+
83
+ async function getMiniProgramSession(para) {
84
+ const { appid, secret, js_code } = para;
85
+ const url = 'https://api.weixin.qq.com/sns/jscode2session';
86
+
87
+ try {
88
+ const response = await axios.get(url, {
89
+ params: {
90
+ appid: appid,
91
+ secret: secret,
92
+ js_code: js_code,
93
+ grant_type: 'authorization_code' // 微信官方文档中这个字段是 'authorization_code'
94
+ }
95
+ });
96
+
97
+ const data = response.data;
98
+
99
+ // 检查微信接口返回的错误码
100
+ if (data.errcode) {
101
+ return {
102
+ code: 1,
103
+ message: `获取用户会话失败: ${data.errmsg || '未知错误'}`,
104
+ error_data: data
105
+ };
106
+ }
107
+
108
+ // 成功时返回 openid 和 session_key
109
+ return {
110
+ code: 0,
111
+ message: '获取用户会话成功',
112
+ data: {
113
+ openid: data.openid,
114
+ session_key: data.session_key,
115
+ unionid: data.unionid // 可能会有 unionid
116
+ }
117
+ };
118
+
119
+ } catch (error) {
120
+ return {
121
+ code: 2,
122
+ message: `网络请求失败: ${error.message}`
123
+ };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * @function getWebOAuthToken
129
+ * @description 通过用户授权 code 获取网页授权 access_token 和 openid。
130
+ * @param {object} para
131
+ * @param {string} para.appid - 微信 AppID
132
+ * @param {string} para.secret - 微信 AppSecret
133
+ * @param {string} para.code - 网页授权后重定向回来的 code
134
+ * @returns {Promise<object>} {code, message, data: {openid, access_token, scope, refresh_token}}
135
+ */
136
+
137
+ /*使用环境与说明
138
+ 使用环境: Node.js 服务器后端(接收公众号/开放平台网页重定向传来的 code)
139
+ 适用场景:
140
+ 1. 公众号网页授权登录:获取用户身份 openid。
141
+ 2. 获取用户基本信息:使用返回的 access_token(用户级别)去调用 /sns/userinfo 接口获取用户的昵称、头像等。
142
+ 重要提示:
143
+ 此处的 access_token 不能用于调用基础 API(如发模板消息),它只用于获取该用户的信息。
144
+ */
145
+
146
+ async function getWebOAuth2Token(para) {
147
+ const { appid, secret, code } = para;
148
+ const url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
149
+
150
+ try {
151
+ const response = await axios.get(url, {
152
+ params: {
153
+ appid: appid,
154
+ secret: secret,
155
+ code: code,
156
+ grant_type: 'authorization_code'
157
+ }
158
+ });
159
+
160
+ const data = response.data;
161
+
162
+ // 检查微信接口返回的错误码
163
+ if (data.errcode) {
164
+ return {
165
+ code: 1,
166
+ message: `获取网页授权信息失败: ${data.errmsg || '未知错误'}`,
167
+ error_data: data
168
+ };
169
+ }
170
+
171
+ // 成功时返回 openid 和网页授权 access_token
172
+ return {
173
+ code: 0,
174
+ message: '获取网页授权信息成功',
175
+ data: {
176
+ openid: data.openid,
177
+ access_token: data.access_token,
178
+ expires_in: data.expires_in,
179
+ scope: data.scope,
180
+ refresh_token: data.refresh_token // 网页授权 token 可刷新
181
+ }
182
+ };
183
+
184
+ } catch (error) {
185
+ return {
186
+ code: 2,
187
+ message: `网络请求失败: ${error.message}`
188
+ };
189
+ }
190
+ }
191
+
192
+ /**
193
+ * @function getUserInfo
194
+ * @description 通过网页授权 access_token 和 openid 获取用户的基本信息(昵称、头像等)。
195
+ * 此接口要求用户在网页授权时 scope 必须为 snsapi_userinfo。
196
+ * @param {object} para
197
+ * @param {string} para.access_token - 网页授权 access_token (由 /sns/oauth2/access_token 接口获取)
198
+ * @param {string} para.openid - 微信用户在公众号/应用中的唯一标识
199
+ * @param {string} [para.lang='zh_CN'] - 返回国家地区语言版本,可选值:zh_CN, zh_TW, en,默认为 zh_CN
200
+ * @returns {Promise<object>} {code, message, data: object}
201
+ */
202
+ async function getUserInfo(para) {
203
+ const { access_token, openid, lang = 'zh_CN' } = para;
204
+ const url = 'https://api.weixin.qq.com/sns/userinfo';
205
+
206
+ // 检查必需参数是否存在
207
+ if (!access_token || !openid) {
208
+ return {
209
+ code: 3,
210
+ message: '缺少必要的 access_token 或 openid 参数'
211
+ };
212
+ }
213
+
214
+ try {
215
+ // 使用 axios 发起 GET 请求,参数通过 params 传递,更安全规范
216
+ const response = await axios.get(url, {
217
+ params: {
218
+ access_token: access_token,
219
+ openid: openid,
220
+ lang: lang
221
+ }
222
+ });
223
+
224
+ const data = response.data;
225
+
226
+ // 规范错误处理:检查微信接口返回的 errcode 字段(网页授权接口错误时会返回)
227
+ if (data.errcode) {
228
+ return {
229
+ code: 1,
230
+ message: `获取微信用户信息失败: ${data.errmsg || '未知错误'}`,
231
+ error_data: data // 包含微信返回的错误码和信息
232
+ };
233
+ }
234
+
235
+ // 成功时,微信返回的数据对象中会包含 openid, nickname 等字段
236
+ if (!data.nickname) {
237
+ // 尽管没有 errcode,但如果返回数据不完整,也视为失败
238
+ return {
239
+ code: 1,
240
+ message: '获取微信用户信息失败: 返回数据不完整 (缺少昵称)'
241
+ };
242
+ }
243
+
244
+ // 成功返回用户信息对象
245
+ return {
246
+ code: 0,
247
+ message: '获取微信用户信息成功',
248
+ data: data // data 中包含了 openid, nickname, sex, province, city, country, headimgurl, unionid 等信息
249
+ };
250
+
251
+ } catch (error) {
252
+ // 处理网络请求等底层错误(如 DNS 解析失败、连接超时等)
253
+ return {
254
+ code: 2,
255
+ message: `网络请求失败: ${error.message}`
256
+ };
257
+ }
258
+ }
259
+
260
+ export default {
261
+ getGlobalAccessToken,
262
+ getMiniProgramSession,
263
+ getWebOAuth2Token,
264
+ getUserInfo
265
+ }
@@ -0,0 +1,50 @@
1
+ 进一步解释一下**access_token (全局) 和 access_token (网页授权) 的具体区别**
2
+
3
+ 全局 access_token 和 网页授权 access_token 虽然名字相同,但它们的作用域、权限和获取方式有着本质的区别。
4
+
5
+ 特性 | 全局 access_token | 网页授权 access_token
6
+ 全称 | 公众号/应用全局接口调用凭证 | 用户网页授权接口调用凭证
7
+ 代表对象 | 公众号/应用本身(你的服务器) | 特定用户
8
+ 作用域 | 全局性,适用于公众号/应用层面。 | 用户级,仅与授权的那个用户相关。
9
+ 主要用途 | 调用所有不依赖用户身份的微信 API,实现公众号功能。 | 仅用于获取该授权用户的 OpenID 和个人信息(昵称、头像等)。
10
+ 获取方式 | 使用 AppID 和 AppSecret (应用身份凭证) 直接请求。 | 用户在网页上同意授权后,使用返回的 code 码请求。
11
+ 获取接口 | /cgi-bin/token | /sns/oauth2/access_token
12
+ 生命周期 | 2小时,需服务器定时刷新和缓存。 | 2小时,但伴随 refresh_token,可以刷新续期。
13
+ 权限高低 | 高权限。可以发送模板消息、创建菜单、上传素材等。 | 低权限。只能在用户授权范围内获取用户信息。
14
+ 是否返回 openid | 否 | 是
15
+
16
+ 详细解释
17
+ 1. 全局 access_token
18
+ 全局 access_token 是你微信公众号或开放平台应用在服务器端调用微信 API 的**“身份令牌”**。
19
+ 🔑 权限和用途:
20
+ 身份代表: 它代表的是你的公众号或应用本身。
21
+ 调用范围: 你可以用它来调用所有与用户无关,或者需要公众号权限的接口。
22
+ 示例:
23
+ 发送客服消息、发送模板消息(给任何关注者)。
24
+ 创建、查询、删除自定义菜单。
25
+ 上传、下载素材。
26
+ 获取公众号二维码等。
27
+ ⚠️ 缓存机制:
28
+ 它只有 2 小时有效期。
29
+ 微信对获取频率有严格限制(通常是 2000 次/天)。
30
+ 因此,它必须由你的服务器缓存起来,并在接近过期时自动刷新。
31
+ 2. 网页授权 access_token
32
+ 网页授权 access_token 是用户在微信浏览器中访问你的网页时,授权给你获取其信息的凭证。
33
+ 🔑 权限和用途:
34
+ 身份代表: 它代表的是授权的这个用户。
35
+ 调用范围: 它的权限非常有限,只能用于调用与该用户相关的 sns 接口。
36
+ 示例:
37
+ 获取该用户的 OpenID(在获取时已返回)。
38
+ 如果授权范围是 snsapi_userinfo,
39
+ 则可以调用 /sns/userinfo 接口获取该用户的昵称、头像等基本信息。
40
+ 无法用于发送模板消息或创建菜单等公众号功能。
41
+ 🔁 刷新机制:
42
+ 它也有 2 小时有效期。
43
+ 但与全局 access_token 不同,它在获取时还会同时返回一个 refresh_token。
44
+ 你可以使用 refresh_token 来刷新网页授权 access_token,从而延长该用户信息的授权时效。
45
+ 混用误区
46
+ 绝对不能用网页授权 access_token 去调用全局接口(如发模板消息),反之亦然。
47
+ ✅ 正确流程:
48
+ 全局功能: 服务器定时获取、缓存全局 access_token,用于发送模板消息。
49
+ 登录功能: 用户访问网页,通过 code 换取网页授权 access_token 和 openid,用于识别用户和获取用户信息。
50
+ 总而言之,全局 access_token 是应用级别权限,网页授权 access_token 是用户级别权限。
@@ -0,0 +1,77 @@
1
+ // v2客户付款码付款
2
+
3
+ import req from 'request'
4
+ import xml2js from 'xml2js'
5
+ import Hash from '../crypto/Hash.js'
6
+ import random from '@yoooloo42/bean/utils/random'
7
+ const xmlToJson = (new xml2js.Parser()).parseString // xml to json
8
+ const para_global = {
9
+ nonce_str32: random.random(32, '0123456789abcdefghijklmnopqrstuvwxyz') // 32位的随机字符串
10
+ }
11
+
12
+ function v2micropay(para){
13
+ // para.appid 微信开放平台或微信公众平台应用id(APPID)
14
+ // para.mchid 商户号
15
+ // para.apikey V2接口密钥
16
+ // para.auth_code 付款码
17
+ // para.total_fee 金额(人民币分)
18
+ // para.body 商品描述
19
+ // para.out_trade_no 订单号(支付记录ID)
20
+ // para.spbill_create_ip 发起支付的终端设备IP地址
21
+ // sign_type: 签名类型,默认:MD5
22
+ // para.nonce_str 32位的随机字符串
23
+
24
+ return new Promise((resolve, reject) => {
25
+ let nonce_str = para.nonce_str ? para.nonce_str : para_global.nonce_str32
26
+
27
+ // Md5签名:参数名按ASCII升序排列拼接
28
+ let textBody = '<appid>' + para.appid + '</appid>' +
29
+ '<auth_code>' + para.auth_code + '</auth_code>' +
30
+ '<body>' + para.body + '</body>' +
31
+ '<mch_id>' + para.mchid + '</mch_id>' +
32
+ '<nonce_str>' + nonce_str + '</nonce_str>' +
33
+ '<out_trade_no>' + para.out_trade_no + '</out_trade_no>' +
34
+ // '<sign_type>MD5</sign_type>' +
35
+ '<spbill_create_ip>' + para.spbill_create_ip + '</spbill_create_ip>' +
36
+ '<total_fee>' + para.total_fee + '</total_fee>',
37
+ textSign = 'appid=' + para.appid +
38
+ '&auth_code=' + para.auth_code +
39
+ '&body=' + para.body +
40
+ '&mch_id=' + para.mchid +
41
+ '&nonce_str=' + nonce_str +
42
+ '&out_trade_no=' + para.out_trade_no +
43
+ // '&sign_type=MD5' +
44
+ '&spbill_create_ip=' + para.spbill_create_ip +
45
+ '&total_fee=' + para.total_fee +
46
+ '&key=' + para.apikey
47
+ let sign = Hash.md5(textSign).toUpperCase()
48
+ textBody = '<xml>' + textBody + '<sign>' + sign + '</sign>' + '</xml>'
49
+
50
+ req({
51
+ url: "https://api.mch.weixin.qq.com/pay/micropay",
52
+ method: "POST",
53
+ body: textBody
54
+ }, function (err, response, rtn_body) {
55
+ if (err) throw err
56
+
57
+ xmlToJson(rtn_body, function (err, result) {
58
+ let rtn_bodyJson = result.xml
59
+ console.log("支付回调结果:", rtn_bodyJson);
60
+
61
+ if (rtn_bodyJson.return_code[0] === 'SUCCESS'){
62
+ resolve({code: 0, message: '支付成功',
63
+ rtn_bodyJson
64
+ })
65
+ } else {
66
+ resolve({code: 1, message: rtn_bodyJson.return_msg[0],
67
+ rtn_bodyJson
68
+ })
69
+ }
70
+ })
71
+ })
72
+ })
73
+ }
74
+
75
+ export {
76
+ v2micropay
77
+ }
@@ -0,0 +1,26 @@
1
+ // v3关闭订单
2
+
3
+ import v3sign from "./v3sign.js"
4
+ function v3close(para){
5
+ // para.mchid
6
+ // para.serial_no
7
+ // para.private_key
8
+ // para.out_trade_no
9
+
10
+ return new Promise(function (resolve, reject) {
11
+ v3sign.v3sign({
12
+ host: 'https://api.mch.weixin.qq.com',
13
+ url: '/v3/pay/transactions/out-trade-no/' + para.out_trade_no + '/close',
14
+ method: "POST",
15
+ mchid: para.mchid,
16
+ serial_no: para.serial_no,
17
+ private_key: para.private_key
18
+ }).then(function () {
19
+ resolve({code: 0, message: ''})
20
+ })
21
+ })
22
+ }
23
+
24
+ export {
25
+ v3close
26
+ }
@@ -0,0 +1,72 @@
1
+ // v3客户微信号付款
2
+ // 获取prepay_id(预支付交易会话标识)
3
+
4
+ import random from '@yoooloo42/bean/utils/random'
5
+ import RSA from '../crypto/RSA.js'
6
+ import v3sign from "./v3sign.js"
7
+
8
+ function v3jsapi(para){
9
+ // para.appid 微信开放平台或微信公众平台应用id(APPID)
10
+ // para.mchid 商户号
11
+ // para.serial_no 证书序列号
12
+ // para.private_key 证书私钥
13
+ // para.openid 微信用户在某一应用(APPID)中的唯一标识
14
+ // para.amount 金额(人民币分)
15
+ // para.description 商品描述
16
+ // para.out_trade_no 订单号(支付记录ID)
17
+ // para.notify_url 异步接收支付结果通知的回调地址
18
+
19
+
20
+ return new Promise(function (resolve, reject) {
21
+ let nonce_str = random.random(32, '0123456789abcdefghijklmnopqrstuvwxyz'), // 32位的随机字符串
22
+ timestamp_seconds = Math.floor(new Date().getTime() / 1000) // 秒级的时间戳
23
+
24
+ v3sign.v3sign({
25
+ host: 'https://api.mch.weixin.qq.com',
26
+ url: '/v3/pay/transactions/jsapi',
27
+ method: 'POST',
28
+ body: {
29
+ appid: para.appid,
30
+ mchid: para.mchid,
31
+ out_trade_no: para.out_trade_no,
32
+ amount: {total: para.amount},
33
+ description: para.description,
34
+ notify_url: para.notify_url,
35
+ payer: {openid: para.openid}
36
+ },
37
+ mchid: para.mchid,
38
+ serial_no: para.serial_no,
39
+ private_key: para.private_key
40
+ }).then(function (result) {
41
+ if (!result.objV3Result || !result.objV3Result.prepay_id) {
42
+ return resolve({
43
+ code: 1,
44
+ message: '获取 prepay_id 失败:' + result.objV3Result.message
45
+ })
46
+ }
47
+
48
+ // RSA-SHA256 签名
49
+ let packageText = 'prepay_id=' + result.objV3Result.prepay_id
50
+ let text = appid + '\n' +
51
+ timestamp_seconds + '\n' +
52
+ nonce_str + '\n' +
53
+ packageText + '\n'
54
+ let paySign = RSA.rsaSign({text, privateKey: para.private_key})
55
+
56
+ resolve({code: 0, message: '获取 prepay_id 成功',
57
+ data: {
58
+ appid: para.appid,
59
+ timeStamp: timestamp_seconds,
60
+ nonceStr: nonce_str,
61
+ package: packageText,
62
+ signType: 'RSA',
63
+ paySign
64
+ }
65
+ })
66
+ })
67
+ })
68
+ }
69
+
70
+ export {
71
+ v3jsapi
72
+ }
@@ -0,0 +1,47 @@
1
+ // v3商户二维码收款
2
+ // 获取code_url
3
+
4
+ import v3sign from "./v3sign.js"
5
+ function v3native(para){
6
+ // para.appid 微信开放平台或微信公众平台应用id(APPID)
7
+ // para.mchid 商户号
8
+ // para.serial_no 证书序列号
9
+ // para.private_key 证书私钥
10
+ // para.amount 金额(人民币分)
11
+ // para.description 商品描述
12
+ // para.out_trade_no 订单号(支付记录ID)
13
+ // para.notify_url 异步接收支付结果通知的回调地址
14
+
15
+ return new Promise(function (resolve, reject) {
16
+ v3sign.v3sign({
17
+ host: "https://api.mch.weixin.qq.com",
18
+ url: "/v3/pay/transactions/native",
19
+ method: "POST",
20
+ body: {
21
+ appid: para.appid,
22
+ mchid: para.mchid,
23
+ amount: {total: Number(para.amount)},
24
+ description: para.description,
25
+ out_trade_no: para.out_trade_no,
26
+ notify_url: para.notify_url
27
+ },
28
+ mchid: para.mchid,
29
+ serial_no: para.serial_no,
30
+ private_key: para.private_key,
31
+ }).then(function (result) {
32
+ // 获取 code_url 失败
33
+ if (!result.objV3Result || !result.objV3Result.code_url) {
34
+ return resolve({code: 1, message: result.message})
35
+ }
36
+
37
+ let code_url = result.objV3Result.code_url
38
+ resolve({code: 0, message: '获取 code_url 成功',
39
+ code_url
40
+ })
41
+ })
42
+ })
43
+ }
44
+
45
+ export {
46
+ v3native
47
+ }
@@ -0,0 +1,28 @@
1
+ // v3查询订单
2
+
3
+ import v3sign from "./v3sign.js"
4
+ function v3outTradeNo(para){
5
+ // para.mchid 商户号
6
+ // para.serial_no 证书序列号
7
+ // para.private_key 证书私钥
8
+ // para.out_trade_no 订单号(支付记录ID)
9
+
10
+ return new Promise(function (resolve, reject) {
11
+ v3sign.v3sign({
12
+ host: 'https://api.mch.weixin.qq.com',
13
+ url: '/v3/pay/transactions/out-trade-no/' + para.out_trade_no + '?mchid=' + para.mchid,
14
+ method: 'GET',
15
+ mchid: para.mchid,
16
+ serial_no: para.serial_no,
17
+ private_key: para.private_key
18
+ }).then(function (result) {
19
+ resolve({code: 0, message: '订单查询成功',
20
+ transaction: result.objV3Result
21
+ })
22
+ })
23
+ })
24
+ }
25
+
26
+ export {
27
+ v3outTradeNo
28
+ }
@@ -0,0 +1,66 @@
1
+ // v3签名:所有的v3请求都由这里向微信支付接口的后台发出
2
+
3
+ import req from 'request'
4
+ import random from '@yoooloo42/bean/utils/random'
5
+ import RSA from '../crypto/RSA.js'
6
+ const para_global = {
7
+ nonce_str32: random.random(32, '0123456789abcdefghijklmnopqrstuvwxyz') // 32位的随机字符串
8
+ }
9
+
10
+ function v3sign(para){
11
+ // para.host 接口域名
12
+ // para.url 接口地址
13
+ // para.method 请求方式
14
+ // para.body 签名内容
15
+ // para.mchid 商户号
16
+ // para.serial_no 证书序列号
17
+ // para.private_key 证书私钥
18
+ // para.nonce_str 32位的随机字符串
19
+ let method = para.method ? para.method : "POST"
20
+
21
+ return new Promise(function (resolve, reject) {
22
+ let nonce_str = para.nonce_str ? para.nonce_str : para_global.nonce_str32,
23
+ timestamp_seconds = Math.floor(new Date().getTime() / 1000) // 秒级的时间戳
24
+
25
+ let strBody = para.body ? JSON.stringify(para.body) : ""
26
+
27
+ // '\n' === String.fromCharCode(0x0A)
28
+ let text = method + String.fromCharCode(0x0A) +
29
+ para.url + '\n' +
30
+ timestamp_seconds + '\n' +
31
+ nonce_str + '\n' +
32
+ strBody + '\n'
33
+
34
+ // RSA-SHA256签名
35
+ let signature = RSA.rsaSign({text, privateKey: para.private_key})
36
+ req({
37
+ url: para.host + para.url,
38
+ headers: {
39
+ 'Accept': 'application/json',
40
+ 'Content-Type': 'application/json',
41
+ 'User-Agent': 'Chrome/17.0.963.56',
42
+ 'Authorization': 'WECHATPAY2-SHA256-RSA2048' + ' ' +
43
+ 'serial_no="' + para.serial_no + '",' +
44
+ 'mchid="' + para.mchid + '",' +
45
+ 'nonce_str="' + nonce_str + '",' +
46
+ 'timestamp="' + timestamp_seconds + '",' +
47
+ 'signature="' + signature + '"'
48
+ },
49
+ method,
50
+ body: strBody
51
+ }, function (err, res, body) {
52
+ if(!body){
53
+ return resolve({code: 1, message: "v3签名失败"})
54
+ }
55
+
56
+ let objV3Result = JSON.parse(body)
57
+ resolve({code: 0, message: "v3签名完成",
58
+ objV3Result
59
+ })
60
+ })
61
+ })
62
+ }
63
+
64
+ export default {
65
+ v3sign
66
+ }