mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 18:26:16 +00:00
feat: integrate okpay switch-network flow and notify handling
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GMWalletApp/epusdt/model/data"
|
||||
"github.com/GMWalletApp/epusdt/model/request"
|
||||
"github.com/GMWalletApp/epusdt/util/constant"
|
||||
"github.com/GMWalletApp/epusdt/util/http_client"
|
||||
"github.com/GMWalletApp/epusdt/util/log"
|
||||
)
|
||||
|
||||
type okPayCreateDepositResponse struct {
|
||||
Status string `json:"status"`
|
||||
Code int `json:"code"`
|
||||
Data struct {
|
||||
OrderID string `json:"order_id"`
|
||||
PayURL string `json:"pay_url"`
|
||||
} `json:"data"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type okPayDepositOrder struct {
|
||||
ProviderOrderID string
|
||||
PayURL string
|
||||
}
|
||||
|
||||
type okPayNotifyPayload struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Code string `json:"code"`
|
||||
OrderID string `json:"order_id"`
|
||||
UniqueID string `json:"unique_id"`
|
||||
PayUserID string `json:"pay_user_id"`
|
||||
Amount string `json:"amount"`
|
||||
Coin string `json:"coin"`
|
||||
PayStatus string `json:"pay_status"`
|
||||
NotifyType string `json:"notify_type"`
|
||||
Sign string `json:"sign"`
|
||||
RawFormData string `json:"raw_form_data"`
|
||||
}
|
||||
|
||||
func okPaySign(form map[string]string, shopID string, shopToken string) map[string]string {
|
||||
values := url.Values{}
|
||||
for key, value := range form {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
continue
|
||||
}
|
||||
values.Set(key, value)
|
||||
}
|
||||
values.Set("id", shopID)
|
||||
query, _ := url.QueryUnescape(values.Encode())
|
||||
sum := md5.Sum([]byte(query + "&token=" + shopToken))
|
||||
|
||||
signed := make(map[string]string, len(values)+1)
|
||||
for key, vals := range values {
|
||||
if len(vals) == 0 {
|
||||
continue
|
||||
}
|
||||
signed[key] = vals[0]
|
||||
}
|
||||
signed["sign"] = strings.ToUpper(hex.EncodeToString(sum[:]))
|
||||
return signed
|
||||
}
|
||||
|
||||
func createOkPayDepositOrder(uniqueID string, amount float64, coin string, returnURL string) (*okPayDepositOrder, error) {
|
||||
shopID := data.GetOkPayShopID()
|
||||
shopToken := data.GetOkPayShopToken()
|
||||
apiURL := strings.TrimSpace(data.GetOkPayAPIURL())
|
||||
callbackURL := data.GetOkPayCallbackURL()
|
||||
if shopID == "" || shopToken == "" || apiURL == "" || callbackURL == "" {
|
||||
return nil, fmt.Errorf("okpay config incomplete")
|
||||
}
|
||||
|
||||
if returnURL == "" {
|
||||
returnURL = data.GetOkPayReturnURL()
|
||||
}
|
||||
|
||||
form := map[string]string{
|
||||
"unique_id": uniqueID,
|
||||
"name": uniqueID,
|
||||
"amount": fmt.Sprintf("%.2f", amount),
|
||||
"coin": strings.ToUpper(strings.TrimSpace(coin)),
|
||||
"callback_url": callbackURL,
|
||||
"return_url": returnURL,
|
||||
}
|
||||
form = okPaySign(form, shopID, shopToken)
|
||||
|
||||
base, err := url.Parse(apiURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base.Path = path.Join(base.Path, "payLink")
|
||||
|
||||
client := http_client.GetHttpClient()
|
||||
client.SetTimeout(time.Duration(data.GetOkPayTimeoutSeconds()) * time.Second)
|
||||
|
||||
var payload okPayCreateDepositResponse
|
||||
resp, err := client.R().
|
||||
SetFormData(form).
|
||||
SetResult(&payload).
|
||||
Post(base.String())
|
||||
if err != nil {
|
||||
log.Sugar.Warnf("[okpay] create order request failed unique_id=%s api_url=%s callback_url=%s return_url=%s err=%v", uniqueID, base.String(), callbackURL, returnURL, err)
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode() >= 400 {
|
||||
log.Sugar.Warnf("[okpay] create order http error unique_id=%s status=%s api_url=%s callback_url=%s return_url=%s body=%s", uniqueID, resp.Status(), base.String(), callbackURL, returnURL, resp.String())
|
||||
return nil, fmt.Errorf("okpay http status: %s", resp.Status())
|
||||
}
|
||||
if payload.Data.OrderID == "" || payload.Data.PayURL == "" {
|
||||
log.Sugar.Warnf("[okpay] create order rejected unique_id=%s api_url=%s callback_url=%s return_url=%s status=%s code=%d msg=%s", uniqueID, base.String(), callbackURL, returnURL, payload.Status, payload.Code, payload.Msg)
|
||||
if payload.Msg != "" {
|
||||
return nil, fmt.Errorf("okpay create order failed: %s (callback_url=%s)", payload.Msg, callbackURL)
|
||||
}
|
||||
return nil, fmt.Errorf("okpay create order failed (callback_url=%s)", callbackURL)
|
||||
}
|
||||
|
||||
return &okPayDepositOrder{
|
||||
ProviderOrderID: payload.Data.OrderID,
|
||||
PayURL: payload.Data.PayURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func verifyOkPayNotify(form map[string]string) bool {
|
||||
shopID := data.GetOkPayShopID()
|
||||
shopToken := data.GetOkPayShopToken()
|
||||
signature := strings.TrimSpace(form["sign"])
|
||||
if shopID == "" || shopToken == "" || signature == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if expected := okPayNotifySign(form, shopToken); expected != "" && strings.EqualFold(expected, signature) {
|
||||
return true
|
||||
}
|
||||
|
||||
unsigned := make(map[string]string, len(form))
|
||||
for key, value := range form {
|
||||
if strings.EqualFold(key, "sign") {
|
||||
continue
|
||||
}
|
||||
unsigned[key] = value
|
||||
}
|
||||
signed := okPaySign(unsigned, shopID, shopToken)
|
||||
return strings.EqualFold(strings.TrimSpace(signed["sign"]), signature)
|
||||
}
|
||||
|
||||
func okPayNotifySign(form map[string]string, shopToken string) string {
|
||||
orderedKeys := []string{
|
||||
"code",
|
||||
"data[order_id]",
|
||||
"data[unique_id]",
|
||||
"data[pay_user_id]",
|
||||
"data[amount]",
|
||||
"data[coin]",
|
||||
"data[status]",
|
||||
"data[type]",
|
||||
"id",
|
||||
"status",
|
||||
}
|
||||
|
||||
pairs := make([]string, 0, len(orderedKeys))
|
||||
for _, key := range orderedKeys {
|
||||
value := strings.TrimSpace(form[key])
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
pairs = append(pairs, url.QueryEscape(key)+"="+url.QueryEscape(value))
|
||||
}
|
||||
if len(pairs) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
query, err := url.QueryUnescape(strings.Join(pairs, "&"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
sum := md5.Sum([]byte(query + "&token=" + shopToken))
|
||||
return strings.ToUpper(hex.EncodeToString(sum[:]))
|
||||
}
|
||||
|
||||
func parseOkPayNotify(form map[string]string, rawFormData string) *okPayNotifyPayload {
|
||||
return &okPayNotifyPayload{
|
||||
ID: strings.TrimSpace(form["id"]),
|
||||
Status: strings.TrimSpace(form["status"]),
|
||||
Code: strings.TrimSpace(form["code"]),
|
||||
OrderID: strings.TrimSpace(form["data[order_id]"]),
|
||||
UniqueID: strings.TrimSpace(form["data[unique_id]"]),
|
||||
PayUserID: strings.TrimSpace(form["data[pay_user_id]"]),
|
||||
Amount: strings.TrimSpace(form["data[amount]"]),
|
||||
Coin: strings.TrimSpace(form["data[coin]"]),
|
||||
PayStatus: strings.TrimSpace(form["data[status]"]),
|
||||
NotifyType: strings.TrimSpace(form["data[type]"]),
|
||||
Sign: strings.TrimSpace(form["sign"]),
|
||||
RawFormData: rawFormData,
|
||||
}
|
||||
}
|
||||
|
||||
func HandleOkPayNotify(form map[string]string, rawFormData string) error {
|
||||
if !verifyOkPayNotify(form) {
|
||||
return fmt.Errorf("invalid okpay notify sign")
|
||||
}
|
||||
|
||||
payload := parseOkPayNotify(form, rawFormData)
|
||||
|
||||
if payload.ID == "" || payload.ID != data.GetOkPayShopID() {
|
||||
return fmt.Errorf("okpay notify shop id mismatch: %s", payload.ID)
|
||||
}
|
||||
if payload.UniqueID == "" || payload.OrderID == "" {
|
||||
return fmt.Errorf("missing okpay notify identifiers")
|
||||
}
|
||||
|
||||
order, err := data.GetOrderInfoByTradeId(payload.UniqueID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if order.ID <= 0 {
|
||||
return fmt.Errorf("okpay notify order not found: %s", payload.UniqueID)
|
||||
}
|
||||
if order.PayProvider != "okpay" {
|
||||
return fmt.Errorf("okpay notify order provider mismatch: %s", payload.UniqueID)
|
||||
}
|
||||
|
||||
providerRow, err := data.GetProviderOrderByTradeIDAndProvider(payload.UniqueID, order.PayProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if providerRow.ID <= 0 {
|
||||
return fmt.Errorf("okpay provider order not found: %s", payload.UniqueID)
|
||||
}
|
||||
|
||||
_ = data.SaveProviderOrderNotify(payload.UniqueID, order.PayProvider, payload.RawFormData)
|
||||
|
||||
if !strings.EqualFold(payload.Status, "success") || !strings.EqualFold(payload.NotifyType, "deposit") || payload.PayStatus != "1" {
|
||||
log.Sugar.Infof("[okpay] notify ignored trade_id=%s status=%s pay_status=%s type=%s", payload.UniqueID, payload.Status, payload.PayStatus, payload.NotifyType)
|
||||
return nil
|
||||
}
|
||||
|
||||
if providerRow.ProviderOrderID != "" && providerRow.ProviderOrderID != payload.OrderID {
|
||||
return fmt.Errorf("okpay provider order id mismatch: got=%s want=%s", payload.OrderID, providerRow.ProviderOrderID)
|
||||
}
|
||||
if !strings.EqualFold(strings.TrimSpace(order.Token), strings.TrimSpace(payload.Coin)) {
|
||||
return fmt.Errorf("okpay coin mismatch: got=%s want=%s", payload.Coin, order.Token)
|
||||
}
|
||||
|
||||
notifyAmount, err := strconv.ParseFloat(payload.Amount, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid okpay amount: %w", err)
|
||||
}
|
||||
if fmt.Sprintf("%.2f", notifyAmount) != fmt.Sprintf("%.2f", order.ActualAmount) {
|
||||
return fmt.Errorf("okpay amount mismatch: got=%.2f want=%.2f", notifyAmount, order.ActualAmount)
|
||||
}
|
||||
|
||||
err = OrderProcessing(&request.OrderProcessingRequest{
|
||||
ReceiveAddress: order.ReceiveAddress,
|
||||
Currency: order.Currency,
|
||||
Token: order.Token,
|
||||
Network: order.Network,
|
||||
Amount: order.ActualAmount,
|
||||
TradeId: order.TradeId,
|
||||
BlockTransactionId: payload.OrderID,
|
||||
})
|
||||
if err != nil && err != constant.OrderBlockAlreadyProcess {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = data.MarkProviderOrderPaid(payload.UniqueID, order.PayProvider, payload.RawFormData); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -120,6 +120,7 @@ func CreateTransaction(req *request.CreateTransactionRequest, apiKey *mdb.ApiKey
|
||||
RedirectUrl: req.RedirectUrl,
|
||||
Name: req.Name,
|
||||
PaymentType: req.PaymentType,
|
||||
PayProvider: mdb.PaymentProviderOnChain,
|
||||
ApiKeyID: apiKeyID(apiKey),
|
||||
}
|
||||
if err = data.CreateOrderWithTransaction(tx, order); err != nil {
|
||||
@@ -199,6 +200,11 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
if err = data.ExpireOrderByTradeId(sub.TradeId); err != nil {
|
||||
log.Sugar.Warnf("[order] expire sub-order failed, trade_id=%s, err=%v", sub.TradeId, err)
|
||||
}
|
||||
if sub.PayProvider != "" && sub.PayProvider != mdb.PaymentProviderOnChain {
|
||||
if err = data.MarkProviderOrderExpired(sub.TradeId, sub.PayProvider); err != nil {
|
||||
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)
|
||||
}
|
||||
@@ -257,6 +263,11 @@ func OrderProcessing(req *request.OrderProcessingRequest) error {
|
||||
|
||||
// Release sibling locks after their status transitions commit.
|
||||
for _, sib := range siblings {
|
||||
if sib.PayProvider != "" && sib.PayProvider != mdb.PaymentProviderOnChain {
|
||||
if err = data.MarkProviderOrderExpired(sib.TradeId, sib.PayProvider); err != nil {
|
||||
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)
|
||||
}
|
||||
@@ -346,6 +357,10 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
return nil, constant.OrderNotWaitPay
|
||||
}
|
||||
|
||||
if network == mdb.PaymentProviderOkPay {
|
||||
return switchToOkPay(parent, token)
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -427,6 +442,7 @@ func SwitchNetwork(req *request.SwitchNetworkRequest) (*response.CheckoutCounter
|
||||
Name: parent.Name,
|
||||
CallBackConfirm: mdb.CallBackConfirmOk, // don't trigger callback on sub-order
|
||||
PaymentType: parent.PaymentType,
|
||||
PayProvider: mdb.PaymentProviderOnChain,
|
||||
ApiKeyID: parent.ApiKeyID, // inherit from parent so resolveOrderApiKey never fails
|
||||
}
|
||||
if err = data.CreateOrderWithTransaction(tx, subOrder); err != nil {
|
||||
@@ -458,7 +474,140 @@ func buildCheckoutResponse(order *mdb.Orders) *response.CheckoutCounterResponse
|
||||
Network: order.Network,
|
||||
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 switchToOkPay(parent *mdb.Orders, token string) (*response.CheckoutCounterResponse, error) {
|
||||
if !data.GetOkPayEnabled() {
|
||||
return nil, constant.PaymentProviderNotEnabled
|
||||
}
|
||||
if data.GetOkPayShopID() == "" || data.GetOkPayShopToken() == "" || data.GetOkPayAPIURL() == "" || data.GetOkPayCallbackURL() == "" {
|
||||
return nil, constant.PaymentProviderConfigErr
|
||||
}
|
||||
if !okPayTokenAllowed(token) {
|
||||
return nil, constant.PaymentProviderNotSupport
|
||||
}
|
||||
|
||||
existing, err := data.GetSubOrderByTokenPayProvider(parent.TradeId, token, mdb.PaymentProviderOkPay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing.ID > 0 {
|
||||
providerRow, err := data.GetProviderOrderByTradeIDAndProvider(existing.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.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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count >= MaxSubOrders {
|
||||
return nil, constant.SubOrderLimitExceeded
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
subTradeID := GenerateCode()
|
||||
amount := math.MustParsePrecFloat64(decimalTokenAmount.InexactFloat64(), 2)
|
||||
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()
|
||||
subOrder := &mdb.Orders{
|
||||
TradeId: subTradeID,
|
||||
OrderId: subTradeID,
|
||||
ParentTradeId: parent.TradeId,
|
||||
Amount: parent.Amount,
|
||||
Currency: parent.Currency,
|
||||
ActualAmount: amount,
|
||||
ReceiveAddress: "OKPAY",
|
||||
Token: token,
|
||||
Network: mdb.NetworkTron,
|
||||
Status: mdb.StatusWaitPay,
|
||||
IsSelected: true,
|
||||
NotifyUrl: "",
|
||||
RedirectUrl: parent.RedirectUrl,
|
||||
Name: parent.Name,
|
||||
CallBackConfirm: mdb.CallBackConfirmOk,
|
||||
PaymentType: parent.PaymentType,
|
||||
PayProvider: mdb.PaymentProviderOkPay,
|
||||
ApiKeyID: parent.ApiKeyID,
|
||||
}
|
||||
if err = data.CreateOrderWithTransaction(tx, subOrder); err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
providerRow := &mdb.ProviderOrder{
|
||||
TradeId: subTradeID,
|
||||
Provider: mdb.PaymentProviderOkPay,
|
||||
ProviderOrderID: "",
|
||||
PayURL: "",
|
||||
Amount: amount,
|
||||
Coin: token,
|
||||
Status: mdb.ProviderOrderStatusCreating,
|
||||
}
|
||||
if err = data.CreateProviderOrderWithTransaction(tx, providerRow); err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err = tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
okpayOrder, err := createOkPayDepositOrder(subTradeID, amount, token, returnURL)
|
||||
if err != nil {
|
||||
_ = data.MarkProviderOrderFailed(subTradeID, mdb.PaymentProviderOkPay)
|
||||
_ = data.ExpireOrderByTradeID(subTradeID)
|
||||
return nil, err
|
||||
}
|
||||
if err = data.UpdateProviderOrderCreated(subTradeID, mdb.PaymentProviderOkPay, okpayOrder.ProviderOrderID, okpayOrder.PayURL); err != nil {
|
||||
_ = 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
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func okPayTokenAllowed(token string) bool {
|
||||
for _, item := range data.GetOkPayAllowTokens() {
|
||||
if strings.EqualFold(item, token) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -612,6 +612,81 @@ func TestOrderProcessingSubOrderPaidParentKeepsOwnFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderProcessingParentDirectPayExpiresOkPayProviderOrder(t *testing.T) {
|
||||
cleanup := testutil.SetupTestDatabases(t)
|
||||
defer cleanup()
|
||||
|
||||
if _, err := data.AddWalletAddress("TTestTronAddress001"); err != nil {
|
||||
t.Fatalf("add tron wallet: %v", err)
|
||||
}
|
||||
|
||||
parentReq := newCreateTransactionRequest("order_parent_direct_okpay_expire", 1)
|
||||
parentReq.Network = mdb.NetworkTron
|
||||
parentResp, err := CreateTransaction(parentReq, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("create parent order: %v", err)
|
||||
}
|
||||
|
||||
subOrder := &mdb.Orders{
|
||||
TradeId: "okpay_sub_parent_direct_expire",
|
||||
OrderId: "okpay_sub_parent_direct_expire",
|
||||
ParentTradeId: parentResp.TradeId,
|
||||
Amount: parentResp.Amount,
|
||||
Currency: parentResp.Currency,
|
||||
ActualAmount: 0.15,
|
||||
ReceiveAddress: "OKPAY",
|
||||
Token: "USDT",
|
||||
Network: mdb.NetworkTron,
|
||||
Status: mdb.StatusWaitPay,
|
||||
NotifyUrl: "",
|
||||
CallBackConfirm: mdb.CallBackConfirmOk,
|
||||
PayProvider: mdb.PaymentProviderOkPay,
|
||||
}
|
||||
if err := dao.Mdb.Create(subOrder).Error; err != nil {
|
||||
t.Fatalf("create okpay sub-order: %v", err)
|
||||
}
|
||||
providerRow := &mdb.ProviderOrder{
|
||||
TradeId: subOrder.TradeId,
|
||||
Provider: mdb.PaymentProviderOkPay,
|
||||
ProviderOrderID: "okp-parent-direct-expire",
|
||||
PayURL: "https://t.me/ExampleWalletBot?start=shop_deposit--okpay-order-parent-direct-expire",
|
||||
Amount: subOrder.ActualAmount,
|
||||
Coin: subOrder.Token,
|
||||
Status: mdb.ProviderOrderStatusPending,
|
||||
}
|
||||
if err := dao.Mdb.Create(providerRow).Error; err != nil {
|
||||
t.Fatalf("create provider row: %v", err)
|
||||
}
|
||||
|
||||
err = OrderProcessing(&request.OrderProcessingRequest{
|
||||
ReceiveAddress: parentResp.ReceiveAddress,
|
||||
Token: strings.ToUpper(parentResp.Token),
|
||||
Network: mdb.NetworkTron,
|
||||
TradeId: parentResp.TradeId,
|
||||
Amount: parentResp.ActualAmount,
|
||||
BlockTransactionId: "block_parent_direct_okpay_expire",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("order processing parent direct pay: %v", err)
|
||||
}
|
||||
|
||||
expiredSub, err := data.GetOrderInfoByTradeId(subOrder.TradeId)
|
||||
if err != nil {
|
||||
t.Fatalf("reload sub-order: %v", err)
|
||||
}
|
||||
if expiredSub.Status != mdb.StatusExpired {
|
||||
t.Fatalf("sub-order status = %d, want %d", expiredSub.Status, mdb.StatusExpired)
|
||||
}
|
||||
|
||||
expiredProviderRow, err := data.GetProviderOrderByTradeIDAndProvider(subOrder.TradeId, mdb.PaymentProviderOkPay)
|
||||
if err != nil {
|
||||
t.Fatalf("reload provider row: %v", err)
|
||||
}
|
||||
if expiredProviderRow.Status != mdb.ProviderOrderStatusExpired {
|
||||
t.Fatalf("provider row status = %q, want %q", expiredProviderRow.Status, mdb.ProviderOrderStatusExpired)
|
||||
}
|
||||
}
|
||||
|
||||
// TestOrderProcessingSubOrderExpiresSiblingsAndReleasesLocks verifies that when one
|
||||
// sub-order is paid all sibling sub-orders are expired and their runtime locks (as
|
||||
// well as the parent's lock) are released.
|
||||
|
||||
Reference in New Issue
Block a user