上文介绍了如何用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))
}