Go SDK

Complete Go examples for integrating with Lorn AI.


Requirements

  • Go 1.18+

Setup

// lorn/client.go
 
package lorn
 
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "time"
)
 
var (
    DefaultBaseURL = getEnvOrDefault("LORN_BASE_URL", "https://{{YOUR_STORE_URL}}")
    DefaultAPIKey  = getEnvOrDefault("LORN_API_KEY", "{{YOUR_API_KEY}}")
)
 
func getEnvOrDefault(key, defaultValue string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return defaultValue
}
 
// Client is the Lorn AI API client
type Client struct {
    BaseURL    string
    APIKey     string
    HTTPClient *http.Client
}
 
// NewClient creates a new Lorn client
func NewClient(baseURL, apiKey string) *Client {
    if baseURL == "" {
        baseURL = DefaultBaseURL
    }
    if apiKey == "" {
        apiKey = DefaultAPIKey
    }
    
    return &Client{
        BaseURL: baseURL,
        APIKey:  apiKey,
        HTTPClient: &http.Client{
            Timeout: 30 * time.Second,
        },
    }
}
 
// Request types
type SearchParams struct {
    Q        string   `json:"q,omitempty"`
    Category string   `json:"category,omitempty"`
    Tag      string   `json:"tag,omitempty"`
    MinPrice *float64 `json:"min_price,omitempty"`
    MaxPrice *float64 `json:"max_price,omitempty"`
    Page     int      `json:"page,omitempty"`
    PageSize int      `json:"page_size,omitempty"`
}
 
type LineItemInput struct {
    ProductID  string `json:"product_id"`
    VariantSKU string `json:"variant_sku,omitempty"`
    Quantity   int    `json:"quantity,omitempty"`
}
 
type Address struct {
    Name       string `json:"name,omitempty"`
    Line1      string `json:"line1,omitempty"`
    Line2      string `json:"line2,omitempty"`
    City       string `json:"city,omitempty"`
    State      string `json:"state,omitempty"`
    PostalCode string `json:"postal_code,omitempty"`
    Country    string `json:"country,omitempty"`
}
 
type Customer struct {
    Email string `json:"email,omitempty"`
    Phone string `json:"phone,omitempty"`
    Name  string `json:"name,omitempty"`
}
 
type CreateCheckoutInput struct {
    Items           []LineItemInput `json:"items"`
    ShippingAddress *Address        `json:"shipping_address,omitempty"`
    Customer        *Customer       `json:"customer,omitempty"`
}
 
type UpdateCheckoutInput struct {
    Items           []LineItemInput `json:"items,omitempty"`
    ShippingAddress *Address        `json:"shipping_address,omitempty"`
    Customer        *Customer       `json:"customer,omitempty"`
}
 
// Response types
type Product struct {
    ID              string            `json:"id"`
    Title           string            `json:"title"`
    Description     string            `json:"description"`
    Vendor          string            `json:"vendor,omitempty"`
    Category        string            `json:"category,omitempty"`
    Price           float64           `json:"price"`
    Currency        string            `json:"currency"`
    Availability    *Inventory        `json:"availability,omitempty"`
    Tags            []string          `json:"tags"`
    Images          []Image           `json:"images"`
    Variants        []Variant         `json:"variants"`
    SimilarityScore *float64          `json:"similarity_score,omitempty"`
}
 
type Variant struct {
    SKU        string            `json:"sku"`
    Title      string            `json:"title,omitempty"`
    Price      float64           `json:"price"`
    Currency   string            `json:"currency"`
    Attributes map[string]string `json:"attributes"`
    Inventory  *Inventory        `json:"inventory,omitempty"`
}
 
type Inventory struct {
    Status   string `json:"status,omitempty"`
    Quantity int    `json:"quantity,omitempty"`
}
 
type Image struct {
    URL     string `json:"url"`
    AltText string `json:"alt_text,omitempty"`
}
 
type SearchResponse struct {
    Items       []Product `json:"items"`
    Page        int       `json:"page"`
    PageSize    int       `json:"page_size"`
    Total       int       `json:"total"`
    Version     string    `json:"version,omitempty"`
    GeneratedAt string    `json:"generated_at,omitempty"`
}
 
type CheckoutSession struct {
    ID              string      `json:"id"`
    Status          string      `json:"status"`
    Currency        string      `json:"currency"`
    LineItems       []LineItem  `json:"line_items"`
    ShippingAddress *Address    `json:"shipping_address,omitempty"`
    Customer        *Customer   `json:"customer,omitempty"`
    Shipping        *Shipping   `json:"shipping,omitempty"`
    Tax             *Tax        `json:"tax,omitempty"`
    Amounts         *Amounts    `json:"amounts,omitempty"`
    ClientSecret    string      `json:"client_secret"`
    CreatedAt       string      `json:"created_at"`
}
 
type LineItem struct {
    ProductID  string  `json:"product_id"`
    VariantSKU string  `json:"variant_sku,omitempty"`
    Quantity   int     `json:"quantity"`
    Title      string  `json:"title,omitempty"`
    UnitPrice  float64 `json:"unit_price"`
    Currency   string  `json:"currency"`
    Subtotal   float64 `json:"subtotal"`
}
 
type Shipping struct {
    Method   string  `json:"method"`
    Amount   float64 `json:"amount"`
    Currency string  `json:"currency"`
}
 
type Tax struct {
    Rate     float64 `json:"rate"`
    Amount   float64 `json:"amount"`
    Currency string  `json:"currency"`
}
 
type Amounts struct {
    Subtotal float64 `json:"subtotal"`
    Tax      float64 `json:"tax"`
    Shipping float64 `json:"shipping"`
    Total    float64 `json:"total"`
    Currency string  `json:"currency"`
}
 
type CheckoutResponse struct {
    CheckoutSession CheckoutSession `json:"checkout_session"`
}
 
type HealthResponse struct {
    Status   string `json:"status"`
    Supabase string `json:"supabase"`
}
 
// API Methods
func (c *Client) Health() (*HealthResponse, error) {
    var result HealthResponse
    err := c.get("/health", nil, &result)
    return &result, err
}
 
func (c *Client) SearchProducts(params SearchParams) (*SearchResponse, error) {
    query := url.Values{}
    if params.Q != "" {
        query.Set("q", params.Q)
    }
    if params.Category != "" {
        query.Set("category", params.Category)
    }
    if params.Tag != "" {
        query.Set("tag", params.Tag)
    }
    if params.MinPrice != nil {
        query.Set("min_price", fmt.Sprintf("%.2f", *params.MinPrice))
    }
    if params.MaxPrice != nil {
        query.Set("max_price", fmt.Sprintf("%.2f", *params.MaxPrice))
    }
    if params.Page > 0 {
        query.Set("page", fmt.Sprintf("%d", params.Page))
    }
    if params.PageSize > 0 {
        query.Set("page_size", fmt.Sprintf("%d", params.PageSize))
    }
 
    var result SearchResponse
    err := c.get("/acp/products", query, &result)
    return &result, err
}
 
func (c *Client) GetProduct(productID string) (*Product, error) {
    var result Product
    err := c.get("/acp/products/"+productID, nil, &result)
    return &result, err
}
 
func (c *Client) CreateCheckout(input CreateCheckoutInput) (*CheckoutResponse, error) {
    var result CheckoutResponse
    err := c.post("/checkout_sessions", input, &result)
    return &result, err
}
 
func (c *Client) GetCheckout(sessionID string) (*CheckoutResponse, error) {
    var result CheckoutResponse
    err := c.get("/checkout_sessions/"+sessionID, nil, &result)
    return &result, err
}
 
func (c *Client) UpdateCheckout(sessionID string, input UpdateCheckoutInput) (*CheckoutResponse, error) {
    var result CheckoutResponse
    err := c.patch("/checkout_sessions/"+sessionID, input, &result)
    return &result, err
}
 
func (c *Client) CompleteCheckout(sessionID string) (*CheckoutResponse, error) {
    var result CheckoutResponse
    err := c.post("/checkout_sessions/"+sessionID+"/complete", nil, &result)
    return &result, err
}
 
func (c *Client) CancelCheckout(sessionID string) (*CheckoutResponse, error) {
    var result CheckoutResponse
    err := c.post("/checkout_sessions/"+sessionID+"/cancel", nil, &result)
    return &result, err
}
 
// HTTP helpers
func (c *Client) get(endpoint string, query url.Values, result interface{}) error {
    urlStr := c.BaseURL + endpoint
    if len(query) > 0 {
        urlStr += "?" + query.Encode()
    }
    
    req, err := http.NewRequest("GET", urlStr, nil)
    if err != nil {
        return err
    }
    
    return c.doRequest(req, result)
}
 
func (c *Client) post(endpoint string, body interface{}, result interface{}) error {
    return c.doWithBody("POST", endpoint, body, result)
}
 
func (c *Client) patch(endpoint string, body interface{}, result interface{}) error {
    return c.doWithBody("PATCH", endpoint, body, result)
}
 
func (c *Client) doWithBody(method, endpoint string, body interface{}, result interface{}) error {
    var bodyReader io.Reader
    if body != nil {
        jsonBody, err := json.Marshal(body)
        if err != nil {
            return err
        }
        bodyReader = bytes.NewBuffer(jsonBody)
    }
    
    req, err := http.NewRequest(method, c.BaseURL+endpoint, bodyReader)
    if err != nil {
        return err
    }
    
    if body != nil {
        req.Header.Set("Content-Type", "application/json")
    }
    
    return c.doRequest(req, result)
}
 
func (c *Client) doRequest(req *http.Request, result interface{}) error {
    req.Header.Set("Accept", "application/json")
    req.Header.Set("X-ACP-API-Key", c.APIKey)
    
    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return err
    }
    
    if resp.StatusCode >= 400 {
        return fmt.Errorf("API error: %d - %s", resp.StatusCode, string(body))
    }
    
    if result != nil {
        return json.Unmarshal(body, result)
    }
    
    return nil
}

Usage Examples

Search Products

package main
 
import (
    "fmt"
    "log"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    // Basic search
    results, err := client.SearchProducts(lorn.SearchParams{
        Q: "running shoes",
    })
    if err != nil {
        log.Fatal(err)
    }
    
    for _, product := range results.Items {
        fmt.Printf("%s - $%.2f\n", product.Title, product.Price)
        if product.SimilarityScore != nil {
            fmt.Printf("  Relevance: %.2f\n", *product.SimilarityScore)
        }
    }
    
    // With filters
    maxPrice := 200.0
    filtered, err := client.SearchProducts(lorn.SearchParams{
        Q:        "wireless headphones",
        MaxPrice: &maxPrice,
        PageSize: 5,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found %d products\n", filtered.Total)
}

Get Product Details

package main
 
import (
    "fmt"
    "log"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    product, err := client.GetProduct("prod_abc123")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Title: %s\n", product.Title)
    fmt.Printf("Price: $%.2f\n", product.Price)
    fmt.Printf("Description: %s\n", product.Description)
    
    // Show variants
    for _, variant := range product.Variants {
        attrs := ""
        for k, v := range variant.Attributes {
            attrs += fmt.Sprintf("%s: %s, ", k, v)
        }
        fmt.Printf("  - %s: %s - $%.2f\n", variant.SKU, attrs, variant.Price)
    }
}

Create Checkout

package main
 
import (
    "fmt"
    "log"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    // Simple checkout
    session, err := client.CreateCheckout(lorn.CreateCheckoutInput{
        Items: []lorn.LineItemInput{
            {ProductID: "prod_abc123", Quantity: 1},
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Session ID: %s\n", session.CheckoutSession.ID)
    fmt.Printf("Total: $%.2f\n", session.CheckoutSession.Amounts.Total)
    
    // Full checkout
    fullSession, err := client.CreateCheckout(lorn.CreateCheckoutInput{
        Items: []lorn.LineItemInput{
            {
                ProductID:  "prod_abc123",
                VariantSKU: "sku_black_m",
                Quantity:   2,
            },
        },
        ShippingAddress: &lorn.Address{
            Name:       "Jane Doe",
            Line1:      "123 Main Street",
            City:       "San Francisco",
            State:      "CA",
            PostalCode: "94102",
            Country:    "US",
        },
        Customer: &lorn.Customer{
            Email: "jane@example.com",
            Name:  "Jane Doe",
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Full checkout total: $%.2f\n", fullSession.CheckoutSession.Amounts.Total)
}

Complete Checkout

package main
 
import (
    "fmt"
    "log"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    order, err := client.CompleteCheckout("cs_demo_abc123")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Status: %s\n", order.CheckoutSession.Status)
    fmt.Printf("Total: $%.2f\n", order.CheckoutSession.Amounts.Total)
}

Complete Purchase Flow

package main
 
import (
    "fmt"
    "log"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    // 1. Search for products
    fmt.Println("Searching for running shoes...")
    maxPrice := 150.0
    results, err := client.SearchProducts(lorn.SearchParams{
        Q:        "running shoes",
        MaxPrice: &maxPrice,
    })
    if err != nil {
        log.Fatal(err)
    }
    
    if len(results.Items) == 0 {
        fmt.Println("No products found")
        return
    }
    
    product := results.Items[0]
    fmt.Printf("Found: %s - $%.2f\n", product.Title, product.Price)
    
    // 2. Get product details
    productDetails, err := client.GetProduct(product.ID)
    if err != nil {
        log.Fatal(err)
    }
    
    var variantSKU string
    if len(productDetails.Variants) > 0 {
        variantSKU = productDetails.Variants[0].SKU
    }
    
    // 3. Create checkout
    fmt.Println("\nCreating checkout...")
    session, err := client.CreateCheckout(lorn.CreateCheckoutInput{
        Items: []lorn.LineItemInput{
            {
                ProductID:  product.ID,
                VariantSKU: variantSKU,
                Quantity:   1,
            },
        },
        ShippingAddress: &lorn.Address{
            Name:       "Jane Doe",
            Line1:      "123 Main St",
            City:       "San Francisco",
            State:      "CA",
            PostalCode: "94102",
            Country:    "US",
        },
        Customer: &lorn.Customer{
            Email: "jane@example.com",
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    
    amounts := session.CheckoutSession.Amounts
    fmt.Printf("Session: %s\n", session.CheckoutSession.ID)
    fmt.Printf("Subtotal: $%.2f\n", amounts.Subtotal)
    fmt.Printf("Tax: $%.2f\n", amounts.Tax)
    fmt.Printf("Shipping: $%.2f\n", amounts.Shipping)
    fmt.Printf("Total: $%.2f\n", amounts.Total)
    
    // 4. Complete checkout
    fmt.Println("\nCompleting purchase...")
    order, err := client.CompleteCheckout(session.CheckoutSession.ID)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("Order completed!")
    fmt.Printf("Status: %s\n", order.CheckoutSession.Status)
}

Error Handling

package main
 
import (
    "fmt"
    "strings"
    
    "your-module/lorn"
)
 
func main() {
    client := lorn.NewClient("", "")
    
    _, err := client.GetProduct("invalid_id")
    if err != nil {
        errMsg := err.Error()
        
        switch {
        case strings.Contains(errMsg, "401"):
            fmt.Println("Invalid API key")
        case strings.Contains(errMsg, "404"):
            fmt.Println("Product not found")
        case strings.Contains(errMsg, "429"):
            fmt.Println("Rate limited - retry later")
        case strings.Contains(errMsg, "500"), strings.Contains(errMsg, "503"):
            fmt.Println("Server error - retry later")
        default:
            fmt.Printf("Error: %s\n", errMsg)
        }
    }
}

Webhook Signature Verification

package main
 
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "strconv"
    "strings"
    "time"
)
 
const toleranceSeconds = 300
 
func VerifyWebhookSignature(body []byte, signatureHeader, secret string) bool {
    // Parse signature header: "t=<timestamp>,v1=<signature>"
    parts := strings.Split(signatureHeader, ",")
    var timestamp int64
    var receivedSig string
    
    for _, part := range parts {
        kv := strings.SplitN(part, "=", 2)
        if len(kv) != 2 {
            continue
        }
        switch kv[0] {
        case "t":
            timestamp, _ = strconv.ParseInt(kv[1], 10, 64)
        case "v1":
            receivedSig = kv[1]
        }
    }
    
    // Check timestamp freshness
    now := time.Now().Unix()
    if abs(now-timestamp) > toleranceSeconds {
        return false
    }
    
    // Compute expected signature
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expectedSig := hex.EncodeToString(mac.Sum(nil))
    
    // Timing-safe comparison
    return hmac.Equal([]byte(expectedSig), []byte(receivedSig))
}
 
func abs(x int64) int64 {
    if x < 0 {
        return -x
    }
    return x
}
 
// HTTP handler example
/*
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Bad request", http.StatusBadRequest)
        return
    }
    
    signature := r.Header.Get("X-Webhook-Signature")
    secret := os.Getenv("LORN_WEBHOOK_SECRET")
    
    if !VerifyWebhookSignature(body, signature, secret) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    
    var event map[string]interface{}
    json.Unmarshal(body, &event)
    
    eventType := event["type"].(string)
    switch eventType {
    case "order.created":
        fmt.Printf("New order: %v\n", event["data"])
    case "order.fulfilled":
        fmt.Printf("Order fulfilled: %v\n", event["data"])
    }
    
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"received": true}`))
}
*/