Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
462 changes: 269 additions & 193 deletions controllers/accounts/profile.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions controllers/accounts/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ func TestProfile(t *testing.T) {
var response types.Response
err = json.Unmarshal(res.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "Rate slippage is too high", response.Message)
assert.Equal(t, "Rate slippage is too high for TST", response.Message)
})

t.Run("fails when rate slippage is less than 0.1", func(t *testing.T) {
Expand All @@ -483,7 +483,7 @@ func TestProfile(t *testing.T) {
var response types.Response
err = json.Unmarshal(res.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "Rate slippage cannot be less than 0.1%", response.Message)
assert.Equal(t, "Rate slippage cannot be less than 0.1% for TST", response.Message)
})

t.Run("succeeds with valid rate slippage", func(t *testing.T) {
Expand Down
38 changes: 35 additions & 3 deletions controllers/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,9 @@ func TestProvider(t *testing.T) {
"status": "success",
"message": "Node is live",
"data": map[string]interface{}{
"currencies": []string{"NGN"},
"serviceInfo": map[string]interface{}{
"currencies": []string{"NGN"},
},
},
})
},
Expand Down Expand Up @@ -1509,14 +1511,29 @@ func TestProvider(t *testing.T) {
})
assert.NoError(t, err)

// Create a provision bucket for the order
provisionBucket, err := db.Client.ProvisionBucket.
Create().
SetMinAmount(decimal.NewFromFloat(100.0)).
SetMaxAmount(decimal.NewFromFloat(1000.0)).
SetCurrency(testCtx.currency).
Save(context.Background())
assert.NoError(t, err)

// Add provision bucket to the order
order, err = order.Update().
SetProvisionBucket(provisionBucket).
Save(context.Background())
assert.NoError(t, err)

orderKey := fmt.Sprintf("order_request_%s", order.ID)

user, err := test.CreateTestUser(map[string]interface{}{
"email": "[email protected]",
})
assert.NoError(t, err)

providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{
_, err = test.CreateTestProviderProfile(map[string]interface{}{
"user_id": user.ID,
"currency_id": testCtx.currency.ID,
})
Expand All @@ -1525,7 +1542,7 @@ func TestProvider(t *testing.T) {
orderRequestData := map[string]interface{}{
"amount": order.Amount.Mul(order.Rate).RoundBank(0).String(),
"institution": order.Institution,
"providerId": providerProfile.ID,
"providerId": testCtx.provider.ID, // Use the same provider as the order
}

err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err()
Expand Down Expand Up @@ -1563,6 +1580,21 @@ func TestProvider(t *testing.T) {
})
assert.NoError(t, err)

// Create a provision bucket for the order
provisionBucket, err := db.Client.ProvisionBucket.
Create().
SetMinAmount(decimal.NewFromFloat(100.0)).
SetMaxAmount(decimal.NewFromFloat(1000.0)).
SetCurrency(testCtx.currency).
Save(context.Background())
assert.NoError(t, err)

// Add provision bucket to the order
order, err = order.Update().
SetProvisionBucket(provisionBucket).
Save(context.Background())
assert.NoError(t, err)

orderKey := fmt.Sprintf("order_request_%s", order.ID)

orderRequestData := map[string]interface{}{
Expand Down
24 changes: 17 additions & 7 deletions controllers/sender/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,10 @@ func (ctrl *SenderController) InitiatePaymentOrder(ctx *gin.Context) {
).
Exist(ctx)
if err != nil {
logger.Errorf("error: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", nil)
logger.Errorf("Reference check error: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", map[string]interface{}{
"context": "reference_check",
})
return
}

Expand Down Expand Up @@ -235,7 +237,9 @@ func (ctrl *SenderController) InitiatePaymentOrder(ctx *gin.Context) {
})
} else {
logger.Errorf("Failed to fetch institution: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to validate institution", nil)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to validate institution", map[string]interface{}{
"context": "institution_fetch",
})
}
return
}
Expand Down Expand Up @@ -348,7 +352,9 @@ func (ctrl *SenderController) InitiatePaymentOrder(ctx *gin.Context) {
rateResponse, err := u.GetTokenRateFromQueue("USDT", normalizedAmount, institutionObj.Edges.FiatCurrency.Code, institutionObj.Edges.FiatCurrency.MarketRate)
if err != nil {
logger.Errorf("InitiatePaymentOrder.GetTokenRateFromQueue: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", nil)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", map[string]interface{}{
"context": "token_rate_queue",
})
return
}
normalizedAmount = payload.Amount.Div(rateResponse)
Expand All @@ -369,8 +375,10 @@ func (ctrl *SenderController) InitiatePaymentOrder(ctx *gin.Context) {
if strings.HasPrefix(payload.Network, "tron") {
address, salt, err := ctrl.receiveAddressService.CreateTronAddress(ctx)
if err != nil {
logger.Errorf("error: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", nil)
logger.Errorf("CreateTronAddress error: %v", err)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", map[string]interface{}{
"context": "create_tron_address",
})
return
}

Expand Down Expand Up @@ -398,7 +406,9 @@ func (ctrl *SenderController) InitiatePaymentOrder(ctx *gin.Context) {
"error": err,
"address": address,
}).Errorf("Failed to create receive address")
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", nil)
u.APIResponse(ctx, http.StatusInternalServerError, "error", "Failed to initiate payment order", map[string]interface{}{
"context": "create_smart_address",
})
return
}

Expand Down
62 changes: 50 additions & 12 deletions controllers/sender/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"strconv"
"testing"
"time"

"github.com/alicebob/miniredis/v2"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/jarcoal/httpmock"
_ "github.com/mattn/go-sqlite3"
"github.com/paycrest/aggregator/ent"
"github.com/paycrest/aggregator/ent/enttest"
Expand Down Expand Up @@ -132,7 +133,6 @@ func setup() error {
testCtx.apiKeySecret = secretKey

for i := 0; i < 9; i++ {
time.Sleep(time.Duration(float64(rand.Intn(12))) * time.Second)

// Create a simple payment order without blockchain dependency
address := fmt.Sprintf("0x%040d", i) // Simple mock address
Expand Down Expand Up @@ -199,16 +199,15 @@ func TestSender(t *testing.T) {

db.Client = client

// Set up mock Redis client
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
defer redisClient.Close()
// Set up in-memory Redis
mr, err := miniredis.Run()
assert.NoError(t, err)
defer mr.Close()

db.RedisClient = redisClient
db.RedisClient = redis.NewClient(&redis.Options{Addr: mr.Addr()})

// Setup test data
err := setup()
err = setup()
assert.NoError(t, err)

senderTokens, err := client.SenderOrderToken.Query().All(context.Background())
Expand All @@ -230,6 +229,32 @@ func TestSender(t *testing.T) {
var paymentOrderUUID uuid.UUID

t.Run("InitiatePaymentOrder", func(t *testing.T) {
// Activate httpmock to intercept all HTTP calls
httpmock.ActivateNonDefault(http.DefaultClient)
defer httpmock.Deactivate()

// Mock the engine service call for receive address creation
httpmock.RegisterResponder("POST", "https://engine.thirdweb.com/v1/accounts",
func(r *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
"result": map[string]interface{}{
"smartAccountAddress": "0x1234567890123456789012345678901234567890",
},
})
},
)

// Mock the engine service call for webhook creation
httpmock.RegisterResponder("POST", "https://1.insight.thirdweb.com/v1/webhooks",
func(r *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
"data": map[string]interface{}{
"id": "webhook_123456789",
"webhook_secret": "secret_123456789",
},
})
},
)

// Fetch network from db
network, err := db.Client.Network.
Expand Down Expand Up @@ -263,6 +288,8 @@ func TestSender(t *testing.T) {
if res.Code != http.StatusCreated {
t.Logf("Response Status: %d", res.Code)
t.Logf("Response Body: %s", res.Body.String())
t.Logf("Request payload: %+v", payload)
t.Logf("Request headers: %+v", headers)
}

// Assert the response body
Expand All @@ -282,7 +309,15 @@ func TestSender(t *testing.T) {
assert.NotEmpty(t, data["validUntil"])

// Parse the payment order ID string to uuid.UUID
paymentOrderUUID, err = uuid.Parse(data["id"].(string))
idValue, exists := data["id"]
if !exists || idValue == nil {
t.Fatalf("ID field is missing or nil in response data: %+v", data)
}
idString, ok := idValue.(string)
if !ok {
t.Fatalf("ID field is not a string, got %T: %+v", idValue, idValue)
}
paymentOrderUUID, err = uuid.Parse(idString)
assert.NoError(t, err)

// Query the database for the payment order
Expand All @@ -303,11 +338,14 @@ func TestSender(t *testing.T) {
assert.Equal(t, data["transactionFee"], network.Fee.String())

t.Run("Check Transaction Logs", func(t *testing.T) {
ts := time.Now().Unix()
sigPayload := map[string]interface{}{"timestamp": ts}
sig := token.GenerateHMACSignature(sigPayload, testCtx.apiKeySecret)
headers := map[string]string{
"API-Key": testCtx.apiKey.ID.String(),
"Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + sig,
}

res, err = test.PerformRequest(t, "GET", fmt.Sprintf("/sender/orders/%s?timestamp=%v", paymentOrderUUID.String(), payload["timestamp"]), nil, headers, router)
res, err = test.PerformRequest(t, "GET", fmt.Sprintf("/sender/orders/%s?timestamp=%v", paymentOrderUUID.String(), ts), nil, headers, router)
assert.NoError(t, err)

type Response struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Drop index "providerordertoken_provider_pr_6a0d0c64fd46fb967691502cb58fa192" from table: "provider_order_tokens"
DROP INDEX "providerordertoken_provider_pr_6a0d0c64fd46fb967691502cb58fa192";

-- Backfill NULL networks from tokens.network -> networks.identifier
UPDATE provider_order_tokens pot
SET network = n.identifier
FROM tokens t
JOIN networks n ON n.id = t.network_tokens
WHERE pot.network IS NULL
AND pot.token_provider_order_tokens = t.id;

-- Modify "provider_order_tokens" table
ALTER TABLE "provider_order_tokens" ALTER COLUMN "network" SET NOT NULL;
-- Create index "providerordertoken_network_pro_78d86f7d16ed79216b911727a796323a" to table: "provider_order_tokens"
CREATE UNIQUE INDEX "providerordertoken_network_pro_78d86f7d16ed79216b911727a796323a" ON "provider_order_tokens" ("network", "provider_profile_order_tokens", "token_provider_order_tokens", "fiat_currency_provider_order_tokens");
3 changes: 2 additions & 1 deletion ent/migrate/migrations/atlas.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
h1:soTiwdfz5QnsZUIX5WVoCoMGC7evo2Qe5GBI2mh705Q=
h1:ke5/IDBnxMBKN8DwH5d8zmkvZTBy7CMWoY/SyCp4FDg=
20240118234246_initial.sql h1:dYuYBqns33WT+3p8VQvbKUP62k3k6w6h8S+FqNqgSvU=
20240130122324_order_from_address.sql h1:mMVI2iBUd1roIYLUqu0d2jZ7+B6exppRN8qqn+aIHx4=
20240202010744_fees_on_order.sql h1:P7ngxZKqDKefBM5vk6M3kbWeMPVwbZ4MZVcLBjEfS34=
Expand Down Expand Up @@ -60,3 +60,4 @@ h1:soTiwdfz5QnsZUIX5WVoCoMGC7evo2Qe5GBI2mh705Q=
20250721051708_add_message_hash_and_protocol_fee.sql h1:uTNSjhrYVx+wmJRIqRDPUpwYxdJcIxuZNEi/8OQMSJc=
20250721080600_update_payment_order_triggers.sql h1:Ei+DoaBGSK/GDnQ0Iq1KNZEJc17fuJAn2nJfU1cbZVc=
20250801142901_add_provider_currencies.sql h1:Xmdc1PylAOMTkH3txmdh6g9vCAHT82pzy4yr45i+Le8=
20250904102613_add_network_unique_to_provider_order_token.sql h1:bjDRzBOYpgZQlwwXgI9kZMaMjB/VCSCrhs7ZDBL044c=
6 changes: 3 additions & 3 deletions ent/migrate/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 0 additions & 19 deletions ent/mutation.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 0 additions & 10 deletions ent/providerordertoken/where.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading