mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 18:26:16 +00:00
feat(payment): support placeholder orders for GMPay and EPay
This commit is contained in:
@@ -102,6 +102,79 @@ func TestHandleOkPayNotifySendsPaymentNotificationOnSuccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleOkPayNotifySettlesPlaceholderParent(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
const (
|
||||
shopID = "okpay-shop-placeholder"
|
||||
shopToken = "okpay-shop-placeholder-token"
|
||||
parentTradeID = "okpay-placeholder-parent-001"
|
||||
providerOrderID = "okpay-placeholder-provider-001"
|
||||
)
|
||||
|
||||
if err := data.SetSetting(mdb.SettingGroupOkPay, mdb.SettingKeyOkPayShopID, shopID, mdb.SettingTypeString); err != nil {
|
||||
t.Fatalf("seed okpay shop id: %v", err)
|
||||
}
|
||||
if err := data.SetSetting(mdb.SettingGroupOkPay, mdb.SettingKeyOkPayShopToken, shopToken, mdb.SettingTypeString); err != nil {
|
||||
t.Fatalf("seed okpay shop token: %v", err)
|
||||
}
|
||||
|
||||
parent := &mdb.Orders{
|
||||
TradeId: parentTradeID,
|
||||
OrderId: "merchant-okpay-placeholder-parent-001",
|
||||
Amount: 1,
|
||||
Currency: "CNY",
|
||||
ActualAmount: 0.15,
|
||||
ReceiveAddress: "OKPAY",
|
||||
Token: "USDT",
|
||||
Network: mdb.PaymentProviderOkPay,
|
||||
Status: mdb.StatusWaitPay,
|
||||
IsSelected: true,
|
||||
PaymentType: mdb.PaymentTypeGmpay,
|
||||
PayProvider: mdb.PaymentProviderOkPay,
|
||||
}
|
||||
if err := dao.Mdb.Create(parent).Error; err != nil {
|
||||
t.Fatalf("seed parent order: %v", err)
|
||||
}
|
||||
if err := dao.Mdb.Create(&mdb.ProviderOrder{
|
||||
TradeId: parentTradeID,
|
||||
Provider: mdb.PaymentProviderOkPay,
|
||||
ProviderOrderID: providerOrderID,
|
||||
Amount: 0.15,
|
||||
Coin: "USDT",
|
||||
Status: mdb.ProviderOrderStatusPending,
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("seed provider order: %v", err)
|
||||
}
|
||||
|
||||
form := okPayNotifyTestForm(shopID, shopToken, providerOrderID, parentTradeID, "0.15000000", "USDT")
|
||||
if err := HandleOkPayNotify(form, "placeholder-okpay-form"); err != nil {
|
||||
t.Fatalf("handle okpay notify: %v", err)
|
||||
}
|
||||
|
||||
reloadedParent, err := data.GetOrderInfoByTradeId(parentTradeID)
|
||||
if err != nil {
|
||||
t.Fatalf("reload parent: %v", err)
|
||||
}
|
||||
if reloadedParent.Status != mdb.StatusPaySuccess {
|
||||
t.Fatalf("parent status = %d, want %d", reloadedParent.Status, mdb.StatusPaySuccess)
|
||||
}
|
||||
if reloadedParent.PayBySubId != 0 {
|
||||
t.Fatalf("parent pay_by_sub_id = %d, want 0", reloadedParent.PayBySubId)
|
||||
}
|
||||
if reloadedParent.Token != "USDT" || reloadedParent.Network != mdb.PaymentProviderOkPay || reloadedParent.ReceiveAddress != "OKPAY" || reloadedParent.ActualAmount != 0.15 {
|
||||
t.Fatalf("okpay parent fields changed: token=%q network=%q address=%q actual=%v", reloadedParent.Token, reloadedParent.Network, reloadedParent.ReceiveAddress, reloadedParent.ActualAmount)
|
||||
}
|
||||
providerRow, err := data.GetProviderOrderByTradeIDAndProvider(parentTradeID, mdb.PaymentProviderOkPay)
|
||||
if err != nil {
|
||||
t.Fatalf("reload provider row: %v", err)
|
||||
}
|
||||
if providerRow.Status != mdb.ProviderOrderStatusPaid {
|
||||
t.Fatalf("provider status = %q, want %q", providerRow.Status, mdb.ProviderOrderStatusPaid)
|
||||
}
|
||||
}
|
||||
|
||||
func okPayNotifyTestForm(shopID, shopToken, providerOrderID, tradeID, amount, coin string) map[string]string {
|
||||
form := map[string]string{
|
||||
"code": "200",
|
||||
|
||||
@@ -70,15 +70,44 @@ func ensureEnabledOrderAsset(network, token string) (*mdb.ChainToken, error) {
|
||||
return tokenRow, nil
|
||||
}
|
||||
|
||||
func validateCreateTransactionTokenNetwork(token, network string) error {
|
||||
if (token == "") != (network == "") {
|
||||
return constant.ParamsMarshalErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildCreateTransactionResponse(order *mdb.Orders) *response.CreateTransactionResponse {
|
||||
return &response.CreateTransactionResponse{
|
||||
TradeId: order.TradeId,
|
||||
OrderId: order.OrderId,
|
||||
Amount: order.Amount,
|
||||
Currency: order.Currency,
|
||||
ActualAmount: order.ActualAmount,
|
||||
ReceiveAddress: order.ReceiveAddress,
|
||||
Token: order.Token,
|
||||
Status: order.Status,
|
||||
ExpirationTime: carbon.Now().AddMinutes(config.GetOrderExpirationTime()).Timestamp(),
|
||||
PaymentUrl: fmt.Sprintf("%s/pay/checkout-counter/%s", config.GetAppUri(), order.TradeId),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTransaction creates a new payment order.
|
||||
func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey) (*response.CreateTransactionResponse, error) {
|
||||
token := strings.ToUpper(strings.TrimSpace(req.Token))
|
||||
currency := strings.ToUpper(strings.TrimSpace(req.Currency))
|
||||
network := strings.ToLower(strings.TrimSpace(req.Network))
|
||||
if err := validateCreateTransactionTokenNetwork(token, network); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notifyURL := strings.TrimSpace(req.NotifyUrl)
|
||||
if err := security.ValidatePublicHTTPURL(notifyURL); err != nil {
|
||||
return nil, constant.NotifyURLErr
|
||||
}
|
||||
paymentType := mdb.PaymentTypeGmpay
|
||||
if strings.EqualFold(req.PaymentType, mdb.PaymentTypeEpay) {
|
||||
paymentType = mdb.PaymentTypeEpay
|
||||
}
|
||||
|
||||
gCreateTransactionLock.Lock()
|
||||
defer gCreateTransactionLock.Unlock()
|
||||
@@ -93,6 +122,32 @@ func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey
|
||||
return nil, constant.OrderAlreadyExists
|
||||
}
|
||||
|
||||
decimalPayAmount := decimal.NewFromFloat(payAmount)
|
||||
if decimalPayAmount.Cmp(decimal.NewFromFloat(CnyMinimumPaymentAmount)) == -1 {
|
||||
return nil, constant.PayAmountErr
|
||||
}
|
||||
|
||||
if token == "" && network == "" {
|
||||
tradeID := GenerateCode()
|
||||
order := &mdb.Orders{
|
||||
TradeId: tradeID,
|
||||
OrderId: req.OrderId,
|
||||
Amount: payAmount,
|
||||
Currency: currency,
|
||||
Status: mdb.StatusWaitSelect,
|
||||
NotifyUrl: notifyURL,
|
||||
RedirectUrl: req.RedirectUrl,
|
||||
Name: req.Name,
|
||||
PaymentType: paymentType,
|
||||
PayProvider: mdb.PaymentProviderOnChain,
|
||||
ApiKeyID: apiKeyID(apiKey),
|
||||
}
|
||||
if err = data.CreateOrderWithTransaction(dao.Mdb, order); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buildCreateTransactionResponse(order), nil
|
||||
}
|
||||
|
||||
if !data.IsChainEnabled(network) {
|
||||
return nil, constant.ChainNotEnabled
|
||||
}
|
||||
@@ -105,11 +160,7 @@ func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey
|
||||
return nil, constant.RateAmountErr
|
||||
}
|
||||
|
||||
decimalPayAmount := decimal.NewFromFloat(payAmount)
|
||||
decimalTokenAmount := decimalPayAmount.Mul(decimal.NewFromFloat(rate))
|
||||
if decimalPayAmount.Cmp(decimal.NewFromFloat(CnyMinimumPaymentAmount)) == -1 {
|
||||
return nil, constant.PayAmountErr
|
||||
}
|
||||
if decimalTokenAmount.Cmp(decimal.NewFromFloat(UsdtMinimumPaymentAmount)) == -1 {
|
||||
return nil, constant.PayAmountErr
|
||||
}
|
||||
@@ -146,7 +197,7 @@ func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey
|
||||
NotifyUrl: notifyURL,
|
||||
RedirectUrl: req.RedirectUrl,
|
||||
Name: req.Name,
|
||||
PaymentType: req.PaymentType,
|
||||
PaymentType: paymentType,
|
||||
PayProvider: mdb.PaymentProviderOnChain,
|
||||
ApiKeyID: apiKeyID(apiKey),
|
||||
}
|
||||
@@ -161,19 +212,7 @@ func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expirationTime := carbon.Now().AddMinutes(config.GetOrderExpirationTime()).Timestamp()
|
||||
resp := &response.CreateTransactionResponse{
|
||||
TradeId: order.TradeId,
|
||||
OrderId: order.OrderId,
|
||||
Amount: order.Amount,
|
||||
Currency: order.Currency,
|
||||
ActualAmount: order.ActualAmount,
|
||||
ReceiveAddress: order.ReceiveAddress,
|
||||
Token: order.Token,
|
||||
ExpirationTime: expirationTime,
|
||||
PaymentUrl: fmt.Sprintf("%s/pay/checkout-counter/%s", config.GetAppUri(), order.TradeId),
|
||||
}
|
||||
return resp, nil
|
||||
return buildCreateTransactionResponse(order), nil
|
||||
}
|
||||
|
||||
// OrderProcessing marks an order as paid and releases its sqlite reservation.
|
||||
@@ -206,15 +245,21 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = data.UnLockTransaction(req.Network, req.ReceiveAddress, req.Token, req.Amount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock transaction after pay success failed, trade_id=%s, err=%v", req.TradeId, err)
|
||||
}
|
||||
|
||||
// Load order to check parent-child relationship
|
||||
order, err := data.GetOrderInfoByTradeId(req.TradeId)
|
||||
if err != nil {
|
||||
if strings.TrimSpace(req.Network) != "" && strings.TrimSpace(req.ReceiveAddress) != "" && strings.TrimSpace(req.Token) != "" && req.Amount > 0 {
|
||||
if unlockErr := data.UnLockTransaction(req.Network, req.ReceiveAddress, req.Token, req.Amount); unlockErr != nil {
|
||||
log.Sugar.Warnf("[order] fallback unlock transaction after pay success failed, trade_id=%s, err=%v", req.TradeId, unlockErr)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("load paid order failed, trade_id=%s: %w", req.TradeId, err)
|
||||
}
|
||||
if hasTransactionLock(order) {
|
||||
if err = data.UnLockTransaction(order.Network, order.ReceiveAddress, order.Token, order.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock transaction after pay success failed, trade_id=%s, err=%v", order.TradeId, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Parent order paid directly: expire all sub-orders and release their locks
|
||||
if order.ParentTradeId == "" {
|
||||
@@ -232,8 +277,10 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
log.Sugar.Warnf("[order] expire provider order failed, trade_id=%s, provider=%s, err=%v", sub.TradeId, sub.PayProvider, err)
|
||||
}
|
||||
}
|
||||
if err = data.UnLockTransaction(sub.Network, sub.ReceiveAddress, sub.Token, sub.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock sub-order transaction failed, trade_id=%s, err=%v", sub.TradeId, err)
|
||||
if hasTransactionLock(&sub) {
|
||||
if err = data.UnLockTransaction(sub.Network, sub.ReceiveAddress, sub.Token, sub.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock sub-order transaction failed, trade_id=%s, err=%v", sub.TradeId, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -284,8 +331,10 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
}
|
||||
|
||||
// Release parent's own wallet lock
|
||||
if err = data.UnLockTransaction(parent.Network, parent.ReceiveAddress, parent.Token, parent.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock parent transaction failed, parent_trade_id=%s, err=%v", parent.TradeId, err)
|
||||
if hasTransactionLock(parent) {
|
||||
if err = data.UnLockTransaction(parent.Network, parent.ReceiveAddress, parent.Token, parent.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock parent transaction failed, parent_trade_id=%s, err=%v", parent.TradeId, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Release sibling locks after their status transitions commit.
|
||||
@@ -295,8 +344,10 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
log.Sugar.Warnf("[order] expire sibling provider order failed, trade_id=%s, provider=%s, err=%v", sib.TradeId, sib.PayProvider, err)
|
||||
}
|
||||
}
|
||||
if err = data.UnLockTransaction(sib.Network, sib.ReceiveAddress, sib.Token, sib.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock sibling transaction failed, trade_id=%s, err=%v", sib.TradeId, err)
|
||||
if hasTransactionLock(&sib) {
|
||||
if err = data.UnLockTransaction(sib.Network, sib.ReceiveAddress, sib.Token, sib.ActualAmount); err != nil {
|
||||
log.Sugar.Warnf("[order] unlock sibling transaction failed, trade_id=%s, err=%v", sib.TradeId, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,9 +499,20 @@ func isOnChainOrder(payProvider string) bool {
|
||||
return payProvider == "" || payProvider == mdb.PaymentProviderOnChain
|
||||
}
|
||||
|
||||
const MaxSubOrders = 2
|
||||
func hasTransactionLock(order *mdb.Orders) bool {
|
||||
return order != nil &&
|
||||
isOnChainOrder(order.PayProvider) &&
|
||||
strings.TrimSpace(order.Network) != "" &&
|
||||
strings.TrimSpace(order.ReceiveAddress) != "" &&
|
||||
strings.TrimSpace(order.Token) != "" &&
|
||||
order.ActualAmount > 0
|
||||
}
|
||||
|
||||
// SwitchNetwork creates or returns an existing sub-order for a different token+network.
|
||||
const MaxSubOrders = 1
|
||||
|
||||
// SwitchNetwork completes a status=4 placeholder in place, or creates/returns
|
||||
// an existing sub-order for a different payment target once the parent is
|
||||
// already concrete.
|
||||
func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounterResponse, error) {
|
||||
gCreateTransactionLock.Lock()
|
||||
defer gCreateTransactionLock.Unlock()
|
||||
@@ -469,7 +531,7 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
if parent.ParentTradeId != "" {
|
||||
return nil, constant.CannotSwitchSubOrder
|
||||
}
|
||||
if parent.Status != mdb.StatusWaitPay {
|
||||
if parent.Status != mdb.StatusWaitPay && parent.Status != mdb.StatusWaitSelect {
|
||||
return nil, constant.OrderNotWaitPay
|
||||
}
|
||||
|
||||
@@ -477,6 +539,10 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
return switchToOkPay(parent, token)
|
||||
}
|
||||
|
||||
if parent.Status == mdb.StatusWaitSelect {
|
||||
return completeWaitSelectOrder(parent, token, network)
|
||||
}
|
||||
|
||||
// 2. Same token+network as parent → mark selected and return
|
||||
if strings.EqualFold(parent.Token, token) && strings.EqualFold(parent.Network, network) {
|
||||
_ = data.MarkOrderSelected(parent.TradeId)
|
||||
@@ -497,8 +563,9 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
return buildCheckoutResponse(existing), nil
|
||||
}
|
||||
|
||||
// 4. Check sub-order limit
|
||||
count, err := data.CountActiveSubOrders(parent.TradeId)
|
||||
// 4. Only one sub-order may ever be created under a parent. Existing
|
||||
// active sub-orders are reused above; any other target after that is blocked.
|
||||
count, err := data.CountSubOrders(parent.TradeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -585,7 +652,63 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
return buildCheckoutResponse(subOrder), nil
|
||||
}
|
||||
|
||||
func completeWaitSelectOrder(parent *mdb.Orders, token string, network string) (*response.CheckoutCounterResponse, error) {
|
||||
if !data.IsChainEnabled(network) {
|
||||
return nil, constant.ChainNotEnabled
|
||||
}
|
||||
if _, err := ensureEnabledOrderAsset(network, token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rate := config.GetRateForCoin(strings.ToLower(token), strings.ToLower(parent.Currency))
|
||||
if rate <= 0 {
|
||||
return nil, constant.RateAmountErr
|
||||
}
|
||||
decimalPayAmount := decimal.NewFromFloat(parent.Amount)
|
||||
decimalTokenAmount := decimalPayAmount.Mul(decimal.NewFromFloat(rate))
|
||||
if decimalTokenAmount.Cmp(decimal.NewFromFloat(UsdtMinimumPaymentAmount)) == -1 {
|
||||
return nil, constant.PayAmountErr
|
||||
}
|
||||
|
||||
walletAddress, err := data.GetAvailableWalletAddressByNetwork(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(walletAddress) <= 0 {
|
||||
return nil, constant.NotAvailableWalletAddress
|
||||
}
|
||||
|
||||
amount := math.MustParsePrecFloat64(decimalTokenAmount.InexactFloat64(), data.GetAmountPrecision())
|
||||
availableAddress, availableAmount, err := ReserveAvailableWalletAndAmount(parent.TradeId, network, token, amount, walletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if availableAddress == "" {
|
||||
return nil, constant.NotAvailableAmountErr
|
||||
}
|
||||
|
||||
updated, err := data.CompleteWaitSelectOrder(parent.TradeId, network, token, availableAddress, availableAmount)
|
||||
if err != nil {
|
||||
_ = data.UnLockTransactionByTradeId(parent.TradeId)
|
||||
return nil, err
|
||||
}
|
||||
if !updated {
|
||||
_ = data.UnLockTransactionByTradeId(parent.TradeId)
|
||||
return nil, constant.OrderStatusConflict
|
||||
}
|
||||
|
||||
order, err := data.GetOrderInfoByTradeId(parent.TradeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buildCheckoutResponse(order), nil
|
||||
}
|
||||
|
||||
func buildCheckoutResponse(order *mdb.Orders) *response.CheckoutCounterResponse {
|
||||
paymentType := mdb.PaymentTypeGmpay
|
||||
if strings.EqualFold(order.PaymentType, mdb.PaymentTypeEpay) {
|
||||
paymentType = mdb.PaymentTypeEpay
|
||||
}
|
||||
return &response.CheckoutCounterResponse{
|
||||
TradeId: order.TradeId,
|
||||
Amount: order.Amount,
|
||||
@@ -594,14 +717,93 @@ func buildCheckoutResponse(order *mdb.Orders) *response.CheckoutCounterResponse
|
||||
Currency: order.Currency,
|
||||
ReceiveAddress: order.ReceiveAddress,
|
||||
Network: order.Network,
|
||||
Status: order.Status,
|
||||
PaymentType: strings.ToLower(paymentType),
|
||||
ExpirationTime: order.CreatedAt.AddMinutes(config.GetOrderExpirationTime()).TimestampMilli(),
|
||||
RedirectUrl: order.RedirectUrl,
|
||||
PaymentUrl: fmt.Sprintf("%s/pay/checkout-counter/%s", config.GetAppUri(), order.TradeId),
|
||||
CreatedAt: order.CreatedAt.TimestampMilli(),
|
||||
IsSelected: order.IsSelected,
|
||||
}
|
||||
}
|
||||
|
||||
func completeWaitSelectOkPayOrder(parent *mdb.Orders, token string) (*response.CheckoutCounterResponse, error) {
|
||||
rate := config.GetRateForCoin(strings.ToLower(token), strings.ToLower(parent.Currency))
|
||||
if rate <= 0 {
|
||||
return nil, constant.RateAmountErr
|
||||
}
|
||||
decimalPayAmount := decimal.NewFromFloat(parent.Amount)
|
||||
decimalTokenAmount := decimalPayAmount.Mul(decimal.NewFromFloat(rate))
|
||||
if decimalTokenAmount.Cmp(decimal.NewFromFloat(UsdtMinimumPaymentAmount)) == -1 {
|
||||
return nil, constant.PayAmountErr
|
||||
}
|
||||
|
||||
amount := math.MustParsePrecFloat64(decimalTokenAmount.InexactFloat64(), data.GetAmountPrecision())
|
||||
returnURL := strings.TrimSpace(parent.RedirectUrl)
|
||||
if returnURL == "" {
|
||||
returnURL = data.GetOkPayReturnURL()
|
||||
}
|
||||
if returnURL == "" {
|
||||
returnURL = fmt.Sprintf("%s/pay/checkout-counter/%s", config.GetAppUri(), parent.TradeId)
|
||||
}
|
||||
|
||||
tx := dao.Mdb.Begin()
|
||||
providerRow := &mdb.ProviderOrder{
|
||||
TradeId: parent.TradeId,
|
||||
Provider: mdb.PaymentProviderOkPay,
|
||||
ProviderOrderID: "",
|
||||
PayURL: "",
|
||||
Amount: amount,
|
||||
Coin: token,
|
||||
Status: mdb.ProviderOrderStatusCreating,
|
||||
NotifyRaw: "",
|
||||
}
|
||||
if err := data.UpsertProviderOrderCreatingWithTransaction(tx, providerRow); err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
okpayOrder, err := createOkPayDepositOrder(parent.TradeId, amount, token, returnURL)
|
||||
if err != nil {
|
||||
_ = data.MarkProviderOrderFailed(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
return nil, constant.PaymentProviderCreateErr
|
||||
}
|
||||
|
||||
finalizeTx := dao.Mdb.Begin()
|
||||
if err = data.UpdateProviderOrderCreatedWithTransaction(finalizeTx, parent.TradeId, mdb.PaymentProviderOkPay, okpayOrder.ProviderOrderID, okpayOrder.PayURL); err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
return nil, err
|
||||
}
|
||||
updated, err := data.CompleteWaitSelectOkPayOrderWithTransaction(finalizeTx, parent.TradeId, token, amount)
|
||||
if err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
return nil, err
|
||||
}
|
||||
if !updated {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
return nil, constant.OrderStatusConflict
|
||||
}
|
||||
if err = finalizeTx.Commit().Error; err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
order, err := data.GetOrderInfoByTradeId(parent.TradeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := buildCheckoutResponse(order)
|
||||
resp.PaymentUrl = okpayOrder.PayURL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func switchToOkPay(parent *mdb.Orders, token string) (*response.CheckoutCounterResponse, error) {
|
||||
if !data.GetOkPayEnabled() {
|
||||
return nil, constant.PaymentProviderNotEnabled
|
||||
@@ -613,6 +815,29 @@ func switchToOkPay(parent *mdb.Orders, token string) (*response.CheckoutCounterR
|
||||
return nil, constant.PaymentProviderNotSupport
|
||||
}
|
||||
|
||||
if parent.Status == mdb.StatusWaitSelect {
|
||||
return completeWaitSelectOkPayOrder(parent, token)
|
||||
}
|
||||
|
||||
if parent.PayProvider == mdb.PaymentProviderOkPay && strings.EqualFold(parent.Token, token) {
|
||||
providerRow, err := data.GetProviderOrderByTradeIDAndProvider(parent.TradeId, mdb.PaymentProviderOkPay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if providerRow.ID == 0 || strings.TrimSpace(providerRow.PayURL) == "" {
|
||||
return nil, constant.SystemErr
|
||||
}
|
||||
_ = data.MarkOrderSelected(parent.TradeId)
|
||||
_ = data.RefreshOrderExpiration(parent.TradeId)
|
||||
order, err := data.GetOrderInfoByTradeId(parent.TradeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := buildCheckoutResponse(order)
|
||||
resp.PaymentUrl = providerRow.PayURL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
existing, err := data.GetSubOrderByTokenPayProvider(parent.TradeId, token, mdb.PaymentProviderOkPay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -625,16 +850,21 @@ func switchToOkPay(parent *mdb.Orders, token string) (*response.CheckoutCounterR
|
||||
if providerRow.ID == 0 || strings.TrimSpace(providerRow.PayURL) == "" {
|
||||
return nil, constant.SystemErr
|
||||
}
|
||||
_ = data.MarkOrderSelected(parent.TradeId)
|
||||
updated, markErr := data.MarkProviderSwitchParentSelectedWithTransaction(dao.Mdb, parent.TradeId)
|
||||
if markErr != nil {
|
||||
return nil, markErr
|
||||
}
|
||||
if !updated {
|
||||
return nil, constant.OrderStatusConflict
|
||||
}
|
||||
_ = data.MarkOrderSelected(existing.TradeId)
|
||||
_ = data.RefreshOrderExpiration(parent.TradeId)
|
||||
existing.IsSelected = true
|
||||
resp := buildCheckoutResponse(existing)
|
||||
resp.PaymentUrl = providerRow.PayURL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
count, err := data.CountActiveSubOrders(parent.TradeId)
|
||||
count, err := data.CountSubOrders(parent.TradeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -711,14 +941,33 @@ func switchToOkPay(parent *mdb.Orders, token string) (*response.CheckoutCounterR
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, constant.PaymentProviderCreateErr
|
||||
}
|
||||
if err = data.UpdateProviderOrderCreated(subTradeID, mdb.PaymentProviderOkPay, okpayOrder.ProviderOrderID, okpayOrder.PayURL); err != nil {
|
||||
|
||||
finalizeTx := dao.Mdb.Begin()
|
||||
if err = data.UpdateProviderOrderCreatedWithTransaction(finalizeTx, subTradeID, mdb.PaymentProviderOkPay, okpayOrder.ProviderOrderID, okpayOrder.PayURL); err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(subTradeID, mdb.PaymentProviderOkPay)
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, err
|
||||
}
|
||||
updated, err := data.MarkProviderSwitchParentSelectedWithTransaction(finalizeTx, parent.TradeId)
|
||||
if err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(subTradeID, mdb.PaymentProviderOkPay)
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, err
|
||||
}
|
||||
if !updated {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(subTradeID, mdb.PaymentProviderOkPay)
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, constant.OrderStatusConflict
|
||||
}
|
||||
if err = finalizeTx.Commit().Error; err != nil {
|
||||
finalizeTx.Rollback()
|
||||
_ = data.MarkProviderOrderFailed(subTradeID, mdb.PaymentProviderOkPay)
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = data.MarkOrderSelected(parent.TradeId)
|
||||
_ = data.RefreshOrderExpiration(parent.TradeId)
|
||||
|
||||
resp := buildCheckoutResponse(subOrder)
|
||||
resp.PaymentUrl = okpayOrder.PayURL
|
||||
|
||||
@@ -87,6 +87,85 @@ func TestCreateTransactionRejectsPrivateNotifyURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTransactionCreatesWaitSelectPlaceholderWithoutTokenNetwork(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
req := newCreateTransactionRequest("order_wait_select_1", 12.34)
|
||||
req.Token = ""
|
||||
req.Network = ""
|
||||
resp, err := CreateTransaction(req, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("create placeholder transaction: %v", err)
|
||||
}
|
||||
if resp.Status != mdb.StatusWaitSelect {
|
||||
t.Fatalf("response status = %d, want %d", resp.Status, mdb.StatusWaitSelect)
|
||||
}
|
||||
if resp.Token != "" || resp.ReceiveAddress != "" || resp.ActualAmount != 0 {
|
||||
t.Fatalf("placeholder chain fields = token %q address %q actual %.4f", resp.Token, resp.ReceiveAddress, resp.ActualAmount)
|
||||
}
|
||||
|
||||
order, err := data.GetOrderInfoByTradeId(resp.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("reload placeholder order: %v", err)
|
||||
}
|
||||
if order.Status != mdb.StatusWaitSelect {
|
||||
t.Fatalf("order status = %d, want %d", order.Status, mdb.StatusWaitSelect)
|
||||
}
|
||||
if order.PaymentType != mdb.PaymentTypeGmpay {
|
||||
t.Fatalf("placeholder payment_type = %q, want %q", order.PaymentType, mdb.PaymentTypeGmpay)
|
||||
}
|
||||
if order.Token != "" || order.Network != "" || order.ReceiveAddress != "" || order.ActualAmount != 0 {
|
||||
t.Fatalf("placeholder order has chain fields: %+v", order)
|
||||
}
|
||||
|
||||
var locks int64
|
||||
if err := dao.RuntimeDB.Model(&mdb.TransactionLock{}).Where("trade_id = ?", resp.TradeId).Count(&locks).Error; err != nil {
|
||||
t.Fatalf("count transaction locks: %v", err)
|
||||
}
|
||||
if locks != 0 {
|
||||
t.Fatalf("placeholder lock count = %d, want 0", locks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTransactionRejectsPartialTokenNetwork(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
req := newCreateTransactionRequest("order_partial_token_network_1", 1)
|
||||
req.Token = ""
|
||||
if _, err := CreateTransaction(req, nil); err != constant.ParamsMarshalErr {
|
||||
t.Fatalf("missing token error = %v, want %v", err, constant.ParamsMarshalErr)
|
||||
}
|
||||
|
||||
req = newCreateTransactionRequest("order_partial_token_network_2", 1)
|
||||
req.Network = ""
|
||||
if _, err := CreateTransaction(req, nil); err != constant.ParamsMarshalErr {
|
||||
t.Fatalf("missing network error = %v, want %v", err, constant.ParamsMarshalErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTransactionNormalizesEpayPaymentTypeCase(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
req := newCreateTransactionRequest("order_epay_payment_type_1", 1)
|
||||
req.Token = ""
|
||||
req.Network = ""
|
||||
req.PaymentType = "epay"
|
||||
resp, err := CreateTransaction(req, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("create epay-compatible transaction: %v", err)
|
||||
}
|
||||
order, err := data.GetOrderInfoByTradeId(resp.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("reload order: %v", err)
|
||||
}
|
||||
if order.PaymentType != mdb.PaymentTypeEpay {
|
||||
t.Fatalf("payment_type = %q, want %q", order.PaymentType, mdb.PaymentTypeEpay)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTransactionAssignsIncrementedAmountsAndLocks(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
@@ -459,6 +538,146 @@ func TestSwitchNetworkCreatesTonSubOrderAndRawLock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwitchNetworkCompletesWaitSelectPlaceholderInPlace(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
if err := data.SetSetting("rate", "rate.forced_rate_list", `{"cny":{"usdt":0.1}}`, "json"); err != nil {
|
||||
t.Fatalf("set forced rate: %v", err)
|
||||
}
|
||||
if _, err := data.AddWalletAddress("TWaitSelectAddress001"); err != nil {
|
||||
t.Fatalf("add tron wallet: %v", err)
|
||||
}
|
||||
|
||||
req := newCreateTransactionRequest("order_wait_select_switch_1", 10)
|
||||
req.Token = ""
|
||||
req.Network = ""
|
||||
parentResp, err := CreateTransaction(req, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("create placeholder: %v", err)
|
||||
}
|
||||
if parentResp.Status != mdb.StatusWaitSelect {
|
||||
t.Fatalf("parent response status = %d, want %d", parentResp.Status, mdb.StatusWaitSelect)
|
||||
}
|
||||
|
||||
switched, err := SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkTron,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("switch placeholder to tron/usdt: %v", err)
|
||||
}
|
||||
if switched.TradeId != parentResp.TradeId {
|
||||
t.Fatalf("switch returned trade_id = %q, want parent %q", switched.TradeId, parentResp.TradeId)
|
||||
}
|
||||
if switched.Status != mdb.StatusWaitPay {
|
||||
t.Fatalf("switch status = %d, want %d", switched.Status, mdb.StatusWaitPay)
|
||||
}
|
||||
if switched.IsSelected {
|
||||
t.Fatal("switch is_selected = true, want false")
|
||||
}
|
||||
if switched.PaymentUrl != "" {
|
||||
t.Fatalf("switch payment_url = %q, want empty for unselected chain parent", switched.PaymentUrl)
|
||||
}
|
||||
if switched.Token != "USDT" || switched.Network != mdb.NetworkTron || switched.ReceiveAddress != "TWaitSelectAddress001" {
|
||||
t.Fatalf("switch chain fields = token %q network %q address %q", switched.Token, switched.Network, switched.ReceiveAddress)
|
||||
}
|
||||
if got := fmt.Sprintf("%.2f", switched.ActualAmount); got != "1.00" {
|
||||
t.Fatalf("switch actual amount = %s, want 1.00", got)
|
||||
}
|
||||
|
||||
parent, err := data.GetOrderInfoByTradeId(parentResp.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("reload parent: %v", err)
|
||||
}
|
||||
if parent.Status != mdb.StatusWaitPay || parent.ParentTradeId != "" {
|
||||
t.Fatalf("parent status/parent_trade_id = %d/%q", parent.Status, parent.ParentTradeId)
|
||||
}
|
||||
if parent.IsSelected {
|
||||
t.Fatal("parent is_selected = true, want false")
|
||||
}
|
||||
if parent.PaymentType != mdb.PaymentTypeGmpay {
|
||||
t.Fatalf("parent payment_type = %q, want %q", parent.PaymentType, mdb.PaymentTypeGmpay)
|
||||
}
|
||||
count, err := data.CountActiveSubOrders(parent.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("count sub orders: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("active sub-order count = %d, want 0", count)
|
||||
}
|
||||
lockTradeID, err := data.GetTradeIdByWalletAddressAndAmountAndToken(mdb.NetworkTron, parent.ReceiveAddress, parent.Token, parent.ActualAmount)
|
||||
if err != nil {
|
||||
t.Fatalf("lookup parent lock: %v", err)
|
||||
}
|
||||
if lockTradeID != parent.TradeId {
|
||||
t.Fatalf("parent lock trade_id = %q, want %q", lockTradeID, parent.TradeId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwitchNetworkAfterWaitSelectCompletionUsesExistingSubOrderFlow(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
if err := data.SetSetting("rate", "rate.forced_rate_list", `{"cny":{"usdt":0.1}}`, "json"); err != nil {
|
||||
t.Fatalf("set forced rate: %v", err)
|
||||
}
|
||||
if _, err := data.AddWalletAddress("TWaitSelectAddress002"); err != nil {
|
||||
t.Fatalf("add tron wallet: %v", err)
|
||||
}
|
||||
if _, err := data.AddWalletAddressWithNetwork(mdb.NetworkEthereum, "0xA1B2c3D4e5F60718293aBcDeF001122334455668"); err != nil {
|
||||
t.Fatalf("add ethereum wallet: %v", err)
|
||||
}
|
||||
|
||||
req := newCreateTransactionRequest("order_wait_select_reswitch_1", 10)
|
||||
req.Token = ""
|
||||
req.Network = ""
|
||||
parentResp, err := CreateTransaction(req, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("create placeholder: %v", err)
|
||||
}
|
||||
first, err := SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkTron,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("first switch: %v", err)
|
||||
}
|
||||
|
||||
second, err := SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkEthereum,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("second switch: %v", err)
|
||||
}
|
||||
if second.TradeId == first.TradeId {
|
||||
t.Fatalf("second switch trade_id = parent %q, want sub-order", second.TradeId)
|
||||
}
|
||||
if second.Network != mdb.NetworkEthereum || second.Token != "USDT" {
|
||||
t.Fatalf("second switch token/network = %s/%s", second.Token, second.Network)
|
||||
}
|
||||
count, err := data.CountActiveSubOrders(parentResp.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("count active sub orders: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("active sub-order count = %d, want 1", count)
|
||||
}
|
||||
|
||||
_, err = SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkBsc,
|
||||
})
|
||||
if err != constant.SubOrderLimitExceeded {
|
||||
t.Fatalf("third switch error = %v, want %v", err, constant.SubOrderLimitExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwitchNetworkRejectsUnsupportedTonToken(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
@@ -1219,7 +1438,6 @@ func TestOrderProcessingSubOrderExpiresSiblingsAndReleasesLocks(t *testing.T) {
|
||||
t.Fatalf("create parent order: %v", err)
|
||||
}
|
||||
|
||||
// Create two sub-orders on different networks.
|
||||
subEthResp, err := SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "usdt",
|
||||
@@ -1229,13 +1447,29 @@ func TestOrderProcessingSubOrderExpiresSiblingsAndReleasesLocks(t *testing.T) {
|
||||
t.Fatalf("switch to ethereum sub-order: %v", err)
|
||||
}
|
||||
|
||||
subBscResp, err := SwitchNetwork(&request.SwitchNetworkRequest{
|
||||
TradeId: parentResp.TradeId,
|
||||
Token: "usdt",
|
||||
Network: mdb.NetworkBsc,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("switch to bsc sub-order: %v", err)
|
||||
bscTradeID := GenerateCode()
|
||||
bscAddress := strings.ToLower("0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
|
||||
subBsc := &mdb.Orders{
|
||||
TradeId: bscTradeID,
|
||||
OrderId: bscTradeID,
|
||||
ParentTradeId: parentResp.TradeId,
|
||||
Amount: parentResp.Amount,
|
||||
Currency: parentResp.Currency,
|
||||
ActualAmount: subEthResp.ActualAmount,
|
||||
ReceiveAddress: bscAddress,
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkBsc,
|
||||
Status: mdb.StatusWaitPay,
|
||||
IsSelected: true,
|
||||
CallBackConfirm: mdb.CallBackConfirmOk,
|
||||
PaymentType: mdb.PaymentTypeGmpay,
|
||||
PayProvider: mdb.PaymentProviderOnChain,
|
||||
}
|
||||
if err := dao.Mdb.Create(subBsc).Error; err != nil {
|
||||
t.Fatalf("seed bsc sibling sub-order: %v", err)
|
||||
}
|
||||
if err := data.LockTransaction(mdb.NetworkBsc, bscAddress, "USDT", bscTradeID, subBsc.ActualAmount, time.Hour); err != nil {
|
||||
t.Fatalf("lock bsc sibling amount: %v", err)
|
||||
}
|
||||
|
||||
// Pay the Ethereum sub-order.
|
||||
@@ -1252,12 +1486,12 @@ func TestOrderProcessingSubOrderExpiresSiblingsAndReleasesLocks(t *testing.T) {
|
||||
}
|
||||
|
||||
// BSC sibling must be expired.
|
||||
subBsc, err := data.GetOrderInfoByTradeId(subBscResp.TradeId)
|
||||
reloadedBsc, err := data.GetOrderInfoByTradeId(subBsc.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("reload bsc sub-order: %v", err)
|
||||
}
|
||||
if subBsc.Status != mdb.StatusExpired {
|
||||
t.Fatalf("bsc sibling status = %d, want %d (expired)", subBsc.Status, mdb.StatusExpired)
|
||||
if reloadedBsc.Status != mdb.StatusExpired {
|
||||
t.Fatalf("bsc sibling status = %d, want %d (expired)", reloadedBsc.Status, mdb.StatusExpired)
|
||||
}
|
||||
|
||||
// Parent runtime lock must be released.
|
||||
@@ -1272,7 +1506,7 @@ func TestOrderProcessingSubOrderExpiresSiblingsAndReleasesLocks(t *testing.T) {
|
||||
|
||||
// BSC sibling runtime lock must be released.
|
||||
sibLock, err := data.GetTradeIdByWalletAddressAndAmountAndToken(
|
||||
mdb.NetworkBsc, subBscResp.ReceiveAddress, subBscResp.Token, subBscResp.ActualAmount)
|
||||
mdb.NetworkBsc, subBsc.ReceiveAddress, subBsc.Token, subBsc.ActualAmount)
|
||||
if err != nil {
|
||||
t.Fatalf("check bsc sibling runtime lock: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/GMWalletApp/epusdt/config"
|
||||
"github.com/GMWalletApp/epusdt/model/data"
|
||||
"github.com/GMWalletApp/epusdt/model/mdb"
|
||||
"github.com/GMWalletApp/epusdt/model/response"
|
||||
"github.com/GMWalletApp/epusdt/util/constant"
|
||||
)
|
||||
@@ -20,6 +23,10 @@ func GetCheckoutCounterByTradeId(tradeId string) (*response.CheckoutCounterRespo
|
||||
if orderInfo.ID <= 0 {
|
||||
return nil, ErrOrder
|
||||
}
|
||||
paymentType := mdb.PaymentTypeGmpay
|
||||
if strings.EqualFold(orderInfo.PaymentType, mdb.PaymentTypeEpay) {
|
||||
paymentType = mdb.PaymentTypeEpay
|
||||
}
|
||||
|
||||
resp := &response.CheckoutCounterResponse{
|
||||
TradeId: orderInfo.TradeId,
|
||||
@@ -29,10 +36,21 @@ func GetCheckoutCounterByTradeId(tradeId string) (*response.CheckoutCounterRespo
|
||||
Currency: orderInfo.Currency,
|
||||
ReceiveAddress: orderInfo.ReceiveAddress,
|
||||
Network: orderInfo.Network,
|
||||
Status: orderInfo.Status,
|
||||
PaymentType: strings.ToLower(paymentType),
|
||||
ExpirationTime: orderInfo.CreatedAt.AddMinutes(config.GetOrderExpirationTime()).TimestampMilli(),
|
||||
RedirectUrl: orderInfo.RedirectUrl,
|
||||
CreatedAt: orderInfo.CreatedAt.TimestampMilli(),
|
||||
IsSelected: orderInfo.IsSelected,
|
||||
}
|
||||
if orderInfo.PayProvider == mdb.PaymentProviderOkPay {
|
||||
providerRow, rowErr := data.GetProviderOrderByTradeIDAndProvider(orderInfo.TradeId, mdb.PaymentProviderOkPay)
|
||||
if rowErr != nil {
|
||||
return nil, rowErr
|
||||
}
|
||||
if providerRow.ID > 0 {
|
||||
resp.PaymentUrl = providerRow.PayURL
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user