Skip to main content
The Kotlin SDK provides convenient access to the Dodo Payments REST API from applications written in Kotlin. It features nullable values, Sequence, suspend functions, and other Kotlin-specific features for ergonomic use.

Installation

Gradle (Kotlin DSL)

Add the dependency to your build.gradle.kts:
build.gradle.kts
implementation("com.dodopayments.api:dodo-payments-kotlin:1.53.4")

Maven

Add the dependency to your pom.xml:
pom.xml
<dependency>
  <groupId>com.dodopayments.api</groupId>
  <artifactId>dodo-payments-kotlin</artifactId>
  <version>1.53.4</version>
</dependency>
The SDK requires Kotlin 1.6 or higher and is compatible with both JVM and Android platforms.

Quick Start

Initialize the client and create a checkout session:
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

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

val params: CheckoutSessionRequest = CheckoutSessionRequest.builder()
    .addProductCart(CheckoutSessionRequest.ProductCart.builder()
        .productId("product_id")
        .quantity(1)
        .build())
    .build()
    
val checkoutSessionResponse: CheckoutSessionResponse = client.checkoutSessions().create(params)
println(checkoutSessionResponse.sessionId())
Always store your API keys securely using environment variables or encrypted configuration. Never commit them to version control.

Core Features

Coroutines

Full support for Kotlin coroutines for asynchronous operations

Null Safety

Leverage Kotlin’s null safety for robust error handling

Extension Functions

Idiomatic Kotlin extensions for enhanced functionality

Data Classes

Type-safe data classes with copy and destructuring support

Configuration

From Environment Variables

Initialize from environment variables or system properties:
val client: DodoPaymentsClient = DodoPaymentsOkHttpClient.fromEnv()

Manual Configuration

Configure manually with all options:
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()

Test Mode

Configure for test/sandbox environment:
val testClient = DodoPaymentsOkHttpClient.builder()
    .fromEnv()
    .testMode()
    .build()

Timeouts and Retries

Configure globally or per-request:
import com.dodopayments.api.core.RequestOptions

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

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

Common Operations

Create a Checkout Session

Generate a checkout session:
val params = CheckoutSessionRequest.builder()
    .addProductCart(CheckoutSessionRequest.ProductCart.builder()
        .productId("prod_123")
        .quantity(1)
        .build())
    .returnUrl("https://yourdomain.com/return")
    .build()

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

Create a Product

Create products with detailed configuration:
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

val createParams = ProductCreateParams.builder()
    .name("Premium Subscription")
    .description("Monthly subscription with all features")
    .price(
        ProductCreateParams.RecurringPrice.builder()
            .currency(Currency.USD)
            .preTaxAmount(2999) // $29.99 in cents
            .paymentFrequencyInterval(ProductCreateParams.RecurringPrice.TimeInterval.MONTH)
            .paymentFrequencyCount(1)
            .build()
    )
    .taxCategory(TaxCategory.DIGITAL_GOODS)
    .build()

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

Activate License Key

Activate license keys for customers:
import com.dodopayments.api.models.licenses.LicenseActivateParams
import com.dodopayments.api.models.licenses.LicenseActivateResponse

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

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

    println("License activated successfully")
    println("Instance ID: ${activationResult.instanceId()}")
    println("Expires at: ${activationResult.expiresAt()}")
} catch (e: UnprocessableEntityException) {
    println("License activation failed: ${e.message}")
}

Usage-Based Billing

Record Usage Events

Track usage for meters:
import com.dodopayments.api.models.usageevents.UsageEventCreateParams
import java.time.OffsetDateTime

val usageParams = UsageEventCreateParams.builder()
    .meterId("meter_123")
    .customerId("cust_456")
    .value(150)
    .timestamp(OffsetDateTime.now())
    .build()

client.usageEvents().create(usageParams)
println("Usage event recorded")

Async Operations

Async Client

Use the async client for coroutine-based operations:
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("cust_123")
    println("Customer email: ${customer.email()}")
}

Error Handling

Handle errors with Kotlin’s exception handling:
import com.dodopayments.api.errors.*

try {
    val payment = client.payments().create(params)
    println("Success: ${payment.id()}")
} catch (e: AuthenticationException) {
    println("Authentication failed: ${e.message}")
} catch (e: InvalidRequestException) {
    println("Invalid request: ${e.message}")
    e.parameter?.let { println("Parameter: $it") }
} catch (e: RateLimitException) {
    println("Rate limit exceeded, retry after: ${e.retryAfter}")
} catch (e: DodoPaymentsServiceException) {
    println("API error: ${e.statusCode()} - ${e.message}")
}

Functional Error Handling

Use Result for functional error handling:
fun safeCreatePayment(client: DodoPaymentsClient): Result<Payment> = runCatching {
    client.payments().create(params)
}

// Usage
safe CreatePayment(client)
    .onSuccess { payment -> println("Created: ${payment.id()}") }
    .onFailure { error -> println("Error: ${error.message}") }
Use Kotlin’s runCatching for a more functional approach to error handling with Result types.

Android Integration

Use with Android applications:
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dodopayments.api.client.DodoPaymentsClient
import kotlinx.coroutines.launch

class PaymentViewModel(application: Application) : ViewModel() {
    private val client = DodoPaymentsOkHttpClient.builder()
        .bearerToken(BuildConfig.DODO_API_KEY)
        .build()
    
    fun createCheckout(productId: String) {
        viewModelScope.launch {
            try {
                val session = client.async().checkoutSessions().create(params)
                // Open checkout URL in browser or WebView
                openUrl(session.url())
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
}

Response Validation

Enable response validation:
import com.dodopayments.api.core.RequestOptions

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

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

Advanced Features

Proxy Configuration

Configure proxy settings:
import java.net.InetSocketAddress
import java.net.Proxy

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

Temporary Configuration

Modify client configuration temporarily:
val customClient = client.withOptions {
    it.baseUrl("https://example.com")
    it.maxRetries(5)
}

Ktor Integration

Integrate with Ktor server applications:
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 {
                val request = call.receive<CheckoutRequest>()
                val session = client.checkoutSessions().create(params)
                call.respond(mapOf("checkout_url" to session.url()))
            } catch (e: DodoPaymentsServiceException) {
                call.respond(HttpStatusCode.BadRequest, mapOf("error" to e.message))
            }
        }
    }
}

Resources

Support

Need help with the Kotlin SDK?

Contributing

We welcome contributions! Check the contributing guidelines to get started.