Files
baseapi/platformapi/wxpay/callback.go
2019-11-20 15:29:23 +08:00

193 lines
6.0 KiB
Go

package wxpay
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"git.rosy.net.cn/baseapi/utils"
"github.com/clbanning/mxj"
)
type CData string
func (c CData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(struct {
string `xml:",cdata"`
}{string(c)}, start)
}
const (
MsgTypeUnkown = 0 // 未知
MsgTypePay = 1 // 支付结果回调
MsgTypeRefund = 2 // 退款结果回调
)
type CallbackResponse struct {
XMLName xml.Name `json:"-" xml:"xml"`
ReturnCode string `json:"return_code" xml:"return_code"`
ReturnMsg CData `json:"return_msg" xml:"return_msg"`
}
type BaseResultMsg struct {
ReturnCode string `json:"return_code" xml:"return_code"`
ReturnMsg string `json:"return_msg" xml:"return_msg"`
}
type PayResultMsg struct {
ReturnCode string `json:"return_code" xml:"return_code"`
ReturnMsg string `json:"return_msg" xml:"return_msg"`
AppID string `json:"appid" xml:"appid"`
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
MchID string `json:"mch_id" xml:"mch_id"`
NonceStr string `json:"nonce_str" xml:"nonce_str"`
Sign string `json:"sign" xml:"sign"`
ResultCode string `json:"result_code" xml:"result_code"`
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
BankType string `json:"bank_type"`
CashFee string `json:"cash_fee"`
FeeType string `json:"fee_type"`
IsSubscribe string `json:"is_subscribe"`
OpenID string `json:"openid"`
OutTradeNo string `json:"out_trade_no"`
TimeEnd string `json:"time_end"`
TotalFee string `json:"total_fee"`
TradeType string `json:"trade_type"`
TransactionID string `json:"transaction_id"`
}
type RefundReqInfo struct {
OutRefundNo string `json:"out_refund_no"`
OutTradeNo string `json:"out_trade_no"`
RefundAccount string `json:"refund_account"`
RefundFee string `json:"refund_fee"`
RefundID string `json:"refund_id"`
RefundRecvAccout string `json:"refund_recv_accout"`
RefundRequestSource string `json:"refund_request_source"`
RefundStatus string `json:"refund_status"`
SettlementRefundFee string `json:"settlement_refund_fee"`
SettlementTotalFee string `json:"settlement_total_fee"`
SuccessTime string `json:"success_time"`
TotalFee string `json:"total_fee"`
TransactionID string `json:"transaction_id"`
}
type RefundResultMsg struct {
ReturnCode string `json:"return_code" xml:"return_code"`
ReturnMsg string `json:"return_msg" xml:"return_msg"`
AppID string `json:"appid" xml:"appid"`
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
MchID string `json:"mch_id" xml:"mch_id"`
NonceStr string `json:"nonce_str" xml:"nonce_str"`
Sign string `json:"sign" xml:"sign"`
ResultCode string `json:"result_code" xml:"result_code"`
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
ReqInfo *RefundReqInfo `json:"req_info,omitempty" xml:"req_info,omitempty"`
}
type CallbackMsg struct {
MsgType int
MapData map[string]interface{}
Data interface{}
}
var (
SuccessResponse = &CallbackResponse{
ReturnCode: ResponseCodeSuccess,
ReturnMsg: "OK",
}
)
func Err2CallbackResponse(err error, data string) *CallbackResponse {
if err == nil {
return SuccessResponse
}
returnMsg := err.Error()
if data != "" {
returnMsg = fmt.Sprintf("%s,%s", returnMsg, data)
}
return &CallbackResponse{
ReturnCode: ResponseCodeFail,
ReturnMsg: CData(returnMsg),
}
}
func (a *API) decodeReqInfo(msg string) (decryptedMsg string, err error) {
binMsg, err := base64.StdEncoding.DecodeString(msg)
if err == nil {
aesKey := []byte(fmt.Sprintf("%x", md5.Sum([]byte(a.appKey))))
binResult, err2 := utils.AESCBCDecpryt(binMsg, aesKey, aesKey[:16])
if err = err2; err == nil {
var msgLen int32
if err = binary.Read(bytes.NewBuffer(binResult[16:]), binary.BigEndian, &msgLen); err == nil {
decryptedMsg = string(binResult[16+4 : 16+4+msgLen])
}
}
}
return decryptedMsg, err
}
func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
data, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil, Err2CallbackResponse(err, "")
}
mapData, _, err := a.checkResultAsMap(string(data))
if err != nil {
return nil, Err2CallbackResponse(err, "")
}
sign := utils.Interface2String(mapData[sigKey])
desiredSign := a.signParam(mapData)
if desiredSign != sign {
return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "")
}
msg = &CallbackMsg{
MapData: mapData,
}
returnCode := utils.Interface2String(mapData["return_code"])
if returnCode != ResponseCodeSuccess {
msg.Data = &BaseResultMsg{
ReturnCode: returnCode,
ReturnMsg: utils.Interface2String(mapData["return_msg"]),
}
} else {
if transactionID := utils.Interface2String(mapData["transaction_id"]); transactionID != "" {
msg.MsgType = MsgTypePay
var payResult *PayResultMsg
if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil {
msg.Data = payResult
}
} else if reqInfo := utils.Interface2String(mapData["req_info"]); reqInfo != "" {
msg.MsgType = MsgTypeRefund
var refundResult *RefundResultMsg
if err = utils.Map2StructByJson(mapData, &refundResult, false); err == nil {
if reqInfo, err = a.decodeReqInfo(reqInfo); err == nil {
mv, err2 := mxj.NewMapXml([]byte(reqInfo))
if err = err2; err == nil {
reqInfoMap := mv["root"].(map[string]interface{})
if err = utils.Map2StructByJson(reqInfoMap, &refundResult.ReqInfo, false); err == nil {
msg.Data = refundResult
}
}
}
}
}
}
if err != nil {
callbackResponse = Err2CallbackResponse(err, "")
}
return msg, callbackResponse
}