首次完整推送,

V:1.20240808.006
This commit is contained in:
fm453
2024-08-13 18:32:37 +08:00
parent 15be3e9373
commit c62d15b288
939 changed files with 111777 additions and 0 deletions

View File

@ -0,0 +1,111 @@
const {
callWxOpenApi,
buildUrl
} = require('../normalize')
module.exports = class Auth {
constructor (options) {
this.options = Object.assign({
baseUrl: 'https://api.weixin.qq.com',
timeout: 5000
}, options)
}
async _requestWxOpenapi ({ name, url, data, options }) {
const defaultOptions = {
method: 'GET',
dataType: 'json',
dataAsQueryString: true,
timeout: this.options.timeout
}
const result = await callWxOpenApi({
name: `auth.${name}`,
url: `${this.options.baseUrl}${buildUrl(url, data)}`,
data,
options,
defaultOptions
})
return result
}
async code2Session (code) {
const url = '/sns/jscode2session'
const result = await this._requestWxOpenapi({
name: 'code2Session',
url,
data: {
grant_type: 'authorization_code',
appid: this.options.appId,
secret: this.options.secret,
js_code: code
}
})
return result
}
async getOauthAccessToken (code) {
const url = '/sns/oauth2/access_token'
const result = await this._requestWxOpenapi({
name: 'getOauthAccessToken',
url,
data: {
grant_type: 'authorization_code',
appid: this.options.appId,
secret: this.options.secret,
code
}
})
if (result.expiresIn) {
result.expired = Date.now() + result.expiresIn * 1000
// delete result.expiresIn
}
return result
}
async getUserInfo ({
accessToken,
openid
} = {}) {
const url = '/sns/userinfo'
const {
nickname,
headimgurl: avatar
} = await this._requestWxOpenapi({
name: 'getUserInfo',
url,
data: {
accessToken,
openid,
appid: this.options.appId,
secret: this.options.secret,
scope: 'snsapi_userinfo'
}
})
return {
nickname,
avatar
}
}
async getPhoneNumber (accessToken, code) {
const url = `/wxa/business/getuserphonenumber?access_token=${accessToken}`
const { phoneInfo } = await this._requestWxOpenapi({
name: 'getPhoneNumber',
url,
data: {
code
},
options: {
method: 'POST',
dataAsQueryString: false,
headers: {
'content-type': 'application/json'
}
}
})
return {
purePhoneNumber: phoneInfo.purePhoneNumber
}
}
}

View File

@ -0,0 +1,95 @@
const {
UniCloudError
} = require('../../../common/error')
const {
camel2snakeJson, snake2camelJson
} = require('../../../common/utils')
function generateApiResult (apiName, data) {
if (data.errcode) {
throw new UniCloudError({
code: data.errcode || -2,
message: data.errmsg || `${apiName} fail`
})
} else {
delete data.errcode
delete data.errmsg
return {
...data,
errMsg: `${apiName} ok`,
errCode: 0
}
}
}
function nomalizeError (apiName, error) {
throw new UniCloudError({
code: error.code || -2,
message: error.message || `${apiName} fail`
})
}
// 微信openapi接口接收蛇形snake case参数返回蛇形参数这里进行转化如果是formdata里面的参数需要在对应api实现时就转为蛇形
async function callWxOpenApi ({
name,
url,
data,
options,
defaultOptions
}) {
let result = {}
// 获取二维码的接口wxacode.get和wxacode.getUnlimited不可以传入access_token可能有其他接口也不可以否则会返回data format error
const dataCopy = camel2snakeJson(Object.assign({}, data))
if (dataCopy && dataCopy.access_token) {
delete dataCopy.access_token
}
try {
options = Object.assign({}, defaultOptions, options, { data: dataCopy })
result = await uniCloud.httpclient.request(url, options)
} catch (e) {
return nomalizeError(name, e)
}
// 有几个接口成功返回buffer失败返回json对这些接口统一成返回buffer然后分别解析
let resData = result.data
const contentType = result.headers['content-type']
if (
Buffer.isBuffer(resData) &&
(contentType.indexOf('text/plain') === 0 ||
contentType.indexOf('application/json') === 0)
) {
try {
resData = JSON.parse(resData.toString())
} catch (e) {
resData = resData.toString()
}
} else if (Buffer.isBuffer(resData)) {
resData = {
buffer: resData,
contentType
}
}
return snake2camelJson(
generateApiResult(
name,
resData || {
errCode: -2,
errMsg: 'Request failed'
}
)
)
}
function buildUrl (url, data) {
let query = ''
if (data && data.accessToken) {
const divider = url.indexOf('?') > -1 ? '&' : '?'
query = `${divider}access_token=${data.accessToken}`
}
return `${url}${query}`
}
module.exports = {
callWxOpenApi,
buildUrl
}

View File

@ -0,0 +1,87 @@
const crypto = require('crypto')
const {
isPlainObject
} = require('../../../common/utils')
// 退款通知解密key=md5(key)
function decryptData (encryptedData, key, iv = '') {
// 解密
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv)
// 设置自动 padding 为 true删除填充补位
decipher.setAutoPadding(true)
let decoded = decipher.update(encryptedData, 'base64', 'utf8')
decoded += decipher.final('utf8')
return decoded
}
function md5 (str, encoding = 'utf8') {
return crypto
.createHash('md5')
.update(str, encoding)
.digest('hex')
}
function sha256 (str, key, encoding = 'utf8') {
return crypto
.createHmac('sha256', key)
.update(str, encoding)
.digest('hex')
}
function getSignStr (obj) {
return Object.keys(obj)
.filter(key => key !== 'sign' && obj[key] !== undefined && obj[key] !== '')
.sort()
.map(key => key + '=' + obj[key])
.join('&')
}
function getNonceStr (length = 16) {
let str = ''
while (str.length < length) {
str += Math.random().toString(32).substring(2)
}
return str.substring(0, length)
}
// 简易版Object转XML只可在微信支付时使用不支持嵌套
function buildXML (obj, rootName = 'xml') {
const content = Object.keys(obj).map(item => {
if (isPlainObject(obj[item])) {
return `<${item}><![CDATA[${JSON.stringify(obj[item])}]]></${item}>`
} else {
return `<${item}><![CDATA[${obj[item]}]]></${item}>`
}
})
return `<${rootName}>${content.join('')}</${rootName}>`
}
function isXML (str) {
const reg = /^(<\?xml.*\?>)?(\r?\n)*<xml>(.|\r?\n)*<\/xml>$/i
return reg.test(str.trim())
};
// 简易版XML转Object只可在微信支付时使用不支持嵌套
function parseXML (xml) {
const xmlReg = /<(?:xml|root).*?>([\s|\S]*)<\/(?:xml|root)>/
const str = xmlReg.exec(xml)[1]
const obj = {}
const nodeReg = /<(.*?)>(?:<!\[CDATA\[){0,1}(.*?)(?:\]\]>){0,1}<\/.*?>/g
let matches = null
// eslint-disable-next-line no-cond-assign
while ((matches = nodeReg.exec(str))) {
obj[matches[1]] = matches[2]
}
return obj
}
module.exports = {
decryptData,
md5,
sha256,
getSignStr,
getNonceStr,
buildXML,
parseXML,
isXML
}