package service import ( "context" "errors" "fmt" log "github.com/sirupsen/logrus" "github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/core/option" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/native" "github.com/wechatpay-apiv3/wechatpay-go/utils" "gorm.io/gorm" "time" "youngee_b_api/app/dao" "youngee_b_api/app/entity" "youngee_b_api/app/util" "youngee_b_api/app/vo" ) type RechargeService struct{} // 充值管理——对公转账 func (s RechargeService) TransferToPublic(param *vo.RechargeTransferParam) (*string, error) { var rechargeId string var phone string //if param.SubAccountId == 0 { // rechargeId = util.MakeRechargeId(param.EnterpriseId) // phone, _ = dao.EnterpriseDao{}.GetEnterprisePhone(param.EnterpriseId) //} else { // rechargeId = util.MakeRechargeId(strconv.FormatInt(param.SubAccountId, 10)) // phone, _ = dao.SubAccountDao{}.GetSubAccountPhone(param.SubAccountId) //} rechargeId = util.GenerateDateRelatedUUID(16) phone, _ = dao.EnterpriseDao{}.GetEnterprisePhone(param.EnterpriseId) rechargeRecord := entity.RechargeRecord{ RechargeID: rechargeId, EnterpriseID: param.EnterpriseId, SubAccountId: param.SubAccountId, RechargeAmount: param.Amount, TransferVoucherUrl: param.TransferVoucherUrl, Phone: phone, RechargeMethod: 1, Status: 1, CommitAt: time.Now(), } err := dao.RechargeRecordDao{}.Insert(&rechargeRecord) if err != nil { return nil, err } return &rechargeId, nil } // 获取微信支付CodeUrl func (s RechargeService) NativeApiServicePrepay(enterpriseId string, subAccountId int64, tradeId string, amount int64) (string, *time.Time, error) { var ( mchID string = "1615933939" // 商户号 mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号 mchAPIv3Key string = "V10987654321younggee12345678910V" // 商户APIv3密钥,用于加密和解密平台证书 ) // 使用 utils 提供的函数从本地文件中加载商户私钥 mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem") // 商户私钥,用于生成请求的签名 if err != nil { log.Print("load merchant private key error") } ctx := context.Background() // 使用商户私钥等初始化 微信支付client,并使它具有自动定时获取微信支付平台证书的能力 opts := []core.ClientOption{ option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key), } client, err := core.NewClient(ctx, opts...) if err != nil { log.Printf("new wechat pay client err: %s", err) } // 采用 Native 支付方式 svc := native.NativeApiService{Client: client} // 发送请求 timeExpire := time.Now().Add(5 * time.Minute) resp, result, err := svc.Prepay(ctx, native.PrepayRequest{ Appid: core.String("wxac396a3be7a16844"), Mchid: core.String("1615933939"), Description: core.String("样叽微信支付充值"), OutTradeNo: core.String(tradeId), TimeExpire: core.Time(timeExpire), Attach: core.String("微信支付充值"), NotifyUrl: core.String("https://www.weixin.qq.com/wxpay/pay.php"), SupportFapiao: core.Bool(true), Amount: &native.Amount{ Currency: core.String("CNY"), Total: core.Int64(amount), }, }, ) if err != nil { // 处理错误 log.Printf("call Prepay err:%s", err) return "", nil, err } else { // 处理返回结果 log.Printf("status=%d resp=%s", result.Response.StatusCode, resp) log.Println("codeUrl:", *resp.CodeUrl) } // 生成充值记录 phone, _ := dao.EnterpriseDao{}.GetEnterprisePhone(enterpriseId) rechargeRecord := entity.RechargeRecord{ RechargeID: tradeId, EnterpriseID: enterpriseId, SubAccountId: subAccountId, RechargeAmount: float64(amount) / 100, Phone: phone, RechargeMethod: 2, Status: 1, CommitAt: time.Now(), RefuseAt: timeExpire, } err = dao.RechargeRecordDao{}.Insert(&rechargeRecord) if err != nil { return "", nil, err } return *resp.CodeUrl, &timeExpire, nil } // 根据交易id查询微信是否扫码付款 // https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml func (s RechargeService) QueryOrderByTradeId(enterpriseId string, subAccountId int64, tradeId string) (tradeState string, err error) { var ( mchID string = "1615933939" // 商户号 mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号 mchAPIv3Key string = "V10987654321younggee12345678910V" // 商户APIv3密钥 ) // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem") if err != nil { log.Print("load merchant private key error") } ctx := context.Background() // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力 opts := []core.ClientOption{ option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key), } client, err := core.NewClient(ctx, opts...) if err != nil { log.Printf("new wechat pay client err: %s", err) } svc := native.NativeApiService{Client: client} resp, result, err := svc.QueryOrderByOutTradeNo(ctx, native.QueryOrderByOutTradeNoRequest{ OutTradeNo: core.String(tradeId), Mchid: core.String("1615933939"), }, ) fmt.Printf("支付 %+v\n", resp) if err != nil { // 处理错误 log.Printf("call QueryOrderByOutTradeNo err: %s", err) return "", err } else { // 处理返回结果 log.Printf("status=%d resp=%s", result.Response.StatusCode, resp) } // 更新充值记录/账户金额相关信息 if "SUCCESS" == *resp.TradeState { payTime := resp.SuccessTime t, _ := time.Parse("2006-01-02 15:04:05", *payTime) err = dao.RechargeRecordDao{}.UpdateRechargeStatus(tradeId, 2, t) amount := float64(*resp.Amount.Total) / 100 _, err = dao.EnterpriseDao{}.UpdateEnterpriseBalance(enterpriseId, amount) } else if "CLOSED" == *resp.TradeState { err = dao.RechargeRecordDao{}.UpdateRechargeStatus(tradeId, 3, time.Now()) } return *resp.TradeState, nil } // 余额管理——总金额、可用余额、冻结金额 func (s RechargeService) ShowBalance(param *vo.BalanceParam) (*vo.ReBalanceShow, error) { reBalanceShow := new(vo.ReBalanceShow) enterprise, err := dao.EnterpriseDao{}.GetEnterpriseInfo(param.EnterpriseId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return reBalanceShow, nil } else { return nil, err } } reBalanceShow.TotalBalance = enterprise.Balance reBalanceShow.AvailBalance = enterprise.AvailableBalance reBalanceShow.FrozenBalance = enterprise.FrozenBalance return reBalanceShow, nil } // 余额管理——冻结记录 func (s RechargeService) FrozenInfoList(param *vo.BalanceParam) (vo.ResultVO, error) { if param.Page <= 0 { param.Page = 1 } if param.PageSize <= 0 { param.PageSize = 10 } var result vo.ResultVO var reBalanceShows []*vo.ReFrozenInfo var selectionInfos []*entity.SelectionInfo var projects []*entity.Project if param.FrozenState == 1 { // 电商带货 selectionInfos, _ = dao.SelectionInfoDAO{}.GetSelectionFrozenList(param.EnterpriseId) // 品牌种草 projects, _ = dao.ProjectDAO{}.GetProjectFrozenList(param.EnterpriseId) // 本地生活 } else { // 电商带货 selectionInfos, _ = dao.SelectionInfoDAO{}.GetSelectionFrozenCancelList(param.EnterpriseId) // 品牌种草 projects, _ = dao.ProjectDAO{}.GetProjectFrozenCancelList(param.EnterpriseId) // 本地生活 } // 汇总结果 for _, selection := range selectionInfos { // 获取商品详情字段 var creatorName string var productName string var productPrice float64 var mainImage string if selection.SubAccountId == 0 { enterprise, err := dao.EnterpriseDao{}.GetEnterprise(selection.EnterpriseID) if err == nil && enterprise != nil { creatorName = enterprise.BusinessName } } else { subAccount, err := dao.SubAccountDao{}.GetSubAccount(selection.SubAccountId) if err == nil && subAccount != nil { creatorName = subAccount.SubAccountName } } product, err := dao.ProductDAO{}.GetProductByID(selection.ProductID) if err == nil && product != nil { productName = product.ProductName productPrice = product.ProductPrice } mainImage, err = dao.ProductPhotoDAO{}.GetMainPhotoByProductID(selection.ProductID) // 电商带货汇总 reBalanceShow := &vo.ReFrozenInfo{ ProductId: selection.ProductID, MainImage: mainImage, ProductName: productName, ProductPrice: productPrice, Platform: selection.Platform, CreatorName: creatorName, TaskType: "电商带货", FrozenBalance: selection.EstimatedCost, FrozenTime: selection.PayAt.Format("2006-01-02 15:04:05"), EnterpriseId: selection.EnterpriseID, SubAccountId: selection.SubAccountId, TaskId: selection.SelectionID, } if param.FrozenState == 2 { reBalanceShow.FrozenCancelBalance = selection.SettlementAmount } reBalanceShows = append(reBalanceShows, reBalanceShow) } for _, project := range projects { // 获取商品详情字段 var creatorName string var productName string var productPrice float64 var mainImage string if project.SubAccountId == 0 { enterprise, err := dao.EnterpriseDao{}.GetEnterprise(project.EnterpriseID) if err == nil && enterprise != nil { creatorName = enterprise.BusinessName } } else { subAccount, err := dao.SubAccountDao{}.GetSubAccount(project.SubAccountId) if err == nil && subAccount != nil { creatorName = subAccount.SubAccountName } } product, err := dao.ProductDAO{}.GetProductByID(project.ProductID) if err == nil && product != nil { productName = product.ProductName productPrice = product.ProductPrice } mainImage, err = dao.ProductPhotoDAO{}.GetMainPhotoByProductID(project.ProductID) // 电商带货汇总 reBalanceShow := &vo.ReFrozenInfo{ ProductId: project.ProductID, MainImage: mainImage, ProductName: productName, ProductPrice: productPrice, Platform: project.ProjectPlatform, CreatorName: creatorName, TaskType: "品牌种草", FrozenBalance: project.NeedPay, FrozenTime: project.PayAt.Format("2006-01-02 15:04:05"), EnterpriseId: project.EnterpriseID, SubAccountId: project.SubAccountId, TaskId: project.ProjectId, } if param.FrozenState == 2 { reBalanceShow.FrozenCancelBalance = project.SettlementAmount } reBalanceShows = append(reBalanceShows, reBalanceShow) } startIndex := (param.Page - 1) * param.PageSize endIndex := startIndex + param.PageSize // 分页 if startIndex >= len(reBalanceShows) { result = vo.ResultVO{ Page: param.Page, PageSize: param.PageSize, Total: int64(len(reBalanceShows)), Data: nil, } return result, nil } if endIndex > len(reBalanceShows) { endIndex = len(reBalanceShows) } result = vo.ResultVO{ Page: param.Page, PageSize: param.PageSize, Total: int64(len(reBalanceShows)), Data: reBalanceShows[startIndex:endIndex], } return result, nil } // 余额管理——冻结记录-角标 func (t RechargeService) FrozenInfoCount(param *vo.BalanceParam) map[string]int64 { res := make(map[string]int64) var blockNum1 int64 var blockNum2 int64 var blockReleaseNum1 int64 var blockReleaseNum2 int64 // 冻结中 dao.Db.Model(entity.SelectionInfo{}).Where(fmt.Sprintf("enterprise_id = ? AND (selection_status between 5 and 6) "), param.EnterpriseId).Count(&blockNum1) dao.Db.Model(entity.Project{}).Where(fmt.Sprintf("enterprise_id = ? AND project_type = ? AND (project_status between 7 and 8) "), param.EnterpriseId, 1).Count(&blockNum2) // 冻结解除 dao.Db.Model(entity.SelectionInfo{}).Where(fmt.Sprintf("enterprise_id = ? AND (selection_status between 7 and 8) "), param.EnterpriseId).Count(&blockReleaseNum1) dao.Db.Model(entity.Project{}).Where(fmt.Sprintf("enterprise_id = ? AND project_type = ? AND (project_status between 9 and 10) "), param.EnterpriseId, 1).Count(&blockReleaseNum2) res["blockNum"] = blockNum1 + blockNum2 res["blockReleaseNum"] = blockReleaseNum1 + blockReleaseNum2 return res } // 充值管理——累计充值金额、确认中金额 func (s RechargeService) ShowRecharge(param *vo.RechargeParam) (*vo.ReRechargeShow, error) { reRechargeShow := new(vo.ReRechargeShow) confirmingRecharge, _ := dao.RechargeRecordDao{}.GetRechargeAmount(param.EnterpriseId, 1) totalRecharge, _ := dao.RechargeRecordDao{}.GetRechargeAmount(param.EnterpriseId, 2) reRechargeShow.ConfirmingRecharge = confirmingRecharge reRechargeShow.TotalRecharge = totalRecharge return reRechargeShow, nil } // 充值管理——充值记录 func (s RechargeService) RechargeInfoList(param *vo.RechargeParam) (vo.ResultVO, error) { if param.Page <= 0 { param.Page = 1 } if param.PageSize <= 0 { param.PageSize = 10 } var result vo.ResultVO var reRechargeInfos []*vo.ReRechargeInfo rechargeRecords, total, err := dao.RechargeRecordDao{}.RechargeInfoList(param) if err != nil { return result, err } for _, rechargeRecord := range rechargeRecords { var creatorName string if rechargeRecord.SubAccountId == 0 { enterprise, err := dao.EnterpriseDao{}.GetEnterprise(rechargeRecord.EnterpriseID) if err == nil && enterprise != nil { creatorName = enterprise.BusinessName } } else { subAccount, err := dao.SubAccountDao{}.GetSubAccount(rechargeRecord.SubAccountId) if err == nil && subAccount != nil { creatorName = subAccount.SubAccountName } } reRechargeInfo := &vo.ReRechargeInfo{ RechargeId: rechargeRecord.RechargeID, CreatorName: creatorName, RechargeAmount: rechargeRecord.RechargeAmount, RechargeMethod: rechargeRecord.RechargeMethod, TransferVoucherUrl: rechargeRecord.TransferVoucherUrl, } if param.RechargeState == 1 { reRechargeInfo.CommitAt = rechargeRecord.CommitAt.Format("2006-01-02 15:04:05") } else if param.RechargeState == 2 { reRechargeInfo.ConfirmAt = rechargeRecord.ConfirmAt.Format("2006-01-02 15:04:05") } else if param.RechargeState == 3 { reRechargeInfo.RefuseAt = rechargeRecord.RefuseAt.Format("2006-01-02 15:04:05") reRechargeInfo.FailReason = rechargeRecord.FailReason } reRechargeInfos = append(reRechargeInfos, reRechargeInfo) } //rechargingNum, _ := dao.RechargeRecordDao{}.RechargeStatusCount(param.EnterpriseId, 1) //rechargedNum, _ := dao.RechargeRecordDao{}.RechargeStatusCount(param.EnterpriseId, 2) //failNum, _ := dao.RechargeRecordDao{}.RechargeStatusCount(param.EnterpriseId, 3) resMap := make(map[string]interface{}) //resMap["rechargingNum"] = rechargingNum //resMap["rechargedNum"] = rechargedNum //resMap["failNum"] = failNum resMap["reRechargeInfos"] = reRechargeInfos result = vo.ResultVO{ Page: param.Page, PageSize: param.PageSize, Total: total, Data: resMap, } return result, nil } // 余额管理——冻结记录-角标 func (t RechargeService) RechargeInfoCount(param *vo.RechargeParam) map[string]int64 { res := make(map[string]int64) var rechargeConfirming int64 var recharged int64 var rechargeFail int64 dao.Db.Model(&entity.RechargeRecord{}).Where("enterprise_id = ? AND status = ?", param.EnterpriseId, 1).Count(&rechargeConfirming) dao.Db.Model(&entity.RechargeRecord{}).Where("enterprise_id = ? AND status = ?", param.EnterpriseId, 2).Count(&recharged) dao.Db.Model(&entity.RechargeRecord{}).Where("enterprise_id = ? AND status = ?", param.EnterpriseId, 3).Count(&rechargeFail) res["rechargeConfirming"] = rechargeConfirming res["recharged"] = recharged res["rechargeFail"] = rechargeFail return res } // 财务待办——充值确认中金额 func (s RechargeService) GetFinance(param *vo.CommonParam) (float64, error) { confirmingRecharge, err := dao.RechargeRecordDao{}.GetRechargeAmount(param.EnterpriseId, 1) if err != nil { return 0, err } return confirmingRecharge, nil }