13 Commits

Author SHA1 Message Date
  loshiqi c3faf5bc5e 增加发送模板消息跳转小程序 2 months ago
  guzeng 38520a86e7 增加发送模板消息 8 months ago
  guzeng 2373d13446 修改扫码支付方法 10 months ago
  guzeng de651b674e 修改扫码支付方法 10 months ago
  guzeng 30cc37109b 增加扫码支付方法 10 months ago
  guzeng 524c79ec91 打印二维码生成结果 1 year ago
  lijianbin 535afb769b 小程序码路径&字符被转义问题 1 year ago
  lijianbin 4046688a55 小程序码问题debug 1 year ago
  lijianbin 8e322f45cc 小程序码问题debug 1 year ago
  lijianbin 229de89019 小程序码打印错误码 1 year ago
  loshiqi e99270d9d8 字段名称修改 1 year ago
  loshiqi 1d2e6fab6f 增加微信退款 1 year ago
  guzeng a237f130a8 修复调用方法 1 year ago
14 changed files with 740 additions and 15 deletions
Split View
  1. +51
    -0
      common.go
  2. +4
    -1
      go.mod
  3. +16
    -1
      go.sum
  4. +2
    -0
      http.go
  5. +334
    -0
      micropay.go
  6. +109
    -0
      mp.go
  7. +46
    -0
      mp_test.go
  8. +1
    -1
      pay.go
  9. BIN
      qqwry.dat
  10. BIN
      qqwry20200228.dat
  11. +13
    -4
      qrcode.go
  12. +126
    -0
      refund.go
  13. +27
    -0
      refund_test.go
  14. +11
    -8
      url.go

+ 51
- 0
common.go View File

@ -0,0 +1,51 @@
package wechat
import (
"crypto/md5"
"encoding/hex"
"net"
"strings"
)
// 密码加密
func Md5Str(str ...string) string {
var build strings.Builder
if len(str) > 0 {
for _, v := range str {
build.WriteString(v)
}
} else {
return ""
}
h := md5.New()
h.Write([]byte(build.String())) // 需要加密的字符串
cipher2Str := h.Sum(nil)
sMd5 := hex.EncodeToString(cipher2Str) // 输出加密结果
return sMd5
}
/**
* 取本地IP
*/
func GetLocalIp() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
// 检查ip地址判断是否回环地址
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil && !ipnet.IP.IsLinkLocalUnicast() {
return ipnet.IP.String()
}
}
}
return ""
}

+ 4
- 1
go.mod View File

@ -3,6 +3,9 @@ module git.tetele.net/tgo/wechat
go 1.16
require (
git.tetele.net/tgo/helper v0.3.2 // indirect
git.tetele.net/tgo/helper v0.3.2
github.com/google/btree v1.1.2 // indirect
github.com/json-iterator/go v1.1.12
github.com/kayon/iploc v0.0.0-20200312105652-bda3e968a794
github.com/wechatpay-apiv3/wechatpay-go v0.2.16
)

+ 16
- 1
go.sum View File

@ -1,11 +1,17 @@
git.tetele.net/tgo/helper v0.3.2 h1:3Pd3Ih3Ux+zFSjNZZv+4MSsVQiZ5HOfQmSus5+7rKR0=
git.tetele.net/tgo/helper v0.3.2/go.mod h1:89mQwyfqZ+t8YXiVwzSxA70gLlUNqoZGDEUxvV46jXk=
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kayon/iploc v0.0.0-20200312105652-bda3e968a794 h1:dWJxw+KQOMeVcoyxqG9I5fppPld1hh1FG8ngv0fKNsQ=
github.com/kayon/iploc v0.0.0-20200312105652-bda3e968a794/go.mod h1:IwrOeG3O3K9vVXmcVvc9T0XLabw67QePi5pKQt5U+Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
@ -13,5 +19,14 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/wechatpay-apiv3/wechatpay-go v0.2.16 h1:7wTQYLxx5PSkzv0Zu/LabGHDVva9VEvH56su/lzRPbE=
github.com/wechatpay-apiv3/wechatpay-go v0.2.16/go.mod h1:Ca9wvI7xFoIWiY163q1jzddarQBS+1NE17OM1ZV24nw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 2
- 0
http.go View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/xml"
"io/ioutil"
"log"
"net/http"
"strings"
)
@ -97,6 +98,7 @@ func SendXml(method, url string, requestxml interface{}, header ...map[string]st
*/
func PostJson(url string, param []byte, header ...map[string]string) ([]byte, error) {
httpClient := &http.Client{}
log.Println("param_code", bytes.NewBuffer(param))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(param))
if err != nil {


+ 334
- 0
micropay.go View File

@ -0,0 +1,334 @@
package wechat
import (
"encoding/xml"
"errors"
"fmt"
"log"
"reflect"
"strings"
"git.tetele.net/tgo/helper"
)
// 请求支付
type PayXml struct {
XMLName xml.Name `xml:"xml"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
DeviceInfo string `xml:"device_info"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
Body string `xml:"body"`
OutTradeNo string `xml:"out_trade_no"`
TotalFee int `xml:"total_fee"`
SpbillCreateIp string `xml:"spbill_create_ip"`
AuthCode string `xml:"auth_code"`
}
// 支付结果
type PayReturnXml struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
DeviceInfo string `xml:"device_info"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
ResultCode string `xml:"result_code"`
ErrCode string `xml:"err_code"`
ErrCodeDes string `xml:"err_code_des"`
Openid string `xml:"openid"`
IsSubscribe string `xml:"is_subscribe"`
TradeType string `xml:"trade_type"`
BankType string `xml:"bank_type"`
FeeType string `xml:"fee_type"`
TotalFee string `xml:"total_fee"`
SettlementTotalFee string `xml:"settlement_total_fee"`
CouponFee string `xml:"coupon_fee"`
CashFeeType string `xml:"cash_fee_type"`
CashFee string `xml:"cash_fee"`
TransactionId string `xml:"transaction_id"`
OutTradeNo string `xml:"out_trade_no"`
Attach string `xml:"attach"`
TimeEnd string `xml:"time_end"`
PromotionDetail string `xml:"promotion_detail"`
}
// 查询支付
type QueryXml struct {
XMLName xml.Name `xml:"xml"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
OutTradeNo string `xml:"out_trade_no"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
}
// 查询结果
type QueryReturnXml struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
ResultCode string `xml:"result_code"`
ErrCode string `xml:"err_code"`
ErrCodeDes string `xml:"err_code_des"`
DeviceInfo string `xml:"device_info"`
Openid string `xml:"openid"`
IsSubscribe string `xml:"is_subscribe"`
TradeType string `xml:"trade_type"`
TradeState string `xml:"trade_state"`
BankType string `xml:"bank_type"`
TotalFee string `xml:"total_fee"`
SettlementTotalFee string `xml:"settlement_total_fee"`
FeeType string `xml:"fee_type"`
CashFee string `xml:"cash_fee"`
CashFeeType string `xml:"cash_fee_type"`
CouponFee string `xml:"coupon_fee"`
CouponCount string `xml:"coupon_count"`
TransactionId string `xml:"transaction_id"`
OutTradeNo string `xml:"out_trade_no"`
Attach string `xml:"attach"`
TimeEnd string `xml:"time_end"`
TradeStateDesc string `xml:"trade_state_desc"`
}
// 撤消
type ReverseXml struct {
XMLName xml.Name `xml:"xml"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
OutTradeNo string `xml:"out_trade_no"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
}
// 撤消结果
type ReverseReturnXml struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
Appid string `xml:"appid"`
MchId string `xml:"mch_id"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
ResultCode string `xml:"result_code"`
ErrCode string `xml:"err_code"`
ErrCodeDes string `xml:"err_code_des"`
Recall string `xml:"recall"`
}
/**
* 微信二维码支付
* 2022/02/07
* @param device_info 设备号
* @param body 商品描述
* @param out_trade_no 商户订单号
* @param total_fee 订单金额
* @param auth_code 付款码
*/
func MicroPay(appid, mchid, mch_key, device_info, body, out_trade_no, auth_code, total_fee string) (PayReturnXml, error) {
log.Println("wechat micropay")
var requestxml PayXml
var retXml PayReturnXml
requestxml.Appid = appid
requestxml.MchId = mchid
requestxml.DeviceInfo = device_info
requestxml.NonceStr = helper.GetRandomString(30)
requestxml.Body = body
requestxml.OutTradeNo = out_trade_no
requestxml.TotalFee = helper.ToInt(helper.FloatMul(total_fee, 100))
requestxml.SpbillCreateIp = GetLocalIp()
requestxml.AuthCode = auth_code
signStr := helper.StringJoin("appid=", requestxml.Appid, "&auth_code=", requestxml.AuthCode,
"&body=", requestxml.Body, "&device_info=", requestxml.DeviceInfo,
"&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
"&out_trade_no=", requestxml.OutTradeNo, "&spbill_create_ip=", requestxml.SpbillCreateIp,
"&total_fee=", helper.ToString(requestxml.TotalFee), "&key=", mch_key)
signMd5 := Md5Str(signStr)
requestxml.Sign = strings.ToUpper(signMd5)
reqXmlJson, err := xml.Marshal(requestxml)
log.Println(requestxml, string(reqXmlJson), err)
url := "https://api.mch.weixin.qq.com/pay/micropay"
ret, err := SendXml("POST", url, requestxml)
log.Println("wechat micropay pay result:", string(ret), err)
if err != nil {
log.Println("Err:", err)
return retXml, errors.New("微信支付失败,请检查网络")
}
err = xml.Unmarshal(ret, &retXml)
if err != nil {
log.Println("xml Unmarshal Err:", retXml, err)
return retXml, errors.New("未知的支付结果")
}
if retXml.ResultCode == "SUCCESS" {
retMap := XmlStructToMap(retXml)
//验证sign
signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
sign := strings.ToUpper(Md5Str(signStr))
if sign != retXml.Sign {
log.Println("Err:签名错误", string(ret), sign)
return retXml, errors.New("支付结果签名错误")
}
} else {
return retXml, errors.New(retXml.ReturnMsg)
}
return retXml, nil
}
func MicroPayQuery(appid, mchid, out_trade_no, mch_key string) (QueryReturnXml, error) {
log.Println("wechat micropay query", out_trade_no)
var requestxml QueryXml
var retXml QueryReturnXml
requestxml.Appid = appid
requestxml.MchId = mchid
requestxml.NonceStr = helper.GetRandomString(30)
requestxml.OutTradeNo = out_trade_no
signStr := helper.StringJoin("appid=", requestxml.Appid,
"&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
"&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key)
signMd5 := Md5Str(signStr)
requestxml.Sign = strings.ToUpper(signMd5)
url := "https://api.mch.weixin.qq.com/pay/orderquery"
ret, err := SendXml("POST", url, requestxml)
if err != nil {
log.Println("Err:", err)
return retXml, errors.New("微信支付失败,请检查网络")
}
err = xml.Unmarshal(ret, &retXml)
if err != nil {
log.Println("xml Unmarshal Err:", retXml, err)
return retXml, errors.New("未知的支付结果")
}
if retXml.ReturnCode == "SUCCESS" {
retMap := XmlStructToMap(retXml)
//验证sign
signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
sign := strings.ToUpper(Md5Str(signStr))
if sign != retXml.Sign {
log.Println("Err:签名错误", string(ret), sign)
return retXml, errors.New("查询支付结果签名错误")
}
} else {
return retXml, errors.New(retXml.ReturnMsg)
}
return retXml, nil
}
/**
* 撤消订单
* 2023/06/13
*/
func MicroPayReverse(appid, mchid, out_trade_no, mch_key string) (ReverseReturnXml, error) {
log.Println("wechat micropay reverse", out_trade_no)
var requestxml ReverseXml
var retXml ReverseReturnXml
requestxml.Appid = appid
requestxml.MchId = mchid
requestxml.NonceStr = helper.GetRandomString(30)
requestxml.OutTradeNo = out_trade_no
signStr := helper.StringJoin("appid=", requestxml.Appid,
"&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
"&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key)
signMd5 := Md5Str(signStr)
requestxml.Sign = strings.ToUpper(signMd5)
url := "https://api.mch.weixin.qq.com/secapi/pay/reverse"
ret, err := SendXml("POST", url, requestxml)
if err != nil {
log.Println("Err:", err)
return retXml, errors.New("撤消微信支付失败,请检查网络")
}
err = xml.Unmarshal(ret, &retXml)
if err != nil {
log.Println("xml Unmarshal Err:", retXml, err)
return retXml, errors.New("未知的撤消结果")
}
if retXml.ReturnCode == "SUCCESS" {
retMap := XmlStructToMap(retXml)
//验证sign
signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
sign := strings.ToUpper(Md5Str(signStr))
if sign != retXml.Sign {
log.Println("Err:签名错误", string(ret), sign)
return retXml, errors.New("查询支付结果签名错误")
}
} else {
return retXml, errors.New(retXml.ReturnMsg)
}
return retXml, nil
}
func XmlStructToMap(retXml interface{}) map[string]string {
t := reflect.TypeOf(retXml)
v := reflect.ValueOf(retXml)
//转为map,验签
retMap := make(map[string]string)
var retValue string
var tag string
for k := 0; k < t.NumField(); k++ {
if t.Field(k).Name != "Sign" {
retValue = helper.ToString(v.Field(k).Interface())
tag = fmt.Sprintf("%s", t.Field(k).Tag)
tag = strings.ReplaceAll(tag, "xml:", "")
tag = strings.ReplaceAll(tag, "\"", "")
retMap[tag] = retValue
}
}
return retMap
}

+ 109
- 0
mp.go View File

@ -0,0 +1,109 @@
package wechat
import (
"errors"
"fmt"
)
type wx_template_msg_res struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
msgid int `json:"msgid"`
}
func SendTemplateMessage(access_token string, touser, template_id, url string, data map[string]interface{}, client_msg_id, miniapp_appid, miniapp_pagepath string) (wx_template_msg_res, error) {
var res wx_template_msg_res
if touser == "" {
return res, errors.New("缺少用户openid")
}
if template_id == "" {
return res, errors.New("缺少消息模板")
}
if access_token == "" {
return res, errors.New("缺少access token")
}
api_url := fmt.Sprintf(MP_TEMPLATE_MESSAGE_API, access_token)
msg := map[string]interface{}{
"touser": touser,
"template_id": template_id,
"url": url,
"data": data,
}
if client_msg_id != "" {
msg["client_msg_id"] = client_msg_id
}
if miniapp_appid != "" {
msg["miniprogram"] = map[string]string{
"appid": miniapp_appid,
"pagepath": miniapp_pagepath,
}
}
msg_json, err := json.Marshal(msg)
if err != nil {
return res, err
}
data_byte, err := PostJson(api_url, msg_json)
if err == nil {
err = json.Unmarshal(data_byte, &res)
}
return res, err
}
func SendTemplateMessageApplet(access_token string, touser, template_id string, miniprogram map[string]string, data map[string]interface{}, client_msg_id, miniapp_appid, miniapp_pagepath string) (wx_template_msg_res, error) {
var res wx_template_msg_res
if touser == "" {
return res, errors.New("缺少用户openid")
}
if template_id == "" {
return res, errors.New("缺少消息模板")
}
if access_token == "" {
return res, errors.New("缺少access token")
}
api_url := fmt.Sprintf(MP_TEMPLATE_MESSAGE_API, access_token)
msg := map[string]interface{}{
"touser": touser,
"template_id": template_id,
"data": data,
}
if len(miniprogram) > 0 {
msg["miniprogram"] = miniprogram
}
if client_msg_id != "" {
msg["client_msg_id"] = client_msg_id
}
if miniapp_appid != "" {
msg["miniprogram"] = map[string]string{
"appid": miniapp_appid,
"pagepath": miniapp_pagepath,
}
}
msg_json, err := json.Marshal(msg)
if err != nil {
return res, err
}
data_byte, err := PostJson(api_url, msg_json)
if err == nil {
err = json.Unmarshal(data_byte, &res)
}
return res, err
}

+ 46
- 0
mp_test.go View File

@ -0,0 +1,46 @@
package wechat
import (
"testing"
)
func Test_SendTemplateMessage(t *testing.T) {
// appid := "wxedaca3ab498ec6c0"
// appsecret := "6c7e567181dba2fe23692339d19ee16a"
touser := "o4AH1jm_rnjDV4-4nsFhGhk4wIEQ"
template_id := "avBk4QTEoA17c4G72XHet9gpGQtonW0ck2ewQWSsUwU"
url := "https://shopv2.tetele.net"
data := map[string]interface{}{
"first": map[string]interface{}{
"value": "已预约成功",
},
"keyword1": map[string]interface{}{
"value": "巧克力",
},
"keyword2": map[string]interface{}{
"value": "2014年9月22日",
},
"remark": map[string]interface{}{
"value": "请点击详情",
},
}
client_msg_id := ""
miniapp_appid := ""
miniapp_pagepath := ""
// mpAT, err := GetAccessToken(appid, appsecret)
// t.Log("access token:", mpAT)
// if err==nil{
// access_token = mpAT..AccessToken
// }
// if err != nil {
// t.Error("access token error:", err)
// }
access_token := "71_sgGezbuBtu94tgRuhzSJHiDvgoOI2mS5kpYyeaynnvILmpPPHEclUmhKq74fj0M3HRjZk7hcNfB54tAJvNZPOrZleXQX2dyWZm_8tyu5m1YpAb8qLrwRnUNRdowMJXhAIAVSG"
ret, err := SendTemplateMessage(access_token, touser, template_id, url, data, client_msg_id, miniapp_appid, miniapp_pagepath)
t.Log(ret)
t.Log(err)
}

+ 1
- 1
pay.go View File

@ -46,7 +46,7 @@ func GetPayResult(dbname, order_sn, wx_appid, wx_mch_id, wx_mch_serial_no, wx_mc
log.Println("平台证书有误:", err)
return false, nil, errors.New("平台证书不正确")
}
client, err = CreateClient(ctx, wechatPayCertificate, privateKey, wx_mch_id, wx_mch_serial_no)
client, err = CreatePayClient(ctx, wechatPayCertificate, privateKey, wx_mch_id, wx_mch_serial_no)
if err != nil {
log.Println("create pay client error:", err)
return false, nil, errors.New("构造失败")


BIN
qqwry.dat View File


BIN
qqwry20200228.dat View File


+ 13
- 4
qrcode.go View File

@ -1,6 +1,7 @@
package wechat
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
@ -49,13 +50,21 @@ func GetMiniappQrcode(access_token string, qrcodeParamsMap map[string]interface{
"auto_color": autoColor,
"is_hyaline": isHyaline,
}
requestDataJson, err := json.Marshal(requestData)
// json.marshal 会转义&字符
/*requestDataJson, err := json.Marshal(requestData)
if err != nil {
return "", err
}
}*/
bf := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(bf)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.Encode(requestData)
response, err := PostJson(getCodeUrl, bf.Bytes())
response, err := PostJson(getCodeUrl, requestDataJson)
log.Println("get mp qrcode response:", string(response), err)
if err != nil {
return "", err
@ -69,7 +78,7 @@ func GetMiniappQrcode(access_token string, qrcodeParamsMap map[string]interface{
}
if _, exist := responseData["errcode"]; exist && helper.ToInt(responseData["errcode"]) != 0 {
return "", errors.New(helper.ToStr(responseData["errmsg"]))
return "", errors.New(helper.ToStr(responseData["errmsg"]) + ",errcode:" + helper.ToStr(responseData["errcode"]))
}
return "data:image/png;base64," + base64.StdEncoding.EncodeToString(response), nil


+ 126
- 0
refund.go View File

@ -0,0 +1,126 @@
package wechat
import (
"context"
"crypto/rsa"
"crypto/x509"
"errors"
"git.tetele.net/tgo/helper"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"io/ioutil"
"log"
)
/**
* 商户订单号,退款单号,总金额,退款金额,微信商户id,微信商户平台API私钥,平台证书,商户平台证书序列号,商品名,商品id,退款原因,回调地址
*/
func CreateRefund(order_sn, out_refund_no, total_amount, refund_amount, wx_mp_mch_id, wx_mch_apiclient_key,
wx_pay_cert, mch_serial_no, title, uuid, reason, notify_url string) (map[string]interface{}, error) {
total := helper.ToInt64(helper.FloatMul(total_amount, 100)) //总金额
refund := helper.ToInt64(helper.FloatMul(refund_amount, 100)) //退款金额
log.Println("refund", order_sn, out_refund_no, refund, total, notify_url)
var (
ctx context.Context = context.Background()
client *core.Client
)
privateKey, err := utils.LoadPrivateKey(wx_mch_apiclient_key)
if err != nil {
log.Println("商户私钥有误:", err)
return map[string]interface{}{}, errors.New("商户私钥有误")
}
wechatPayCertificate, err := utils.LoadCertificate(wx_pay_cert)
if err != nil {
log.Println("平台证书有误:", err)
return map[string]interface{}{}, errors.New("商户平台证书有误")
}
client, err = CreateClient(ctx, wechatPayCertificate, privateKey, wx_mp_mch_id, mch_serial_no)
if err != nil {
return map[string]interface{}{}, errors.New("请检查支付参数配置")
}
// now := time.Now()
svc := refunddomestic.RefundsApiService{Client: client}
_, result, err := svc.Create(ctx,
refunddomestic.CreateRequest{
Amount: &refunddomestic.AmountReq{
Currency: core.String("CNY"),
// From: []refunddomestic.FundsFromItem{refunddomestic.FundsFromItem{
// Account: refunddomestic.ACCOUNT_AVAILABLE.Ptr(),
// Amount: core.Int64(444),
// }},
Refund: core.Int64(refund),
Total: core.Int64(total),
},
// FundsAccount: refunddomestic.REQFUNDSACCOUNT_AVAILABLE.Ptr(),
GoodsDetail: []refunddomestic.GoodsDetail{refunddomestic.GoodsDetail{
GoodsName: core.String(title),
MerchantGoodsId: core.String(uuid),
RefundAmount: core.Int64(refund),
RefundQuantity: core.Int64(1),
UnitPrice: core.Int64(refund),
// WechatpayGoodsId: core.String("1001"),
}},
NotifyUrl: core.String(notify_url),
OutRefundNo: core.String(out_refund_no),
OutTradeNo: core.String(order_sn),
Reason: core.String(reason),
// SubMchid: core.String("1900000109"),
// TransactionId: core.String("1217752501201407033233368018"),
},
)
res := map[string]interface{}{}
log.Println("Response:", result.Response)
log.Println("Response Body:", result.Response.Body)
if err != nil {
response_body := map[string]interface{}{}
body, _ := ioutil.ReadAll(result.Response.Body)
err_body := json.Unmarshal(body, &response_body)
if err_body != nil {
res["code"] = "RESPONSE_BODY_ERROR"
res["message"] = "退款回调参数异常"
return res, err_body
}
log.Println("response_body:", response_body)
res["code"] = response_body["code"]
res["message"] = response_body["message"]
return res, errors.New(helper.ToStr(response_body["message"]))
}
res["code"] = "success"
res["message"] = "退款成功"
return res, nil
}
func CreateClient(ctx context.Context, wechatPayCertificate *x509.Certificate, privateKey *rsa.PrivateKey, mchID, mchSerialNo string) (*core.Client, error) {
var client *core.Client
var err error
if wechatPayCertificate != nil {
client, err = core.NewClient(
ctx, option.WithMerchantCredential(mchID, mchSerialNo, privateKey),
option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
)
if err != nil {
log.Println("创建 Client 失败:", err)
return nil, err
}
} else {
client, err = core.NewClient(
ctx, option.WithMerchantCredential(mchID, mchSerialNo, privateKey), option.WithoutValidator(),
)
if err != nil {
log.Println("创建 Client 失败:", err)
return nil, err
}
}
return client, nil
}

+ 27
- 0
refund_test.go View File

@ -0,0 +1,27 @@
package wechat
import (
"github.com/kayon/iploc"
"testing"
)
func Test_CreateRefund(t *testing.T) {
wx_mch_apiclient_key := "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDsea/umgdNz68w\nBdISLMPg+5E5Od2EDblO0ifz60P92kyIIQgFMKnw7sawfYlQNMs7fTOKMUBFoSJH\nzUNdOXUxLTmvPg9Pnh2jBBDQkUqBCMvUSsk5WzqF2VTSAmESy2XnfbGrXz9Q4KhV\nsLwP9O0qFDCrxgqjhKStUew6wNBq03lPnyvOee7QQ3eBZYY2z98XXjHai4nH7Lwi\nzwfYN23P/1QjWcs7a+J53v9KXjlEofNQzoXKy0hJnkPySTF8J9V7vBkbNwRceDVD\ng/uCsZ+ZBYEqwVtu7TkBaFZS06/y8/9iGNyvVLTBOzyX5XTxMAjSctRKuHEkzTII\n4ikNGtxbAgMBAAECggEAG8ez3lDj+MUkLH0Sj6YIoN6M0e81UbUXBuZzYRlLVIAX\nq4pN2eY8oJw8ZTFqNyzsjyGHM33Tshpa35qChmoglVVdyHN+8vh9PNBWdVoMKZpx\n74+VkhJgjqbYaw9yffW9E/PVMm+vDX5G6EIp5an9TosuqI9MYqdLfTz5hnFuSo4x\ngZvJmQZ/NytYjUDUOKejHjMFcfb9oBJpMDD+cOpqbK8DI9t+a4WWFxgX25TsgtzY\n9Dwnx/H4gXoDBU5nayZ5aQ1EXwNcH6s+XbAYQUpneWyjdsmg5mCuIRf/DaC0amAx\ncyGU74YJA1/eapD+TJsrNmboMdXFBUVsdwGrlF1IMQKBgQD/bUiCdYKKMC5DKidB\nrT6YqJC3lDoSbAQw/vhOy6xP1zegJdX3irlFweKCEzhRTlStV7C/vBe7eylz303Q\nUS6T59piEtKLlSJNxiPTWH0Gr+KGdJJgXa/fZPL0OEEDE7ScDrbGtAPrKLCM+Nx1\nXHujy/g2i1hbKuD82wWFidcfwwKBgQDtAYSslD/43qSGngWom2UPCr7b5nlhk9Xh\nihztlrTV+txCE5KUx1dq2kuYU2sywDlDJN7+v0XW+YhQi7PVGI5eWwh4voR87x/4\nSKhHxhkKPG1EFSU/oX8rmWUvYArgYBNFBfC7EdxbjfBGZn6lJrpRwQN1MJivGJlB\nTS4NFXDfiQKBgQD+Iwd/9dcaEvIUlX5VlD+xuBW08eANsDdDYIxCKdeZ4LJRvxNy\npi3MwCnT7bec/ei2R4duXYDnYpVN2qIbYIqNUqQv7DZb9tgYtcmzYFmVK66gxJ9X\nDiAMju5EGyxkGVkb5G/qp/U2JlfzCzCd/JRv1mNyMZaJrtIIAwstYIYP7QKBgD2p\npnYsQBSlAJNRq9E5VOq79fpKlYwQpZX/lBUwn64IjaPXacyxrInNb9ua4Iocvwk8\nH5SR+T68P34NSuWHdQsRjjuz/c5vfJbheH7sCHnETP1orv9GKksSpuPTjiDgfkL4\nD7DsLbPYUwpOAmc/kzxQBnFICoGL2dvGk9Otn2dZAoGBAOsn4AKGN/8PcFAJ2CgA\nZiqlninYOqWZl6dYdLRnhZ8sFF9MlOFPH+0OnRgNWihuF1Mbjjs1vuIP/2bgWAEw\nBr5hb+CyVD17FKICcb9Aa+hZarppRwuPkdNNcrOAOtCcHBV1k3Syp853OyUUqO8a\nxM/YXk8qkTIx0EdnYgemHeWh\n-----END PRIVATE KEY-----"
wx_pay_cert := "-----BEGIN CERTIFICATE-----\nMIID3DCCAsSgAwIBAgIUWlZytNdS/jkalg7CE2RiKZk0qa8wDQYJKoZIhvcNAQEL\nBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT\nFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg\nQ0EwHhcNMjAwODA0MDgwMzI0WhcNMjUwODAzMDgwMzI0WjBuMRgwFgYDVQQDDA9U\nZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl\nbnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo\nZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDU68UowJdEQlwqB1ID\nTpnfmsn6hR9xeTOTMt4db6tuAtExA5yNRU9T8ilBeEPIqjAPxHq/V6mDxwhw7gur\nSbqS80YhaHqqwFTu4lfktlHa3/xi3Itty9+70e75evlem+OY2OQEjpWtKnKjq7Op\nMrHpihkpxXbYxvTQe8yR99cSm3Pa9SR5FXL3rqs3f/M1LBw5faUBG/7ak+LZvqmD\nokdJVXk5VJg3TPtt/QOAwC4QeDCFt1uzPqs8LJWuLbQczHd0hzUvDye+9tIQ5eDM\nDoyME0YTZfspnn4WD3fQSCnf2oL8t2E1Che9w0lydXpNkYEgMgTLuX344jHSn6DY\nDNLVAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBc\nMFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js\nP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ\nKoZIhvcNAQELBQADggEBAClIek1KQAFhAIoP1UKXb/zFOGdWVuBD4vg/3aT33MlW\nsXmCH+oB6QyuY0tqIaQ5XfnvUgFFuTk+kMQuq2Tayj6XNozu1pteVtQ//3u2V9DF\nGXNxiT588EeN1od9qT+zhLH6nuptw++mlxBorQQ3BJvgX3NIOgFU/ajCiSkGAnpj\nD4QcHWzsM6kwpK6JUuPDRq3E7O/1cCJkt6a20JQj+fbYJ0By28PgdvaphuK8lW3e\nuSlkSNLP/a1F7sp5xHMW4a/E+/OF9D4ETHfoQhVYwOTHlxpnXPLApQtj9zcwr64/\nU9DP6BALDin+8uBDDkxsF29VyCYZ/l6LdsnYD+ihbSo=\n-----END CERTIFICATE-----"
ret, err := CreateRefund("6006167143914016978", "111223", "0.01", "0.01", "1298704001", wx_mch_apiclient_key, wx_pay_cert, "4642B326DDDDEAC74C677C163FB82BC95DCE43C0", "中餐", "122331", "yuanyin", "https://ttl-mini.tetele.net/admin/user/user/ttt_refund")
t.Log(ret)
t.Log(err)
}
func Test_Qqwry(t *testing.T) {
loc, err := iploc.Open("qqwry.dat")
if err != nil {
panic(err)
}
detail := loc.Find("219.131.197.178")
t.Log(detail.String())
t.Log(detail.Country, detail.Province, detail.City, detail.County)
}

+ 11
- 8
url.go View File

@ -6,28 +6,31 @@ import (
var json = jsoniter.ConfigCompatibleWithStandardLibrary
//获取access token
// 获取access token
const ACCESS_TOKEN_API string = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
//小程序发送统一服务消息
// 小程序发送统一服务消息
const MINIAPP_UNIFORM_MESSAGE_API string = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=%s"
//小程序获取openid
// 小程序获取openid
const GET_MINIAPP_OPENID_API string = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
//公众号获取openid
// 公众号获取openid
const GET_MP_OPENID_API string = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
//小程序发送订阅消息
// 小程序发送订阅消息
const MINIAPP_SUBSCRIBE_MESSAGE_SEND_API string = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s"
//app获取openid
// app获取openid
const GET_APP_OPENID string = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
const GET_MINIAPP_QRCODE string = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
//公众号授权
// 公众号授权
const OFFICAL_ACCOUNT_AUTHORIZATION_URL string = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect"
//公众号获取用户信息
// 公众号获取用户信息
const MP_USERINFO_API string = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=%s"
// 公众号模板消息
const MP_TEMPLATE_MESSAGE_API string = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s"

Loading…
Cancel
Save