首次完整推送,
V:1.20240808.006
This commit is contained in:
111
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js
vendored
Normal file
111
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
95
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js
vendored
Normal file
95
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js
vendored
Normal 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
|
||||
}
|
87
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js
vendored
Normal file
87
uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js
vendored
Normal 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
|
||||
}
|
Reference in New Issue
Block a user