修复:优化创建交易流程代码,将金额修改为保留3位小数点

This commit is contained in:
Ashang
2022-04-04 16:18:31 +08:00
parent 20c576b9bc
commit 68f92dd1b1
10 changed files with 144 additions and 168 deletions
+7 -1
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/spf13/viper" "github.com/spf13/viper"
"os" "os"
"time"
) )
var ( var (
@@ -53,7 +54,7 @@ func Init() {
} }
func GetAppVersion() string { func GetAppVersion() string {
return "0.0.1" return "0.0.2"
} }
func GetAppName() string { func GetAppName() string {
@@ -90,3 +91,8 @@ func GetOrderExpirationTime() int {
} }
return timer return timer
} }
func GetOrderExpirationTimeDuration() time.Duration {
timer := GetOrderExpirationTime()
return time.Minute * time.Duration(timer)
}
+2 -2
View File
@@ -7,8 +7,8 @@ require (
github.com/go-resty/resty/v2 v2.7.0 github.com/go-resty/resty/v2 v2.7.0
github.com/golang-module/carbon/v2 v2.0.1 github.com/golang-module/carbon/v2 v2.0.1
github.com/gookit/color v1.5.0 github.com/gookit/color v1.5.0
github.com/gookit/goutil v0.4.4 github.com/gookit/goutil v0.4.6
github.com/gookit/validate v1.2.11 github.com/gookit/validate v1.3.1
github.com/hibiken/asynq v0.22.1 github.com/hibiken/asynq v0.22.1
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/labstack/echo/v4 v4.6.0 github.com/labstack/echo/v4 v4.6.0
+5
View File
@@ -197,8 +197,12 @@ github.com/gookit/filter v1.1.2/go.mod h1:pVXLLDD+A8yH9GRztq2Cp7zwZocnuTUpbZs9Q+
github.com/gookit/goutil v0.3.12/go.mod h1:ITj7Lw0muhJNOX+QRa+j+HH0+RNoQVuTmZx5d5LE1vE= github.com/gookit/goutil v0.3.12/go.mod h1:ITj7Lw0muhJNOX+QRa+j+HH0+RNoQVuTmZx5d5LE1vE=
github.com/gookit/goutil v0.4.4 h1:18xr8NKbs4LteyOHZfQ8g7JqTqZal801mAT6LERGdpE= github.com/gookit/goutil v0.4.4 h1:18xr8NKbs4LteyOHZfQ8g7JqTqZal801mAT6LERGdpE=
github.com/gookit/goutil v0.4.4/go.mod h1:qlGVh0PI+WnWSjYnIocfz/7tkeogxL6+EDNP1mRe+7o= github.com/gookit/goutil v0.4.4/go.mod h1:qlGVh0PI+WnWSjYnIocfz/7tkeogxL6+EDNP1mRe+7o=
github.com/gookit/goutil v0.4.6 h1:LSATnmIyR0rV4BMj3XyaKFYtX/HbRURnPVHt6Zb5ABs=
github.com/gookit/goutil v0.4.6/go.mod h1:pq1eTibwb2wN96jrci0xy7xogWzzo9CihOQJEAvz4yQ=
github.com/gookit/validate v1.2.11 h1:zUMsezhMrW3Cy8St3cQJgCKB1ZIbKOWK8e7WMSuVIRc= github.com/gookit/validate v1.2.11 h1:zUMsezhMrW3Cy8St3cQJgCKB1ZIbKOWK8e7WMSuVIRc=
github.com/gookit/validate v1.2.11/go.mod h1:wXo0Vr+AzFUCEUCbTFXgKlPfT+V/V0wPK3zLp49jQq0= github.com/gookit/validate v1.2.11/go.mod h1:wXo0Vr+AzFUCEUCbTFXgKlPfT+V/V0wPK3zLp49jQq0=
github.com/gookit/validate v1.3.1 h1:YoxBxG5H+WucKjfEo8X+QvYOzED7ue3Mfpq6jtpIbYs=
github.com/gookit/validate v1.3.1/go.mod h1:mzcr3U5XtAlQVAgbW0wbmDGiuefU/MNuDkXFMBNb1ko=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@@ -370,6 +374,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+20 -41
View File
@@ -8,11 +8,11 @@ import (
"github.com/assimon/luuu/model/request" "github.com/assimon/luuu/model/request"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"gorm.io/gorm" "gorm.io/gorm"
"time"
) )
var ( var (
CacheWalletAddressToAvailableAmountKey = "WalletAddressToAvailableAmountKey:" // 钱包 - 可用金额 - 过期时间 CacheWalletAddressWithAmountToTradeIdKey = "wallet:%s_%v" // 钱包_待支付金额 : 交易号
CacheWalletAddressToOrdersKey = "WalletAddressToOrdersKey:" // 钱包 - 待支付金额 - 订单号
) )
// GetOrderInfoByOrderId 通过客户订单号查询订单 // GetOrderInfoByOrderId 通过客户订单号查询订单
@@ -78,36 +78,11 @@ func UpdateOrderIsExpirationById(id uint64) error {
return err return err
} }
// LockPayCache 锁定支付池 // GetTradeIdByWalletAddressAndAmount 通过钱包地址,支付金额获取交易号
func LockPayCache(token, tradeId, amount string, expirationTime int64) error { func GetTradeIdByWalletAddressAndAmount(token string, amount float64) (string, error) {
// 载入Redis
ctx := context.Background() ctx := context.Background()
pipe := dao.Rdb.TxPipeline() cacheKey := fmt.Sprintf(CacheWalletAddressWithAmountToTradeIdKey, token, amount)
lockAvailableAmountKey := fmt.Sprintf("%s%s", CacheWalletAddressToAvailableAmountKey, token) result, err := dao.Rdb.Get(ctx, cacheKey).Result()
// 占用金额
pipe.HSet(ctx, lockAvailableAmountKey, amount, expirationTime)
// 标记金额对应订单号
lockWalletOrdersKey := fmt.Sprintf("%s%s", CacheWalletAddressToOrdersKey, token)
pipe.HSet(ctx, lockWalletOrdersKey, amount, tradeId)
_, err := pipe.Exec(ctx)
return err
}
// ClearPayCache 清理支付池
func ClearPayCache(token, amount string) error {
ctx := context.Background()
lockAvailableAmountKey := fmt.Sprintf("%s%s", CacheWalletAddressToAvailableAmountKey, token)
lockWalletOrdersKey := fmt.Sprintf("%s%s", CacheWalletAddressToOrdersKey, token)
pipe := dao.Rdb.TxPipeline()
pipe.HDel(ctx, lockAvailableAmountKey, amount)
pipe.HDel(ctx, lockWalletOrdersKey, amount)
_, err := pipe.Exec(ctx)
return err
}
func GetTradeIdByAmount(ctx context.Context, token, amount string) (string, error) {
cacheKey := fmt.Sprintf("%s%s", CacheWalletAddressToOrdersKey, token)
result, err := dao.Rdb.HGet(ctx, cacheKey, amount).Result()
if err == redis.Nil { if err == redis.Nil {
return "", nil return "", nil
} }
@@ -117,14 +92,18 @@ func GetTradeIdByAmount(ctx context.Context, token, amount string) (string, erro
return result, nil return result, nil
} }
func GetExpirationTimeByAmount(ctx context.Context, token, amount string) (string, error) { // LockTransaction 锁定交易
cacheKey := fmt.Sprintf("%s%s", CacheWalletAddressToAvailableAmountKey, token) func LockTransaction(token, tradeId string, amount float64, expirationTime time.Duration) error {
result, err := dao.Rdb.HGet(ctx, cacheKey, amount).Result() ctx := context.Background()
if err == redis.Nil { cacheKey := fmt.Sprintf(CacheWalletAddressWithAmountToTradeIdKey, token, amount)
return "", nil err := dao.Rdb.Set(ctx, cacheKey, tradeId, expirationTime).Err()
} return err
if err != nil { }
return "", err
} // UnLockTransaction 解锁交易
return result, nil func UnLockTransaction(token string, amount float64) error {
ctx := context.Background()
cacheKey := fmt.Sprintf(CacheWalletAddressWithAmountToTradeIdKey, token, amount)
err := dao.Rdb.Del(ctx, cacheKey).Err()
return err
} }
+1 -1
View File
@@ -23,7 +23,7 @@ func (r CreateTransactionRequest) Translates() map[string]string {
// OrderProcessingRequest 订单处理 // OrderProcessingRequest 订单处理
type OrderProcessingRequest struct { type OrderProcessingRequest struct {
Token string Token string
Amount string Amount float64
TradeId string TradeId string
BlockTransactionId string BlockTransactionId string
} }
+56 -92
View File
@@ -1,7 +1,6 @@
package service package service
import ( import (
"context"
"fmt" "fmt"
"github.com/assimon/luuu/config" "github.com/assimon/luuu/config"
"github.com/assimon/luuu/model/dao" "github.com/assimon/luuu/model/dao"
@@ -11,35 +10,40 @@ import (
"github.com/assimon/luuu/model/response" "github.com/assimon/luuu/model/response"
"github.com/assimon/luuu/mq" "github.com/assimon/luuu/mq"
"github.com/assimon/luuu/mq/handle" "github.com/assimon/luuu/mq/handle"
"github.com/assimon/luuu/telegram"
"github.com/assimon/luuu/util/constant" "github.com/assimon/luuu/util/constant"
"github.com/assimon/luuu/util/math"
"github.com/golang-module/carbon/v2" "github.com/golang-module/carbon/v2"
"github.com/gookit/goutil/mathutil"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"math/rand" "math/rand"
"strconv"
"sync" "sync"
"time" "time"
) )
const (
CnyMinimumPaymentAmount = 0.01 // cny最低支付金额
UsdtMinimumPaymentAmount = 0.001 // usdt最低支付金额
UsdtAmountPerIncrement = 0.001 // usdt每次递增金额
IncrementalMaximumNumber = 100 // 最大递增次数
)
var gCreateTransactionLock sync.Mutex var gCreateTransactionLock sync.Mutex
// CreateTransaction 创建订单 // CreateTransaction 创建订单
func CreateTransaction(req *request.CreateTransactionRequest) (*response.CreateTransactionResponse, error) { func CreateTransaction(req *request.CreateTransactionRequest) (*response.CreateTransactionResponse, error) {
gCreateTransactionLock.Lock() gCreateTransactionLock.Lock()
defer gCreateTransactionLock.Unlock() defer gCreateTransactionLock.Unlock()
// 汇率计算金额 payAmount := math.MustParsePrecFloat64(req.Amount, 2)
rmb := decimal.NewFromFloat(req.Amount) // 按照汇率转化USDT
rate := decimal.NewFromFloat(config.GetUsdtRate()) decimalPayAmount := decimal.NewFromFloat(payAmount)
amount := rmb.Div(rate).InexactFloat64() decimalRate := decimal.NewFromFloat(config.GetUsdtRate())
actualAmountStr := fmt.Sprintf("%.4f", amount) decimalUsdt := decimalPayAmount.Div(decimalRate)
actualAmountFloat, err := strconv.ParseFloat(actualAmountStr, 64) // cny 是否可以满足最低支付金额
if err != nil { if decimalPayAmount.Cmp(decimal.NewFromFloat(CnyMinimumPaymentAmount)) == -1 {
return nil, err return nil, constant.PayAmountErr
} }
// 是否可以满足最低支付金额 // Usdt是否可以满足最低支付金额
if actualAmountFloat <= 0 { if decimalUsdt.Cmp(decimal.NewFromFloat(UsdtMinimumPaymentAmount)) == -1 {
return nil, constant.PayAmountErr return nil, constant.PayAmountErr
} }
// 已经存在了的交易 // 已经存在了的交易
@@ -58,23 +62,20 @@ func CreateTransaction(req *request.CreateTransactionRequest) (*response.CreateT
if len(walletAddress) <= 0 { if len(walletAddress) <= 0 {
return nil, constant.NotAvailableWalletAddress return nil, constant.NotAvailableWalletAddress
} }
availableToken, availableAmountStr, err := CalculateAvailableWalletTokenAndAmount(actualAmountStr, walletAddress) amount := math.MustParsePrecFloat64(decimalUsdt.InexactFloat64(), 3)
availableToken, availableAmount, err := CalculateAvailableWalletAndAmount(amount, walletAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if availableToken == "" || availableAmountStr == "" { if availableToken == "" {
return nil, constant.NotAvailableAmountErr return nil, constant.NotAvailableAmountErr
} }
availableAmountFloat, err := strconv.ParseFloat(availableAmountStr, 64)
if err != nil {
return nil, err
}
tx := dao.Mdb.Begin() tx := dao.Mdb.Begin()
order := &mdb.Orders{ order := &mdb.Orders{
TradeId: GenerateCode(), TradeId: GenerateCode(),
OrderId: req.OrderId, OrderId: req.OrderId,
Amount: req.Amount, Amount: req.Amount,
ActualAmount: availableAmountFloat, ActualAmount: availableAmount,
Token: availableToken, Token: availableToken,
Status: mdb.StatusWaitPay, Status: mdb.StatusWaitPay,
NotifyUrl: req.NotifyUrl, NotifyUrl: req.NotifyUrl,
@@ -85,9 +86,8 @@ func CreateTransaction(req *request.CreateTransactionRequest) (*response.CreateT
tx.Rollback() tx.Rollback()
return nil, err return nil, err
} }
ExpirationTime := carbon.Now().AddMinutes(config.GetOrderExpirationTime()).Timestamp()
// 锁定支付池 // 锁定支付池
err = data.LockPayCache(availableToken, order.TradeId, availableAmountStr, ExpirationTime) err = data.LockTransaction(availableToken, order.TradeId, availableAmount, config.GetOrderExpirationTimeDuration())
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return nil, err return nil, err
@@ -95,7 +95,8 @@ func CreateTransaction(req *request.CreateTransactionRequest) (*response.CreateT
tx.Commit() tx.Commit()
// 超时过期消息队列 // 超时过期消息队列
orderExpirationQueue, _ := handle.NewOrderExpirationQueue(order.TradeId) orderExpirationQueue, _ := handle.NewOrderExpirationQueue(order.TradeId)
mq.MClient.Enqueue(orderExpirationQueue, asynq.ProcessIn(time.Minute*time.Duration(config.GetOrderExpirationTime()))) mq.MClient.Enqueue(orderExpirationQueue, asynq.ProcessIn(config.GetOrderExpirationTimeDuration()))
ExpirationTime := carbon.Now().AddMinutes(config.GetOrderExpirationTime()).Timestamp()
resp := &response.CreateTransactionResponse{ resp := &response.CreateTransactionResponse{
TradeId: order.TradeId, TradeId: order.TradeId,
OrderId: order.OrderId, OrderId: order.OrderId,
@@ -125,88 +126,51 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
tx.Rollback() tx.Rollback()
return err return err
} }
err = data.ClearPayCache(req.Token, req.Amount) // 解锁交易
tx.Commit() err = data.UnLockTransaction(req.Token, req.Amount)
order, err := data.GetOrderInfoByTradeId(req.TradeId)
if err != nil { if err != nil {
tx.Rollback()
return err return err
} }
// 回调队列 tx.Commit()
orderCallbackQueue, _ := handle.NewOrderCallbackQueue(order)
mq.MClient.Enqueue(orderCallbackQueue, asynq.MaxRetry(5))
// 发送机器人消息
msgTpl := `
<b>📢📢有新的交易支付成功!</b>
<pre>交易号:%s</pre>
<pre>订单号:%s</pre>
<pre>请求支付金额:%.4f cny</pre>
<pre>实际支付金额:%.4f usdt</pre>
<pre>钱包地址:%s</pre>
<pre>订单创建时间:%s</pre>
<pre>支付成功时间:%s</pre>
`
msg := fmt.Sprintf(msgTpl, order.TradeId, order.OrderId, order.Amount, order.ActualAmount, order.Token, order.CreatedAt.ToDateTimeString(), carbon.Now().ToDateTimeString())
telegram.SendToBot(msg)
return nil return nil
} }
func CalculateAvailableWalletTokenAndAmount(amount string, walletAddress []mdb.WalletAddress) (string, string, error) { // CalculateAvailableWalletAndAmount 计算可用钱包地址和金额
calculateAmountStr := amount func CalculateAvailableWalletAndAmount(amount float64, walletAddress []mdb.WalletAddress) (string, float64, error) {
availableAmountStr := ""
availableToken := "" availableToken := ""
for i := 0; i < 100; i++ { availableAmount := amount
token, err := CalculateAvailableWalletToken(calculateAmountStr, walletAddress) calculateAvailableWalletFunc := func(amount float64) (string, error) {
if err != nil { availableWallet := ""
return "", "", err for _, address := range walletAddress {
token := address.Token
result, err := data.GetTradeIdByWalletAddressAndAmount(token, amount)
if err != nil {
return "", err
}
if result == "" {
availableWallet = token
break
}
} }
// 这个金额没有拿到可用的钱包,重试,金额+0.0001 return availableWallet, nil
}
for i := 0; i < IncrementalMaximumNumber; i++ {
token, err := calculateAvailableWalletFunc(availableAmount)
if err != nil {
return "", 0, err
}
// 拿不到可用钱包就累加金额
if token == "" { if token == "" {
x, err := decimal.NewFromString(calculateAmountStr) decimalOldAmount := decimal.NewFromFloat(availableAmount)
if err != nil { decimalIncr := decimal.NewFromFloat(UsdtAmountPerIncrement)
return "", "", err availableAmount = decimalOldAmount.Add(decimalIncr).InexactFloat64()
}
y, err := decimal.NewFromString("0.0001")
if err != nil {
return "", "", err
}
calculateAmountStr = x.Add(y).String()
continue continue
} }
availableAmountStr = calculateAmountStr
availableToken = token availableToken = token
break break
} }
return availableToken, availableAmountStr, nil return availableToken, availableAmount, nil
}
// CalculateAvailableWalletToken 计算可用钱包token
func CalculateAvailableWalletToken(payAmount string, walletAddress []mdb.WalletAddress) (string, error) {
nowTime := time.Now().Unix()
ctx := context.Background()
walletToken := ""
for _, address := range walletAddress {
result, err := data.GetExpirationTimeByAmount(ctx, address.Token, payAmount)
if err != nil {
return "", err
}
// 这个钱包金额被占用了
if result != "" {
endTime := mathutil.MustInt64(result)
// 但是过期了
if endTime < nowTime {
// 删掉过期,还能继续用这个地址
err = data.ClearPayCache(address.Token, payAmount)
if err != nil {
return "", err
}
} else {
continue
}
}
walletToken = address.Token
break
}
return walletToken, nil
} }
// GenerateCode 订单号生成 // GenerateCode 订单号生成
+34 -22
View File
@@ -1,20 +1,21 @@
package service package service
import ( import (
"context"
"fmt" "fmt"
"github.com/assimon/luuu/model/data" "github.com/assimon/luuu/model/data"
"github.com/assimon/luuu/model/request" "github.com/assimon/luuu/model/request"
"github.com/assimon/luuu/mq"
"github.com/assimon/luuu/mq/handle"
"github.com/assimon/luuu/telegram"
"github.com/assimon/luuu/util/http_client" "github.com/assimon/luuu/util/http_client"
"github.com/assimon/luuu/util/json" "github.com/assimon/luuu/util/json"
"github.com/assimon/luuu/util/log" "github.com/assimon/luuu/util/log"
"github.com/golang-module/carbon/v2" "github.com/golang-module/carbon/v2"
"github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/stdutil" "github.com/gookit/goutil/stdutil"
"github.com/hibiken/asynq"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"net/http" "net/http"
"sync" "sync"
"time"
) )
const UsdtTrc20ApiUri = "https://apilist.tronscan.org/api/token_trc20/transfers" const UsdtTrc20ApiUri = "https://apilist.tronscan.org/api/token_trc20/transfers"
@@ -90,38 +91,33 @@ func Trc20CallBack(token string, wg *sync.WaitGroup) {
if trc20Resp.Total <= 0 { if trc20Resp.Total <= 0 {
return return
} }
ctx := context.Background()
nowTime := time.Now().Unix()
for _, transfer := range trc20Resp.TokenTransfers { for _, transfer := range trc20Resp.TokenTransfers {
if transfer.ToAddress != token || transfer.FinalResult != "SUCCESS" { if transfer.ToAddress != token || transfer.FinalResult != "SUCCESS" {
continue continue
} }
x, _ := decimal.NewFromString(transfer.Quant) decimalQuant, err := decimal.NewFromString(transfer.Quant)
y, _ := decimal.NewFromString("1000000")
quant := x.Div(y).InexactFloat64()
amount := fmt.Sprintf("%.4f", quant)
result, err := data.GetExpirationTimeByAmount(ctx, token, amount)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if result != "" { decimalDivisor := decimal.NewFromFloat(1000000)
expTime := mathutil.MustInt64(result) amount := decimalQuant.Div(decimalDivisor).InexactFloat64()
// 但是过期了 tradeId, err := data.GetTradeIdByWalletAddressAndAmount(token, amount)
if expTime < nowTime {
// 删掉过期
_ = data.ClearPayCache(token, amount)
continue
}
}
// 该钱包下有无匹配金额订单
tradeId, err := data.GetTradeIdByAmount(ctx, token, amount)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if tradeId == "" { if tradeId == "" {
continue continue
} }
// 到这一步就匹配到金额了 order, err := data.GetOrderInfoByTradeId(tradeId)
if err != nil {
panic(err)
}
// 区块的确认时间必须在订单创建时间之后
createTime := order.CreatedAt.TimestampWithMillisecond()
if transfer.BlockTs < createTime {
panic("Orders cannot actually be matched")
}
// 到这一步就完全算是支付成功了
req := &request.OrderProcessingRequest{ req := &request.OrderProcessingRequest{
Token: token, Token: token,
TradeId: tradeId, TradeId: tradeId,
@@ -132,5 +128,21 @@ func Trc20CallBack(token string, wg *sync.WaitGroup) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// 回调队列
orderCallbackQueue, _ := handle.NewOrderCallbackQueue(order)
mq.MClient.Enqueue(orderCallbackQueue, asynq.MaxRetry(5))
// 发送机器人消息
msgTpl := `
<b>📢📢有新的交易支付成功!</b>
<pre>交易号:%s</pre>
<pre>订单号:%s</pre>
<pre>请求支付金额:%f cny</pre>
<pre>实际支付金额:%f usdt</pre>
<pre>钱包地址:%s</pre>
<pre>订单创建时间:%s</pre>
<pre>支付成功时间:%s</pre>
`
msg := fmt.Sprintf(msgTpl, order.TradeId, order.OrderId, order.Amount, order.ActualAmount, order.Token, order.CreatedAt.ToDateTimeString(), carbon.Now().ToDateTimeString())
telegram.SendToBot(msg)
} }
} }
+1 -3
View File
@@ -2,7 +2,6 @@ package handle
import ( import (
"context" "context"
"fmt"
"github.com/assimon/luuu/model/data" "github.com/assimon/luuu/model/data"
"github.com/assimon/luuu/model/mdb" "github.com/assimon/luuu/model/mdb"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
@@ -28,8 +27,7 @@ func OrderExpirationHandle(ctx context.Context, t *asynq.Task) error {
if err != nil { if err != nil {
return err return err
} }
actualAmount := fmt.Sprintf("%.4f", orderInfo.ActualAmount) err = data.UnLockTransaction(orderInfo.Token, orderInfo.ActualAmount)
err = data.ClearPayCache(orderInfo.Token, actualAmount)
if err != nil { if err != nil {
return err return err
} }
+2 -6
View File
@@ -1,12 +1,11 @@
package task package task
import ( import (
"fmt"
"github.com/assimon/luuu/config" "github.com/assimon/luuu/config"
"github.com/assimon/luuu/util/http_client" "github.com/assimon/luuu/util/http_client"
"github.com/assimon/luuu/util/json" "github.com/assimon/luuu/util/json"
"github.com/assimon/luuu/util/log" "github.com/assimon/luuu/util/log"
"strconv" "github.com/assimon/luuu/util/math"
"time" "time"
) )
@@ -56,10 +55,7 @@ func (r UsdtRateJob) Run() {
} }
for _, points := range usdtResp.Data.Points { for _, points := range usdtResp.Data.Points {
if len(points.C) > 0 && points.C[0] > 0 { if len(points.C) > 0 && points.C[0] > 0 {
config.UsdtRate, err = strconv.ParseFloat(fmt.Sprintf("%.2f", points.C[0]), 64) config.UsdtRate = math.MustParsePrecFloat64(points.C[0], 2)
if err != nil {
log.Sugar.Error("Calculate UsdtRate err:", err.Error())
}
return return
} }
} }
+16
View File
@@ -0,0 +1,16 @@
package math
import (
"fmt"
"strconv"
)
// MustParsePrecFloat64 按小数位截取float
func MustParsePrecFloat64(value float64, prec int) float64 {
format := fmt.Sprintf("%%.%df", prec)
v, err := strconv.ParseFloat(fmt.Sprintf(format, value), 64)
if err != nil {
return 0
}
return v
}