Files
baseapi/platformapi/wxpayapi/callback.go
邹宗楠 324e3671df 1
2022-10-17 14:06:04 +08:00

211 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wxpayapi
import (
"encoding/base64"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"strings"
"git.rosy.net.cn/baseapi/utils"
"github.com/clbanning/mxj"
"github.com/nanjishidu/gomini/gocrypto"
)
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"`
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"`
ResultMsg string `json:"result_msg" xml:"result_msg"`
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
}
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"`
ResultMsg string `json:"result_msg" xml:"result_msg"`
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" xml:"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"`
ResultMsg string `json:"result_msg" xml:"result_msg"`
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
ReqInfoStr string `json:"req_info,omitempty"`
ReqInfoObj *RefundReqInfo `json:"req_info_obj,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])
gocrypto.SetAesKey(strings.ToLower(gocrypto.Md5(a.appKey)))
binResult, err2 := gocrypto.AesECBDecrypt(binMsg)
if err = err2; err == nil {
decryptedMsg = string(binResult)
}
}
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, "")
}
return a.getCallbackMsg(string(data))
}
func (a *API) getCallbackMsg(msgBody string) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
mapData, _, err := a.parseXmlStrAsMap(msgBody)
if err != nil {
return nil, Err2CallbackResponse(err, "")
}
returnCode := utils.Interface2String(mapData["return_code"])
if returnCode != ResponseCodeSuccess { // 如果return_code出错直接返回
return nil, SuccessResponse
}
reqInfo := utils.Interface2String(mapData["req_info"])
transactionID := utils.Interface2String(mapData["transaction_id"])
if reqInfo == "" && transactionID != "" { // 对于支付结果通知进行签名验证(退款结果通知不支持验证)
sigType := utils.Interface2String(mapData[sigTypeKey])
sign := utils.Interface2String(mapData[sigKey])
desiredSign := a.signParam(sigType, mapData)
if desiredSign != sign {
return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "")
}
}
msg = &CallbackMsg{
MapData: mapData,
}
if 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.ReqInfoObj, false); err == nil {
msg.Data = refundResult
}
}
}
}
} else if transactionID != "" {
msg.MsgType = MsgTypePay
var payResult *PayResultMsg
if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil {
msg.Data = payResult
}
}
if err != nil {
callbackResponse = Err2CallbackResponse(err, "")
}
return msg, callbackResponse
}