parent
b77d14b664
commit
65da76425a
@ -1,40 +0,0 @@
|
||||
name: coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ v3 ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
branches: [ v3 ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test with Coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
go mod download
|
||||
|
||||
- name: Run Unit tests
|
||||
run: |
|
||||
go test -race -covermode atomic -coverprofile=covprofile ./...
|
||||
|
||||
- name: Install goveralls
|
||||
run: go install github.com/mattn/goveralls@latest
|
||||
|
||||
- name: Send coverage
|
||||
env:
|
||||
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: goveralls -coverprofile=covprofile -service=github
|
@ -1,31 +0,0 @@
|
||||
name: Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ v3 ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
branches: [ v3 ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
@ -1 +0,0 @@
|
||||
Subproject commit 7d3f9e33ee862f8acd65a4cb8ecffb56c4b860a2
|
@ -0,0 +1,125 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
BLOCK_SIZE = 32 // PKCS#7
|
||||
BLOCK_MASK = BLOCK_SIZE - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
|
||||
)
|
||||
|
||||
// 把整数 n 格式化成 4 字节的网络字节序
|
||||
func encodeNetworkByteOrder(b []byte, n uint32) {
|
||||
b[0] = byte(n >> 24)
|
||||
b[1] = byte(n >> 16)
|
||||
b[2] = byte(n >> 8)
|
||||
b[3] = byte(n)
|
||||
}
|
||||
|
||||
// 从 4 字节的网络字节序里解析出整数
|
||||
func decodeNetworkByteOrder(b []byte) (n uint32) {
|
||||
return uint32(b[0])<<24 |
|
||||
uint32(b[1])<<16 |
|
||||
uint32(b[2])<<8 |
|
||||
uint32(b[3])
|
||||
}
|
||||
|
||||
// AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
|
||||
func AESEncryptMsg(random, rawXMLMsg []byte, appId string, aesKey []byte) (ciphertext []byte) {
|
||||
appIdOffset := 20 + len(rawXMLMsg)
|
||||
contentLen := appIdOffset + len(appId)
|
||||
amountToPad := BLOCK_SIZE - contentLen&BLOCK_MASK
|
||||
plaintextLen := contentLen + amountToPad
|
||||
|
||||
plaintext := make([]byte, plaintextLen)
|
||||
|
||||
// 拼接
|
||||
copy(plaintext[:16], random)
|
||||
encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
|
||||
copy(plaintext[20:], rawXMLMsg)
|
||||
copy(plaintext[appIdOffset:], appId)
|
||||
|
||||
// PKCS#7 补位
|
||||
for i := contentLen; i < plaintextLen; i++ {
|
||||
plaintext[i] = byte(amountToPad)
|
||||
}
|
||||
|
||||
// 加密
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, aesKey[:16])
|
||||
mode.CryptBlocks(plaintext, plaintext)
|
||||
|
||||
ciphertext = plaintext
|
||||
return
|
||||
}
|
||||
|
||||
// AESDecryptMsg c解密
|
||||
func AESDecryptMsg(decryptStr, aesKey string) (string, error) {
|
||||
cipherText, err := base64.StdEncoding.DecodeString(decryptStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 解密
|
||||
block, err := aes.NewCipher([]byte(aesKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
blockMode := cipher.NewCBCDecrypter(block, []byte(aesKey))
|
||||
decrypted := make([]byte, len(cipherText))
|
||||
blockMode.CryptBlocks(decrypted, cipherText)
|
||||
|
||||
decrypted = pkcs5UnPadding(decrypted)
|
||||
return string(decrypted), nil
|
||||
}
|
||||
|
||||
func pkcs5UnPadding(decrypted []byte) []byte {
|
||||
length := len(decrypted)
|
||||
unPadding := int(decrypted[length-1])
|
||||
return decrypted[:(length - unPadding)]
|
||||
}
|
||||
|
||||
func AESDecryptData(cipherText, aesKey, iv []byte) (rawData []byte, err error) {
|
||||
if len(cipherText) < BLOCK_SIZE {
|
||||
err = fmt.Errorf("the length of ciphertext too short: %d", len(cipherText))
|
||||
return
|
||||
}
|
||||
|
||||
plaintext := make([]byte, len(cipherText)) // len(plaintext) >= BLOCK_SIZE
|
||||
|
||||
// 解密
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(plaintext, cipherText)
|
||||
|
||||
// PKCS#7 去除补位
|
||||
amountToPad := int(plaintext[len(plaintext)-1])
|
||||
if amountToPad < 1 || amountToPad > BLOCK_SIZE {
|
||||
err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
|
||||
return
|
||||
}
|
||||
plaintext = plaintext[:len(plaintext)-amountToPad]
|
||||
|
||||
// 反拼接
|
||||
// len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
|
||||
if len(plaintext) <= 20 {
|
||||
err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
|
||||
return
|
||||
}
|
||||
|
||||
rawData = plaintext
|
||||
|
||||
return
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"dtapps/dta/library/utils/gomongo"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitee.com/dtapps/go-library/utils/gohttp"
|
||||
"gitee.com/dtapps/go-library/utils/gotime"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// App 微信公众号服务
|
||||
type App struct {
|
||||
componentAccessToken string // 第三方平台 access_token
|
||||
componentVerifyTicket string // 微信后台推送的 ticket
|
||||
preAuthCode string // 预授权码
|
||||
|
||||
authorizerAccessToken string // 接口调用令牌
|
||||
authorizerRefreshToken string // 刷新令牌
|
||||
AuthorizerAppid string // 授权方 appid
|
||||
|
||||
ComponentAppId string // 第三方平台 appid
|
||||
ComponentAppSecret string // 第三方平台 app_secret
|
||||
MessageToken string
|
||||
MessageKey string
|
||||
|
||||
Mongo gomongo.App // 非关系数据库服务
|
||||
Db *gorm.DB // 关系数据库服务
|
||||
}
|
||||
|
||||
func (app *App) request(url string, params map[string]interface{}, method string) (resp []byte, err error) {
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
get, err := gohttp.Get(url, params)
|
||||
// 日志
|
||||
go app.mongoLog(url, params, method, get)
|
||||
return get.Body, err
|
||||
case http.MethodPost:
|
||||
// 请求参数
|
||||
paramsStr, err := json.Marshal(params)
|
||||
postJson, err := gohttp.PostJson(url, paramsStr)
|
||||
// 日志
|
||||
go app.mongoLog(url, params, method, postJson)
|
||||
return postJson.Body, err
|
||||
default:
|
||||
return nil, errors.New("请求类型不支持")
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthorizerAccessToken 获取授权方令牌
|
||||
func (app *App) GetAuthorizerAccessToken() string {
|
||||
if app.Db == nil {
|
||||
return app.authorizerAccessToken
|
||||
}
|
||||
var result AuthorizerAccessToken
|
||||
app.Db.Where("component_app_id = ?", app.ComponentAppId).Where("authorizer_app_id = ?", app.AuthorizerAppid).Where("expire_time >= ?", gotime.Current().Format()).Last(&result)
|
||||
return result.AuthorizerAccessToken
|
||||
}
|
||||
|
||||
// GetAuthorizerRefreshToken 获取刷新令牌
|
||||
func (app *App) GetAuthorizerRefreshToken() string {
|
||||
if app.Db == nil {
|
||||
return app.authorizerRefreshToken
|
||||
}
|
||||
var result AuthorizerAccessToken
|
||||
app.Db.Where("component_app_id = ?", app.ComponentAppId).Where("authorizer_app_id = ?", app.AuthorizerAppid).Last(&result)
|
||||
return result.AuthorizerRefreshToken
|
||||
}
|
||||
|
||||
// GetPreAuthCode 获取预授权码
|
||||
func (app *App) GetPreAuthCode() string {
|
||||
if app.Db == nil {
|
||||
return app.preAuthCode
|
||||
}
|
||||
var result PreAuthCode
|
||||
app.Db.Where("app_id = ?", app.ComponentAppId).Where("expire_time >= ?", gotime.Current().Format()).Last(&result)
|
||||
return result.PreAuthCode
|
||||
}
|
||||
|
||||
// GetComponentAccessToken 获取 access_token
|
||||
func (app *App) GetComponentAccessToken() string {
|
||||
if app.Db == nil {
|
||||
return app.componentAccessToken
|
||||
}
|
||||
var result ComponentAccessToken
|
||||
app.Db.Where("app_id = ?", app.ComponentAppId).Where("expire_time >= ?", gotime.Current().Format()).Last(&result)
|
||||
return result.ComponentAccessToken
|
||||
}
|
||||
|
||||
// GetComponentVerifyTicket 获取 Ticket
|
||||
func (app *App) GetComponentVerifyTicket() string {
|
||||
if app.Db == nil {
|
||||
return app.componentVerifyTicket
|
||||
}
|
||||
var result ComponentVerifyTicket
|
||||
app.Db.Where("app_id = ?", app.ComponentAppId).Where("expire_time >= ?", gotime.Current().Format()).Last(&result)
|
||||
return result.ComponentVerifyTicket
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CgiBinComponentApiAuthorizerTokenResponse struct {
|
||||
AuthorizerAccessToken string `json:"authorizer_access_token"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
AuthorizerRefreshToken string `json:"authorizer_refresh_token"`
|
||||
}
|
||||
|
||||
type CgiBinComponentApiAuthorizerTokenResult struct {
|
||||
Result CgiBinComponentApiAuthorizerTokenResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
authorizerAppid string // 授权方 appid
|
||||
}
|
||||
|
||||
func NewCgiBinComponentApiAuthorizerTokenResult(result CgiBinComponentApiAuthorizerTokenResponse, body []byte, err error, authorizerAppid string) *CgiBinComponentApiAuthorizerTokenResult {
|
||||
return &CgiBinComponentApiAuthorizerTokenResult{Result: result, Body: body, Err: err, authorizerAppid: authorizerAppid}
|
||||
}
|
||||
|
||||
// CgiBinComponentApiAuthorizerToken 获取/刷新接口调用令牌
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/api_authorizer_token.html
|
||||
func (app *App) CgiBinComponentApiAuthorizerToken() *CgiBinComponentApiAuthorizerTokenResult {
|
||||
app.componentAccessToken = app.GetComponentAccessToken()
|
||||
// 参数
|
||||
param := NewParams()
|
||||
param["component_appid"] = app.ComponentAppId // 第三方平台 appid
|
||||
param["authorizer_appid"] = app.AuthorizerAppid // 授权方 appid
|
||||
param["authorizer_refresh_token"] = app.GetAuthorizerRefreshToken() // 授权码, 会在授权成功时返回给第三方平台
|
||||
params := app.NewParamsWith(param)
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%v", app.componentAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response CgiBinComponentApiAuthorizerTokenResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewCgiBinComponentApiAuthorizerTokenResult(response, body, err, app.AuthorizerAppid)
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CgiBinComponentApiComponentTokenResponse struct {
|
||||
ComponentAccessToken string `json:"component_access_token"` // 第三方平台 access_token
|
||||
ExpiresIn int64 `json:"expires_in"` // 有效期,单位:秒
|
||||
}
|
||||
|
||||
type CgiBinComponentApiComponentTokenResult struct {
|
||||
Result CgiBinComponentApiComponentTokenResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewCgiBinComponentApiComponentTokenResult(result CgiBinComponentApiComponentTokenResponse, body []byte, err error) *CgiBinComponentApiComponentTokenResult {
|
||||
return &CgiBinComponentApiComponentTokenResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// CgiBinComponentApiComponentToken 令牌
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/component_access_token.html
|
||||
func (app *App) CgiBinComponentApiComponentToken() *CgiBinComponentApiComponentTokenResult {
|
||||
app.componentVerifyTicket = app.GetComponentVerifyTicket()
|
||||
// 参数
|
||||
param := NewParams()
|
||||
param["component_appid"] = app.ComponentAppId // 第三方平台 appid
|
||||
param["component_appsecret"] = app.ComponentAppSecret // 第三方平台 appsecret
|
||||
param["component_verify_ticket"] = app.componentVerifyTicket // 微信后台推送的 ticket
|
||||
params := app.NewParamsWith(param)
|
||||
// 请求
|
||||
body, err := app.request("https://api.weixin.qq.com/cgi-bin/component/api_component_token", params, http.MethodPost)
|
||||
// 定义
|
||||
var response CgiBinComponentApiComponentTokenResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewCgiBinComponentApiComponentTokenResult(response, body, err)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CgiBinComponentApiCreatePreAuthCodenResponse struct {
|
||||
PreAuthCode string `json:"pre_auth_code"` // 预授权码
|
||||
ExpiresIn int64 `json:"expires_in"` // 有效期,单位:秒
|
||||
}
|
||||
|
||||
type CgiBinComponentApiCreatePreAuthCodenResult struct {
|
||||
Result CgiBinComponentApiCreatePreAuthCodenResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewCgiBinComponentApiCreatePreAuthCodenResult(result CgiBinComponentApiCreatePreAuthCodenResponse, body []byte, err error) *CgiBinComponentApiCreatePreAuthCodenResult {
|
||||
return &CgiBinComponentApiCreatePreAuthCodenResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// CgiBinComponentApiCreatePreAuthCoden 预授权码
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/pre_auth_code.html
|
||||
func (app *App) CgiBinComponentApiCreatePreAuthCoden() *CgiBinComponentApiCreatePreAuthCodenResult {
|
||||
app.componentAccessToken = app.GetComponentAccessToken()
|
||||
// 参数
|
||||
param := NewParams()
|
||||
param["component_appid"] = app.ComponentAppId // 第三方平台 appid
|
||||
params := app.NewParamsWith(param)
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%v", app.componentAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response CgiBinComponentApiCreatePreAuthCodenResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewCgiBinComponentApiCreatePreAuthCodenResult(response, body, err)
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GetCallBackIpResponse struct {
|
||||
IpList []string `json:"ip_list"`
|
||||
}
|
||||
|
||||
type GetCallBackIpResult struct {
|
||||
Result GetCallBackIpResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewGetCallBackIpResult(result GetCallBackIpResponse, body []byte, err error) *GetCallBackIpResult {
|
||||
return &GetCallBackIpResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// CgiBinGetApiDomainIp 获取微信服务器IP地址
|
||||
// https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
|
||||
func (app *App) CgiBinGetApiDomainIp(componentAccessToken string) *GetCallBackIpResult {
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=%s", componentAccessToken), map[string]interface{}{}, http.MethodGet)
|
||||
// 定义
|
||||
var response GetCallBackIpResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewGetCallBackIpResult(response, body, err)
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"gitee.com/dtapps/go-library/utils/gotime"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetComponentAccessTokenMonitor 获取令牌和监控
|
||||
func (app *App) GetComponentAccessTokenMonitor() string {
|
||||
// 查询
|
||||
componentAccessToken := app.GetComponentAccessToken()
|
||||
// 判断
|
||||
result := app.CgiBinGetApiDomainIp(componentAccessToken)
|
||||
if len(result.Result.IpList) > 0 {
|
||||
return componentAccessToken
|
||||
}
|
||||
// 重新获取
|
||||
return app.SetComponentAccessToken(app.CgiBinComponentApiComponentToken())
|
||||
}
|
||||
|
||||
// SetComponentAccessToken 设置令牌
|
||||
func (app *App) SetComponentAccessToken(info *CgiBinComponentApiComponentTokenResult) string {
|
||||
if app.Db == nil || info.Result.ComponentAccessToken == "" {
|
||||
return ""
|
||||
}
|
||||
app.Db.Create(&ComponentAccessToken{
|
||||
AppId: app.ComponentAppId,
|
||||
ComponentAccessToken: info.Result.ComponentAccessToken,
|
||||
ExpiresIn: info.Result.ExpiresIn,
|
||||
ExpireTime: gotime.Current().AfterSeconds(7200).Time,
|
||||
})
|
||||
return info.Result.ComponentAccessToken
|
||||
}
|
||||
|
||||
type ComponentAccessToken struct {
|
||||
gorm.Model
|
||||
AppId string `json:"app_id"` // 第三方平台 appid
|
||||
ComponentAccessToken string `json:"component_access_token"` // 第三方平台 access_token
|
||||
ExpiresIn int64 `json:"expires_in"` // 有效期,单位:秒
|
||||
ExpireTime time.Time `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
func (m *ComponentAccessToken) TableName() string {
|
||||
return "component_access_token"
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"gitee.com/dtapps/go-library/utils/gohttp"
|
||||
"gitee.com/dtapps/go-library/utils/gotime"
|
||||
)
|
||||
|
||||
// 日志
|
||||
type mongoZap struct {
|
||||
Url string `json:"url" bson:"url"`
|
||||
Params interface{} `json:"params" bson:"params"`
|
||||
Method string `json:"method" bson:"method"`
|
||||
Header interface{} `json:"header" bson:"header"`
|
||||
Status string `json:"status" bson:"status"`
|
||||
StatusCode int `json:"status_code" bson:"status_code"`
|
||||
Body interface{} `json:"body" bson:"body"`
|
||||
ContentLength int64 `json:"content_length" bson:"content_length"`
|
||||
CreateTime string `json:"create_time" bson:"create_time"`
|
||||
}
|
||||
|
||||
func (m *mongoZap) Database() string {
|
||||
return "zap_logs"
|
||||
}
|
||||
|
||||
func (m *mongoZap) TableName() string {
|
||||
return "wechatopen_" + gotime.Current().SetFormat("200601")
|
||||
}
|
||||
|
||||
func (app *App) mongoLog(url string, params map[string]interface{}, method string, request gohttp.Response) {
|
||||
if app.Mongo.Db == nil {
|
||||
return
|
||||
}
|
||||
var body map[string]interface{}
|
||||
_ = json.Unmarshal(request.Body, &body)
|
||||
app.Mongo.Model(&mongoZap{}).InsertOne(mongoZap{
|
||||
Url: url,
|
||||
Params: params,
|
||||
Method: method,
|
||||
Header: request.Header,
|
||||
Status: request.Status,
|
||||
StatusCode: request.StatusCode,
|
||||
Body: body,
|
||||
ContentLength: request.ContentLength,
|
||||
CreateTime: gotime.Current().Format(),
|
||||
})
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package wechatopen
|
||||
|
||||
// Params 请求参数
|
||||
type Params map[string]interface{}
|
||||
|
||||
func NewParams() Params {
|
||||
p := make(Params)
|
||||
return p
|
||||
}
|
||||
|
||||
func (app *App) NewParamsWith(params ...Params) Params {
|
||||
p := make(Params)
|
||||
for _, v := range params {
|
||||
p.SetParams(v)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p Params) Set(key string, value interface{}) {
|
||||
p[key] = value
|
||||
}
|
||||
|
||||
func (p Params) SetParams(params Params) {
|
||||
for key, value := range params {
|
||||
p[key] = value
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"gitee.com/dtapps/go-library/utils/gotime"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetPreAuthCodeMonitor 获取预授权码和监控
|
||||
func (app *App) GetPreAuthCodeMonitor() string {
|
||||
// 查询
|
||||
preAuthCode := app.GetPreAuthCode()
|
||||
if preAuthCode != "" {
|
||||
return preAuthCode
|
||||
}
|
||||
// 重新获取
|
||||
return app.SetPreAuthCode(app.CgiBinComponentApiCreatePreAuthCoden())
|
||||
}
|
||||
|
||||
// SetPreAuthCode 设置预授权码和自动获取
|
||||
func (app *App) SetPreAuthCode(info *CgiBinComponentApiCreatePreAuthCodenResult) string {
|
||||
if app.Db == nil || info.Result.PreAuthCode == "" {
|
||||
return ""
|
||||
}
|
||||
app.Db.Create(&PreAuthCode{
|
||||
AppId: app.ComponentAppId,
|
||||
PreAuthCode: info.Result.PreAuthCode,
|
||||
ExpiresIn: info.Result.ExpiresIn,
|
||||
ExpireTime: gotime.Current().AfterSeconds(1700).Time,
|
||||
})
|
||||
return info.Result.PreAuthCode
|
||||
}
|
||||
|
||||
type PreAuthCode struct {
|
||||
gorm.Model
|
||||
AppId string `json:"app_id"` // 第三方平台 appid
|
||||
PreAuthCode string `json:"pre_auth_code"` // 预授权码
|
||||
ExpiresIn int64 `json:"expires_in"` // 有效期,单位:秒
|
||||
ExpireTime time.Time `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
func (m *PreAuthCode) TableName() string {
|
||||
return "pre_auth_code"
|
||||
}
|
||||
|
||||
// PreAuthCodeDelete 删除过期或使用过的预授权码
|
||||
func (app *App) PreAuthCodeDelete(id uint) int64 {
|
||||
return app.Db.Where("id = ?", id).Delete(&PreAuthCode{}).RowsAffected
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ServeHttpAuthorizerAppid 授权跳转
|
||||
func (app *App) ServeHttpAuthorizerAppid(r *http.Request) (resp CgiBinComponentApiQueryAuthResponse, agentUserId int64, pacId uint, err error) {
|
||||
var (
|
||||
query = r.URL.Query()
|
||||
|
||||
authCode = query.Get("auth_code")
|
||||
expiresIn = query.Get("expires_in")
|
||||
)
|
||||
|
||||
agentUserId = ToInt64(query.Get("agent_user_id"))
|
||||
|
||||
pacId = ToUint(query.Get("pac_id"))
|
||||
|
||||
if authCode == "" {
|
||||
return resp, agentUserId, pacId, errors.New("找不到授权码参数")
|
||||
}
|
||||
|
||||
if expiresIn == "" {
|
||||
return resp, agentUserId, pacId, errors.New("找不到过期时间参数")
|
||||
}
|
||||
|
||||
info := app.CgiBinComponentApiQueryAuth(authCode)
|
||||
if info.Result.AuthorizationInfo.AuthorizerAppid == "" {
|
||||
return resp, agentUserId, pacId, errors.New("获取失败")
|
||||
}
|
||||
|
||||
return info.Result, agentUserId, pacId, nil
|
||||
}
|
||||
|
||||
// ToFloat64 string到float64
|
||||
func ToFloat64(s string) float64 {
|
||||
i, _ := strconv.ParseFloat(s, 64)
|
||||
return i
|
||||
}
|
||||
|
||||
// ToInt64 string到int64
|
||||
func ToInt64(s string) int64 {
|
||||
i, err := strconv.ParseInt(s, 10, 64)
|
||||
if err == nil {
|
||||
return i
|
||||
}
|
||||
return int64(ToFloat64(s))
|
||||
}
|
||||
|
||||
// ToUint string到uint64
|
||||
func ToUint(s string) uint {
|
||||
i, err := strconv.ParseUint(s, 10, 64)
|
||||
if err == nil {
|
||||
return uint(i)
|
||||
}
|
||||
return 0
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Sign 微信公众号 url 签名.
|
||||
func Sign(token, timestamp, nonce string) (signature string) {
|
||||
strs := sort.StringSlice{token, timestamp, nonce}
|
||||
strs.Sort()
|
||||
|
||||
buf := make([]byte, 0, len(token)+len(timestamp)+len(nonce))
|
||||
buf = append(buf, strs[0]...)
|
||||
buf = append(buf, strs[1]...)
|
||||
buf = append(buf, strs[2]...)
|
||||
|
||||
hashsum := sha1.Sum(buf)
|
||||
return hex.EncodeToString(hashsum[:])
|
||||
}
|
||||
|
||||
// MsgSign 微信公众号/企业号 消息体签名.
|
||||
func MsgSign(token, timestamp, nonce, encryptedMsg string) (signature string) {
|
||||
strs := sort.StringSlice{token, timestamp, nonce, encryptedMsg}
|
||||
strs.Sort()
|
||||
|
||||
h := sha1.New()
|
||||
|
||||
bufw := bufio.NewWriterSize(h, 128) // sha1.BlockSize 的整数倍
|
||||
bufw.WriteString(strs[0])
|
||||
bufw.WriteString(strs[1])
|
||||
bufw.WriteString(strs[2])
|
||||
bufw.WriteString(strs[3])
|
||||
bufw.Flush()
|
||||
|
||||
hashsum := h.Sum(nil)
|
||||
return hex.EncodeToString(hashsum)
|
||||
}
|
||||
|
||||
// CheckSignature 微信公众号签名检查
|
||||
func CheckSignature(signature, timeStamp, nonce string, token string) bool {
|
||||
paramsArray := []string{token, timeStamp, nonce}
|
||||
// 字典序排序
|
||||
sort.Strings(paramsArray)
|
||||
paramsMsg := ""
|
||||
for _, value := range paramsArray {
|
||||
//fmt.Println(value)
|
||||
paramsMsg += value
|
||||
}
|
||||
//sha1
|
||||
sha1Param := sha1.New()
|
||||
sha1Param.Write([]byte(paramsMsg))
|
||||
msg := hex.EncodeToString(sha1Param.Sum([]byte("")))
|
||||
return msg == signature
|
||||
}
|
||||
|
||||
func AesDecrypt(cipherData []byte, aesKey []byte) ([]byte, error) {
|
||||
k := len(aesKey) //PKCS#7
|
||||
|
||||
if len(cipherData)%k != 0 {
|
||||
return nil, errors.New("crypto/cipher: 密文大小不是aes密钥长度的倍数")
|
||||
}
|
||||
|
||||
// 创建加密算法实例
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 创建加密客户端实例
|
||||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||
plainData := make([]byte, len(cipherData))
|
||||
blockMode.CryptBlocks(plainData, cipherData)
|
||||
|
||||
return plainData, nil
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SnsComponentJsCode2sessionResponse struct {
|
||||
Openid string `json:"openid"` // 用户唯一标识的 openid
|
||||
SessionKey string `json:"session_key"` // 会话密钥
|
||||
Unionid string `json:"unionid"` // 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
|
||||
}
|
||||
|
||||
type SnsComponentJsCode2sessionResult struct {
|
||||
Result SnsComponentJsCode2sessionResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewSnsComponentJsCode2sessionResult(result SnsComponentJsCode2sessionResponse, body []byte, err error) *SnsComponentJsCode2sessionResult {
|
||||
return &SnsComponentJsCode2sessionResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// SnsComponentJsCode2session 小程序登录
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/others/WeChat_login.html
|
||||
func (app *App) SnsComponentJsCode2session(jsCode string) *SnsComponentJsCode2sessionResult {
|
||||
app.componentAccessToken = app.GetComponentAccessToken()
|
||||
// 参数
|
||||
params := NewParams()
|
||||
params["appid"] = app.AuthorizerAppid // 小程序的 AppID
|
||||
params["js_code"] = jsCode // wx.login 获取的 code
|
||||
params["grant_type"] = "authorization_code" // 填 authorization_code
|
||||
params["component_appid"] = app.ComponentAppId // 第三方平台 appid
|
||||
params["component_access_token"] = app.componentAccessToken // 第三方平台的component_access_token
|
||||
// 请求
|
||||
body, err := app.request("https://api.weixin.qq.com/sns/component/jscode2session", params, http.MethodGet)
|
||||
// 定义
|
||||
var response SnsComponentJsCode2sessionResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewSnsComponentJsCode2sessionResult(response, body, err)
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
EncryptedData string `json:"encrypted_data"`
|
||||
Iv string `json:"iv"`
|
||||
}
|
||||
|
||||
type UserInfoResponse struct {
|
||||
OpenId string `json:"openId"`
|
||||
NickName string `json:"nickName"`
|
||||
Gender int `json:"gender"`
|
||||
City string `json:"city"`
|
||||
Province string `json:"province"`
|
||||
Country string `json:"country"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
UnionId string `json:"unionId"`
|
||||
Watermark struct {
|
||||
AppID string `json:"appid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
type UserInfoResult struct {
|
||||
Result UserInfoResponse // 结果
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewUserInfoResult(result UserInfoResponse, err error) *UserInfoResult {
|
||||
return &UserInfoResult{Result: result, Err: err}
|
||||
}
|
||||
|
||||
// UserInfo 解密用户信息
|
||||
func (r *SnsComponentJsCode2sessionResult) UserInfo(param UserInfo) *UserInfoResult {
|
||||
var response UserInfoResponse
|
||||
aesKey, err := base64.StdEncoding.DecodeString(r.Result.SessionKey)
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(param.EncryptedData)
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
ivBytes, err := base64.StdEncoding.DecodeString(param.Iv)
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, ivBytes)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, err = r.pkcs7Unpaid(cipherText, block.BlockSize())
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
err = json.Unmarshal(cipherText, &response)
|
||||
if err != nil {
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
return NewUserInfoResult(response, err)
|
||||
}
|
||||
|
||||
func (u *UserInfoResponse) UserInfoAvatarUrlReal() string {
|
||||
return strings.Replace(u.AvatarUrl, "/132", "/0", -1)
|
||||
}
|
||||
|
||||
func (r *SnsComponentJsCode2sessionResult) pkcs7Unpaid(data []byte, blockSize int) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid block size")
|
||||
}
|
||||
if len(data)%blockSize != 0 || len(data) == 0 {
|
||||
return nil, errors.New("invalid PKCS7 data")
|
||||
}
|
||||
c := data[len(data)-1]
|
||||
n := int(c)
|
||||
if n == 0 || n > len(data) {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if data[len(data)-n+i] != c {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-n], nil
|
||||
}
|
@ -0,0 +1 @@
|
||||
package wechatopen
|
@ -0,0 +1,53 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaBindTesterResponse struct {
|
||||
Errcode int `json:"errcode"` // 错误码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
Userstr string `json:"userstr"` // 人员对应的唯一字符串
|
||||
}
|
||||
|
||||
type WxaBindTesterResult struct {
|
||||
Result WxaBindTesterResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaBindTesterResult(result WxaBindTesterResponse, body []byte, err error) *WxaBindTesterResult {
|
||||
return &WxaBindTesterResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaBindTester 绑定微信用户为体验者
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_AdminManagement/Admin.html
|
||||
func (app *App) WxaBindTester(wechatid string) *WxaBindTesterResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := NewParams()
|
||||
params["wechatid"] = wechatid
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/bind_tester?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaBindTesterResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaBindTesterResult(response, body, err)
|
||||
}
|
||||
|
||||
// ErrcodeInfo 错误描述
|
||||
func (resp *WxaBindTesterResult) ErrcodeInfo() string {
|
||||
switch resp.Result.Errcode {
|
||||
case 85001:
|
||||
return "微信号不存在或微信号设置为不可搜索"
|
||||
case 85002:
|
||||
return "小程序绑定的体验者数量达到上限"
|
||||
case 85003:
|
||||
return "微信号绑定的小程序体验者达到上限"
|
||||
case 85004:
|
||||
return "微信号已经绑定"
|
||||
}
|
||||
return "系统繁忙"
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGetAuditStatusResponse struct {
|
||||
Errcode int `json:"errcode"` // 返回码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
Auditid int `json:"auditid"` // 最新的审核 ID
|
||||
Status int `json:"status"` // 审核状态
|
||||
Reason string `json:"reason"` // 当审核被拒绝时,返回的拒绝原因
|
||||
ScreenShot string `json:"ScreenShot"` // 当审核被拒绝时,会返回审核失败的小程序截图示例。用 | 分隔的 media_id 的列表,可通过获取永久素材接口拉取截图内容
|
||||
}
|
||||
|
||||
type WxaGetAuditStatusResult struct {
|
||||
Result WxaGetAuditStatusResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaGetAuditStatusResult(result WxaGetAuditStatusResponse, body []byte, err error) *WxaGetAuditStatusResult {
|
||||
return &WxaGetAuditStatusResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaGetAuditStatus 查询指定发布审核单的审核状态
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_auditstatus.html
|
||||
func (app *App) WxaGetAuditStatus(auditid int64) *WxaGetAuditStatusResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := app.NewParamsWith()
|
||||
params.Set("auditid", auditid)
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_auditstatus?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaGetAuditStatusResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaGetAuditStatusResult(response, body, err)
|
||||
}
|
||||
|
||||
// ErrcodeInfo 错误描述
|
||||
func (resp *WxaGetAuditStatusResult) ErrcodeInfo() string {
|
||||
switch resp.Result.Errcode {
|
||||
case 86000:
|
||||
return "不是由第三方代小程序进行调用"
|
||||
case 86001:
|
||||
return "不存在第三方的已经提交的代码"
|
||||
case 85012:
|
||||
return "无效的审核 id"
|
||||
}
|
||||
return "系统繁忙"
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGetLatestAuditStatusResponse struct {
|
||||
Errcode int `json:"errcode"` // 返回码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
Auditid int `json:"auditid"` // 最新的审核 ID
|
||||
Status int `json:"status"` // 审核状态
|
||||
Reason string `json:"reason"` // 当审核被拒绝时,返回的拒绝原因
|
||||
ScreenShot string `json:"ScreenShot"` // 当审核被拒绝时,会返回审核失败的小程序截图示例。用 | 分隔的 media_id 的列表,可通过获取永久素材接口拉取截图内容
|
||||
}
|
||||
|
||||
type WxaGetLatestAuditStatusResult struct {
|
||||
Result WxaGetLatestAuditStatusResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaGetLatestAuditStatusResult(result WxaGetLatestAuditStatusResponse, body []byte, err error) *WxaGetLatestAuditStatusResult {
|
||||
return &WxaGetLatestAuditStatusResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaGetLatestAuditStatus 查询最新一次提交的审核状态
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_auditstatus.html
|
||||
func (app *App) WxaGetLatestAuditStatus() *WxaGetLatestAuditStatusResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_latest_auditstatus?access_token=%s", app.authorizerAccessToken), map[string]interface{}{}, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaGetLatestAuditStatusResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaGetLatestAuditStatusResult(response, body, err)
|
||||
}
|
||||
|
||||
// ErrcodeInfo 错误描述
|
||||
func (resp *WxaGetLatestAuditStatusResult) ErrcodeInfo() string {
|
||||
switch resp.Result.Errcode {
|
||||
case 86000:
|
||||
return "不是由第三方代小程序进行调用"
|
||||
case 86001:
|
||||
return "不存在第三方的已经提交的代码"
|
||||
case 85012:
|
||||
return "无效的审核 id"
|
||||
}
|
||||
return "系统繁忙"
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGetPageResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
PageList []string `json:"page_list"` // page_list 页面配置列表
|
||||
}
|
||||
|
||||
type WxaGetPageResult struct {
|
||||
Result WxaGetPageResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaGetPageResult(result WxaGetPageResponse, body []byte, err error) *WxaGetPageResult {
|
||||
return &WxaGetPageResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaGetPage 获取已上传的代码的页面列表
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_page.html
|
||||
func (app *App) WxaGetPage() *WxaGetPageResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_page?access_token=%s", app.authorizerAccessToken), map[string]interface{}{}, http.MethodGet)
|
||||
// 定义
|
||||
var response WxaGetPageResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaGetPageResult(response, body, err)
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGetQrcodeResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
type WxaGetQrcodeResult struct {
|
||||
Result WxaGetQrcodeResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaGetQrcodeResult(result WxaGetQrcodeResponse, body []byte, err error) *WxaGetQrcodeResult {
|
||||
return &WxaGetQrcodeResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaGetQrcode 获取体验版二维码
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_qrcode.html
|
||||
func (app *App) WxaGetQrcode(path string) *WxaGetQrcodeResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := NewParams()
|
||||
if path != "" {
|
||||
params["path"] = path // 指定二维码扫码后直接进入指定页面并可同时带上参数)
|
||||
}
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_qrcode?access_token=%s", app.authorizerAccessToken), params, http.MethodGet)
|
||||
// 定义
|
||||
var response WxaGetQrcodeResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaGetQrcodeResult(response, body, err)
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGetTemplateDraftListResponse struct {
|
||||
Errcode int `json:"errcode"` // 返回码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
DraftList []struct {
|
||||
CreateTime int `json:"create_time"` // 开发者上传草稿时间戳
|
||||
UserVersion string `json:"user_version"` // 版本号,开发者自定义字段
|
||||
UserDesc string `json:"user_desc"` // 版本描述 开发者自定义字段
|
||||
DraftId int `json:"draft_id"` // 草稿 id
|
||||
SourceMiniprogramAppid string `json:"source_miniprogram_appid"`
|
||||
SourceMiniprogram string `json:"source_miniprogram"`
|
||||
Developer string `json:"developer"`
|
||||
CategoryList []interface{} `json:"category_list"`
|
||||
} `json:"draft_list"` // 草稿信息列表
|
||||
}
|
||||
|
||||
type WxaGetTemplateDraftListResult struct {
|
||||
Result WxaGetTemplateDraftListResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaGetTemplateDraftListResult(result WxaGetTemplateDraftListResponse, body []byte, err error) *WxaGetTemplateDraftListResult {
|
||||
return &WxaGetTemplateDraftListResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaGetTemplateDraftList 获取代码草稿列表
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/code_template/gettemplatedraftlist.html
|
||||
func (app *App) WxaGetTemplateDraftList() *WxaGetTemplateDraftListResult {
|
||||
app.componentAccessToken = app.GetComponentAccessToken()
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/gettemplatedraftlist?access_token=%s", app.componentAccessToken), map[string]interface{}{}, http.MethodGet)
|
||||
// 定义
|
||||
var response WxaGetTemplateDraftListResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaGetTemplateDraftListResult(response, body, err)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaMemberAuthResponse struct {
|
||||
Errcode int `json:"errcode"` // 错误码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
Members []struct {
|
||||
Userstr string `json:"userstr"` // 人员对应的唯一字符串
|
||||
} `json:"members"` // 人员信息列表
|
||||
}
|
||||
|
||||
type WxaMemberAuthResult struct {
|
||||
Result WxaMemberAuthResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaMemberAuthResult(result WxaMemberAuthResponse, body []byte, err error) *WxaMemberAuthResult {
|
||||
return &WxaMemberAuthResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaMemberAuth 获取体验者列表
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_AdminManagement/memberauth.html
|
||||
func (app *App) WxaMemberAuth() *WxaMemberAuthResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := NewParams()
|
||||
params["action"] = "get_experiencer"
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/memberauth?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaMemberAuthResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaMemberAuthResult(response, body, err)
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaModifyDomainResponse struct {
|
||||
Errcode int `json:"errcode"` // 错误码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
Requestdomain []string `json:"requestdomain"` // request 合法域名
|
||||
Wsrequestdomain []string `json:"wsrequestdomain"` // socket 合法域名
|
||||
Uploaddomain []string `json:"uploaddomain"` // uploadFile 合法域名
|
||||
Downloaddomain []string `json:"downloaddomain"` // downloadFile 合法域名
|
||||
Udpdomain []string `json:"udpdomain"` // udp 合法域名
|
||||
Tcpdomain []string `json:"tcpdomain"` // tcp 合法域名
|
||||
InvalidRequestdomain []string `json:"invalid_requestdomain"` // request 不合法域名
|
||||
InvalidWsrequestdomain []string `json:"invalid_wsrequestdomain"` // socket 不合法域名
|
||||
InvalidUploaddomain []string `json:"invalid_uploaddomain"` // uploadFile 不合法域名
|
||||
InvalidDownloaddomain []string `json:"invalid_downloaddomain"` // downloadFile 不合法域名
|
||||
InvalidUdpdomain []string `json:"invalid_udpdomain"` // udp 不合法域名
|
||||
InvalidTcpdomain []string `json:"invalid_tcpdomain"` // tcp 不合法域名
|
||||
NoIcpDomain []string `json:"no_icp_domain"` // 没有经过icp备案的域名
|
||||
}
|
||||
|
||||
type WxaModifyDomainResult struct {
|
||||
Result WxaModifyDomainResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaModifyDomainResult(result WxaModifyDomainResponse, body []byte, err error) *WxaModifyDomainResult {
|
||||
return &WxaModifyDomainResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaModifyDomain 设置服务器域名
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_Basic_Info/Server_Address_Configuration.html
|
||||
func (app *App) WxaModifyDomain(notMustParams ...Params) *WxaModifyDomainResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := app.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/modify_domain?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaModifyDomainResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaModifyDomainResult(response, body, err)
|
||||
}
|
@ -0,0 +1 @@
|
||||
package wechatopen
|
@ -0,0 +1 @@
|
||||
package wechatopen
|
@ -0,0 +1,36 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaSubmitAuditResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
type WxaSubmitAuditResult struct {
|
||||
Result WxaSubmitAuditResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaSubmitAuditResult(result WxaSubmitAuditResponse, body []byte, err error) *WxaSubmitAuditResult {
|
||||
return &WxaSubmitAuditResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaSubmitAudit 提交审核
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/submit_audit.html
|
||||
func (app *App) WxaSubmitAudit(notMustParams ...Params) *WxaSubmitAuditResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := app.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/submit_audit?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaSubmitAuditResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaSubmitAuditResult(response, body, err)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package wechatopen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaUnbindTesterResponse struct {
|
||||
Errcode int `json:"errcode"` // 错误码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
}
|
||||
|
||||
type WxaUnbindTesterResult struct {
|
||||
Result WxaUnbindTesterResponse // 结果
|
||||
Body []byte // 内容
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func NewWxaUnbindTesterResult(result WxaUnbindTesterResponse, body []byte, err error) *WxaUnbindTesterResult {
|
||||
return &WxaUnbindTesterResult{Result: result, Body: body, Err: err}
|
||||
}
|
||||
|
||||
// WxaUnbindTester 解除绑定体验者
|
||||
// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_AdminManagement/unbind_tester.html
|
||||
func (app *App) WxaUnbindTester(wechatid, userstr string) *WxaUnbindTesterResult {
|
||||
app.authorizerAccessToken = app.GetAuthorizerAccessToken()
|
||||
// 参数
|
||||
params := NewParams()
|
||||
if wechatid != "" {
|
||||
params["wechatid"] = wechatid
|
||||
}
|
||||
params["userstr"] = userstr
|
||||
// 请求
|
||||
body, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/unbind_tester?access_token=%s", app.authorizerAccessToken), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaUnbindTesterResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
return NewWxaUnbindTesterResult(response, body, err)
|
||||
}
|
@ -0,0 +1 @@
|
||||
package wechatopen
|
Loading…
Reference in new issue