# Authorizing and capturing a payment In this guide, you'll authorize and capture a test payment using the Ratepay Java SDK. You'll initialize the client, authorize a payment, handle the authorization outcome, and capture the payment. Use `Environment.INTEGRATION` with Ratepay's shared test credentials. To request dedicated integration environment credentials, contact [integration@ratepay.com](mailto:integration@ratepay.com). ## 1. Build the client Create a `RatepayClient` using your API credentials and the integration environment. For additional configuration options, see [Client configuration](/docs/developer/sdk/client/client-configuration). ```java Java RatepayClient client = RatepayClient.builder() .clientId(System.getenv("RATEPAY_CLIENT_ID")) .clientSecret(System.getenv("RATEPAY_CLIENT_SECRET")) .environment(Environment.INTEGRATION) .build(); ``` ```kotlin Kotlin val client = RatepayClient { clientId(System.getenv("RATEPAY_CLIENT_ID")) clientSecret(System.getenv("RATEPAY_CLIENT_SECRET")) environment(Environment.INTEGRATION) } ``` ## 2. Authorize a payment Create an authorization request with the buyer's information, payment method, currency, and shopping basket. This example uses Open Invoice (`OPEN_INVOICE`) with bank transfer (`BANK_TRANSFER`), which does not require the buyer's bank details during authorization. ```java Java import com.ratepay.sdk.AuthorizeOutcome; import com.ratepay.sdk.model.api.payment.*; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; import java.util.UUID; var partnerTransactionId = UUID.randomUUID().toString(); AuthorizationRequest request = new AuthorizationRequest( new Buyer( new Address("Berlin", "DE", "Torstraße", "10119", null, "1"), // city, countryCode, street, zipCode, streetAddition, streetNumber new Contact("jane.doe@example.com", "+49 30 1234567"), null, // company is mutually exclusive with person as this is a B2C buyer null, // language new Person(LocalDate.of(1990, 4, 12), "Jane", "Doe", null), // dateOfBirth, firstName, lastName, title null // shopBuyerId ), "EUR", new PaymentMethod(PaymentOption.OPEN_INVOICE, TransferType.BANK_TRANSFER), List.of( new ShopBasket( new BigDecimal("29.99"), // grossAmount List.of( new Item( new BigDecimal("29.99"), "Wireless Mouse", BigDecimal.ONE, new BigDecimal("19"), // grossUnitPrice, name, quantity, taxRate "SKU-1001", null, null, null, null, null, null, null, null ) ), "SHOP-001", // partnerShopId null, null, partnerTransactionId, null // vats, shopMerchantId, shopTransactionId, specialItems ) ), null, null, null, null, null, null, partnerTransactionId, null, null ); AuthorizeOutcome outcome = client.authorize(request, null); // null = let the SDK generate an idempotency key String ratepayTransactionId = outcome.ratepayTransactionId(); // always present, whatever the outcome ``` ```kotlin Kotlin import com.ratepay.sdk.model.api.payment.* import java.math.BigDecimal import java.time.LocalDate import java.util.UUID val partnerTransactionId = UUID.randomUUID().toString() val request = AuthorizationRequest( paymentMethod = PaymentMethod(PaymentOption.OPEN_INVOICE, TransferType.BANK_TRANSFER), currency = "EUR", buyer = Buyer( billingAddress = Address(street = "Torstraße", streetNumber = "1", zipCode = "10119", city = "Berlin", countryCode = "DE"), contact = Contact(email = "jane.doe@example.com", phone = "+49 30 1234567"), person = Person(firstName = "Jane", lastName = "Doe", dateOfBirth = LocalDate.of(1990, 4, 12)), ), shopBaskets = listOf( ShopBasket( partnerShopId = "SHOP-001", grossAmount = BigDecimal("29.99"), items = listOf( Item( grossUnitPrice = BigDecimal("29.99"), name = "Wireless Mouse", taxRate = BigDecimal("19"), quantity = BigDecimal.ONE, articleNumber = "SKU-1001", ), ), vats = null, shopTransactionId = partnerTransactionId, ), ), partnerTransactionId = partnerTransactionId, ) val outcome = client.authorize(request) // idempotencyKey defaults to null → SDK generates one val ratepayTransactionId = outcome.ratepayTransactionId() // always present, whatever the outcome ``` For additional request fields, supported payment methods, and B2B buyer details, see [Authorizing a transaction](/docs/developer/sdk/guides/authorizing-a-transaction). ## 3. Handle the outcome The authorization returns one of three typed outcomes, identified by `AuthorizeOutcome.Kind`. Payment declines are handled as business outcomes rather than exceptions. | Outcome Kind | Description | | --- | --- | | `ACCEPTED` | The payment is authorized. You can proceed with capture once the order is fulfilled. | | `DECLINED` | The authorization was declined. Inspect the decline category and reason to determine how to handle the result. Do not automatically retry the same request. | | `OTP_REQUIRED` | The buyer must complete one-time passcode (OTP) verification before the authorization can be confirmed. See [Completing OTP verification](/docs/developer/sdk/guides/completing-otp-verification). | ```java Java switch (outcome.kind()) { case ACCEPTED -> { var accepted = (AuthorizeOutcome.Accepted) outcome; System.out.println("Authorized, expires " + accepted.getAuthorizationExpires()); } case DECLINED -> { var declined = (AuthorizeOutcome.Declined) outcome; System.out.println("Declined: " + declined.getDecline().getCategory()); } case OTP_REQUIRED -> { var otp = (AuthorizeOutcome.OtpRequired) outcome; System.out.println("OTP required: " + otp.getOtpDetails().getMessage().getEn()); } } ``` ```kotlin Kotlin when (outcome) { is AuthorizeOutcome.Accepted -> println("Authorized, expires ${outcome.authorizationExpires}") is AuthorizeOutcome.Declined -> println("Declined: ${outcome.decline.category}") is AuthorizeOutcome.OtpRequired -> println("OTP required: ${outcome.otpDetails.message.en}") } ``` ## 4. Capture the transaction Once the authorization is accepted, capture the payment when the order is fulfilled, typically upon shipment or order confirmation. Use the Ratepay transaction ID returned during authorization to submit the capture request. This example captures the full authorized amount of €29.99. A successful capture returns a Ratepay capture ID. ```java Java import com.ratepay.sdk.CaptureOutcome; import com.ratepay.sdk.model.api.payment.CaptureRequest; CaptureRequest captureRequest = new CaptureRequest( new BigDecimal("29.99"), null, null, null, null, null, null, null, null ); CaptureOutcome captureOutcome = client.capture(ratepayTransactionId, captureRequest, null); if (captureOutcome.kind() == CaptureOutcome.Kind.ACCEPTED) { var accepted = (CaptureOutcome.Accepted) captureOutcome; System.out.println("Captured " + accepted.getRatepayCaptureId()); } ``` ```kotlin Kotlin import com.ratepay.sdk.CaptureOutcome import com.ratepay.sdk.model.api.payment.CaptureRequest val captureRequest = CaptureRequest(grossAmount = BigDecimal("29.99")) val captureOutcome = client.capture(ratepayTransactionId, captureRequest) if (captureOutcome is CaptureOutcome.Accepted) { println("Captured ${captureOutcome.ratepayCaptureId}") } ``` For additional capture scenarios, see [Capturing a transaction](/docs/developer/sdk/guides/capturing-a-transaction). ## Next - Explore additional [authorization fields](/docs/developer/sdk/guides/authorizing-a-transaction), [cancellations](/docs/developer/sdk/guides/cancelling-a-transaction), [refunds](/docs/developer/sdk/guides/refunding-a-transaction), [incremental authorizations](/docs/developer/sdk/guides/incremental-authorization) and [adding shipping information](/docs/developer/sdk/guides/adding-shipping-information). - Learn how to handle [exceptions](/docs/developer/sdk/errors-and-resilience/exception-hierarchy).