diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..4842ae7f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,27 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [3.2.0] - 2026-05-27
+
+### Added
+
+- **PreApprovalPlan**: subscription plan template management — create, get, update, search
+ (`POST/GET/PUT /preapproval_plan`). Enables reusable billing templates for subscriptions.
+
+- **AdvancedPayment**: marketplace split-payment management — create, get, search, capture, cancel,
+ updateReleaseDate (`POST/GET/PUT /v1/advanced_payments`). Enables distributing a single
+ payment among multiple sellers.
+
+- **Invoice**: retrieval and search of subscription billing invoices — get, search
+ (`GET /authorized_payments`). Enables monitoring of billing cycles generated by preapprovals.
+
+- **DisbursementRefund**: refunds for split-payment disbursements — createAll, create
+ (`POST /v1/advanced_payments/{id}/refunds`). Enables partial and full refunds of individual
+ disbursements within an advanced payment.
+
+- **Chargeback**: read-only access to payment dispute records — get, search
+ (`GET /v1/chargebacks`). Enables monitoring and response to cardholder disputes.
diff --git a/mp_ref_report.md b/mp_ref_report.md
deleted file mode 100644
index dc53fa5f..00000000
--- a/mp_ref_report.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# MP API Reference Audit Report — Java SDK
-_Generado: 2026-04-24_
-
-## Resumen
-| Categoria | Cantidad |
-|-----------|----------|
-| Total metodos API | 48 |
-| Con reference URL | 28 (58%) |
-| Sin reference URL (gaps) | 20 (42%) |
-| URLs no canonicas (dominio pais) | 16 |
-| URLs canonicas (.com) | 10 |
-
----
-
-## URLs no canonicas (dominio por pais)
-
-Estas URLs usan dominios por pais (`.com.br`, `.com.ar`) en lugar del canonico `https://www.mercadopago.com/developers/en/reference/...`:
-
-| Archivo | URL encontrada | Dominio |
-|---------|---------------|---------|
-| PaymentRefundClient.java | `https://www.mercadopago.com.br/developers/en/reference/chargebacks/_payments_id_refunds/post` | .com.br |
-| PaymentRefundClient.java | `https://www.mercadopago.com.br/developers/en/reference/chargebacks/_payments_id_refunds_refund_id/get` | .com.br |
-| PaymentRefundClient.java | `https://www.mercadopago.com.br/developers/en/reference/chargebacks/_payments_id_refunds/get` | .com.br |
-| CustomerClient.java | `https://www.mercadopago.com.br/developers/en/reference/online-payments/checkout-api/customers/search-customer/get` | .com.br |
-| CustomerCardClient.java | `https://www.mercadopago.com.br/developers/en/reference/cards/_customers_customer_id_cards/get` | .com.br |
-| IdentificationTypeClient.java | `https://www.mercadopago.com.br/developers/en/reference/identification_types/_identification_types/get` | .com.br |
-| MerchantOrderClient.java | `https://www.mercadopago.com.br/developers/en/reference/merchant_orders/_merchant_orders/post` | .com.br |
-| MerchantOrderClient.java | `https://www.mercadopago.com.br/developers/en/reference/merchant_orders/_merchant_orders_id/get` | .com.br |
-| MerchantOrderClient.java | `https://www.mercadopago.com.br/developers/en/reference/merchant_orders/_merchant_orders_id/put` | .com.br |
-| MerchantOrderClient.java | `https://www.mercadopago.com.br/developers/en/reference/merchant_orders/_merchant_orders_search/get` | .com.br |
-| OauthClient.java | `https://www.mercadopago.com.br/developers/en/reference/oauth/_oauth_token/post` | .com.br |
-| PreferenceClient.java | `https://www.mercadopago.com.br/developers/en/reference/preferences/_checkout_preferences/post` | .com.br |
-| PreferenceClient.java | `https://www.mercadopago.com.br/developers/en/reference/preferences/_checkout_preferences_id/get` | .com.br |
-| PreferenceClient.java | `https://www.mercadopago.com.br/developers/en/reference/preferences/_checkout_preferences_id/put` | .com.br |
-| PreferenceClient.java | `https://www.mercadopago.com.br/developers/en/reference/preferences/_checkout_preferences_search/get` | .com.br |
-| PaymentMethodClient.java | `https://www.mercadopago.com.br/developers/en/reference/payment_methods/_payment_methods/get` | .com.br |
-| PointClient.java | `https://www.mercadopago.com.ar/developers/en/reference/in-person-payments/point/orders/create-order/post` | .com.ar |
-| PointClient.java | `https://www.mercadopago.com.ar/developers/en/reference/in-person-payments/point/orders/get-order/get` | .com.ar |
-| PointClient.java | `https://www.mercadopago.com.ar/developers/en/reference/in-person-payments/point/orders/cancel-order/post` | .com.ar |
-| PointClient.java | `https://www.mercadopago.com.ar/developers/en/reference/in-person-payments/point/terminals/get-terminals/get` | .com.ar |
-| PointClient.java | `https://www.mercadopago.com.ar/developers/en/reference/in-person-payments/point/terminals/update-operation-mode/patch` | .com.ar |
-
----
-
-## Links faltantes (gaps) — 20 metodos sin referencia
-
-### PaymentClient.java (4 gaps de 5 metodos)
-- [ ] `get(Long id)` — GET /v1/payments/{id}
-- [ ] `cancel(Long id)` — PUT /v1/payments/{id} (status=cancelled)
-- [ ] `capture(Long id, BigDecimal amount)` — PUT /v1/payments/{id} (capture=true)
-- [ ] `search(MPSearchRequest request)` — GET /v1/payments/search
-
-### CustomerCardClient.java (4 gaps de 4 metodos)
-- [ ] `get(String customerId, String cardId)` — GET /v1/customers/{id}/cards/{cardId}
-- [ ] `create(String customerId, CustomerCardCreateRequest request)` — POST /v1/customers/{id}/cards
-- [ ] `delete(String customerId, String cardId)` — DELETE /v1/customers/{id}/cards/{cardId}
-- [ ] `listAll(String customerId)` — GET /v1/customers/{id}/cards
-
-### CardTokenClient.java (2 gaps de 2 metodos)
-- [ ] `get(String id)` — GET /v1/card_tokens/{id}
-- [ ] `create(CardTokenRequest request)` — POST /v1/card_tokens
-
-### OrderClient.java (9 gaps de 10 metodos)
-- [ ] `get(String id)` — GET /v1/orders/{id}
-- [ ] `process(String id)` — POST /v1/orders/{id}/process
-- [ ] `createTransaction(String orderId, OrderTransactionRequest request)` — POST /v1/orders/{id}/transactions
-- [ ] `updateTransaction(String orderId, String transactionId, OrderPaymentRequest request)` — PUT /v1/orders/{id}/transactions/{transactionId}
-- [ ] `cancel(String orderId)` — POST /v1/orders/{id}/cancel
-- [ ] `capture(String orderId)` — POST /v1/orders/{id}/capture
-- [ ] `deleteTransaction(String orderId, String transactionId)` — DELETE /v1/orders/{id}/transactions/{transactionId}
-- [ ] `refund(String orderId, OrderRefundRequest request)` — POST /v1/orders/{id}/refund
-- [ ] `search(MPSearchRequest request)` — GET /v1/orders
-
-### PreapprovalClient.java (4 gaps de 4 metodos)
-- [ ] `get(String id)` — GET /preapproval/{id}
-- [ ] `create(PreapprovalCreateRequest request)` — POST /preapproval
-- [ ] `update(String id, PreapprovalUpdateRequest request)` — PUT /preapproval/{id}
-- [ ] `search(MPSearchRequest request)` — GET /preapproval/search
-
-### Otros
-- [ ] `CustomerClient.delete(String customerId)` — DELETE /v1/customers/{id}
-- [ ] `OauthClient.getAuthorizationURL(...)` — URL builder (no endpoint directo)
-- [ ] `PointClient.getPaymentIntentStatus(String id)` — GET /point/integration-api/payment-intents/{id}/events
-- [ ] `UserClient.get()` — GET /users/me
-
----
-
-## Cobertura por cliente
-
-| Cliente | Metodos | Con URL | Sin URL | Cobertura |
-|---------|---------|---------|---------|-----------|
-| PaymentClient | 6 | 1 | 5* | 17% |
-| PaymentRefundClient | 3 | 3 | 0 | 100% |
-| CustomerClient | 5 | 4 | 1 | 80% |
-| CustomerCardClient | 4 | 0 | 4 | 0% |
-| CardTokenClient | 2 | 0 | 2 | 0% |
-| IdentificationTypeClient | 1 | 1 | 0 | 100% |
-| MerchantOrderClient | 4 | 4 | 0 | 100% |
-| OauthClient | 3 | 2 | 1 | 67% |
-| OrderClient | 10 | 1 | 9 | 10% |
-| PointClient | 7 | 6 | 1 | 86% |
-| PreapprovalClient | 4 | 0 | 4 | 0% |
-| PreferenceClient | 4 | 4 | 0 | 100% |
-| PaymentMethodClient | 1 | 1 | 0 | 100% |
-| UserClient | 1 | 0 | 1 | 0% |
-
-*PaymentClient: tiene 1 URL a nivel de clase pero solo cubre create()
-
----
-
-## Lista de revision manual
-- [ ] PaymentClient.java — 4 metodos sin referencia API
-- [ ] CustomerCardClient.java — 4 metodos sin referencia API (0% cobertura)
-- [ ] CardTokenClient.java — 2 metodos sin referencia API (0% cobertura)
-- [ ] OrderClient.java — 9 metodos sin referencia API (10% cobertura)
-- [ ] PreapprovalClient.java — 4 metodos sin referencia API (0% cobertura)
-- [ ] CustomerClient.java — delete() sin referencia API
-- [ ] OauthClient.java — getAuthorizationURL() sin referencia API
-- [ ] PointClient.java — getPaymentIntentStatus() sin referencia API
-- [ ] UserClient.java — get() sin referencia API
-- [ ] 16 URLs usan dominio por pais (.com.br/.com.ar) en lugar del canonico (.com)
diff --git a/pom.xml b/pom.xml
index 9b2aaf3c..ad6ec1eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
Enables marketplace integrations to collect a single payment and distribute funds among + * multiple sellers (disbursements). Supports two-step flows (authorise → capture) and individual + * disbursement release-date control. + * + *
Usage example: + *
{@code
+ * AdvancedPaymentClient client = new AdvancedPaymentClient();
+ * AdvancedPayment payment = client.create(createRequest);
+ * AdvancedPayment captured = client.capture(payment.getId());
+ * }
+ *
+ * @see
+ * Advanced Payments API reference
+ */
+public class AdvancedPaymentClient extends MercadoPagoClient {
+
+ /** Class-level logger for advanced payment operations. */
+ private static final Logger LOGGER = Logger.getLogger(AdvancedPaymentClient.class.getName());
+
+ /** URL for the advanced payments collection endpoint. */
+ private static final String URL = "/v1/advanced_payments";
+
+ /** URL template for single advanced payment endpoints. */
+ private static final String URL_WITH_ID = "/v1/advanced_payments/%s";
+
+ /** URL template for the disburses (release date) endpoint. */
+ private static final String URL_DISBURSES = "/v1/advanced_payments/%s/disburses";
+
+ /**
+ * Default constructor. Uses the default HTTP client provided by {@link MercadoPagoConfig}.
+ */
+ public AdvancedPaymentClient() {
+ this(MercadoPagoConfig.getHttpClient());
+ }
+
+ /**
+ * Constructs an {@code AdvancedPaymentClient} with a custom HTTP client.
+ *
+ * @param httpClient the {@link MPHttpClient} used to execute HTTP requests
+ */
+ public AdvancedPaymentClient(MPHttpClient httpClient) {
+ super(httpClient);
+ StreamHandler streamHandler = getStreamHandler();
+ streamHandler.setLevel(MercadoPagoConfig.getLoggingLevel());
+ LOGGER.addHandler(streamHandler);
+ LOGGER.setLevel(MercadoPagoConfig.getLoggingLevel());
+ }
+
+ /**
+ * Retrieves an advanced payment by its unique identifier.
+ *
+ * @param id the unique identifier of the advanced payment
+ * @return the requested {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment get(Long id) throws MPException, MPApiException {
+ return this.get(id, null);
+ }
+
+ /**
+ * Retrieves an advanced payment by its unique identifier with custom request options.
+ *
+ * @param id the unique identifier of the advanced payment
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the requested {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment get(Long id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending get advanced payment request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.GET, null, null, requestOptions);
+
+ AdvancedPayment result = deserializeFromJson(AdvancedPayment.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Creates a new advanced (split) payment.
+ *
+ * @param request the {@link AdvancedPaymentCreateRequest} with payment and disbursement details
+ * @return the created {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment create(AdvancedPaymentCreateRequest request)
+ throws MPException, MPApiException {
+ return this.create(request, null);
+ }
+
+ /**
+ * Creates a new advanced (split) payment with custom request options.
+ *
+ * @param request the {@link AdvancedPaymentCreateRequest} with payment and disbursement details
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the created {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment create(AdvancedPaymentCreateRequest request,
+ MPRequestOptions requestOptions) throws MPException, MPApiException {
+ LOGGER.info("Sending create advanced payment request");
+ MPResponse response =
+ send(URL, HttpMethod.POST, serializeToJson(request), null, requestOptions);
+
+ AdvancedPayment result = deserializeFromJson(AdvancedPayment.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Captures a previously authorised advanced payment.
+ *
+ * @param id the unique identifier of the advanced payment to capture
+ * @return the captured {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment capture(Long id) throws MPException, MPApiException {
+ return this.capture(id, null);
+ }
+
+ /**
+ * Captures a previously authorised advanced payment with custom request options.
+ *
+ * @param id the unique identifier of the advanced payment to capture
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the captured {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment capture(Long id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending capture advanced payment request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.PUT,
+ serializeToJson(new AdvancedPaymentCaptureRequest()), null, requestOptions);
+
+ AdvancedPayment result = deserializeFromJson(AdvancedPayment.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Cancels an advanced payment by setting its status to "cancelled".
+ *
+ * @param id the unique identifier of the advanced payment to cancel
+ * @return the cancelled {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment cancel(Long id) throws MPException, MPApiException {
+ return this.cancel(id, null);
+ }
+
+ /**
+ * Cancels an advanced payment with custom request options.
+ *
+ * @param id the unique identifier of the advanced payment to cancel
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the cancelled {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment cancel(Long id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending cancel advanced payment request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.PUT,
+ serializeToJson(new AdvancedPaymentCancelRequest()), null, requestOptions);
+
+ AdvancedPayment result = deserializeFromJson(AdvancedPayment.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Changes the money release date for all disbursements of an advanced payment.
+ *
+ * @param id the unique identifier of the advanced payment
+ * @param releaseDate the new release date in ISO 8601 format
+ * @return the updated {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment updateReleaseDate(Long id, String releaseDate)
+ throws MPException, MPApiException {
+ return this.updateReleaseDate(id, releaseDate, null);
+ }
+
+ /**
+ * Changes the money release date for all disbursements with custom request options.
+ *
+ * @param id the unique identifier of the advanced payment
+ * @param releaseDate the new release date in ISO 8601 format
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the updated {@link AdvancedPayment}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public AdvancedPayment updateReleaseDate(Long id, String releaseDate,
+ MPRequestOptions requestOptions) throws MPException, MPApiException {
+ LOGGER.info("Sending update release date advanced payment request");
+ AdvancedPaymentUpdateReleaseDateRequest releaseDateRequest =
+ AdvancedPaymentUpdateReleaseDateRequest.builder().moneyReleaseDate(releaseDate).build();
+ MPResponse response =
+ send(String.format(URL_DISBURSES, id), HttpMethod.POST,
+ serializeToJson(releaseDateRequest), null, requestOptions);
+
+ AdvancedPayment result = deserializeFromJson(AdvancedPayment.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Searches advanced payments matching the given criteria.
+ *
+ * @param request the {@link MPSearchRequest} with search filters and pagination parameters
+ * @return an {@link MPResultsResourcesPage} of {@link AdvancedPayment} results
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public MPResultsResourcesPageProvides read-only access to chargeback dispute records initiated by cardholders through + * their issuing bank. + * + *
Usage example: + *
{@code
+ * ChargebackClient client = new ChargebackClient();
+ * Chargeback chargeback = client.get("CB-001");
+ * }
+ *
+ * @see
+ * Chargebacks API reference
+ */
+public class ChargebackClient extends MercadoPagoClient {
+
+ /** Class-level logger for chargeback operations. */
+ private static final Logger LOGGER = Logger.getLogger(ChargebackClient.class.getName());
+
+ /** URL template for single-chargeback endpoints. */
+ private static final String URL_WITH_ID = "/v1/chargebacks/%s";
+
+ /**
+ * Default constructor. Uses the default HTTP client provided by {@link MercadoPagoConfig}.
+ */
+ public ChargebackClient() {
+ this(MercadoPagoConfig.getHttpClient());
+ }
+
+ /**
+ * Constructs a {@code ChargebackClient} with a custom HTTP client.
+ *
+ * @param httpClient the {@link MPHttpClient} used to execute HTTP requests
+ */
+ public ChargebackClient(MPHttpClient httpClient) {
+ super(httpClient);
+ StreamHandler streamHandler = getStreamHandler();
+ streamHandler.setLevel(MercadoPagoConfig.getLoggingLevel());
+ LOGGER.addHandler(streamHandler);
+ LOGGER.setLevel(MercadoPagoConfig.getLoggingLevel());
+ }
+
+ /**
+ * Retrieves a chargeback by its unique identifier.
+ *
+ * @param id the unique identifier of the chargeback
+ * @return the requested {@link Chargeback}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public Chargeback get(String id) throws MPException, MPApiException {
+ return this.get(id, null);
+ }
+
+ /**
+ * Retrieves a chargeback by its unique identifier with custom request options.
+ *
+ * @param id the unique identifier of the chargeback
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the requested {@link Chargeback}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public Chargeback get(String id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending get chargeback request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.GET, null, null, requestOptions);
+
+ Chargeback result = deserializeFromJson(Chargeback.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Searches chargebacks matching the given criteria.
+ *
+ * @param request the {@link MPSearchRequest} with search filters and pagination parameters
+ * @return an {@link MPResultsResourcesPage} of {@link Chargeback} results
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public MPResultsResourcesPageEnables full and partial refunds of individual disbursements within an advanced (split) + * payment. Use this client alongside {@link com.mercadopago.client.advancedpayment.AdvancedPaymentClient}. + * + *
Usage example: + *
{@code
+ * DisbursementRefundClient client = new DisbursementRefundClient();
+ * DisbursementRefund refund = client.create(advancedPaymentId, disbursementId, createRequest);
+ * }
+ */
+public class DisbursementRefundClient extends MercadoPagoClient {
+
+ /** Class-level logger for disbursement refund operations. */
+ private static final Logger LOGGER = Logger.getLogger(DisbursementRefundClient.class.getName());
+
+ /** URL template for bulk refund endpoints. */
+ private static final String URL_REFUNDS = "/v1/advanced_payments/%s/refunds";
+
+ /** URL template for individual disbursement refund endpoints. */
+ private static final String URL_DISBURSEMENT_REFUND =
+ "/v1/advanced_payments/%s/disbursements/%s/refunds";
+
+ /**
+ * Default constructor. Uses the default HTTP client provided by {@link MercadoPagoConfig}.
+ */
+ public DisbursementRefundClient() {
+ this(MercadoPagoConfig.getHttpClient());
+ }
+
+ /**
+ * Constructs a {@code DisbursementRefundClient} with a custom HTTP client.
+ *
+ * @param httpClient the {@link MPHttpClient} used to execute HTTP requests
+ */
+ public DisbursementRefundClient(MPHttpClient httpClient) {
+ super(httpClient);
+ StreamHandler streamHandler = getStreamHandler();
+ streamHandler.setLevel(MercadoPagoConfig.getLoggingLevel());
+ LOGGER.addHandler(streamHandler);
+ LOGGER.setLevel(MercadoPagoConfig.getLoggingLevel());
+ }
+
+ /**
+ * Refunds all disbursements of an advanced payment at once.
+ *
+ * @param advancedPaymentId the unique identifier of the advanced payment
+ * @param request the {@link DisbursementRefundCreateRequest} with refund details
+ * @return the created bulk {@link DisbursementRefund}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public DisbursementRefund createAll(Long advancedPaymentId,
+ DisbursementRefundCreateRequest request) throws MPException, MPApiException {
+ return this.createAll(advancedPaymentId, request, null);
+ }
+
+ /**
+ * Refunds all disbursements of an advanced payment at once with custom request options.
+ *
+ * @param advancedPaymentId the unique identifier of the advanced payment
+ * @param request the {@link DisbursementRefundCreateRequest} with refund details
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the created bulk {@link DisbursementRefund}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public DisbursementRefund createAll(Long advancedPaymentId,
+ DisbursementRefundCreateRequest request, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending create all disbursement refund request");
+ MPResponse response = send(
+ String.format(URL_REFUNDS, advancedPaymentId),
+ HttpMethod.POST, serializeToJson(request), null, requestOptions);
+
+ DisbursementRefund result =
+ deserializeFromJson(DisbursementRefund.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Refunds a specific disbursement by amount.
+ *
+ * @param advancedPaymentId the unique identifier of the parent advanced payment
+ * @param disbursementId the unique identifier of the disbursement to refund
+ * @param request the {@link DisbursementRefundCreateRequest} with the refund amount
+ * @return the created {@link DisbursementRefund}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public DisbursementRefund create(Long advancedPaymentId, Long disbursementId,
+ DisbursementRefundCreateRequest request) throws MPException, MPApiException {
+ return this.create(advancedPaymentId, disbursementId, request, null);
+ }
+
+ /**
+ * Refunds a specific disbursement by amount with custom request options.
+ *
+ * @param advancedPaymentId the unique identifier of the parent advanced payment
+ * @param disbursementId the unique identifier of the disbursement to refund
+ * @param request the {@link DisbursementRefundCreateRequest} with the refund amount
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the created {@link DisbursementRefund}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public DisbursementRefund create(Long advancedPaymentId, Long disbursementId,
+ DisbursementRefundCreateRequest request, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending create disbursement refund request");
+ MPResponse response = send(
+ String.format(URL_DISBURSEMENT_REFUND, advancedPaymentId, disbursementId),
+ HttpMethod.POST, serializeToJson(request), null, requestOptions);
+
+ DisbursementRefund result =
+ deserializeFromJson(DisbursementRefund.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+}
diff --git a/src/main/java/com/mercadopago/client/disbursementrefund/DisbursementRefundCreateRequest.java b/src/main/java/com/mercadopago/client/disbursementrefund/DisbursementRefundCreateRequest.java
new file mode 100644
index 00000000..7f80c3af
--- /dev/null
+++ b/src/main/java/com/mercadopago/client/disbursementrefund/DisbursementRefundCreateRequest.java
@@ -0,0 +1,18 @@
+package com.mercadopago.client.disbursementrefund;
+
+import java.math.BigDecimal;
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * Request to create a disbursement refund.
+ *
+ * @see com.mercadopago.client.disbursementrefund.DisbursementRefundClient
+ */
+@Getter
+@Builder
+public class DisbursementRefundCreateRequest {
+
+ /** Amount to refund. When {@code null}, the full disbursement amount is refunded. */
+ private final BigDecimal amount;
+}
diff --git a/src/main/java/com/mercadopago/client/invoice/InvoiceClient.java b/src/main/java/com/mercadopago/client/invoice/InvoiceClient.java
new file mode 100644
index 00000000..db338edc
--- /dev/null
+++ b/src/main/java/com/mercadopago/client/invoice/InvoiceClient.java
@@ -0,0 +1,131 @@
+package com.mercadopago.client.invoice;
+
+import static com.mercadopago.MercadoPagoConfig.getStreamHandler;
+import static com.mercadopago.serialization.Serializer.deserializeFromJson;
+import static com.mercadopago.serialization.Serializer.deserializeResultsResourcesPageFromJson;
+
+import com.google.gson.reflect.TypeToken;
+import com.mercadopago.MercadoPagoConfig;
+import com.mercadopago.client.MercadoPagoClient;
+import com.mercadopago.core.MPRequestOptions;
+import com.mercadopago.exceptions.MPApiException;
+import com.mercadopago.exceptions.MPException;
+import com.mercadopago.net.HttpMethod;
+import com.mercadopago.net.MPHttpClient;
+import com.mercadopago.net.MPResponse;
+import com.mercadopago.net.MPResultsResourcesPage;
+import com.mercadopago.net.MPSearchRequest;
+import com.mercadopago.resources.invoice.Invoice;
+import java.lang.reflect.Type;
+import java.util.logging.Logger;
+import java.util.logging.StreamHandler;
+
+/**
+ * Client for the MercadoPago Authorized Payments (Invoice) API.
+ *
+ * Provides access to invoices generated from subscription (preapproval) billing cycles. Each + * invoice represents a scheduled payment attempt against the subscriber's payment method. + * + *
Usage example: + *
{@code
+ * InvoiceClient client = new InvoiceClient();
+ * Invoice invoice = client.get(6114264375L);
+ * }
+ *
+ * @see
+ * Invoice API reference
+ */
+public class InvoiceClient extends MercadoPagoClient {
+
+ /** Class-level logger for invoice operations. */
+ private static final Logger LOGGER = Logger.getLogger(InvoiceClient.class.getName());
+
+ /** URL template for single-invoice endpoints. */
+ private static final String URL_WITH_ID = "/authorized_payments/%s";
+
+ /**
+ * Default constructor. Uses the default HTTP client provided by {@link MercadoPagoConfig}.
+ */
+ public InvoiceClient() {
+ this(MercadoPagoConfig.getHttpClient());
+ }
+
+ /**
+ * Constructs an {@code InvoiceClient} with a custom HTTP client.
+ *
+ * @param httpClient the {@link MPHttpClient} used to execute HTTP requests
+ */
+ public InvoiceClient(MPHttpClient httpClient) {
+ super(httpClient);
+ StreamHandler streamHandler = getStreamHandler();
+ streamHandler.setLevel(MercadoPagoConfig.getLoggingLevel());
+ LOGGER.addHandler(streamHandler);
+ LOGGER.setLevel(MercadoPagoConfig.getLoggingLevel());
+ }
+
+ /**
+ * Retrieves an invoice (authorized payment) by its unique identifier.
+ *
+ * @param id the unique numeric identifier of the invoice
+ * @return the requested {@link Invoice}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public Invoice get(Long id) throws MPException, MPApiException {
+ return this.get(id, null);
+ }
+
+ /**
+ * Retrieves an invoice (authorized payment) by its unique identifier with custom request options.
+ *
+ * @param id the unique numeric identifier of the invoice
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the requested {@link Invoice}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public Invoice get(Long id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending get invoice request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.GET, null, null, requestOptions);
+
+ Invoice result = deserializeFromJson(Invoice.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Searches invoices matching the given criteria.
+ *
+ * @param request the {@link MPSearchRequest} with filters such as preapproval_id and payer_id
+ * @return an {@link MPResultsResourcesPage} of {@link Invoice} results
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public MPResultsResourcesPageProvides operations to create, retrieve, update, and search subscription plan templates. + * A plan defines the billing frequency, currency, and amount shared by all subscriptions + * attached to it. + * + *
Usage example: + *
{@code
+ * PreApprovalPlanClient client = new PreApprovalPlanClient();
+ * PreApprovalPlan plan = client.create(createRequest);
+ * }
+ *
+ * @see
+ * PreApproval Plan API reference
+ */
+public class PreApprovalPlanClient extends MercadoPagoClient {
+
+ /** Class-level logger for preapproval plan operations. */
+ private static final Logger LOGGER = Logger.getLogger(PreApprovalPlanClient.class.getName());
+
+ /** URL for the preapproval plan collection endpoint. */
+ private static final String URL = "/preapproval_plan";
+
+ /** URL template for single-plan endpoints. */
+ private static final String URL_WITH_ID = "/preapproval_plan/%s";
+
+ /**
+ * Default constructor. Uses the default HTTP client provided by {@link MercadoPagoConfig}.
+ */
+ public PreApprovalPlanClient() {
+ this(MercadoPagoConfig.getHttpClient());
+ }
+
+ /**
+ * Constructs a {@code PreApprovalPlanClient} with a custom HTTP client.
+ *
+ * @param httpClient the {@link MPHttpClient} used to execute HTTP requests
+ */
+ public PreApprovalPlanClient(MPHttpClient httpClient) {
+ super(httpClient);
+ StreamHandler streamHandler = getStreamHandler();
+ streamHandler.setLevel(MercadoPagoConfig.getLoggingLevel());
+ LOGGER.addHandler(streamHandler);
+ LOGGER.setLevel(MercadoPagoConfig.getLoggingLevel());
+ }
+
+ /**
+ * Retrieves a subscription plan by its unique identifier.
+ *
+ * @param id the unique identifier of the plan
+ * @return the requested {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan get(String id) throws MPException, MPApiException {
+ return this.get(id, null);
+ }
+
+ /**
+ * Retrieves a subscription plan by its unique identifier with custom request options.
+ *
+ * @param id the unique identifier of the plan
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the requested {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan get(String id, MPRequestOptions requestOptions)
+ throws MPException, MPApiException {
+ LOGGER.info("Sending get preapproval plan request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.GET, null, null, requestOptions);
+
+ PreApprovalPlan result = deserializeFromJson(PreApprovalPlan.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Creates a new subscription plan.
+ *
+ * @param request the {@link PreApprovalPlanCreateRequest} with plan details
+ * @return the created {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan create(PreApprovalPlanCreateRequest request)
+ throws MPException, MPApiException {
+ return this.create(request, null);
+ }
+
+ /**
+ * Creates a new subscription plan with custom request options.
+ *
+ * @param request the {@link PreApprovalPlanCreateRequest} with plan details
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the created {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan create(PreApprovalPlanCreateRequest request,
+ MPRequestOptions requestOptions) throws MPException, MPApiException {
+ LOGGER.info("Sending create preapproval plan request");
+ MPResponse response =
+ send(URL, HttpMethod.POST, serializeToJson(request), null, requestOptions);
+
+ PreApprovalPlan result = deserializeFromJson(PreApprovalPlan.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Updates an existing subscription plan.
+ *
+ * @param id the unique identifier of the plan to update
+ * @param request the {@link PreApprovalPlanUpdateRequest} with the fields to modify
+ * @return the updated {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan update(String id, PreApprovalPlanUpdateRequest request)
+ throws MPException, MPApiException {
+ return this.update(id, request, null);
+ }
+
+ /**
+ * Updates an existing subscription plan with custom request options.
+ *
+ * @param id the unique identifier of the plan to update
+ * @param request the {@link PreApprovalPlanUpdateRequest} with the fields to modify
+ * @param requestOptions optional {@link MPRequestOptions}; may be {@code null}
+ * @return the updated {@link PreApprovalPlan}
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public PreApprovalPlan update(String id, PreApprovalPlanUpdateRequest request,
+ MPRequestOptions requestOptions) throws MPException, MPApiException {
+ LOGGER.info("Sending update preapproval plan request");
+ MPResponse response =
+ send(String.format(URL_WITH_ID, id), HttpMethod.PUT, serializeToJson(request), null,
+ requestOptions);
+
+ PreApprovalPlan result = deserializeFromJson(PreApprovalPlan.class, response.getContent());
+ result.setResponse(response);
+ return result;
+ }
+
+ /**
+ * Searches subscription plans matching the given criteria.
+ *
+ * @param request the {@link MPSearchRequest} with search filters and pagination parameters
+ * @return an {@link MPResultsResourcesPage} of {@link PreApprovalPlan} results
+ * @throws MPException if a transport-level or SDK-internal error occurs
+ * @throws MPApiException if the API returns a non-successful HTTP status code
+ */
+ public MPResultsResourcesPageAn advanced payment allows a marketplace to collect a single payment and distribute the
+ * funds among multiple sellers (disbursements). Supports two-step flows (authorise → capture)
+ * and individual disbursement release-date control.
+ *
+ * @see com.mercadopago.client.advancedpayment.AdvancedPaymentClient
+ */
+@Getter
+public class AdvancedPayment extends MPResource {
+
+ /** Unique identifier of the advanced payment. */
+ private Long id;
+
+ /** Identifier of the MercadoPago application that created this payment. */
+ private String applicationId;
+
+ /** Integrator-provided external reference for reconciliation. */
+ private String externalReference;
+
+ /** Human-readable description of the payment. */
+ private String description;
+
+ /** Overall status of the advanced payment. */
+ private String status;
+
+ /** Whether the payment has been captured. */
+ private Boolean capture;
+
+ /** When true, the payment is approved or rejected immediately. */
+ private Boolean binaryMode;
+
+ /** Timestamp when this advanced payment was created. */
+ private OffsetDateTime dateCreated;
+
+ /** Timestamp of the last update. */
+ private OffsetDateTime dateLastUpdated;
+
+ /** List of disbursements associated with this advanced payment. */
+ private List A chargeback is initiated by a cardholder through their issuing bank when they dispute a
+ * payment. This resource provides read-only access to the dispute details.
+ *
+ * @see com.mercadopago.client.chargeback.ChargebackClient
+ */
+@Getter
+public class Chargeback extends MPResource {
+
+ /** Unique identifier of the chargeback. */
+ private String id;
+
+ /** Identifier of the payment that originated the dispute. */
+ private Long paymentId;
+
+ /** Current status of the chargeback (e.g., new, in_review, won, lost). */
+ private String status;
+
+ /** Amount disputed by the cardholder. */
+ private BigDecimal amount;
+
+ /** ISO 4217 currency code of the disputed amount. */
+ private String currencyId;
+
+ /** Card-network reason code for the dispute. */
+ private String reasonId;
+
+ /** Human-readable description of the dispute reason. */
+ private String reason;
+
+ /** Timestamp when the chargeback was created. */
+ private OffsetDateTime dateCreated;
+
+ /** Timestamp of the last modification. */
+ private OffsetDateTime lastModified;
+}
diff --git a/src/main/java/com/mercadopago/resources/disbursementrefund/DisbursementRefund.java b/src/main/java/com/mercadopago/resources/disbursementrefund/DisbursementRefund.java
new file mode 100644
index 00000000..055b47ee
--- /dev/null
+++ b/src/main/java/com/mercadopago/resources/disbursementrefund/DisbursementRefund.java
@@ -0,0 +1,33 @@
+package com.mercadopago.resources.disbursementrefund;
+
+import com.mercadopago.net.MPResource;
+import java.math.BigDecimal;
+import java.time.OffsetDateTime;
+import lombok.Getter;
+
+/**
+ * Resource representing a refund on a disbursement within an advanced payment.
+ *
+ * @see com.mercadopago.client.disbursementrefund.DisbursementRefundClient
+ */
+@Getter
+public class DisbursementRefund extends MPResource {
+
+ /** Unique identifier of this disbursement refund. */
+ private Long id;
+
+ /** Identifier of the parent advanced payment. */
+ private Long advancedPaymentId;
+
+ /** Identifier of the disbursement that was refunded. */
+ private Long disbursementId;
+
+ /** Amount refunded. */
+ private BigDecimal amount;
+
+ /** Current status of the refund. */
+ private String status;
+
+ /** Timestamp when the refund was created. */
+ private OffsetDateTime dateCreated;
+}
diff --git a/src/main/java/com/mercadopago/resources/invoice/Invoice.java b/src/main/java/com/mercadopago/resources/invoice/Invoice.java
new file mode 100644
index 00000000..eec036e1
--- /dev/null
+++ b/src/main/java/com/mercadopago/resources/invoice/Invoice.java
@@ -0,0 +1,66 @@
+package com.mercadopago.resources.invoice;
+
+import com.mercadopago.net.MPResource;
+import java.math.BigDecimal;
+import java.time.OffsetDateTime;
+import lombok.Getter;
+
+/**
+ * Resource representing a subscription invoice (authorized payment) in MercadoPago.
+ *
+ * Each invoice corresponds to a billing cycle of a {@code Preapproval} and tracks the charge
+ * amount, status, retry attempts, and the resulting payment.
+ *
+ * @see com.mercadopago.client.invoice.InvoiceClient
+ */
+@Getter
+public class Invoice extends MPResource {
+
+ /** Unique invoice identifier assigned by MercadoPago. */
+ private Long id;
+
+ /** Invoice type (e.g., scheduled). */
+ private String type;
+
+ /** Timestamp when the invoice was created. */
+ private OffsetDateTime dateCreated;
+
+ /** Timestamp of the last modification. */
+ private OffsetDateTime lastModified;
+
+ /** Identifier of the preapproval (subscription) that generated this invoice. */
+ private String preapprovalId;
+
+ /** Description or reason for the invoice charge. */
+ private String reason;
+
+ /** Integrator-provided external reference for reconciliation. */
+ private String externalReference;
+
+ /** ISO 4217 currency code for the invoice amount. */
+ private String currencyId;
+
+ /** Amount charged to the subscriber. */
+ private BigDecimal transactionAmount;
+
+ /** Scheduled date for the next retry if the payment failed. */
+ private OffsetDateTime nextRetryDate;
+
+ /** Scheduled date for the payment debit. */
+ private OffsetDateTime debitDate;
+
+ /** Payment method identifier used for this invoice. */
+ private String paymentMethodId;
+
+ /** Current retry attempt number. */
+ private Integer retryAttempt;
+
+ /** Invoice status (e.g., scheduled, processed, recycling, cancelled). */
+ private String status;
+
+ /** Summary description of the invoice status. */
+ private String summarized;
+
+ /** Payment details associated with this invoice. */
+ private InvoicePayment payment;
+}
diff --git a/src/main/java/com/mercadopago/resources/invoice/InvoicePayment.java b/src/main/java/com/mercadopago/resources/invoice/InvoicePayment.java
new file mode 100644
index 00000000..6f10a55c
--- /dev/null
+++ b/src/main/java/com/mercadopago/resources/invoice/InvoicePayment.java
@@ -0,0 +1,21 @@
+package com.mercadopago.resources.invoice;
+
+import lombok.Getter;
+
+/**
+ * Payment details embedded within a subscription invoice.
+ *
+ * @see Invoice
+ */
+@Getter
+public class InvoicePayment {
+
+ /** Unique payment identifier. */
+ private Long id;
+
+ /** Payment status (e.g., approved, rejected, pending). */
+ private String status;
+
+ /** Additional detail about the payment status. */
+ private String statusDetail;
+}
diff --git a/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlan.java b/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlan.java
new file mode 100644
index 00000000..a872ae97
--- /dev/null
+++ b/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlan.java
@@ -0,0 +1,53 @@
+package com.mercadopago.resources.preapprovalplan;
+
+import com.mercadopago.net.MPResource;
+import java.time.OffsetDateTime;
+import lombok.Getter;
+
+/**
+ * Resource representing a MercadoPago subscription plan template.
+ *
+ * A plan defines the billing frequency, currency, and amount shared by all subscriptions
+ * attached to it. Create one plan and link multiple subscribers to it.
+ *
+ * @see com.mercadopago.client.preapprovalplan.PreApprovalPlanClient
+ */
+@Getter
+public class PreApprovalPlan extends MPResource {
+
+ /** Unique identifier of this subscription plan. */
+ private String id;
+
+ /** Short descriptive title of the plan. */
+ private String reason;
+
+ /** Integrator-provided external reference. */
+ private String externalReference;
+
+ /** Current status of the plan (active, cancelled). */
+ private String status;
+
+ /** URL to redirect the subscriber after completing the checkout flow. */
+ private String backUrl;
+
+ /** MercadoPago user identifier of the seller who receives the charges. */
+ private Long collectorId;
+
+ /** Identifier of the application that created this plan. */
+ private String applicationId;
+
+ /** Timestamp when the plan was created. */
+ private OffsetDateTime dateCreated;
+
+ /** Timestamp of the last modification. */
+ private OffsetDateTime lastModified;
+
+ /** Production checkout URL for subscribing to this plan. */
+ private String initPoint;
+
+ /** Sandbox checkout URL for testing. */
+ private String sandboxInitPoint;
+
+ /** Recurring billing configuration. */
+ private PreApprovalPlanAutoRecurring autoRecurring;
+}
diff --git a/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlanAutoRecurring.java b/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlanAutoRecurring.java
new file mode 100644
index 00000000..f7f959cc
--- /dev/null
+++ b/src/main/java/com/mercadopago/resources/preapprovalplan/PreApprovalPlanAutoRecurring.java
@@ -0,0 +1,28 @@
+package com.mercadopago.resources.preapprovalplan;
+
+import java.math.BigDecimal;
+import lombok.Getter;
+
+/**
+ * Recurring billing configuration of a subscription plan.
+ *
+ * @see PreApprovalPlan
+ */
+@Getter
+public class PreApprovalPlanAutoRecurring {
+
+ /** Billing frequency unit (e.g., days, months). */
+ private String frequencyType;
+
+ /** Number of frequency units between each billing cycle. */
+ private Integer frequency;
+
+ /** ISO 4217 currency code for the recurring charge. */
+ private String currencyId;
+
+ /** Amount charged to the subscriber on each billing cycle. */
+ private BigDecimal transactionAmount;
+
+ /** Maximum number of billing cycles; {@code null} for indefinite. */
+ private Integer repetitions;
+}
diff --git a/src/test/java/com/mercadopago/client/advancedpayment/AdvancedPaymentClientTest.java b/src/test/java/com/mercadopago/client/advancedpayment/AdvancedPaymentClientTest.java
new file mode 100644
index 00000000..b5bad117
--- /dev/null
+++ b/src/test/java/com/mercadopago/client/advancedpayment/AdvancedPaymentClientTest.java
@@ -0,0 +1,88 @@
+package com.mercadopago.client.advancedpayment;
+
+import static com.mercadopago.helper.MockHelper.generateHttpResponseFromFile;
+import static com.mercadopago.net.HttpStatus.CREATED;
+import static com.mercadopago.net.HttpStatus.OK;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+
+import com.mercadopago.BaseClientTest;
+import com.mercadopago.exceptions.MPApiException;
+import com.mercadopago.exceptions.MPException;
+import com.mercadopago.net.MPResultsResourcesPage;
+import com.mercadopago.net.MPSearchRequest;
+import com.mercadopago.resources.advancedpayment.AdvancedPayment;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.protocol.HttpContext;
+import org.junit.jupiter.api.Test;
+
+class AdvancedPaymentClientTest extends BaseClientTest {
+
+ private static final String paymentBaseJson = "advancedPayment/advancedpayment_base.json";
+ private static final String paymentListJson = "advancedPayment/advancedpayment_list.json";
+ private final AdvancedPaymentClient client = new AdvancedPaymentClient();
+
+ @Test
+ void getSuccess() throws IOException, MPException, MPApiException {
+ HttpResponse httpResponse = generateHttpResponseFromFile(paymentBaseJson, OK);
+ doReturn(httpResponse)
+ .when(HTTP_CLIENT)
+ .execute(any(HttpRequestBase.class), any(HttpContext.class));
+
+ AdvancedPayment payment = client.get(20458724L);
+
+ assertNotNull(payment.getResponse());
+ assertEquals(OK, payment.getResponse().getStatusCode());
+ assertEquals(20458724L, payment.getId());
+ assertEquals("approved", payment.getStatus());
+ assertNotNull(payment.getDisbursements());
+ assertEquals(1, payment.getDisbursements().size());
+ }
+
+ @Test
+ void createSuccess() throws IOException, MPException, MPApiException {
+ HttpResponse httpResponse = generateHttpResponseFromFile(paymentBaseJson, CREATED);
+ doReturn(httpResponse)
+ .when(HTTP_CLIENT)
+ .execute(any(HttpRequestBase.class), any(HttpContext.class));
+
+ AdvancedPaymentCreateRequest request = AdvancedPaymentCreateRequest.builder()
+ .applicationId("59441713004005")
+ .disbursements(Arrays.asList(AdvancedPaymentDisbursementRequest.builder()
+ .collectorId(488656838L)
+ .amount(new BigDecimal("80.00"))
+ .build()))
+ .payer(AdvancedPaymentPayerRequest.builder().email("buyer@example.com").build())
+ .externalReference("ADV-REF-001")
+ .capture(false)
+ .build();
+
+ AdvancedPayment payment = client.create(request);
+
+ assertNotNull(payment.getResponse());
+ assertEquals(CREATED, payment.getResponse().getStatusCode());
+ assertEquals(20458724L, payment.getId());
+ }
+
+ @Test
+ void searchSuccess() throws IOException, MPException, MPApiException {
+ HttpResponse httpResponse = generateHttpResponseFromFile(paymentListJson, OK);
+ doReturn(httpResponse)
+ .when(HTTP_CLIENT)
+ .execute(any(HttpRequestBase.class), any(HttpContext.class));
+
+ MPSearchRequest searchRequest = MPSearchRequest.builder().limit(30).offset(0).build();
+ MPResultsResourcesPage