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

# Go

> Usa el Go SDK de Dodo Payments para crear sesiones de checkout, clientes, suscripciones y eventos de uso, con compatibilidad con context, reintentos y middleware.

El Go SDK proporciona a las aplicaciones Go acceso tipado a la REST API de Dodo Payments. Cada método recibe un `context.Context`, los parámetros de solicitud usan un envoltorio `Field` que separa los valores cero de los campos omitidos, y puedes añadir middleware a cada solicitud.

## Instalación

Añade el módulo a tu proyecto:

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

Para fijar una versión específica:

```bash theme={null}
go get -u 'github.com/dodopayments/dodopayments-go@v1.118.0'
```

<Info>
  El SDK requiere Go 1.22 o una versión posterior.
</Info>

## Inicio Rápido

Crea un cliente y, a continuación, crea una sesión de checkout:

```go expandable theme={null}
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/dodopayments/dodopayments-go"
	"github.com/dodopayments/dodopayments-go/option"
)

func main() {
	client := dodopayments.NewClient(
		option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")), // This is the default and can be omitted
		option.WithEnvironmentTestMode(),                            // defaults to option.WithEnvironmentLiveMode()
	)

	checkoutSessionResponse, err := client.CheckoutSessions.New(context.TODO(), dodopayments.CheckoutSessionNewParams{
		CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
			ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{{
				ProductID: dodopayments.F("pdt_123"),
				Quantity:  dodopayments.F(int64(1)),
			}}),
		},
	})
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("Session ID: %s\n", checkoutSessionResponse.SessionID)
}
```

Si omites `option.WithBearerToken`, `NewClient` lee la variable de entorno `DODO_PAYMENTS_API_KEY`. Si omites `option.WithEnvironmentTestMode()`, el cliente se conecta al modo live. Una clave de API del modo test solo funciona en el modo test.

<Warning>
  Guarda las claves de API en variables de entorno o en un gestor de secretos. Nunca las incluyas directamente en el código fuente.
</Warning>

## Funciones principales

<CardGroup cols={2}>
  <Card title="Context Support" icon="clock">
    Cada método recibe un `context.Context` para cancelaciones y timeouts.
  </Card>

  <Card title="Strong Typing" icon="shield-check">
    Parámetros de solicitud y estructuras de respuesta tipados para realizar comprobaciones en tiempo de compilación.
  </Card>

  <Card title="Middleware" icon="layer-group">
    Añade middleware con `option.WithMiddleware` para el registro, las métricas y la lógica personalizada.
  </Card>

  <Card title="Goroutine Safe" icon="bolt">
    Comparte un cliente entre goroutines.
  </Card>
</CardGroup>

## Configuración

`NewClient` lee `DODO_PAYMENTS_API_KEY`, `DODO_PAYMENTS_WEBHOOK_KEY` (tu secreto de firma de webhook) y `DODO_PAYMENTS_BASE_URL` desde el entorno. Las opciones que pasas, como `option.WithBearerToken`, `option.WithWebhookKey` e `option.WithBaseURL`, las sustituyen.

Para verificar un webhook, pasa el cuerpo sin procesar de la solicitud y los encabezados a `client.Webhooks.Unwrap(rawBody, r.Header)`. Comprueba la firma con tu clave de webhook y devuelve el evento analizado. `client.Webhooks.UnsafeUnwrap(rawBody)` analiza el cuerpo sin verificarlo, así que úsalo únicamente para pruebas. Consulta [Webhooks](/developer-resources/webhooks).

Los ejemplos de esta página usan el `client` de [Inicio rápido](#quick-start).

### Context y timeouts

Las solicitudes no tienen timeout de forma predeterminada. Un plazo límite de context limita la llamada completa, incluidos los reintentos. Para limitar cada intento, añade `option.WithRequestTimeout()`:

```go expandable theme={null}
import (
	"context"
	"log"
	"time"
	
	"github.com/dodopayments/dodopayments-go"
	"github.com/dodopayments/dodopayments-go/option"
)

// Create a context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

// Note: POST /payments is deprecated — prefer checkout sessions for new
// integrations. Shown here only to illustrate context handling.
_, err := client.Payments.New(ctx, dodopayments.PaymentNewParams{
	Billing: dodopayments.F(dodopayments.BillingAddressParam{
		Country: dodopayments.F(dodopayments.CountryCodeUs),
		City:    dodopayments.F("San Francisco"),
		State:   dodopayments.F("CA"),
		Street:  dodopayments.F("1 Market St"),
		Zipcode: dodopayments.F("94105"),
	}),
	Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
		dodopayments.AttachExistingCustomerParam{
			CustomerID: dodopayments.F("cus_123"),
		},
	),
	ProductCart: dodopayments.F([]dodopayments.OneTimeProductCartItemParam{{
		ProductID: dodopayments.F("pdt_456"),
		Quantity:  dodopayments.F(int64(1)),
	}}),
}, option.WithRequestTimeout(5*time.Second)) // Per-attempt timeout
if err != nil {
	if ctx.Err() == context.DeadlineExceeded {
		log.Println("Request timed out")
	} else {
		log.Fatal(err)
	}
}
```

### Configuración de reintentos

El SDK reintenta los errores de conexión y las respuestas con status 408, 409, 429 o 500 y superiores. De forma predeterminada, realiza dos reintentos con backoff exponencial. Establece `option.WithMaxRetries` en el cliente o en una sola solicitud:

```go theme={null}
// Configure default for all requests (default is 2)
client := dodopayments.NewClient(
	option.WithMaxRetries(0), // disable retries
)

// Override per-request
client.CheckoutSessions.New(
	context.TODO(),
	dodopayments.CheckoutSessionNewParams{
		CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
			ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{{
				ProductID: dodopayments.F("pdt_123"),
				Quantity:  dodopayments.F(int64(1)),
			}}),
		},
	},
	option.WithMaxRetries(5),
)
```

## Operaciones comunes

Los ejemplos de esta sección también usan un context, por ejemplo `ctx := context.Background()`.

### Crear una sesión de checkout

Crea una sesión de checkout y, a continuación, redirige al cliente a la `CheckoutURL` devuelta:

```go theme={null}
session, err := client.CheckoutSessions.New(ctx, dodopayments.CheckoutSessionNewParams{
	CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
		ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{{
			ProductID: dodopayments.F("pdt_123"),
			Quantity:  dodopayments.F(int64(1)),
		}}),
		ReturnURL: dodopayments.F("https://yourdomain.com/return"),
	},
})
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Checkout URL: %s\n", session.CheckoutURL)
```

Cada URL de checkout funciona una sola vez y caduca después de 24 horas. Consulta [Sesiones de checkout](/developer-resources/checkout-session) para ver todas las opciones de sesión.

### Gestionar clientes

Crea un cliente con una dirección de email y un nombre y, a continuación, recupéralo por ID. Los valores de metadata usan los tipos unión del paquete `shared`:

```go theme={null}
import "github.com/dodopayments/dodopayments-go/shared"

// Create a customer
customer, err := client.Customers.New(ctx, dodopayments.CustomerNewParams{
	Email: dodopayments.F("customer@example.com"),
	Name:  dodopayments.F("John Doe"),
	Metadata: dodopayments.F(dodopayments.MetadataParam{
		"user_id": shared.UnionString("12345"),
	}),
})
if err != nil {
	log.Fatal(err)
}

// Retrieve customer
customer, err = client.Customers.Get(ctx, "cus_123")
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Customer: %s (%s)\n", customer.Name, customer.Email)
```

### Gestionar suscripciones

Crea una suscripción, realiza un cargo en una suscripción on-demand y lee el historial de uso de una suscripción.

<Warning>
  `POST /subscriptions` (el método `Subscriptions.New` del SDK) está **obsoleto**. Sigue funcionando para integraciones existentes, pero las integraciones nuevas deben crear suscripciones mediante una [sesión de checkout](/developer-resources/checkout-session).
</Warning>

```go expandable theme={null}
import "time"

// Create a subscription
subscription, err := client.Subscriptions.New(ctx, dodopayments.SubscriptionNewParams{
	Billing: dodopayments.F(dodopayments.BillingAddressParam{
		Country: dodopayments.F(dodopayments.CountryCodeUs),
		City:    dodopayments.F("San Francisco"),
		State:   dodopayments.F("CA"),
		Street:  dodopayments.F("1 Market St"),
		Zipcode: dodopayments.F("94105"),
	}),
	Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
		dodopayments.AttachExistingCustomerParam{
			CustomerID: dodopayments.F("cus_123"),
		},
	),
	ProductID: dodopayments.F("pdt_456"),
	Quantity:  dodopayments.F(int64(1)),
})
if err != nil {
	log.Fatal(err)
}

// Charge an on-demand subscription
// ProductPrice is in the lowest currency denomination (e.g., 2500 = $25.00 USD)
chargeResponse, err := client.Subscriptions.Charge(ctx, subscription.SubscriptionID,
	dodopayments.SubscriptionChargeParams{
		ProductPrice: dodopayments.F(int64(2500)),
	},
)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Payment ID: %s\n", chargeResponse.PaymentID)

// Get usage history (for metered subscriptions)
usageHistory, err := client.Subscriptions.GetUsageHistory(
	ctx,
	subscription.SubscriptionID,
	dodopayments.SubscriptionGetUsageHistoryParams{
		StartDate: dodopayments.F(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)),
		EndDate:   dodopayments.F(time.Date(2024, 3, 31, 23, 59, 59, 0, time.UTC)),
	},
)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Usage periods on this page: %d\n", len(usageHistory.Items))
```

<Info>
  `Billing` solo requiere `Country`, un código de país ISO de dos letras. `Customer` es un `CustomerRequestUnionParam`: pasa `AttachExistingCustomerParam{CustomerID: ...}` para un cliente existente o `NewCustomerParam{Email: ..., Name: ...}` para crear uno. `Charge` se utiliza para [suscripciones on-demand](/developer-resources/ondemand-subscriptions), e `ProductPrice` está expresado en la unidad monetaria más pequeña. `GetUsageHistory` devuelve una página de resultados; `GetUsageHistoryAutoPaging` itera por todas las páginas.
</Info>

## Facturación basada en el uso

### Ingerir eventos de uso

Envía eventos de uso para un cliente:

```go theme={null}
import (
	"time"

	"github.com/dodopayments/dodopayments-go"
)

response, err := client.UsageEvents.Ingest(ctx, dodopayments.UsageEventIngestParams{
	Events: dodopayments.F([]dodopayments.EventInputParam{{
		EventID:    dodopayments.F("api_call_12345"),
		CustomerID: dodopayments.F("cus_abc123"),
		EventName:  dodopayments.F("api_request"),
		Timestamp:  dodopayments.F(time.Now()),
	}}),
})
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Ingested %d events\n", response.IngestedCount)
```

<Info>
  El `EventID` es la clave de idempotencia, así que asigna a cada evento un valor único. Si el mismo `EventID` aparece dos veces en una solicitud, se rechaza la solicitud completa. Si ya se había ingerido un `EventID`, se ignora el evento nuevo. Una solicitud acepta hasta 1.000 eventos. `Timestamp` toma de forma predeterminada la hora actual y se rechaza si es de hace más de 1 hora o si está más de 5 minutos en el futuro.
</Info>

### Enumerar eventos de uso

Enumera los eventos filtrados por cliente y nombre del evento:

```go theme={null}
// List events with filters
params := dodopayments.UsageEventListParams{
	CustomerID: dodopayments.F("cus_abc123"),
	EventName:  dodopayments.F("api_request"),
}

events, err := client.UsageEvents.List(ctx, params)
if err != nil {
	log.Fatal(err)
}

for _, event := range events.Items {
	fmt.Printf("Event %s: %s at %s\n", event.EventID, event.EventName, event.Timestamp)
}
```

`List` devuelve una página. Para iterar por todas las páginas, llama a `client.UsageEvents.ListAutoPaging(ctx, params)` y usa un bucle con `iter.Next()`, `iter.Current()` e `iter.Err()`. Otros métodos de enumeración tienen la misma variante `AutoPaging`, y cada página tiene un método `GetNextPage()`.

## Gestión de errores

Cuando la API devuelve un status code que no indica éxito, el SDK devuelve un error de tipo `*dodopayments.Error`. Contiene `StatusCode`, `*http.Request` e `*http.Response`, además del JSON del cuerpo del error. Usa `errors.As` para inspeccionarlo y usa `StatusCode` para gestionar casos específicos:

```go expandable theme={null}
// Note: POST /payments is deprecated — prefer checkout sessions for new
// integrations. Shown here only to illustrate error handling.
_, err := client.Payments.New(ctx, dodopayments.PaymentNewParams{
	Billing: dodopayments.F(dodopayments.BillingAddressParam{
		Country: dodopayments.F(dodopayments.CountryCodeUs),
		City:    dodopayments.F("San Francisco"),
		State:   dodopayments.F("CA"),
		Street:  dodopayments.F("1 Market St"),
		Zipcode: dodopayments.F("94105"),
	}),
	Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
		dodopayments.AttachExistingCustomerParam{CustomerID: dodopayments.F("cus_123")},
	),
	ProductCart: dodopayments.F([]dodopayments.OneTimeProductCartItemParam{{
		ProductID: dodopayments.F("pdt_456"),
		Quantity:  dodopayments.F(int64(1)),
	}}),
})
if err != nil {
	var apiErr *dodopayments.Error
	if errors.As(err, &apiErr) {
		fmt.Printf("Status Code: %d\n", apiErr.StatusCode)
		fmt.Println(string(apiErr.DumpResponse(true))) // Serialized HTTP response

		// Handle specific status codes
		switch apiErr.StatusCode {
		case 401:
			log.Println("Authentication failed")
		case 422:
			log.Println("Invalid request parameters")
		case 429:
			log.Println("Rate limit exceeded")
		default:
			log.Printf("API error: %s", apiErr.Error())
		}
	} else {
		log.Fatal(err)
	}
}
```

Los demás errores se devuelven sin envoltorio. Por ejemplo, si falla el transporte HTTP, podrías recibir un `*url.Error` que envuelve un `*net.OpError`. `apiErr.DumpRequest(true)` devuelve la solicitud serializada.

## Middleware

Añade middleware con `option.WithMiddleware`. Un middleware recibe cada solicitud y una función `next` que la envía:

```go theme={null}
func Logger(req *http.Request, next option.MiddlewareNext) (res *http.Response, err error) {
	// Before the request
	start := time.Now()
	log.Printf("Request: %s %s\n", req.Method, req.URL)

	// Forward the request to the next handler
	res, err = next(req)

	// After the request (res is nil when err is not nil)
	if err != nil {
		log.Printf("Request failed after %v: %v\n", time.Since(start), err)
		return res, err
	}
	log.Printf("Response: %d in %v\n", res.StatusCode, time.Since(start))

	return res, err
}

client := dodopayments.NewClient(
	option.WithMiddleware(Logger),
)
```

Varios middleware en una llamada a `option.WithMiddleware` se ejecutan de izquierda a derecha. El middleware que se pasa a `NewClient` se ejecuta antes que el middleware que se pasa a una sola solicitud.

## Concurrencia

El cliente es seguro para su uso concurrente, por lo que puedes compartir un cliente entre goroutines:

```go theme={null}
package main

import (
	"context"
	"sync"
	"log"

	"github.com/dodopayments/dodopayments-go"
)

func main() {
	client := dodopayments.NewClient()
	
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(idx int) {
			defer wg.Done()
			
			// Note: POST /payments is deprecated — prefer checkout sessions
			// for new integrations. Shown here only to illustrate concurrency.
			payment, err := client.Payments.New(context.Background(), dodopayments.PaymentNewParams{
				Billing: dodopayments.F(dodopayments.BillingAddressParam{
					Country: dodopayments.F(dodopayments.CountryCodeUs),
					City:    dodopayments.F("San Francisco"),
					State:   dodopayments.F("CA"),
					Street:  dodopayments.F("1 Market St"),
					Zipcode: dodopayments.F("94105"),
				}),
				Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
					dodopayments.AttachExistingCustomerParam{CustomerID: dodopayments.F("cus_123")},
				),
				ProductCart: dodopayments.F([]dodopayments.OneTimeProductCartItemParam{{
					ProductID: dodopayments.F("pdt_456"),
					Quantity:  dodopayments.F(int64(1)),
				}}),
			})
			if err != nil {
				log.Printf("Failed to create payment %d: %v", idx, err)
				return
			}
			
			log.Printf("Created payment %d: %s", idx, payment.PaymentID)
		}(i)
	}
	
	wg.Wait()
}
```

## Recursos

<CardGroup cols={2}>
  <Card title="GitHub Repository" icon="github" href="https://github.com/dodopayments/dodopayments-go">
    Código fuente, versiones y lista completa de métodos.
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Todos los endpoints, parámetros y respuestas.
  </Card>

  <Card title="Discord Community" icon="discord" href="https://discord.gg/bYqAp4ayYh">
    Haz preguntas y habla con otros desarrolladores.
  </Card>

  <Card title="Report Issues" icon="bug" href="https://github.com/dodopayments/dodopayments-go/issues">
    Informa de errores o solicita funciones.
  </Card>
</CardGroup>

## Soporte

Para obtener ayuda con el Go SDK:

* **Discord**: Únete al [servidor de la comunidad](https://discord.gg/bYqAp4ayYh) para recibir ayuda en tiempo real.
* **Email**: Contacta con [support@dodopayments.com](mailto:support@dodopayments.com).
* **GitHub**: Abre un issue en el [repositorio](https://github.com/dodopayments/dodopayments-go).

## Contribuciones

Para contribuir, lee las [directrices de contribución](https://github.com/dodopayments/dodopayments-go/blob/main/CONTRIBUTING.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.