Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

91 строка
2.5 KiB

  1. package model
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/QuantumNous/new-api/common"
  6. "github.com/QuantumNous/new-api/logger"
  7. "github.com/shopspring/decimal"
  8. "gorm.io/gorm"
  9. )
  10. // RechargeWechat 微信支付充值完成(由回调触发)
  11. func RechargeWechat(tradeNo string) error {
  12. return rechargeByQRCodePayment(tradeNo, "微信支付")
  13. }
  14. // RechargeAlipay 支付宝充值完成(由回调触发)
  15. func RechargeAlipay(tradeNo string) error {
  16. return rechargeByQRCodePayment(tradeNo, "支付宝")
  17. }
  18. // rechargeByQRCodePayment 扫码支付充值完成(微信/支付宝通用)
  19. // 使用事务+行锁保证幂等
  20. func rechargeByQRCodePayment(tradeNo string, paymentMethod string) error {
  21. if tradeNo == "" {
  22. return errors.New("未提供支付单号")
  23. }
  24. var quotaToAdd int64
  25. var payMoney float64
  26. var userId int
  27. refCol := "`trade_no`"
  28. if common.UsingPostgreSQL {
  29. refCol = `"trade_no"`
  30. }
  31. err := DB.Transaction(func(tx *gorm.DB) error {
  32. topUp := &TopUp{}
  33. if err := tx.Set("gorm:query_option", "FOR UPDATE").Where(refCol+" = ?", tradeNo).First(topUp).Error; err != nil {
  34. return errors.New("充值订单不存在")
  35. }
  36. if topUp.Status == common.TopUpStatusSuccess {
  37. return nil
  38. }
  39. if topUp.Status != common.TopUpStatusPending {
  40. return errors.New("充值订单状态错误")
  41. }
  42. topUp.CompleteTime = common.GetTimestamp()
  43. topUp.Status = common.TopUpStatusSuccess
  44. if err := tx.Save(topUp).Error; err != nil {
  45. return err
  46. }
  47. dMoney := decimal.NewFromFloat(topUp.Money)
  48. dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
  49. // 防止跨网关回调攻击:扫码支付 webhook 只能完成对应渠道创建的订单
  50. if topUp.PaymentProvider != "" && topUp.PaymentProvider != PaymentProviderWechat && topUp.PaymentProvider != PaymentProviderAlipay {
  51. return fmt.Errorf("支付网关不匹配: 订单由 %s 创建, 但 %s webhook 尝试完成", topUp.PaymentProvider, paymentMethod)
  52. }
  53. quotaToAdd = dMoney.Mul(dQuotaPerUnit).IntPart()
  54. if quotaToAdd <= 0 {
  55. return errors.New("无效的充值额度")
  56. }
  57. if err := tx.Model(&User{}).Where("id = ?", topUp.UserId).Update("quota", gorm.Expr("quota + ?", quotaToAdd)).Error; err != nil {
  58. return err
  59. }
  60. userId = topUp.UserId
  61. payMoney = topUp.Money
  62. return nil
  63. })
  64. if err != nil {
  65. return err
  66. }
  67. if quotaToAdd > 0 {
  68. RecordLog(userId, LogTypeTopup, fmt.Sprintf("使用%s充值成功,充值金额: %v,支付金额:%.2f", paymentMethod, logger.FormatQuota(int(quotaToAdd)), payMoney))
  69. }
  70. return nil
  71. }