Go SDK
The official Go SDK for Unified Commerce Platform. Built with idiomatic Go patterns, featuring context-aware operations, structured error handling, and comprehensive test coverage.
Version: 1.5.2 | License: MIT | Go: 1.21+ | Module: github.com/unified-commerce/go-sdk
Installation
Install the SDK using Go modules:
# Using go get
go get github.com/unified-commerce/go-sdk
# Or add to go.mod
require github.com/unified-commerce/go-sdk v1.5.2
Requirements
- Go 1.21 or higher
- Go modules enabled
Optional Packages
# GraphQL support
go get github.com/unified-commerce/go-sdk/graphql
# Webhook utilities
go get github.com/unified-commerce/go-sdk/webhooks
# Testing utilities
go get github.com/unified-commerce/go-sdk/testing
Quick Start
Basic Setup
package main
import (
"context"
"fmt"
"log"
"os"
unifiedcommerce "github.com/unified-commerce/go-sdk"
)
func main() {
// Initialize the client
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
unifiedcommerce.WithEnvironment("production"),
)
// Create a context
ctx := context.Background()
// Query products
products, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Category: unifiedcommerce.String("electronics"),
Limit: unifiedcommerce.Int64(10),
})
if err != nil {
log.Fatalf("Error fetching products: %v", err)
}
for _, product := range products.Data {
fmt.Printf("%s: $%.2f\n", product.Name, product.Price)
}
}
Using Context
import (
"context"
"time"
)
func fetchProducts() error {
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// All SDK methods accept context
products, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Limit: unifiedcommerce.Int64(100),
})
if err != nil {
return fmt.Errorf("failed to fetch products: %w", err)
}
return nil
}
Configuration
Client Options
import (
unifiedcommerce "github.com/unified-commerce/go-sdk"
)
// Create client with custom configuration
client := unifiedcommerce.NewClient(
// Required: API key
unifiedcommerce.WithAPIKey("uc_live_xxxxxxxxxxxxx"),
// Environment: production or sandbox
unifiedcommerce.WithEnvironment("production"),
// Custom API URL
unifiedcommerce.WithAPIURL("https://api.custom-domain.com"),
// Request timeout
unifiedcommerce.WithTimeout(60 * time.Second),
// Maximum retry attempts
unifiedcommerce.WithMaxRetries(5),
// Custom HTTP client
unifiedcommerce.WithHTTPClient(&http.Client{
Timeout: 30 * time.Second,
}),
// Enable debug mode
unifiedcommerce.WithDebug(true),
// Custom headers
unifiedcommerce.WithHeaders(map[string]string{
"X-Custom-Header": "value",
}),
// Custom user agent
unifiedcommerce.WithUserAgent("MyApp/1.0"),
)
Configuration Struct
// Alternative: using configuration struct
config := &unifiedcommerce.Config{
APIKey: os.Getenv("UNIFIED_COMMERCE_API_KEY"),
Environment: "production",
Timeout: 60 * time.Second,
MaxRetries: 5,
Debug: true,
}
client := unifiedcommerce.NewClientWithConfig(config)
Environment Variables
// Load from environment
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
unifiedcommerce.WithEnvironment(os.Getenv("UNIFIED_COMMERCE_ENV")),
)
// Using .env file
import "github.com/joho/godotenv"
func init() {
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file")
}
}
Authentication
API Key Authentication
import (
"context"
unifiedcommerce "github.com/unified-commerce/go-sdk"
)
// Method 1: Via client initialization
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey("uc_live_xxxxxxxxxxxxx"),
)
// Method 2: From environment variable
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
)
// Method 3: Via context
ctx := unifiedcommerce.WithAPIKey(context.Background(), "different_api_key")
products, err := client.Products.List(ctx, params)
Request Signing
// Enable request signing for enhanced security
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
unifiedcommerce.WithRequestSigning(true),
unifiedcommerce.WithSigningSecret(os.Getenv("SIGNING_SECRET")),
)
Core Concepts
Products
import (
"context"
unifiedcommerce "github.com/unified-commerce/go-sdk"
)
func manageProducts(client *unifiedcommerce.Client) error {
ctx := context.Background()
// List products with filters
products, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Category: unifiedcommerce.String("electronics"),
MinPrice: unifiedcommerce.Float64(100),
MaxPrice: unifiedcommerce.Float64(1000),
InStock: unifiedcommerce.Bool(true),
Limit: unifiedcommerce.Int64(20),
Offset: unifiedcommerce.Int64(0),
SortBy: unifiedcommerce.String("price"),
SortOrder: unifiedcommerce.String("asc"),
})
if err != nil {
return fmt.Errorf("list products: %w", err)
}
// Get a single product
product, err := client.Products.Get(ctx, "product-123")
if err != nil {
return fmt.Errorf("get product: %w", err)
}
// Create a product
newProduct, err := client.Products.Create(ctx, &unifiedcommerce.ProductCreateParams{
Name: unifiedcommerce.String("Wireless Headphones"),
Description: unifiedcommerce.String("Premium noise-cancelling headphones"),
Price: unifiedcommerce.Float64(299.99),
Category: unifiedcommerce.String("electronics"),
SKU: unifiedcommerce.String("WH-1000XM5"),
Inventory: &unifiedcommerce.InventoryParams{
Quantity: unifiedcommerce.Int64(100),
TrackInventory: unifiedcommerce.Bool(true),
},
})
if err != nil {
return fmt.Errorf("create product: %w", err)
}
// Update a product
updated, err := client.Products.Update(ctx, "product-123", &unifiedcommerce.ProductUpdateParams{
Price: unifiedcommerce.Float64(279.99),
Inventory: &unifiedcommerce.InventoryParams{
Quantity: unifiedcommerce.Int64(150),
},
})
if err != nil {
return fmt.Errorf("update product: %w", err)
}
// Delete a product
err = client.Products.Delete(ctx, "product-123")
if err != nil {
return fmt.Errorf("delete product: %w", err)
}
return nil
}
Orders
func manageOrders(client *unifiedcommerce.Client) error {
ctx := context.Background()
// Create an order
order, err := client.Orders.Create(ctx, &unifiedcommerce.OrderCreateParams{
Items: []*unifiedcommerce.OrderItemParams{
{
ProductID: unifiedcommerce.String("product-123"),
Quantity: unifiedcommerce.Int64(2),
Price: unifiedcommerce.Float64(299.99),
},
{
ProductID: unifiedcommerce.String("product-456"),
Quantity: unifiedcommerce.Int64(1),
Price: unifiedcommerce.Float64(149.99),
},
},
Customer: &unifiedcommerce.CustomerParams{
ID: unifiedcommerce.String("customer-789"),
Email: unifiedcommerce.String("customer@example.com"),
Name: unifiedcommerce.String("John Doe"),
},
ShippingAddress: &unifiedcommerce.AddressParams{
Street: unifiedcommerce.String("123 Main St"),
City: unifiedcommerce.String("San Francisco"),
State: unifiedcommerce.String("CA"),
PostalCode: unifiedcommerce.String("94105"),
Country: unifiedcommerce.String("US"),
},
PaymentMethod: &unifiedcommerce.PaymentMethodParams{
Type: unifiedcommerce.String("card"),
Token: unifiedcommerce.String("pm_xxxxxxxxxxxxx"),
},
})
if err != nil {
return fmt.Errorf("create order: %w", err)
}
// List orders
orders, err := client.Orders.List(ctx, &unifiedcommerce.OrderListParams{
CustomerID: unifiedcommerce.String("customer-789"),
Status: unifiedcommerce.String("pending"),
Limit: unifiedcommerce.Int64(20),
})
if err != nil {
return fmt.Errorf("list orders: %w", err)
}
// Get order details
orderDetails, err := client.Orders.Get(ctx, "order-123")
if err != nil {
return fmt.Errorf("get order: %w", err)
}
// Update order status
err = client.Orders.Update(ctx, "order-123", &unifiedcommerce.OrderUpdateParams{
Status: unifiedcommerce.String("shipped"),
Tracking: &unifiedcommerce.TrackingParams{
Carrier: unifiedcommerce.String("UPS"),
TrackingNumber: unifiedcommerce.String("1Z999AA10123456784"),
},
})
if err != nil {
return fmt.Errorf("update order: %w", err)
}
// Cancel an order
err = client.Orders.Cancel(ctx, "order-123", &unifiedcommerce.OrderCancelParams{
Reason: unifiedcommerce.String("Customer requested cancellation"),
})
if err != nil {
return fmt.Errorf("cancel order: %w", err)
}
return nil
}
Customers
func manageCustomers(client *unifiedcommerce.Client) error {
ctx := context.Background()
// Create a customer
customer, err := client.Customers.Create(ctx, &unifiedcommerce.CustomerCreateParams{
Email: unifiedcommerce.String("customer@example.com"),
Name: unifiedcommerce.String("John Doe"),
Phone: unifiedcommerce.String("+1234567890"),
Addresses: []*unifiedcommerce.AddressParams{
{
Type: unifiedcommerce.String("shipping"),
Street: unifiedcommerce.String("123 Main St"),
City: unifiedcommerce.String("San Francisco"),
State: unifiedcommerce.String("CA"),
PostalCode: unifiedcommerce.String("94105"),
Country: unifiedcommerce.String("US"),
},
},
})
if err != nil {
return fmt.Errorf("create customer: %w", err)
}
// List customers
customers, err := client.Customers.List(ctx, &unifiedcommerce.CustomerListParams{
Search: unifiedcommerce.String("john"),
Limit: unifiedcommerce.Int64(20),
})
if err != nil {
return fmt.Errorf("list customers: %w", err)
}
// Update customer
err = client.Customers.Update(ctx, "customer-123", &unifiedcommerce.CustomerUpdateParams{
Phone: unifiedcommerce.String("+9876543210"),
})
if err != nil {
return fmt.Errorf("update customer: %w", err)
}
return nil
}
Inventory
func manageInventory(client *unifiedcommerce.Client) error {
ctx := context.Background()
// Get inventory for a product
inventory, err := client.Inventory.Get(ctx, "product-123")
if err != nil {
return fmt.Errorf("get inventory: %w", err)
}
// Update inventory
err = client.Inventory.Update(ctx, "product-123", &unifiedcommerce.InventoryUpdateParams{
Quantity: unifiedcommerce.Int64(100),
Location: unifiedcommerce.String("warehouse-1"),
ReservedQuantity: unifiedcommerce.Int64(5),
})
if err != nil {
return fmt.Errorf("update inventory: %w", err)
}
// Bulk update
err = client.Inventory.BulkUpdate(ctx, []*unifiedcommerce.InventoryBulkUpdateParams{
{ProductID: unifiedcommerce.String("product-123"), Quantity: unifiedcommerce.Int64(100)},
{ProductID: unifiedcommerce.String("product-456"), Quantity: unifiedcommerce.Int64(50)},
})
if err != nil {
return fmt.Errorf("bulk update inventory: %w", err)
}
// Track inventory movements
movements, err := client.Inventory.Movements(ctx, "product-123", &unifiedcommerce.InventoryMovementsParams{
StartDate: unifiedcommerce.String("2024-01-01"),
EndDate: unifiedcommerce.String("2024-01-31"),
})
if err != nil {
return fmt.Errorf("get movements: %w", err)
}
return nil
}
Error Handling
Error Types
import (
"errors"
unifiedcommerce "github.com/unified-commerce/go-sdk"
)
func handleErrors(client *unifiedcommerce.Client) error {
ctx := context.Background()
product, err := client.Products.Get(ctx, "invalid-id")
if err != nil {
// Type assertion for specific errors
var notFoundErr *unifiedcommerce.NotFoundError
var authErr *unifiedcommerce.AuthenticationError
var rateLimitErr *unifiedcommerce.RateLimitError
var validationErr *unifiedcommerce.ValidationError
switch {
case errors.As(err, ¬FoundErr):
log.Printf("Product not found: %s", notFoundErr.Message)
return err
case errors.As(err, &authErr):
log.Printf("Authentication failed: %s", authErr.Message)
return err
case errors.As(err, &rateLimitErr):
log.Printf("Rate limit exceeded. Retry after: %v", rateLimitErr.RetryAfter)
time.Sleep(rateLimitErr.RetryAfter)
// Retry the request
return nil
case errors.As(err, &validationErr):
log.Printf("Validation errors:")
for field, errs := range validationErr.Errors {
log.Printf(" %s: %v", field, errs)
}
return err
default:
log.Printf("Unexpected error: %v", err)
return err
}
}
return nil
}
Automatic Retries
// Enable automatic retries with exponential backoff
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
unifiedcommerce.WithMaxRetries(5),
unifiedcommerce.WithRetryBackoff(2*time.Second),
)
// The SDK automatically retries on:
// - Network errors
// - 5xx server errors
// - Rate limit errors (with retry-after header)
Custom Retry Logic
import "github.com/unified-commerce/go-sdk/retry"
// Create custom retry policy
retryPolicy := retry.NewExponentialBackoff(
retry.WithMaxRetries(5),
retry.WithInitialDelay(1*time.Second),
retry.WithMaxDelay(30*time.Second),
retry.WithMultiplier(2),
)
client := unifiedcommerce.NewClient(
unifiedcommerce.WithAPIKey(os.Getenv("UNIFIED_COMMERCE_API_KEY")),
unifiedcommerce.WithRetryPolicy(retryPolicy),
)
Concurrency
Goroutines
import (
"sync"
"context"
)
func fetchProductsConcurrently(client *unifiedcommerce.Client, categories []string) ([]*unifiedcommerce.Product, error) {
ctx := context.Background()
var (
mu sync.Mutex
wg sync.WaitGroup
products []*unifiedcommerce.Product
errs []error
)
for _, category := range categories {
wg.Add(1)
go func(cat string) {
defer wg.Done()
result, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Category: unifiedcommerce.String(cat),
Limit: unifiedcommerce.Int64(100),
})
mu.Lock()
defer mu.Unlock()
if err != nil {
errs = append(errs, err)
return
}
products = append(products, result.Data...)
}(category)
}
wg.Wait()
if len(errs) > 0 {
return nil, fmt.Errorf("errors occurred: %v", errs)
}
return products, nil
}
Worker Pool
func processProductsWithWorkerPool(client *unifiedcommerce.Client, productIDs []string) error {
ctx := context.Background()
numWorkers := 10
jobs := make(chan string, len(productIDs))
results := make(chan error, len(productIDs))
// Start workers
for w := 0; w < numWorkers; w++ {
go func() {
for productID := range jobs {
product, err := client.Products.Get(ctx, productID)
if err != nil {
results <- err
continue
}
// Process product
if err := processProduct(product); err != nil {
results <- err
continue
}
results <- nil
}
}()
}
// Send jobs
for _, id := range productIDs {
jobs <- id
}
close(jobs)
// Collect results
for range productIDs {
if err := <-results; err != nil {
log.Printf("Error processing product: %v", err)
}
}
return nil
}
Context Cancellation
func fetchWithCancellation(client *unifiedcommerce.Client) error {
// Create cancelable context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start fetching in goroutine
done := make(chan error)
go func() {
products, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Limit: unifiedcommerce.Int64(1000),
})
if err != nil {
done <- err
return
}
// Process products
for _, p := range products.Data {
select {
case <-ctx.Done():
done <- ctx.Err()
return
default:
processProduct(p)
}
}
done <- nil
}()
// Wait for completion or timeout
select {
case err := <-done:
return err
case <-time.After(30 * time.Second):
cancel() // Cancel the context
return errors.New("operation timed out")
}
}
Webhooks
Webhook Verification
import (
"net/http"
"io"
"github.com/unified-commerce/go-sdk/webhooks"
)
func handleWebhook(w http.ResponseWriter, r *http.Request) {
// Read request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusBadRequest)
return
}
// Get signature from header
signature := r.Header.Get("X-Unified-Commerce-Signature")
// Verify webhook
event, err := webhooks.Verify(body, signature, os.Getenv("WEBHOOK_SECRET"))
if err != nil {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Handle event based on type
switch event.Type {
case webhooks.OrderCreated:
order := event.Data.(*unifiedcommerce.Order)
handleOrderCreated(order)
case webhooks.OrderUpdated:
order := event.Data.(*unifiedcommerce.Order)
handleOrderUpdated(order)
case webhooks.InventoryLow:
inventory := event.Data.(*unifiedcommerce.Inventory)
handleLowInventory(inventory)
default:
log.Printf("Unhandled webhook type: %s", event.Type)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"received": true}`))
}
Webhook Handler
import "github.com/unified-commerce/go-sdk/webhooks"
func setupWebhookHandler() http.Handler {
handler := webhooks.NewHandler(os.Getenv("WEBHOOK_SECRET"))
// Register event handlers
handler.On(webhooks.OrderCreated, func(event *webhooks.Event) error {
order := event.Data.(*unifiedcommerce.Order)
log.Printf("Order created: %s", order.ID)
return sendConfirmationEmail(order)
})
handler.On(webhooks.PaymentFailed, func(event *webhooks.Event) error {
payment := event.Data.(*unifiedcommerce.Payment)
log.Printf("Payment failed: %s", payment.ID)
return notifyCustomer(payment)
})
handler.On(webhooks.InventoryLow, func(event *webhooks.Event) error {
inventory := event.Data.(*unifiedcommerce.Inventory)
log.Printf("Low inventory: %s", inventory.ProductID)
return reorderStock(inventory)
})
return handler
}
Advanced Features
GraphQL Queries
import "github.com/unified-commerce/go-sdk/graphql"
func customGraphQLQuery(client *unifiedcommerce.Client) error {
ctx := context.Background()
query := `
query GetProductsWithReviews($category: String!) {
products(category: $category) {
id
name
price
reviews {
id
rating
comment
author {
name
}
}
}
}
`
var result struct {
Products []struct {
ID string
Name string
Price float64
Reviews []struct {
ID string
Rating int
Comment string
Author struct {
Name string
}
}
}
}
err := client.GraphQL.Query(ctx, query, map[string]interface{}{
"category": "electronics",
}, &result)
if err != nil {
return fmt.Errorf("graphql query: %w", err)
}
return nil
}
Batch Operations
func batchOperations(client *unifiedcommerce.Client) error {
ctx := context.Background()
// Batch create products
products, err := client.Products.BatchCreate(ctx, []*unifiedcommerce.ProductCreateParams{
{Name: unifiedcommerce.String("Product 1"), Price: unifiedcommerce.Float64(99.99)},
{Name: unifiedcommerce.String("Product 2"), Price: unifiedcommerce.Float64(149.99)},
{Name: unifiedcommerce.String("Product 3"), Price: unifiedcommerce.Float64(199.99)},
})
if err != nil {
return fmt.Errorf("batch create: %w", err)
}
// Batch update
err = client.Products.BatchUpdate(ctx, []*unifiedcommerce.ProductBatchUpdateParams{
{ID: unifiedcommerce.String("product-1"), Price: unifiedcommerce.Float64(89.99)},
{ID: unifiedcommerce.String("product-2"), Price: unifiedcommerce.Float64(139.99)},
})
if err != nil {
return fmt.Errorf("batch update: %w", err)
}
return nil
}
Pagination
func paginateProducts(client *unifiedcommerce.Client) ([]*unifiedcommerce.Product, error) {
ctx := context.Background()
var allProducts []*unifiedcommerce.Product
// Method 1: Cursor-based pagination
cursor := ""
for {
result, err := client.Products.List(ctx, &unifiedcommerce.ProductListParams{
Limit: unifiedcommerce.Int64(100),
Cursor: unifiedcommerce.String(cursor),
})
if err != nil {
return nil, err
}
allProducts = append(allProducts, result.Data...)
if result.NextCursor == "" {
break
}
cursor = result.NextCursor
}
// Method 2: Using iterator
iter := client.Products.Iterate(ctx, &unifiedcommerce.ProductListParams{
Category: unifiedcommerce.String("electronics"),
})
for iter.Next() {
product := iter.Product()
allProducts = append(allProducts, product)
}
if err := iter.Err(); err != nil {
return nil, err
}
return allProducts, nil
}
File Uploads
import "os"
func uploadProductImage(client *unifiedcommerce.Client) error {
ctx := context.Background()
// Open file
file, err := os.Open("product-image.jpg")
if err != nil {
return err
}
defer file.Close()
// Upload image
image, err := client.Products.UploadImage(ctx, "product-123", &unifiedcommerce.ImageUploadParams{
File: file,
Alt: unifiedcommerce.String("Product image"),
Position: unifiedcommerce.Int64(0),
})
if err != nil {
return err
}
log.Printf("Image uploaded: %s", image.URL)
return nil
}
Testing
Mock Client
import (
"testing"
"github.com/unified-commerce/go-sdk/testing"
)
func TestFetchProducts(t *testing.T) {
// Create mock client
mockClient := testing.NewMockClient()
// Setup mock response
mockClient.Products.On("List", mock.Anything, mock.Anything).Return(
&unifiedcommerce.ProductList{
Data: []*unifiedcommerce.Product{
{ID: "1", Name: "Test Product", Price: 99.99},
},
Total: 1,
},
nil,
)
// Use in test
products, err := mockClient.Products.List(context.Background(), &unifiedcommerce.ProductListParams{
Limit: unifiedcommerce.Int64(10),
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(products.Data) != 1 {
t.Errorf("expected 1 product, got %d", len(products.Data))
}
mockClient.Products.AssertExpectations(t)
}
Test Utilities
import "github.com/unified-commerce/go-sdk/testing"
func TestOrderCreation(t *testing.T) {
// Create test fixtures
testProduct := testing.MockProduct(t, testing.ProductOptions{
Name: "Test Product",
Price: 99.99,
})
testOrder := testing.MockOrder(t, testing.OrderOptions{
Items: []*unifiedcommerce.OrderItem{
{Product: testProduct, Quantity: 2},
},
})
// Use in tests
if testOrder.Total != 199.98 {
t.Errorf("expected total 199.98, got %f", testOrder.Total)
}
}
Changelog
Version 1.5.2 (Latest)
- Added Go 1.22 support
- Improved context cancellation handling
- New webhook verification utilities
- Performance optimizations in HTTP client
- Fixed memory leak in long-running operations
Version 1.5.0
- Breaking: Minimum Go version is now 1.21
- Added generics support for type-safe operations
- New GraphQL client with query builder
- Improved error types with structured errors
- Added batch operations support
Version 1.4.0
- Added worker pool utilities
- New file upload support
- Improved pagination with iterators
- Added request signing
- Performance improvements
Resources
Support
Need help? We're here for you: