package wxpay import ( "encoding/xml" "errors" "github.com/gin-gonic/gin" "github.com/golangkit/formatime" "io/ioutil" "log" "net/http" "recook/internal/api/mobile/pay/public" "recook/internal/dbc" "recook/internal/model/order" "recook/internal/back" "recook/tools" "sort" "strconv" "strings" "time" ) // ==================================================================================================== type PayCallbackNotification struct { AppID string `xml:"appid" json:"appid"` BankType string `xml:"bank_type" json:"bank_type"` CashFee uint `xml:"cash_fee" json:"cash_fee"` FeeType string `xml:"fee_type" json:"fee_type"` IsSubscribe string `xml:"is_subscribe" json:"is_subscribe"` MchID string `xml:"mch_id" json:"mch_id"` NonceStr string `xml:"nonce_str" json:"nonce_str"` OpenID string `xml:"openid" json:"openid"` OutTradeNo string `xml:"out_trade_no" json:"out_trade_no"` ResultCode string `xml:"result_code" json:"result_code"` ReturnCode string `xml:"return_code" json:"return_code"` Sign string `xml:"sign" json:"sign"` TradeType string `xml:"trade_type" json:"trade_type"` TotalFee uint `xml:"total_fee" json:"total_fee"` TransactionID string `xml:"transaction_id" json:"transaction_id"` Attach string `xml:"attach" json:"attach"` TimeEnd string `xml:"time_end" json:"time_end"` // 付款完成时间 20191030133525 } func (r *PayCallbackNotification) IsOK() bool { return r.ReturnCode == "SUCCESS" } /* 当获取到微信通知后 根据参数再向微信查询一次 */ func paySuccessMore(orderID int, result PayCallbackNotification) error { var od []order.Information if err := dbc.DB.Find(&od, "virtual_id = ?", orderID).Error; err != nil { jsonStr, _ := json.Marshal(&result) log.Println(jsonStr) log.Println(err.Error()) return err } ignore := false for _, v := range od { if v.Status == 1 { ignore = true } } if ignore { return nil } tx := dbc.DB.Begin() { for _, v := range od { completeTime, _ := time.ParseInLocation("20060102150405", result.TimeEnd, time.Local) if err := public.PaySuccessCallback(tx, v, formatime.NewSecondFrom(completeTime)); err != nil { tx.Rollback() return errors.New(err.Error()) } } } tx.Commit() return nil } func PayCallback(c *gin.Context) { FAIL := gin.H{ "return_code": "FAIL", "return_msg": "OK", } SUCCESS := gin.H{ "return_code": "SUCCESS", "return_msg": "OK", } body, err := ioutil.ReadAll(c.Request.Body) if err != nil { panic(err) } if verifySign(body) == false { c.XML(http.StatusOK, FAIL) return } var notification PayCallbackNotification err = xml.Unmarshal(body, ¬ification) if err != nil { c.XML(http.StatusOK, FAIL) return } if (¬ification).IsOK() == false { c.XML(http.StatusOK, FAIL) return } rightPart := strings.Split(notification.OutTradeNo, "M1")[1] orderId, err := strconv.Atoi(rightPart) if err != nil || notification.IsOK() == false { jsonStr, _ := json.Marshal(¬ification) log.Println(jsonStr) c.XML(http.StatusOK, SUCCESS) return } if !public.JudgeString(rightPart) { if err = paySuccessMore(orderId, notification); err != nil { c.XML(http.StatusOK, FAIL) return } c.Set("status", "SyncOrder") c.Set("id", orderId) c.XML(http.StatusOK, SUCCESS) return } var orderInfo order.Information err = dbc.DB.First(&orderInfo, "id = ?", orderId).Error { if err != nil { jsonStr, _ := json.Marshal(¬ification) log.Println(jsonStr) c.XML(http.StatusOK, FAIL) return } if orderInfo.Status == 1 { // 重复的通知 不用处理 c.XML(http.StatusOK, SUCCESS) return } } tx := dbc.DB.Begin() { completeTime, _ := time.ParseInLocation("20060102150405", notification.TimeEnd, time.Local) if err = public.PaySuccessCallback(tx, orderInfo, formatime.NewSecondFrom(completeTime)); err != nil { back.Err(c, err.Error()) tx.Rollback() return } } tx.Commit() c.Set("status", "SyncOrder") c.Set("id", orderInfo.ID) c.XML(http.StatusOK, SUCCESS) return } func verifySign(s []byte) bool { var param notificationMap err := xml.Unmarshal(s, ¶m) if err != nil { panic(err) } wxSign := param.m["sign"] delete(param.m, "sign") if len(wxSign) == 0 { return false } var keyList []string for key, val := range param.m { if len(val) > 0 { keyList = append(keyList, key) } } sort.Strings(keyList) var valList []string for _, key := range keyList { valList = append(valList, key+"="+param.m[key]) } valList = append(valList, "key="+APIKey) sign := strings.ToUpper(tools.MD5(strings.Join(valList, "&"))) return sign == wxSign } type notificationMap struct { m map[string]string } func (c *notificationMap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { c.m = map[string]string{} for { t, _ := d.Token() switch elem := t.(type) { case xml.StartElement: var item string if err := d.DecodeElement(&item, &elem); err != nil { return err } c.m[elem.Name.Local] = item case xml.EndElement: if elem == start.End() { return nil } } } }