# Client configuration `RatepayClient` is the main interface for interacting with the Ratepay SDK. It manages HTTP connections and OAuth access tokens, and supports concurrent requests. Create one client per credential set and reuse it throughout your application's lifecycle. For multi-shop integrations where each shop uses separate OAuth credentials, create a dedicated client for each credential set. ## Initialize the client To initialize a `RatepayClient`, provide your OAuth client ID, client secret, and target [environment](/docs/developer/sdk/client/authentication#environments). ```java Java import com.ratepay.sdk.Environment; import com.ratepay.sdk.RatepayClient; RatepayClient client = RatepayClient.builder() .clientId(System.getenv("RATEPAY_CLIENT_ID")) .clientSecret(System.getenv("RATEPAY_CLIENT_SECRET")) .environment(Environment.INTEGRATION) .build(); ``` ```kotlin Kotlin import com.ratepay.sdk.Environment import com.ratepay.sdk.RatepayClient val client = RatepayClient { clientId(System.getenv("RATEPAY_CLIENT_ID")) clientSecret(System.getenv("RATEPAY_CLIENT_SECRET")) environment(Environment.INTEGRATION) } ``` Kotlin supports the `RatepayClient { ... }` configuration syntax shown above. Alternatively, you can use the standard Java-style builder. The SDK manages HTTP transport, timeouts, and token refresh behavior internally. See [Configuration limitations](/docs/developer/sdk/getting-started/compatibility#configuration-limitations) for details. ## Validate your credentials Creating a client with `build()` does **not** initiate a network request. It validates the required configuration and throws `RatepayConfigurationException` if the client ID, client secret, or environment is missing or invalid. Authentication occurs automatically when the client makes its first API request. To validate your credentials before processing payments, call `validateCredentials()`. This method obtains or reuses an OAuth access token without initiating a payment. ```java Java try { client.validateCredentials(); } catch (RatepayAuthException e) { // Credentials are wrong or revoked, fail startup instead of on // the first customer's checkout. throw new IllegalStateException("Ratepay credentials rejected", e); } ``` ```kotlin Kotlin try { client.validateCredentials() } catch (e: RatepayAuthException) { // Credentials are wrong or revoked, fail startup instead of on // the first customer's checkout. throw IllegalStateException("Ratepay credentials rejected", e) } ``` Calling `validateCredentials()` during application startup is optional but recommended. It helps detect authentication issues before processing payments and populates the SDK's token cache for subsequent requests. For more information, see [Authentication](/docs/developer/sdk/client/authentication). ## Thread safety and reuse A built `RatepayClient` is thread-safe and can be shared across concurrent requests. However, `RatepayClient.Builder` is not thread-safe and should only be used once. Create a new builder for each client instance rather than sharing or reusing an existing builder. *Note: For applications serving multiple shop profiles, initialize a separate client for each credential set.* Reusing client instances allows the SDK to maintain pooled HTTP connections and cached OAuth tokens, avoiding unnecessary initialization and authentication overhead. ```java Java // Do this once, at startup, and keep the reference. private static final RatepayClient CLIENT = RatepayClient.builder() .clientId(clientId) .clientSecret(clientSecret) .environment(Environment.PRODUCTION) .build(); // Every request thread can safely call CLIENT.authorize(...), CLIENT.capture(...), etc. ``` ```kotlin Kotlin // Do this once, at startup, and keep the reference — e.g. as a singleton bean. val client: RatepayClient = RatepayClient { clientId(clientId) clientSecret(clientSecret) environment(Environment.PRODUCTION) } // Every request thread can safely call client.authorize(...), client.capture(...), etc. ``` ## Manage the client lifecycle `RatepayClient` implements `AutoCloseable`. Call `close()` when the client is no longer needed, typically during application shutdown, to release its HTTP connections. - **Close the client at shutdown:** Avoid closing it after individual payment operations. - **Do not reuse a closed client:** Create a new instance if additional requests are required. - **Use application-managed lifecycle hooks:** In frameworks such as Spring, register the client as a singleton bean and configure it to close during shutdown. For applications that manage resources explicitly, use Java's try-with-resources or Kotlin's `use` function. ```java Java // close() declares no checked exception public static void main(String[] args) { try (RatepayClient client = RatepayClient.builder() .clientId(clientId) .clientSecret(clientSecret) .environment(Environment.PRODUCTION) .build()) { client.validateCredentials(); // ... run your application using `client` for its whole lifetime ... } // connections released here } ``` ```kotlin Kotlin RatepayClient { clientId(clientId) clientSecret(clientSecret) environment(Environment.PRODUCTION) }.use { client -> client.validateCredentials() // ... run your application using `client` for its whole lifetime ... } // connections released here ``` ## Extend or wrap the client `RatepayClient` is an interface, allowing you to create custom wrappers for logging, metrics, circuit breaking, or testing without subclassing the SDK's internal implementation. For implementation examples, see [Logging and observability](/docs/developer/sdk/operations/logging-and-observability) and [Testing overview](/docs/developer/sdk/testing/testing-overview).