diff --git a/CHANGELOG.md b/CHANGELOG.md
index f13e13c..79a7267 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.1.0] - 2026-09-04
+
+### Added
+
+- Add `invoices.paymentSummary(String id, double amount)` to get the related-document object needed to build a payment complement (complemento de pago): installment number, previous balance, and taxes prorated to the paid amount.
+- Add the `PaymentSummary` model.
+
## [2.0.0] - 2026-08-24
### Added
diff --git a/pom.xml b/pom.xml
index a69187f..de17cc0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
io.facturapi
facturapi-java
- 2.0.0
+ 2.1.0
facturapi-java
Official Java SDK for Facturapi
https://github.com/facturapi/facturapi-java
diff --git a/src/main/java/io/facturapi/models/PaymentSummary.java b/src/main/java/io/facturapi/models/PaymentSummary.java
new file mode 100644
index 0000000..3244b70
--- /dev/null
+++ b/src/main/java/io/facturapi/models/PaymentSummary.java
@@ -0,0 +1,144 @@
+package io.facturapi.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Related document summary for a payment complement (complemento de pago).
+ * Ready to be used as an element of the complement's related documents.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PaymentSummary {
+ private String uuid;
+ private Integer folioNumber;
+ private String series;
+ private Integer installment;
+ private Double lastBalance;
+ private Double total;
+ private String currency;
+ private Double amount;
+ private List taxes = new ArrayList<>();
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public Integer getFolioNumber() {
+ return folioNumber;
+ }
+
+ public void setFolioNumber(Integer folioNumber) {
+ this.folioNumber = folioNumber;
+ }
+
+ public String getSeries() {
+ return series;
+ }
+
+ public void setSeries(String series) {
+ this.series = series;
+ }
+
+ public Integer getInstallment() {
+ return installment;
+ }
+
+ public void setInstallment(Integer installment) {
+ this.installment = installment;
+ }
+
+ public Double getLastBalance() {
+ return lastBalance;
+ }
+
+ public void setLastBalance(Double lastBalance) {
+ this.lastBalance = lastBalance;
+ }
+
+ public Double getTotal() {
+ return total;
+ }
+
+ public void setTotal(Double total) {
+ this.total = total;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(String currency) {
+ this.currency = currency;
+ }
+
+ public Double getAmount() {
+ return amount;
+ }
+
+ public void setAmount(Double amount) {
+ this.amount = amount;
+ }
+
+ public List getTaxes() {
+ return taxes;
+ }
+
+ public void setTaxes(List taxes) {
+ this.taxes = taxes;
+ }
+
+ /** Prorated tax breakdown for the paid amount. */
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class PaymentSummaryTax {
+ private Double base;
+ private Double rate;
+ private String type;
+ private String factor;
+ private Boolean withholding;
+
+ public Double getBase() {
+ return base;
+ }
+
+ public void setBase(Double base) {
+ this.base = base;
+ }
+
+ public Double getRate() {
+ return rate;
+ }
+
+ public void setRate(Double rate) {
+ this.rate = rate;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getFactor() {
+ return factor;
+ }
+
+ public void setFactor(String factor) {
+ this.factor = factor;
+ }
+
+ public Boolean getWithholding() {
+ return withholding;
+ }
+
+ public void setWithholding(Boolean withholding) {
+ this.withholding = withholding;
+ }
+ }
+}
diff --git a/src/main/java/io/facturapi/resources/InvoicesResource.java b/src/main/java/io/facturapi/resources/InvoicesResource.java
index 6dac015..7cdbdd4 100644
--- a/src/main/java/io/facturapi/resources/InvoicesResource.java
+++ b/src/main/java/io/facturapi/resources/InvoicesResource.java
@@ -4,6 +4,7 @@
import io.facturapi.http.FacturapiHttpClient;
import io.facturapi.models.GenericResponse;
import io.facturapi.models.Invoice;
+import io.facturapi.models.PaymentSummary;
import io.facturapi.models.SearchResult;
import java.io.InputStream;
import java.util.Map;
@@ -52,6 +53,22 @@ public Invoice retrieve(String id) {
return get("/invoices/" + id, null, Invoice.class);
}
+ /**
+ * Gets the information needed to add this invoice as a related document in a
+ * payment complement (complemento de pago): the installment number according
+ * to the payment history, the previous balance, and the invoice tax breakdown
+ * prorated to the amount being paid.
+ *
+ * @param id Invoice id.
+ * @param amount Amount being paid, expressed in the invoice currency. Cannot
+ * exceed the outstanding balance.
+ * @return Payment summary ready to be used as a related document.
+ * @see API reference
+ */
+ public PaymentSummary paymentSummary(String id, double amount) {
+ return get("/invoices/" + id + "/payment-summary", Map.of("amount", amount), PaymentSummary.class);
+ }
+
/**
* Cancels an invoice.
*
diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java
index ef43828..e425076 100644
--- a/src/test/java/io/facturapi/FacturapiResourcesTest.java
+++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java
@@ -46,6 +46,36 @@ void invoiceCreateUsesExpectedPath() {
assertEquals("/v2/invoices?test=true", request.uri().getPath() + "?" + request.uri().getQuery());
}
+ @Test
+ void invoicePaymentSummaryUsesExpectedPath() {
+ StubHttpClient httpClient = new StubHttpClient();
+ httpClient.enqueueJson(
+ 200,
+ "{\"uuid\":\"6CF6CE33-1BD2-4F88-A443-33013C069169\",\"installment\":1,"
+ + "\"last_balance\":100,\"total\":100,\"currency\":\"MXN\",\"amount\":58,"
+ + "\"taxes\":[{\"base\":50,\"rate\":0.16,\"type\":\"IVA\",\"factor\":\"Tasa\",\"withholding\":false}]}"
+ );
+
+ Facturapi sdk = new Facturapi(
+ FacturapiConfig.builder("sk_test")
+ .httpClient(httpClient.client())
+ .build()
+ );
+
+ var response = sdk.invoices().paymentSummary("inv_1", 58.0);
+
+ assertEquals("6CF6CE33-1BD2-4F88-A443-33013C069169", response.getUuid());
+ assertEquals(1, response.getInstallment());
+ assertEquals(50.0, response.getTaxes().get(0).getBase());
+
+ var request = httpClient.requests().get(0);
+ assertEquals("GET", request.method());
+ assertEquals(
+ "/v2/invoices/inv_1/payment-summary?amount=58.0",
+ request.uri().getPath() + "?" + request.uri().getQuery()
+ );
+ }
+
@Test
void receiptsToInvoiceUsesExpectedPath() {
StubHttpClient httpClient = new StubHttpClient();