diff --git a/api/proto/vas/proto/op.go b/api/proto/vas/proto/op.go index b09e86c8..c70dde4a 100644 --- a/api/proto/vas/proto/op.go +++ b/api/proto/vas/proto/op.go @@ -42,3 +42,7 @@ type OpCoinOrderListData struct { type DealOneCoinOrderReq struct { CoinOrderId string `json:"coin_order_id"` } + +type DealOneOrderReq struct { + OrderId string `json:"order_id"` +} diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index d979d6f7..e9b8c3da 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -215,6 +215,7 @@ func Init(r *gin.Engine) { vasPayGroup.POST("withdraw_send_verifycode", middleware.JSONParamValidator(vasproto.WithdrawSendVerifycodeReq{}), WithdrawSendVerifycode) vasPayGroup.POST("withdraw_apply", middleware.JSONParamValidator(vasproto.WithdrawApplyReq{}), WithdrawApply) vasPayGroup.POST("deal_one_coin_order", middleware.JSONParamValidator(vasproto.DealOneCoinOrderReq{}), DealOneCoinOrder) + vasPayGroup.POST("deal_one_order", middleware.JSONParamValidator(vasproto.DealOneOrderReq{}), DealOneOrder) extVasPayGroup := r.Group("/ext/vas") extVasPayGroup.POST("alipay_callback", AlipayCallback) diff --git a/app/mix/controller/vas.go b/app/mix/controller/vas.go index 123eb7df..e984ba6d 100644 --- a/app/mix/controller/vas.go +++ b/app/mix/controller/vas.go @@ -261,9 +261,29 @@ func WithdrawApply(ctx *gin.Context) { // 处理一个订单 func DealOneCoinOrder(ctx *gin.Context) { req := ctx.MustGet("client_req").(*vasproto.DealOneCoinOrderReq) - ec := service.DefaultService.DealOneCoinOrder(ctx, req) + ec, err := service.DefaultService.DealOneCoinOrder(ctx, req) if ec != errcode.ErrCodeVasSrvOk { logger.Error("DealOneCoinOrder fail, req: %v, ec: %v", util.ToJson(req), ec) + if err != nil { + ReplyErrorMsg(ctx, err.Error()) + return + } + ReplyErrCodeMsg(ctx, ec) + return + } + ReplyOk(ctx, nil) +} + +// 处理一个订单 +func DealOneOrder(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*vasproto.DealOneOrderReq) + ec, err := service.DefaultService.DealOneOrder(ctx, req) + if ec != errcode.ErrCodeVasSrvOk { + logger.Error("DealOneOrder fail, req: %v, ec: %v", util.ToJson(req), ec) + if err != nil { + ReplyErrorMsg(ctx, err.Error()) + return + } ReplyErrCodeMsg(ctx, ec) return } diff --git a/app/mix/service/logic/vas.go b/app/mix/service/logic/vas.go index c4d2470c..45bc9669 100644 --- a/app/mix/service/logic/vas.go +++ b/app/mix/service/logic/vas.go @@ -792,6 +792,7 @@ func (v *Vas) OneStepUnlockContact(ctx *gin.Context, req *vasproto.OneStepUnlock ID: goproto.String(coinOrderId), Mid: goproto.Int64(req.Mid), Uid: goproto.Int64(req.Uid), + Oid1: goproto.String(fmt.Sprintf("%d", req.InviterMid)), ProductId: goproto.String(contactProductId), Coins: goproto.Int64(coinPrice), Ct: goproto.Int64(timeNow), @@ -1939,7 +1940,7 @@ func (v *Vas) WithdrawApply(ctx *gin.Context, req *vasproto.WithdrawApplyReq) (t return } -// 结算订单 +// 结算金币订单 func (v *Vas) DealOneCoinOrder(ctx *gin.Context, coinOrderId string) (err error) { coinOrder, err := v.store.GetCoinOrderById(ctx, nil, coinOrderId) if err != nil { @@ -2010,6 +2011,7 @@ func (v *Vas) DealOneCoinOrder(ctx *gin.Context, coinOrderId string) (err error) BeforeWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds()), AfterWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds() + ch.GetChange()), Change: goproto.Int64(ch.GetChange()), + ProductId: goproto.String(coinOrder.GetProductId()), } errIn = v.store.CreateWithdrawDiamondsHis(ctx, tx, h) if errIn != nil { @@ -2036,6 +2038,139 @@ func (v *Vas) DealOneCoinOrder(ctx *gin.Context, coinOrderId string) (err error) return } +// 结算订单 +func (v *Vas) DealOneOrder(ctx *gin.Context, orderId string) (err error) { + order, err := v.store.GetOrderById(ctx, nil, orderId) + if err != nil { + logger.Error("GetCoinOrderById fail, id: %v, err: %v", orderId, err) + return + } + if order == nil { + err = errs.ErrVasOrderNotExists + logger.Error("GetCoinOrderById fail nil, id: %v, err: %v", orderId, err) + return + } + + // 支付宝查询订单 + alipayCli := alipaycli.GetDefaultAlipayClient() + alipayResp, err := alipayCli.QueryOrder(ctx, &alipaycli.QueryOrderParam{ + OutTradeNo: orderId, + }) + if err != nil { + logger.Error("alipayCli.QueryOrder fail, id: %v, err: %v", orderId, err) + return + } + if alipayResp == nil { + err = errors.New("alipayCli.QueryOrder resp nil") + logger.Error("alipayCli.QueryOrder nil, id: %v, err: %v", orderId, err) + return + } + // 已退款 + if alipayResp.Response.TradeStatus == "TRADE_CLOSED" { + // 更新订单状态 + err = v.store.UpdateOrderStatus(ctx, nil, order.GetID(), dbstruct.VasOrderStatusPaySuccess, dbstruct.VasOrderStatusRefund) + if err != nil { + logger.Error("UpdateOrderStatus fail, orderId: %v, err: %v", order.GetID(), err) + return + } + err = errors.New("closed order") + return + } + + // 判断时间,小于7天不处理 + if time.Now().Unix()-order.GetCt() < 7*86400 { + logger.Info("DealOneCoinOrder ct fail, order: %v", order.ToString()) + return + } + if order.GetOrderStatus() >= dbstruct.VasOrderStatusFinish { + err = errors.New("repeat deal") + logger.Info("DealOneCoinOrder status fail, order: %v", order.ToString()) + return + } + + // 开启事务 + tx, err := v.store.VasBegin(ctx) + if err != nil { + logger.Error("vas begin fail, err: %v", err) + return + } + defer func() { + if err != nil { + logger.Error("global err, order: %v, err: %v", util.ToJson(order), err) + } + errTx := v.store.DealTxCR(tx, err) + if errTx != nil { + logger.Error("DealTxCR fail, err: %v", errTx) + return + } + }() + + switch order.GetProductId() { + case dbstruct.ProductIdMembership: + return v.dealOneMembershipOrder(ctx, tx, order) + } + return +} + +// 结算会员订单 +func (v *Vas) dealOneMembershipOrder(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order) (err error) { + // 把订单对应的收入记录拿出来 + chList, err := v.store.GetIncomeCHList(ctx, tx, order.GetID()) + if err != nil { + logger.Error("GetIncomeCHList fail, orderId: %v, err: %v", order.GetID(), err) + return + } + + // 处理 + for _, ch := range chList { + if ch.GetMid() == common.OfficialMid { + continue + } + + // 获取钱包 + wallet, errIn := v.store.GetWalletForUpdate(ctx, tx, ch.GetMid()) + if errIn != nil { + logger.Error("GetWalletForUpdate fail, mid: %v, err: %v", ch.GetMid(), errIn) + err = errIn + return + } + + // 添加记录 + h := &dbstruct.WithdrawDiamondsHis{ + Mid: goproto.Int64(ch.GetMid()), + IncomeChId: goproto.Int64(ch.GetId()), + OrderId: goproto.String(ch.GetOrderId()), + Ct: goproto.Int64(time.Now().Unix()), + BeforeWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds()), + AfterWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds() + ch.GetChange()), + Change: goproto.Int64(ch.GetChange()), + ProductId: goproto.String(order.GetProductId()), + } + errIn = v.store.CreateWithdrawDiamondsHis(ctx, tx, h) + if errIn != nil { + logger.Error("CreateWithdrawDiamondsHis fail, mid: %v, err: %v", ch.GetMid(), errIn) + err = errIn + return + } + + // 更新钱包 + errIn = v.store.IncWithdrawDiamonds(ctx, tx, ch.GetMid(), ch.GetChange()) + if errIn != nil { + logger.Error("IncWithdrawDiamonds fail, mid: %v, change: %v, err: %v", ch.GetMid(), ch.GetChange(), errIn) + err = errIn + return + } + } + + // 更新订单状态 + err = v.store.UpdateOrderStatus(ctx, tx, order.GetID(), dbstruct.VasOrderStatusPaySuccess, dbstruct.VasOrderStatusFinish) + if err != nil { + logger.Error("UpdateOrderStatus fail, orderId: %v, err: %v", order.GetID(), err) + return + } + return +} + // 解锁会员资格 func (v *Vas) UnlockMembership(ctx *gin.Context, mid int64, product *dbstruct.Product, order *dbstruct.Order, wallet *dbstruct.Wallet) (orderId string, err error) { diff --git a/app/mix/service/vasservice.go b/app/mix/service/vasservice.go index 7257809c..02ce01d6 100644 --- a/app/mix/service/vasservice.go +++ b/app/mix/service/vasservice.go @@ -502,11 +502,23 @@ func (s *Service) OpCoinOrderList(ctx *gin.Context, req *vasproto.OpCoinOrderLis return } -func (s *Service) DealOneCoinOrder(ctx *gin.Context, req *vasproto.DealOneCoinOrderReq) (ec errcode.ErrCode) { - err := _DefaultVas.DealOneCoinOrder(ctx, req.CoinOrderId) - ec, err = errs.DealVasErr(err) +func (s *Service) DealOneCoinOrder(ctx *gin.Context, req *vasproto.DealOneCoinOrderReq) (ec errcode.ErrCode, err error) { + ec = errcode.ErrCodeVasSrvOk + err = _DefaultVas.DealOneCoinOrder(ctx, req.CoinOrderId) if err != nil { logger.Error("DealOneCoinOrder fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeVasSrvFail + return + } + return +} + +func (s *Service) DealOneOrder(ctx *gin.Context, req *vasproto.DealOneOrderReq) (ec errcode.ErrCode, err error) { + ec = errcode.ErrCodeVasSrvOk + err = _DefaultVas.DealOneOrder(ctx, req.OrderId) + if err != nil { + logger.Error("DealOneCoinOrder fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeVasSrvFail return } return diff --git a/dbstruct/vas.sql b/dbstruct/vas.sql index ecc858c3..7f23c710 100644 --- a/dbstruct/vas.sql +++ b/dbstruct/vas.sql @@ -30,6 +30,7 @@ CREATE TABLE `vas_order` ); CREATE INDEX out_order_id ON vas_order (out_order_id); CREATE INDEX ct_status ON vas_order (ct, order_status); +CREATE INDEX ix_ct_productid ON vas_order (ct, product_id); CREATE TABLE `vas_wallet` ( diff --git a/dbstruct/vas_mysql.go b/dbstruct/vas_mysql.go index 2aef9158..24f4643c 100644 --- a/dbstruct/vas_mysql.go +++ b/dbstruct/vas_mysql.go @@ -804,6 +804,7 @@ type WithdrawDiamondsHis struct { BeforeWithdrawDiamonds *int64 `json:"before_withdraw_diamonds" db:"before_withdraw_diamonds"` AfterWithdrawDiamonds *int64 `json:"after_withdraw_diamonds" db:"after_withdraw_diamonds"` Change *int64 `json:"change" db:"change"` + ProductId *string `json:"product_id" db:"product_id"` } func (p *WithdrawDiamondsHis) GetMid() int64 { diff --git a/library/payclients/alipaycli/client.go b/library/payclients/alipaycli/client.go index e2270dbf..684290fe 100644 --- a/library/payclients/alipaycli/client.go +++ b/library/payclients/alipaycli/client.go @@ -34,10 +34,15 @@ func Init(cfg *configcenter.AlipayClientConfig) (err error) { } alipayCli.SetNotifyUrl(cfg.NotifyUrl) + //err = alipayCli.SetCertSnByPath( + // "/app/wishpal-ironfan/etc/mix/alipaycert/appCertPublicKey_2021004115647165.crt", + // "/app/wishpal-ironfan/etc/mix/alipaycert/alipayRootCert.crt", + // "/app/wishpal-ironfan/etc/mix/alipaycert/alipayCertPublicKey_RSA2.crt", + //) err = alipayCli.SetCertSnByPath( - "/app/wishpal-ironfan/etc/mix/alipaycert/appCertPublicKey_2021004115647165.crt", - "/app/wishpal-ironfan/etc/mix/alipaycert/alipayRootCert.crt", - "/app/wishpal-ironfan/etc/mix/alipaycert/alipayCertPublicKey_RSA2.crt", + "/Users/erwin/wishpalv2/service/etc/mix/alipaycert/appCertPublicKey_2021004115647165.crt", + "/Users/erwin/wishpalv2/service/etc/mix/alipaycert/alipayRootCert.crt", + "/Users/erwin/wishpalv2/service/etc/mix/alipaycert/alipayCertPublicKey_RSA2.crt", ) if err != nil { logger.Error("SetCertSnByPath fail, cfg: %v, err: %v", util.ToJson(cfg), err) @@ -157,3 +162,19 @@ func (c *AlipayClient) UniTransfer(ctx context.Context, param *UniTransferParam) logger.Info("alipay UniTransfer param: %v, resp: %v", bm.JsonBody(), util.ToJson(resp)) return } + +// 订单查询 +type QueryOrderParam struct { + OutTradeNo string // 商家订单id,我们自己的订单id +} + +func (c *AlipayClient) QueryOrder(ctx context.Context, param *QueryOrderParam) (resp *alipay.TradeQueryResponse, err error) { + bm := gopay.BodyMap{ + "out_trade_no": param.OutTradeNo, + } + resp, err = c.TradeQuery(ctx, bm) + if err != nil { + return + } + return +} diff --git a/library/payclients/alipaycli/client_test.go b/library/payclients/alipaycli/client_test.go index 77fe4a36..1f3ca071 100644 --- a/library/payclients/alipaycli/client_test.go +++ b/library/payclients/alipaycli/client_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "service/bizcommon/util" "service/library/configcenter" "service/library/idgenerator" "testing" @@ -12,7 +13,7 @@ import ( func TestMain(m *testing.M) { cfg := &configcenter.AlipayClientConfig{ Appid: "2021004115647165", - PrivateKey: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeA8ADj0arLzUK5B3R5bqbOkXm8TBe0dI6XpefxnHeNyksEFhQuWTFtQA550lGj/VMyNQC0R+gL6VK/5SdgonzxMx8bC+/1lIWrl3Q8F+JLsAUcNh6C+WzDw7m5YSrG9361f3+i/gPVC4GRTzx91qLdk1ySGZv/GpSVv49k9qtD83gRCbxJq5FY1QA83Wb5xkJoMwl+SlKvFLNxIIzPQh9DvDF+32FTsVQgm3sX1lsArTA4yp7Y4SG3A8mkOanidYX1KiUawMBWaDgZi1CMpQ+Zbb3w0C0txCeZ8ek5JeUw2EYHJ0awMnFE6+vPDm1LNJ0DodkvszGbmLvXQS2eMfVAgMBAAECggEABj1nq7xQshnfIlr3wn4r7E1UzVXg7x2iC6QIoMqZXIkZGyBaHHX1wMjxBVhY0GyG501W8fWIJi87e8az+7RX9kRjPtaW3CB+hV7FXabgXu51r1j1c3xcycFieUO/BXLIRUuGCDNGzHQqjRzW10dJUBCP5l9tjCaPnEjLKWtZS1QwhtmjmAJalC+oQLnhvlmURSWwyvIIgrqK82DZj5DFgXrxlah21Sou/N4xV/W/d0OkaouDxHtQ4Oy/yE/oc2z+vbcBcHrIt+r2g8gymKPdsDFyglUqZeNRlwYnifSUH7Bmn+1uiXAi86RlUdtCYngNUUNoOStB9oC7gidPHyeb0QKBgQDV58AFBOCHdG2gO+VvKiIpbquWMYTbmbRGmCai911G6zX8X/37nawKWZkyk4kIkrohZtwwL5s6AIlD5F9nRBp+Uk1QvlIYokD3vjUuVPyvpC3raASUvTs0WoWbq3gfPH5Qn5vS1C89CQ/68FC+89CYiexxwq7ybX86lzhYGpRXrwKBgQC9HE7e5Cr9akJrUuPW8NjbKATYg2h8s3/FMa4AbeTLPSEPPp20yRGA5P0xPgV1JocfmVd3VC6ivF6YHKKVnKoVbR74InCi4hhriGb3uLz7i6ngjcxysBfOlKA8lOlMAMY4ZrE2zTp2mUKF1Wp3a0IiC0xGPmdh44xvrO8h4UK1uwKBgGkY3Lealdb31YUM6MWdpftFbPj2i5h6BKFK0HDxqNrXZkcBwwCFj6nmK+c1pfaYYOtYZocmXNY6vTamVlmelIXMWkB5wkxoLBONWQPSrfR+bjFjghyyfQxUDaFWtkZiqo1iXmVP64XJlhhtSr8HfE32Nj4OsXZbZk+bTQu8w3XxAoGBAJJg3wvYQQ52uYKpXoJ5WuIIT/fnf3FG35ebpLvgVECcyIIsTZYqLMpAdVswTrknYWAsKW/MUWzVscK8J3z3DxAWruoSfm0V5SEXK9yDuoRazttp5t6/Py5ADOfXu92Am+38Tx/EK4SxjLRbRVW6SC5h4GBe3Vb/8yxeB5uJ6UERAoGAbsCHaS1NW21qYxeWAD8XC1x8B5vLwcVILP0kIt5CPFgKRcRbJPEfnSF8fFC0OKwJrp32+AhMTOEISwJVkR10Jzqn7VU1bu7lHYkiiOIZJCb1cdjfPxYUR60vpIttoKuz+CBIgc0zo1tu3TgCVdCy//coVRCoLeK+JawP43VLJ+g=", + PrivateKey: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtj6Nu2olEB8c8SDSANExaPbmk8LoYPEGB/APZDskhezO/w4OwPmG1Ak79XDpUUIihdmTZ8i2nBLhhRpFZGU4MMdil4X7a5nYHBm5dkGo3isIInn/qV7AYDqek4pGpMUPI6fbR05NWnUTZ+3AvMjTNBa979MgLyqS3jzpvuK6yXM5RinKVv8IF9KgthRKjH4LHyyRXBGu2cJSA5utjzFCL9KIu/T4XXZ09HOgz+JKPxXDj25Ob5eTtcBDsoJfXChcOvwCA7nxb1jzvGDrkqF36H1CC8KT1aoLRLisBsM+7sjEfNcB4RG5pGHSDGQJwZ0stGAhXf/fuWiLmI/zMOXODAgMBAAECggEAEL7CAuj8w16Iv20r+46QK0i3R42eNsZhf5wD9wYxK5TKal7/rppmLOObIWCrlATtGb7lfg2aj/mpnGEFlvYVDKImh+KYrZ/8lTLupQJQ7SjrDY/VQZPSPo/zZrohWZSSOKkyEg56samcwfc7XKJYa8t3odr9Df4wJDGibrL/z99xrJrz85le+NHBISXUyddS5ojuDNfGwE39wgLu52P74QYxv6s0xZKj2aALXaLXyTzF4ys34nVhhv75gXFtENiUet5/PVRYe7OE4cndcOP9WI3nXP5ojak8N4fY15S0YOm/0NZP8w6nWUemFjUNRXYRY42J+W+/myW8RIppaGuwkQKBgQDYH263Lh8C9EkjGA2po68LUTBhM2fT2L3batzHjXo0jvLT0IR88/9V7xhC3ZQZNcxQCK0xaCYfzFx3UV9veC1zeVRW0i/hyJ8B40w3WXCoGDiHgzQ/kcu/Iaw2qmJyhEFzOwAfrxIgco9EiZBaY7fXyy7YkZLGqXNFcLsuWR5qWwKBgQDNlc19CESIukT31Ap2QBe6t9YNXHZuKTePBRaIUCan3vZwPfZePLJQCgtoVhnCGoHZJ+ZlNhYri9x6DNLZpxCSHZNjccmqFGj3N7xWBqU38gu2dcPdMAxT1ERwf2gdlhgWAhVzD56hsWyrbP9YZACXy7TlevSj7s/5GaNaVuHT+QKBgQC2VZZ3zt51BJnrlLB6LVFRz/ZsGw1+qj5LLpYDeXXff7aYQzRzovsJigVC7GO0/TFZWGid5Us8ypI8TBejGJXn9TXVZdDlwPd9hUFY9QlZl82hbm0XMK7fms4K9KbIDJKXX/CTyoUVgPEkFpcF21lQIuhr6C0XlABfVmNlD+TcPwKBgHJuot2ov3UXsZH8/gHKNSsibswrHmS9HobGPz+K1al1Stk5NCxKPrqcjLL70gSf+ozkT7MggwCkLgnln2u1OV0Lh2HAEY9RIwgQhw2fT1GvseNS873no8T5j0rLMCnfxPJjIItWM2cvOhzFY/BQYaAcrElbwlaJdEvkgG+lkrgJAoGBAInYRdHo9V1exDUS0ucQuraoEtuvgOrqatnCbuNZ9EpgjFJxDbHXOIJYPvgfpyws4amdksNQL0Ux9+p+esJOS+JYYkE1nmCTCrfb6SNKr+PdeUteKtTiHJb4zJhGVC2Lk4uBg9AGa/d7j5apWw4RsZ/R1ky1S6KQUkqItbVrhC8l", NotifyUrl: "", } err := Init(cfg) @@ -37,3 +38,15 @@ func TestAlipayClient_AppPay(t *testing.T) { } fmt.Println(paramStr) } + +func TestAlipayClient_QueryOrder(t *testing.T) { + cli := GetDefaultAlipayClient() + resp, err := cli.QueryOrder(context.Background(), &QueryOrderParam{ + OutTradeNo: "1750539072771731456", + }) + if err != nil { + fmt.Println(err.Error()) + return + } + fmt.Println(util.ToJson(resp)) +}