上文介绍了如何用go开发微信H5支付下单的接口,支付成功后,微信会请求notify_url指向的地址,通知支付结果。

回调地址链接是通过【统一下单API】中提交的参数notify_url设置,
如果链接无法访问,商户将无法接收到微信通知。
通知url必须为直接可访问的url,不能携带参数。
示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”

本文主要介绍如何开发微信支付回调的接口

Talk is cheap. Show me the code

微信回调入口方法
package wxpay

import (
	"crypto/md5"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"sort"
	"strings"

	"github.com/golang/glog"
)

//微信支付回调

const (
	AckSuccess = `<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>`
	AckFail    = `<xml><return_code><![CDATA[FAIL]]></return_code></xml>`
)

//微信 商户Key
var WXPApiKey string = ""

type WXPayNotify 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"`
	TotalFee      int64  `xml:"total_fee"`
	FeeType       string `xml:"fee_type"`
	CashFee       int64  `xml:"cash_fee"`
	CashFeeType   string `xml:"cash_fee_type"`
	CouponFee     int64  `xml:"coupon_fee"`
	CouponCount   int64  `xml:"coupon_count"`
	CouponID0     string `xml:"coupon_id_0"`
	CouponFee0    int64  `xml:"coupon_fee_0"`
	TransactionID string `xml:"transaction_id"`
	OutTradeNo    string `xml:"out_trade_no"`
	Attach        string `xml:"attach"`
	TimeEnd       string `xml:"time_end"`
}

/*
* 微信回调入口
* param w http.ResponseWriter
* param req *http.Request
 */
func HandleWXNotify(w http.ResponseWriter, req *http.Request) {
	log.Println("调用支付")
	glog.V(8).Info(req)

	if req.Method != "POST" {
		fmt.Fprint(w, AckFail)
		return
	}

	bodydata, err := ioutil.ReadAll(req.Body)
	if err != nil {
		glog.Error(err)
		fmt.Fprint(w, AckFail)
		return
	}

	glog.Info(string(bodydata))
	var wxn WXPayNotify
	err = xml.Unmarshal(bodydata, &wxn)
	if err != nil {
		glog.Error(err)
		fmt.Fprint(w, AckFail)
		return
	}
	if ProcessWX(wxn) {
		glog.Info("PROCESSWX SUCCESS")
		fmt.Fprint(w, AckSuccess)
		return
	}

	fmt.Fprint(w, AckFail)
}
处理微信订单
/*
* 处理微信订单
* param wxn WXPayNotify
* reply true/false
 */
func ProcessWX(wxn WXPayNotify) bool {

	if !WXPayVerify(wxn) {
		glog.Warning("SIGN FAILED")
		return false
	}

	if !(wxn.ReturnCode == "SUCCESS" && wxn.ResultCode == "SUCCESS") {
		glog.Warning("INVALID STATUS", wxn)
		return false
	}

	/*
		业务逻辑 start

		.
		.
		.

		业务逻辑 end
	*/
	return true
}
支付信息验证
/*
* 支付信息验证
* param data WXPayNotify
* reply true/false
 */
func WXPayVerify(data WXPayNotify) bool {
	glog.Info(data)
	sign := WXmd5Sign(data)
	if data.Sign == sign {
		return true
	} else {
		glog.V(8).Info(data.Sign, "  !=  ", sign)
		glog.Warning("WEIXIN PAY VERIFY FAIL")
	}
	return false
}
md5签名
/*
* md5签名
* param data interface{}
* reply sign 签名字符串
 */
func WXmd5Sign(data interface{}) (sign string) {
	val := make(map[string]string)
	datavalue := reflect.ValueOf(data)
	if datavalue.Kind() != reflect.Struct {
		glog.Warning("NOT A STRUCT ", data)
		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" {
			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.V(8).Info("SIGN STRING ", strb)
	hstr := md5.Sum([]byte(strb))

	sum := fmt.Sprintf("%x", hstr)
	sign = strings.ToUpper(sum)
	return sign
}
main方法
package main

import (
	"flag"
	"log"
	"net/http"
	"test/wxpay"

	"github.com/golang/glog"
)

func main() {
	flag.Parse()
	defer glog.Flush()

	mux := http.NewServeMux()
	mux.HandleFunc("/", wxpay.HandleWXNotify)

	log.Println("server is listening at", ":12138")
	log.Fatalln(http.ListenAndServe(":12138", mux))
}