go 开发微信H5支付接口【统一下单】

最近的做了个开发微信H5支付的需求,H5支付和app支付的流程基本一样,差别主要有下面两点:

  1. H5支付的微信请求参数多了场景信息(scene_info)参数;
  2. H5支付的返回结果多了支付跳转链接(mweb_url),mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。

Talk is cheap. Show me the code

创建订单方法
package product

import (
	"bytes"
	"crypto/md5"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/golang/glog"
)

// 创建H5微信订单

//CreateH5WXOrderParam	请求参数
type CreateH5WXOrderParam struct {
	UserID string `json:"userid"`   //用户名
}
//CreateH5WXOrderReply	返回结果
type CreateH5WXOrderReply struct {
	MwebURL string `json:"mweb_url"` //支付跳转链接
}

//WXOrderParam	微信请求参数
type WXOrderParam struct {
	APPID          string `xml:"appid"`            //公众账号ID
	MchID          string `xml:"mch_id"`           //商户号
	NonceStr       string `xml:"nonce_str"`        //随机字符串
	Sign           string `xml:"sign"`             //签名
	Body           string `xml:"body"`             //商品描述
	OutTradeNo     string `xml:"out_trade_no"`     //商户订单号
	TotalFee       string `xml:"total_fee"`        //总金额
	SpbillCreateIP string `xml:"spbill_create_ip"` //终端IP
	NotifyUrl      string `xml:"notify_url"`       //通知地址
	TradeType      string `xml:"trade_type"`       //交易类型
	SceneInfo      string `xml:"scene_info"`       //场景信息
}

//WXOrderReply	微信请求返回结果
type WXOrderReply struct {
	ReturnCode string `xml:"return_code"` //返回状态码
	ReturnMsg  string `xml:"return_msg"`  //返回信息

	APPID      string `xml:"appid"`        //公众账号ID
	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"` //错误代码描述

	TradeType string `xml:"trade_type"` //交易类型
	PrepayID  string `xml:"prepay_id"`  //预支付交易会话标识
	MwebURL   string `xml:"mweb_url"`   //支付跳转链接
}

const (
	WXPackage   = "Sign=WXPay"
	WXAppID     = "" 					//公众账号ID
	WXMchID     = ""   					//商户号
	WXPApiKEY   = ""  					//商户key
	WXOrderURL  = "https://api.mch.weixin.qq.com/pay/unifiedorder"
	WXTradeType = "MWEB"
	WXNotifyURL = "" 					//支付回调链接
)
/*
*	CreateH5WXOrder 创建微信H5订单
*	使用rpc框架 
*/
func (p *Product) CreateH5WXOrder(r *http.Request, param *CreateH5WXOrderParam, reply *CreateH5WXOrderReply) error {
	
	var data WXOrderParam 
	
	userid, _ := strconv.ParseInt(param.UserID, 10, 64)
	
	data.OutTradeNo = "W" + orderIDGenerator(userid)	//生成订单

	data.TotalFee = "1"
	data.Body = "测试购买"

	data.APPID = WXAppID
	data.MchID = WXMchID
	data.NonceStr = getRandStr(32)	//获取32随机数
	data.SpbillCreateIP = getIpAddress(r)	//获取IP地址
	data.NotifyUrl = WXNotifyURL
	data.TradeType = WXTradeType
	data.SceneInfo = `{"h5_info": {"type":"Wap","wap_url": "WAP网站URL地址","wap_name": "WAP网站名"}`

	data.Sign = WXmd5Sign(data)	//生成md5签名

	prepayid, mweb_url := submitWXOrder(data)	//提交微信
	if "" == prepayid {
		err := errors.New(`获取prepayid失败`)
		glog.Error(err)
		return err
	}

	//返回支付跳转链接
	reply.MwebURL  = mweb_url 
	
	return nil
}
生成订单id方法
/*
*	orderIDGenerator	生成订单id
*	param	userid	用户名
*	reply	生成的订单
*/
func orderIDGenerator(userid int64) string {
	str1 := fmt.Sprintf("%d", (userid>>4)%10000)
	tag := 'A' + userid%10
	str1 = string(tag) + str1
	now := time.Now()
	t := now.Year()%100*10000000000 + int(now.Month())*100000000 + now.Day()*1000000 + now.Hour()*10000 + now.Minute()*100 + now.Second()
	rnd := mathrand.New(mathrand.NewSource(int64(t)))
	t = t*1000 + rnd.Intn(10000)
	str2 := strconv.Itoa(t)
	return str1 + str2
}

获取随机字符串方法
/*
*	getRandStr 获取随机字符串
*	param	n	位数
*	reply	随机字符串
*/
func getRandStr(n int) string {
	leterset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	var leteridxbits uint = 6
	var mask int64 = 1<<leteridxbits - 1

	rnd := rand.NewSource(time.Now().UnixNano())
	res := make([]byte, 0, n)

	for i, bits := 0, rnd.Int63(); i < n; i++ {
		if bits == 0 {
			bits = rnd.Int63()
		}
		idx := int(bits & mask)
		if idx < len(leterset) {
			res = append(res, leterset[idx])
		} else {
			i--
		}
		bits >>= leteridxbits
	}
	return string(res[:n])
}
获取IP地址方法
/*
*	getIpAddress 获取IP地址
*	param	r	 *http.Request
*	reply	IP地址
*/
func getIpAddress(r *http.Request) string {
	realip := r.Header.Get("X-Real-Ip")
	if realip != "" {
		return realip
	}
	remoteAddr := r.RemoteAddr
	if remoteAddr != "" {
		idx := strings.Index(remoteAddr, ":")
		return remoteAddr[:idx]
	}
	ips := r.Header.Get("X-Forwarded-For")
	glog.Info("X-Forwarded-For", ips)
	if ips != "" {
		iplist := strings.Split(ips, ",")
		return strings.TrimSpace(iplist[0])
	}
	return ""
}

微信md5签名方法
/*
*	WXmd5Sign 微信 md5 签名
*	param  data		interface{}
*	reply	sign	生成的签名
*/
func WXmd5Sign(data interface{})(sign string) {
	val := make(map[string]string)
	datavalue := reflect.ValueOf(data)
	glog.Info("datavalue:", datavalue)
	if datavalue.Kind() != reflect.Struct {
		glog.Error("NOT A STRUCT")
		return ""
	}

	var keys []string
	for i := 0; i < datavalue.NumField(); i++ {
		k := datavalue.Type().Field(i)
		kl := k.Tag.Get("xml")
		v := fmt.Sprintf("%v", datavalue.Field(i))
		if v != "" && v != "0" && kl != "sign" {
			glog.Info(kl, v)
			val[kl] = v
			keys = append(keys, kl)
		}
	}
	sort.Strings(keys)
	var stra string
	for _, v := range keys {
		stra = stra + v + "=" + val[v] + "&"
	}

	strb := stra + "key=" + WXPApiKEY
	glog.Info("signstr:", strb)
	hstr := md5.Sum([]byte(strb))
	sum := fmt.Sprintf("%x", hstr)
	sign = strings.ToUpper(sum)
	
	return sign
}
提交微信订单方法
/*
*	submitWXOrder	提交微信订单
*	param	data	WXOrderParam
*	reply	prepay_id	预支付交易会话标识
*	reply	mweb_url	支付跳转链接
*/
func submitWXOrder(data WXOrderParam) (prepay_id string, mweb_url string) {

	xdata, err := xml.Marshal(data)
	if err != nil {
		glog.Error(err, xdata)
	}
	glog.Info(string(xdata))
	xmldata := strings.Replace(string(xdata), "WXOrderParam", "xml", -1)
	glog.Info("request : ", xmldata)

	body := bytes.NewBufferString(xmldata)
	resp, err := http.Post(WXOrderURL, "content-type:text/xml; charset=utf-8", body)
	if err != nil {
		glog.Error(err)
	}
	defer resp.Body.Close()

	result, err := ioutil.ReadAll(resp.Body)
	var reply WXOrderReply
	err = xml.Unmarshal(result, &reply)
	if err != nil {
		glog.Error(err)
		return "", ""
	}
	glog.Info(reply)

	if reply.ReturnCode == "SUCCESS" && reply.ResultCode == "SUCCESS" {
		return reply.PrepayID, reply.MwebURL
	}
	return "", ""
}

补:go开发微信支付回调接口

参考:微信H5【统一下单】文档

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值