Skip to content

Commit 902f655

Browse files
committed
feat: Add checkout session ID support to orders and enhance order retrieval logic
1 parent 945ee2b commit 902f655

11 files changed

Lines changed: 349 additions & 38 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ docker-push: ## Push Docker image to registry (use REGISTRY and TAG)
7575

7676
docker-build-push: docker-build-tag docker-push ## Build and push Docker image (use REGISTRY and TAG)
7777

78+
docker-dev-build: ## Build Docker image for development
79+
docker build -t ghcr.io/zenfulcode/commercifygo:dev .
80+
7881
# Development commands
7982
test: ## Run tests
8083
go test ./...

internal/domain/repository/order_repository.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "github.com/zenfulcode/commercify/internal/domain/entity"
66
type OrderRepository interface {
77
Create(order *entity.Order) error
88
GetByID(orderID uint) (*entity.Order, error)
9+
GetByCheckoutSessionID(checkoutSessionID string) (*entity.Order, error)
910
Update(order *entity.Order) error
1011
GetByUser(userID uint, offset, limit int) ([]*entity.Order, error)
1112
ListByStatus(status entity.OrderStatus, offset, limit int) ([]*entity.Order, error)

internal/infrastructure/repository/postgres/order_repository.go

Lines changed: 207 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ func (r *OrderRepository) Create(order *entity.Order) error {
5858
user_id, total_amount, status, payment_status, shipping_address, billing_address,
5959
payment_id, payment_provider, tracking_code, created_at, updated_at, completed_at, final_amount,
6060
customer_email, customer_phone, customer_full_name, is_guest_order, shipping_method_id, shipping_cost,
61-
total_weight, currency
61+
total_weight, currency, checkout_session_id
6262
)
63-
VALUES (NULL, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
63+
VALUES (NULL, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
6464
RETURNING id
6565
`
6666

@@ -86,6 +86,7 @@ func (r *OrderRepository) Create(order *entity.Order) error {
8686
order.ShippingCost,
8787
order.TotalWeight,
8888
order.Currency,
89+
order.CheckoutSessionID,
8990
).Scan(&order.ID)
9091
} else {
9192
// Regular user order
@@ -94,9 +95,9 @@ func (r *OrderRepository) Create(order *entity.Order) error {
9495
user_id, total_amount, status, payment_status, shipping_address, billing_address,
9596
payment_id, payment_provider, tracking_code, created_at, updated_at, completed_at, final_amount,
9697
customer_email, customer_phone, customer_full_name, shipping_method_id, shipping_cost, total_weight,
97-
currency
98+
currency, checkout_session_id
9899
)
99-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
100+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
100101
RETURNING id
101102
`
102103

@@ -122,6 +123,7 @@ func (r *OrderRepository) Create(order *entity.Order) error {
122123
order.ShippingCost,
123124
order.TotalWeight,
124125
order.Currency,
126+
order.CheckoutSessionID,
125127
).Scan(&order.ID)
126128
}
127129

@@ -179,7 +181,7 @@ func (r *OrderRepository) GetByID(orderID uint) (*entity.Order, error) {
179181
payment_id, payment_provider, tracking_code, created_at, updated_at, completed_at,
180182
discount_amount, discount_id, discount_code, final_amount, action_url,
181183
customer_email, customer_phone, customer_full_name, is_guest_order, shipping_method_id, shipping_cost,
182-
total_weight, currency
184+
total_weight, currency, checkout_session_id
183185
FROM orders
184186
WHERE id = $1
185187
`
@@ -199,6 +201,7 @@ func (r *OrderRepository) GetByID(orderID uint) (*entity.Order, error) {
199201

200202
var discountID sql.NullInt64
201203
var discountCode sql.NullString
204+
var checkoutSessionID sql.NullString
202205

203206
err := r.db.QueryRow(query, orderID).Scan(
204207
&order.ID,
@@ -228,6 +231,7 @@ func (r *OrderRepository) GetByID(orderID uint) (*entity.Order, error) {
228231
&shippingCost,
229232
&totalWeight,
230233
&order.Currency,
234+
&checkoutSessionID,
231235
)
232236

233237
if err == sql.ErrNoRows {
@@ -309,6 +313,11 @@ func (r *OrderRepository) GetByID(orderID uint) (*entity.Order, error) {
309313
order.TotalWeight = totalWeight.Float64
310314
}
311315

316+
// Set checkout session ID if valid
317+
if checkoutSessionID.Valid {
318+
order.CheckoutSessionID = checkoutSessionID.String
319+
}
320+
312321
// Get order items
313322
query = `
314323
SELECT oi.id, oi.order_id, oi.product_id, oi.product_variant_id, oi.quantity, oi.price, oi.subtotal, oi.weight,
@@ -359,6 +368,199 @@ func (r *OrderRepository) GetByID(orderID uint) (*entity.Order, error) {
359368
return order, nil
360369
}
361370

371+
// GetByCheckoutSessionID retrieves an order by checkout session ID
372+
func (r *OrderRepository) GetByCheckoutSessionID(checkoutSessionID string) (*entity.Order, error) {
373+
// Get order
374+
query := `
375+
SELECT id, order_number, user_id, total_amount, status, payment_status, shipping_address, billing_address,
376+
payment_id, payment_provider, tracking_code, created_at, updated_at, completed_at,
377+
discount_amount, discount_id, discount_code, final_amount, action_url,
378+
customer_email, customer_phone, customer_full_name, is_guest_order, shipping_method_id, shipping_cost,
379+
total_weight, currency, checkout_session_id
380+
FROM orders
381+
WHERE checkout_session_id = $1
382+
`
383+
384+
order := &entity.Order{}
385+
var shippingAddrJSON, billingAddrJSON []byte
386+
var completedAt sql.NullTime
387+
var paymentProvider sql.NullString
388+
var orderNumber sql.NullString
389+
var actionURL sql.NullString
390+
var userID sql.NullInt64 // Use NullInt64 to handle NULL user_id
391+
var customerEmail, customerPhone, customerFullName sql.NullString
392+
var isGuestOrder sql.NullBool
393+
var shippingMethodID sql.NullInt64
394+
var shippingCost sql.NullInt64
395+
var totalWeight sql.NullFloat64
396+
var discountID sql.NullInt64
397+
var discountCode sql.NullString
398+
var checkoutSessionIDResult sql.NullString
399+
400+
err := r.db.QueryRow(query, checkoutSessionID).Scan(
401+
&order.ID,
402+
&orderNumber,
403+
&userID,
404+
&order.TotalAmount,
405+
&order.Status,
406+
&order.PaymentStatus,
407+
&shippingAddrJSON,
408+
&billingAddrJSON,
409+
&order.PaymentID,
410+
&paymentProvider,
411+
&order.TrackingCode,
412+
&order.CreatedAt,
413+
&order.UpdatedAt,
414+
&completedAt,
415+
&order.DiscountAmount,
416+
&discountID,
417+
&discountCode,
418+
&order.FinalAmount,
419+
&actionURL,
420+
&customerEmail,
421+
&customerPhone,
422+
&customerFullName,
423+
&isGuestOrder,
424+
&shippingMethodID,
425+
&shippingCost,
426+
&totalWeight,
427+
&order.Currency,
428+
&checkoutSessionIDResult,
429+
)
430+
431+
if err == sql.ErrNoRows {
432+
return nil, errors.New("order not found")
433+
}
434+
435+
if err != nil {
436+
return nil, err
437+
}
438+
439+
// Handle user_id properly
440+
if userID.Valid {
441+
order.UserID = uint(userID.Int64)
442+
} else {
443+
order.UserID = 0 // Use 0 to represent NULL in our application
444+
}
445+
446+
// Handle guest order fields
447+
if isGuestOrder.Valid && isGuestOrder.Bool {
448+
order.IsGuestOrder = true
449+
order.CustomerDetails = &entity.CustomerDetails{}
450+
if customerEmail.Valid {
451+
order.CustomerDetails.Email = customerEmail.String
452+
}
453+
if customerPhone.Valid {
454+
order.CustomerDetails.Phone = customerPhone.String
455+
}
456+
if customerFullName.Valid {
457+
order.CustomerDetails.FullName = customerFullName.String
458+
}
459+
}
460+
461+
// Set order number if valid
462+
if orderNumber.Valid {
463+
order.OrderNumber = orderNumber.String
464+
}
465+
466+
// Set payment provider if valid
467+
if paymentProvider.Valid {
468+
order.PaymentProvider = paymentProvider.String
469+
}
470+
471+
// Set action URL if valid
472+
if actionURL.Valid {
473+
order.ActionURL = actionURL.String
474+
}
475+
476+
// Set checkout session ID if valid
477+
if checkoutSessionIDResult.Valid {
478+
order.CheckoutSessionID = checkoutSessionIDResult.String
479+
}
480+
481+
// Unmarshal addresses
482+
if err := json.Unmarshal(shippingAddrJSON, &order.ShippingAddr); err != nil {
483+
return nil, err
484+
}
485+
486+
if err := json.Unmarshal(billingAddrJSON, &order.BillingAddr); err != nil {
487+
return nil, err
488+
}
489+
490+
// Set completed at if valid
491+
if completedAt.Valid {
492+
order.CompletedAt = &completedAt.Time
493+
}
494+
495+
// Set shipping method ID if valid
496+
if shippingMethodID.Valid {
497+
order.ShippingMethodID = uint(shippingMethodID.Int64)
498+
}
499+
500+
// Set shipping cost if valid
501+
if shippingCost.Valid {
502+
order.ShippingCost = shippingCost.Int64
503+
}
504+
505+
// Set total weight if valid
506+
if totalWeight.Valid {
507+
order.TotalWeight = totalWeight.Float64
508+
}
509+
510+
// Get order items
511+
query = `
512+
SELECT oi.id, oi.order_id, oi.product_id, oi.product_variant_id, oi.quantity, oi.price, oi.subtotal, oi.weight,
513+
p.name as product_name, p.product_number as sku
514+
FROM order_items oi
515+
LEFT JOIN products p ON p.id = oi.product_id
516+
WHERE oi.order_id = $1
517+
`
518+
519+
rows, err := r.db.Query(query, order.ID)
520+
if err != nil {
521+
return nil, err
522+
}
523+
defer rows.Close()
524+
525+
order.Items = []entity.OrderItem{}
526+
for rows.Next() {
527+
item := entity.OrderItem{}
528+
var productName, sku sql.NullString
529+
var productVariantID sql.NullInt64
530+
err := rows.Scan(
531+
&item.ID,
532+
&item.OrderID,
533+
&item.ProductID,
534+
&productVariantID,
535+
&item.Quantity,
536+
&item.Price,
537+
&item.Subtotal,
538+
&item.Weight,
539+
&productName,
540+
&sku,
541+
)
542+
if err != nil {
543+
return nil, err
544+
}
545+
546+
if productVariantID.Valid {
547+
item.ProductVariantID = uint(productVariantID.Int64)
548+
}
549+
550+
if productName.Valid {
551+
item.ProductName = productName.String
552+
}
553+
554+
if sku.Valid {
555+
item.SKU = sku.String
556+
}
557+
558+
order.Items = append(order.Items, item)
559+
}
560+
561+
return order, nil
562+
}
563+
362564
// Update updates an order
363565
func (r *OrderRepository) Update(order *entity.Order) error {
364566
// Marshal addresses to JSON

internal/interfaces/api/handler/email_test_handler.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ func (h *EmailTestHandler) TestEmail(w http.ResponseWriter, r *http.Request) {
4343

4444
// Create a mock order
4545
mockOrder := &entity.Order{
46-
ID: 12345,
47-
OrderNumber: "ORD-12345",
48-
UserID: mockUser.ID,
49-
Status: entity.OrderStatusCompleted,
50-
PaymentStatus: entity.PaymentStatusCaptured,
51-
TotalAmount: 9950, // $99.50 in cents (subtotal before shipping/discounts)
52-
ShippingCost: 850, // $8.50 shipping cost
53-
DiscountAmount: 1500, // $15.00 discount
54-
FinalAmount: 8300, // $83.00 final amount (99.50 + 8.50 - 15.00)
55-
Currency: "USD",
46+
ID: 12345,
47+
OrderNumber: "ORD-12345",
48+
UserID: mockUser.ID,
49+
Status: entity.OrderStatusCompleted,
50+
PaymentStatus: entity.PaymentStatusCaptured,
51+
TotalAmount: 9950, // $99.50 in cents (subtotal before shipping/discounts)
52+
ShippingCost: 850, // $8.50 shipping cost
53+
DiscountAmount: 1500, // $15.00 discount
54+
FinalAmount: 8300, // $83.00 final amount (99.50 + 8.50 - 15.00)
55+
Currency: "USD",
56+
CheckoutSessionID: "test-checkout-session-12345", // Add checkout session ID for testing
5657
ShippingAddr: entity.Address{
5758
Street: "123 Test Street",
5859
City: "Test City",

internal/interfaces/api/handler/health_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (h *HealthHandler) Health(w http.ResponseWriter, r *http.Request) {
6060
healthStatus := HealthStatus{
6161
Status: status,
6262
Timestamp: time.Now(),
63-
Version: "1.0.4", // TODO: Make this configurable
63+
Version: "1.0.6", // TODO: Make this configurable
6464
Services: services,
6565
Uptime: uptime,
6666
}

0 commit comments

Comments
 (0)