diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index dccccbf8..44da5797 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -146,7 +146,7 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeMomentCreateTimesSrvFail: "动态创建频次表服务错误", ErrCodeMomentCreateTimesNotExist: "动态创建频次表不存在", - ErrCodeMomentCreateTimesReachedDailyUpperbound: "动态创建次数已达每日上限", + ErrCodeMomentCreateTimesReachedDailyUpperbound: "每天仅可发布三条动态", ErrCodeAlignedAuditTaskSrvFail: "已对齐审核任务服务错误", ErrCodeAlignedAuditTaskNotExist: "已对齐审核任务不存在", diff --git a/api/proto/vas/proto/pay.go b/api/proto/vas/proto/pay.go index d303bf60..64dc1f0b 100644 --- a/api/proto/vas/proto/pay.go +++ b/api/proto/vas/proto/pay.go @@ -25,9 +25,15 @@ type GetMembershipProductListData struct { // 创建订单 const ( - PayTypeOp = "op" // op直冲 - PayTypeAlipay = "alipay" // 支付宝 - PayTypeAlipayH5 = "alipay_h5" // 支付宝 h5 + PayTypeOp = "op" // op直冲 + PayTypeAlipay = "alipay" // 支付宝 + PayTypeAlipayH5 = "alipay_h5" // 支付宝 h5 + PayTypeWxpayNative = "wxpay_native" // 微信native +) + +const ( + CallBackPayTypeAlipay = "alipay" + CallBackPayTypeWxpay = "wxpay" ) const ( @@ -53,9 +59,10 @@ type CreateOrderReq struct { } type CreateOrderData struct { - OrderId string `json:"order_id"` // 订单id - AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数 - AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数 + OrderId string `json:"order_id"` // 订单id + AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数 + AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数 + WxpayNativeParamStr string `json:"wxpay_native_param_str"` // 微信支付 native支付参数 } // 预解锁联系方式 @@ -132,10 +139,11 @@ type H5DirectUnlockWechatReq struct { } type H5DirectUnlockWechatData struct { - CoinEnough int32 `json:"coin_enough"` // 0:不够(用下面的支付宝参数),1:够 - OrderId string `json:"order_id"` // 订单id - AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数 - AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数 + CoinEnough int32 `json:"coin_enough"` // 0:不够(用下面的支付宝参数),1:够 + OrderId string `json:"order_id"` // 订单id + AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数 + AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数 + WxpayNativeParamStr string `json:"wxpay_native_param_str"` // 微信支付 native支付参数 } // 支付宝回调参数 @@ -144,6 +152,13 @@ type AlipayCallbackParamIn struct { AlipayOrderId string `json:"alipay_order_id"` // 支付宝订单id } +// 支付回调参数 +type PayCallbackParamIn struct { + OrderId string `json:"order_id"` // 我们自己服务的订单id + OutOrderId string `json:"out_order_id"` // 外部订单id,比如支付宝、微信 + CallbackPayType string `json:"callback_pay_type"` // 支付类型 +} + // 解锁会员资格 type UnlockMembershipReq struct { base.BaseRequest diff --git a/app/mix/controller/alipay_callback.go b/app/mix/controller/alipay_callback.go index 8eda9d93..08afbe5d 100644 --- a/app/mix/controller/alipay_callback.go +++ b/app/mix/controller/alipay_callback.go @@ -19,9 +19,10 @@ func AlipayCallback(ctx *gin.Context) { } if bm.GetString("trade_status") == "TRADE_SUCCESS" { - service.DefaultService.AlipayCallback(ctx, &vasproto.AlipayCallbackParamIn{ - OrderId: bm.GetString("out_trade_no"), - AlipayOrderId: bm.GetString("trade_no"), + service.DefaultService.PayCallback(ctx, &vasproto.PayCallbackParamIn{ + OrderId: bm.GetString("out_trade_no"), + OutOrderId: bm.GetString("trade_no"), + CallbackPayType: vasproto.CallBackPayTypeAlipay, }) } ctx.String(200, "success") diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index 14170eac..563df6ab 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -227,6 +227,7 @@ func Init(r *gin.Engine) { extVasPayGroup := r.Group("/ext/vas") extVasPayGroup.POST("alipay_callback", AlipayCallback) + extVasPayGroup.POST("wxpay_callback", WxpayCallback) opVasPayGroup := r.Group("/op/vas", PrepareOp()) opVasPayGroup.POST("create_order", middleware.JSONParamValidator(vasproto.OpCreateOrderReq{}), OpCreateOrder) diff --git a/app/mix/controller/wxpay_callback.go b/app/mix/controller/wxpay_callback.go new file mode 100644 index 00000000..d39f3cb0 --- /dev/null +++ b/app/mix/controller/wxpay_callback.go @@ -0,0 +1,33 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + wxpay "github.com/go-pay/gopay/wechat/v3" + vasproto "service/api/proto/vas/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + "service/library/payclients/wxpaycli" +) + +func WxpayCallback(ctx *gin.Context) { + notify, err := wxpaycli.GetDefaultWxpayClient().ParseNotify(ctx.Request) + if err != nil { + logger.Error("ParseNotify fail, req: %v, err: %v", util.ToJson(notify), err) + return + } + if notify == nil { + logger.Error("ParseNotify nil, req: %v, err: %v", util.ToJson(notify), err) + return + } + logger.Info("WxpayCallback, notify: %v", util.ToJson(notify)) + + if notify.TradeState == wxpay.TradeStateSuccess { + service.DefaultService.PayCallback(ctx, &vasproto.PayCallbackParamIn{ + OrderId: notify.OutTradeNo, + OutOrderId: notify.TransactionId, + CallbackPayType: vasproto.CallBackPayTypeWxpay, + }) + } + ctx.String(200, "success") +} diff --git a/app/mix/service/business_validator/auth.go b/app/mix/service/business_validator/auth.go index 061c60f6..194d5364 100644 --- a/app/mix/service/business_validator/auth.go +++ b/app/mix/service/business_validator/auth.go @@ -287,6 +287,7 @@ func (l *AuthBusinessValidator) EnsureMomentCreateTimesNotReachedDailyUpperbound return } + logger.Info("momentCreateTimes: %v, maxDailyMomentCreateTimes : %v", l.momentCreateTimes.CreateTimes, maxDailyMomentCreateTimes) if l.momentCreateTimes.CreateTimes >= int64(maxDailyMomentCreateTimes) { logger.Error("the moment create times of this mid has reached its daily upperbound") l.ec = errcode.ErrCodeMomentCreateTimesReachedDailyUpperbound diff --git a/app/mix/service/logic/media.go b/app/mix/service/logic/media.go index 6b8f9999..f499c6d4 100644 --- a/app/mix/service/logic/media.go +++ b/app/mix/service/logic/media.go @@ -191,14 +191,23 @@ func (m *Media) saveImage(ctx *gin.Context, image *dbstruct.Image) (*dbstruct.Im } id := mediaSeqId.Seq doc := &dbstruct.Image{ - Id: id, - Ct: time.Now().Unix(), - Ut: time.Now().Unix(), - SrcId: image.SrcId, - MD5: image.MD5, - W: image.W, - H: image.H, - Fmt: image.Fmt, + Id: id, + Ct: time.Now().Unix(), + Ut: time.Now().Unix(), + SrcId: image.SrcId, + SizeSrc: 0, + SrcId720: "", + Size720: 0, + SrcId1080: "", + Size1080: 0, + SrcId1440: "", + Size1440: 0, + Status: 0, + ResizeT: 0, + MD5: image.MD5, + W: image.W, + H: image.H, + Fmt: image.Fmt, } err = m.store.SaveImage(ctx, doc) if err != nil { diff --git a/app/mix/service/logic/vas.go b/app/mix/service/logic/vas.go index ab03cec0..9d38c96f 100644 --- a/app/mix/service/logic/vas.go +++ b/app/mix/service/logic/vas.go @@ -20,6 +20,7 @@ import ( "service/library/idgenerator" "service/library/logger" "service/library/payclients/alipaycli" + "service/library/payclients/wxpaycli" "time" "github.com/go-pay/gopay/alipay" @@ -93,8 +94,9 @@ func (v *Vas) CreateOrder(ctx *gin.Context, req *vasproto.CreateOrderReq) (data orderId = idgenerator.GenOrderId() // 订单id ) var ( - alipayParamStr string - alipayH5ParamStr string + alipayParamStr string + alipayH5ParamStr string + wxpayNativeParamStr string ) defer func() { @@ -180,16 +182,29 @@ func (v *Vas) CreateOrder(ctx *gin.Context, req *vasproto.CreateOrderReq) (data } case vasproto.PayTypeAlipayH5: alipayCli := alipaycli.GetDefaultAlipayClient() - appPayParam := &alipaycli.WapPayParam{ + wapPayParam := &alipaycli.WapPayParam{ OutTradeNo: orderId, Subject: product.Subject, TotalAmount: product.RealPrice, TimeOutSeconds: 900, ReturnUrl: req.ReturnUrl, } - alipayH5ParamStr, err = alipayCli.WapPay(ctx, appPayParam) + alipayH5ParamStr, err = alipayCli.WapPay(ctx, wapPayParam) if err != nil { - logger.Error("alipay WapPay fail, req: %v, wapPayParam: %v, err: %v", util.ToJson(req), util.ToJson(appPayParam), err) + logger.Error("alipay WapPay fail, req: %v, wapPayParam: %v, err: %v", util.ToJson(req), util.ToJson(wapPayParam), err) + return + } + case vasproto.PayTypeWxpayNative: + wxpayCli := wxpaycli.GetDefaultWxpayClient() + nativePayParam := &wxpaycli.NativePayParam{ + Description: product.Subject, + OutTradeNo: orderId, + TotalAmount: product.RealPrice, + TimeOutSeconds: 900, + } + wxpayNativeParamStr, err = wxpayCli.NativePay(ctx, nativePayParam) + if err != nil { + logger.Error("wxpay NativePay fail, req: %v, wapPayParam: %v, err: %v", util.ToJson(req), util.ToJson(nativePayParam), err) return } } @@ -230,9 +245,10 @@ func (v *Vas) CreateOrder(ctx *gin.Context, req *vasproto.CreateOrderReq) (data } data = &vasproto.CreateOrderData{ - OrderId: orderId, - AlipayParamStr: alipayParamStr, - AlipayH5ParamStr: alipayH5ParamStr, + OrderId: orderId, + AlipayParamStr: alipayParamStr, + AlipayH5ParamStr: alipayH5ParamStr, + WxpayNativeParamStr: wxpayNativeParamStr, } return } @@ -1497,9 +1513,10 @@ func (v *Vas) H5DirectUnlockWechat(ctx *gin.Context, req *vasproto.H5DirectUnloc return } data = &vasproto.H5DirectUnlockWechatData{ - OrderId: cData.OrderId, - AlipayParamStr: cData.AlipayParamStr, - AlipayH5ParamStr: cData.AlipayH5ParamStr, + OrderId: cData.OrderId, + AlipayParamStr: cData.AlipayParamStr, + AlipayH5ParamStr: cData.AlipayH5ParamStr, + WxpayNativeParamStr: cData.WxpayNativeParamStr, } return } @@ -1524,13 +1541,14 @@ func (v *Vas) GetUserWechatUnlock(ctx *gin.Context, mid, uid int64) (uu *dbstruc return } -// 支付宝【支付】回调 -func (v *Vas) AlipayCallback(ctx *gin.Context, p *vasproto.AlipayCallbackParamIn) { +// 【支付】回调 +func (v *Vas) PayCallback(ctx *gin.Context, p *vasproto.PayCallbackParamIn) { var ( - orderId = p.OrderId - alipayOrderId = p.AlipayOrderId - afterStatus int32 + orderId = p.OrderId + outOrderId = p.OutOrderId + afterStatus int32 ) + logger.Info("PayCallback, param: %v", util.ToJson(p)) // 获取订单 checkOrder, err := v.store.GetOrderById(ctx, nil, orderId) @@ -1548,8 +1566,8 @@ func (v *Vas) AlipayCallback(ctx *gin.Context, p *vasproto.AlipayCallbackParamIn return } - // ali_order_id检查 - outOrder, err := v.store.GetOrderByOutOrderId(ctx, nil, alipayOrderId) + // out_order_id检查 + outOrder, err := v.store.GetOrderByOutOrderId(ctx, nil, outOrderId) switch err { case sql.ErrNoRows: err = nil @@ -1665,8 +1683,8 @@ func (v *Vas) AlipayCallback(ctx *gin.Context, p *vasproto.AlipayCallbackParamIn } afterStatus = dbstruct.VasOrderStatusPaySuccess - // 更新支付宝订单 - err = v.store.UpdateOutOrderId(ctx, tx, orderId, alipayOrderId) + // 更新外部订单 + err = v.store.UpdateOutOrderId(ctx, tx, orderId, outOrderId) if err != nil { logger.Error("UpdateOutOrderId fail, p: %v", util.ToJson(p)) return @@ -2062,28 +2080,35 @@ func (v *Vas) DealOneOrder(ctx *gin.Context, orderId string) (err error) { } // 支付宝查询订单 - 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) + switch order.GetPayType() { + case vasproto.PayTypeAlipayH5: + alipayCli := alipaycli.GetDefaultAlipayClient() + var alipayResp *alipay.TradeQueryResponse + alipayResp, err = alipayCli.QueryOrder(ctx, &alipaycli.QueryOrderParam{ + OutTradeNo: orderId, + }) if err != nil { - logger.Error("UpdateOrderStatus fail, orderId: %v, err: %v", order.GetID(), err) + logger.Error("alipayCli.QueryOrder fail, id: %v, err: %v", orderId, err) return } - err = errors.New("closed order") + 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 + } + case vasproto.CallBackPayTypeWxpay: + err = errors.New("暂不处理微信订单") return } diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 3f858c92..3521674c 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -48,6 +48,7 @@ import ( "service/library/melody" "service/library/mycrypto" "service/library/payclients/alipaycli" + "service/library/payclients/wxpaycli" "service/library/redis" "go.mongodb.org/mongo-driver/mongo" @@ -129,7 +130,13 @@ func (s *Service) Init(c any) (err error) { err = alipaycli.Init(cfg.Alipay) if err != nil { - logger.Error("alipaycli.Init fail, cfg: %v, err: %v", util.ToJson(cfg), err) + logger.Error("alipaycli.Init fail, cfg: %v, err: %v", util.ToJson(cfg.Alipay), err) + return + } + + err = wxpaycli.Init(cfg.Wxpay) + if err != nil { + logger.Error("wxpaycli.Init fail, cfg: %v, err: %v", util.ToJson(cfg.Wxpay), err) return } diff --git a/app/mix/service/vasservice.go b/app/mix/service/vasservice.go index 02ce01d6..102b2f3b 100644 --- a/app/mix/service/vasservice.go +++ b/app/mix/service/vasservice.go @@ -369,9 +369,9 @@ func (s *Service) H5DirectUnlockWechat(ctx *gin.Context, req *vasproto.H5DirectU return } -// 支付宝回调 -func (s *Service) AlipayCallback(ctx *gin.Context, req *vasproto.AlipayCallbackParamIn) (data *vasproto.H5DirectUnlockWechatData, ec errcode.ErrCode) { - _DefaultVas.AlipayCallback(ctx, req) +// 支付回调 +func (s *Service) PayCallback(ctx *gin.Context, req *vasproto.PayCallbackParamIn) (data *vasproto.H5DirectUnlockWechatData, ec errcode.ErrCode) { + _DefaultVas.PayCallback(ctx, req) return } diff --git a/dbstruct/media.go b/dbstruct/media.go index e49235ed..bea94a1a 100644 --- a/dbstruct/media.go +++ b/dbstruct/media.go @@ -6,15 +6,40 @@ import ( ) // 图片 +const ( + ImageStatusResizeInit = 0 + ImageStatusResizeDone = 100 + ImageStatusResizeFail = 101 +) + type Image struct { - Id int64 `json:"id" bson:"_id"` // 图片id - Ct int64 `json:"ct" bson:"ct"` // 创建时间 - Ut int64 `json:"ut" bson:"ut"` // 更新时间 - SrcId string `json:"src_id" bson:"src_id"` // 源id - MD5 string `json:"md5" bson:"md5"` // 视频md5 - W int64 `json:"w" bson:"w"` // 宽 - H int64 `json:"h" bson:"h"` // 高 - Fmt string `json:"fmt" bson:"fmt"` // 图片格式 + Id int64 `json:"id" bson:"_id"` // 图片id + Ct int64 `json:"ct" bson:"ct"` // 创建时间 + Ut int64 `json:"ut" bson:"ut"` // 更新时间 + SrcId string `json:"src_id" bson:"src_id"` // 源id + SizeSrc int64 `json:"size_src" bson:"size_src"` // 原图片大小 + SrcId720 string `json:"src_id_720" bson:"src_id_720"` // 720P图片 + Size720 int64 `json:"size_720" bson:"size_720"` // 720P图片大小 + SrcId1080 string `json:"src_id_1080" bson:"src_id_1080"` // 1080P图片 + Size1080 int64 `json:"size_1080" bson:"size_1080"` // 1080P图片大小 + SrcId1440 string `json:"src_id_1440" bson:"src_id_1440"` // 1440P图片 + Size1440 int64 `json:"size_1440" bson:"size_1440"` // 1440P图片大小 + Status int `json:"status" bson:"status"` // 状态 + ResizeT int64 `json:"resize_t" bson:"resize_t"` // 压缩时间 + MD5 string `json:"md5" bson:"md5"` // 视频md5 + W int64 `json:"w" bson:"w"` // 宽 + H int64 `json:"h" bson:"h"` // 高 + Fmt string `json:"fmt" bson:"fmt"` // 图片格式 +} + +func (i *Image) SelectMinSizeOssId() string { + if i == nil { + return "" + } + if len(i.SrcId720) > 0 && i.Size720 < i.SizeSrc { + return i.SrcId720 + } + return i.SrcId } type ToCImage struct { diff --git a/etc/mix/mix-prod.yaml b/etc/mix/mix-prod.yaml index b9c6144b..a58e0cca 100644 --- a/etc/mix/mix-prod.yaml +++ b/etc/mix/mix-prod.yaml @@ -27,11 +27,6 @@ mix_mysql: read_timeout_s: 5 write_timeout_s: 3 -wxpay: - mchid: "1111" - serial_no: "1111" - apiv3_key: "1111" - private_key: "af21aw65aw23efwa132f1waef56wa56awef" crypto: aes: @@ -48,6 +43,14 @@ alipay: private_key: "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" notify_url: "https://api.tiefen.fun/ext/vas/alipay_callback" +wxpay: + mchid: "1665016206" + appid: "wxc28fd8aaf31984b6" + serial_no: "51D8E75620B9569F10FF5363022F3C9D2152DF50" + apiv3_key: "UoaswgnuKYwIadqxCVag1IZlq5n9USvt" + private_key_path: "/app/wishpal-ironfan/etc/mix/wxpaycert/apiclient_key.pem" + notify_url: "https://api.tiefen.fun/ext/vas/wxpay_callback" + apollo: app_id: "wishpal_live_service" cluster: "dev" diff --git a/etc/mix/mix-test.yaml b/etc/mix/mix-test.yaml index e34401f5..f8dcfeee 100644 --- a/etc/mix/mix-test.yaml +++ b/etc/mix/mix-test.yaml @@ -28,10 +28,12 @@ mix_mysql: write_timeout_s: 3 wxpay: - mchid: "1111" - serial_no: "1111" - apiv3_key: "1111" - private_key: "af21aw65aw23efwa132f1waef56wa56awef" + mchid: "1665016206" + appid: "wxc28fd8aaf31984b6" + serial_no: "51D8E75620B9569F10FF5363022F3C9D2152DF50" + apiv3_key: "UoaswgnuKYwIadqxCVag1IZlq5n9USvt" + private_key_path: "/app/wishpal-ironfan/etc/mix/wxpaycert/apiclient_key.pem" + notify_url: "https://api.wishpal.cn/ext/vas/wxpay_callback" crypto: aes: diff --git a/etc/mix/wxpaycert/apiclient_cert.p12 b/etc/mix/wxpaycert/apiclient_cert.p12 new file mode 100644 index 00000000..3536f260 Binary files /dev/null and b/etc/mix/wxpaycert/apiclient_cert.p12 differ diff --git a/etc/mix/wxpaycert/apiclient_cert.pem b/etc/mix/wxpaycert/apiclient_cert.pem new file mode 100644 index 00000000..4fe4e212 --- /dev/null +++ b/etc/mix/wxpaycert/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKDCCAxCgAwIBAgIUUdjnViC5Vp8Q/1NjAi88nSFS31AwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjQwMTMwMDIxMDEyWhcNMjkwMTI4MDIxMDEyWjCBgTETMBEGA1UEAwwK +MTY2NTAxNjIwNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL +DCTmiJDpg73lv4PmhI/liLDkuobnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYT +AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAODFaaohlVkRV079c07NzrpSTJgBLr8vNMBY/pZb/Ah+KVoNIMB732NS +ykUqXVia5smQMnljZZodw7KKuB9hfcYcyuvXHu+2o43p5A+IZKGV/0eU5AJ2LXOZ +SsF4xaPlNyWX6V3JrNhqpHjIbpqGGPUGeypn5bpJr3G1N8y1NSsG8m4ebh1YBBHr +YDZN562T1g31zGI7AvsFdomjmFhq6Y2hvPukjPpceoGASEEfpC/yRrvjo05HVQVK +8x6oda/67IHNS2F0goUsJESBCYVOhezugmy4XaeyFkqcw+1rde+LzBs9sBlzhvgr +Asu/1LEEqJGHO1qAXwqpifWThEHyYUMCAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG +A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu +aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC +MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB +OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQAcaVb/ +Eg+fOt1qPr9we38kzUwVRktgamFB3hRfKvZpva3hx4lh8ZC3Et7bIP/r4IV5qCNC +wzN1Niq0aBXtnehOWL+mnnBmmW5QnB80UX1/yFxtaBEK+6R6IObAj7ClUSb0F8ak +0fRkCyWGl985sAUyZVE5g0jQEEcWFhdV9lpwPdprY4S9vck+cCfjPi4EyxEzRh7z +knsvZbjoj/0oTSKgg39D4UEQl6X0e3YR74kIXfKmFK08WxoKKet201pL3bzJKBAY +s+8VBqAKsTvNZJ1/GYPqax7ArpACX4LjgmzCv7/rrM75ce6rXWlkIOBSAz2dHuF0 +Q6iPI92vMh32p19Q +-----END CERTIFICATE----- diff --git a/etc/mix/wxpaycert/apiclient_key.pem b/etc/mix/wxpaycert/apiclient_key.pem new file mode 100644 index 00000000..4dff5710 --- /dev/null +++ b/etc/mix/wxpaycert/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDgxWmqIZVZEVdO +/XNOzc66UkyYAS6/LzTAWP6WW/wIfilaDSDAe99jUspFKl1YmubJkDJ5Y2WaHcOy +irgfYX3GHMrr1x7vtqON6eQPiGShlf9HlOQCdi1zmUrBeMWj5Tcll+ldyazYaqR4 +yG6ahhj1BnsqZ+W6Sa9xtTfMtTUrBvJuHm4dWAQR62A2Teetk9YN9cxiOwL7BXaJ +o5hYaumNobz7pIz6XHqBgEhBH6Qv8ka746NOR1UFSvMeqHWv+uyBzUthdIKFLCRE +gQmFToXs7oJsuF2nshZKnMPta3Xvi8wbPbAZc4b4KwLLv9SxBKiRhztagF8KqYn1 +k4RB8mFDAgMBAAECggEAFfMxXmvpKcmHvS8DE68FgSyITk/PQNxbSm1mb0iMVEf9 +wc2GZUWziv+KwTZh50U5RHXQeAo84dAGTGk/kdDzd2VYa6+WVdKAJluw6dNoAF+l +jlf77EGeLqvJoRsqMdcwi3tKTt5jAr9nUGRCaNSvmz4GyR1cUdTgTTplOJh5mLnm +GGZQPVR87pex00QCTtQ7dggG/vg8QM+XbBeSAkWByeUDsrCo4pKbsY+kxKGCkhV0 +3hxgHz2v7QZ8kanbgVPZzB76tOxRf9ghUQ5TFPc9kjgtQmn5ViqzMTXdotFXlE/u +n82BreEK03E9kWE3Uvt9jf21oJkQGQsRqTNYd8uIAQKBgQDyNOIIOnOgwt3coiZk +LYmo+F/zQQm6R8gebYWO1XCKLkMto9JaeJyEpehZQngMJvJc4r+4tWNFv8rcOHu5 +/7SnteVUJK4RBXpKosLqswqD0ySEXzNLloumvxXz8HnXIiTSQ3rtISQSHzp0uunq +fkTzezc9ZRT08Jx6rmLvpKsJ3QKBgQDtklbus1fRQ/zbdddKbTfYMZsWSi7qQiA3 +AKM7rY6wrS1gUJtVk9mhNgUe0uIvkkdQvtm/QKiSrtgGkre59g9sekZppCyWId6a +i1tlFvkG16Oc/TsAJDBNJgtWrkPDsjjHSx2VExTeRJ30XGd8lx3EEWHEbSboGPbf +G3gxm9a1nwKBgAdH06uPpj4k3WpubV0BiWvM24WCZPp2get4O1WJ0PI2ZcqPbBlQ +GtRZ8FwOhXFIEmz8W+r/eNZ153ErOXzj1NhdvWEEIT9dvMlVjypi62P/Cs/31KDn +C5edktlcVy1CV30CjkVmg6EP00ADBlkIJqZzA7wSt8iNGwcNCuhNON75AoGBAOGA +GXIj24RwM1Agr8UenHZix6HFsnh7YdazjT11RU7gYoTcnkUBvP5vpzaV10puX7D+ +JvOJrTjmK3k5xJkIPaWq/rEBu6yZ45DwHEV9I81h3BSErX852nswVGznprzXq1tI +KoE5BoLfwMqU90nkqsVT9mgbb2W5ZINrsI/uK0RjAoGBAL+9L8iJx6/M4yQosTrQ +IL6SOxPU3HlfkqZzObiQ8vs7VdIpGhB1EMc9UEKe8RcnpTUeLsVeNHdiDZzRHtJ3 +RxZS/AsjJtYAZ2f/maJhgQsh6JN1AceDhMhx1iTsjr/ockyUZuXKiCr5Q43tzrMJ +tMrOrkquEUSn+XDIomat7Bnw +-----END PRIVATE KEY----- diff --git a/library/configcenter/configcenter.go b/library/configcenter/configcenter.go index 533fe409..de720227 100644 --- a/library/configcenter/configcenter.go +++ b/library/configcenter/configcenter.go @@ -63,10 +63,12 @@ type RedisConfig struct { // 微信支付客户端配置 type WxpayClientConfig struct { - MchId string `json:"mchid" yaml:"mchid"` // 商户id - SerialNo string `json:"serial_no" yaml:"serial_no"` // 商户API证书的证书序列号 - ApiV3Key string `json:"apiv3_key" yaml:"apiv3_key"` // APIv3Key,商户平台获取 - PrivateKey string `json:"private_key" yaml:"private_key"` // 商户API证书下载后,私钥 apiclient_key.pem 读取后的字符串内容 + MchId string `json:"mchid" yaml:"mchid"` // 商户id + AppId string `json:"appid" yaml:"appid"` // appid + SerialNo string `json:"serial_no" yaml:"serial_no"` // 商户API证书的证书序列号 + ApiV3Key string `json:"apiv3_key" yaml:"apiv3_key"` // APIv3Key,商户平台获取 + PrivateKeyPath string `json:"private_key_path" yaml:"private_key_path"` // 商户API证书下载后,私钥 apiclient_key.pem 读取后的字符串内容 + NotifyUrl string `json:"notify_url" yaml:"notify_url"` // 回调地址 } // 账号相关验密配置 diff --git a/library/mediafiller/mediafillerhelper.go b/library/mediafiller/mediafillerhelper.go index ce657729..6650e905 100644 --- a/library/mediafiller/mediafillerhelper.go +++ b/library/mediafiller/mediafillerhelper.go @@ -46,12 +46,16 @@ func transToCImage(image *dbstruct.Image) *dbstruct.ToCImage { if image == nil { return nil } + + // 选择内存最小的图片 + imgSrcId := image.SelectMinSizeOssId() + return &dbstruct.ToCImage{ Id: image.Id, W: image.W, H: image.H, Fmt: image.Fmt, - Urls: []string{defaultMediaFiller.fileServerDomainName + image.SrcId}, + Urls: []string{defaultMediaFiller.fileServerDomainName + imgSrcId}, } } @@ -70,7 +74,8 @@ func transToCVideo(video *dbstruct.Video, coverImg *dbstruct.Image) *dbstruct.To ret.CoverW = coverImg.W ret.CoverH = coverImg.H ret.CoverFmt = coverImg.Fmt - ret.CoverUrls = []string{defaultMediaFiller.fileServerDomainName + coverImg.SrcId} + imgSrcId := coverImg.SelectMinSizeOssId() + ret.CoverUrls = []string{defaultMediaFiller.fileServerDomainName + imgSrcId} } return ret } diff --git a/library/payclients/wxpaycli/client.go b/library/payclients/wxpaycli/client.go index 4d74c0c5..d5ed24a2 100644 --- a/library/payclients/wxpaycli/client.go +++ b/library/payclients/wxpaycli/client.go @@ -1,24 +1,117 @@ package wxpaycli import ( + "context" + "net/http" + "os" + "time" + + "github.com/go-pay/gopay" wxpay "github.com/go-pay/gopay/wechat/v3" + "service/bizcommon/util" "service/library/configcenter" "service/library/logger" ) +const ( + DefaultOrderTimeoutSeconds = 900 // 默认订单超时时间,单位: s +) + +var defaultWxpayClient *WxpayClient + type WxpayClient struct { *wxpay.ClientV3 + AppId string `json:"app_id"` + NotifyUrl string `json:"notify_url"` } -func NewWxpayClient(cfg *configcenter.WxpayClientConfig) (cli *WxpayClient, err error) { - wxPayCli, err := wxpay.NewClientV3(cfg.MchId, cfg.SerialNo, cfg.ApiV3Key, cfg.PrivateKey) +func GetDefaultWxpayClient() *WxpayClient { + return defaultWxpayClient +} + +func Init(cfg *configcenter.WxpayClientConfig) (err error) { + // private key + bs, err := os.ReadFile(cfg.PrivateKeyPath) + if err != nil { + logger.Error("real PrivateKeyPath fail, cfg: %v, err: %v", util.ToJson(cfg), err) + return + } + privateKey := string(bs) + + wxpayCli, err := wxpay.NewClientV3(cfg.MchId, cfg.SerialNo, cfg.ApiV3Key, privateKey) if err != nil { logger.Error("NewClientV3 fail, cfg: %v, err: %v", util.ToJson(cfg), err) return } - cli = &WxpayClient{ - wxPayCli, + + defaultWxpayClient = &WxpayClient{ + ClientV3: wxpayCli, + AppId: cfg.AppId, + NotifyUrl: cfg.NotifyUrl, } return } + +// 验签 +func (c *WxpayClient) ParseNotify(req *http.Request) (notify *wxpay.V3DecryptResult, err error) { + notifyReq, err := wxpay.V3ParseNotify(req) + if err != nil { + logger.Error("V3ParseNotify fail, notifyReq: %v, err: %v", util.ToJson(notifyReq), err) + return + } + if notifyReq == nil { + logger.Error("V3ParseNotify nil, err: %v", err) + return + } + + notifyTmp, err := notifyReq.DecryptCipherText(string(c.ApiV3Key)) + if err != nil { + logger.Error("DecryptCipherText fail, notifyTmp: %v, err: %v", util.ToJson(notifyTmp), err) + return + } + if notifyTmp == nil { + logger.Error("DecryptCipherText nil, err: %v", err) + return + } + logger.Info("Wxpay ParseNotify, %v", util.ToJson(notifyTmp)) + + notify = notifyTmp + return +} + +// 微信支付 native支付 +type NativePayParam struct { + Description string + OutTradeNo string // 商家订单id,我们自己的订单id + TotalAmount int64 // 金额,单位:分 + TimeOutSeconds int // 订单有效时间,单位:秒 +} + +func (c *WxpayClient) NativePay(ctx context.Context, param *NativePayParam) (wxpayNativeParamStr string, err error) { + if param.TimeOutSeconds <= 0 { + param.TimeOutSeconds = DefaultOrderTimeoutSeconds + } + bm := gopay.BodyMap{ + "appid": c.AppId, + "description": param.Description, + "out_trade_no": param.OutTradeNo, + "time_expire": time.Now().Add(time.Second * time.Duration(param.TimeOutSeconds)).Format(time.RFC3339), + "notify_url": c.NotifyUrl, + "amount": gopay.BodyMap{ + "total": param.TotalAmount, + "currency": "CNY", + }, + } + resp, err := c.V3TransactionNative(ctx, bm) + if err != nil { + return + } + if resp.Code != wxpay.Success { + logger.Info("wxpay NativePay fail, code: %v, error: %v, response: %v", resp.Code, resp.Error, util.ToJson(resp.Response)) + return + } + wxpayNativeParamStr = resp.Response.CodeUrl + logger.Info("wxpay NativePay success, code: %v, error: %v, response: %v", resp.Code, resp.Error, util.ToJson(resp.Response)) + return +} diff --git a/library/payclients/wxpaycli/client_test.go b/library/payclients/wxpaycli/client_test.go new file mode 100644 index 00000000..c8874ccf --- /dev/null +++ b/library/payclients/wxpaycli/client_test.go @@ -0,0 +1,42 @@ +package wxpaycli + +import ( + "context" + "fmt" + "os" + "service/app/mix/conf" + "service/library/configcenter" + "service/library/idgenerator" + "testing" +) + +func TestMain(m *testing.M) { + cfg := new(conf.ConfigSt) + err := configcenter.LoadConfig("/Users/erwin/wishpalv2/service/etc/mix/mix-test.yaml", cfg) + if err != nil { + fmt.Printf("%v\n", err) + } + + err = Init(cfg.Wxpay) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + m.Run() +} + +func TestWxpayClient_NativePay(t *testing.T) { + cli := GetDefaultWxpayClient() + resp, err := cli.NativePay(context.Background(), &NativePayParam{ + Description: "测试哈哈", + OutTradeNo: idgenerator.GenOrderId(), + TotalAmount: 1, + TimeOutSeconds: 3600 * 24, + }) + if err != nil { + fmt.Println(err.Error()) + return + } + t.Log(fmt.Sprintf("%#v", resp)) + return +}