yaotian пре 8 година
родитељ
комит
ce1d0fe26f
2 измењених фајлова са 364 додато и 0 уклоњено
  1. 180 0
      mch/pay/simple.go
  2. 184 0
      util/string.go

+ 180 - 0
mch/pay/simple.go

@@ -0,0 +1,180 @@
+package pay
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"hash"
+	"sort"
+	"time"
+	"unicode/utf8"
+
+	"github.com/yaotian/gowechat/util"
+)
+
+//Order 下单
+//https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
+type Order struct {
+	OpenID      string //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
+	Body        string //String(128)
+	OutTradeNum string //String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
+	TotalFee    int    //分为单位
+	IP          string
+	NotifyURL   string //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
+	TradeType   string //JSAPI,NATIVE,APP
+	ProductID   string //trade_type=NATIVE时(即扫码支付),此参数必传
+}
+
+//GetPrepayMap 支付页面需要的信息,map格式
+func (c *Pay) GetPrepayMap(order Order) (result map[string]string, err error) {
+	err = c.checkOrder(order)
+	if err != nil {
+		return
+	}
+	var prepayID string
+	prepayID, err = c.getPrepayID(order)
+	if err != nil {
+		return
+	}
+
+	nocestr := util.RandomStr(8)
+	timestamp := fmt.Sprint(time.Now().Unix())
+
+	result = make(map[string]string)
+	result["appId"] = c.AppID
+	result["timeStamp"] = timestamp
+	result["nonceStr"] = nocestr
+	result["package"] = "prepay_id=" + prepayID
+	result["signType"] = "MD5"
+
+	sign := sign(result, c.MchAPIKey, nil)
+	result["paySign"] = sign
+	return
+}
+
+// 调用 UnifiedOrder 获得 prepayID
+func (c *Pay) getPrepayID(order Order) (prepayID string, err error) {
+	input := c.createUnifiedOrderMap(order)
+	var result map[string]string
+	if result, err = c.UnifiedOrder(input); err == nil { //有prepay_id
+		prepayID := result["prepay_id"]
+		if prepayID != "" {
+			return prepayID, nil
+		}
+		err = fmt.Errorf("prepayID is empty")
+	}
+	return
+}
+
+func (c *Pay) createUnifiedOrderMap(order Order) (input map[string]string) {
+	input = make(map[string]string)
+	input["appid"] = c.AppID               //设置微信分配的公众账号ID
+	input["mch_id"] = c.MchID              //设置微信支付分配的商户号
+	input["nonce_str"] = util.RandomStr(5) //设置随机字符串,不长于32位。推荐随机数生成算法
+	input["body"] = order.Body             //获取商品或支付单简要描述的值
+
+	input["out_trade_no"] = order.OutTradeNum       //设置商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
+	input["total_fee"] = util.ToStr(order.TotalFee) //设置订单总金额,只能为整数,详见支付金额
+	input["spbill_create_ip"] = order.IP            //设置APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
+	input["notify_url"] = order.NotifyURL           //设置接收微信支付异步通知回调地址
+
+	input["trade_type"] = order.TradeType
+	//设置取值如下:JSAPI,NATIVE,APP,详细说明见参数规定
+
+	if order.ProductID != "" {
+		input["product_id"] = order.ProductID //这个
+	}
+
+	input["openid"] = order.OpenID //设置trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid
+
+	//sign
+	sign := sign(input, c.MchAPIKey, nil)
+	input["sign"] = sign
+	return
+}
+
+// 微信支付签名.
+//  parameters: 待签名的参数集合
+//  apiKey:     API密钥
+//  fn:         func() hash.Hash, 如果 fn == nil 则默认用 md5.New
+func sign(parameters map[string]string, apiKey string, fn func() hash.Hash) string {
+	ks := make([]string, 0, len(parameters))
+	for k := range parameters {
+		if k == "sign" {
+			continue
+		}
+		ks = append(ks, k)
+	}
+	sort.Strings(ks)
+
+	if fn == nil {
+		fn = md5.New
+	}
+	h := fn()
+
+	buf := make([]byte, 256)
+	for _, k := range ks {
+		v := parameters[k]
+		if v == "" {
+			continue
+		}
+
+		buf = buf[:0]
+		buf = append(buf, k...)
+		buf = append(buf, '=')
+		buf = append(buf, v...)
+		buf = append(buf, '&')
+		h.Write(buf)
+	}
+	buf = buf[:0]
+	buf = append(buf, "key="...)
+	buf = append(buf, apiKey...)
+	h.Write(buf)
+
+	signature := make([]byte, h.Size()*2)
+	hex.Encode(signature, h.Sum(nil))
+	return string(bytes.ToUpper(signature))
+}
+
+func (c *Pay) checkOrder(order Order) (err error) {
+	tradeType := order.TradeType
+	if tradeType != "JSAPI" && tradeType != "APP" && tradeType != "NATIVE" {
+		return fmt.Errorf("tradeType is invalid")
+	}
+	if tradeType == "NATIVE" {
+		if order.ProductID == "" {
+			err = fmt.Errorf("Native TradeType need ProductID")
+			return
+		}
+	}
+	if tradeType == "JSAPI" {
+		if order.OpenID == "" {
+			err = fmt.Errorf("OpenID can not be empty when pay mode is JSAPI")
+			return
+		}
+	}
+	if utf8.RuneCountInString(order.Body) > 128 || order.Body == "" {
+		err = fmt.Errorf("Body is invalid. Size can not exceed 128.")
+		return
+	}
+	if utf8.RuneCountInString(order.OutTradeNum) > 32 || order.OutTradeNum == "" {
+		err = fmt.Errorf("OutTradeNum is invalid. Size can not exceed 128.")
+		return
+	}
+	if order.TotalFee <= 0 {
+		err = fmt.Errorf("Order TotalFee is invalid.")
+		return
+	}
+	if order.IP == "" {
+		err = fmt.Errorf("Order IP is invalid.")
+		return
+	}
+	if order.NotifyURL == "" {
+		err = fmt.Errorf("Notify URL is invalid.")
+		return
+	}
+
+	return
+
+}

+ 184 - 0
util/string.go

@@ -1,7 +1,10 @@
 package util
 
 import (
+	"fmt"
 	"math/rand"
+	"reflect"
+	"strconv"
 	"time"
 )
 
@@ -16,3 +19,184 @@ func RandomStr(length int) string {
 	}
 	return string(result)
 }
+
+// convert string to specify type
+
+type StrTo string
+
+func (f *StrTo) Set(v string) {
+	if v != "" {
+		*f = StrTo(v)
+	} else {
+		f.Clear()
+	}
+}
+
+func (f *StrTo) Clear() {
+	*f = StrTo(0x1E)
+}
+
+func (f StrTo) Exist() bool {
+	return string(f) != string(0x1E)
+}
+
+func (f StrTo) Bool() (bool, error) {
+	if f == "on" {
+		return true, nil
+	}
+	return strconv.ParseBool(f.String())
+}
+
+func (f StrTo) Float32() (float32, error) {
+	v, err := strconv.ParseFloat(f.String(), 32)
+	return float32(v), err
+}
+
+func (f StrTo) Float64() (float64, error) {
+	return strconv.ParseFloat(f.String(), 64)
+}
+
+func (f StrTo) Int() (int, error) {
+	v, err := strconv.ParseInt(f.String(), 10, 32)
+	return int(v), err
+}
+
+func (f StrTo) Int8() (int8, error) {
+	v, err := strconv.ParseInt(f.String(), 10, 8)
+	return int8(v), err
+}
+
+func (f StrTo) Int16() (int16, error) {
+	v, err := strconv.ParseInt(f.String(), 10, 16)
+	return int16(v), err
+}
+
+func (f StrTo) Int32() (int32, error) {
+	v, err := strconv.ParseInt(f.String(), 10, 32)
+	return int32(v), err
+}
+
+func (f StrTo) Int64() (int64, error) {
+	v, err := strconv.ParseInt(f.String(), 10, 64)
+	return int64(v), err
+}
+
+func (f StrTo) Uint() (uint, error) {
+	v, err := strconv.ParseUint(f.String(), 10, 32)
+	return uint(v), err
+}
+
+func (f StrTo) Uint8() (uint8, error) {
+	v, err := strconv.ParseUint(f.String(), 10, 8)
+	return uint8(v), err
+}
+
+func (f StrTo) Uint16() (uint16, error) {
+	v, err := strconv.ParseUint(f.String(), 10, 16)
+	return uint16(v), err
+}
+
+func (f StrTo) Uint32() (uint32, error) {
+	v, err := strconv.ParseUint(f.String(), 10, 32)
+	return uint32(v), err
+}
+
+func (f StrTo) Uint64() (uint64, error) {
+	v, err := strconv.ParseUint(f.String(), 10, 64)
+	return uint64(v), err
+}
+
+func (f StrTo) String() string {
+	if f.Exist() {
+		return string(f)
+	}
+	return ""
+}
+
+// convert any type to string
+func ToStr(value interface{}, args ...int) (s string) {
+	switch v := value.(type) {
+	case bool:
+		s = strconv.FormatBool(v)
+	case float32:
+		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
+	case float64:
+		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
+	case int:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int8:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int16:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int32:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int64:
+		s = strconv.FormatInt(v, argInt(args).Get(0, 10))
+	case uint:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint8:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint16:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint32:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint64:
+		s = strconv.FormatUint(v, argInt(args).Get(0, 10))
+	case string:
+		s = v
+	case []byte:
+		s = string(v)
+	default:
+		s = fmt.Sprintf("%v", v)
+	}
+	return s
+}
+
+// convert any numeric value to int64
+func ToInt64(value interface{}) (d int64, err error) {
+	val := reflect.ValueOf(value)
+	switch value.(type) {
+	case int, int8, int16, int32, int64:
+		d = val.Int()
+	case uint, uint8, uint16, uint32, uint64:
+		d = int64(val.Uint())
+	default:
+		err = fmt.Errorf("ToInt64 need numeric not `%T`", value)
+	}
+	return
+}
+
+type argString []string
+
+func (a argString) Get(i int, args ...string) (r string) {
+	if i >= 0 && i < len(a) {
+		r = a[i]
+	} else if len(args) > 0 {
+		r = args[0]
+	}
+	return
+}
+
+type argInt []int
+
+func (a argInt) Get(i int, args ...int) (r int) {
+	if i >= 0 && i < len(a) {
+		r = a[i]
+	}
+	if len(args) > 0 {
+		r = args[0]
+	}
+	return
+}
+
+type argAny []interface{}
+
+func (a argAny) Get(i int, args ...interface{}) (r interface{}) {
+	if i >= 0 && i < len(a) {
+		r = a[i]
+	}
+	if len(args) > 0 {
+		r = args[0]
+	}
+	return
+}