commit 66565506589e15f22b92faa83fa4d61ba276c642 Author: 李光春 Date: Sat Jun 11 15:28:46 2022 +0800 - init diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c56c479 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,17 @@ +kind: pipeline +type: docker +name: clone + +steps: + - name: Test + image: golang:1.18 + commands: + - go env -w GO111MODULE=on + - go env -w GOPROXY=https://goproxy.cn,direct + - go test -v ./... + - name: Benchmark + image: golang:1.18 + commands: + - go env -w GO111MODULE=on + - go env -w GOPROXY=https://goproxy.cn,direct + - go test -bench=. -benchmem \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..502d67a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.env +.git +.svn +.idea +.vscode +*.log +goinit.sh +gomod.sh +/vendor/ +*_test.go \ No newline at end of file diff --git a/aes_crypto.go b/aes_crypto.go new file mode 100644 index 0000000..053dc3e --- /dev/null +++ b/aes_crypto.go @@ -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 + +} diff --git a/app.go b/app.go new file mode 100644 index 0000000..3480d8e --- /dev/null +++ b/app.go @@ -0,0 +1,88 @@ +package wechatopen + +import ( + "go.dtapp.net/golog" + "go.dtapp.net/goredis" + "go.dtapp.net/gorequest" + "gorm.io/gorm" +) + +// 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 + redis *goredis.Client // 缓存数据库 + pgsql *gorm.DB // pgsql数据库 + client *gorequest.App // 请求客户端 + log *golog.Api // 日志服务 + logTableName string // 日志表名 + logStatus bool // 日志状态 +} + +// NewApp 实例化 +func NewApp(componentAppId string, componentAppSecret string, messageToken string, messageKey string, redis *goredis.Client, pgsql *gorm.DB) *App { + app := &App{componentAppId: componentAppId, componentAppSecret: componentAppSecret, messageToken: messageToken, messageKey: messageKey, redis: redis} + app.client = gorequest.NewHttp() + if pgsql != nil { + app.pgsql = pgsql + app.logStatus = true + app.logTableName = "wechatopen" + app.log = golog.NewApi(&golog.ApiConfig{ + Db: pgsql, + TableName: app.logTableName, + }) + } + return app +} + +// Config 配置 +func (app *App) Config(componentAppId, componentAppSecret string) *App { + app.componentAppId = componentAppId + app.componentAppSecret = componentAppSecret + return app +} + +// ConfigAuthorizer 配置第三方 +func (app *App) ConfigAuthorizer(authorizerAppid string) *App { + app.authorizerAppid = authorizerAppid + return app +} + +func (app *App) request(url string, params map[string]interface{}, method string) (resp gorequest.Response, err error) { + + // 创建请求 + client := app.client + + // 设置请求地址 + client.SetUri(url) + + // 设置方式 + client.SetMethod(method) + + // 设置格式 + client.SetContentTypeJson() + + // 设置参数 + client.SetParams(params) + + // 发起请求 + request, err := client.Request() + if err != nil { + return gorequest.Response{}, err + } + + // 日志 + if app.logStatus == true { + go app.postgresqlLog(request) + } + + return request, err +} diff --git a/cgi-bin.account.getaccountbasicinfo.go b/cgi-bin.account.getaccountbasicinfo.go new file mode 100644 index 0000000..b287813 --- /dev/null +++ b/cgi-bin.account.getaccountbasicinfo.go @@ -0,0 +1,65 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinAccountGetAccountBasicInfoResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 错误信息 + Appid string `json:"appid"` // 帐号 appid + AccountType int `json:"account_type"` // 帐号类型(1:订阅号,2:服务号,3:小程序) + PrincipalType int `json:"principal_type"` // 主体类型 + PrincipalName string `json:"principal_name"` // 主体名称 + Credential string `json:"credential"` // 主体标识 + RealnameStatus int `json:"realname_status"` // 实名验证状态 1=实名验证成功 2=实名验证中 3=实名验证失败 + WxVerifyInfo struct { + QualificationVerify bool `json:"qualification_verify"` // 是否资质认证,若是,拥有微信认证相关的权限 + NamingVerify bool `json:"naming_verify"` // 是否名称认证 + AnnualReview bool `json:"annual_review"` // 是否需要年审(qualification_verify == true 时才有该字段) + AnnualReviewBeginTime int `json:"annual_review_begin_time"` // 年审开始时间,时间戳(qualification_verify == true 时才有该字段) + AnnualReviewEndTime int `json:"annual_review_end_time"` // 年审截止时间,时间戳(qualification_verify == true 时才有该字段) + } `json:"wx_verify_info"` // 微信认证信息 + SignatureInfo struct { + Signature string `json:"signature"` // 功能介绍 + ModifyUsedCount int `json:"modify_used_count"` // 功能介绍已使用修改次数(本月) + ModifyQuota int `json:"modify_quota"` // 功能介绍修改次数总额度(本月) + } `json:"signature_info"` // 功能介绍信息 + HeadImageInfo struct { + HeadImageUrl string `json:"head_image_url"` // 头像 url + ModifyUsedCount int `json:"modify_used_count"` // 头像已使用修改次数(本年) + ModifyQuota int `json:"modify_quota"` // 头像修改次数总额度(本年) + } `json:"head_image_info"` // 头像信息 + NicknameInfo struct { + Nickname string `json:"nickname"` // 小程序名称 + ModifyUsedCount int `json:"modify_used_count"` // 小程序名称已使用修改次数(本年) + ModifyQuota int `json:"modify_quota"` // 小程序名称修改次数总额度(本年) + } `json:"nickname_info"` // 名称信息 + RegisteredCountry int `json:"registered_country"` // 注册国家 + Nickname string `json:"nickname"` // 小程序名称 +} + +type CgiBinAccountGetAccountBasicInfoResult struct { + Result CgiBinAccountGetAccountBasicInfoResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinAccountGetAccountBasicInfoResult(result CgiBinAccountGetAccountBasicInfoResponse, body []byte, http gorequest.Response, err error) *CgiBinAccountGetAccountBasicInfoResult { + return &CgiBinAccountGetAccountBasicInfoResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinAccountGetAccountBasicInfo 获取基本信息 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_Basic_Info/Mini_Program_Information_Settings.html +func (app *App) CgiBinAccountGetAccountBasicInfo() *CgiBinAccountGetAccountBasicInfoResult { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo?access_token=%v", app.GetAuthorizerAccessToken()), map[string]interface{}{}, http.MethodGet) + // 定义 + var response CgiBinAccountGetAccountBasicInfoResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinAccountGetAccountBasicInfoResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.api_authorizer_token.go b/cgi-bin.component.api_authorizer_token.go new file mode 100644 index 0000000..a22752c --- /dev/null +++ b/cgi-bin.component.api_authorizer_token.go @@ -0,0 +1,43 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 + authorizerAppid string // 授权方 appid +} + +func NewCgiBinComponentApiAuthorizerTokenResult(result CgiBinComponentApiAuthorizerTokenResponse, body []byte, http gorequest.Response, err error, authorizerAppid string) *CgiBinComponentApiAuthorizerTokenResult { + return &CgiBinComponentApiAuthorizerTokenResult{Result: result, Body: body, Http: http, 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(authorizerRefreshToken string) *CgiBinComponentApiAuthorizerTokenResult { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 第三方平台 appid + param["authorizer_appid"] = app.authorizerAppid // 授权方 appid + param["authorizer_refresh_token"] = authorizerRefreshToken // 授权码, 会在授权成功时返回给第三方平台 + params := app.NewParamsWith(param) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%v", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentApiAuthorizerTokenResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiAuthorizerTokenResult(response, request.ResponseBody, request, err, app.authorizerAppid) +} diff --git a/cgi-bin.component.api_component_token.go b/cgi-bin.component.api_component_token.go new file mode 100644 index 0000000..5ecba91 --- /dev/null +++ b/cgi-bin.component.api_component_token.go @@ -0,0 +1,40 @@ +package wechatopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentApiComponentTokenResult(result CgiBinComponentApiComponentTokenResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentApiComponentTokenResult { + return &CgiBinComponentApiComponentTokenResult{Result: result, Body: body, Http: http, 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 { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 第三方平台 appid + param["component_appsecret"] = app.componentAppSecret // 第三方平台 appsecret + param["component_verify_ticket"] = app.GetComponentVerifyTicket() // 微信后台推送的 ticket + params := app.NewParamsWith(param) + // 请求 + request, err := app.request("https://api.weixin.qq.com/cgi-bin/component/api_component_token", params, http.MethodPost) + // 定义 + var response CgiBinComponentApiComponentTokenResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiComponentTokenResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.api_create_preauthcode.go b/cgi-bin.component.api_create_preauthcode.go new file mode 100644 index 0000000..c217181 --- /dev/null +++ b/cgi-bin.component.api_create_preauthcode.go @@ -0,0 +1,39 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentApiCreatePreAuthCodenResponse struct { + PreAuthCode string `json:"pre_auth_code"` // 预授权码 + ExpiresIn int64 `json:"expires_in"` // 有效期,单位:秒 +} + +type CgiBinComponentApiCreatePreAuthCodenResult struct { + Result CgiBinComponentApiCreatePreAuthCodenResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentApiCreatePreAuthCodenResult(result CgiBinComponentApiCreatePreAuthCodenResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentApiCreatePreAuthCodenResult { + return &CgiBinComponentApiCreatePreAuthCodenResult{Result: result, Body: body, Http: http, 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 { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 第三方平台 appid + params := app.NewParamsWith(param) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%v", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentApiCreatePreAuthCodenResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiCreatePreAuthCodenResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.api_get_authorizer_info.go b/cgi-bin.component.api_get_authorizer_info.go new file mode 100644 index 0000000..b7183e8 --- /dev/null +++ b/cgi-bin.component.api_get_authorizer_info.go @@ -0,0 +1,98 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentApiGetAuthorizerInfoResponse struct { + AuthorizerInfo struct { + NickName string `json:"nick_name"` // 昵称 + HeadImg string `json:"head_img"` // 头像 + ServiceTypeInfo struct { + Id int `json:"id"` // 0=普通小程序 2=门店小程序 3=门店小程序 4=小游戏 10=小商店 12=试用小程序 + } `json:"service_type_info"` // 小程序类型 + VerifyTypeInfo struct { + Id int `json:"id"` // -1=未认证 0=微信认证 + } `json:"verify_type_info"` // 小程序认证类型 + UserName string `json:"user_name"` // 原始 ID + PrincipalName string `json:"principal_name"` // 主体名称 + Signature string `json:"signature"` // 帐号介绍 + BusinessInfo struct { + OpenPay int `json:"open_pay"` + OpenShake int `json:"open_shake"` + OpenScan int `json:"open_scan"` + OpenCard int `json:"open_card"` + OpenStore int `json:"open_store"` + } `json:"business_info"` // 用以了解功能的开通状况(0代表未开通,1代表已开通) + QrcodeUrl string `json:"qrcode_url"` // 二维码图片的 URL,开发者最好自行也进行保存 + MiniProgramInfo struct { + Network struct { + RequestDomain []string `json:"RequestDomain"` + WsRequestDomain []string `json:"WsRequestDomain"` + UploadDomain []string `json:"UploadDomain"` + DownloadDomain []string `json:"DownloadDomain"` + BizDomain []string `json:"BizDomain"` + UDPDomain []string `json:"UDPDomain"` + TCPDomain []interface{} `json:"TCPDomain"` + NewRequestDomain []interface{} `json:"NewRequestDomain"` + NewWsRequestDomain []interface{} `json:"NewWsRequestDomain"` + NewUploadDomain []interface{} `json:"NewUploadDomain"` + NewDownloadDomain []interface{} `json:"NewDownloadDomain"` + NewBizDomain []interface{} `json:"NewBizDomain"` + NewUDPDomain []interface{} `json:"NewUDPDomain"` + NewTCPDomain []interface{} `json:"NewTCPDomain"` + } `json:"network"` // 小程序配置的合法域名信息 + Categories []struct { + First string `json:"first"` + Second string `json:"second"` + } `json:"categories"` // 小程序配置的类目信息 + VisitStatus int `json:"visit_status"` + } `json:"MiniProgramInfo"` // 小程序配置,根据这个字段判断是否为小程序类型授权 + Alias string `json:"alias"` // 公众号所设置的微信号,可能为空 + Idc int `json:"idc"` + } `json:"authorizer_info"` // 小程序帐号信息 + AuthorizationInfo struct { + AuthorizerAppid string `json:"authorizer_appid"` // 授权方 appid + FuncInfo []struct { + FuncscopeCategory struct { + Id int `json:"id"` + } `json:"funcscope_category"` + ConfirmInfo struct { + NeedConfirm int `json:"need_confirm"` + AlreadyConfirm int `json:"already_confirm"` + CanConfirm int `json:"can_confirm"` + } `json:"confirm_info,omitempty"` + } `json:"func_info"` // 授权给开发者的权限集列表 + AuthorizerRefreshToken string `json:"authorizer_refresh_token"` + } `json:"authorization_info"` // 授权信息 +} + +type CgiBinComponentApiGetAuthorizerInfoResult struct { + Result CgiBinComponentApiGetAuthorizerInfoResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentApiGetAuthorizerInfoResult(result CgiBinComponentApiGetAuthorizerInfoResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentApiGetAuthorizerInfoResult { + return &CgiBinComponentApiGetAuthorizerInfoResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinComponentApiGetAuthorizerInfo 获取授权帐号详情 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/api_get_authorizer_info.html +func (app *App) CgiBinComponentApiGetAuthorizerInfo() *CgiBinComponentApiGetAuthorizerInfoResult { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 第三方平台 appid + param["authorizer_appid"] = app.authorizerAppid // 授权方 appid + params := app.NewParamsWith(param) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%v", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentApiGetAuthorizerInfoResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiGetAuthorizerInfoResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.api_query_auth.go b/cgi-bin.component.api_query_auth.go new file mode 100644 index 0000000..b29e293 --- /dev/null +++ b/cgi-bin.component.api_query_auth.go @@ -0,0 +1,54 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentApiQueryAuthResponse struct { + AuthorizationInfo struct { + AuthorizerAppid string `json:"authorizer_appid"` // 授权方 appid + AuthorizerAccessToken string `json:"authorizer_access_token"` // 接口调用令牌(在授权的公众号/小程序具备 API 权限时,才有此返回值) + ExpiresIn int64 `json:"expires_in"` // authorizer_access_token 的有效期(在授权的公众号/小程序具备API权限时,才有此返回值),单位:秒 + AuthorizerRefreshToken string `json:"authorizer_refresh_token"` // 刷新令牌(在授权的公众号具备API权限时,才有此返回值),刷新令牌主要用于第三方平台获取和刷新已授权用户的 authorizer_access_token。一旦丢失,只能让用户重新授权,才能再次拿到新的刷新令牌。用户重新授权后,之前的刷新令牌会失效 + FuncInfo []struct { + FuncscopeCategory struct { + Id int `json:"id"` + } `json:"funcscope_category"` + ConfirmInfo struct { + NeedConfirm int `json:"need_confirm"` + AlreadyConfirm int `json:"already_confirm"` + CanConfirm int `json:"can_confirm"` + } `json:"confirm_info,omitempty"` + } `json:"func_info"` + } `json:"authorization_info"` +} + +type CgiBinComponentApiQueryAuthResult struct { + Result CgiBinComponentApiQueryAuthResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentApiQueryAuthResult(result CgiBinComponentApiQueryAuthResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentApiQueryAuthResult { + return &CgiBinComponentApiQueryAuthResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinComponentApiQueryAuth 使用授权码获取授权信息 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/authorization_info.html +func (app *App) CgiBinComponentApiQueryAuth(authorizationCode string) *CgiBinComponentApiQueryAuthResult { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 第三方平台 appid + param["authorization_code"] = authorizationCode // 授权码, 会在授权成功时返回给第三方平台 + params := app.NewParamsWith(param) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%v", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentApiQueryAuthResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiQueryAuthResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.api_start_push_ticket.go b/cgi-bin.component.api_start_push_ticket.go new file mode 100644 index 0000000..9aad174 --- /dev/null +++ b/cgi-bin.component.api_start_push_ticket.go @@ -0,0 +1,41 @@ +package wechatopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentApiStartPushTicketResponse struct { + AccessToken string `json:"access_token"` // 获取到的凭证 + ExpiresIn int `json:"expires_in"` // 凭证有效时间,单位:秒。目前是7200秒之内的值 + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 +} + +type CgiBinComponentApiStartPushTicketResult struct { + Result CgiBinComponentApiStartPushTicketResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentApiStartPushTicketResult(result CgiBinComponentApiStartPushTicketResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentApiStartPushTicketResult { + return &CgiBinComponentApiStartPushTicketResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinComponentApiStartPushTicket 启动ticket推送服务 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/component_verify_ticket_service.html +func (app *App) CgiBinComponentApiStartPushTicket() *CgiBinComponentApiStartPushTicketResult { + // 参数 + param := NewParams() + param["component_appid"] = app.componentAppId // 平台型第三方平台的appid + param["component_secret"] = app.componentAppSecret // 平台型第三方平台的APPSECRET + params := app.NewParamsWith(param) + // 请求 + request, err := app.request("https://api.weixin.qq.com/cgi-bin/component/api_start_push_ticket", params, http.MethodPost) + // 定义 + var response CgiBinComponentApiStartPushTicketResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentApiStartPushTicketResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.getprivacysetting.go b/cgi-bin.component.getprivacysetting.go new file mode 100644 index 0000000..d79b99b --- /dev/null +++ b/cgi-bin.component.getprivacysetting.go @@ -0,0 +1,71 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentGetPrivacySettingResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 返回码信息 + CodeExist int `json:"code_exist"` // 代码是否存在, 0 不存在, 1 存在 。如果最近没有通过commit接口上传代码,则会出现 code_exist=0的情况。 + PrivacyList []string `json:"privacy_list"` // 代码检测出来的用户信息类型(privacy_key) + SettingList []struct { + PrivacyKey string `json:"privacy_key"` // 用户信息类型的英文名称 + PrivacyText string `json:"privacy_text"` // 该用户信息类型的用途 + PrivacyLabel string `json:"privacy_label"` // 用户信息类型的中文名称 + } `json:"setting_list"` // 要收集的用户信息配置 + UpdateTime int `json:"update_time"` // 更新时间 + OwnerSetting struct { + ContactPhone string `json:"contact_phone"` // 信息收集方(开发者)的邮箱 + ContactEmail string `json:"contact_email"` // 信息收集方(开发者)的手机号 + ContactQq string `json:"contact_qq"` // 信息收集方(开发者)的qq + ContactWeixin string `json:"contact_weixin"` // 信息收集方(开发者)的微信号 + NoticeMethod string `json:"notice_method"` // 通知方式,指的是当开发者收集信息有变动时,通过该方式通知用户 + StoreExpireTimestamp string `json:"store_expire_timestamp"` // 存储期限,指的是开发者收集用户信息存储多久 + ExtFileMediaId string `json:"ext_file_media_id"` // 自定义 用户隐私保护指引文件的media_id + } `json:"owner_setting"` // 收集方(开发者)信息配置 + PrivacyDesc struct { + PrivacyDescList []struct { + PrivacyKey string `json:"privacy_key"` // 用户信息类型的英文key + PrivacyDesc string `json:"privacy_desc"` // 用户信息类型的中文描述 + } `json:"privacy_desc_list"` // 用户信息类型 + } `json:"privacy_desc"` // 用户信息类型对应的中英文描述 + SdkPrivacyInfoList []struct { + SdkName string `json:"sdk_name"` // sdk的名称 + SdkBizName string `json:"sdk_biz_name"` // sdk提供方的主体名称 + SdkList []struct { + PrivacyKey string `json:"privacy_key"` // sdk收集的信息描述 + PrivacyText string `json:"privacy_text"` // sdk收集的信息用途说明 + PrivacyLabel string `json:"privacy_label"` + } `json:"sdk_list"` // sdk收集的信息以及用途 + } `json:"sdk_privacy_info_list"` // sdk +} + +type CgiBinComponentGetPrivacySettingResult struct { + Result CgiBinComponentGetPrivacySettingResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentGetPrivacySettingResult(result CgiBinComponentGetPrivacySettingResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentGetPrivacySettingResult { + return &CgiBinComponentGetPrivacySettingResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinComponentGetPrivacySetting 查询小程序用户隐私保护指引 +// @privacyVer 1表示现网版本,即,传1则该接口返回的内容是现网版本的;2表示开发版,即,传2则该接口返回的内容是开发版本的。默认是2。 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/get_privacy_setting.html +func (app *App) CgiBinComponentGetPrivacySetting(privacyVer int) *CgiBinComponentGetPrivacySettingResult { + // 参数 + params := NewParams() + params["privacy_ver"] = privacyVer + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/getprivacysetting?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentGetPrivacySettingResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentGetPrivacySettingResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.component.setprivacysetting.go b/cgi-bin.component.setprivacysetting.go new file mode 100644 index 0000000..4ff290e --- /dev/null +++ b/cgi-bin.component.setprivacysetting.go @@ -0,0 +1,56 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinComponentSetPrivacySettingResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 返回码信息 +} + +type CgiBinComponentSetPrivacySettingResult struct { + Result CgiBinComponentSetPrivacySettingResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinComponentSetPrivacySettingResult(result CgiBinComponentSetPrivacySettingResponse, body []byte, http gorequest.Response, err error) *CgiBinComponentSetPrivacySettingResult { + return &CgiBinComponentSetPrivacySettingResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinComponentSetPrivacySetting 配置小程序用户隐私保护指引 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html +func (app *App) CgiBinComponentSetPrivacySetting(notMustParams ...Params) *CgiBinComponentSetPrivacySettingResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/component/setprivacysetting?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinComponentSetPrivacySettingResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinComponentSetPrivacySettingResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *CgiBinComponentSetPrivacySettingResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 86069: + return "owner_setting必填字段字段缺失" + case 86070: + return "notice_method必填字段字段缺失" + case 86072: + return "store_expire_timestamp参数无效。如果是编码格式不对,也会报这个错" + case 86073: + return "ext_file_media_id参数无效" + case 86074: + return "现网隐私协议不存在" + case 86075: + return "现网隐私协议的ext_file_media_id禁止修改" + } + return "系统繁忙" +} diff --git a/cgi-bin.get_api_domain_ip.go b/cgi-bin.get_api_domain_ip.go new file mode 100644 index 0000000..ebc1cd8 --- /dev/null +++ b/cgi-bin.get_api_domain_ip.go @@ -0,0 +1,34 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type GetCallBackIpResponse struct { + IpList []string `json:"ip_list"` +} + +type GetCallBackIpResult struct { + Result GetCallBackIpResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewGetCallBackIpResult(result GetCallBackIpResponse, body []byte, http gorequest.Response, err error) *GetCallBackIpResult { + return &GetCallBackIpResult{Result: result, Body: body, Http: http, 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 { + // 请求 + request, 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(request.ResponseBody, &response) + return NewGetCallBackIpResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.shorturl.go b/cgi-bin.shorturl.go new file mode 100644 index 0000000..3cbcce4 --- /dev/null +++ b/cgi-bin.shorturl.go @@ -0,0 +1,40 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinShortUrlResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + ShortUrl string `json:"short_url"` // 短链接。 +} + +type CgiBinShortUrlResult struct { + Result CgiBinShortUrlResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinShortUrlResult(result CgiBinShortUrlResponse, body []byte, http gorequest.Response, err error) *CgiBinShortUrlResult { + return &CgiBinShortUrlResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinShortUrl 将二维码长链接转成短链接 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/shorturl.html +func (app *App) CgiBinShortUrl(longUrl string) *CgiBinShortUrlResult { + // 参数 + params := NewParams() + params["action"] = "long2short" // 此处填long2short,代表长链接转短链接 + params["long_url"] = longUrl // 需要转换的长链接,支持http://、https://、weixin://wxpay 格式的url + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinShortUrlResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinShortUrlResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.wxopen.getcategory.go b/cgi-bin.wxopen.getcategory.go new file mode 100644 index 0000000..bc17b56 --- /dev/null +++ b/cgi-bin.wxopen.getcategory.go @@ -0,0 +1 @@ +package wechatopen diff --git a/cgi-bin.wxopen.qrcodejumpadd.go b/cgi-bin.wxopen.qrcodejumpadd.go new file mode 100644 index 0000000..0fbe30c --- /dev/null +++ b/cgi-bin.wxopen.qrcodejumpadd.go @@ -0,0 +1,62 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinWxOpenQrCodeJumpAddResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type CgiBinWxOpenQrCodeJumpAddResult struct { + Result CgiBinWxOpenQrCodeJumpAddResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinWxOpenQrCodeJumpAddResult(result CgiBinWxOpenQrCodeJumpAddResponse, body []byte, http gorequest.Response, err error) *CgiBinWxOpenQrCodeJumpAddResult { + return &CgiBinWxOpenQrCodeJumpAddResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinWxOpenQrCodeJumpAdd 增加或修改二维码规则 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/qrcodejumpadd.html +func (app *App) CgiBinWxOpenQrCodeJumpAdd(notMustParams ...Params) *CgiBinWxOpenQrCodeJumpAddResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpadd?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinWxOpenQrCodeJumpAddResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinWxOpenQrCodeJumpAddResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *CgiBinWxOpenQrCodeJumpAddResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 44990: + return "接口请求太快(超过5次/秒)" + case 85066: + return "链接错误" + case 85068: + return "测试链接不是子链接" + case 85069: + return "校验文件失败" + case 85070: + return "URL命中黑名单,无法添加" + case 85071: + return "已添加该链接,请勿重复添加" + case 85072: + return "该链接已被占用" + case 85073: + return "二维码规则已满" + case 85075: + return "个人类型小程序无法设置二维码规则" + } + return "系统繁忙" +} diff --git a/cgi-bin.wxopen.qrcodejumpdelete.go b/cgi-bin.wxopen.qrcodejumpdelete.go new file mode 100644 index 0000000..f68da61 --- /dev/null +++ b/cgi-bin.wxopen.qrcodejumpdelete.go @@ -0,0 +1,38 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinWxOpenQrCodeJumpDeleteResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type CgiBinWxOpenQrCodeJumpDeleteResult struct { + Result CgiBinWxOpenQrCodeJumpDeleteResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinWxOpenQrCodeJumpDeleteResult(result CgiBinWxOpenQrCodeJumpDeleteResponse, body []byte, http gorequest.Response, err error) *CgiBinWxOpenQrCodeJumpDeleteResult { + return &CgiBinWxOpenQrCodeJumpDeleteResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinWxOpenQrCodeJumpDelete 删除已设置的二维码规则 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/qrcodejumpdelete.html +func (app *App) CgiBinWxOpenQrCodeJumpDelete(prefix string) *CgiBinWxOpenQrCodeJumpDeleteResult { + // 参数 + params := NewParams() + params["prefix"] = prefix + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdelete?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinWxOpenQrCodeJumpDeleteResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinWxOpenQrCodeJumpDeleteResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.wxopen.qrcodejumpdownloa.go b/cgi-bin.wxopen.qrcodejumpdownloa.go new file mode 100644 index 0000000..57a0f19 --- /dev/null +++ b/cgi-bin.wxopen.qrcodejumpdownloa.go @@ -0,0 +1,39 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinWxOpenQrCodeJumpDownloadResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + FileName string `json:"file_name"` + FileContent string `json:"file_content"` +} + +type CgiBinWxOpenQrCodeJumpDownloadResult struct { + Result CgiBinWxOpenQrCodeJumpDownloadResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinWxOpenQrCodeJumpDownloadResult(result CgiBinWxOpenQrCodeJumpDownloadResponse, body []byte, http gorequest.Response, err error) *CgiBinWxOpenQrCodeJumpDownloadResult { + return &CgiBinWxOpenQrCodeJumpDownloadResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinWxOpenQrCodeJumpDownload 获取校验文件名称及内容 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/qrcodejumpdownload.html +func (app *App) CgiBinWxOpenQrCodeJumpDownload() *CgiBinWxOpenQrCodeJumpDownloadResult { + // 参数 + params := NewParams() + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdownload?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinWxOpenQrCodeJumpDownloadResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinWxOpenQrCodeJumpDownloadResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.wxopen.qrcodejumpget.go b/cgi-bin.wxopen.qrcodejumpget.go new file mode 100644 index 0000000..c4016e0 --- /dev/null +++ b/cgi-bin.wxopen.qrcodejumpget.go @@ -0,0 +1,48 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinWxOpenQrCodeJumpGetResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + RuleList []struct { + Prefix string `json:"prefix"` // 二维码规则 + PermitSubRule int `json:"permit_sub_rule"` // 是否独占符合二维码前缀匹配规则的所有子规 1 为不占用,2 为占用 + Path string `json:"path"` // 小程序功能页面 + OpenVersion int `json:"open_version"` // 测试范围 + DebugUrl []string `json:"debug_url"` // 测试链接(选填)可填写不多于 5 个用于测试的二维码完整链接,此链接必须符合已填写的二维码规则。 + State int `json:"state"` // 发布标志位,1 表示未发布,2 表示已发布 + } `json:"rule_list"` // 二维码规则详情列表 + QrcodejumpOpen int `json:"qrcodejump_open"` // 是否已经打开二维码跳转链接设置 + ListSize int `json:"list_size"` // 二维码规则数量 + QrcodejumpPubQuota int `json:"qrcodejump_pub_quota"` // 本月还可发布的次数 +} + +type CgiBinWxOpenQrCodeJumpGetResult struct { + Result CgiBinWxOpenQrCodeJumpGetResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinWxOpenQrCodeJumpGetResult(result CgiBinWxOpenQrCodeJumpGetResponse, body []byte, http gorequest.Response, err error) *CgiBinWxOpenQrCodeJumpGetResult { + return &CgiBinWxOpenQrCodeJumpGetResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinWxOpenQrCodeJumpGet 获取已设置的二维码规则 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/qrcodejumpadd.html +func (app *App) CgiBinWxOpenQrCodeJumpGet() *CgiBinWxOpenQrCodeJumpGetResult { + // 参数 + params := NewParams() + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpget?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinWxOpenQrCodeJumpGetResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinWxOpenQrCodeJumpGetResult(response, request.ResponseBody, request, err) +} diff --git a/cgi-bin.wxopen.qrcodejumppublish.go b/cgi-bin.wxopen.qrcodejumppublish.go new file mode 100644 index 0000000..94a90fa --- /dev/null +++ b/cgi-bin.wxopen.qrcodejumppublish.go @@ -0,0 +1,55 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type CgiBinWxOpenQrCodeJumpPublishResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type CgiBinWxOpenQrCodeJumpPublishResult struct { + Result CgiBinWxOpenQrCodeJumpPublishResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCgiBinWxOpenQrCodeJumpPublishResult(result CgiBinWxOpenQrCodeJumpPublishResponse, body []byte, http gorequest.Response, err error) *CgiBinWxOpenQrCodeJumpPublishResult { + return &CgiBinWxOpenQrCodeJumpPublishResult{Result: result, Body: body, Http: http, Err: err} +} + +// CgiBinWxOpenQrCodeJumpPublish 发布已设置的二维码规则 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/qrcode/qrcodejumppublish.html +func (app *App) CgiBinWxOpenQrCodeJumpPublish(prefix string) *CgiBinWxOpenQrCodeJumpPublishResult { + // 参数 + params := NewParams() + params["prefix"] = prefix + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumppublish?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response CgiBinWxOpenQrCodeJumpPublishResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCgiBinWxOpenQrCodeJumpPublishResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *CgiBinWxOpenQrCodeJumpPublishResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 44990: + return "接口请求太快(超过5次/秒)" + case 85074: + return "小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则" + case 85075: + return "个人类型小程序无法设置二维码规则" + case 85095: + return "数据异常,请删除后重新添加" + case 886000: + return "本月发布次数达到上线(100次)" + } + return "系统繁忙" +} diff --git a/get.go b/get.go new file mode 100644 index 0000000..9ed4886 --- /dev/null +++ b/get.go @@ -0,0 +1,5 @@ +package wechatopen + +func (app *App) GetComponentAppId() string { + return app.componentAppId +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d5fc7ca --- /dev/null +++ b/go.mod @@ -0,0 +1,35 @@ +module go.dtapp.net/wechatopen + +go 1.18 + +require ( + github.com/mitchellh/mapstructure v1.5.0 + go.dtapp.net/gojson v1.0.0 + go.dtapp.net/golog v1.0.13 + go.dtapp.net/goredis v1.0.1 + go.dtapp.net/gorequest v1.0.19 + gorm.io/datatypes v1.0.6 + gorm.io/gorm v1.23.6 +) + +require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect + github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + go.dtapp.net/goip v1.0.17 // indirect + go.dtapp.net/gostring v1.0.3 // indirect + go.dtapp.net/gotime v1.0.2 // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect + golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.5 // indirect + gorm.io/driver/mysql v1.3.4 // indirect + gorm.io/driver/postgres v1.3.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4ddd3dc --- /dev/null +++ b/go.sum @@ -0,0 +1,269 @@ +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= +github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= +github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= +github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= +github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= +github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= +github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda h1:h+YpzUB/bGVJcLqW+d5GghcCmE/A25KbzjXvWJQi/+o= +github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda/go.mod h1:MSotTrCv1PwoR8QgU1JurEx+lNNbtr25I+m0zbLyAGw= +github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f h1:PF9WV5j/x6MT+x/sauUHd4objCvJbZb0wdxZkHSdd5A= +github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f h1:1cJITU3JUI8qNS5T0BlXwANsVdyoJQHQ4hvOxbunPCw= +github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f/go.mod h1:LyBTue+RWeyIfN3ZJ4wVxvDuvlGJtDgCLgCb6HCPgps= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.dtapp.net/goip v1.0.17 h1:xkUSTwMNjx1uc4MCs6vmSmiDNlcFNlCTA1tqrM9fmAY= +go.dtapp.net/goip v1.0.17/go.mod h1:VrFnytj/KJH81m7Hx43mekbqJvsHbiNOS1Ikm2XaFFw= +go.dtapp.net/gojson v1.0.0 h1:jmRjeWChRyv2tKEByHvnW3kXh1jUcL8B7VurV0Zbygc= +go.dtapp.net/gojson v1.0.0/go.mod h1:TkkpTNxHBKxul0e7gC5MrL1K4ICFB9mQ7wHzjBah3/k= +go.dtapp.net/golog v1.0.13 h1:SnU6G4onDYZPOfZ9cgmj5rHdtGGOWP/Qee31aM49Wj0= +go.dtapp.net/golog v1.0.13/go.mod h1:6w5Lt1x6/yUN3iptAi59irm4kqDJHaolDsrZ9ApsZUQ= +go.dtapp.net/goredis v1.0.1 h1:TyjPXj8juXlu/OoCUFSnWWDT0UYRBJH+H+AMmQYKBtQ= +go.dtapp.net/goredis v1.0.1/go.mod h1:Wmrgb5yfbV7SiIK0NLdBOFWKnrQs+5g8p3t5+cjQkMM= +go.dtapp.net/gorequest v1.0.19 h1:ZBkXb/oD59aChfzHfReK6M3M4eu3dwpaPTefe1c8P90= +go.dtapp.net/gorequest v1.0.19/go.mod h1:EwOfdfxsWPszOWrphCWHTN4DbYtU6fyQ/fuWQyQwSnk= +go.dtapp.net/gostring v1.0.3 h1:KSOq4D77/g5yZN/bqWfZ0kOOaPr/P1240vg03+XdENI= +go.dtapp.net/gostring v1.0.3/go.mod h1:+ggrOvgQDQturi1QGsXEpyRN/ZPoRDaqhMujIk5lrgQ= +go.dtapp.net/gotime v1.0.2 h1:CFIJHQXC/4t9bsJhk2cLhjHd6rpdPcJXr8BcHKHDuQo= +go.dtapp.net/gotime v1.0.2/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.0.6 h1:3cqbakp1DIgC+P7wyODb5k+lSjW8g3mjkg/BIsmhjlE= +gorm.io/datatypes v1.0.6/go.mod h1:Gh/Xd/iUWWybMEk8CzYCK/swqlni2r+ROeM1HGIM0ck= +gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= +gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q= +gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE= +gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= +gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ= +gorm.io/driver/postgres v1.3.7/go.mod h1:f02ympjIcgtHEGFMZvdgTxODZ9snAHDb4hXfigBVuNI= +gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk= +gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= +gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI= +gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= +gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0= +gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/params.go b/params.go new file mode 100644 index 0000000..b404f31 --- /dev/null +++ b/params.go @@ -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 + } +} diff --git a/pgsql.go b/pgsql.go new file mode 100644 index 0000000..7942cd7 --- /dev/null +++ b/pgsql.go @@ -0,0 +1,26 @@ +package wechatopen + +import ( + "go.dtapp.net/gojson" + "go.dtapp.net/golog" + "go.dtapp.net/gorequest" + "gorm.io/datatypes" +) + +// 记录日志 +func (app *App) postgresqlLog(request gorequest.Response) { + app.log.Record(golog.ApiPostgresqlLog{ + RequestTime: golog.TimeString{Time: request.RequestTime}, //【请求】时间 + RequestUri: request.RequestUri, //【请求】链接 + RequestUrl: gorequest.UriParse(request.RequestUri).Url, //【请求】链接 + RequestApi: gorequest.UriParse(request.RequestUri).Path, //【请求】接口 + RequestMethod: request.RequestMethod, //【请求】方式 + RequestParams: datatypes.JSON(gojson.JsonEncodeNoError(request.RequestParams)), //【请求】参数 + RequestHeader: datatypes.JSON(gojson.JsonEncodeNoError(request.RequestHeader)), //【返回】头部 + ResponseHeader: datatypes.JSON(gojson.JsonEncodeNoError(request.ResponseHeader)), //【返回】头部 + ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码 + ResponseBody: request.ResponseBody, //【返回】内容 + ResponseContentLength: request.ResponseContentLength, //【返回】大小 + ResponseTime: golog.TimeString{Time: request.ResponseTime}, //【返回】时间 + }) +} diff --git a/redis.go b/redis.go new file mode 100644 index 0000000..543d860 --- /dev/null +++ b/redis.go @@ -0,0 +1,141 @@ +package wechatopen + +import ( + "context" + "fmt" + "time" +) + +// 微信后台推送的ticke +func (app *App) getComponentVerifyTicketCacheKeyName() string { + return fmt.Sprintf("wechat_open:component_verify_ticket:%v", app.componentAppId) +} + +// SetComponentVerifyTicket 设置微信后台推送的ticke +func (app *App) SetComponentVerifyTicket(componentVerifyTicket string) string { + if componentVerifyTicket == "" { + return "" + } + app.redis.Db.Set(context.Background(), app.getComponentVerifyTicketCacheKeyName(), componentVerifyTicket, time.Hour*12) + return app.GetComponentVerifyTicket() +} + +// GetComponentVerifyTicket 获取微信后台推送的ticke +func (app *App) GetComponentVerifyTicket() string { + if app.redis.Db == nil { + return app.componentVerifyTicket + } + result, _ := app.redis.Db.Get(context.Background(), app.getComponentVerifyTicketCacheKeyName()).Result() + return result +} + +// 令牌 +func (app *App) getComponentAccessTokenCacheKeyName() string { + return fmt.Sprintf("wechat_open:component_access_token:%v", app.componentAppId) +} + +// SetComponentAccessToken 设置令牌 +func (app *App) SetComponentAccessToken(componentAccessToken string) string { + if componentAccessToken == "" { + return "" + } + app.redis.Db.Set(context.Background(), app.getComponentAccessTokenCacheKeyName(), componentAccessToken, time.Second*7200) + return app.GetComponentAccessToken() +} + +// GetComponentAccessToken 获取令牌 +func (app *App) GetComponentAccessToken() string { + if app.redis.Db == nil { + return app.componentAccessToken + } + result, _ := app.redis.Db.Get(context.Background(), app.getComponentAccessTokenCacheKeyName()).Result() + return result +} + +// MonitorComponentAccessToken 监控令牌 +func (app *App) MonitorComponentAccessToken() string { + // 查询 + componentAccessToken := app.GetComponentAccessToken() + // 判断 + result := app.CgiBinGetApiDomainIp(componentAccessToken) + if len(result.Result.IpList) > 0 { + return componentAccessToken + } + // 重新获取 + return app.SetComponentAccessToken(app.CgiBinComponentApiComponentToken().Result.ComponentAccessToken) +} + +// 授权方令牌 +func (app *App) getAuthorizerAccessTokenCacheKeyName() string { + return fmt.Sprintf("wechat_open:authorizer_access_token:%v:%v", app.componentAppId, app.authorizerAppid) +} + +// SetAuthorizerAccessToken 设置授权方令牌 +func (app *App) SetAuthorizerAccessToken(authorizerAccessToken string) string { + if authorizerAccessToken == "" { + return "" + } + app.redis.Db.Set(context.Background(), app.getAuthorizerAccessTokenCacheKeyName(), authorizerAccessToken, time.Hour*2) + return app.GetComponentAccessToken() +} + +// GetAuthorizerAccessToken 获取授权方令牌 +func (app *App) GetAuthorizerAccessToken() string { + if app.redis.Db == nil { + return app.authorizerAccessToken + } + result, _ := app.redis.Db.Get(context.Background(), app.getAuthorizerAccessTokenCacheKeyName()).Result() + return result +} + +// MonitorAuthorizerAccessToken 监控授权方令牌 +func (app *App) MonitorAuthorizerAccessToken(authorizerRefreshToken string) string { + // 查询 + authorizerAccessToken := app.GetAuthorizerAccessToken() + // 判断 + if authorizerAccessToken != "" { + return authorizerAccessToken + } + // 重新获取 + return app.SetAuthorizerAccessToken(app.CgiBinComponentApiAuthorizerToken(authorizerRefreshToken).Result.AuthorizerAccessToken) +} + +// 预授权码 +func (app *App) getPreAuthCodeCacheKeyName() string { + return fmt.Sprintf("wechat_open:pre_auth_code:%v", app.componentAppId) +} + +// SetPreAuthCode 设置预授权码 +func (app *App) SetPreAuthCode(preAuthCode string) string { + if preAuthCode == "" { + return "" + } + app.redis.Db.Set(context.Background(), app.getPreAuthCodeCacheKeyName(), preAuthCode, time.Second*1700) + return app.GetComponentAccessToken() +} + +// GetPreAuthCode 获取预授权码 +func (app *App) GetPreAuthCode() string { + if app.redis.Db == nil { + return app.authorizerAccessToken + } + result, _ := app.redis.Db.Get(context.Background(), app.getPreAuthCodeCacheKeyName()).Result() + return result +} + +// DelPreAuthCode 删除预授权码 +func (app *App) DelPreAuthCode() error { + return app.redis.Db.Del(context.Background(), app.getPreAuthCodeCacheKeyName()).Err() +} + +// MonitorPreAuthCode 监控预授权码 +func (app *App) MonitorPreAuthCode() string { + // 查询 + preAuthCode := app.GetPreAuthCode() + // 判断 + if preAuthCode != "" { + return preAuthCode + } + // 重新获取 + return app.SetPreAuthCode(app.CgiBinComponentApiCreatePreAuthCoden().Result.PreAuthCode) +} diff --git a/save_img.go b/save_img.go new file mode 100644 index 0000000..bcf8147 --- /dev/null +++ b/save_img.go @@ -0,0 +1,41 @@ +package wechatopen + +import ( + "go.dtapp.net/gorequest" + "log" + "os" +) + +type SaveImgResponse struct { + Path string + Name string +} + +func (app *App) SaveImg(resp gorequest.Response, dir, saveName string) SaveImgResponse { + // 返回是二进制图片,或者json错误 + if resp.ResponseHeader.Get("Content-Type") == "image/jpeg" || resp.ResponseHeader.Get("Content-Type") == "image/png" { + // 保存在output目录 + outputFileName := saveName + + if resp.ResponseHeader.Get("Content-Type") == "image/jpeg" { + outputFileName = outputFileName + ".jpg" + } else { + outputFileName = outputFileName + ".png" + } + here: + log.Println(dir + outputFileName) + f, err := os.OpenFile(dir+outputFileName, os.O_CREATE|os.O_RDWR, 0666) + log.Println(err) + if err != nil { + os.Mkdir(dir, 0755) + goto here + } + f.Write(resp.ResponseBody) + f.Close() + return SaveImgResponse{ + Path: dir + outputFileName, + Name: outputFileName, + } + } + return SaveImgResponse{} +} diff --git a/service_http.authorizer_appid.go b/service_http.authorizer_appid.go new file mode 100644 index 0000000..8249000 --- /dev/null +++ b/service_http.authorizer_appid.go @@ -0,0 +1,58 @@ +package wechatopen + +import ( + "errors" + "net/http" + "strconv" +) + +// ServeHttpAuthorizerAppid 授权跳转 +func (app *App) ServeHttpAuthorizerAppid(r *http.Request) (resp CgiBinComponentApiQueryAuthResponse, agentUserId string, err error) { + var ( + query = r.URL.Query() + + authCode = query.Get("auth_code") + expiresIn = query.Get("expires_in") + ) + + agentUserId = query.Get("agent_user_id") + + if authCode == "" { + return resp, agentUserId, errors.New("找不到授权码参数") + } + + if expiresIn == "" { + return resp, agentUserId, errors.New("找不到过期时间参数") + } + + info := app.CgiBinComponentApiQueryAuth(authCode) + if info.Result.AuthorizationInfo.AuthorizerAppid == "" { + return resp, agentUserId, errors.New("获取失败") + } + + return info.Result, agentUserId, 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 +} diff --git a/service_http.verify_ticket.go b/service_http.verify_ticket.go new file mode 100644 index 0000000..b965ac2 --- /dev/null +++ b/service_http.verify_ticket.go @@ -0,0 +1,131 @@ +package wechatopen + +import ( + "encoding/base64" + "encoding/xml" + "errors" + "fmt" + "github.com/mitchellh/mapstructure" + "io/ioutil" + "net/http" + "strings" +) + +// ResponseServeHttpVerifyTicket 验证票据推送 +type ResponseServeHttpVerifyTicket struct { + XMLName xml.Name + AppId string `xml:"appId" json:"appId"` // 第三方平台 appid + CreateTime int64 `xml:"CreateTime" json:"CreateTime"` // 时间戳,单位:s + InfoType string `xml:"InfoType" json:"InfoType"` // 固定为:"component_verify_ticket" + ComponentVerifyTicket string `xml:"ComponentVerifyTicket" json:"ComponentVerifyTicket"` // Ticket 内容 +} + +type cipherRequestHttpBody struct { + AppId string `xml:"appId" json:"appId"` // 第三方平台 appid + Encrypt string `xml:"Encrypt" json:"Encrypt"` // 加密内容 +} + +// ServeHttpVerifyTicket 验证票据推送 +func (app *App) ServeHttpVerifyTicket(r *http.Request) (resp *ResponseServeHttpVerifyTicket, err error) { + var ( + query = r.URL.Query() + + wantSignature string + haveSignature = query.Get("signature") + timestamp = query.Get("timestamp") + nonce = query.Get("nonce") + + // post + haveMsgSignature = query.Get("msg_signature") + encryptType = query.Get("encrypt_type") + + // handle vars + data []byte + requestHttpBody = &cipherRequestHttpBody{} + ) + + if haveSignature == "" { + err = errors.New("找不到签名参数") + return + } + + if timestamp == "" { + return resp, errors.New("找不到时间戳参数") + } + + if nonce == "" { + return resp, errors.New("未找到随机数参数") + } + + wantSignature = Sign(app.messageToken, timestamp, nonce) + if haveSignature != wantSignature { + return resp, errors.New("签名错误") + } + + // 进入事件执行 + if encryptType != "aes" { + err = errors.New("未知的加密类型: " + encryptType) + return + } + if haveMsgSignature == "" { + err = errors.New("找不到签名参数") + return + } + + data, err = ioutil.ReadAll(r.Body) + if err != nil { + return resp, err + } + + xmlDecode := XmlDecode(string(data)) + if len(xmlDecode) <= 0 { + return resp, errors.New(fmt.Sprintf("Xml解码错误:%s", xmlDecode)) + } + + err = mapstructure.Decode(xmlDecode, &requestHttpBody) + if err != nil { + return resp, errors.New(fmt.Sprintf("mapstructure 解码错误:%s", xmlDecode)) + } + + if requestHttpBody.Encrypt == "" { + return resp, errors.New(fmt.Sprintf("未找到加密数据:%s", requestHttpBody)) + } + + cipherData, err := base64.StdEncoding.DecodeString(requestHttpBody.Encrypt) + if err != nil { + return resp, errors.New(fmt.Sprintf("Encrypt 解码字符串错误:%v", err)) + } + + AesKey, err := base64.StdEncoding.DecodeString(app.messageKey + "=") + if err != nil { + return resp, errors.New(fmt.Sprintf("messageKey 解码字符串错误:%v", err)) + } + + msg, err := AesDecrypt(cipherData, AesKey) + if err != nil { + return resp, errors.New(fmt.Sprintf("AES解密错误:%v", err)) + } + + str := string(msg) + + left := strings.Index(str, "") + if left <= 0 { + return resp, errors.New(fmt.Sprintf("匹配不到:%v", left)) + } + right := strings.Index(str, "") + if right <= 0 { + return resp, errors.New(fmt.Sprintf("匹配不到:%v", right)) + } + msgStr := str[left:right] + if len(msgStr) == 0 { + return resp, errors.New(fmt.Sprintf("提取错误:%v", msgStr)) + } + + resp = &ResponseServeHttpVerifyTicket{} + err = xml.Unmarshal([]byte(msgStr+""), resp) + if err != nil { + return resp, errors.New(fmt.Sprintf("解析错误:%v", err)) + } + + return resp, nil +} diff --git a/set.go b/set.go new file mode 100644 index 0000000..b539289 --- /dev/null +++ b/set.go @@ -0,0 +1,7 @@ +package wechatopen + +// SetAuthorizerAppid 设置代理商小程序 +func (app *App) SetAuthorizerAppid(authorizerAppid string) { + app.authorizerAppid = authorizerAppid + return +} diff --git a/sign.go b/sign.go new file mode 100644 index 0000000..a157702 --- /dev/null +++ b/sign.go @@ -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 +} diff --git a/sns.component.jscode2session.go b/sns.component.jscode2session.go new file mode 100644 index 0000000..d3378ab --- /dev/null +++ b/sns.component.jscode2session.go @@ -0,0 +1,132 @@ +package wechatopen + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/json" + "errors" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewSnsComponentJsCode2sessionResult(result SnsComponentJsCode2sessionResponse, body []byte, http gorequest.Response, err error) *SnsComponentJsCode2sessionResult { + return &SnsComponentJsCode2sessionResult{Result: result, Body: body, Http: http, 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 { + // 参数 + 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.GetComponentAccessToken() // 第三方平台的component_access_token + // 请求 + request, err := app.request("https://api.weixin.qq.com/sns/component/jscode2session", params, http.MethodGet) + // 定义 + var response SnsComponentJsCode2sessionResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewSnsComponentJsCode2sessionResult(response, request.ResponseBody, request, 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 +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..d047e40 --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package wechatopen + +const Version = "1.0.0" diff --git a/wxa.addtotemplate.go b/wxa.addtotemplate.go new file mode 100644 index 0000000..d1abf0f --- /dev/null +++ b/wxa.addtotemplate.go @@ -0,0 +1,50 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaAddToTemplateResponse struct { + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 +} + +type WxaAddToTemplateResult struct { + Result WxaAddToTemplateResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaAddToTemplateResult(result WxaAddToTemplateResponse, body []byte, http gorequest.Response, err error) *WxaAddToTemplateResult { + return &WxaAddToTemplateResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaAddToTemplate 将草稿添加到代码模板库 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/code_template/addtotemplate.html +func (app *App) WxaAddToTemplate(draftId string, templateType int) *WxaAddToTemplateResult { + // 参数 + params := NewParams() + params["draft_id"] = draftId + params["template_type"] = templateType + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/addtotemplate?access_token=%s", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response WxaAddToTemplateResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaAddToTemplateResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaAddToTemplateResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85064: + return "找不到草稿" + case 85065: + return "模板库已满" + } + return "系统繁忙" +} diff --git a/wxa.bind_tester.go b/wxa.bind_tester.go new file mode 100644 index 0000000..ec8ff03 --- /dev/null +++ b/wxa.bind_tester.go @@ -0,0 +1,54 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaBindTesterResponse struct { + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 + Userstr string `json:"userstr"` // 人员对应的唯一字符串 +} + +type WxaBindTesterResult struct { + Result WxaBindTesterResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaBindTesterResult(result WxaBindTesterResponse, body []byte, http gorequest.Response, err error) *WxaBindTesterResult { + return &WxaBindTesterResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := NewParams() + params["wechatid"] = wechatid + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/bind_tester?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaBindTesterResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaBindTesterResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaBindTesterResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85001: + return "微信号不存在或微信号设置为不可搜索" + case 85002: + return "小程序绑定的体验者数量达到上限" + case 85003: + return "微信号绑定的小程序体验者达到上限" + case 85004: + return "微信号已经绑定" + } + return "系统繁忙" +} diff --git a/wxa.business.getuserphonenumber.go b/wxa.business.getuserphonenumber.go new file mode 100644 index 0000000..4dcbc54 --- /dev/null +++ b/wxa.business.getuserphonenumber.go @@ -0,0 +1,80 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaBusinessGetUserPhoneNumberResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + PhoneInfo struct { + PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号(国外手机号会有区号) + PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号 + CountryCode int `json:"countryCode"` // 区号 + Watermark struct { + Timestamp int `json:"timestamp"` // 用户获取手机号操作的时间戳 + Appid string `json:"appid"` // 小程序appid + } `json:"watermark"` // 数据水印 + } `json:"phone_info"` // 用户手机号信息 +} + +type WxaBusinessGetUserPhoneNumberResult struct { + Result WxaBusinessGetUserPhoneNumberResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaBusinessGetUserPhoneNumberResult(result WxaBusinessGetUserPhoneNumberResponse, body []byte, http gorequest.Response, err error) *WxaBusinessGetUserPhoneNumberResult { + return &WxaBusinessGetUserPhoneNumberResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaBusinessGetUserPhoneNumber code换取用户手机号。 每个 code 只能使用一次,code的有效期为5min +// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html +func (app *App) WxaBusinessGetUserPhoneNumber(code string) *WxaBusinessGetUserPhoneNumberResult { + // 参数 + params := gorequest.NewParams() + params.Set("code", code) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaBusinessGetUserPhoneNumberResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaBusinessGetUserPhoneNumberResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaBusinessGetUserPhoneNumberResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85013: + return "无效的自定义配置" + case 85014: + return "无效的模板编号" + case 85043: + return "模板错误" + case 85044: + return "代码包超过大小限制" + case 85045: + return "ext_json 有不存在的路径" + case 85046: + return "tabBar 中缺少 path" + case 85047: + return "pages 字段为空" + case 85048: + return "ext_json 解析失败" + case 80082: + return "没有权限使用该插件" + case 80067: + return "找不到使用的插件" + case 80066: + return "非法的插件版本" + case 9402202: + return "请勿频繁提交,待上一次操作完成后再提交" + case 9402203: + return `标准模板ext_json错误,传了不合法的参数, 如果是标准模板库的模板,则ext_json支持的参数仅为{"extAppid":'', "ext": {}, "window": {}}` + } + return "系统繁忙" +} diff --git a/wxa.commit.go b/wxa.commit.go new file mode 100644 index 0000000..ac567da --- /dev/null +++ b/wxa.commit.go @@ -0,0 +1,70 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaCommitResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type WxaCommitResult struct { + Result WxaCommitResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaCommitResult(result WxaCommitResponse, body []byte, http gorequest.Response, err error) *WxaCommitResult { + return &WxaCommitResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaCommit 上传小程序代码并生成体验版 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/commit.html +func (app *App) WxaCommit(notMustParams ...gorequest.Params) *WxaCommitResult { + // 参数 + params := gorequest.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/commit?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaCommitResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaCommitResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaCommitResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85013: + return "无效的自定义配置" + case 85014: + return "无效的模板编号" + case 85043: + return "模板错误" + case 85044: + return "代码包超过大小限制" + case 85045: + return "ext_json 有不存在的路径" + case 85046: + return "tabBar 中缺少 path" + case 85047: + return "pages 字段为空" + case 85048: + return "ext_json 解析失败" + case 80082: + return "没有权限使用该插件" + case 80067: + return "找不到使用的插件" + case 80066: + return "非法的插件版本" + case 9402202: + return "请勿频繁提交,待上一次操作完成后再提交" + case 9402203: + return `标准模板ext_json错误,传了不合法的参数, 如果是标准模板库的模板,则ext_json支持的参数仅为{"extAppid":'', "ext": {}, "window": {}}` + } + return "系统繁忙" +} diff --git a/wxa.deletetemplate.go b/wxa.deletetemplate.go new file mode 100644 index 0000000..88e3987 --- /dev/null +++ b/wxa.deletetemplate.go @@ -0,0 +1,47 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaDeleteTemplateResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type WxaDeleteTemplateResult struct { + Result WxaDeleteTemplateResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaDeleteTemplateResult(result WxaDeleteTemplateResponse, body []byte, http gorequest.Response, err error) *WxaDeleteTemplateResult { + return &WxaDeleteTemplateResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaDeleteTemplate 删除指定代码模板 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/code_template/deletetemplate.html +func (app *App) WxaDeleteTemplate(templateId string) *WxaDeleteTemplateResult { + // 参数 + params := NewParams() + params.Set("template_id", templateId) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/deletetemplate?access_token=%s", app.GetComponentAccessToken()), params, http.MethodPost) + // 定义 + var response WxaDeleteTemplateResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaDeleteTemplateResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaDeleteTemplateResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85064: + return "找不到模板,请检查模板id是否输入正确" + } + return "系统繁忙" +} diff --git a/wxa.get_auditstatus.go b/wxa.get_auditstatus.go new file mode 100644 index 0000000..13a97f7 --- /dev/null +++ b/wxa.get_auditstatus.go @@ -0,0 +1,55 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetAuditStatusResult(result WxaGetAuditStatusResponse, body []byte, http gorequest.Response, err error) *WxaGetAuditStatusResult { + return &WxaGetAuditStatusResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := app.NewParamsWith() + params.Set("auditid", auditid) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_auditstatus?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaGetAuditStatusResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetAuditStatusResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaGetAuditStatusResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 86000: + return "不是由第三方代小程序进行调用" + case 86001: + return "不存在第三方的已经提交的代码" + case 85012: + return "无效的审核 id" + } + return "系统繁忙" +} diff --git a/wxa.get_category.go b/wxa.get_category.go new file mode 100644 index 0000000..6e43bb0 --- /dev/null +++ b/wxa.get_category.go @@ -0,0 +1,43 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetCategoryResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + CategoryList []struct { + FirstClass string `json:"first_class"` // 一级类目名称 + SecondClass string `json:"second_class"` // 二级类目名称 + ThirdClass string `json:"third_class"` // 三级类目名称 + FirstId int `json:"first_id"` // 一级类目的 ID 编号 + SecondId int `json:"second_id"` // 二级类目的 ID 编号 + ThirdId int `json:"third_id"` // 三级类目的 ID 编号 + } `json:"category_list"` +} + +type WxaGetCategoryResult struct { + Result WxaGetCategoryResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetCategoryResult(result WxaGetCategoryResponse, body []byte, http gorequest.Response, err error) *WxaGetCategoryResult { + return &WxaGetCategoryResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaGetCategory 获取审核时可填写的类目信息 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/category/get_category.html +func (app *App) WxaGetCategory() *WxaGetCategoryResult { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_category?access_token=%s", app.GetAuthorizerAccessToken()), map[string]interface{}{}, http.MethodGet) + // 定义 + var response WxaGetCategoryResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetCategoryResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.get_effective_domain.go b/wxa.get_effective_domain.go new file mode 100644 index 0000000..12eb0df --- /dev/null +++ b/wxa.get_effective_domain.go @@ -0,0 +1,61 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetEffectiveDomainResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + MpDomain struct { + Requestdomain []interface{} `json:"requestdomain"` + Wsrequestdomain []interface{} `json:"wsrequestdomain"` + Uploaddomain []interface{} `json:"uploaddomain"` + Downloaddomain []interface{} `json:"downloaddomain"` + Udpdomain []interface{} `json:"udpdomain"` + Tcpdomain []interface{} `json:"tcpdomain"` + } `json:"mp_domain"` + ThirdDomain struct { + Requestdomain []interface{} `json:"requestdomain"` + Wsrequestdomain []interface{} `json:"wsrequestdomain"` + Uploaddomain []interface{} `json:"uploaddomain"` + Downloaddomain []interface{} `json:"downloaddomain"` + Udpdomain []interface{} `json:"udpdomain"` + Tcpdomain []interface{} `json:"tcpdomain"` + } `json:"third_domain"` + DirectDomain struct { + Requestdomain []interface{} `json:"requestdomain"` + Wsrequestdomain []interface{} `json:"wsrequestdomain"` + Uploaddomain []interface{} `json:"uploaddomain"` + Downloaddomain []interface{} `json:"downloaddomain"` + Udpdomain []interface{} `json:"udpdomain"` + Tcpdomain []interface{} `json:"tcpdomain"` + } `json:"direct_domain"` +} + +type WxaGetEffectiveDomainResult struct { + Result WxaGetEffectiveDomainResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetEffectiveDomainResult(result WxaGetEffectiveDomainResponse, body []byte, http gorequest.Response, err error) *WxaGetEffectiveDomainResult { + return &WxaGetEffectiveDomainResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaGetEffectiveDomain 获取发布后生效服务器域名列表 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_Basic_Info/get_effective_domain.html +func (app *App) WxaGetEffectiveDomain(notMustParams ...Params) *WxaGetEffectiveDomainResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_effective_domain?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaGetEffectiveDomainResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetEffectiveDomainResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.get_latest_auditstatus.go b/wxa.get_latest_auditstatus.go new file mode 100644 index 0000000..620b680 --- /dev/null +++ b/wxa.get_latest_auditstatus.go @@ -0,0 +1,52 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetLatestAuditStatusResult(result WxaGetLatestAuditStatusResponse, body []byte, http gorequest.Response, err error) *WxaGetLatestAuditStatusResult { + return &WxaGetLatestAuditStatusResult{Result: result, Body: body, Http: http, 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 { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_latest_auditstatus?access_token=%s", app.GetAuthorizerAccessToken()), map[string]interface{}{}, http.MethodPost) + // 定义 + var response WxaGetLatestAuditStatusResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetLatestAuditStatusResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaGetLatestAuditStatusResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 86000: + return "不是由第三方代小程序进行调用" + case 86001: + return "不存在第三方的已经提交的代码" + case 85012: + return "无效的审核 id" + } + return "系统繁忙" +} diff --git a/wxa.get_page.go b/wxa.get_page.go new file mode 100644 index 0000000..5353c41 --- /dev/null +++ b/wxa.get_page.go @@ -0,0 +1,36 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetPageResult(result WxaGetPageResponse, body []byte, http gorequest.Response, err error) *WxaGetPageResult { + return &WxaGetPageResult{Result: result, Body: body, Http: http, 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 { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_page?access_token=%s", app.GetAuthorizerAccessToken()), map[string]interface{}{}, http.MethodGet) + // 定义 + var response WxaGetPageResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetPageResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.get_qrcode.go b/wxa.get_qrcode.go new file mode 100644 index 0000000..6e0c542 --- /dev/null +++ b/wxa.get_qrcode.go @@ -0,0 +1,40 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetQrcodeResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type WxaGetQrcodeResult struct { + Result WxaGetQrcodeResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetQrcodeResult(result WxaGetQrcodeResponse, body []byte, http gorequest.Response, err error) *WxaGetQrcodeResult { + return &WxaGetQrcodeResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := NewParams() + if path != "" { + params["path"] = path // 指定二维码扫码后直接进入指定页面并可同时带上参数) + } + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/get_qrcode?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodGet) + // 定义 + var response WxaGetQrcodeResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetQrcodeResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.gettemplatedraftlist.go b/wxa.gettemplatedraftlist.go new file mode 100644 index 0000000..a6e66ae --- /dev/null +++ b/wxa.gettemplatedraftlist.go @@ -0,0 +1,54 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetTemplateDraftListResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 错误信息 + DraftList []struct { + CreateTime int64 `json:"create_time"` // 开发者上传草稿时间戳 + UserVersion string `json:"user_version"` // 版本号,开发者自定义字段 + UserDesc string `json:"user_desc"` // 版本描述 开发者自定义字段 + DraftId int64 `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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetTemplateDraftListResult(result WxaGetTemplateDraftListResponse, body []byte, http gorequest.Response, err error) *WxaGetTemplateDraftListResult { + return &WxaGetTemplateDraftListResult{Result: result, Body: body, Http: http, 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 { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/gettemplatedraftlist?access_token=%s", app.GetComponentAccessToken()), map[string]interface{}{}, http.MethodGet) + // 定义 + var response WxaGetTemplateDraftListResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetTemplateDraftListResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaGetTemplateDraftListResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85064: + return "找不到模板" + } + return "系统繁忙" +} diff --git a/wxa.gettemplatelist.go b/wxa.gettemplatelist.go new file mode 100644 index 0000000..26978e4 --- /dev/null +++ b/wxa.gettemplatelist.go @@ -0,0 +1,57 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetTemplateListResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + TemplateList []struct { + CreateTime int `json:"create_time"` // 被添加为模板的时间 + UserVersion string `json:"user_version"` // 模板版本号,开发者自定义字段 + UserDesc string `json:"user_desc"` // 模板描述,开发者自定义字段 + TemplateId int64 `json:"template_id"` // 模板 id + TemplateType int `json:"template_type"` // 0对应普通模板,1对应标准模板 + SourceMiniprogramAppid string `json:"source_miniprogram_appid"` // 开发小程序的appid + SourceMiniprogram string `json:"source_miniprogram"` // 开发小程序的名称 + Developer string `json:"developer"` // 开发者 + CategoryList []interface{} `json:"category_list"` + } `json:"template_list"` // 模板信息列表 +} + +type WxaGetTemplateListResult struct { + Result WxaGetTemplateListResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetTemplateListResult(result WxaGetTemplateListResponse, body []byte, http gorequest.Response, err error) *WxaGetTemplateListResult { + return &WxaGetTemplateListResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaGetTemplateList 获取代码模板列表 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/code_template/gettemplatelist.html +func (app *App) WxaGetTemplateList() *WxaGetTemplateListResult { + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/gettemplatelist?access_token=%s", app.GetComponentAccessToken()), map[string]interface{}{}, http.MethodGet) + // 定义 + var response WxaGetTemplateListResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetTemplateListResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaGetTemplateListResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 43001: + return "请使用GET,不要使用post" + case 85064: + return "找不到模板" + } + return "系统繁忙" +} diff --git a/wxa.getversioninfo.go b/wxa.getversioninfo.go new file mode 100644 index 0000000..dfbdefb --- /dev/null +++ b/wxa.getversioninfo.go @@ -0,0 +1,50 @@ +package wechatopen + +import ( + "encoding/json" + "errors" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetVersionInfoResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + ExpInfo struct { + ExpTime int64 `json:"exp_time"` // 提交体验版的时间 + ExpVersion string `json:"exp_version"` // 体验版版本信息 + ExpDesc string `json:"exp_desc"` // 体验版版本描述 + } `json:"exp_info"` // 体验版信息 + ReleaseInfo struct { + ReleaseTime int64 `json:"release_time"` // 发布线上版的时间 + ReleaseVersion string `json:"release_version"` // 线上版版本信息 + ReleaseDesc string `json:"release_desc"` // 线上版本描述 + } `json:"release_info"` // 线上版信息 +} + +type WxaGetVersionInfoResult struct { + Result WxaGetVersionInfoResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetVersionInfoResult(result WxaGetVersionInfoResponse, body []byte, http gorequest.Response, err error) *WxaGetVersionInfoResult { + return &WxaGetVersionInfoResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaGetVersionInfo 查询小程序版本信息 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_versioninfo.html +func (app *App) WxaGetVersionInfo() *WxaGetVersionInfoResult { + accessToken := app.GetAuthorizerAccessToken() + if accessToken == "" { + return NewWxaGetVersionInfoResult(WxaGetVersionInfoResponse{}, nil, gorequest.Response{}, errors.New("访问令牌为空")) + } + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/getversioninfo?access_token=%s", accessToken), map[string]interface{}{}, http.MethodPost) + // 定义 + var response WxaGetVersionInfoResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetVersionInfoResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.getwxacodeunlimit.go b/wxa.getwxacodeunlimit.go new file mode 100644 index 0000000..69bc26d --- /dev/null +++ b/wxa.getwxacodeunlimit.go @@ -0,0 +1,52 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaGetWxaCodeUnLimitResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + ContentType string `json:"contentType"` + Buffer interface{} `json:"buffer"` +} + +type WxaGetWxaCodeUnLimitResult struct { + Result WxaGetWxaCodeUnLimitResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaGetWxaCodeUnLimitResult(result WxaGetWxaCodeUnLimitResponse, body []byte, http gorequest.Response, err error) *WxaGetWxaCodeUnLimitResult { + return &WxaGetWxaCodeUnLimitResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaGetWxaCodeUnLimit 获取小程序码,适用于需要的码数量极多的业务场景。通过该接口生成的小程序码,永久有效,数量暂无限制 +// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html +func (app *App) WxaGetWxaCodeUnLimit(notMustParams ...Params) *WxaGetWxaCodeUnLimitResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaGetWxaCodeUnLimitResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaGetWxaCodeUnLimitResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaGetWxaCodeUnLimitResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 45009: + return "调用分钟频率受限(目前5000次/分钟,会调整),如需大量小程序码,建议预生成" + case 41030: + return "page 不合法(页面不存在或者小程序没有发布、根路径前加 /或者携带参数)" + case 40097: + return "env_version 不合法" + } + return "系统繁忙" +} diff --git a/wxa.memberauth.go b/wxa.memberauth.go new file mode 100644 index 0000000..9718b9b --- /dev/null +++ b/wxa.memberauth.go @@ -0,0 +1,46 @@ +package wechatopen + +import ( + "encoding/json" + "errors" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaMemberAuthResult(result WxaMemberAuthResponse, body []byte, http gorequest.Response, err error) *WxaMemberAuthResult { + return &WxaMemberAuthResult{Result: result, Body: body, Http: http, 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 { + accessToken := app.GetAuthorizerAccessToken() + if accessToken == "" { + return NewWxaMemberAuthResult(WxaMemberAuthResponse{}, nil, gorequest.Response{}, errors.New("访问令牌为空")) + } + // 参数 + params := NewParams() + params["action"] = "get_experiencer" + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/memberauth?access_token=%s", accessToken), params, http.MethodPost) + // 定义 + var response WxaMemberAuthResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaMemberAuthResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.modify_domain.go b/wxa.modify_domain.go new file mode 100644 index 0000000..a4e8241 --- /dev/null +++ b/wxa.modify_domain.go @@ -0,0 +1,71 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "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 // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaModifyDomainResult(result WxaModifyDomainResponse, body []byte, http gorequest.Response, err error) *WxaModifyDomainResult { + return &WxaModifyDomainResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/modify_domain?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaModifyDomainResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaModifyDomainResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaModifyDomainResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85015: + return "该账号不是小程序账号" + case 85016: + return "域名数量超限制" + case 85017: + return "域名输入为空,或者没有新增域名,请确认小程序已经添加了域名或该域名是否没有在第三方平台添加" + case 85018: + return "域名没有在第三方平台设置" + case 85301: + return "存在 “不符合域名规则的域名”导致无修改" + case 85302: + return "存在 “ 缺少ICP备案的域名”导致无修改" + case 85303: + return "同时存在“不符合域名规则的域名”以及“ 缺少ICP备案的域名”导致无修改" + } + return "系统繁忙" +} diff --git a/wxa.modify_domain_directly.go b/wxa.modify_domain_directly.go new file mode 100644 index 0000000..6a3021d --- /dev/null +++ b/wxa.modify_domain_directly.go @@ -0,0 +1,56 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaModifyDomainDirectlyResponse struct { + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 +} + +type WxaModifyDomainDirectlyResult struct { + Result WxaModifyDomainDirectlyResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaModifyDomainDirectlyResult(result WxaModifyDomainDirectlyResponse, body []byte, http gorequest.Response, err error) *WxaModifyDomainDirectlyResult { + return &WxaModifyDomainDirectlyResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaModifyDomainDirectly 快速设置小程序服务器域名 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Mini_Program_Basic_Info/modify_domain_directly.html +func (app *App) WxaModifyDomainDirectly(notMustParams ...Params) *WxaModifyDomainDirectlyResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/modify_domain_directly?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaModifyDomainDirectlyResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaModifyDomainDirectlyResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaModifyDomainDirectlyResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85015: + return "该账号不是小程序账号" + case 86100: + return "该 URL 的协议头有误" + case 45082: + return "域名需要 icp 备案,否则无法添加" + case 86101: + return "不支持配置api.weixin.qq.com" + case 85016: + return "域名数量超限制" + case 86102: + return "每个月只能修改50次,超过域名修改次数限制" + } + return "系统繁忙" +} diff --git a/wxa.release.go b/wxa.release.go new file mode 100644 index 0000000..d502ee3 --- /dev/null +++ b/wxa.release.go @@ -0,0 +1,48 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaReleaseResponse struct { + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 +} + +type WxaReleaseResult struct { + Result WxaReleaseResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaReleaseResult(result WxaReleaseResponse, body []byte, http gorequest.Response, err error) *WxaReleaseResult { + return &WxaReleaseResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaRelease 发布已通过审核的小程序 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/release.html +func (app *App) WxaRelease() *WxaReleaseResult { + // 参数 + params := NewParams() + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/release?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaReleaseResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaReleaseResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaReleaseResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 85019: + return "没有审核版本" + case 85020: + return "审核状态未满足发布" + } + return "系统繁忙" +} diff --git a/wxa.revertcoderelease.go b/wxa.revertcoderelease.go new file mode 100644 index 0000000..bc17b56 --- /dev/null +++ b/wxa.revertcoderelease.go @@ -0,0 +1 @@ +package wechatopen diff --git a/wxa.security.apply_privacy_interface.go b/wxa.security.apply_privacy_interface.go new file mode 100644 index 0000000..ee848db --- /dev/null +++ b/wxa.security.apply_privacy_interface.go @@ -0,0 +1,59 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaSecurityApplyPrivacyInterfaceResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 返回码信息 + AuditId int64 `json:"audit_id"` // 审核单id +} + +type WxaSecurityApplyPrivacyInterfaceResult struct { + Result WxaSecurityApplyPrivacyInterfaceResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaSecurityApplyPrivacyInterfaceResult(result WxaSecurityApplyPrivacyInterfaceResponse, body []byte, http gorequest.Response, err error) *WxaSecurityApplyPrivacyInterfaceResult { + return &WxaSecurityApplyPrivacyInterfaceResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaSecurityApplyPrivacyInterface 申请接口 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/apply_api/apply_privacy_interface.html +func (app *App) WxaSecurityApplyPrivacyInterface(notMustParams ...Params) *WxaSecurityApplyPrivacyInterfaceResult { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/security/apply_privacy_interface?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaSecurityApplyPrivacyInterfaceResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaSecurityApplyPrivacyInterfaceResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaSecurityApplyPrivacyInterfaceResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 61031: + return "审核中,请不要重复申请" + case 61032: + return "视频格式不对,要传mp4格式的" + case 61033: + return "视频下载失败" + case 61034: + return "必填的参数没填,检查后重新提交" + case 61035: + return "输入的api(api_name严格区分大小写)无需申请,可以直接使用" + case 61036: + return "该帐号不可申请,请检查类目是否符合" + case 61037: + return "需要以ntf-8的编码格式提交" + } + return "系统繁忙" +} diff --git a/wxa.security.get_privacy_interface.go b/wxa.security.get_privacy_interface.go new file mode 100644 index 0000000..ed7ab0f --- /dev/null +++ b/wxa.security.get_privacy_interface.go @@ -0,0 +1,57 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaSecurityGetPrivacyInterfaceResponse struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 返回码信息 + InterfaceList []struct { + ApiName string `json:"api_name"` // api 英文名 + ApiChName string `json:"api_ch_name"` // api 中文名 + ApiDesc string `json:"api_desc"` // api描述 + ApplyTime int64 `json:"apply_time,omitempty"` // 申请时间 ,该字段发起申请后才会有 + Status int `json:"status"` // 接口状态,该字段发起申请后才会有 + AuditId int `json:"audit_id,omitempty"` // 申请单号,该字段发起申请后才会有 + FailReason string `json:"fail_reason,omitempty"` // 申请被驳回原因或者无权限,该字段申请驳回时才会有 + ApiLink string `json:"api_link"` // api文档链接 + GroupName string `json:"group_name"` // 分组名 + } `json:"interface_list"` // 隐私接口 +} + +type WxaSecurityGetPrivacyInterfaceResult struct { + Result WxaSecurityGetPrivacyInterfaceResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaSecurityGetPrivacyInterfaceResult(result WxaSecurityGetPrivacyInterfaceResponse, body []byte, http gorequest.Response, err error) *WxaSecurityGetPrivacyInterfaceResult { + return &WxaSecurityGetPrivacyInterfaceResult{Result: result, Body: body, Http: http, Err: err} +} + +// WxaSecurityGetPrivacyInterface 获取接口列表 +// https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/apply_api/get_privacy_interface.html +func (app *App) WxaSecurityGetPrivacyInterface() *WxaSecurityGetPrivacyInterfaceResult { + // 参数 + params := NewParams() + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/security/get_privacy_interface?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodGet) + // 定义 + var response WxaSecurityGetPrivacyInterfaceResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaSecurityGetPrivacyInterfaceResult(response, request.ResponseBody, request, err) +} + +// ErrcodeInfo 错误描述 +func (resp *WxaSecurityGetPrivacyInterfaceResult) ErrcodeInfo() string { + switch resp.Result.Errcode { + case 61031: + return "审核中,请不要重复申请" + } + return "系统繁忙" +} diff --git a/wxa.submit_audit.go b/wxa.submit_audit.go new file mode 100644 index 0000000..e5b09d8 --- /dev/null +++ b/wxa.submit_audit.go @@ -0,0 +1,38 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaSubmitAuditResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + Auditid int64 `json:"auditid"` +} + +type WxaSubmitAuditResult struct { + Result WxaSubmitAuditResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaSubmitAuditResult(result WxaSubmitAuditResponse, body []byte, http gorequest.Response, err error) *WxaSubmitAuditResult { + return &WxaSubmitAuditResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := app.NewParamsWith(notMustParams...) + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/submit_audit?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaSubmitAuditResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaSubmitAuditResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.unbind_tester.go b/wxa.unbind_tester.go new file mode 100644 index 0000000..97d90d9 --- /dev/null +++ b/wxa.unbind_tester.go @@ -0,0 +1,41 @@ +package wechatopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type WxaUnbindTesterResponse struct { + Errcode int `json:"errcode"` // 错误码 + Errmsg string `json:"errmsg"` // 错误信息 +} + +type WxaUnbindTesterResult struct { + Result WxaUnbindTesterResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewWxaUnbindTesterResult(result WxaUnbindTesterResponse, body []byte, http gorequest.Response, err error) *WxaUnbindTesterResult { + return &WxaUnbindTesterResult{Result: result, Body: body, Http: http, 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 { + // 参数 + params := NewParams() + if wechatid != "" { + params["wechatid"] = wechatid + } + params["userstr"] = userstr + // 请求 + request, err := app.request(fmt.Sprintf("https://api.weixin.qq.com/wxa/unbind_tester?access_token=%s", app.GetAuthorizerAccessToken()), params, http.MethodPost) + // 定义 + var response WxaUnbindTesterResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewWxaUnbindTesterResult(response, request.ResponseBody, request, err) +} diff --git a/wxa.undocodeaudit.go b/wxa.undocodeaudit.go new file mode 100644 index 0000000..bc17b56 --- /dev/null +++ b/wxa.undocodeaudit.go @@ -0,0 +1 @@ +package wechatopen diff --git a/xml.go b/xml.go new file mode 100644 index 0000000..8181bbb --- /dev/null +++ b/xml.go @@ -0,0 +1,43 @@ +package wechatopen + +import ( + "encoding/xml" + "io" + "strings" +) + +func XmlDecode(data string) map[string]string { + decoder := xml.NewDecoder(strings.NewReader(data)) + result := make(map[string]string) + key := "" + for { + token, err := decoder.Token() //读取一个标签或者文本内容 + if err == io.EOF { + return result + } + if err != nil { + return result + } + switch tp := token.(type) { //读取的TOKEN可以是以下三种类型:StartElement起始标签,EndElement结束标签,CharData文本内容 + case xml.StartElement: + se := xml.StartElement(tp) //强制类型转换 + if se.Name.Local != "xml" { + key = se.Name.Local + } + if len(se.Attr) != 0 { + //读取标签属性 + } + case xml.EndElement: + ee := xml.EndElement(tp) + if ee.Name.Local == "xml" { + return result + } + case xml.CharData: //文本数据,注意一个结束标签和另一个起始标签之间可能有空格 + cd := xml.CharData(tp) + data := strings.TrimSpace(string(cd)) + if len(data) != 0 { + result[key] = data + } + } + } +}