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
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, ¬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
|
|
}
|
|
}
|
|
}
|
|
}
|