> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snappay.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Complete guide for integrating SnapPay with Go applications using idiomatic Go patterns and concurrency

## Overview

The SnapPay Go SDK provides a comprehensive, idiomatic interface for integrating payment processing, subscription management, and real-time event streaming into your Go applications. Built with modern Go patterns including context support, channel-based concurrency, and explicit error handling, it offers seamless integration with SnapPay's platform.

**Requirements:** Go 1.21+

### Key Features

* **Idiomatic Go design** with context.Context support for cancellation and timeouts
* **Channel-based concurrency** for optimal performance and real-time event handling
* **Explicit error handling** following Go conventions
* **Full type safety** with struct definitions and interface contracts
* **Goroutine-safe client** for concurrent usage across your application
* **Real-time event streaming** via Server-Sent Events (SSE) using channels
* **Context-aware operations** with deadline propagation and cancellation
* **Environment-based configuration** with sensible defaults

## Installation

```bash theme={null}
go get github.com/snappay/snappay-go
```

Add to your `go.mod`:

```go theme={null}
module your-app

go 1.21

require (
    github.com/snappay/snappay-go v0.0.1-beta
)
```

## Configuration & Initialization

The SDK client is configured via a constructor function that accepts an optional configuration struct. All operations support context.Context for proper timeout and cancellation handling.

### API Key Authentication

Authentication is handled via an API key that must start with `pk_test_` (for testing) or `pk_live_` (for production). Configure your API key in one of these ways:

**Environment Variable (Recommended):**

```bash theme={null}
export SNAPPAY_API_KEY="pk_test_xxxxxxxxxx"
```

**Direct Configuration:**

```go theme={null}
package main

import (
    "context"
    "log"
    "github.com/snappay/snappay-go"
    "github.com/snappay/snappay-go/services"
)

func main() {
    // Using environment variable (recommended)
    client, err := snappay.NewClient(nil)
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }

    // Using direct API key
    client, err = snappay.NewClient(&services.Config{
        APIKey: "pk_test_xxxxxxxxxx",
    })
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
}
```

## Core Methods

All methods accept a `context.Context` as the first parameter and return a result struct pointer and an error, following standard Go patterns. The client is safe for concurrent use across multiple goroutines.

### Customer Management

#### GetCustomer

Retrieves or creates a customer record (upsert logic).

```go theme={null}
import "github.com/snappay/snappay-go/services/customer"

ctx := context.Background()
customer, err := customer.Get(client, ctx, customer.GetCustomerArgs{
    CustomerId: "cus_123",
    Email: "user@example.com", // Optional
    Name:  stringPtr("John Doe"), // Optional
})
if err != nil {
    log.Fatalf("Failed to get customer: %v", err)
}
fmt.Printf("Customer ID: %s\n", customer.CustomerID)
```

**Parameters:**

* `CustomerId` (string): Customer identifier
* `Email` (\*string): Customer email address (optional)
* `Name` (\*string): Customer full name (optional)

**Returns:** `(*Customer, error)`

```go theme={null}
type Customer struct {
    CustomerID string  `json:"cus_id"`      // SnapPay customer ID
    Email      string  `json:"email"`       // Customer email
    Name       *string `json:"name"`        // Customer name (optional)
}
```

### Checkout Sessions

#### CreateCheckoutSession

Creates a payment checkout session URL for customer purchases.

```go theme={null}
import "github.com/snappay/snappay-go/services/checkout"

ctx := context.Background()
session, err := checkout.CreateSession(client, ctx, checkout.CreateCheckoutSessionArgs{
    CustomerID: "cus_123",
    ProductID:  "premium-plan",
    SuccessURL: "https://yourapp.com/success",
    CancelURL:  stringPtr("https://yourapp.com/cancel"), // Optional
})
if err != nil {
    log.Fatalf("Failed to create checkout session: %v", err)
}
fmt.Printf("Checkout URL: %s\n", session.URL)
```

**Parameters:**

* `CustomerID` (string): SnapPay customer ID
* `ProductID` (string): Product ID from your snappay dashboard
* `PriceID` (\*string): Price ID from your snappay dashboard (optional)
* `SuccessURL` (string): URL to redirect after successful payment
* `CancelURL` (\*string): URL to redirect on cancellation (optional)

**Returns:** `(*CheckoutSession, error)`

```go theme={null}
type CheckoutSession struct {
    SessionID string  `json:"session_id"` // Unique session identifier
    URL       string  `json:"url"`        // Checkout URL for customer
    ExpiresAt string  `json:"expires_at"` // Session expiration timestamp
}
```

### Access Control

#### CheckAccess

Checks if a customer has access to a specific feature based on their subscription and usage limits.

```go theme={null}
import "github.com/snappay/snappay-go/services/access"

ctx := context.Background()
access, err := access.Check(client, ctx, access.CheckAccessArgs{
    CustomerID: "cus_123",
    FeatureID:  "premium-features",
})
if err != nil {
    log.Fatalf("Failed to check access: %v", err)
}

if access.HasAccess {
    if access.Usage != nil && access.Allowance != nil {
        remaining := *access.Allowance - *access.Usage
        fmt.Printf("Access granted. %d/%d used\n", *access.Usage, *access.Allowance)
        fmt.Printf("Remaining: %d\n", remaining)
    } else {
        fmt.Println("Access granted (unlimited feature)")
    }
} else {
    fmt.Println("Access denied. Upgrade required.")
}
```

**Parameters:**

* `CustomerID` (string): SnapPay customer ID
* `FeatureID` (string): Feature identifier

**Returns:** `(*AccessCheck, error)`

```go theme={null}
type AccessCheck struct {
    HasAccess   bool   `json:"has_access"`
    FeatureID   string `json:"feature_id"`
    Usage       *int64 `json:"usage"`
    Allowance   *int64 `json:"allowance"`
    NextResetAt *int64 `json:"next_reset_at"`
}
```

### Usage Tracking

#### TrackUsage

Reports usage for a metered feature. This action is idempotent to prevent duplicate tracking.

```go theme={null}
import "github.com/snappay/snappay-go/services/usage"

ctx := context.Background()
result, err := usage.Track(client, ctx, usage.TrackUsageArgs{
    CustomerID:     "cus_123",
    FeatureID:      "ai-messages",
    Usage:          1,   // integer usage units
    IdempotencyKey: stringPtr("unique-operation-123"), // Optional
})
if err != nil {
    log.Fatalf("Failed to track usage: %v", err)
}
fmt.Printf("Tracked %d units. Total: %d (idempotency: %v)\n", result.Added, result.Used, result.IdempotencyKey)
```

**Parameters:**

* `CustomerID` (string): SnapPay customer ID
* `FeatureID` (string): Feature identifier for usage tracking
* `Usage` (int64): Usage amount to track
* `IdempotencyKey` (\*string): Prevents duplicate tracking (optional)

**Returns:** `(*TrackUsageResponse, error)`

```go theme={null}
type TrackUsageResponse struct {
	CustomerID     string  `json:"customer_id"`     // Customer ID
	FeatureID      string  `json:"feature_id"`      // Feature ID
	UsageRecorded  int64   `json:"usage_recorded"`  // Amount of usage recorded
	UsageTotal     int64   `json:"usage_total"`     // Total usage after this operation
	Timestamp      string  `json:"timestamp"`       // Timestamp of the operation
	IdempotencyKey *string `json:"idempotency_key"` // Idempotency key used (if any)
}
```

#### GetUsage

Retrieves current usage details for a customer's feature.

```go theme={null}
ctx := context.Background()
items, err := usage.Get(client, ctx, usage.GetUsageArgs{
    CustomerID: "cus_123",
    FeatureID:  "ai-messages",
})
if err != nil {
    log.Fatalf("Failed to get usage: %v", err)
}
for i, u := range *items {
    fmt.Printf("Record %d:\n", i+1)
    fmt.Printf("  Product ID: %s\n", u.ProductID)
    fmt.Printf("  Feature ID: %s\n", u.FeatureID)
    fmt.Printf("  Total usage: %d\n", u.TotalUsage)
    fmt.Printf("  Remaining: %s\n", formatUsage(u.Remaining))
    fmt.Printf("  Limit: %s\n", formatUsage(u.Limit))
    if u.NextResetAt != nil {
        fmt.Printf("  Next reset: %d\n", *u.NextResetAt)
    }
    fmt.Println()
}
```

**Parameters:**

* `CustomerID` (string): SnapPay customer ID
* `FeatureID` (string): Feature identifier

**Returns:** `(*[]GetUsageResponse, error)`

```go theme={null}
type GetUsageResponse struct {
	TotalUsage  int64  `json:"total_usage"`
	ProductID   string `json:"product_id"`
	FeatureID   string `json:"feature_id"`
	Remaining   *int64 `json:"remaining,omitempty"`
	Limit       *int64 `json:"limit,omitempty"`
	NextResetAt *int64 `json:"next_reset_at,omitempty"`
}
```

## Real-time Event Handling

**Say goodbye to webhook hell!** snappay eliminates the complexity of managing payment webhooks by processing all provider webhooks (Stripe, PayPal, etc.) internally and delivering clean, structured events directly to your application via Server-Sent Events (SSE) using Go channels.

### 🚫 What You DON'T Need:

* **No webhook endpoints** to create and maintain
* **No webhook signature verification**
* **No webhook retry logic** or failure handling
* **No webhook security concerns**
* **No debugging webhook delivery issues**

### ✅ What You GET:

* **Real-time events** delivered instantly via Go channels
* **Guaranteed delivery** with automatic reconnection
* **Clean, structured data** - no raw webhook payloads
* **Channel-based concurrency** perfect for Go's goroutine model
* **Context-aware** with proper cancellation support

### Supported Events

See the list of supported events: [Server-sent Event Types](/events/index)

### Stream Events (Async Generator)

Consume the SSE stream via channels for direct, context-aware processing:

```go theme={null}
package main

import (
    "context"
    "log"
    "os/signal"
    "syscall"
    "github.com/snappay/snappay-sdk-go"
    "github.com/snappay/snappay-sdk-go/services/sse"
)

func main() {
    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    client, err := snappay.NewClient(nil)
    if err != nil {
        log.Fatalf("create client: %v", err)
    }

    if err := client.StartEvents(ctx); err != nil {
        log.Fatalf("start events: %v", err)
    }

    evCh, errCh := client.StreamEvents(ctx)

    for {
        select {
        case <-ctx.Done():
            return
        case err := <-errCh:
            if err != nil { log.Printf("sse error: %v", err) }
        case ev := <-evCh:
            if ev == nil { continue }
            if ev.Type == sse.EventTypeSubscriptionUpdated {
                if d, err := ev.GetSubscriptionData(); err == nil {
                    log.Printf("subscription: customer=%s status=%s", d.CustomerID, d.PaymentStatus)
                }
            }
        }
    }
}
```

### Event Handlers

Register handlers as separate functions and wire them up succinctly:

```go theme={null}
package main

import (
    "context"
    "log"
    "fmt"
    "os"
	"os/signal"
    "syscall"
    "github.com/snappay/snappay-sdk-go"
    "github.com/snappay/snappay-sdk-go/services/sse"
)

func handleSubscriptionUpdated(e *sse.SSEEvent) error {
    if d, err := e.GetSubscriptionData(); err == nil {
        log.Printf("subscription: customer=%s status=%s", d.CustomerID, d.PaymentStatus)
    }
    return nil
}

func handleAnyEvent(e *sse.SSEEvent) error {
    if e.IsSystemEvent() { return nil }
    log.Printf("event: %s", e.Type)
    return nil
}

func setupEventHandlers(client *snappay.Client) {
    client.OnEvent(sse.EventTypeSubscriptionUpdated, handleSubscriptionUpdated)
    client.OnAnyEvent(handleAnyEvent)
}

func main() {
    client, err := snappay.NewClient(nil)
    if err != nil { log.Fatalf("create client: %v", err) }

    setupEventHandlers(client)

    ctx := context.Background()
    if err := client.StartEvents(ctx); err != nil { log.Fatalf("start events: %v", err) }

    // Set up signal handling for graceful shutdown
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Wait for interrupt signal
	<-sigChan
	fmt.Println("\n\n🛑 Stopping SSE connection...")

	if err := client.StopEvents(); err != nil {
		log.Printf("Error stopping events: %v", err)
	}
}
```

## Error Handling

### Error Types

```go theme={null}
// Error hierarchy
type SnapPayError interface {
    error
    StatusCode() int
    RequestID() string
}

type AuthenticationError struct {
    Message   string
    Code      int
    ReqID     string
}

type ValidationError struct {
    Message   string
    Code      int
    ReqID     string
    Fields    map[string]string // Field-specific validation errors
}

type RateLimitError struct {
    Message     string
    Code        int
    ReqID       string
    RetryAfter  int // Seconds to wait before retrying
}

type NotFoundError struct {
    Message   string
    Code      int
    ReqID     string
}

type ServerError struct {
    Message   string
    Code      int
    ReqID     string
}
```
