k.zhang 1 rok pred
rodič
commit
39dfc20e99

+ 42 - 0
apis/common/random.go

@@ -0,0 +1,42 @@
+package common
+
+import (
+	"math/rand"
+	"time"
+)
+
+// 随机生成字符串
+func GetRandomString(l int) string {
+	str := "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
+	bytes := []byte(str)
+	var result []byte
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < l; i++ {
+		result = append(result, bytes[r.Intn(len(bytes))])
+	}
+	return string(result)
+}
+
+// 随机生成纯字符串
+func GetRandomPureString(l int) string {
+	str := "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
+	bytes := []byte(str)
+	var result []byte
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < l; i++ {
+		result = append(result, bytes[r.Intn(len(bytes))])
+	}
+	return string(result)
+}
+
+// 随机生成数字字符串
+func GetRandomNumber(l int) string {
+	str := "0123456789"
+	bytes := []byte(str)
+	var result []byte
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < l; i++ {
+		result = append(result, bytes[r.Intn(len(bytes))])
+	}
+	return string(result)
+}

+ 2 - 0
apis/shanghu/base.go

@@ -24,5 +24,7 @@ func InitShangHuRouter(engine *gin.RouterGroup) {
 		v1.POST("/client/card/list", ClientCardList)                        //客户端卡列表
 		v1.POST("/client/card/info", GetClientCard)                         //获取客户卡信息
 		v1.POST("/client/qr/get", GetClientCardQR)                          //获取二维码
+		v1.POST("/client/unified/order", UnifiedOrder)                      //生成订单
+		v1.POST("/client/pay/callback", PayCallBack)                        //支付回调
 	}
 }

+ 12 - 0
apis/shanghu/models/pay.go

@@ -0,0 +1,12 @@
+package models
+
+import "github.com/shopspring/decimal"
+
+type UnifiedOrderRequest struct {
+	RequestId      string          `json:"request_id"`       //request id
+	OutTradeNo     string          `json:"out_trade_no"`     //交易id
+	MerchantCardId int64           `json:"merchant_card_id"` //商户卡id
+	ClientOpenId   string          `json:"client_open_id"`   //openid
+	Amount         decimal.Decimal `json:"amount"`           // 交易金额
+
+}

+ 201 - 0
apis/shanghu/pay.go

@@ -0,0 +1,201 @@
+package shanghu
+
+import (
+	"duoduo/apis/common"
+	"duoduo/apis/shanghu/models"
+	"duoduo/models/shanghu"
+	"duoduo/tools"
+	"duoduo/tools/app"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/go-pay/gopay"
+	"github.com/go-pay/gopay/wechat"
+	"github.com/shopspring/decimal"
+	"net/http"
+	"strconv"
+	"time"
+)
+
+type wechatCallbackResp struct {
+	XMLName    xml.Name `xml:"xml"`
+	ReturnCode Cdata    `xml:"return_code"`
+	ReturnMsg  Cdata    `xml:"return_msg"`
+}
+
+type Cdata struct {
+	Value string `xml:",cdata"`
+}
+
+var (
+	successResp = &wechatCallbackResp{ReturnCode: Cdata{Value: "SUCCESS"}, ReturnMsg: Cdata{Value: "OK"}}
+	failResp    = &wechatCallbackResp{ReturnCode: Cdata{Value: "FAIL"}, ReturnMsg: Cdata{Value: "数据处理异常"}}
+)
+
+func UnifiedOrder(c *gin.Context) {
+	var inData models.UnifiedOrderRequest
+	var sqlData shanghu.ClientPayTrans
+
+	err := c.ShouldBindJSON(&inData)
+	if err != nil {
+		app.Error(c, 400, err, err.Error())
+		return
+	}
+	//校验 防止同一笔记录存在
+	sqlData.RequestID = inData.RequestId
+	if sqlData.GetRequestNum() > 0 {
+		app.Error(c, 400, errors.New("交易已存在"), "交易已存在")
+		return
+	}
+
+	//校验金额
+	// 检查微信相关参数
+	if !inData.Amount.GreaterThan(decimal.NewFromInt(0)) {
+		app.Error(c, 400, errors.New("amount:金额必须大于0"), "amount:金额必须大于0")
+		return
+
+	}
+	if inData.Amount.Round(2).String() != inData.Amount.String() {
+		app.Error(c, 400, errors.New("total_fee:金额最多只能保留两位小数"), "total_fee:金额最多只能保留两位小数")
+		return
+	}
+
+	//创建支付记录
+	sqlData.ClientOpenID = inData.ClientOpenId
+	sqlData.RequestID = inData.RequestId
+	sqlData.CreatedAt = time.Now()
+	sqlData.UpdatedAt = time.Now()
+	sqlData.Amount = inData.Amount
+	sqlData.OutTradeNo = strconv.FormatInt(inData.MerchantCardId, 10) + strconv.FormatInt(time.Now().UnixNano(), 10)
+	sqlData.Status = 1 //未支付
+	sqlData.MerchantCardID = inData.MerchantCardId
+	_, err = sqlData.Create()
+	if err != nil {
+		app.Error(c, 400, err, "创建支付失败")
+		return
+	}
+	fmt.Println(sqlData.OutTradeNo)
+	bm := make(gopay.BodyMap)
+	bm.Set("nonce_str", common.GetRandomString(32))
+	bm.Set("body", "商户卡")
+	bm.Set("out_trade_no", sqlData.OutTradeNo)
+	bm.Set("total_fee", inData.Amount.Mul(decimal.NewFromInt(100)).IntPart())
+	bm.Set("spbill_create_ip", "127.0.0.1")
+	bm.Set("notify_url", "https://shisanmiao.com/v1/client/pay/callback")
+	bm.Set("device_info", "WEB")
+	bm.Set("trade_type", "JSAPI")
+	bm.Set("sign_type", wechat.SignType_MD5)
+	bm.Set("openid", inData.ClientOpenId)
+
+	client := NewWechatService()
+	//请求支付下单,成功后得到结果
+	wxResp, err := client.UnifiedOrder(c, bm)
+	if err != nil {
+		app.Error(c, 400, err, "下单失败")
+		return
+	}
+
+	if wxResp.ReturnCode != "SUCCESS" {
+		app.Error(c, 400, errors.New(wxResp.ReturnMsg), "下单失败")
+		return
+	}
+	if wxResp.ResultCode != "SUCCESS" {
+		app.Error(c, 400, errors.New(wxResp.ErrCode+"--"+wxResp.ErrCodeDes), "下单失败")
+		return
+	}
+
+	//merchant, count, err := sqlData.GetOpenIdList(pageSize, pageIndex)
+	//if err != nil {
+	//	app.Error(c, 500, err, err.Error())
+	//	return
+	//}
+	app.OK(c, wxResp, app.Success)
+
+}
+
+func PayCallBack(c *gin.Context) {
+	var payLog shanghu.PayCallbackLog
+	var payTrans shanghu.ClientPayTrans
+	wxNotify, err := wechat.ParseNotifyToBodyMap(c.Request)
+	if err != nil {
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+
+	//通知回调log
+	payLog.CallBackLog = wxNotify.JsonBody()
+	payLog.ThirdTradeNo = wxNotify.Get("transaction_id")
+	payLog.OutTradeNo = wxNotify.Get("out_trade_no")
+	payLog.CreatedAt = time.Now()
+	payLog.UpdatedAt = time.Now()
+
+	_, err = payLog.Create()
+	if err != nil {
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+
+	if wxNotify.Get("return_code") != "SUCCESS" || wxNotify.Get("result_code") != "SUCCESS" {
+		payLog.ErrLog = "微信返回错误:" + wxNotify.Get("return_code") + "--" + wxNotify.Get("result_code")
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+
+	//校验金额
+	payTrans.OutTradeNo = wxNotify.Get("out_trade_no")
+	payTransInfo, err := payTrans.GetPayTransByTradeNo()
+	if err != nil {
+		payLog.ErrLog = "查询交易信息错误:" + " err=" + err.Error()
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+	// 判断金额与支付流水是否一致
+	totalFee, err := decimal.NewFromString(wxNotify.Get("total_fee"))
+	if err != nil {
+		payLog.ErrLog = "解析总金额报错:" + "err=" + err.Error()
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+	if !totalFee.Equal(payTransInfo.Amount.Mul(decimal.NewFromInt(100))) {
+		payLog.ErrLog = "验证金额报错:total_fee=" + wxNotify.Get("total_fee") + " amount=" + payTransInfo.Amount.String()
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+	// 解析支付时间
+	timeEnd, err := time.ParseInLocation("20060102150405", wxNotify.Get("time_end"), tools.TimeLocation)
+	if err != nil {
+		payLog.ErrLog = "付款时间解析出错:err=" + err.Error()
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+
+	payTrans.ThirdTradeNo = wxNotify.Get("transaction_id")
+	payTrans.Status = 2 //支付成功
+	payTrans.PayTime = timeEnd
+
+	err = payTrans.UpdatePayTransByTradeNo()
+	if err != nil {
+		payLog.ErrLog = "更新支付状态失败:err=" + err.Error()
+		payLog.UpdateMerchant()
+		c.XML(http.StatusOK, failResp)
+		return
+	}
+
+	c.XML(http.StatusOK, successResp)
+
+}
+
+func NewWechatService() *wechat.Client {
+
+	client := wechat.NewClient("wx25357518f710b8ce", "1501641641", "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz", true)
+	//设置国家
+	client.SetCountry(wechat.China)
+
+	return client
+}

+ 6 - 5
go.mod

@@ -7,11 +7,11 @@ require (
 	github.com/asmcos/requests v0.0.0-20210319030608-c839e8ae4946
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/gin-gonic/gin v1.8.1
+	github.com/go-pay/gopay v1.5.97
 	github.com/go-sql-driver/mysql v1.6.0
 	github.com/jinzhu/gorm v1.9.16
 	github.com/olivere/elastic/v7 v7.0.4
 	github.com/qiniu/go-sdk/v7 v7.11.1
-	github.com/robfig/cron v1.2.0
 	github.com/shopspring/decimal v1.2.0
 	github.com/sirupsen/logrus v1.4.2
 	github.com/unrolled/secure v1.10.0
@@ -25,6 +25,7 @@ require (
 	github.com/go-playground/validator/v10 v10.11.1 // indirect
 	github.com/goccy/go-json v0.9.11 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
@@ -36,11 +37,11 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/smartystreets/goconvey v1.7.2 // indirect
 	github.com/ugorji/go/codec v1.2.7 // indirect
-	golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
-	golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 // indirect
+	golang.org/x/crypto v0.14.0 // indirect
+	golang.org/x/net v0.10.0 // indirect
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
-	golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
-	golang.org/x/text v0.3.7 // indirect
+	golang.org/x/sys v0.13.0 // indirect
+	golang.org/x/text v0.13.0 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect

+ 12 - 10
go.sum

@@ -37,6 +37,8 @@ github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
 github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-pay/gopay v1.5.97 h1:EPkwzCAwMhV/q+YHGAJVtDiQJ8AL5MNReQaPQ4wozHo=
+github.com/go-pay/gopay v1.5.97/go.mod h1:843cD8QnXyA5lYF2gleHzm2abXsqPCGq3JTEgzRVxW4=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
@@ -79,8 +81,9 @@ github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
 github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
 github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@@ -146,8 +149,6 @@ github.com/qiniu/go-sdk/v7 v7.11.1 h1:/LZ9rvFS4p6SnszhGv11FNB1+n4OZvBCwFg7opH5Ov
 github.com/qiniu/go-sdk/v7 v7.11.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
 github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
-github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
@@ -184,8 +185,8 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
-golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -204,8 +205,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 h1:TWZxd/th7FbRSMret2MVQdlI8uT49QEtwZdvJrxjEHU=
-golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -231,15 +232,16 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
-golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 11 - 11
models/shanghu/client.trans.go

@@ -6,15 +6,15 @@ import (
 
 // 客户端交易记录
 type MerchantClientTrans struct {
-	ID             int       `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`     // 主键
-	Type           int       `gorm:"column:type;type:int(11)" json:"type"`                            // 1-收款
-	MerchantCardID int64     `gorm:"column:merchant_card_id;type:bigint(20)" json:"merchant_card_id"` // 商户卡id
-	Amount         string    `gorm:"column:amount;type:decimal(10,2)" json:"amount"`                  // 交易金额
-	TransID        string    `gorm:"column:trans_id;type:varchar(255)" json:"trans_id"`               // 交易id
-	ClientOpenID   string    `gorm:"column:client_open_id;type:varchar(255)" json:"client_open_id"`   // openid
-	CreateBy       int64     `gorm:"column:create_by;type:bigint(20)" json:"create_by"`               // 创建者
-	UpdateBy       int64     `gorm:"column:update_by;type:bigint(20)" json:"update_by"`               // 更新者
-	CreatedAt      time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`            // 创建时间
-	UpdatedAt      time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`            // 最后更新时间
-	DeletedAt      time.Time `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`            // 删除时间
+	ID             int       `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`       // 主键
+	Type           int       `gorm:"column:type;type:int(11)" json:"type"`                              // 1-收款
+	MerchantCardID int64     `gorm:"column:merchant_card_id;type:bigint(20)" json:"merchant_card_id"`   // 商户卡id
+	Amount         string    `gorm:"column:amount;type:decimal(10,2)" json:"amount"`                    // 交易金额
+	TransID        string    `gorm:"column:trans_id;type:varchar(255)" json:"trans_id"`                 // 交易id
+	ClientOpenID   string    `gorm:"column:client_open_id;type:varchar(255)" json:"client_open_id"`     // openid
+	CreateBy       int64     `gorm:"column:create_by;type:bigint(20)" json:"create_by"`                 // 创建者
+	UpdateBy       int64     `gorm:"column:update_by;type:bigint(20)" json:"update_by"`                 // 更新者
+	CreatedAt      time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`              // 创建时间
+	UpdatedAt      time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`              // 最后更新时间
+	DeletedAt      time.Time `gorm:"column:deleted_at;type:datetime(3);default:null" json:"deleted_at"` // 删除时间
 }

+ 48 - 0
models/shanghu/pay.callback.log.go

@@ -0,0 +1,48 @@
+package shanghu
+
+import (
+	orm "duoduo/database"
+	"time"
+)
+
+type PayCallbackLog struct {
+	ID           int64     `gorm:"column:id;type:bigint(20);primary_key;AUTO_INCREMENT" json:"id"`
+	CallBackLog  string    `gorm:"column:call_back_log;type:text" json:"call_back_log"`
+	ErrLog       string    `gorm:"column:err_log;type:varchar(255)" json:"err_log"`                   // 处理失败原因
+	OutTradeNo   string    `gorm:"column:out_trade_no;type:varchar(255)" json:"out_trade_no"`         // 交易id
+	ThirdTradeNo string    `gorm:"column:third_trade_no;type:varchar(255)" json:"third_trade_no"`     // 微信交易id
+	CreateBy     int64     `gorm:"column:create_by;type:bigint(20)" json:"create_by"`                 // 创建者
+	UpdateBy     int64     `gorm:"column:update_by;type:bigint(20)" json:"update_by"`                 // 更新者
+	CreatedAt    time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`              // 创建时间
+	UpdatedAt    time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`              // 最后更新时间
+	DeletedAt    time.Time `gorm:"column:deleted_at;type:datetime(3);default:null" json:"deleted_at"` // 删除时间
+}
+
+func (m *PayCallbackLog) TableName() string {
+	return "pay_callback_log"
+}
+
+func (u *PayCallbackLog) Create() (PayCallbackLog, error) {
+	var doc PayCallbackLog
+	var err error
+
+	doc = *u
+	err = orm.ShMysql.Table(u.TableName()).Create(&doc).Error
+	if err != nil {
+		return doc, err
+	}
+
+	return doc, nil
+}
+
+func (m *PayCallbackLog) UpdateMerchant() error {
+
+	if err := orm.ShMysql.Table(m.TableName()).Model(&m).Where("third_trade_no = ? ", m.ThirdTradeNo).Updates(
+		map[string]interface{}{
+			"err_log":    m.ErrLog,
+			"updated_at": time.Now()}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}

+ 92 - 0
models/shanghu/pay.go

@@ -0,0 +1,92 @@
+package shanghu
+
+import (
+	orm "duoduo/database"
+	"github.com/shopspring/decimal"
+	"time"
+)
+
+type ClientPayTrans struct {
+	ID             int64           `gorm:"column:id;type:bigint(20);primary_key" json:"id"`                   // 主键
+	RequestID      string          `gorm:"column:request_id;type:varchar(255)" json:"request_id"`             // 请求id,幂等性
+	OutTradeNo     string          `gorm:"column:out_trade_no;type:varchar(255)" json:"out_trade_no"`         // 交易id
+	MerchantCardID int64           `gorm:"column:merchant_card_id;type:bigint(20)" json:"merchant_card_id"`   // 商户卡id
+	ClientOpenID   string          `gorm:"column:client_open_id;type:varchar(255)" json:"client_open_id"`     // 客户端openid
+	Status         int             `gorm:"column:status;type:int(11)" json:"status"`                          // 1-未支付 2-支付成功 3-取消支付 4-退款
+	Amount         decimal.Decimal `gorm:"column:amount;type:decimal(10,2)" json:"amount"`                    // 交易金额
+	ThirdTradeNo   string          `gorm:"column:third_trade_no;type:varchar(255)" json:"third_trade_no"`     // 微信交易id
+	PayTime        time.Time       `gorm:"column:pay_time;type:datetime(3);default:null" json:"pay_time"`     // 支付时间
+	CreateBy       int64           `gorm:"column:create_by;type:bigint(20)" json:"create_by"`                 // 创建者
+	UpdateBy       int64           `gorm:"column:update_by;type:bigint(20)" json:"update_by"`                 // 更新者
+	CreatedAt      time.Time       `gorm:"column:created_at;type:datetime(3)" json:"created_at"`              // 创建时间
+	UpdatedAt      time.Time       `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`              // 最后更新时间
+	DeletedAt      time.Time       `gorm:"column:deleted_at;type:datetime(3);default:null" json:"deleted_at"` // 删除时间
+}
+
+func (m *ClientPayTrans) TableName() string {
+	return "client_pay_trans"
+}
+
+func (m *ClientPayTrans) GetRequestNum() int {
+	var count int
+
+	tableCount := orm.ShMysql.Table(m.TableName()).Where("request_id = ? ", m.RequestID)
+	tableCount.Count(&count)
+	return count
+
+}
+
+func (m *ClientPayTrans) GetPayTransByTradeNo() (ClientPayTrans, error) {
+	var doc ClientPayTrans
+
+	table := orm.ShMysql.Table(m.TableName())
+	table = table.Where("out_trade_no = ?  ", m.OutTradeNo)
+
+	if err := table.Select("*").First(&doc).Error; err != nil {
+		return doc, err
+	}
+
+	return doc, nil
+}
+
+func (u *ClientPayTrans) Create() (ClientPayTrans, error) {
+	var doc ClientPayTrans
+	var err error
+
+	doc = *u
+
+	err = orm.ShMysql.Table(u.TableName()).Create(&doc).Error
+	if err != nil {
+		return doc, err
+	}
+
+	return doc, nil
+}
+
+//var data biz.EbankAccountVa
+//err := c.data.mysql.WithContext(ctx).Table(req.TableName()).Where("open_id = ? and pay_agency = ? and  account_no = ?", req.OpenID, req.PayAgency, req.AccountNo).Select("id").First(&data).Error
+//
+//if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
+//return err
+//}
+//req.ID = data.ID
+//err = c.data.mysql.Clauses(clause.OnConflict{
+//UpdateAll: true,
+//}).Table(req.TableName()).Create(&req).Error
+//if err != nil {
+//return err
+//}
+//return nil
+
+func (m *ClientPayTrans) UpdatePayTransByTradeNo() error {
+
+	if err := orm.ShMysql.Table(m.TableName()).Model(&m).Where("out_trade_no = ? ", m.OutTradeNo).Updates(
+		map[string]interface{}{
+			"third_trade_no": m.ThirdTradeNo,
+			"pay_time":       m.PayTime,
+			"updated_at":     time.Now()}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}

+ 18 - 0
tools/utils.go

@@ -1 +1,19 @@
 package tools
+
+import (
+	"fmt"
+	"time"
+)
+
+var (
+	TimeLocation, _       = time.LoadLocation("Asia/Shanghai")
+	TimeLayoutWithoutTime = "2006-01-02"
+	TimeLayout            = "2006-01-02 15:04:05"
+)
+
+type JSONTime time.Time
+
+func (j JSONTime) MarshalJSON() ([]byte, error) {
+	var stamp = fmt.Sprintf("\"%s\"", time.Time(j).Format("2006-01-02 15:04:05"))
+	return []byte(stamp), nil
+}