Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions cmd/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ func initializeRoutes(engine *gin.Engine) {

// Orders
engine.GET("/v2/orders", middleware.RequireAuth, handlers.CreateHandler(handlers.GetUserOrders))
engine.GET("/v2/orders/:order_id/:transaction_id", middleware.RequireAuth, handlers.CreateHandler(handlers.GetUserOrder))
engine.GET("/v2/orders/donations/trial", middleware.RequireAuth, handlers.CreateHandler(handlers.GetUserFreeTrialDonatorOrder))
engine.GET("/v2/orders/donations/prices", handlers.CreateHandler(handlers.GetDonatorPrices))
engine.POST("/v2/orders/checkout", middleware.RequireAuth, handlers.CreateHandler(handlers.CreateOrderCheckoutSession))
Expand Down
19 changes: 19 additions & 0 deletions db/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,25 @@ func GetUserOrders(userId int) ([]*Order, error) {
return orders, nil
}

// GetUserOrdersByIds retrieves a user's orders by their order and transaction ids.
// Multiple order rows can share these ids when a transaction contains multiple items.
func GetUserOrdersByIds(userId int, orderId int, transactionId string) ([]*Order, error) {
var orders = make([]*Order, 0)

result := SQL.
Preload("Receiver").
Preload("Item").
Preload("Subscription").
Where("orders.user_id = ? AND orders.order_id = ? AND orders.transaction_id = ?", userId, orderId, transactionId).
Find(&orders)

if result.Error != nil {
return nil, result.Error
}

return orders, nil
}

// GetSteamOrdersByIds Retrieves orders by their steam order id & transaction id.
// Multiple orders in the database can have them if a user has multiple items in their cart.
func GetSteamOrdersByIds(steamOrderId string, transactionId string) ([]*Order, error) {
Expand Down
53 changes: 53 additions & 0 deletions handlers/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net"
"net/http"
"slices"
"strconv"
)

type DonatorPricing struct {
Expand All @@ -26,6 +27,12 @@ type DonatorPaymentMethod struct {
Months12 float32 `json:"months_12"`
}

type userOrderResponse struct {
*db.Order
OrderId int `json:"order_id"`
TransactionId string `json:"transaction_id"`
}

type checkoutPaymentMethod int

const (
Expand Down Expand Up @@ -85,6 +92,52 @@ func GetUserOrders(c *gin.Context) *APIError {
return nil
}

// GetUserOrder retrieves the authenticated user's orders by order and transaction ids.
// Endpoint: GET /v2/orders/:order_id/:transaction_id
func GetUserOrder(c *gin.Context) *APIError {
user := getAuthedUser(c)

if user == nil {
return nil
}

orderId, err := strconv.Atoi(c.Param("order_id"))

if err != nil {
return APIErrorBadRequest("You must provide a valid order_id.")
}

transactionId := c.Param("transaction_id")

if transactionId == "" {
return APIErrorBadRequest("You must provide a valid transaction_id.")
}

orders, err := db.GetUserOrdersByIds(user.Id, orderId, transactionId)

if err != nil {
return APIErrorServerError("Error retrieving order from db", err)
}

if len(orders) == 0 {
return APIErrorNotFound("Order")
}

orderResponses := make([]*userOrderResponse, 0, len(orders))
for _, order := range orders {
orderResponses = append(orderResponses, &userOrderResponse{
Order: order,
OrderId: order.OrderId,
TransactionId: order.TransactionId,
})
}

c.JSON(http.StatusOK, gin.H{
"orders": orderResponses,
})
return nil
}

// GetUserFreeTrialDonatorOrder Retrieves a user's free trial donator order if they've had one before
func GetUserFreeTrialDonatorOrder(c *gin.Context) *APIError {
user := getAuthedUser(c)
Expand Down
40 changes: 39 additions & 1 deletion handlers/orders_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
package handlers

import "testing"
import (
"encoding/json"
"github.com/Quaver/api2/db"
"testing"
)

func TestUserOrderResponseIncludesPaymentIdentifiers(t *testing.T) {
order := &db.Order{
OrderId: 123,
TransactionId: "transaction-456",
IPAddress: "192.168.1.1",
}

data, err := json.Marshal(userOrderResponse{
Order: order,
OrderId: order.OrderId,
TransactionId: order.TransactionId,
})
if err != nil {
t.Fatal(err)
}

var response map[string]any
if err := json.Unmarshal(data, &response); err != nil {
t.Fatal(err)
}

if response["order_id"] != float64(123) {
t.Fatalf("expected order_id to be included, got %#v", response["order_id"])
}

if response["transaction_id"] != "transaction-456" {
t.Fatalf("expected transaction_id to be included, got %#v", response["transaction_id"])
}

if _, exists := response["ip_address"]; exists {
t.Fatal("expected ip_address to remain private")
}
}

func TestGetOrderIpIpv4(t *testing.T) {
ip := getOrderIp("192.168.1.1")
Expand Down
Loading