# Generating a SECCI For transactions subject to the EU Consumer Credit Directive II (CCD2), buyers must receive Standard European Consumer Credit Information (SECCI) before payment authorization. Use `generateSecci()` to generate and deliver the document. The method returns an attestation token that must be included in the subsequent authorization request. For details on CCD2 requirements and the buyer experience, see [CCD2 with Payment API 2.0](https://docs.ratepay.com/docs/developer/api_integration/payment_2.0/payment_api_2.0). ## Request The `SecciRequest` specifies the payment method, shopping basket, currency, and delivery method. | Field | Description | | --- | --- | | `currency` | Three-letter uppercase currency code, such as `EUR`. | | `delivery.method` | `PDF` to return the document, or `EMAIL` to send it to the buyer. | | `delivery.action` | `DOWNLOAD` or `PRINT`. Required for `PDF` delivery. | | `delivery.email` | Buyer's email address. Required for `EMAIL` delivery. | | `delivery.countryCode`, `delivery.language` | Optional country and language settings for the document. | | `paymentMethod.paymentOption` | Payment option, such as `OPEN_INVOICE` or `PAY_NOW`. | | `paymentMethod.transferType` | Transfer type, such as `BANK_TRANSFER` or `SEPA_DIRECT_DEBIT`. | | `shopBaskets` | At least one basket containing `partnerShopId`, `grossAmount`, and at least one item or special item. | The following example generates a SECCI document for an Open Invoice payment and returns the PDF for delivery to the buyer. ```java Java import com.ratepay.sdk.SecciOutcome; import com.ratepay.sdk.model.api.secci.*; import java.math.BigDecimal; import java.util.List; SecciRequest request = new SecciRequest( "EUR", new SecciDelivery( SecciDeliveryMethod.PDF, SecciDeliveryAction.DOWNLOAD, "DE", // countryCode null, // email "de" // language ), new SecciPaymentMethod(SecciPaymentMethodOption.OPEN_INVOICE, null), // paymentOption, transferType List.of( new SecciShopBasket( new BigDecimal("249.00"), // grossAmount "SHOP-001", // partnerShopId List.of( new SecciItem(new BigDecimal("249.00"), "Ergonomic Office Chair", new BigDecimal("1"), new BigDecimal("19")) // grossUnitPrice, name, quantity, taxRate ), null // specialItems ) ) ); SecciOutcome outcome = client.generateSecci(request, null); if (outcome.isDocument()) { var document = (SecciOutcome.Document) outcome; byte[] pdf = document.getPdf(); // show or print this to the buyer String attestationToken = document.getAttestationToken(); } else { var emailed = (SecciOutcome.Emailed) outcome; System.out.println("SECCI sent, id " + emailed.getSecciId()); } ``` ```kotlin Kotlin import com.ratepay.sdk.SecciOutcome import com.ratepay.sdk.model.api.secci.* import java.math.BigDecimal val request = SecciRequest( currency = "EUR", delivery = SecciDelivery( method = SecciDeliveryMethod.PDF, action = SecciDeliveryAction.DOWNLOAD, countryCode = "DE", language = "de", ), paymentMethod = SecciPaymentMethod(paymentOption = SecciPaymentMethodOption.OPEN_INVOICE), shopBaskets = listOf( SecciShopBasket( grossAmount = BigDecimal("249.00"), partnerShopId = "SHOP-001", items = listOf( SecciItem( grossUnitPrice = BigDecimal("249.00"), name = "Ergonomic Office Chair", quantity = BigDecimal("1"), taxRate = BigDecimal("19")), ), ), ), ) when (val outcome = client.generateSecci(request)) { is SecciOutcome.Document -> { val pdf: ByteArray = outcome.pdf // show or print this to the buyer val attestationToken = outcome.attestationToken } is SecciOutcome.Emailed -> println("SECCI sent, id ${outcome.secciId}") } ``` ## Deliver the SECCI by email To send the document directly to the buyer, set `delivery.method` to `EMAIL` and provide the buyer's email address. ```java Java SecciDelivery delivery = new SecciDelivery( SecciDeliveryMethod.EMAIL, null, // action, not used for email "DE", // countryCode "buyer@example.com", // email "de" // language ); ``` ```kotlin Kotlin val delivery = SecciDelivery( method = SecciDeliveryMethod.EMAIL, email = "buyer@example.com", countryCode = "DE", language = "de", ) ``` For email delivery, the SDK returns `SecciOutcome.Emailed` with a `secciId` rather than PDF content. The `attestationToken` is available for both delivery methods. ## Reuse a basket from an authorization request If you have already constructed an `AuthorizationRequest`, you can pass its `shopBaskets` directly to the `generateSecci()` overload instead of constructing the basket again. ```java Java SecciOutcome outcome = client.generateSecci(request, authorizationRequest.getShopBaskets(), null); ``` ```kotlin Kotlin val outcome = client.generateSecci(request, authorizationRequest.shopBaskets) ``` The SDK converts the supplied Payment API baskets into the SECCI request format, replacing any `shopBaskets` already present in the `SecciRequest`. Other fields, such as `currency` and `paymentMethod`, remain unchanged. ## Handle the response The SDK returns a `SecciOutcome` based on the selected delivery method. | Outcome Kind | Description | | --- | --- | | `DOCUMENT` | PDF delivery. Contains the SECCI PDF bytes and an attestation token. | | `EMAILED` | Email delivery. Contains the SECCI ID and an attestation token. No PDF is returned. | Both outcomes include an `attestationToken`, corresponding to the API's `X-Attestation-Token` response header. SECCI generation failures throw exceptions rather than returning a decline outcome. ## Authorize the payment with the attestation token After providing the SECCI to the buyer, pass the returned attestation token to the `authorize()` method. ```java Java AuthorizeOutcome authorization = client.authorize(authorizationRequest, outcome.getAttestationToken(), null); ``` ```kotlin Kotlin val authorization = client.authorize(authorizationRequest, outcome.attestationToken) ``` The SDK adds the token to complianceDetails.attestationToken in the authorization request. The authorization must use the same basket and payment method as the SECCI. If these details change, generate and provide a new SECCI before authorizing the payment. The attestation token is valid for seven days. If it expires before authorization, repeat the SECCI process. For details, see [Authorizing a payment](/docs/developer/sdk/guides/authorizing-a-transaction). ## Client-side validation Before sending the request, the SDK validates the required fields and throws `RatepaySdkValidationException` if validation fails. The checks include: - **Delivery:** A valid delivery method, action for PDF delivery, and a correctly formatted email address for email delivery. - **Payment method:** A supported payment option; UNKNOWN values are rejected. - **Currency:** Three uppercase letters. - **Shopping baskets:** At least one basket with a non-empty partnerShopId, a positive grossAmount, and the required item information. - **Item details:** Valid names, quantities, amounts, and tax rates. ## Exceptions The following exceptions may be thrown when generating a SECCI: | Exception | HTTP Status Code | | --- | --- | | `RatepaySdkValidationException` | (local checks) | | `RatepayAuthException` | 401/403 | | `RatepayValidationException` | 400 | | `RatepayLegalException` | 451 | | `RatepayNetworkException` | 5xx/transport | The SDK supports idempotent requests. You can supply an `idempotencyKey` or let the SDK generate one automatically. For more information, see [Exception hierarchy](/docs/developer/sdk/errors-and-resilience/exception-hierarchy) and [Idempotency and retries](/docs/developer/sdk/errors-and-resilience/idempotency-and-retries). ## Next steps - [Authorize](/docs/developer/sdk/guides/authorizing-a-transaction) a transaction using the SECCI attestation token.