update orderflow
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OrderClient is a minimal HTTP client for the order service. It is the
|
||||
// checkout service's counterpart to the CartClient — the same pattern, but
|
||||
// calling into order-service endpoints instead of cart-service ones.
|
||||
//
|
||||
// Currently only CreateOrder is implemented (the from-checkout handoff).
|
||||
// Add GetOrder/ListOrders methods as the backoffice order UI evolves.
|
||||
type OrderClient struct {
|
||||
httpClient *http.Client
|
||||
baseURL string // e.g. "http://order-service:8092"
|
||||
}
|
||||
|
||||
// NewOrderClient returns a client that talks to the order service at baseURL.
|
||||
func NewOrderClient(baseURL string) *OrderClient {
|
||||
return &OrderClient{
|
||||
httpClient: &http.Client{Timeout: 30 * time.Second},
|
||||
baseURL: baseURL,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOrderReq is the JSON payload POSTed to the order service's
|
||||
// /api/orders/from-checkout endpoint. Every field is required except where
|
||||
// noted. See cmd/order/handlers_checkout.go for the full definition.
|
||||
type CreateOrderReq struct {
|
||||
CheckoutId string `json:"checkoutId"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
|
||||
CartId string `json:"cartId"`
|
||||
Currency string `json:"currency"`
|
||||
Locale string `json:"locale,omitempty"`
|
||||
Country string `json:"country"`
|
||||
CustomerEmail string `json:"customerEmail,omitempty"`
|
||||
CustomerName string `json:"customerName,omitempty"`
|
||||
Lines []OrderLine `json:"lines"`
|
||||
|
||||
Payment struct {
|
||||
Provider string `json:"provider"`
|
||||
Reference string `json:"reference"`
|
||||
Amount int64 `json:"amount"`
|
||||
} `json:"payment"`
|
||||
}
|
||||
|
||||
// OrderLine is one orderable item in the CreateOrderReq, matching the
|
||||
// lineReq type in the order service.
|
||||
type OrderLine struct {
|
||||
Reference string `json:"reference"`
|
||||
Sku string `json:"sku"`
|
||||
Name string `json:"name"`
|
||||
Quantity int32 `json:"quantity"`
|
||||
UnitPrice int64 `json:"unitPrice"`
|
||||
TaxRate int32 `json:"taxRate"`
|
||||
}
|
||||
|
||||
// CreateOrderResult is the success response from POST /api/orders/from-checkout.
|
||||
type CreateOrderResult struct {
|
||||
OrderId string `json:"orderId"`
|
||||
Flow json.RawMessage `json:"flow,omitempty"`
|
||||
Order json.RawMessage `json:"order,omitempty"`
|
||||
// Existing is true when the response is a 409 Conflict — the idempotency
|
||||
// key was already used and this is the previously-created order.
|
||||
Existing bool `json:"existing,omitempty"`
|
||||
}
|
||||
|
||||
// CreateOrder sends a settled checkout's data to the order service and returns
|
||||
// the created (or previously-created, via idempotency) order. An idempotencyKey
|
||||
// should be generated per checkout (e.g. "checkout-{checkoutId}") to guard
|
||||
// against retries. flowName overrides the default flow; use "" for the default.
|
||||
func (c *OrderClient) CreateOrder(ctx context.Context, req CreateOrderReq, flowName string) (*CreateOrderResult, error) {
|
||||
url := c.baseURL + "/api/orders/from-checkout"
|
||||
if flowName != "" {
|
||||
url += "?flow=" + flowName
|
||||
}
|
||||
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("order client: marshal: %w", err)
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("order client: new request: %w", err)
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("order client: do: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Both 201 (created) and 409 (idempotent hit) are valid responses.
|
||||
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusConflict {
|
||||
return nil, fmt.Errorf("order client: %s", resp.Status)
|
||||
}
|
||||
|
||||
var result CreateOrderResult
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return nil, fmt.Errorf("order client: decode: %w", err)
|
||||
}
|
||||
result.Existing = resp.StatusCode == http.StatusConflict
|
||||
return &result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user