> ## 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.

# Kotlin

> Usa el SDK de Kotlin de Dodo Payments para crear sesiones de checkout, productos, suscripciones y eventos de uso desde Kotlin, con tipos anulables y coroutines.

El SDK de Kotlin proporciona a las aplicaciones Kotlin acceso tipado a la API REST de Dodo Payments. Usa tipos de Kotlin en todo momento: valores anulables para campos que pueden faltar, `Sequence` para iterar resultados y funciones `suspend` para llamadas asíncronas.

## Instalación

### Gradle (Kotlin DSL)

Agrega la dependencia a tu `build.gradle.kts`:

```kotlin build.gradle.kts theme={null}
implementation("com.dodopayments.api:dodo-payments-kotlin:1.118.0")
```

### Maven

Agrega la dependencia a tu `pom.xml`:

```xml pom.xml theme={null}
<dependency>
  <groupId>com.dodopayments.api</groupId>
  <artifactId>dodo-payments-kotlin</artifactId>
  <version>1.118.0</version>
</dependency>
```

<Tip>
  Las versiones del SDK incorporan compatibilidad con cambios en la API. Para encontrar la versión más reciente, consulta [Maven Central](https://central.sonatype.com/artifact/com.dodopayments.api/dodo-payments-kotlin).
</Tip>

<Info>
  El SDK requiere Java 8 o una versión posterior. Se ejecuta en la JVM y en Android, e incluye reglas de conservación para ProGuard y R8.
</Info>

## Inicio Rápido

Crea un cliente y, después, una sesión de checkout:

```kotlin theme={null}
import com.dodopayments.api.client.DodoPaymentsClient
import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClient
import com.dodopayments.api.models.checkoutsessions.CheckoutSessionRequest
import com.dodopayments.api.models.checkoutsessions.CheckoutSessionResponse
import com.dodopayments.api.models.checkoutsessions.ProductItemReq

// Configure using environment variables (DODO_PAYMENTS_API_KEY, DODO_PAYMENTS_WEBHOOK_KEY, DODO_PAYMENTS_BASE_URL)
// Or system properties (dodopayments.apiKey, dodopayments.webhookKey, dodopayments.baseUrl)
val client: DodoPaymentsClient = DodoPaymentsOkHttpClient.fromEnv()

val params: CheckoutSessionRequest = CheckoutSessionRequest.builder()
    .addProductCart(ProductItemReq.builder()
        .productId("pdt_123")
        .quantity(1)
        .build())
    .build()
    
val checkoutSessionResponse: CheckoutSessionResponse = client.checkoutSessions().create(params)
println(checkoutSessionResponse.sessionId())
```

`fromEnv()` se conecta al modo activo, a menos que `DODO_PAYMENTS_BASE_URL` o `dodopayments.baseUrl` indiquen lo contrario. Para usar el modo de prueba, consulta [Modo de prueba](#test-mode). Una clave de API del modo de prueba solo funciona en el modo de prueba.

<Warning>
  Guarda las claves de API en variables de entorno o en un gestor de secretos. Nunca las incluyas en el control de versiones.
</Warning>

## Funciones principales

<CardGroup cols={2}>
  <Card title="Coroutines" icon="bolt">
    Los métodos del cliente asíncrono son funciones `suspend` que llamas desde una coroutine.
  </Card>

  <Card title="Null Safety" icon="shield-check">
    Los campos que pueden faltar son tipos anulables, no `Optional`.
  </Card>

  <Card title="Sequences" icon="code">
    En el cliente síncrono, `autoPager()` devuelve un `Sequence` que obtiene más páginas a medida que iteras. En el cliente asíncrono, devuelve un `Flow`.
  </Card>

  <Card title="Immutable Models" icon="layer-group">
    Las clases de modelo son inmutables, e `toBuilder()` devuelve un builder para una copia modificada.
  </Card>
</CardGroup>

## Configuración

### Desde variables de entorno

`fromEnv()` lee tu configuración de las variables de entorno o de las propiedades del sistema. Las propiedades del sistema tienen prioridad:

```kotlin theme={null}
val client: DodoPaymentsClient = DodoPaymentsOkHttpClient.fromEnv()
```

La clave de API proviene de `DODO_PAYMENTS_API_KEY` o `dodopayments.apiKey`. El secreto de firma del webhook proviene de `DODO_PAYMENTS_WEBHOOK_KEY` o `dodopayments.webhookKey`, y la URL base de `DODO_PAYMENTS_BASE_URL` o `dodopayments.baseUrl`. Crea un cliente y reutilízalo, porque cada cliente tiene su propio grupo de conexiones y sus propios grupos de hilos.

Para verificar un webhook, pasa el cuerpo sin procesar de la solicitud y los encabezados a `client.webhooks().unwrap(UnwrapWebhookParams.builder().body(rawBody).headers(headers).build())`, donde `headers` es un `com.dodopayments.api.core.http.Headers`. Comprueba la firma con tu clave de webhook y devuelve el evento analizado, o lanza `DodoPaymentsWebhookException`. Sin encabezados, `unwrap` no verifica la firma. `client.webhooks().unsafeUnwrap(rawBody)` analiza el cuerpo sin verificarlo, así que úsalo únicamente para pruebas. Consulta [Webhooks](/developer-resources/webhooks).

### Configuración manual

Establece cada opción en el builder:

```kotlin theme={null}
import java.time.Duration

val client = DodoPaymentsOkHttpClient.builder()
    .bearerToken("your_api_key_here")
    .baseUrl("https://live.dodopayments.com")
    .maxRetries(3)
    .timeout(Duration.ofSeconds(30))
    .build()
```

### Modo de prueba

Para usar el modo de prueba (`https://test.dodopayments.com`), llama a `testMode()` en el builder:

```kotlin theme={null}
val testClient = DodoPaymentsOkHttpClient.builder()
    .fromEnv()
    .testMode()
    .build()
```

### Tiempos de espera y reintentos

De forma predeterminada, el cliente reintenta dos veces y agota el tiempo de espera después de 1 minuto. Reintenta los errores de conexión y las respuestas con estado 408, 409, 429 o 500 y superiores, con retroceso exponencial. Establece los valores predeterminados en el cliente o pasa `RequestOptions` a una sola llamada:

```kotlin theme={null}
import com.dodopayments.api.core.RequestOptions
import java.time.Duration

// Global configuration
val client = DodoPaymentsOkHttpClient.builder()
    .fromEnv()
    .timeout(Duration.ofSeconds(45))
    .maxRetries(3)
    .build()

// Per-request timeout override
val product = client.products().retrieve(
    "pdt_123",
    RequestOptions.builder()
        .timeout(Duration.ofSeconds(10))
        .build()
)
```

## Operaciones comunes

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

### Crear una sesión de checkout

Crea una sesión de checkout y, después, redirige al cliente a la URL de checkout devuelta:

```kotlin theme={null}
val params = CheckoutSessionRequest.builder()
    .addProductCart(ProductItemReq.builder()
        .productId("pdt_123")
        .quantity(1)
        .build())
    .returnUrl("https://yourdomain.com/return")
    .build()

val session = client.checkoutSessions().create(params)
println("Checkout URL: ${session.checkoutUrl()}")
```

`checkoutUrl()` devuelve un `String?` anulable. Cada URL de checkout funciona una sola vez y caduca después de 24 horas. Para consultar todas las opciones de sesión, visita [Sesiones de checkout](/developer-resources/checkout-session).

### Crear un producto

Crea un producto de suscripción mensual con un precio de \$29.99:

```kotlin theme={null}
import com.dodopayments.api.models.products.Price
import com.dodopayments.api.models.products.Product
import com.dodopayments.api.models.products.ProductCreateParams
import com.dodopayments.api.models.misc.Currency
import com.dodopayments.api.models.misc.TaxCategory
import com.dodopayments.api.models.subscriptions.TimeInterval

val createParams = ProductCreateParams.builder()
    .name("Premium Subscription")
    .description("Monthly subscription with all features")
    .price(
        Price.RecurringPrice.builder()
            .currency(Currency.USD)
            .price(2999) // $29.99 in cents
            .discountBps(0)
            .purchasingPowerParity(false)
            .paymentFrequencyCount(1)
            .paymentFrequencyInterval(TimeInterval.MONTH)
            .subscriptionPeriodCount(1)
            .subscriptionPeriodInterval(TimeInterval.MONTH)
            .build()
    )
    .taxCategory(TaxCategory.DIGITAL_PRODUCTS)
    .build()

val product: Product = client.products().create(createParams)
println("Created product ID: ${product.productId()}")
```

`price` está expresado en la unidad monetaria más pequeña. `discountBps` establece el descuento en puntos básicos y reemplaza el campo obsoleto `discount`.

### Activar una clave de licencia

Activa una clave de licencia para un dispositivo o una instalación. Si la clave ha alcanzado su límite de activaciones, la API devuelve `422` y el SDK lanza `UnprocessableEntityException`. Una clave inactiva devuelve `403` (`PermissionDeniedException`), y una clave desconocida devuelve `404` (`NotFoundException`):

```kotlin theme={null}
import com.dodopayments.api.errors.UnprocessableEntityException
import com.dodopayments.api.models.licenses.LicenseActivateParams
import com.dodopayments.api.models.licenses.LicenseActivateResponse

val activateParams = LicenseActivateParams.builder()
    .licenseKey("XXXX-XXXX-XXXX-XXXX")
    .name("user-laptop-01")
    .build()

try {
    val activationResult: LicenseActivateResponse = client.licenses()
        .activate(activateParams)

    println("License activated successfully")
    println("Instance ID: ${activationResult.id()}")
    println("License key ID: ${activationResult.licenseKeyId()}")
} catch (e: UnprocessableEntityException) {
    println("License activation failed: ${e.message}")
}
```

### Gestionar suscripciones

Crea una suscripción y, después, cóbrala si es una suscripción bajo demanda.

<Warning>
  `POST /subscriptions` (el método `subscriptions().create()` 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>

```kotlin theme={null}
import com.dodopayments.api.models.payments.AttachExistingCustomer
import com.dodopayments.api.models.payments.BillingAddress
import com.dodopayments.api.models.misc.CountryCode
import com.dodopayments.api.models.subscriptions.SubscriptionChargeParams
import com.dodopayments.api.models.subscriptions.SubscriptionCreateParams

// Create a subscription
val subscriptionParams = SubscriptionCreateParams.builder()
    .billing(BillingAddress.builder()
        .city("San Francisco")
        .country(CountryCode.US)
        .state("CA")
        .street("1 Market St")
        .zipcode("94105")
        .build())
    .customer(AttachExistingCustomer.builder()
        .customerId("cus_123")
        .build())
    .productId("pdt_456")
    .quantity(1)
    .build()

val subscription = client.subscriptions().create(subscriptionParams)
println("Subscription ID: ${subscription.subscriptionId()}")

// Charge an on-demand subscription
// productPrice is in the lowest currency denomination (e.g., 2500 = $25.00 USD)
val chargeParams = SubscriptionChargeParams.builder()
    .subscriptionId(subscription.subscriptionId())
    .productPrice(2500)
    .build()

val chargeResponse = client.subscriptions().charge(chargeParams)
println("Payment ID: ${chargeResponse.paymentId()}")
```

<Info>
  `billing` solo requiere `country`, un código de país ISO de dos letras. Usa `AttachExistingCustomer` para asociar un cliente existente o `NewCustomer` para crear uno. `charge` es para [suscripciones bajo demanda](/developer-resources/ondemand-subscriptions), e `productPrice` está expresado en la unidad monetaria más pequeña.
</Info>

## Facturación basada en el uso

### Registrar eventos de uso

Envía un evento de uso para un cliente. Los medidores que registran su `eventName` lo agregan:

```kotlin theme={null}
import com.dodopayments.api.models.usageevents.EventInput
import com.dodopayments.api.models.usageevents.UsageEventIngestParams

val usageParams = UsageEventIngestParams.builder()
    .addEvent(EventInput.builder()
        .customerId("cus_456")
        .eventId("event_123")
        .eventName("api_call")
        .build())
    .build()

client.usageEvents().ingest(usageParams)
println("Usage event recorded")
```

El `eventId` es la clave de idempotencia, así que asigna un valor único a cada evento. Una solicitud acepta hasta 1.000 eventos.

## Operaciones asíncronas

### Cliente asíncrono

El cliente asíncrono tiene los mismos métodos que el cliente síncrono, pero la mayoría son funciones `suspend`. Llámalas desde una coroutine:

```kotlin theme={null}
import com.dodopayments.api.client.DodoPaymentsClientAsync
import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClientAsync
import kotlinx.coroutines.runBlocking

val asyncClient: DodoPaymentsClientAsync = DodoPaymentsOkHttpClientAsync.fromEnv()

runBlocking {
    val customer = asyncClient.customers().retrieve("cus_123")
    println("Customer email: ${customer.email()}")
}
```

También puedes llamar a `client.async()` en un cliente síncrono para obtener su versión asíncrona.

## Gestión de errores

Para un estado de error, el SDK lanza una subclase de `DodoPaymentsServiceException` que tiene `statusCode()`, `headers()` y `body()`. Las subclases son `BadRequestException` (400), `UnauthorizedException` (401), `PermissionDeniedException` (403), `NotFoundException` (404), `UnprocessableEntityException` (422), `RateLimitException` (429), `InternalServerException` (5xx) y `UnexpectedStatusCodeException` para otros estados, como 409:

```kotlin theme={null}
import com.dodopayments.api.errors.*

try {
    // Note: POST /payments is deprecated — prefer checkout sessions for new
    // integrations. Shown here only to illustrate error handling.
    // paymentParams is a PaymentCreateParams with billing, customer, and productCart.
    val payment = client.payments().create(paymentParams)
    println("Success: ${payment.paymentId()}")
} catch (e: UnauthorizedException) {
    println("Authentication failed: ${e.message}")
} catch (e: BadRequestException) {
    println("Invalid request: ${e.message}")
    println("Response body: ${e.body()}")
} catch (e: RateLimitException) {
    println("Rate limit exceeded, retry after: ${e.headers().values("retry-after")}")
} catch (e: DodoPaymentsServiceException) {
    println("API error: ${e.statusCode()} - ${e.message}")
}
```

Los fallos de red lanzan `DodoPaymentsIoException`, y las respuestas que el SDK no puede interpretar lanzan `DodoPaymentsInvalidDataException`. Todas las excepciones del SDK extienden `DodoPaymentsException`.

### Gestión funcional de errores

Usa `Result` para la gestión funcional de errores:

```kotlin theme={null}
import com.dodopayments.api.client.DodoPaymentsClient
import com.dodopayments.api.models.payments.PaymentCreateResponse

fun safeCreatePayment(client: DodoPaymentsClient): Result<PaymentCreateResponse> = runCatching {
    // Note: POST /payments is deprecated — prefer checkout sessions for new integrations
    // paymentParams is a PaymentCreateParams with billing, customer, and productCart.
    client.payments().create(paymentParams)
}

// Usage
safeCreatePayment(client)
    .onSuccess { payment -> println("Created: ${payment.paymentId()}") }
    .onFailure { error -> println("Error: ${error.message}") }
```

<Tip>
  `runCatching` captura todas las excepciones, incluidas las excepciones del SDK, y las devuelve como un `Result` fallido.
</Tip>

## Integración con Android

El Kotlin SDK es un server SDK. Se autentica con tu secret API key, y cualquiera que tenga tu APK puede extraer una clave compilada en él, así que nunca lo uses dentro de una aplicación Android.

Para aceptar pagos en una aplicación Android:

1. En tu servidor, crea la sesión de checkout con este SDK (consulta [Integración con Ktor](#ktor-integration)) y devuelve su `checkout_url`.
2. En la aplicación, obtén ese `checkout_url` desde tu servidor y ábrelo con el [Android SDK](/developer-resources/sdks/android), que no contiene ninguna API key.

```kotlin theme={null}
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class PaymentViewModel(private val api: YourBackendApi) : ViewModel() {
    fun createCheckout(productId: String) {
        viewModelScope.launch {
            // Your backend creates the session with the Kotlin SDK and returns checkout_url.
            // YourBackendApi and openCheckout are your app's own code; openCheckout
            // passes the URL to DodoCheckout from the Android SDK.
            val checkoutUrl = api.createCheckoutSession(productId).checkoutUrl
            openCheckout(checkoutUrl)
        }
    }
}
```

## Validación de respuestas

De forma predeterminada, el SDK lanza `DodoPaymentsInvalidDataException` únicamente cuando lees una propiedad con un tipo inesperado. Para comprobar toda la respuesta de antemano, habilita la validación para una solicitud o llama a `validate()` en una respuesta:

```kotlin theme={null}
import com.dodopayments.api.core.RequestOptions

// Per-request validation
val product = client.products().retrieve(
    "pdt_123",
    RequestOptions.builder()
        .responseValidation(true)
        .build()
)

// Or validate explicitly
val validatedProduct = product.validate()
```

## Funciones avanzadas

### Configuración del proxy

Para enviar solicitudes a través de un proxy, pasa un `java.net.Proxy` al builder:

```kotlin theme={null}
import java.net.InetSocketAddress
import java.net.Proxy

val client = DodoPaymentsOkHttpClient.builder()
    .fromEnv()
    .proxy(
        Proxy(
            Proxy.Type.HTTP,
            InetSocketAddress("proxy.example.com", 8080)
        )
    )
    .build()
```

### Configuración temporal

`withOptions` devuelve un cliente con ajustes modificados que comparte las conexiones y los pools de hilos del cliente original. El cliente original no cambia:

```kotlin theme={null}
val customClient = client.withOptions {
    it.baseUrl("https://example.com")
    it.maxRetries(5)
}
```

## Integración con Ktor

Crea el cliente una vez y llámalo desde una ruta:

```kotlin theme={null}
import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClient
import com.dodopayments.api.errors.DodoPaymentsServiceException
import com.dodopayments.api.models.checkoutsessions.CheckoutSessionRequest
import com.dodopayments.api.models.checkoutsessions.ProductItemReq
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun Application.configureRouting() {
    val client = DodoPaymentsOkHttpClient.builder()
        .bearerToken(environment.config.property("dodo.apiKey").getString())
        .build()
    
    routing {
        post("/create-checkout") {
            try {
                // CheckoutRequest is your own @Serializable request class with a productId field
                val request = call.receive<CheckoutRequest>()
                val params = CheckoutSessionRequest.builder()
                    .addProductCart(ProductItemReq.builder().productId(request.productId).quantity(1).build())
                    .build()
                val session = client.checkoutSessions().create(params)
                call.respond(mapOf("checkout_url" to session.checkoutUrl()))
            } catch (e: DodoPaymentsServiceException) {
                call.respond(HttpStatusCode.BadRequest, mapOf("error" to e.message))
            }
        }
    }
}
```

## Recursos

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

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Cada endpoint, parámetro y respuesta.
  </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-kotlin/issues">
    Informa de errores o solicita funciones.
  </Card>
</CardGroup>

## Soporte

Para obtener ayuda con el Kotlin SDK:

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

## Contribuciones

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


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