38 Commits

Author SHA1 Message Date
  lijianbin e845674f4b update 3 years ago
  lijianbin c0ce0771ac 增加site_id参数 3 years ago
  guzeng 560548b22c 微信支付退款client 3 years ago
  guzeng 10f3760acb 增加data处理方法 3 years ago
  guzeng 7aadadbae3 增加支付退款 3 years ago
  listen bd48d1cc30 加入图片和文字检测 3 years ago
  guzeng f94ec81d83 更新pb.go 3 years ago
  listen 131fea8347 加入检测方法 3 years ago
  listen 30e4e1d689 更改小程序码参数 3 years ago
  listen eb61287f73 修复bug 3 years ago
  listen 716c994c1f 添加app获取openid方法 3 years ago
  guzeng 772d930427 更新pb.go 3 years ago
  listen 1235e63a18 新增app获取openid方法 3 years ago
  listen 1d365c1ee6 新增获取小程序码方法 3 years ago
  listen c800f05775 删除文件 3 years ago
  listen ffa8f898a8 测试rpc 3 years ago
  guzeng 5810bc14fe 更新pb.go 3 years ago
  listen 9f96115ccd 提交 3 years ago
  guzeng 3691ba30ea 更新pb.go 3 years ago
  listen 385eecdb31 新增方法 3 years ago
  listen ea8de4ed43 修复错误 3 years ago
  listen e68c361408 更新结构 3 years ago
  listen 7ecafe3fea 添加获取公众号用户信息的方法 3 years ago
  listen e2def3228e 加入公众号获取openid方法 3 years ago
  guzeng 92944dc02d 增加获取公众号OPENID与获取公众号用户信息方法 3 years ago
  listen 6e5ee948fe 新增方法 3 years ago
  listen 0d399213c3 更新获取openid方法的结构体 3 years ago
  guzeng 3310a1fff7 增加调试解析openid 3 years ago
  guzeng 1270ae23c8 增加调试解析openid 3 years ago
  guzeng 712ffcffd7 增加调试解析openid 3 years ago
  guzeng 72a419de81 增加调试解析openid 3 years ago
  guzeng 14fd3f2ea4 增加发送小程序订阅消息方法 3 years ago
  guzeng e7b20fb459 修改获取小程序openid接口调用 3 years ago
  guzeng decadd1d2b 修改获取小程序openid接口调用 3 years ago
  guzeng 40d013b753 修改获取小程序openid接口调用 3 years ago
  guzeng 27950743ff 增加获取小程序openid接口调用 3 years ago
  guzeng c6d8cea871 测试发送消息 3 years ago
  guzeng 386f58a565 修改参数类型 3 years ago
21 changed files with 1032 additions and 31 deletions
Unified View
  1. +71
    -0
      app_openid.client.go
  2. +77
    -2
      common.go
  3. +106
    -0
      data.go
  4. +5
    -2
      go.mod
  5. +8
    -4
      go.sum
  6. +80
    -0
      miniapp_openid.client.go
  7. +62
    -0
      miniapp_qrcode.client.go
  8. +33
    -0
      miniapp_qrcode.client_test.go
  9. +80
    -0
      mp_open_id.client.go
  10. +77
    -0
      mp_userinfo.client.go
  11. +63
    -0
      pay.client.go
  12. +76
    -0
      pay.client_test.go
  13. +97
    -0
      security_check.go
  14. +31
    -0
      security_check_test.go
  15. +70
    -0
      send_subscribe_message.client.go
  16. +30
    -0
      send_subscribe_message.client_test.go
  17. +1
    -1
      send_uniform_message.client.go
  18. +19
    -17
      send_uniform_message.client_test.go
  19. +2
    -1
      sign.go
  20. +35
    -3
      weixin.pb.go
  21. +9
    -1
      weixin.proto

+ 71
- 0
app_openid.client.go View File

@ -0,0 +1,71 @@
package weixinrpc
import (
"encoding/json"
"log"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func GetAppOpenid(appid, secret, js_code string, url ...string) (map[string]string, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return nil, err
}
defer conn.Close()
data := MiniAppOpenidReq{}
data.Appid = appid
data.Secret = secret
data.JsCode = js_code
data_json, err := json.Marshal(data)
if err != nil {
return nil, err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.GetAppOpenid(req, res)
if err != nil {
return nil, err
}
//解密
res_data_de, err := GetOrgData(res)
if err != nil {
return nil, err
}
var res_arr map[string]string
err = json.Unmarshal([]byte(res_data_de), &res_arr)
if err != nil {
log.Println(err, res_data_de)
return nil, err
}
return res_arr, nil
}

+ 77
- 2
common.go View File

@ -1,5 +1,9 @@
package weixinrpc package weixinrpc
import (
"git.tetele.net/yueheng/conf"
)
const DES_KEY = "wxserrpc" const DES_KEY = "wxserrpc"
type WxApiRes struct { type WxApiRes struct {
@ -30,16 +34,87 @@ type UniformMessageReq struct {
MpAppid string MpAppid string
TemplateId string TemplateId string
Url string Url string
MiniProgram string
MiniProgram interface{}
Data interface{} Data interface{}
} }
type SubscribeMessageReq struct {
Req
Appid string
Secret string
Touser string
TemplateId string
Page string
MiniprogramState string
Lang string
Data interface{}
}
/**
* 获取小程序openid请求参数
*/
type MiniAppOpenidReq struct {
Appid string
Secret string
JsCode string
}
/**
* 获取小程序openid返回参数
*/
type MiniAppOpenidRes struct {
Openid string `json:"openid"`
SessionKey interface{} `json:"session_key"`
Unionid interface{} `json:"unionid"`
NickName string `json:"nick_name"`
AvatarUrl string `json:"avatar_url"`
WxApiRes
}
/**
* 获取公众号openid返回参数
*/
type MpOpenidRes struct {
AccessToken string `json:"access_token"`
ExpiresIn string `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
Openid string `json:"openid"`
Scope string `json:"scope"`
}
/**
* 获取公众号用户信息请求参数
*/
type MpUserInfoReq struct {
AccessToken string
Openid string
}
/**
* 根据用户access_token换取用户信息返回参数
*/
type MpUserInfoRes struct {
Openid string
Nickname string
Sex string
Province string
City string
Country string
Headimgurl string
Privilege string
Unionid string
}
func rpc_server_conn(url ...string) (*WeixinRpcServiceClient, error) { func rpc_server_conn(url ...string) (*WeixinRpcServiceClient, error) {
var wx_rpc_url string = "127.0.0.1:7969"
var wx_rpc_url string
if len(url) > 0 && url[0] != "" { if len(url) > 0 && url[0] != "" {
wx_rpc_url = url[0] wx_rpc_url = url[0]
} else {
wx_rpc_url = "127.0.0.1:" + conf.WEIXIN_RPC_PORT
} }
conn, _, err := DialWeixinRpcService("tcp", wx_rpc_url) conn, _, err := DialWeixinRpcService("tcp", wx_rpc_url)
if err != nil { if err != nil {
return nil, err return nil, err


+ 106
- 0
data.go View File

@ -0,0 +1,106 @@
package weixinrpc
import (
"encoding/json"
"errors"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func SetResData(data interface{}, res *Response) {
res_data_json, err := json.Marshal(data)
if err == nil {
encryData := crypter.DesEn(string(res_data_json), DES_KEY)
now_str := strconv.FormatInt(time.Now().Unix(), 10)
res_sign := Sign(encryData, now_str)
res.Data = proto.String(encryData)
res.Time = proto.String(now_str)
res.Sign = proto.String(res_sign)
}
}
func SetReqData(arg interface{}) (*Request, error) {
data_json, err := json.Marshal(arg)
if err != nil {
return nil, err
}
now_int64 := time.Now().Unix()
encryData := crypter.DesEn(string(data_json), DES_KEY)
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
return &Request{proto.String(encryData), proto.String(now), proto.String(sign), nil}, nil
}
func GetReqData(req *Request) (string, error) {
res_data := req.GetData()
if res_data != "" {
time_int64, err := strconv.ParseInt(req.GetTime(), 10, 64)
if err != nil {
return "", err
}
now_int64 := time.Now().Unix()
if now_int64-time_int64 > 10 || time_int64-now_int64 > 10 {
//时间误差前后10秒,返回
return "", errors.New("返回时间错误")
}
check_sign := CheckSign(req.GetSign(), res_data, req.GetTime())
if !check_sign {
return "", errors.New("返回数据签名错误")
}
//解密
return crypter.DesDe(res_data, DES_KEY), nil
}
return "", nil
}
func GetResData(res *Response) (string, error) {
res_data := res.GetData()
if res_data != "" {
time_int64, err := strconv.ParseInt(res.GetTime(), 10, 64)
if err != nil {
return "", err
}
now_int64 := time.Now().Unix()
if now_int64-time_int64 > 10 || time_int64-now_int64 > 10 {
//时间误差前后10秒,返回
return "", errors.New("返回时间错误")
}
check_sign := CheckSign(res.GetSign(), res_data, res.GetTime())
if !check_sign {
return "", errors.New("返回数据签名错误")
}
//解密
return crypter.DesDe(res_data, DES_KEY), nil
}
return "", nil
}

+ 5
- 2
go.mod View File

@ -3,8 +3,11 @@ module git.tetele.net/tgo/weixinrpc
go 1.14 go 1.14
require ( require (
git.tetele.net/tgo/crypter v0.2.2 // indirect
github.com/chai2010/protorpc v1.0.0 // indirect
git.tetele.net/tgo/crypter v0.2.2
git.tetele.net/tgo/helper v0.2.6
git.tetele.net/yueheng/conf v1.2.4
github.com/chai2010/protorpc v1.1.3
github.com/golang/protobuf v1.0.0
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
) )

+ 8
- 4
go.sum View File

@ -1,11 +1,15 @@
git.tetele.net/tgo/crypter v0.2.2 h1:YMQJh2Gj5Po4ZfelJUmXBKi01UbmtiSy3bmqRfnYQMo= git.tetele.net/tgo/crypter v0.2.2 h1:YMQJh2Gj5Po4ZfelJUmXBKi01UbmtiSy3bmqRfnYQMo=
git.tetele.net/tgo/crypter v0.2.2/go.mod h1:vfvRLZA8+lHNgNXneOcgvVhDyuv25ZRb+C6xHOmXNx0= git.tetele.net/tgo/crypter v0.2.2/go.mod h1:vfvRLZA8+lHNgNXneOcgvVhDyuv25ZRb+C6xHOmXNx0=
github.com/chai2010/protorpc v1.0.0 h1:aJ45G9sl1utSKo35EqnBSTs5jqTpdJDJAuZMMYPAtFo=
github.com/chai2010/protorpc v1.0.0/go.mod h1:woR3WwjaQDqFjlzdVsFEKiK5Ur12QL8mYxVPjfr5z54=
git.tetele.net/tgo/helper v0.2.6 h1:JC+N+If/bGvcwuUSklva17YDNtKBV2Qpvl5RQpMySOY=
git.tetele.net/tgo/helper v0.2.6/go.mod h1:89mQwyfqZ+t8YXiVwzSxA70gLlUNqoZGDEUxvV46jXk=
git.tetele.net/yueheng/conf v1.2.4 h1:QQr6yflZa9PjISTyNDBL25qOmkRYR06BCJJWWagETQI=
git.tetele.net/yueheng/conf v1.2.4/go.mod h1:qRujMTqjMByvdC05qdfscZMFRKM5XA1qbtz4rnFyStY=
github.com/chai2010/protorpc v1.1.3 h1:VJK5hIoZn0XCGol0GmbxZkUG6FbTI5LP2Lam6RVd15w=
github.com/chai2010/protorpc v1.1.3/go.mod h1:/wO0kiyVdu7ug8dCMrA2yDr2vLfyhsLEuzLa9J2HJ+I=
github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ= github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ=
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=


+ 80
- 0
miniapp_openid.client.go View File

@ -0,0 +1,80 @@
package weixinrpc
import (
"encoding/json"
"log"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func GetMiniAppOpenid(appid, secret, js_code string, url ...string) (*MiniAppOpenidRes, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return nil, err
}
defer conn.Close()
data := MiniAppOpenidReq{}
data.Appid = appid
data.Secret = secret
data.JsCode = js_code
data_json, err := json.Marshal(data)
if err != nil {
return nil, err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.GetMiniAppOpenid(req, res)
if err != nil {
return nil, err
}
return HandleMiniAppOpenid(res)
}
/**
* 处理返回结果
*/
func HandleMiniAppOpenid(res *Response) (*MiniAppOpenidRes, error) {
//解密
res_data_de, err := GetOrgData(res)
if err != nil {
return nil, err
}
var res_arr MiniAppOpenidRes
err = json.Unmarshal([]byte(res_data_de), &res_arr)
if err != nil {
log.Println(err, res_data_de)
return nil, err
}
return &res_arr, nil
}

+ 62
- 0
miniapp_qrcode.client.go View File

@ -0,0 +1,62 @@
package weixinrpc
import (
"encoding/json"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func GetMiniAppQrcode(siteId, appId, appSecret string, qrcodeParams map[string]interface{}, url ...string) (string, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return "", err
}
defer conn.Close()
data := map[string]interface{}{}
data["site_id"] = siteId
data["app_id"] = appId
data["app_secret"] = appSecret
data["qrcode"] = qrcodeParams
data_json, err := json.Marshal(data)
if err != nil {
return "", err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.GetMiniappQrcode(req, res)
if err != nil {
return "", err
}
// 解密
res_data_de, err := GetOrgData(res)
if err != nil {
return "", err
}
return res_data_de, nil
}

+ 33
- 0
miniapp_qrcode.client_test.go View File

@ -0,0 +1,33 @@
package weixinrpc
import (
"testing"
)
func Test_GetMiniappCode(t *testing.T){
site_id := "1198881"
appid := "wx3d53ccbaf69f7995"
appSecret := "165983626235636be54a16404e3e70a7"
page := ""
scene := "user_id=19"
qrcodeParams := map[string]interface{}{
"page":page,
"scene":scene,
// "is_hyaline":true,
}
qrcode,err := GetMiniAppQrcode(site_id,appid,appSecret,qrcodeParams)
t.Log(qrcode)
t.Log(err)
}
func Test_GetMiniappOpenid(t *testing.T){
appid := "wx3d53ccbaf69f7995"
appSecret := "165983626235636be54a16404e3e70a7"
js_code := "063iBAll27ez884FT6nl254pW02iBAlw"
res,err := GetMiniAppOpenid(appid,appSecret,js_code)
t.Log(res)
t.Log(err)
}

+ 80
- 0
mp_open_id.client.go View File

@ -0,0 +1,80 @@
package weixinrpc
import (
"encoding/json"
"log"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func GetMpOpenId(appid, secret, js_code string, url ...string) (*MpOpenidRes, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return nil, err
}
defer conn.Close()
data := MiniAppOpenidReq{}
data.Appid = appid
data.Secret = secret
data.JsCode = js_code
data_json, err := json.Marshal(data)
if err != nil {
return nil, err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.GetMpOpenid(req, res)
if err != nil {
return nil, err
}
return HandleMpOpenid(res)
}
/**
* 处理返回结果
*/
func HandleMpOpenid(res *Response) (*MpOpenidRes, error) {
//解密
res_data_de, err := GetOrgData(res)
if err != nil {
return nil, err
}
var res_arr MpOpenidRes
err = json.Unmarshal([]byte(res_data_de), &res_arr)
if err != nil {
log.Println(err, res_data_de)
return nil, err
}
return &res_arr, nil
}

+ 77
- 0
mp_userinfo.client.go View File

@ -0,0 +1,77 @@
package weixinrpc
import (
"encoding/json"
"log"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
func GetMpUserInfo(accessToken,openId string,url ...string) (*MpUserInfoRes, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return nil, err
}
defer conn.Close()
data := MpUserInfoReq{}
data.AccessToken = accessToken
data.Openid = openId
data_json, err := json.Marshal(data)
if err != nil {
return nil, err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.GetMpUserInfo(req, res)
if err != nil {
return nil, err
}
return HandleMpUserInfo(res)
}
/**
* 处理返回结果
*/
func HandleMpUserInfo(res *Response) (*MpUserInfoRes, error) {
//解密
res_data_de, err := GetOrgData(res)
if err != nil {
return nil, err
}
var res_arr MpUserInfoRes
err = json.Unmarshal([]byte(res_data_de), &res_arr)
if err != nil {
log.Println(err, res_data_de)
return nil, err
}
return &res_arr, nil
}

+ 63
- 0
pay.client.go View File

@ -0,0 +1,63 @@
package weixinrpc
import (
"encoding/json"
)
/**
* 订单号,子单号,总金额,退款金额,微信商户id,微信商户平台API私钥,平台证书,商户平台证书序列号,商品名,退款原因商品id,回调地址
*/
func PayRefund(order_sn, sn, total, refund, wx_mp_mch_id, wx_mch_apiclient_key,
wx_pay_cert, mch_serial_no, title, uuid, reason, notify_url string,
url ...string) (map[string]string, error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return nil, err
}
defer conn.Close()
data := map[string]string{
"order_sn": order_sn,
"sn": sn,
"total": total,
"refund": refund,
"wx_mp_mch_id": wx_mp_mch_id,
"wx_mch_apiclient_key": wx_mch_apiclient_key,
"wx_pay_cert": wx_pay_cert,
"mch_serial_no": mch_serial_no,
"reason": reason,
"uuid": uuid,
"title": title,
"notify_url": notify_url,
}
req, err := SetReqData(data)
if err != nil {
return nil, err
}
res := &Response{}
err = conn.PayRefund(req, res)
if err != nil {
return nil, err
}
res_data_de, err := GetResData(res)
if err != nil {
return nil, err
}
if res_data_de == "" {
return nil, nil
}
var res_arr map[string]string
err = json.Unmarshal([]byte(res_data_de), &res_arr)
if err != nil {
return nil, err
}
return res_arr, nil
}

+ 76
- 0
pay.client_test.go View File

@ -0,0 +1,76 @@
package weixinrpc
import (
"testing"
)
func Test_PayRefund(t *testing.T) {
order_sn := "1638614195578314926" //订单号
sn := "1638614195546464238" //子单号
total := "0.04" //总金额
refund := "0.04" //退款金额
wx_mp_mch_id := "1612824503" //微信商户id
wx_mch_apiclient_key := `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDS6pGtfloagNoX
NPc27PFaw+K/H4n8Z7sdHyXSD6hlLzvTq7Fx4/MZYgmmXrhAjtdW/+KeKrHnYyAq
jNlkIamCVhteNZd9XQ3SCWpmqLVtT6sMV5Xtx31A8WqRi0ABReiNlTjnlqOntpkr
bP/9NTwQJLsAVKCbkOtaL3fFm5aTd6Jsu4BTq/4GWQOhY4UfsBcwBRTOJ9LsowX3
CZA4npha8nVftKT/ut7eqKgD11l6VrCgsksJ+swEmz4Je7WhEplDp+4nNdpEXwE0
u5/CWqu/kZCf2LFHmikHMstVy/FMZ66l5sHXpEA8e5dfr92/mfK7R2nvER/M6VPg
vkFWmL19AgMBAAECggEAOAH4G0URMjaDM2RqPVigIsUfojcC0lbNVkdeKLDlu7DW
Rzgxj5fDhzsEavx+Gw6TWlPq3qthwJsPAJ2UYMWbmLO64sKCbtTWBa9gZNBqFRPx
zP45HwI5XZR+AxYiMfRZc7dre/wBi1iuxdxVAe0v/O10BvtyBr1t8e3m9T+Ctbe0
m6DLABIkrnwULBuHuWr1aFq2anmDrEiVzE09792qSPOspP/gbBnhkispu3DyqcaK
0lI8s/asGfa0OI7XPVR0L+9pXmbPIE75JcHp2YOxXIL/PWcGxy6G6M7Riw4CSb4N
zHrK1hoz8bynGrRGM5UzUfKgcZHYpLfEV5mOuybvYQKBgQDwdMqyhDvIx61Xw+cg
HfCuk6pddZoIlq4cdA8by2WWtM2+KVO5RIOnRWkMrVHQ02piYyzXLEbFk+jWi0U9
3f6Ph0aiX9Zg5tr8/EMArYySOyL6CNFxPe1rkK3edcbwZRphJ2RZi0nIhAzbwqKU
MOYqdM2KgAHSkofCA4nFYFc9NQKBgQDgjO70uHWB3Er8orkxo9BIoFqOyZKZMTjE
yDuhu3EyY6O+TrdWJQGeTcoeXeGh2sIqxri7T2CeLip669TiSkTHm7M6EhF5LB4g
qMCl2zjDtdLAxc1zlDXHMjWh9VdQ9adsb7eQqhIm7vRCQ7oABhGiXXvDvxugJ8RN
mpHYK04wKQKBgAld81t67T1IEgeeOQ22PN8AH53ea+6zt/qhxsjlZoK1bmJfB8Tq
11oXyRzWT56b7cQBawjCz8r4lRRufncHg9ghg+3lX9uMgrFiPlXzbMtxhE5LM6V+
rRYIn81Ptz08Iwfv2efnFiu6gSm6bkzpjr3OqQchL3D9hk5XhvFqbFuVAoGBAIKX
M/k6tYwac7nNYOrPnDhAmank7M8O4zb+RD/g5QorDHWHDfSZfQF1uBBFBQ2+TaHX
BGRa25PiwW3PbXVVP/7ePDOPgn33/GV9/tv2TGL1gWX/I2cOUOBSAQL5zhgVZZMY
vabyYbAm1AMdpDSTUvocZ/gFUqmntvBy7lLLjKNRAoGAaj3O6BP/djkE9zsm7Ao0
tqgwec15iaGz/5rRYXyVrWo0UqF0lNvXK2m5D0EiQeGSv3LR9EuilysVego4MvQk
Dn8ITNRti+YCeRgPBGd38+o4wWXsS50WtlQMWx2DmYrORPYadHEuA9lo1rt5uDoR
IOLIPinMkOLYWAdLHqPzL7E=
-----END PRIVATE KEY-----` //微信商户平台API私钥
wx_pay_cert := `-----BEGIN CERTIFICATE-----
MIID3DCCAsSgAwIBAgIUSdPFwujSihpwlK7m057+WdAXRnAwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjEwODExMDcwMzExWhcNMjYwODEwMDcwMzExWjBuMRgwFgYDVQQDDA9U
ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl
bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo
ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSzutwzsCf03UmqtP5
z+oMAyoPK78uNH9+inpMZlkLJslruerVhRVgz+U/Yz7t25GXha4Ui0EQ/HoaIUea
wbOghYA7GAk21lOU0WLaX8a9U2t96nme9PJ5yqnqi8P7FpX+HsJG+3WRhL0D3hm4
iLBEhMbHXvzri2NIbYDiac62Ekx6xtk3XjmfyTgabYRzTIJ8cw8RWW2+X0GYlyi9
biXy9diVd/rohl5++6ATcRmk51skJaPI4qaQDBjpedaS/pSd83ujhs51M/CvqgQ6
Woczk/ha5w9PVFJTIwIMzmr3HSXjYtKxgMucrfpNOlrb2uVD4xmulDA6XAhk9Hx/
bj8fAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBc
MFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js
P0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ
KoZIhvcNAQELBQADggEBAA69QPm7w9LcXU5lmyQ0HQdQFp9Rdq3IYmpEPDytRL0M
L40PKRc4VdS9ttZN4PUxh0Gat4EoYD7YR3nfB7Lx1xu00G3Rondx7g7ryTVN3y5E
SioX94JsZ8QD3NDHuCPwgMT5SS1FIyO9ls8x82vtagZc1M9YjP1x8kfledJxC4NI
JhpX1cuKgCbRLhi0qrerRoQlbbb7oP6NsBnXuVCPCHzxDg+YgLuPrz82w7Z5+UxZ
G0KYKvGdupW5Sc8n+yJczCsmhhhmJy8N17qf60UGi9vQdHXVy2nwHRMTnjTpP56x
52hbci3SxbjNY9IC+J+jaYDM6ChMPVIsdEniJKpof5E=
-----END CERTIFICATE-----` //平台证书
mch_serial_no := "735B1DE549DB6BF12796BA0EF87BABA7D3853080" //商户平台证书序列号
reason := "缺货取消" //退款原因
uuid := "28303" //退款原因
title := "抽巾纹~~" //回调地址
notify_url := "https://manager.youpincat.com/api/wxrefunds/refund_notify" //回调地址
ret, err := PayRefund(order_sn, sn, total, refund, wx_mp_mch_id, wx_mch_apiclient_key,
wx_pay_cert, mch_serial_no, title, uuid, reason, notify_url)
t.Log(ret)
t.Log(err)
}

+ 97
- 0
security_check.go View File

@ -0,0 +1,97 @@
package weixinrpc
import (
"encoding/json"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
"strconv"
"time"
)
//检测图片是否合法
func ImgSecCheck(appId, appSecret,imgUrl string, url ...string) (error) {
conn, err := rpc_server_conn(url...)
if err != nil {
return err
}
defer conn.Close()
data := map[string]interface{}{}
data["app_id"] = appId
data["app_secret"] = appSecret
data["img_url"] = imgUrl
data_json, err := json.Marshal(data)
if err != nil {
return err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.ImgSecCheck(req, res)
if err != nil {
return err
}
return nil
}
//检测文本是否合法
func MsgSecCheck(appId, appSecret string,checkData map[string]interface{}, url ...string)error{
conn, err := rpc_server_conn(url...)
if err != nil {
return err
}
defer conn.Close()
data := map[string]interface{}{}
data["app_id"] = appId
data["app_secret"] = appSecret
data["check_data"] = checkData
data_json, err := json.Marshal(data)
if err != nil {
return err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.MsgSecCheck(req, res)
if err != nil {
return err
}
return nil
}

+ 31
- 0
security_check_test.go View File

@ -0,0 +1,31 @@
package weixinrpc
import (
"git.tetele.net/tgo/helper"
"testing"
)
func Test_CheckImg(t *testing.T){
appid := "wx3d53ccbaf69f7995"
appSecret := "165983626235636be54a16404e3e70a7"
imgUrl := helper.TencentCloudImageCompress("https://images-1306193253.cos.ap-guangzhou.myqcloud.com/uploads/20211026/1635213788c5713f49816c74a0e130694ed6544e71.jpg","450")
//imgUrl := "https://images-1306193253.cos.ap-guangzhou.myqcloud.com/uploads/20211026/1635213788c5713f49816c74a0e130694ed6544e71.jpg"
err := ImgSecCheck(appid,appSecret,imgUrl)
t.Log(err)
}
func Test_CheckMsg(t *testing.T){
appid := "wx3d53ccbaf69f7995"
appSecret := "165983626235636be54a16404e3e70a7"
checkData := map[string]interface{}{
"openid":"oLBHE4kRuj8ywGKuXMgLbmFKrdkM",
"scene":3,
"content":"",
}
err := MsgSecCheck(appid,appSecret,checkData)
t.Log(err)
}

+ 70
- 0
send_subscribe_message.client.go View File

@ -0,0 +1,70 @@
package weixinrpc
import (
"encoding/json"
"strconv"
"time"
"git.tetele.net/tgo/crypter"
"github.com/golang/protobuf/proto"
)
/**
* 发送小程序订阅服务消息
* appid,secret 小程序ID 密钥
* touser 小程序openid
* mp_appid 关联公众号appid要求与小程序有绑定且同主体
* temp_id 模板id
* page 跳转的url
* lang 进入小程序查看的语言类型支持zh_CN(简体中文)en_US(英文)zh_HK(繁体中文)zh_TW(繁体中文)默认为zh_CN
* miniprogram_state 跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版
* data 公众号模板消息的数据
*/
func SendMiniSubscribeMessage(dbname, site_id string, appid, secret, touser, temp_id, page string, miniprogram_state, lang string, data interface{}, url ...string) (*WxApiRes, error) {
conn, err := rpc_server_conn(url...)
message_data := SubscribeMessageReq{}
message_data.SiteId = site_id
message_data.Dbname = dbname
message_data.Appid = appid
message_data.Secret = secret
message_data.Touser = touser
message_data.TemplateId = temp_id
message_data.Page = page
message_data.MiniprogramState = miniprogram_state
message_data.Lang = lang
message_data.Data = data
data_json, err := json.Marshal(message_data)
if err != nil {
return nil, err
}
encryData := crypter.DesEn(string(data_json), DES_KEY)
now_int64 := time.Now().Unix()
now := strconv.FormatInt(now_int64, 10)
sign := Sign(encryData, now)
req := &Request{
proto.String(encryData),
proto.String(now),
proto.String(sign),
nil}
res := &Response{}
err = conn.SendMiniappSubscribeMessage(req, res)
if err != nil {
return nil, err
}
return HandleRes(res)
}

+ 30
- 0
send_subscribe_message.client_test.go View File

@ -0,0 +1,30 @@
package weixinrpc
import (
"testing"
)
func Test_SendMiniSubscribeMessage(t *testing.T) {
db := "gkxy_tetele_net"
site_id := "1101832"
appid := "wx7b72dc719ad1f6d3"
secret := "b8ae6d64605a963056ff067cceffefd2"
touser := "osxVD5S0PAh82gyIXSiXCJzM_k6s"
temp_id := "0Qbw6nuzj3X4kvOqGbGgD-dlu06NF6u9_ZYIiw0TY4Y"
page := "pages/order/order"
miniprogram_state := ""
lang := ""
data := map[string]map[string]string{
"character_string1": map[string]string{"value": "961617783456"},
"phrase2": map[string]string{"value": "配送中"},
"thing3": map[string]string{"value": "小火锅"},
"thing4": map[string]string{"value": "香洲区测试用"},
"thing5": map[string]string{"value": "下单备注"},
}
// data_json, _ := json.Marshal(data)
ret, err := SendMiniSubscribeMessage(db, site_id, appid, secret, touser, temp_id, page, miniprogram_state, lang, data, "49.232.85.91:17969")
t.Log(ret, err)
}

+ 1
- 1
send_uniform_message.client.go View File

@ -21,7 +21,7 @@ import (
* data 公众号模板消息的数据 * data 公众号模板消息的数据
*/ */
func SendUniformMessage(dbname, site_id string, appid, secret, touser, mp_appid, temp_id, redirect_url, mini_program string, data interface{}, url ...string) (*WxApiRes, error) {
func SendUniformMessage(dbname, site_id string, appid, secret, touser, mp_appid, temp_id, redirect_url string, mini_program, data interface{}, url ...string) (*WxApiRes, error) {
conn, err := rpc_server_conn(url...) conn, err := rpc_server_conn(url...)


+ 19
- 17
send_uniform_message.client_test.go View File

@ -1,33 +1,35 @@
package weixinrpc package weixinrpc
import ( import (
"encoding/json"
"testing" "testing"
) )
func Test_SendUniformMessage(t *testing.T) { func Test_SendUniformMessage(t *testing.T) {
db := "gkxy_tetele_net" db := "gkxy_tetele_net"
site_id := "1101832"
appid := "wx7be627d0325135f4"
secret := "984c6d213c60419c4f30c159fbbe1ce9"
touser := "osxVD5S0PAh82gyIXSiXCJzM_k6s"
mp_appid := "wx056995d61ca222f5"
temp_id := "6xgj_65KivigMvwdJgiveR4wLXJLDpzsT2-hShczLmU"
site_id := "1111"
appid := "wx5ef4ad840cb3d269"
secret := "a937a3f5ac918ebba171deefe2c2ab60"
touser := "ox7nK5OQNuAfNAeIWVE8CsVL6D94" //"oDTib5KC5uX5rVivTnbK-sFkrC2Q" //
mp_appid := "wx5ef4ad840cb3d269"
temp_id := "VDpSj4VVJTH8-wVDp2t3izAuXXl79JYb0l6lezN4kig"
url := "" url := ""
mini_program := "wx7be627d0325135f4"
mini_program := map[string]string{
"appid": "wx5ef4ad840cb3d269",
"pagepath": "pages/order/index",
}
data := map[string]map[string]string{ data := map[string]map[string]string{
"first": map[string]string{"value": "来新订单了"},
"keyword1": map[string]string{"value": "订单号是。。。"},
"keyword2": map[string]string{"value": "取货位置"},
"keyword3": map[string]string{"value": "收货人"},
"keyword4": map[string]string{"value": "联系电话"},
"keyword5": map[string]string{"value": "收货地址"},
"remark": map[string]string{"value": "这是备注"},
"first": map[string]string{"value": "商城小程序"},
"keyword1": map[string]string{"value": "961617783456"},
"keyword2": map[string]string{"value": "测试商家"},
"keyword3": map[string]string{"value": "顾曾"},
"keyword4": map[string]string{"value": "18607565510"},
"keyword5": map[string]string{"value": "香洲区测试用"},
"remark": map[string]string{"value": "下单备注"},
} }
data_json, _ := json.Marshal(data)
// data_json, _ := json.Marshal(data)
//, "49.232.85.91:17969"
ret, err := SendUniformMessage(db, site_id, appid, secret, touser, mp_appid, temp_id, url, mini_program, data) ret, err := SendUniformMessage(db, site_id, appid, secret, touser, mp_appid, temp_id, url, mini_program, data)
t.Log(ret, err) t.Log(ret, err)
} }

+ 2
- 1
sign.go View File

@ -5,6 +5,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"log"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -84,7 +85,7 @@ func HandleRes(res *Response) (*WxApiRes, error) {
//解密 //解密
res_data_de, err := GetOrgData(res) res_data_de, err := GetOrgData(res)
log.Println("res_data_de", res_data_de)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 35
- 3
weixin.pb.go View File

@ -99,8 +99,16 @@ func init() {
type WeixinRpcService interface { type WeixinRpcService interface {
GetAccessToken(in *Request, out *Response) error GetAccessToken(in *Request, out *Response) error
SendMiniprogramSubscribeMessage(in *Request, out *Response) error
SendMiniappSubscribeMessage(in *Request, out *Response) error
SendUniformMessage(in *Request, out *Response) error SendUniformMessage(in *Request, out *Response) error
GetMiniAppOpenid(in *Request, out *Response) error
GetMpOpenid(in *Request, out *Response) error
GetAppOpenid(in *Request, out *Response) error
GetMpUserInfo(in *Request, out *Response) error
GetMiniappQrcode(in *Request, out *Response) error
ImgSecCheck(in *Request, out *Response) error
MsgSecCheck(in *Request, out *Response) error
PayRefund(in *Request, out *Response) error
} }
// AcceptWeixinRpcServiceClient accepts connections on the listener and serves requests // AcceptWeixinRpcServiceClient accepts connections on the listener and serves requests
@ -175,12 +183,36 @@ func NewWeixinRpcServiceClient(conn io.ReadWriteCloser) (*WeixinRpcServiceClient
func (c *WeixinRpcServiceClient) GetAccessToken(in *Request, out *Response) error { func (c *WeixinRpcServiceClient) GetAccessToken(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetAccessToken", in, out) return c.Call("WeixinRpcService.GetAccessToken", in, out)
} }
func (c *WeixinRpcServiceClient) SendMiniprogramSubscribeMessage(in *Request, out *Response) error {
return c.Call("WeixinRpcService.SendMiniprogramSubscribeMessage", in, out)
func (c *WeixinRpcServiceClient) SendMiniappSubscribeMessage(in *Request, out *Response) error {
return c.Call("WeixinRpcService.SendMiniappSubscribeMessage", in, out)
} }
func (c *WeixinRpcServiceClient) SendUniformMessage(in *Request, out *Response) error { func (c *WeixinRpcServiceClient) SendUniformMessage(in *Request, out *Response) error {
return c.Call("WeixinRpcService.SendUniformMessage", in, out) return c.Call("WeixinRpcService.SendUniformMessage", in, out)
} }
func (c *WeixinRpcServiceClient) GetMiniAppOpenid(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetMiniAppOpenid", in, out)
}
func (c *WeixinRpcServiceClient) GetMpOpenid(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetMpOpenid", in, out)
}
func (c *WeixinRpcServiceClient) GetAppOpenid(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetAppOpenid", in, out)
}
func (c *WeixinRpcServiceClient) GetMpUserInfo(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetMpUserInfo", in, out)
}
func (c *WeixinRpcServiceClient) GetMiniappQrcode(in *Request, out *Response) error {
return c.Call("WeixinRpcService.GetMiniappQrcode", in, out)
}
func (c *WeixinRpcServiceClient) ImgSecCheck(in *Request, out *Response) error {
return c.Call("WeixinRpcService.ImgSecCheck", in, out)
}
func (c *WeixinRpcServiceClient) MsgSecCheck(in *Request, out *Response) error {
return c.Call("WeixinRpcService.MsgSecCheck", in, out)
}
func (c *WeixinRpcServiceClient) PayRefund(in *Request, out *Response) error {
return c.Call("WeixinRpcService.PayRefund", in, out)
}
// DialWeixinRpcService connects to an WeixinRpcService at the specified network address. // DialWeixinRpcService connects to an WeixinRpcService at the specified network address.
func DialWeixinRpcService(network, addr string) (*WeixinRpcServiceClient, *rpc.Client, error) { func DialWeixinRpcService(network, addr string) (*WeixinRpcServiceClient, *rpc.Client, error) {


+ 9
- 1
weixin.proto View File

@ -19,6 +19,14 @@ message Response {
// rpc方法 // rpc方法
service WeixinRpcService { service WeixinRpcService {
rpc getAccessToken (Request) returns (Response); // access token rpc getAccessToken (Request) returns (Response); // access token
rpc sendMiniprogramSubscribeMessage (Request) returns (Response); //
rpc sendMiniappSubscribeMessage (Request) returns (Response); //
rpc sendUniformMessage(Request) returns (Response); // rpc sendUniformMessage(Request) returns (Response); //
rpc getMiniAppOpenid(Request) returns (Response); // openid
rpc getMpOpenid(Request) returns (Response); // openid
rpc getAppOpenid(Request) returns (Response); // app的openid
rpc getMpUserInfo(Request) returns (Response); //
rpc getMiniappQrcode(Request) returns (Response); //
rpc imgSecCheck(Request) returns (Response); //
rpc msgSecCheck(Request) returns (Response); //
rpc payRefund(Request) returns (Response); //v3支付退款
} }

Loading…
Cancel
Save