recharge_service.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. log "github.com/sirupsen/logrus"
  7. "github.com/wechatpay-apiv3/wechatpay-go/core"
  8. "github.com/wechatpay-apiv3/wechatpay-go/core/option"
  9. "github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
  10. "github.com/wechatpay-apiv3/wechatpay-go/utils"
  11. "gorm.io/gorm"
  12. "time"
  13. "youngee_b_api/app/dao"
  14. "youngee_b_api/app/entity"
  15. "youngee_b_api/app/util"
  16. "youngee_b_api/app/vo"
  17. )
  18. type RechargeService struct{}
  19. // 充值管理——对公转账
  20. func (s RechargeService) TransferToPublic(param *vo.RechargeTransferParam) (*string, error) {
  21. var rechargeId string
  22. var phone string
  23. //if param.SubAccountId == 0 {
  24. // rechargeId = util.MakeRechargeId(param.EnterpriseId)
  25. // phone, _ = dao.EnterpriseDao{}.GetEnterprisePhone(param.EnterpriseId)
  26. //} else {
  27. // rechargeId = util.MakeRechargeId(strconv.FormatInt(param.SubAccountId, 10))
  28. // phone, _ = dao.SubAccountDao{}.GetSubAccountPhone(param.SubAccountId)
  29. //}
  30. rechargeId = util.GenerateDateRelatedUUID()
  31. phone, _ = dao.EnterpriseDao{}.GetEnterprisePhone(param.EnterpriseId)
  32. rechargeRecord := entity.RechargeRecord{
  33. RechargeID: rechargeId,
  34. EnterpriseID: param.EnterpriseId,
  35. SubAccountId: param.SubAccountId,
  36. RechargeAmount: param.Amount,
  37. TransferVoucherUrl: param.TransferVoucherUrl,
  38. Phone: phone,
  39. RechargeMethod: 1,
  40. Status: 1,
  41. InvoiceStatus: 1,
  42. CommitAt: time.Now(),
  43. }
  44. err := dao.RechargeRecordDao{}.Insert(&rechargeRecord)
  45. if err != nil {
  46. return nil, err
  47. }
  48. return &rechargeId, nil
  49. }
  50. // 获取微信支付CodeUrl
  51. func (s RechargeService) NativeApiServicePrepay(tradeId string, amount int64) (string, *time.Time, error) {
  52. var (
  53. mchID string = "1615933939" // 商户号
  54. mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号
  55. mchAPIv3Key string = "V10987654321younggee12345678910V" // 商户APIv3密钥,用于加密和解密平台证书
  56. )
  57. fmt.Println("充值的金额为:", amount)
  58. // 使用 utils 提供的函数从本地文件中加载商户私钥
  59. mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem") // 商户私钥,用于生成请求的签名
  60. if err != nil {
  61. log.Print("load merchant private key error")
  62. }
  63. ctx := context.Background()
  64. // 使用商户私钥等初始化 微信支付client,并使它具有自动定时获取微信支付平台证书的能力
  65. opts := []core.ClientOption{
  66. option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
  67. }
  68. client, err := core.NewClient(ctx, opts...)
  69. if err != nil {
  70. log.Printf("new wechat pay client err: %s", err)
  71. }
  72. // 采用 Native 支付方式
  73. svc := native.NativeApiService{Client: client}
  74. // 发送请求
  75. timeExpire := time.Now().Add(10 * time.Minute)
  76. resp, result, err := svc.Prepay(ctx,
  77. native.PrepayRequest{
  78. Appid: core.String("wxac396a3be7a16844"),
  79. Mchid: core.String("1615933939"),
  80. Description: core.String("样叽微信支付充值"),
  81. OutTradeNo: core.String(tradeId),
  82. TimeExpire: core.Time(timeExpire),
  83. Attach: core.String("微信支付充值"),
  84. NotifyUrl: core.String("https://www.weixin.qq.com/wxpay/pay.php"),
  85. SupportFapiao: core.Bool(true),
  86. Amount: &native.Amount{
  87. Currency: core.String("CNY"),
  88. Total: core.Int64(amount),
  89. },
  90. },
  91. )
  92. if err != nil {
  93. // 处理错误
  94. log.Printf("call Prepay err:%s", err)
  95. return "", nil, err
  96. } else {
  97. // 处理返回结果
  98. log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
  99. log.Println("codeUrl:", *resp.CodeUrl)
  100. }
  101. return *resp.CodeUrl, &timeExpire, nil
  102. }
  103. // 根据交易id查询微信是否扫码付款
  104. // https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml
  105. func (s RechargeService) QueryOrderByTradeId(tradeId string) (tradeState string, err error) {
  106. var (
  107. mchID string = "1615933939" // 商户号
  108. mchCertificateSerialNumber string = "33DDFEC51BF5412F663B9B56510FD567B625FC68" // 商户证书序列号
  109. mchAPIv3Key string = "V10987654321younggee12345678910V" // 商户APIv3密钥
  110. )
  111. // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
  112. mchPrivateKey, err := utils.LoadPrivateKeyWithPath("./apiclient_key.pem")
  113. if err != nil {
  114. log.Print("load merchant private key error")
  115. }
  116. ctx := context.Background()
  117. // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
  118. opts := []core.ClientOption{
  119. option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
  120. }
  121. client, err := core.NewClient(ctx, opts...)
  122. if err != nil {
  123. log.Printf("new wechat pay client err: %s", err)
  124. }
  125. svc := native.NativeApiService{Client: client}
  126. resp, result, err := svc.QueryOrderByOutTradeNo(ctx,
  127. native.QueryOrderByOutTradeNoRequest{
  128. OutTradeNo: core.String(tradeId),
  129. Mchid: core.String("1615933939"),
  130. },
  131. )
  132. fmt.Printf("支付 %+v\n", resp)
  133. if err != nil {
  134. // 处理错误
  135. log.Printf("call QueryOrderByOutTradeNo err: %s", err)
  136. return "", err
  137. } else {
  138. // 处理返回结果
  139. log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
  140. }
  141. fmt.Printf("支付状态 %+v\n", *resp.TradeState)
  142. return *resp.TradeState, nil
  143. }
  144. // 余额管理——总金额、可用余额、冻结金额
  145. func (s RechargeService) ShowBalance(param *vo.BalanceParam) (*vo.ReBalanceShow, error) {
  146. reBalanceShow := new(vo.ReBalanceShow)
  147. enterprise, err := dao.EnterpriseDao{}.GetEnterpriseInfo(param.EnterpriseId)
  148. if err != nil {
  149. if errors.Is(err, gorm.ErrRecordNotFound) {
  150. return reBalanceShow, nil
  151. } else {
  152. return nil, err
  153. }
  154. }
  155. reBalanceShow.TotalBalance = enterprise.Balance
  156. reBalanceShow.AvailBalance = enterprise.AvailableBalance
  157. reBalanceShow.FrozenBalance = enterprise.FrozenBalance
  158. return reBalanceShow, nil
  159. }
  160. // 余额管理——冻结记录
  161. func (s RechargeService) FrozenInfoList(param *vo.BalanceParam) (vo.ResultVO, error) {
  162. if param.Page <= 0 {
  163. param.Page = 1
  164. }
  165. if param.PageSize <= 0 {
  166. param.PageSize = 10
  167. }
  168. var result vo.ResultVO
  169. var reBalanceShows []*vo.ReFrozenInfo
  170. var selectionInfos []*entity.SelectionInfo
  171. var projects []*entity.Project
  172. if param.FrozenState == 1 {
  173. // 电商带货
  174. selectionInfos, _ = dao.SelectionInfoDAO{}.GetSelectionFrozenList(param.EnterpriseId)
  175. // 品牌种草
  176. projects, _ = dao.ProjectDAO{}.GetProjectFrozenList(param.EnterpriseId)
  177. // 本地生活
  178. } else {
  179. // 电商带货
  180. selectionInfos, _ = dao.SelectionInfoDAO{}.GetSelectionFrozenCancelList(param.EnterpriseId)
  181. // 品牌种草
  182. projects, _ = dao.ProjectDAO{}.GetProjectFrozenCancelList(param.EnterpriseId)
  183. // 本地生活
  184. }
  185. // 汇总结果
  186. for _, selection := range selectionInfos {
  187. // 获取商品详情字段
  188. var creatorName string
  189. var productName string
  190. var productPrice float64
  191. var mainImage string
  192. if selection.SubAccountId == 0 {
  193. enterprise, err := dao.EnterpriseDao{}.GetEnterprise(selection.EnterpriseID)
  194. if err == nil && enterprise != nil {
  195. creatorName = enterprise.BusinessName
  196. }
  197. } else {
  198. subAccount, err := dao.SubAccountDao{}.GetSubAccount(selection.SubAccountId)
  199. if err == nil && subAccount != nil {
  200. creatorName = subAccount.SubAccountName
  201. }
  202. }
  203. product, err := dao.ProductDAO{}.GetProductByID(selection.ProductID)
  204. if err == nil && product != nil {
  205. productName = product.ProductName
  206. productPrice = product.ProductPrice
  207. }
  208. mainImage, err = dao.ProductPhotoDAO{}.GetMainPhotoByProductID(selection.ProductID)
  209. // 电商带货汇总
  210. reBalanceShow := &vo.ReFrozenInfo{
  211. ProductId: selection.ProductID,
  212. MainImage: mainImage,
  213. ProductName: productName,
  214. ProductPrice: productPrice,
  215. Platform: selection.Platform,
  216. CreatorName: creatorName,
  217. TaskType: "电商带货",
  218. FrozenBalance: selection.EstimatedCost,
  219. FrozenTime: selection.PayAt.Format("2006-01-02 15:04:05"),
  220. EnterpriseId: selection.EnterpriseID,
  221. SubAccountId: selection.SubAccountId,
  222. TaskId: selection.SelectionID,
  223. }
  224. if param.FrozenState == 2 {
  225. reBalanceShow.FrozenCancelBalance = selection.SettlementAmount
  226. }
  227. reBalanceShows = append(reBalanceShows, reBalanceShow)
  228. }
  229. for _, project := range projects {
  230. // 获取商品详情字段
  231. var creatorName string
  232. var productName string
  233. var productPrice float64
  234. var mainImage string
  235. if project.SubAccountId == 0 {
  236. enterprise, err := dao.EnterpriseDao{}.GetEnterprise(project.EnterpriseID)
  237. if err == nil && enterprise != nil {
  238. creatorName = enterprise.BusinessName
  239. }
  240. } else {
  241. subAccount, err := dao.SubAccountDao{}.GetSubAccount(project.SubAccountId)
  242. if err == nil && subAccount != nil {
  243. creatorName = subAccount.SubAccountName
  244. }
  245. }
  246. product, err := dao.ProductDAO{}.GetProductByID(project.ProductID)
  247. if err == nil && product != nil {
  248. productName = product.ProductName
  249. productPrice = product.ProductPrice
  250. }
  251. mainImage, err = dao.ProductPhotoDAO{}.GetMainPhotoByProductID(project.ProductID)
  252. // 电商带货汇总
  253. reBalanceShow := &vo.ReFrozenInfo{
  254. ProductId: project.ProductID,
  255. MainImage: mainImage,
  256. ProductName: productName,
  257. ProductPrice: productPrice,
  258. Platform: project.ProjectPlatform,
  259. CreatorName: creatorName,
  260. TaskType: "品牌种草",
  261. FrozenBalance: project.PaymentAmount,
  262. FrozenTime: project.PayAt.Format("2006-01-02 15:04:05"),
  263. EnterpriseId: project.EnterpriseID,
  264. SubAccountId: project.SubAccountId,
  265. TaskId: project.ProjectId,
  266. }
  267. if param.FrozenState == 2 {
  268. reBalanceShow.FrozenCancelBalance = project.SettlementAmount
  269. }
  270. reBalanceShows = append(reBalanceShows, reBalanceShow)
  271. }
  272. startIndex := (param.Page - 1) * param.PageSize
  273. endIndex := startIndex + param.PageSize
  274. // 分页
  275. if startIndex >= len(reBalanceShows) {
  276. result = vo.ResultVO{
  277. Page: param.Page,
  278. PageSize: param.PageSize,
  279. Total: int64(len(reBalanceShows)),
  280. Data: nil,
  281. }
  282. return result, nil
  283. }
  284. if endIndex > len(reBalanceShows) {
  285. endIndex = len(reBalanceShows)
  286. }
  287. result = vo.ResultVO{
  288. Page: param.Page,
  289. PageSize: param.PageSize,
  290. Total: int64(len(reBalanceShows)),
  291. Data: reBalanceShows[startIndex:endIndex],
  292. }
  293. return result, nil
  294. }
  295. // 充值管理——累计充值金额、确认中金额
  296. func (s RechargeService) ShowRecharge(param *vo.RechargeParam) (*vo.ReRechargeShow, error) {
  297. reRechargeShow := new(vo.ReRechargeShow)
  298. confirmingRecharge, _ := dao.RechargeRecordDao{}.GetRechargeAmount(param.EnterpriseId, 1)
  299. totalRecharge, _ := dao.RechargeRecordDao{}.GetRechargeAmount(param.EnterpriseId, 2)
  300. reRechargeShow.ConfirmingRecharge = confirmingRecharge
  301. reRechargeShow.TotalRecharge = totalRecharge
  302. return reRechargeShow, nil
  303. }
  304. // 充值管理——充值记录
  305. func (s RechargeService) RechargeInfoList(param *vo.RechargeParam) (vo.ResultVO, error) {
  306. if param.Page <= 0 {
  307. param.Page = 1
  308. }
  309. if param.PageSize <= 0 {
  310. param.PageSize = 10
  311. }
  312. var result vo.ResultVO
  313. var reRechargeInfos []*vo.ReRechargeInfo
  314. rechargeRecords, total, err := dao.RechargeRecordDao{}.RechargeInfoList(param)
  315. if err != nil {
  316. return result, err
  317. }
  318. for _, rechargeRecord := range rechargeRecords {
  319. var creatorName string
  320. if rechargeRecord.SubAccountId == 0 {
  321. enterprise, err := dao.EnterpriseDao{}.GetEnterprise(rechargeRecord.EnterpriseID)
  322. if err == nil && enterprise != nil {
  323. creatorName = enterprise.BusinessName
  324. }
  325. } else {
  326. subAccount, err := dao.SubAccountDao{}.GetSubAccount(rechargeRecord.SubAccountId)
  327. if err == nil && subAccount != nil {
  328. creatorName = subAccount.SubAccountName
  329. }
  330. }
  331. reRechargeInfo := &vo.ReRechargeInfo{
  332. RechargeId: rechargeRecord.RechargeID,
  333. CreatorName: creatorName,
  334. RechargeAmount: rechargeRecord.RechargeAmount,
  335. RechargeMethod: rechargeRecord.RechargeMethod,
  336. TransferVoucherUrl: rechargeRecord.TransferVoucherUrl,
  337. }
  338. if param.RechargeState == 1 {
  339. reRechargeInfo.CommitAt = rechargeRecord.CommitAt.Format("2006-01-02 15:04:05")
  340. } else if param.RechargeState == 2 {
  341. reRechargeInfo.ConfirmAt = rechargeRecord.ConfirmAt.Format("2006-01-02 15:04:05")
  342. } else if param.RechargeState == 3 {
  343. reRechargeInfo.RefuseAt = rechargeRecord.RefuseAt.Format("2006-01-02 15:04:05")
  344. reRechargeInfo.FailReason = rechargeRecord.FailReason
  345. }
  346. reRechargeInfos = append(reRechargeInfos, reRechargeInfo)
  347. }
  348. result = vo.ResultVO{
  349. Page: param.Page,
  350. PageSize: param.PageSize,
  351. Total: total,
  352. Data: reRechargeInfos,
  353. }
  354. return result, nil
  355. }