init: web_hgcg_server 初始化提交
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package web_recharge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/vipordermod"
|
||||
"videoweb/pub/payCenter"
|
||||
"videoweb/pub/pubdef"
|
||||
srv_recharge "videoweb/web/service/srv_recharge"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// newPayCenterCallbackDeps 将查单和入账边界集中,便于用内存替身验证回调不会错误入账。
|
||||
type newPayCenterCallbackDeps struct {
|
||||
findOrder func(context.Context, string) (*vipordermod.VipOrder, *srvdef.SrvErr)
|
||||
queryOrder func(context.Context, string) (payCenter.QueryOrderInfo, error)
|
||||
applyRecharge func(*gin.Context, *srv_recharge.BillCallbackParam) *srvdef.SrvErr
|
||||
}
|
||||
|
||||
// NewPayCenterRchgCallBack 接收新支付中心签名充值通知。
|
||||
// @Tags 支付回调
|
||||
// @Summary 新支付中心充值回调
|
||||
// @Description 校验商户号、MD5签名、金额及平台订单号,查单取得支付时间后按原流程入账;重复成功通知直接应答成功。
|
||||
// @Accept json
|
||||
// @Produce plain
|
||||
// @Param request body payCenter.RechargeCallbackResp true "支付中心签名回调"
|
||||
// @Success 200 {string} string "success"
|
||||
// @Failure 400 {string} string "fail"
|
||||
// @Router /api/web/third/newpaycenter/recharge/callback [POST]
|
||||
func NewPayCenterRchgCallBack(ctx *gin.Context) {
|
||||
deps := newPayCenterCallbackDeps{
|
||||
findOrder: func(c context.Context, tradeNo string) (*vipordermod.VipOrder, *srvdef.SrvErr) {
|
||||
return vipordermod.FindOneVipOrder(c, bson.M{vipordermod.FN.VipOrder.TradeNo: tradeNo})
|
||||
},
|
||||
queryOrder: func(c context.Context, tradeNo string) (payCenter.QueryOrderInfo, error) {
|
||||
return (&payCenter.Recharge{Ctx: c, TradeNo: tradeNo}).QueryOrder(c)
|
||||
},
|
||||
applyRecharge: srv_recharge.OnPayCenterCallback,
|
||||
}
|
||||
handleNewPayCenterRchgCallBack(ctx, deps)
|
||||
}
|
||||
|
||||
func handleNewPayCenterRchgCallBack(ctx *gin.Context, deps newPayCenterCallbackDeps) {
|
||||
if err := processNewPayCenterRchgCallBack(ctx, deps); err != nil {
|
||||
log.Error("【/api/web/third/newpaycenter/recharge/callback】充值回调处理失败", log.E(err))
|
||||
ctx.String(http.StatusBadRequest, "fail")
|
||||
return
|
||||
}
|
||||
ctx.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
func processNewPayCenterRchgCallBack(ctx *gin.Context, deps newPayCenterCallbackDeps) error {
|
||||
var callback payCenter.RechargeCallbackResp
|
||||
bindErr := ctx.ShouldBindBodyWith(&callback, binding.JSON)
|
||||
bodyValue, _ := ctx.Get(gin.BodyBytesKey)
|
||||
rawBody, _ := bodyValue.([]byte)
|
||||
log.Info("【/api/web/third/newpaycenter/recharge/callback】收到充值回调",
|
||||
log.Any("tradeNo", callback.TradeNo),
|
||||
log.Any("requestBody", string(rawBody)),
|
||||
log.Any("clientIP", ctx.ClientIP()),
|
||||
)
|
||||
if bindErr != nil {
|
||||
return fmt.Errorf("回调参数解析失败: %w", bindErr)
|
||||
}
|
||||
request := &payCenter.Recharge{TradeNo: callback.TradeNo}
|
||||
if request.GetMercID() == "" || request.GetAppSecret() == "" {
|
||||
return errors.New("新支付中心回调配置缺少 mercID 或 mercSecret")
|
||||
}
|
||||
if callback.Code != http.StatusOK || callback.MercID != request.GetMercID() {
|
||||
return fmt.Errorf("回调状态或商户不匹配,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
signSource := strconv.Itoa(callback.Code) + callback.MercID + callback.OID + callback.PayMoney + callback.TradeNo + request.GetAppSecret()
|
||||
if !payCenter.VerifySign(callback.Sign, signSource) {
|
||||
return fmt.Errorf("回调验签失败,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
amount, err := payCenter.ParseYuanToFen(callback.PayMoney)
|
||||
if err != nil || amount <= 0 {
|
||||
return fmt.Errorf("回调金额格式错误或非正数,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
order, serr := deps.findOrder(ctx, callback.TradeNo)
|
||||
if serr != nil {
|
||||
return fmt.Errorf("查找本地充值订单失败,tradeNo=%s: %w", callback.TradeNo, serr)
|
||||
}
|
||||
if order == nil || order.TradeNo != callback.TradeNo {
|
||||
return fmt.Errorf("本地充值订单不存在,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
amountMismatch := amount != order.Amount
|
||||
oidMismatch := order.Oid != "" && callback.OID != order.Oid
|
||||
if amountMismatch || oidMismatch {
|
||||
log.Error("【/api/web/third/newpaycenter/recharge/callback】回调金额或支付中心订单号不匹配",
|
||||
log.Any("tradeNo", callback.TradeNo),
|
||||
log.Any("userId", order.UserId),
|
||||
log.Any("callbackPayMoneyYuan", callback.PayMoney),
|
||||
log.Any("callbackAmountFen", amount),
|
||||
log.Any("orderAmountFen", order.Amount),
|
||||
log.Any("callbackOid", callback.OID),
|
||||
log.Any("orderOid", order.Oid),
|
||||
log.Any("orderStatus", order.Status),
|
||||
log.Any("amountMismatch", amountMismatch),
|
||||
log.Any("oidMismatch", oidMismatch),
|
||||
)
|
||||
return fmt.Errorf("回调金额或支付中心订单号不匹配,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
if order.Status == pubdef.VipOrderSuccess {
|
||||
log.Info("【/api/web/third/newpaycenter/recharge/callback】重复成功通知", log.Any("tradeNo", callback.TradeNo))
|
||||
return nil
|
||||
}
|
||||
info, err := deps.queryOrder(ctx, callback.TradeNo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("支付中心查单失败,tradeNo=%s: %w", callback.TradeNo, err)
|
||||
}
|
||||
if (info.MercID != "" && info.MercID != callback.MercID) || (info.TradeNo != "" && info.TradeNo != callback.TradeNo) {
|
||||
return fmt.Errorf("支付中心查单返回了其他商户或订单,tradeNo=%s", callback.TradeNo)
|
||||
}
|
||||
paymentAt, err := time.ParseInLocation(time.RFC3339, info.PayTime, time.Local)
|
||||
if err != nil {
|
||||
return fmt.Errorf("支付中心支付时间无效,tradeNo=%s: %w", callback.TradeNo, err)
|
||||
}
|
||||
param := &srv_recharge.BillCallbackParam{
|
||||
TradeNo: callback.TradeNo,
|
||||
Oid: callback.OID,
|
||||
RealAmount: amount,
|
||||
PaymentAt: paymentAt,
|
||||
}
|
||||
if serr := deps.applyRecharge(ctx, param); serr != nil {
|
||||
return fmt.Errorf("本地充值入账失败,tradeNo=%s: %w", callback.TradeNo, serr)
|
||||
}
|
||||
log.Info("【/api/web/third/newpaycenter/recharge/callback】充值入账成功", log.Any("tradeNo", callback.TradeNo), log.Any("userId", order.UserId), log.Any("amount", amount))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package web_recharge
|
||||
|
||||
import (
|
||||
"videoweb/pub/common"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/web/service/srv_viporder"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RechargeSummary 统计指定时间范围内的充值汇总
|
||||
// @Tags wbapi统计
|
||||
// @Summary 统计充值汇总
|
||||
// @Description 按北京时间的开始/结束时间汇总支付成功订单金额与笔数
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param startTime query string true "开始时间,北京时间,支持 2006-01-02、2006-01-02 15:04:05、RFC3339"
|
||||
// @Param endTime query string true "结束时间,北京时间,支持 2006-01-02、2006-01-02 15:04:05、RFC3339"
|
||||
// @Success 200 {object} srv_viporder.RechargeSummaryResp "成功"
|
||||
// @Failure 400 {string} string "失败"
|
||||
// @Router /wbapi/91djweb/recharge/summary [GET]
|
||||
func RechargeSummary(ctx *gin.Context) {
|
||||
var req srv_viporder.RechargeSummaryReq
|
||||
if err := ctx.ShouldBindQuery(&req); err != nil {
|
||||
common.ServerJSON(ctx, srvdef.ErrParamError, err.Error())
|
||||
return
|
||||
}
|
||||
resp, serr := srv_viporder.GetRechargeSummary(ctx, &req)
|
||||
if serr != nil {
|
||||
common.ServerJSON(ctx, serr, nil)
|
||||
return
|
||||
}
|
||||
common.ServerJSON(ctx, srvdef.Success, resp)
|
||||
}
|
||||
Reference in New Issue
Block a user