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

# Java

> Usa el SDK de Java de Dodo Payments para crear sesiones de checkout, clientes, suscripciones, medidores y eventos de uso desde Java 8+, con clientes síncronos y asíncronos.

El SDK de Java proporciona a las aplicaciones Java acceso tipado a la API REST de Dodo Payments. Usa tipos de Java en todo momento: `Optional` para campos que pueden faltar, `Stream` para iterar sobre resultados y `CompletableFuture` para llamadas asíncronas.

## Instalación

### Maven

Agrega la dependencia a tu `pom.xml`:

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

### Gradle

Añade la dependencia a tu `build.gradle.kts`:

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

<Tip>
  Las versiones del SDK añaden 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-java).
</Tip>

<Info>
  El SDK requiere Java 8 o una versión posterior, por lo que también funciona con Java 11, 17 y 21.
</Info>

## Inicio Rápido

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

```java 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)
DodoPaymentsClient client = DodoPaymentsOkHttpClient.fromEnv();

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

`fromEnv()` se conecta al modo live, 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 API key del modo de prueba solo funciona en el modo de prueba.

<Warning>
  Guarda las API keys en variables de entorno, propiedades del sistema o un gestor de secretos. Nunca las escribas directamente en el código fuente.
</Warning>

## Funciones principales

<CardGroup cols={2}>
  <Card title="Type Safety" icon="shield-check">
    Clases de solicitudes y respuestas tipadas para realizar comprobaciones en tiempo de compilación.
  </Card>

  <Card title="Shared Client" icon="bolt">
    Crea un solo cliente y reutilízalo en todas las solicitudes: mantiene la conexión y los grupos de hilos. Los objetos de solicitudes y respuestas son inmutables.
  </Card>

  <Card title="Builder Pattern" icon="layer-group">
    Cada clase de solicitud tiene un builder, y `toBuilder()` crea una copia modificada.
  </Card>

  <Card title="Async Support" icon="arrows-rotate">
    `client.async()` devuelve un cliente cuyos métodos retornan `CompletableFuture`.
  </Card>
</CardGroup>

## Configuración

### Variables de entorno

`fromEnv()` lee estas variables de entorno o las propiedades del sistema correspondientes. Las propiedades del sistema tienen prioridad:

```bash .env theme={null}
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_PAYMENTS_BASE_URL=https://live.dodopayments.com
```

```java theme={null}
// Automatically reads from environment variables
DodoPaymentsClient client = DodoPaymentsOkHttpClient.fromEnv();
```

La API key se obtiene de `DODO_PAYMENTS_API_KEY` o `dodopayments.apiKey`. El secreto de firma del webhook se obtiene de `DODO_PAYMENTS_WEBHOOK_KEY` o `dodopayments.webhookKey`, y la URL base de `DODO_PAYMENTS_BASE_URL` o `dodopayments.baseUrl`. Crea un solo 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

Define cada opción en el builder:

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

DodoPaymentsClient client = DodoPaymentsOkHttpClient.builder()
    .bearerToken("your_api_key_here")
    .baseUrl("https://live.dodopayments.com")
    .maxRetries(4)
    .timeout(Duration.ofSeconds(30))
    .responseValidation(true)
    .build();
```

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. Para cambiar el tiempo de espera de una llamada, pasa `RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()` como segundo argumento del método. `responseValidation(true)` comprueba de antemano que toda la respuesta coincida con los tipos esperados. Sin esta opción, el SDK lanza `DodoPaymentsInvalidDataException` únicamente cuando lees una propiedad con un tipo inesperado.

### Modo de prueba

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

```java theme={null}
DodoPaymentsClient testClient = DodoPaymentsOkHttpClient.builder()
    .fromEnv()
    .testMode()
    .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, a continuación, redirige al cliente a la URL de checkout devuelta:

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

CheckoutSessionResponse session = client.checkoutSessions().create(params);
System.out.println("Checkout URL: " + session.checkoutUrl().orElse(""));
```

`checkoutUrl()` devuelve un `Optional<String>`. 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 correo electrónico, un nombre y metadatos; después, recupéralo por su ID:

```java theme={null}
import com.dodopayments.api.core.JsonValue;
import com.dodopayments.api.models.customers.Customer;
import com.dodopayments.api.models.customers.CustomerCreateParams;
import com.dodopayments.api.models.misc.Metadata;

// Create a customer
CustomerCreateParams createParams = CustomerCreateParams.builder()
    .email("customer@example.com")
    .name("John Doe")
    .metadata(Metadata.builder()
        .putAdditionalProperty("user_id", JsonValue.from("12345"))
        .build())
    .build();

Customer customer = client.customers().create(createParams);

// Retrieve customer
Customer retrieved = client.customers().retrieve("cus_123");
System.out.println("Customer: " + retrieved.name() + " (" + retrieved.email() + ")");
```

### Gestionar suscripciones

Crea una suscripción con un enlace de pago y, a continuación, realiza un cargo 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>

```java 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.SubscriptionChargeResponse;
import com.dodopayments.api.models.subscriptions.SubscriptionCreateParams;
import com.dodopayments.api.models.subscriptions.SubscriptionCreateResponse;

// Create a subscription
SubscriptionCreateParams 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)
    .paymentLink(true)
    .returnUrl("https://yourdomain.com/return")
    .build();

SubscriptionCreateResponse subscription = client.subscriptions().create(subscriptionParams);
System.out.println("Subscription ID: " + subscription.subscriptionId());

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

SubscriptionChargeResponse chargeResponse = client.subscriptions().charge(chargeParams);
System.out.println("Payment ID: " + chargeResponse.paymentId());
```

<Info>
  `productPrice` se expresa en la unidad monetaria más pequeña, como centavos para USD o paise para INR. Para realizar un cargo de \$25.00, pasa `2500`.
</Info>

<Tip>
  `subscriptions().charge(...)` se utiliza para [suscripciones bajo demanda](/developer-resources/ondemand-subscriptions). Dodo Payments factura automáticamente las demás suscripciones según el calendario de facturación del producto.
</Tip>

## Facturación basada en el uso

### Configurar medidores

Crea un medidor que cuente eventos y, después, muestra tus medidores. `autoPager()` itera sobre cada medidor y obtiene más páginas según sea necesario:

```java theme={null}
import com.dodopayments.api.models.meters.*;

// Create API calls meter
MeterCreateParams apiMeterParams = MeterCreateParams.builder()
    .name("API Requests")
    .eventName("api_request")
    .aggregation(MeterAggregation.builder()
        .type(MeterAggregation.Type.COUNT)
        .build())
    .measurementUnit("calls")
    .build();

Meter apiMeter = client.meters().create(apiMeterParams);
System.out.println("Meter created: " + apiMeter.id());

// List all meters
client.meters().list()
    .autoPager()
    .forEach(m -> System.out.println("Meter: " + m.name() + " - " + m.aggregation()));
```

### Ingerir eventos de uso

Envía un evento de uso para un cliente. Los valores de metadatos del evento son objetos `JsonValue`:

```java theme={null}
import com.dodopayments.api.core.JsonValue;
import com.dodopayments.api.models.usageevents.EventInput;
import com.dodopayments.api.models.usageevents.*;
import java.time.OffsetDateTime;

// Ingest single event
UsageEventIngestParams singleEventParams = UsageEventIngestParams.builder()
    .addEvent(EventInput.builder()
        .eventId("api_call_" + System.currentTimeMillis())
        .customerId("cus_abc123")
        .eventName("api_request")
        .timestamp(OffsetDateTime.now())
        .metadata(EventInput.Metadata.builder()
            .putAdditionalProperty("endpoint", JsonValue.from("/api/v1/users"))
            .putAdditionalProperty("method", JsonValue.from("GET"))
            .putAdditionalProperty("tokens_used", JsonValue.from("150"))
            .build())
        .build())
    .build();

UsageEventIngestResponse response = client.usageEvents().ingest(singleEventParams);
System.out.println("Processed: " + response.ingestedCount());
```

`eventId` es la clave de idempotencia, así que asigna un valor único a cada evento. Se rechaza un `timestamp` de más de 1 hora de antigüedad o de más de 5 minutos en el futuro.

### Ingerir eventos por lotes

Envía hasta 1.000 eventos en una sola solicitud. Este ejemplo usa las importaciones del ejemplo anterior:

```java theme={null}
UsageEventIngestParams.Builder batchBuilder = UsageEventIngestParams.builder();

for (int i = 0; i < 100; i++) {
    batchBuilder.addEvent(EventInput.builder()
        .eventId("batch_event_" + i + "_" + System.currentTimeMillis())
        .customerId("cus_abc123")
        .eventName("video_transcode")
        .timestamp(OffsetDateTime.now().minusSeconds(i))
        .metadata(EventInput.Metadata.builder()
            .putAdditionalProperty("video_id", JsonValue.from("video_" + i))
            .putAdditionalProperty("duration_seconds", JsonValue.from(String.valueOf(120 + i)))
            .build())
        .build());
}

UsageEventIngestResponse batchResponse = client.usageEvents().ingest(batchBuilder.build());
System.out.println("Batch processed: " + batchResponse.ingestedCount() + " events");
```

## Gestión de errores

El SDK lanza excepciones no comprobadas. Para un estado de error, lanza una subclase de `DodoPaymentsServiceException`, que contiene `statusCode()`, `headers()` y `body()`. Captura las clases específicas que quieras gestionar antes de la clase base:

```java theme={null}
import com.dodopayments.api.errors.*;
import com.dodopayments.api.models.payments.Payment;

try {
    Payment payment = client.payments().retrieve("pay_invalid");
} catch (NotFoundException e) {
    System.err.println("Payment not found: " + e.getMessage());
} catch (UnauthorizedException e) {
    System.err.println("Authentication failed: " + e.getMessage());
} catch (PermissionDeniedException e) {
    System.err.println("Permission denied: " + e.getMessage());
} catch (BadRequestException e) {
    System.err.println("Invalid request: " + e.getMessage());
} catch (UnprocessableEntityException e) {
    System.err.println("Validation error: " + e.getMessage());
} catch (RateLimitException e) {
    System.err.println("Rate limit exceeded: " + e.getMessage());
    // Thrown after the SDK's automatic retries are used up
} catch (InternalServerException e) {
    System.err.println("Server error: " + e.getMessage());
} catch (DodoPaymentsServiceException e) {
    System.err.println("API error: " + e.statusCode() + " - " + e.getMessage());
}
```

Los estados que no tienen su propia clase, como 409, lanzan `UnexpectedStatusCodeException`. Los fallos de red lanzan `DodoPaymentsIoException`, y las respuestas que el SDK no puede interpretar lanzan `DodoPaymentsInvalidDataException`. Todas estas clases extienden `DodoPaymentsException`.

<Tip>
  El SDK reintenta los errores de conexión y las respuestas con estado 408, 409, 429 o 500 y superiores, dos veces de forma predeterminada, con retroceso exponencial.
</Tip>

## Operaciones asíncronas

Llama a `async()` en el cliente para obtener un cliente asíncrono. Sus métodos devuelven un `CompletableFuture`:

```java theme={null}
import java.util.concurrent.CompletableFuture;

CompletableFuture<CheckoutSessionResponse> future = client.async()
    .checkoutSessions()
    .create(params);

// Handle response asynchronously
future.thenAccept(response -> {
    System.out.println("Session created: " + response.sessionId());
}).exceptionally(ex -> {
    System.err.println("Error: " + ex.getMessage());
    return null;
});
```

Para crear un cliente asíncrono desde el principio, usa `DodoPaymentsOkHttpClientAsync.fromEnv()`.

## Integración con Spring Boot

### Clase de configuración

Registra un cliente como bean y elige el entorno desde una propiedad:

```java theme={null}
import com.dodopayments.api.client.DodoPaymentsClient;
import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DodoPaymentsConfig {
    
    @Value("${dodo.api.key}")
    private String apiKey;
    
    @Value("${dodo.environment:test}")
    private String environment;
    
    @Bean
    public DodoPaymentsClient dodoPayments() {
        return DodoPaymentsOkHttpClient.builder()
            .bearerToken(apiKey)
            .baseUrl(environment.equals("live") 
                ? "https://live.dodopayments.com" 
                : "https://test.dodopayments.com")
            .build();
    }
}
```

### Capa de servicio

Inyecta el cliente en un servicio:

```java theme={null}
import com.dodopayments.api.client.DodoPaymentsClient;
import com.dodopayments.api.models.checkoutsessions.*;
import java.util.List;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {
    
    private final DodoPaymentsClient client;
    
    public PaymentService(DodoPaymentsClient client) {
        this.client = client;
    }
    
    public CheckoutSessionResponse createCheckout(List<ProductItemReq> items) {
        CheckoutSessionRequest params = CheckoutSessionRequest.builder()
            .productCart(items)
            .returnUrl("https://yourdomain.com/return")
            .build();
            
        return client.checkoutSessions().create(params);
    }
}
```

## Recursos

<CardGroup cols={2}>
  <Card title="GitHub Repository" icon="github" href="https://github.com/dodopayments/dodopayments-java">
    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 conversa con otros desarrolladores.
  </Card>

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

## Soporte

Para obtener ayuda con el SDK de Java:

* **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-java).

## Contribuir

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


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