package srv_recharge import ( "context" "fmt" "hkdj-web-server/pub/common/log" "hkdj-web-server/pub/common/srvdef" "hkdj-web-server/pub/models/cointransmod" "hkdj-web-server/pub/models/rechargepackagemod" "hkdj-web-server/pub/models/usermod" "hkdj-web-server/pub/models/vipordermod" "hkdj-web-server/pub/payment" "hkdj-web-server/pub/pubdef" "time" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" ) // BillCallbackParam 嘉华支付回调参数 type BillCallbackParam struct { TradeNo string `json:"transNo" form:"transNo"` Oid string `json:"oid" form:"oid"` RealAmount int64 `json:"payMoney" form:"payMoney"` PaymentAt time.Time `json:"paymentAt" form:"paymentAt"` SuccessAt time.Time `json:"successAt" form:"successAt"` } // OnPayCenterCallback 统一支付回调入口 // 通过 vip_order 表查询订单,再按 OrderType 分支处理。 func OnPayCenterCallback(ctx *gin.Context, param *BillCallbackParam) *srvdef.SrvErr { log.Info("srv_recharge:OnPayCenterCallback", log.Any("tradeNo", param.TradeNo), log.Any("oid", param.Oid), log.Any("realAmount", param.RealAmount), ) if param.TradeNo == "" { return srvdef.New(srvdef.ErrParamError, "tradeNo 不能为空", "") } order, serr := vipordermod.FindOneVipOrder(ctx, bson.M{ vipordermod.FN.VipOrder.TradeNo: param.TradeNo, }) if serr != nil { log.Error("OnPayCenterCallback FindOneVipOrder failed", log.Any("tradeNo", param.TradeNo), log.E(serr)) return serr } if order.Status == pubdef.VipOrderSuccess { log.Info("OnPayCenterCallback already success", log.Any("tradeNo", param.TradeNo)) return nil } if order.Oid != "" && order.Oid != param.Oid { return srvdef.New(srvdef.ErrParamError, fmt.Sprintf("oid 不匹配: %s vs %s", order.Oid, param.Oid), "") } paymentAt := resolvePaymentAt(param) if serr = vipordermod.UpdateOrderSuccess(ctx, param.TradeNo, param.Oid, param.RealAmount, paymentAt); serr != nil { log.Error("OnPayCenterCallback UpdateOrderSuccess failed", log.Any("tradeNo", param.TradeNo), log.E(serr)) return serr } orderSnap := *order realAmount := param.RealAmount oid := param.Oid switch order.OrderType { case vipordermod.OrderTypeCoin: go handleCoinOrderSuccess(orderSnap, oid, realAmount) default: go handleVipOrderSuccess(orderSnap, oid, realAmount) } return nil } func handleVipOrderSuccess(order vipordermod.VipOrder, oid string, realAmount int64) { ctx := context.Background() _, _ = oid, realAmount pkg, err := findVipPackage(ctx, order.CardId) if err != nil { log.Error("handleVipOrderSuccess find package failed", log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err)) return } expire, err := payment.ActivateVip(ctx, order.UserId, pkg) if err != nil { log.Error("handleVipOrderSuccess ActivateVip failed, retrying", log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err)) time.Sleep(3 * time.Second) expire, err = payment.ActivateVip(ctx, order.UserId, pkg) if err != nil { log.Error("handleVipOrderSuccess ActivateVip retry failed", log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err)) } } log.Info("handleVipOrderSuccess done", log.Any("userId", order.UserId), log.Any("expire", expire)) if pkg.GiftCoin > 0 { _ = payment.WriteCoinTransByUser(ctx, payment.WriteCoinTransOpt{ UserId: order.UserId, FlowDir: cointransmod.FlowIn, TranType: cointransmod.TranTypeVipGiftCoin, Amount: pkg.GiftCoin, OrderNo: order.TradeNo, Remark: payment.CoinTransRemark(cointransmod.TranTypeVipGiftCoin, pkg.Name), }) } } func handleCoinOrderSuccess(order vipordermod.VipOrder, oid string, realAmount int64) { ctx := context.Background() _, _ = oid, realAmount if err := addUserCoins(ctx, order.UserId, order.Coin); err != nil { log.Error("handleCoinOrderSuccess addUserCoins failed, retrying", log.Any("userId", order.UserId), log.Any("coin", order.Coin), log.E(err)) time.Sleep(3 * time.Second) if err = addUserCoins(ctx, order.UserId, order.Coin); err != nil { log.Error("handleCoinOrderSuccess addUserCoins retry failed", log.Any("userId", order.UserId), log.Any("coin", order.Coin), log.E(err)) } } log.Info("handleCoinOrderSuccess done", log.Any("userId", order.UserId), log.Any("coin", order.Coin)) writeCoinOrderTrans(ctx, order) } func writeCoinOrderTrans(ctx context.Context, order vipordermod.VipOrder) { baseCoin, giftCoin := resolveCoinOrderSplit(ctx, order) balanceAfter, ok := coinOrderBalanceAfter(ctx, order.UserId) if !ok || giftCoin <= 0 { _ = payment.WriteCoinTransByUser(ctx, payment.WriteCoinTransOpt{ UserId: order.UserId, FlowDir: cointransmod.FlowIn, TranType: cointransmod.TranTypeCoinRecharge, Amount: order.Coin, OrderNo: order.TradeNo, Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinRecharge, ""), }) return } if baseCoin > 0 { _ = payment.WriteCoinTrans(ctx, payment.WriteCoinTransOpt{ UserId: order.UserId, FlowDir: cointransmod.FlowIn, TranType: cointransmod.TranTypeCoinRecharge, Amount: baseCoin, OrderNo: order.TradeNo, Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinRecharge, ""), }, balanceAfter-giftCoin) } _ = payment.WriteCoinTrans(ctx, payment.WriteCoinTransOpt{ UserId: order.UserId, FlowDir: cointransmod.FlowIn, TranType: cointransmod.TranTypeCoinGift, Amount: giftCoin, OrderNo: order.TradeNo, Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinGift, ""), }, balanceAfter) } func resolveCoinOrderSplit(ctx context.Context, order vipordermod.VipOrder) (int64, int64) { if order.BaseCoin+order.GiftCoin == order.Coin && (order.BaseCoin > 0 || order.GiftCoin > 0) { return order.BaseCoin, order.GiftCoin } pkg, _ := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{ rechargepackagemod.FN.RechargePackage.Id: order.CoinCfgId, rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeCoin, }) if pkg != nil && pkg.BaseCoin+pkg.GiftCoin == order.Coin { return pkg.BaseCoin, pkg.GiftCoin } return order.Coin, 0 } func coinOrderBalanceAfter(ctx context.Context, userId int64) (int64, bool) { user, serr := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: userId}) if serr != nil || user == nil { return 0, false } return user.Recharge, true } func resolvePaymentAt(param *BillCallbackParam) time.Time { if !param.PaymentAt.IsZero() { return param.PaymentAt } if !param.SuccessAt.IsZero() { return param.SuccessAt } return time.Now() } func findVipPackage(ctx context.Context, packageId int64) (*rechargepackagemod.RechargePackage, *srvdef.SrvErr) { pkg, serr := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{ rechargepackagemod.FN.RechargePackage.Id: packageId, rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeVip, }) if serr != nil || pkg == nil { return nil, srvdef.New(srvdef.ErrNoData, "会员套餐不存在", "") } return pkg, nil } func addUserCoins(ctx context.Context, userId int64, coin int64) *srvdef.SrvErr { return usermod.UpdateOneUser(ctx, bson.M{usermod.FN.User.Id: userId}, bson.M{ "$inc": bson.M{usermod.FN.User.Recharge: coin}, "$set": bson.M{usermod.FN.User.UpdatedAt: time.Now()}, }, ) }