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