You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
5.1 KiB

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, &notification)
if err != nil {
c.XML(http.StatusOK, FAIL)
return
}
if (&notification).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(&notification)
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(&notification)
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, &param)
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
}
}
}
}