micropay
This commit is contained in:
parent
10492bab9f
commit
65fedea2db
|
@ -0,0 +1,17 @@
|
||||||
|
package weixin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func randomStr(length int) string {
|
||||||
|
str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
bytes := []byte(str)
|
||||||
|
result := []byte{}
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
result = append(result, bytes[r.Intn(len(bytes))])
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
package weixin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Params map[string]string
|
||||||
|
|
||||||
|
func (p Params) Set(k string, v interface{}) {
|
||||||
|
p[k] = fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Params) GetString(k string) string {
|
||||||
|
s, _ := p[k]
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Params) GetUint64(k string) uint64 {
|
||||||
|
s, _ := strconv.ParseUint(p[k], 10, 64)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Params) GetInt64(k string) int64 {
|
||||||
|
i, _ := strconv.ParseInt(p.GetString(k), 10, 64)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Params) GetInt(k string) int64 {
|
||||||
|
i, _ := strconv.ParseInt(p.GetString(k), 10, 64)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Params) GetFloat64(k string) float64 {
|
||||||
|
f, _ := strconv.ParseFloat(p.GetString(k), 64)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
func (p Params) GetBool(k string) bool {
|
||||||
|
b, _ := strconv.ParseBool(p.GetString(k))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML解码
|
||||||
|
func (p Params) Decode(body []byte) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = fmt.Errorf("%v", e)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var (
|
||||||
|
d *xml.Decoder
|
||||||
|
start *xml.StartElement
|
||||||
|
)
|
||||||
|
buf := bytes.NewBuffer(body)
|
||||||
|
d = xml.NewDecoder(buf)
|
||||||
|
for {
|
||||||
|
tok, err := d.Token()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
start = &t
|
||||||
|
case xml.CharData:
|
||||||
|
if t = bytes.TrimSpace(t); len(t) > 0 {
|
||||||
|
p.Set(start.Name.Local, string(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML编码
|
||||||
|
func (p Params) Encode() []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(`<xml>`)
|
||||||
|
for k, v := range p {
|
||||||
|
buf.WriteString(`<`)
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteString(`><![CDATA[`)
|
||||||
|
buf.WriteString(v)
|
||||||
|
buf.WriteString(`]]></`)
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteString(`>`)
|
||||||
|
}
|
||||||
|
buf.WriteString(`</xml>`)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
func (p Params) CheckSign(key string) bool {
|
||||||
|
return p.GetString("sign") == p.SignMd5(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成签名
|
||||||
|
func (p Params) getSignStr(key string) string {
|
||||||
|
var keys = make([]string, 0, len(p))
|
||||||
|
for k, _ := range p {
|
||||||
|
if k != "sign" {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, k := range keys {
|
||||||
|
if len(p.GetString(k)) > 0 {
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteString(`=`)
|
||||||
|
buf.WriteString(p.GetString(k))
|
||||||
|
buf.WriteString(`&`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString(`key=`)
|
||||||
|
buf.WriteString(key)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成签名
|
||||||
|
func (p Params) SignMd5(key string) string {
|
||||||
|
sum := md5.Sum([]byte(p.getSignStr(key)))
|
||||||
|
str := hex.EncodeToString(sum[:])
|
||||||
|
return strings.ToUpper(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成签名
|
||||||
|
func (p Params) SignHmacSha256(key string) string {
|
||||||
|
h := hmac.New(sha256.New, []byte(key))
|
||||||
|
io.WriteString(h, p.getSignStr(key))
|
||||||
|
str := hex.EncodeToString(h.Sum(nil))
|
||||||
|
return strings.ToUpper(str)
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package weixin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUserPaying = errors.New("USERPAYING")
|
||||||
|
)
|
||||||
|
|
||||||
|
type WxpayPartnerConfig struct {
|
||||||
|
MchId string
|
||||||
|
AppId string
|
||||||
|
ApiKeyV2 string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WxpayPartner struct {
|
||||||
|
config *WxpayPartnerConfig
|
||||||
|
stdClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type MicroPayRequest struct {
|
||||||
|
SubMchId string
|
||||||
|
SubAppId string
|
||||||
|
GoodsName string
|
||||||
|
OutTradeNo string
|
||||||
|
TotalFee int64
|
||||||
|
AuthCode string
|
||||||
|
ServerIp string
|
||||||
|
Attach string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWxpayPartner(cfg *WxpayPartnerConfig) *WxpayPartner {
|
||||||
|
return &WxpayPartner{
|
||||||
|
config: cfg,
|
||||||
|
stdClient: &http.Client{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WxpayPartner) MicroPay(req *MicroPayRequest) error {
|
||||||
|
params := make(Params)
|
||||||
|
params.Set("appid", p.config.AppId)
|
||||||
|
params.Set("mch_id", p.config.MchId)
|
||||||
|
params.Set("sub_mch_id", req.SubMchId)
|
||||||
|
params.Set("nonce_str", randomStr(32))
|
||||||
|
params.Set("body", req.GoodsName)
|
||||||
|
params.Set("out_trade_no", req.OutTradeNo)
|
||||||
|
params.Set("total_fee", req.TotalFee)
|
||||||
|
params.Set("auth_code", req.AuthCode)
|
||||||
|
|
||||||
|
if req.ServerIp == "" {
|
||||||
|
params.Set("spbill_create_ip", "113.248.223.215")
|
||||||
|
} else {
|
||||||
|
params.Set("spbill_create_ip", req.ServerIp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Attach != "" {
|
||||||
|
params.Set("attach", req.Attach)
|
||||||
|
}
|
||||||
|
if req.SubAppId != "" {
|
||||||
|
params.Set("sub_appid", req.SubAppId)
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Set("sign", params.SignMd5(p.config.ApiKeyV2))
|
||||||
|
reqUrl := "https://api.mch.weixin.qq.com/pay/micropay"
|
||||||
|
body, err := p.post(reqUrl, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result := make(Params)
|
||||||
|
result.Decode(body)
|
||||||
|
|
||||||
|
if result.GetString("return_code") != "SUCCESS" {
|
||||||
|
return errors.New(string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.GetString("result_code") != "SUCCESS" {
|
||||||
|
if result.GetString("err_code") == "USERPAYING" {
|
||||||
|
return ErrUserPaying
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s:%s",
|
||||||
|
result.GetString("err_code"), result.GetString("err_code_des"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
func (c *WxpayPartner) post(url string, params Params) ([]byte, error) {
|
||||||
|
var httpc *http.Client
|
||||||
|
|
||||||
|
httpc = c.stdClient
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(params.Encode())
|
||||||
|
resp, err := httpc.Post(url, "application/xml; charset=utf-8", buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
}
|
Loading…
Reference in New Issue